galkahana / HummusJS

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

Modify existing page adding content from another PDF #225

Open peter-borgstedt opened 6 years ago

peter-borgstedt commented 6 years ago

I'm trying to create an formXObject from a page in a file and then modify a page and render into upon that.

This works If I use a regular writer on a blank page, but using pagemodifier this does not work. I get following error: "page.getResourcesDictionary is not a function"

How can I make this work?

`

const templatePath = fs.readFileSync(path.resolve(__dirname + "/" + template.pdf));
const templateBuffer = new PDFRStreamForBuffer(templatePath);

const font = writer.getFontForFile(hummusHelper.Fonts.Arial);
const copyingContext = writer.createPDFCopyingContext(templateBuffer);
const templateParser = copyingContext.getSourceDocumentParser();
const templateDimension = templateParser.parsePage(0).getMediaBox();

const formObjectId = copyingContext.createFormXObjectFromPDFPage(0, hummus.ePDFPageBoxMediaBox);

const pageDimension = page.mediaBox;

//const pageContent = writer.startPageContentContext(page);
const pageContent = page.startContext().getContext();

const templateXObj = page.getResourcesDictionary().addFormXObjectMapping(formObjectId);

pageContent
    .q()
    .cm(1, 0, 0, 1, template.pos.x, template.pos.y)
    .doXObject(templateXObj)
    .Q();

`

I found this link, but it does not work. I've added the function to the protype, but it return null. https://groups.google.com/forum/#!topic/pdfhummus-interest-group/dAY48PPx3dM

hummus.PDFPageModifier.prototype.getResourcesDictionary = function(){ return this.contextForm ? this.contextForm.getResourcesDictinary() : null; };

Looking in the source code I can clear see:

PDFFormXObject* PDFModifiedPage::GetCurrentFormContext(){ return mCurrentContext; }

https://github.com/galkahana/HummusJS/blob/8913a2844afb498fdf69bf582aa9b5eb59d5cd15/src/deps/PDFWriter/PDFModifiedPage.cpp

PDFModifiedPage.cpp

ResourcesDictionary* PDFModifiedPage::GetCurrentResourcesDictionary(){ return mCurrentContext ? &(mCurrentContext->GetResourcesDictionary()):NULL; } I've also tried doing the later thing described in the attached link above (google forum).

create a form xobject, where you can implement the drawing, and get the resoruces dictionary to > get gsName. see here for details - https://github.com/galkahana/HummusJS/wiki/Reusable-forms. it's not written there but you can call myForm. getResourcesDictinary() to get the resources dictionary. then use myResDict. addExtGStateMapping() with the object id of the ExtGState that you created to get gsName

place the form in the page using cm and do [see the form usage example in the link above to see details]

I don't really get the thing with the ExtGState. But I did this, and it did not work:

`

const formObjectId = copyingContext.createFormXObjectFromPDFPage(0,hummus.ePDFPageBoxMediaBox);

const templateXObj = writer.createFormXObject(0,0,tw,th);
templateXObj.q()
.k(0,100,100,0) // White color
.re(0,0,tw,th)
.f()
.Q();
writer.endFormXObject(templateXObj);

const id = templateXObj.getResourcesDictionary().addFormXObjectMapping(formObjectId);
page.startContext().getContext()
.q()
.cm(1, 0, 0, 1, template.pos.x, template.pos.y)
.doXObject(id)
.Q();

`

peter-borgstedt commented 6 years ago

I've made something work! However, it feel more like a hack than a correct solution.

`

const formObjectId = copyingContext.createFormXObjectFromPDFPage(0,
        hummus.ePDFPageBoxMediaBox);

const templateXObj = writer.createFormXObject(0,0,w,h);
templateXObj.getContentContext()
.q()
.re(0,0,w,h) // Set dimension
.cm(1,0,0,1,0,0) // Positioning, no scaling and so on.
.doXObject(templateXObj.getResourcesDictionary().addFormXObjectMapping(formObjectId))
.Q();
writer.endFormXObject(templateXObj);

const page = new hummus.PDFPageModifier(writer, 0, true);
const pageContent = page.startContext().getContext()
.q()
.cm(1, 0, 0, 1, 0, 0)
.doXObject(templateXObj)
.Q();
page.endContext();
page.writePage();

`

chunyenHuang commented 6 years ago

First, I think you need to pause the page content if you want to write something during page modification. Things like writer.pausePageContentContext(pageContext) in https://github.com/galkahana/HummusJS/blob/02730266893d288ac6a88148c8f624c41519236e/tests/FormXObjectTest.js#L15

Second, for ExtGState, it's pdf graphic state for stroke and fill opacity. You can find example from #142. Or you can take look in HummusRecipe for some examples.

create ExtGStates use ExtGStates get GsName

mattgaspar commented 5 years ago

I figured out how to simplify the solution from @peter-borgstedt My page was created using a different method though so may not work exactly the same way.

const pdfWriter = hummus.createWriter(outputFilePath);
const pdfStream = new hummus.PDFRStreamForBuffer(nodeFileBuffer);

const copyingContext = pdfWriter.createPDFCopyingContext(pdfStream);
const formObjectId = copyingContext.createFormXObjectFromPDFPage(0,
    hummus.ePDFPageBoxMediaBox);

let page = pdfWriter.createPage(0, 0, 792, 612);
let pageContext = pdfWriter.startPageContentContext(page);
pdfWriter.pausePageContentContext(pageContext);

pageContext.q()
    .cm(1, 0, 0, 1, 0, 0) // no scaling, use last 2 for position
    .doXObject(page.getResourcesDictionary().addFormXObjectMapping(formObjectId))
    .Q();

pdfWriter.writePage(page);