iTextSharp CreateInk method: curves and corners

2019-09-11 16:01发布

问题:

I'm using iTextSharp to draw markup graphics in PDF documents, using PdfAnnotation.CreateInk. I'm trying to draw rectangles, passing in an array of five co-ordinates. I know iTextSharp has a dedicated function for drawing rectangles, but I'm trying to use just one method to draw the various kinds of markup lines I need, and it seems that CeateInk should be able to do that.

The problem is that CreateInk is drawing the rectangle with curves rather than corners. I haven't been able to figure out how to change this. This answer suggests that the solution might be to create a PdfAppearance; I haven't yet figured out whether that will work. Here's my code (the first bit converts a list of list of points to an array of arrays, which InkList requires):

    public void MarkupInk(List<List<float>> InkList){   
    float[][] Coords = new float[InkList.Count][];                      
    for (int i = 0; i < InkList.Count; i++) {                   
        float[] thisarr = InkList [i].ToArray ();
        Coords[i] = new float[InkList[i].Count];
        System.Array.Copy (thisarr, Coords [i], InkList [i].Count);  
    }
    using(MemoryStream ms = new MemoryStream ()){   
        PdfReader reader = new PdfReader (textmaster.pdfDocArr);
        PdfStamper stamper = new PdfStamper (reader, ms);
        pagerect = reader.GetPageSize (textmaster.currentfirstpage + 1);
        PdfAnnotation an2 = PdfAnnotation.CreateInk (stamper.Writer, pagerect, "", Coords);
        an2.Color = strokeColor;
        an2.BorderStyle = new PdfBorderDictionary (strokeWeight, PdfBorderDictionary.STYLE_SOLID);
        stamper.AddAnnotation (an2, textmaster.currentfirstpage+1);
        stamper.Close ();
        textmaster.pdfDocArr = ms.ToArray ();
        reader.Close ();
    }
}

Any suggestions are much appreciated. Thanks!

EDIT: following @mkl's code I now have code that creates PDFAnnotations with appearances. And most of those annotations show up correctly in the viewing applications I use. But there is one odd behavior that I have not been able to fix.

What's happening is that the most recently created annotation does not appear in the viewing applications until I've created another annotation. So if I create annotation A, it's invisible until I create annotation B, at which point annotation A appears and B does not. Creating annotation C causes annotation B to appear, and so on.

This behavior persists even if I close the pdf file and the viewing application and re-load from disk. So the data describing the last-created annotation exists as part of the pdf file, but it doesn't render until I've created a new annotation.

I suspect there's something I'm still missing in the code I'm using to create the annotations and pdfAppearances. Here's code that creates a single line annotation:

public void WriteLineAnnotation(List<float> polyCoords){    

    using (MemoryStream ms = new MemoryStream ()) {
        PdfReader reader = new PdfReader (textmaster.pdfDocArr);
        PdfStamper stamper = new PdfStamper (reader, ms) { AnnotationFlattening = true };
        pagerect = reader.GetPageSize (textmaster.currentfirstpage + 1);

        //Create the pdfAnnotation polyline
        PdfAnnotation polyann = PdfAnnotation.CreatePolygonPolyline (stamper.Writer, pagerect, "", false, new PdfArray (polyCoords.ToArray ()));
        polyann.Color = strokeColor;
        polyann.BorderStyle = new PdfBorderDictionary (strokeWeight, PdfBorderDictionary.STYLE_SOLID);
        polyann.Flags = iTextSharp.text.pdf.PdfAnnotation.FLAGS_PRINT;

        //create the PdfAppearance and set attributes
        PdfAppearance app = PdfAppearance.CreateAppearance (stamper.Writer, pagerect.Width, pagerect.Height);
        app.SetColorStroke (strokeColor);
        app.MoveTo (polyCoords [0], polyCoords [1]);
        app.LineTo (polyCoords [2], polyCoords [3]);
        app.Stroke ();

        //set the annotation's appearance, add annotation, clean up
        polyann.SetAppearance (PdfName.N, app);
        stamper.AddAnnotation (polyann, textmaster.currentfirstpage + 1);
        stamper.Close ();
        reader.Close ();

        //create bytearray from memorystream and send to pdf renderer
        textmaster.pdfDocArr = ms.ToArray ();
    }
}

