pdfbox-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Tilman Hausherr (JIRA)" <j...@apache.org>
Subject [jira] [Commented] (PDFBOX-4556) how the visual representation of the signature is achieved
Date Tue, 18 Jun 2019 16:38:00 GMT

    [ https://issues.apache.org/jira/browse/PDFBOX-4556?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16866815#comment-16866815
] 

Tilman Hausherr commented on PDFBOX-4556:
-----------------------------------------

You did pass a PNG image. It is a green checkmark. Open the PDF file with an editor you'll
see "‰PNG" at offset 9809.

The IllegalStateException message is poorly done, I'll improve it. It means that the image
has neither 3 (RGB) nor 1 (B/W) color components. Here's the current source code of {{PDJpeg.setPropertiesFromAWT}}:
{code:java}
    private void setPropertiesFromAWT(BufferedImage image) throws IllegalStateException
    {
        setBitsPerComponent( 8 );
        if (image.getColorModel().getNumComponents() == 3)
        {
            setColorSpace( PDDeviceRGB.INSTANCE );
        }
        else
        {
            if (image.getColorModel().getNumComponents() == 1)
            {
                setColorSpace( new PDDeviceGray() );
            }
            else
            {
                throw new IllegalStateException("");
            }
        }
        setHeight( image.getHeight() );
        setWidth( image.getWidth() );
    }
{code}
To find out how many color components your image has, try this:
{code:java}
BufferedImage bim = ImageIO.read(....);
System.out.println("color component count: " + bim.getColorModel().getNumComponents());
System.out.println("color model: " + bim.getColorModel());
System.out.println("image: " + bim);
{code}
It could be the image has transparency (ARGB) or CMYK.

The other problem (NPE) happens when calling save(). You had that one before, see my earlier
comments.

I did some more research... You use the existing PDDocument, but should have created a new
PDDocument. You wrote into the page content stream, but should have written into the appearance
stream (this "emptiness" resulted in the NPE). Sadly there are no methods to write PDF operators
into an appearance stream in 1.8, so you have to do it the hard way. (Or write into a page
content stream and then read the result, and write it back into the appearance stream).

