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

Add transparent text overlay (stamp) to existing PDF #242

Closed richard-kurtosys closed 6 years ago

richard-kurtosys commented 6 years ago

First, Awesome library, thank you!

I'm trying to get a transparent watermark overlaid on each page of an existing PDF. I have read issue 160 and gone through issue 142 and read the wiki and examples a couple of times but I can't get it working correctly.

I can write text to each page of the pdf but I can't make it transparent. I have a small code test of trying to get just this working:

var hummus = require('hummus');

var pdfWriter = hummus.createWriterToModify('./page.pdf', {
    modifiedFilePath: './page_modified.pdf'
});
var fontCouri = pdfWriter.getFontForFile('./Arial.ttf');

/* CREATING THE TRANSPARENCY OBJECT? */
var objCxt = pdfWriter.getObjectsContext();
var gsId = objCxt.startNewIndirectObject();
var dict = objCxt.startDictionary()
dict.writeKey("type");
dict.writeNameValue("ExtGState");
dict.writeKey("CA");
objCxt.writeNumber(0.5);
objCxt.endLine();
objCxt.endDictionary(dict);

/* GETTING THE gsName? */
var page = pdfWriter.createPage(0, 0, 595, 840);
var resourcesDict = page.getResourcesDictionary();
var gsName = resourcesDict.addExtGStateMapping(gsId);

var pageModifier = new hummus.PDFPageModifier(pdfWriter, 0);
var xxx = pageModifier.startContext().getContext();

/* USING THE gsName ? */
xxx.q()
    .BT() // Begin Text
    .gs(gsName)
    .k(0, 0, 1, 0) // Set Color (CMYK, 0-1)
    .Tf(fontCouri, 50) // Text font? Font & size?
    .Tm(1, 0, 0, 1, 50, 350) // Text Matrix, set to coord 5,20
    .Tj('Deparment') // Show text
    .Tm(1, 0, 0, 1, 5, 5) // Text Matrix, set to coord 5,5 (to place below previous line)
    .Tj('Page Index ' + 0) // More text
    .ET() // End Text
    .q() // Push Current Matrix
    .cm(1, 0, 0, 1, 0, 35) // Set Current Matrix - scale to 100% (x and y), translate 0,35
    .Q()
pageModifier.endContext().writePage();

pdfWriter.end();

The 3 questions I have are: Am I creating the transparency object correctly? Am I getting the gsName correctly? Am I using .gs(gsName) correctly?

Thank you for your time and assistance!

chunyenHuang commented 6 years ago

For first question, Am I creating the transparency object correctly? I think you miss this part in https://github.com/galkahana/HummusJS/issues/160#issuecomment-300127454

For gsName, I believe you do it in the correct way, but you attach to page but not to the document structure.

However, here is one of the solution for your case. I think this will also fix your problem for translate to (0,35). If your watermark is a static string like DEMO, you can re-use this xObject on each page without creating a new one.

var hummus = require('hummus');

var pdfWriter = hummus.createWriterToModify('./page.pdf', {
    modifiedFilePath: './page_modified.pdf'
});
var fontCouri = pdfWriter.getFontForFile('./Arial.ttf');

/* CREATING THE TRANSPARENCY OBJECT? */
var objCxt = pdfWriter.getObjectsContext();
var gsId = objCxt.startNewIndirectObject();
var dict = objCxt.startDictionary()
dict.writeKey("type");
dict.writeNameValue("ExtGState");
// #242: Change from CA to ca
dict.writeKey("ca");
objCxt.writeNumber(0.5);
objCxt.endLine();
objCxt.endDictionary(dict);

/* GETTING THE gsName? */
var page = pdfWriter.createPage(0, 0, 595, 840);

// #242: Use xObjectForm
var xObject = pdfWriter.createFormXObject(0, 0, 595, 840);
var resourcesDict = xObject.getResourcesDictinary(); // This is not a typo =~=
var gsName = resourcesDict.addExtGStateMapping(gsId);

xObject.getContentContext()
    .q()
    .gs(gsName)
    .BT() // Begin Text
    .k(0, 0, 1, 0) // Set Color (CMYK, 0-1)
    .Tf(fontCouri, 50) // Text font? Font & size?
    .Tm(1, 0, 0, 1, 50, 350) // Text Matrix, set to coord 5,20
    .Tj('Deparment') // Show text
    .Tm(1, 0, 0, 1, 5, 5) // Text Matrix, set to coord 5,5 (to place below previous line)
    .Tj('Page Index ' + 0) // More text
    .ET() // End Text    
    .Q();
pdfWriter.endFormXObject(xObject);

var pageModifier = new hummus.PDFPageModifier(pdfWriter, 0);
var xxx = pageModifier.startContext().getContext();

xxx.q()
    .cm(1, 0, 0, 1, 0, 35) // Set Current Matrix - scale to 100% (x and y), translate 0,35
    // #242 add xobject
    .doXObject(xObject)
    .Q()
pageModifier.endContext().writePage();

pdfWriter.end();
richard-kurtosys commented 6 years ago

Thank you very much @chunyenHuang !

This works perfectly. Once I'm done I'll make a PR into the tests.

chunyenHuang commented 6 years ago

Transparent text is now supported in hummus-recipe@1.6.7 as well.

https://github.com/chunyenHuang/hummusRecipe/blob/6ee165672ef74bbe3827dcaf28680640395c27ac/tests/text.js#L16

            recipe
                .editPage(i)
                .text('WATERMARK', 'center', 'center', {
                    bold: true,
                    size: 60,
                    color: '#0000FF',
                    align: 'center center',
                    opacity: 0.3
                })
                .endPage()
aathirag commented 6 years ago

how can I draw a transparent rectangle using HummusJS. I tried adding opacity.

cxt.drawRectangle(x,y,width,height,{type: 'fill',
           color: '#eeeeee',
           opacity: 0.9});  

Am I missing anything?

goatandsheep commented 4 years ago

How do you do this on all the pages?