galkahana / HummusJS

Node.js module for high performance creation, modification and parsing of PDF files and streams
http://www.pdfhummus.com
Other
1.14k stars 169 forks source link

No image using XObject with PageModifier #277

Open KernelDeimos opened 6 years ago

KernelDeimos commented 6 years ago

I'm attempting to render a PNG on a context given to me by a PageModifier. I believe I need to use createFromXObjectFromPNG because I eventually need to get my image from a stream rather than a file (as it's base64-encoded). Right now I'm testing it with a regular png file.

I am not pausing the context because I don't know how to do that when using the page modifier. I tried using pdfWriter.pausePageContentContext, but this induces a segfault (I'm assuming because it was created with the modifier)

This is how I'm attempting to render the image:

    var imageXObject = pdfWriter.createFormXObjectFromPNG(
        // imageStream // please work ...
        "test.png"
    );
    var inX = 0;
    var inY = 0;
    var scaleX = pageWidth;
    var scaleY = pageHeight;
    // src/deps/PDFWriter/AbstractContentContext.cpp
    ctx.q().cm(scaleX,0,0,scaleY,inX,inY)
        .doXObject(imageXObject).Q();

This snippet of code is being called inside the renderPage function of the follow code which instantiates the page modifier. It also draws a test image which gets rendered successfully.

        var ins = new hummus.PDFRStreamForFile("./input.pdf");
        var ous = new hummus.PDFStreamForResponse(res);
        var pdfWriter = hummus.createWriterToModify(ins, ous);

        var pdfReader = pdfWriter.getModifiedFileParser();
        var pageCount = pdfReader.getPagesCount();

        console.log("rendering PDF with " + pageCount + " pages")
        for (var i=0; i < pageCount; i++) {
            // Start context for page modifier
            var pageModifier = new hummus.PDFPageModifier(pdfWriter, i);
            var ctx = pageModifier.startContext().getContext();
            var pageInfo = pdfReader.parsePage(i);

            // from left, from bottom
            ctx.drawImage(20,20,"test.png");

            renderPage(pdfWriter, ctx, i, pageInfo, parsedFormData, parsedSessData);

            // Write modified page to buffer (well, I hope it's a buffer)
            pageModifier.endContext().writePage();
        }

        // Finish writing pdf and send data
        pdfWriter.end();
        res.end()
galkahana commented 6 years ago

Wonderful question! you can use pageModifier.endContext() to temporarily pause writing, and then continue with pageModifier.startContext().

read more here: https://github.com/galkahana/HummusJS/wiki/Modification#adding-content-to-existing-pages

As an alternative you can make the call to creating the png form later, and use a previously allocated object ID that you allocate while writing the page content. i call this forward referencing, you can see an example here: https://github.com/galkahana/HummusJS/blob/master/tests/ImagesAndFormsForwardReferenceTest.js

KernelDeimos commented 6 years ago

Awesome, thanks! I think I'll use forward referencing since it'll make my code the cleanest. Is there any notable performance difference? I'm guessing the only difference is it changes the output slightly, putting all the images in one place in the binary - would that be correct?

galkahana commented 6 years ago

correct. nothing that should hurt you in terms of performance.

KernelDeimos commented 6 years ago

I got it partially working. For now I'm just using the startContext() and endContext() functions. I made the assumption that I need to call getContext() again to update my context.

The problem I'm having now is that, instead of rendering my images, my code is rendering semi-transparent black boxes in the wrong locations.

This is how I'm getting a stream from my base64 image:

imageData = base64data.split(',')[1];
var imageBuffer = new Buffer(imageData, 'base64');
var imageStream = new hummus.PDFRStreamForBuffer(
    imageBuffer
);

pdf.pageModifier.endContext();
var imageXObject = pdf.writer.createFormXObjectFromPNG(
    imageStream
);

pdf.ctx = pdf.pageModifier.startContext().getContext();

pdf.ctx.q().cm(scaleX,0,0,scaleY,inX,inY)
    .doXObject(imageXObject).Q();

Is there anything obvious that I'm doing wrong? I tried writing a file using the same base64 data and that file works fine. I'm heading to sleep now but tomorrow I'll double-check that writing to a png file from the buffer I made works

galkahana commented 6 years ago

Try first to drop the cm. See that you get the images one on top of each other at top left corner. Continue fron there.

Also try adding true as second param wgen initting pagemodifier

chunyenHuang commented 6 years ago

The problem I'm having now is that, instead of rendering my images, my code is rendering semi-transparent black boxes in the wrong locations.

Try first to drop the cm. See that you get the images one on top of each other at top left corner. Continue fron there.

If you find the image is placed in wrong place, can you check the mediaBox of your pdf file? If it does not look like [0, 0, , ], you will need to calibrate your inX, inY.