我正在尝试使用PDFKit创建PDF文件。我插入一个像这样的图像:
var PDFDocument = require('pdfkit');
var doc = new PDFDocument();
doc.image(some_image_as_buffer);
它正如预期的那样工作。但现在想要修剪图像,我发现GraphicsMagick for node.js。但我遇到的问题是让它与PDFKit一起使用。 doc.image
需要一个文件名或缓冲区,但由于我已经有了一个缓冲区,我想使用缓冲区(因为缓冲区直接来自数据库,所以在任何地方都没有文件)。
剪裁的工作原理如下:
var gm = require('gm');
gm(some_image_as_buffer, 'image.png')
.trim()
.toBuffer(function(err, trimmed_image_buffer) {
// trimmed_image_buffer is correct,
// but I can't put it to the document like this:
doc.image(trimmed_image_buffer);
// beacause I don't know which page and/or position
// the doc is currently on, because of the asynchronous
// nature of this callback.
});
更新:
为了澄清:我希望能够在PDFKit的同步代码中使用异步修剪图像。 PDFKit仅同步工作,gm
不提供同步接口。
UPDATE2 :
var gm = require('gm');
gm(some_image_as_buffer, 'image.png')
.trim()
.toBuffer(function(err, trimmed_image_buffer) {
// trimmed_image_buffer is correct,
// but I can't put it to the document like this:
doc.image(trimmed_image_buffer);
// beacause I don't know which page and/or position
// the doc is currently on, because of the asynchronous
// nature of this callback.
});
doc.text('some text');
// is not guaranteed to run after image is inserted
// and a couple of hundred lines more
在这个示例的最后一行之后,有更多的代码行向PDF添加内容,但我不想把所有内容(几百行)放在一个回调中,因为我需要异步函数操纵图像。
有没有办法让这种操作同步?
答案 0 :(得分:3)
您基本上要求停止执行代码,直到某些异步操作完成。 肯定在一般情况下是不可能的。
如果是gm
模块,则也不可能。 gm
模块生成一个执行命令的新进程(在您的情况下为 trim()),并且用于生成新进程的API本质上是异步的。
在您的方案中使用承诺:
var gm = require('gm'),
Q = require('Q'),
PDFDocument = require('pdfkit'),
doc = new PDFDocument();
function getTrimmedImage(some_image_as_buffer){
var deferred = Q.defer();
gm(some_image_as_buffer, 'image.png')
.trim()
.toBuffer(function(err, trimmed_image_buffer) {
if(err) { deferred.reject(err); }
else { deferred.resolve(trimmed_image_buffer); }
});
return deferred.promise;
}
// here goes all manipulations before the trimmed image is inserted
getTrimmedImage(some_image_as_buffer).then(
function(trimmed_image_buffer){
doc.image(trimmed_image_buffer);
// here goes all manipulations after the trimmed image is inserted
}
);
正如我在上面的评论中所写,基于承诺的解决方案应该优雅地运作。我使用Q library,但任何其他的promise库也可以完成这项工作。
一种选择是在开始操作pdf之前收集所有异步性质的资源。然后你可以保证没有竞争条件发生,但它可能会减慢整个过程。我使用了一个玩具示例让它在浏览器环境中工作,如果您将其转换为用例,请告诉我:
function getAsyncResource(){
var defer = Q.defer();
setTimeout(function(){
var result = "Some value: " + Date.now();
console.log("Async resource resolved: " + result);
defer.resolve(result);
}, Math.random() * 5000);
return defer.promise;
}
function someOperationThatNeedsAsyncResources(A, B, C){
console.log("Have all resources: ", A, B, C);
}
var A = getAsyncResource(),
B = getAsyncResource(),
C = getAsyncResource();
Q.all([A,B,C]).spread(someOperationThatNeedsAsyncResources);
<script src="//cdnjs.cloudflare.com/ajax/libs/q.js/1.1.2/q.js"></script>
其他选择是将流程拆分为以下步骤:
function getAsyncResource(value){
var defer = Q.defer();
setTimeout(function(){
var result = "Some value: " + value;
console.log("Async resource resolved: " + result);
defer.resolve(result);
}, Math.random() * 5000);
return defer.promise;
}
function nextStep(resource){
console.log("Next step: " + resource);
}
var A = getAsyncResource("A"),
B = getAsyncResource("B"),
C = getAsyncResource("C");
A.then(nextStep)
.then(function(){return B;})
.then(nextStep)
.then(function(){return C;})
.then(nextStep);
<script src="//cdnjs.cloudflare.com/ajax/libs/q.js/1.1.2/q.js"></script>