pdfclown

PDFClown Copy annotations and then manipulate them


I have the need to copy annotations from one PDF File to another. I have used the excellent PDFClown library but unable to manipulate things like color,rotation etc. Is this possible? I can see the baseobject information but also unsure how to manipulate that directly.

I can copy the appearance via cloning appearance but can't "edit" it.

Thanks in advance. Alex

P.S If Stephano the author is listeing ,is project dead?


Solution

  • On annotations in general and Callout annotations in particular

    I looked into it a bit, and I'm afraid there is not much you can deterministically manipulate for arbitrary inputs using high level methods. The reason is that there are numerous alternative ways to set the appearance of a Callout annotation and PDF Clown only supports the less prioritized ways with explicit high level methods. From high priority downwards

    Your tasks

    Concerning the attributes you stated you want to change, therefore,

    Some example code

    I created a file with an example Callout annotation using Adobe Acrobat: Callout-Yellow.pdf. It contains an appearance stream, rich text, and simple attributes, so one can use this file for example manipulations at different levels.

    The I applied this code to it with different values for keepAppearanceStream and keepRichText (you didn't mention whether you used PDF Clown for Java or .Net; so I chose Java; a port to .Net should be trivial, though...):

    boolean keepAppearanceStream = ...;
    boolean keepRichText = ...;
    
    try (   InputStream sourceResource = GET_STREAM_FOR("Callout-Yellow.pdf");
            InputStream targetResource = GET_STREAM_FOR("test123.pdf");
            org.pdfclown.files.File sourceFile = new org.pdfclown.files.File(sourceResource);
            org.pdfclown.files.File targetFile = new org.pdfclown.files.File(targetResource); ) {
        Document sourceDoc = sourceFile.getDocument();
        Page sourcePage = sourceDoc.getPages().get(0);
        Annotation<?> sourceAnnotation = sourcePage.getAnnotations().get(0);
    
        Document targetDoc = targetFile.getDocument();
        Page targetPage = targetDoc.getPages().get(0);
    
        StaticNote targetAnnotation = (StaticNote) sourceAnnotation.clone(targetDoc);
    
        if (keepAppearanceStream) {
            // changing properties of an appearance
            // rotating the appearance in the appearance rectangle
            targetAnnotation.getAppearance().getNormal().get(null).setMatrix(AffineTransform.getRotateInstance(100, 10));
        } else {
            // removing the appearance to allow lower level properties changes
            targetAnnotation.setAppearance(null);
        }
    
        // changing text background color
        targetAnnotation.setColor(new DeviceRGBColor(0, 0, 1));
    
        if (keepRichText) {
            // changing rich text properties
            PdfString richText = (PdfString) targetAnnotation.getBaseDataObject().get(PdfName.RC);
            String richTextString = richText.getStringValue();
            // replacing the font family
            richTextString = richTextString.replaceAll("font-family:Helvetica", "font-family:Courier");
            richText = new PdfString(richTextString);
            targetAnnotation.getBaseDataObject().put(PdfName.RC, richText);
        } else {
            targetAnnotation.getBaseDataObject().remove(PdfName.RC);
            targetAnnotation.getBaseDataObject().remove(PdfName.DS);
        }
    
        // changing default appearance properties
        PdfString defaultAppearance = (PdfString) targetAnnotation.getBaseDataObject().get(PdfName.DA);
        String defaultAppearanceString = defaultAppearance.getStringValue();
        // replacing the font
        defaultAppearanceString = defaultAppearanceString.replaceFirst("Helv", "HeBo");
        // replacing the text and line color
        defaultAppearanceString = defaultAppearanceString.replaceFirst(". . . rg", ".5 g");
        defaultAppearance = new PdfString(defaultAppearanceString);
        targetAnnotation.getBaseDataObject().put(PdfName.DA, defaultAppearance);
    
        // changing the text value
        PdfString contents = (PdfString) targetAnnotation.getBaseDataObject().get(PdfName.Contents);
        String contentsString = contents.getStringValue();
        contentsString = contentsString.replaceFirst("text", "text line");
        contents = new PdfString(contentsString);
        targetAnnotation.getBaseDataObject().put(PdfName.Contents, contents);
    
        // change the line width and style
        targetAnnotation.setBorder(new Border(0, new LineDash(new double[] {3, 2})));
    
        targetPage.getAnnotations().add(targetAnnotation);
    
        targetFile.save(new File(RESULT_FOLDER, "test123-withCalloutCopy.pdf"),  SerializationModeEnum.Standard);
    }
    

    (CopyCallOut test testCopyCallout)

    Beware, the code only has proof-of-concept quality: For arbitrary PDFs you cannot simply expect a string replace of "font-family:Helvetica" by "font-family:Courier" or "Helv" by "HeBo" or ". . . rg" by ".5 g" to do the job: fonts can be given using different style attributes or names, and different coloring instructions may be used.

    Screenshots in Adobe