The code below works, although many things are hard-wired, and the exception handling is not
"best practice". And your text is all in one line.
{code:java}
    private static InputStream createVisualSignatureTemplate(PDDocument signedDoc, int stampingPage,
String tickImagePath, String name, String location, String reason, int xCo_ordinates, int
yCo_ordinates, int signatureHeight, int signatureWidth)
    {
        File imageFile = new File(tickImagePath);
        //PDPage page = (PDPage) signedDoc.getDocumentCatalog().getAllPages().get(stampingPage
- 1);
        PDPage page = new PDPage(((PDPage) (signedDoc.getDocumentCatalog().getAllPages().get(stampingPage-1))).findMediaBox());
        //NEW! Don't use "signedDoc" except to get page dimensions
        PDDocument doc = new PDDocument();
        doc.addPage(page); // dummy page, just to hold the annotation

        float x = (float) xCo_ordinates;
        float y = (float) yCo_ordinates;
        float sigWidth = (float) signatureWidth;
        float sigheight = (float) signatureHeight;
        


        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        try
        {

            Rectangle2D humanRectangle = new Rectangle2D.Float(xCo_ordinates, yCo_ordinates,
signatureWidth, signatureHeight);
            // create signature rectangle
            PDRectangle pdRectangle = null;
            if (pdRectangle == null)
            {
                // quick solution now
                //pdRectangle = createSignatureRectangle(signedDoc, humanRectangle, stampingPage);
                pdRectangle = new PDRectangle((int) humanRectangle.getWidth(), (int) humanRectangle.getHeight());
            }

            PDAcroForm acroForm = new PDAcroForm(doc);
            // signedDoc.getDocumentCatalog().setAcroForm(acroForm); // wrong
            doc.getDocumentCatalog().setAcroForm(acroForm);
            //signedDoc.getDocumentCatalog().getCOSObject().setNeedToBeUpdate(true); // wrong
            doc.getDocumentCatalog().getCOSObject().setNeedToBeUpdate(true);

            PDSignatureField signatureField = new PDSignatureField(acroForm);
            //List<PDField> acroFormFields = new List<PDField>(); // maybe works
in C++
            List<PDField> acroFormFields = new ArrayList<PDField>();
            acroForm.getCOSObject().setDirect(true);
            acroForm.getCOSObject().setNeedToBeUpdate(true);
            acroFormFields.add(signatureField);
            signatureField.getCOSObject().setNeedToBeUpdate(true);
            
            acroForm.setFields(acroFormFields); //NEW

            //PDAnnotationWidget Widget = new PDAnnotationWidget(); // wrong!!!
            PDAnnotationWidget Widget = signatureField.getWidget();
            //PDAnnotation Widget = PDAnnotation.createAnnotation(signedDoc.getDocumentCatalog().getCOSObject());
            Widget.getCOSObject().setNeedToBeUpdate(true);
            Widget.setRectangle(pdRectangle);
            Widget.setPage(page);

            //PDStream pdfstream = new PDStream(signedDoc); // wrong!!!
            PDStream pdfstream = new PDStream(doc);
            pdfstream.getCOSObject().setNeedToBeUpdate(true);
            PDXObjectForm form = new PDXObjectForm(pdfstream);
            form.getCOSObject().setNeedToBeUpdate(true);
            // set resources to xobj
            PDResources resources = new PDResources();
            resources.getCOSObject().setNeedToBeUpdate(true);
            PDFont font = PDType1Font.HELVETICA;

            form.setResources(resources);
            form.setFormType(1);
            PDRectangle bbox = new PDRectangle(pdRectangle.getWidth(), pdRectangle.getHeight());
            float height = bbox.getHeight();
            form.setBBox(pdRectangle);
            //PDAppearanceDictionary appearanceDictionary = new PDAppearanceDictionary(signedDoc.getDocumentCatalog().getCOSDictionary());
// wrong!!!
            PDAppearanceDictionary appearanceDictionary = new PDAppearanceDictionary();
            appearanceDictionary.getCOSObject().setDirect(true);
            appearanceDictionary.getCOSObject().setNeedToBeUpdate(true);
            PDAppearanceStream appearanceStream = new PDAppearanceStream(form.getCOSStream());
            appearanceStream.getCOSObject().setNeedToBeUpdate(true);
            appearanceDictionary.setNormalAppearance(appearanceStream);
            
            OutputStream os = form.getCOSStream().createFilteredStream();
            // appearance content stream must be written the hard way :-(
            // see "operator summary" in the PDF specification
            os.write(("0 0 0 rg\n" +
                    "0 0 100 100 re\n" + // assumed 100 x 100 at position 0,0
                                         // replace with  x  y  sigWidth  sigheight);
                    "f\n" +
                    "q\n" +
                    "  163 0 0 39 0 0 cm\n" + // my image size 163 x 39
                    "  /Im0 Do\n" +
                    "Q\n" +
                    "q\n" +
                    "Q\n" +
                    "/F0 10 Tf\n" +
                    "BT\n" +
                    "  (signed By:) Tj\n" + // all on the same line :-(
                    "  (Name:name) Tj\n" +
                    "  (Location:location) Tj\n" +
                    "  (Reason:reason) Tj\n" +
                    "ET").getBytes());
            os.close();
            resources.addFont(font, "F0"); // exact key
            resources.addXObject(new PDJpeg(doc, new FileInputStream(tickImagePath)), "Im");
// just prefix
            
            Widget.setAppearance(appearanceDictionary);
            try
            {
                if (false) // disabled. This was just to see what ends in the content stream.
We need to write into the appearance stream.
                {
                    //PDPageContentStream pagecontentStream = new PDPageContentStream(signedDoc,
page, true, false); // wrong!!!
                    PDPageContentStream pagecontentStream = new PDPageContentStream(doc, page,
true, false);
                    pagecontentStream.setNonStrokingColor(java.awt.Color.black);
                    pagecontentStream.addRect(x, y, sigWidth, sigheight);
                    int windingRule = PathIterator.WIND_NON_ZERO;
                    pagecontentStream.fill(windingRule);
                    PDXObjectImage imgobj = null;
                    //imgobj = new PDJpeg(signedDoc, new FileInputStream(tickImagePath));
// wrong!!!
                    imgobj = new PDJpeg(doc, new FileInputStream(tickImagePath));
                    //imgobj = createImageobject(tickImagePath, signedDoc, stampingPage);
                    // draw xobject with coordinates

                    pagecontentStream.drawImage(imgobj, x, y);
                    pagecontentStream.saveGraphicsState();

                    pagecontentStream.restoreGraphicsState();

                    float fontSize = 8;
                    float leading = fontSize * 1.0f;
                    pagecontentStream.setFont(font, 10);
                    pagecontentStream.beginText();

                    //if (!string.IsNullOrEmpty(name)) // can't use this one in java
                    {
                        pagecontentStream.drawString("signed By:");
                        pagecontentStream.drawString("Name:" + name);
                    }
                    //if (!string.IsNullOrEmpty(location))
                    {
                        pagecontentStream.drawString("Location:" + location);
                    }
                    //if (!string.IsNullOrEmpty(reason))
                    {
                        pagecontentStream.drawString("Reason:" + reason);
                    }
                    pagecontentStream.endText();
                    pagecontentStream.close();
                }
            }
            catch (IOException ex)
            {
                ex.printStackTrace();
                return null;
            }

            // NEW must add widget to page
            List<PDAnnotation> annotations = new ArrayList<PDAnnotation>();
            annotations.add(Widget);
            page.setAnnotations(annotations);

            //signedDoc.save(baos); // wrong
            doc.save(baos);
            doc.close(); // new
            return new ByteArrayInputStream(baos.toByteArray());
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
            return null;
        }
    }
{code}

> how the visual representation of the signature is achieved  
> ------------------------------------------------------------
>
>                 Key: PDFBOX-4556
>                 URL: https://issues.apache.org/jira/browse/PDFBOX-4556
>             Project: PDFBox
>          Issue Type: Wish
>          Components: .NET
>    Affects Versions: 1.8.16
>            Reporter: bal
>            Priority: Major
>         Attachments: image  error stacktrace.txt, npe at adappearence dictionary.txt,
pdfAnnotation.pdf, visibleSignature.pdf
>
>




--
This message was sent by Atlassian JIRA
(v7.6.3#76005)

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@pdfbox.apache.org
For additional commands, e-mail: dev-help@pdfbox.apache.org


Mime
View raw message