How to underlay a content stream with using PDPageContentStream?

馋奶兔 提交于 2019-12-25 03:19:06

问题


I am trying to create a watermark with using PDPageContentStream. This is what I have right now

PDPageContentStream contentStream = new PDPageContentStream(doc,page, true,true);
contentStream.beginText();
contentStream.setFont(font,40);
contentStream.setTextRotation(Math.PI/4,page.getMediaBox().getWidth()/4,page.getMediaBox().getHeight()/4);
contentStream.setNonStrokingColor(210,210,210); //light grey
contentStream.drawString(_text);
contentStream.endText();
contentStream.close();

What happens is it creates a 45 degree angled text with the light grey color. But -of course- it overlays the actual page content beneath it and it is not possible to see some of the content.

Is it possible to create the contentStream first and then append the page content? I found this example. It uses PDExtendedGraphicsState and PDResources. I am new to pdfbox and almost have no graphics experience. Are these what I need and What is a resource in pdfbox?

Thanks in advance.

p.s. I am aware that I can use overlay utility with a jpeg. But I am trying to figure out this problem with PDPageContentStream for now.


回答1:


If you use PDFBox 2.0+, then its slightly easier now:

PDExtendedGraphicsState extendedGraphicsState = new PDExtendedGraphicsState();
extendedGraphicsState.setNonStrokingAlphaConstant((float) alpha);
contents.saveGraphicsState();
contents.setGraphicsStateParameters(extendedGraphicsState);
// do your stuff 
contents.restoreGraphicsState();



回答2:


I figured it out. The answer is actually here. But I had to walk it around a little bit. Here's my code:

PDExtendedGraphicsState extendedGraphicsState = new PDExtendedGraphicsState();
// Set the transparency/opacity
extendedGraphicsState.setNonStrokingAlphaConstant(0.5f);
PDResources resources = page.findResources();// Get the page resources.
// Get the defined graphic states.
Map graphicsStateDictionary = resources.getGraphicsStates();

 if (graphicsStateDictionary != null){ 
     graphicsStateDictionary.put("TransparentState", extendedGraphicsState); 
     resources.setGraphicsStates(graphicsStateDictionary); 
    }
 PDPageContentStream contentStream = new PDPageContentStream(document, page,true,true); 
 contentStream.appendRawCommands("/TransparentState gs\n");
 contentStream.setNonStrokingColor(210,210,210);

This code snippet creates a PDExtendedGraphicsState object. My understanding is 'Resources' is some kind of dictionary and holds attributes of the different PD objects like PDPage or PDGraphics.

At the beginning there is no state such as 'transparent state'. We create it with

 extendedGraphicsState.setNonStrokingAlphaConstant(0.5f);

then we name the graphicsState object as TransparentState. This is how we use it in the AppendRawCommands.

This explanation might be insufficient or wrong. Please leave your comments. I'd be happy to understand it better.




回答3:


As mentioned in a comment to the OP's own answer, that answer did not exactly what he had asked for (how to underlay an existing stream) but instead overlaying with transparency.

Thus, here an answer showing how one can do the originally asked for, underlaying an existing stream.

How to actually prepend a content stream

Underlaying essentially means prepending the new content to the existing content because the later content covers the former.

Unfortunately the PDFBox class PDPageContentStream does only provide constructors to append or replace everything with a new content stream but none to prepend a new content stream.

It is possible to cheat a bit, though: One can first append a new stream, fill it as if it was prepended, and finally reorder the streams:

void transformPage(PDDocument document, PDPage page) throws IOException, COSVisitorException
{
    PDPageContentStream stream = new PDPageContentStream(document, page, true, true);
    // add any content to stream as if it was the first stream
    stream.close();

    COSBase contents = page.getCOSDictionary().getDictionaryObject(COSName.CONTENTS);
    if (contents instanceof COSStreamArray)
    {
        COSStreamArray contentsArray = (COSStreamArray) contents;
        COSArray newArray = new COSArray();
        newArray.add(contentsArray.get(contentsArray.getStreamCount() - 1));

        for (int i = 0; i < contentsArray.getStreamCount() - 1; i++)
        {
            newArray.add(contentsArray.get(i));
        }

        COSStreamArray newStreamArray = new COSStreamArray(newArray);
        page.getCOSDictionary().setItem(COSName.CONTENTS, newStreamArray);
    }
}

Which is better, prepending or appending with transparency

The answer is... it depends. ;)

On one hand the solution using transparency often is nearer to the expected appearance than the solution using underlaying: If e.g. you underlay beneath a content that first fills the whole area in white, you won't see anything of the underlayed content!

On the other hand there are multiple context forbidding the use of transparency, especially in case of PDF/A variants. As overlaying with transparency in such context is not permitted, one either has to overlay using very thin lines only or simply underlay.



来源:https://stackoverflow.com/questions/28502226/how-to-underlay-a-content-stream-with-using-pdpagecontentstream

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!