[/code]

Is there something obvious that I'm missing? Thanks in advance for any help.

回答1:

Which annotation type to use

I know iTextSharp has a dedicated function for drawing rectangles, but I'm trying to use just one method to draw the various kinds of markup lines I need, and it seems that CeateInk should be able to do that.

Please be aware that iText not merely has separate functions for those different forms, these different functions also create different types of PDF annotations.

In particular the Ink annotation -- which you would like to use for all forms -- is specified as

An ink annotation (PDF 1.3) represents a freehand “scribble” composed of one or more disjoint paths.

(Section 12.5.6.13 - Ink Annotations - ISO 32000-1)

As a freehand “scribble” commonly is not considered to be a sequence of straight lines and sharp corners but instead more soft and rounded; thus, it is only natural that PDF viewers will display an ink annotation given by coordinates of the corners of a rectangle with curves rather than corners.

Of course you can use an appearance stream to provide a visualization of the appearance but that would be a small misuse of this PDF feature.

Instead I would propose you use a different kind of annotation to draw the various kinds of markup lines you need: Polyline annotations. They are specified as:

Polygon annotations (PDF 1.5) display closed polygons on the page. Such polygons may have any number of vertices connected by straight lines. Polyline annotations (PDF 1.5) are similar to polygons, except that the first and last vertex are not implicitly connected.

(Section 12.5.6.9 - Polygon and Polyline Annotations - ISO 32000-1)

iText(Sharp) provides a method for this kind of annotations, too:

/**
 * Creates a polygon or -line annotation
 * @param writer the PdfWriter
 * @param rect the annotation position
 * @param contents the textual content of the annotation
 * @param polygon if true, the we're creating a polygon annotation, if false, a polyline
 * @param vertices an array with the vertices of the polygon or -line
 * @since 5.0.2
 */
public static PdfAnnotation CreatePolygonPolyline(
    PdfWriter writer, Rectangle rect, String contents, bool polygon, PdfArray vertices)

You might eventually still have to create annotations as not all PDF viewers, in particular so called "pre-viewers", generate appearances but instead count on appearances being provided in PDFs they pre-display...

Examples

Without own appearance

using (PdfReader pdfReader = new PdfReader(inputPath))
using (PdfStamper pdfStamper = new PdfStamper(pdfReader, outputStream))
{
    PdfArray vertices = new PdfArray(new int[]{ 100, 100, 300, 300, 100, 300, 300, 100 });
    PdfAnnotation polyLine = PdfAnnotation.CreatePolygonPolyline(pdfStamper.Writer, pdfReader.GetPageSize(1),
        "", false, vertices);
    polyLine.Color = BaseColor.GREEN;
    pdfStamper.AddAnnotation(polyLine, 1);
}

adds this:

in PDF viewers supporting annotations including appearance generation according to the specification.

With own appearance

using (PdfReader pdfReader = new PdfReader(inputPath))
using (PdfStamper pdfStamper = new PdfStamper(pdfReader, outputStream))
{
    Rectangle pageSize = pdfReader.GetPageSize(1);
    PdfArray vertices = new PdfArray(new int[] { 100, 100, 300, 300, 100, 300, 300, 100 });
    PdfAnnotation polyLine = PdfAnnotation.CreatePolygonPolyline(pdfStamper.Writer, pageSize,
        "", false, vertices);
    polyLine.Color = BaseColor.GREEN;

    PdfAppearance appearance = PdfAppearance.CreateAppearance(pdfStamper.Writer, pageSize.Width, pageSize.Height);
    appearance.SetColorStroke(BaseColor.RED);
    appearance.MoveTo(100, 100);
    appearance.LineTo(300, 300);
    appearance.LineTo(100, 300);
    appearance.LineTo(300, 100);
    appearance.Stroke();

    polyLine.SetAppearance(PdfName.N, appearance);
    pdfStamper.AddAnnotation(polyLine, 1);
}

adds this

in PDF viewers supporting annotations which bring along their annotation according to the specification.

(I explicitly used a different color in my appearance to make sure the PDF viewer shows my appearance and does not create one itself.)



标签: c# itext