itextsharp: How to find the fill color of a rectan

2019-03-03 23:30发布

问题:

I follow ItextSharp example for getting a graphical structure, to get the rectangle coordinates, using code:

class VectorGraphicsListener : IExtRenderListener
{
public void ModifyPath(PathConstructionRenderInfo renderInfo)
{
    if (renderInfo.Operation == PathConstructionRenderInfo.RECT)
    {
        float x = renderInfo.SegmentData[0];
        float y = renderInfo.SegmentData[1];
        float w = renderInfo.SegmentData[2];
        float h = renderInfo.SegmentData[3];  
...      

I tried the renderInfo.GetFillColor(); but there is no such a method.

I want to get additional rectangle properties :
like rectangle fill color,( some analog to DIV style="background: blue; border:black;" tag in html) how I get it ?

回答1:

TextRenderInfo has the method you are looking for for PathConstructionRenderInfo does not.

Where did you get the example from? You are probably using a different version than the version documentation used.

This article explains what you need to do but it provides Java source code which should be pretty easy to port to C#: http://techqa.info/programming/question/41728853/how-to-extract-the-color-of-a-rectangle-in-a-pdf,-with-itext

To summarize the relevant portions, it uses GraphicsState to get the fill color. To achieve this you will need parse all of the PDF's content with a PdfReaderContentParser passing a derived ExtRenderListener to the processContent method as follows (taken from the Java Code above):

PdfReader pdfReader = new PdfReader(resource);
ExtRenderListener extRenderListener = new ExtRenderListener();
for (int page = 1; page <= pdfReader.getNumberOfPages(); page++)
{
    PdfReaderContentParser parser = new PdfReaderContentParser(pdfReader);
    parser.processContent(page, extRenderListener);

}

The ExtRenderListener will need to override renderPath:

public override Path renderPath(PathPaintingRenderInfo renderInfo)
{
    Field gsField = PathPaintingRenderInfo.class.getDeclaredField("gs");
    gsField.setAccessible(true);
    GraphicsState graphicsState = (GraphicsState) gsField.get(renderInfo);
    if ((renderInfo.getOperation() & PathPaintingRenderInfo.FILL) != 0)
    {
        var fillColor = graphicsState.getFillColor();
        bool filledRect= false;
        for (PathConstructionRenderInfo pathConstructionRenderInfo in pathInfos) 
        {
            if(pathConstructionRenderInfo.getOperation()==PathConstructionRenderInfo.RECT)
            {
                filledRect=true;
                break;
            }
            if (filledRect && fillColor!=null)
                Console.WriteLine("{0},{1},{2}",
                fillColor.getRed(), fillColor.getGreen(), fillColor.getBlue());
        }
    }
}


回答2:

The page Alexander in his answer posted a link to is a copy of an earlier answer by me here on stack overflow.

Thus, I simply ported the code from that answer (which was using iText / Java) to iTextSharp / C#:

The custom IExtRenderListener implementation

class ExtRenderListener : IExtRenderListener
{
    public void BeginTextBlock() { }
    public void RenderText(TextRenderInfo renderInfo) { }
    public void EndTextBlock() { }
    public void RenderImage(ImageRenderInfo renderInfo) { }

    public void ModifyPath(PathConstructionRenderInfo renderInfo)
    {
        pathInfos.Add(renderInfo);
    }

    public iTextSharp.text.pdf.parser.Path RenderPath(PathPaintingRenderInfo renderInfo)
    {
        GraphicsState graphicsState = getGraphicsState(renderInfo);
        Matrix ctm = graphicsState.GetCtm();

        if ((renderInfo.Operation & PathPaintingRenderInfo.FILL) != 0)
        {
            Console.Write("FILL ({0}) ", toString(graphicsState.FillColor));
            if ((renderInfo.Operation & PathPaintingRenderInfo.STROKE) != 0)
                Console.Write("and ");
        }
        if ((renderInfo.Operation & PathPaintingRenderInfo.STROKE) != 0)
        {
            Console.Write("STROKE ({0}) ", toString(graphicsState.StrokeColor));
        }

        Console.Write("the path ");

        foreach (PathConstructionRenderInfo pathConstructionRenderInfo in pathInfos)
        {
            switch (pathConstructionRenderInfo.Operation)
            {
                case PathConstructionRenderInfo.MOVETO:
                    Console.Write("move to {0} ", toString(transform(ctm, pathConstructionRenderInfo.SegmentData)));
                    break;
                case PathConstructionRenderInfo.CLOSE:
                    Console.Write("close {0} ", toString(transform(ctm, pathConstructionRenderInfo.SegmentData)));
                    break;
                case PathConstructionRenderInfo.CURVE_123:
                    Console.Write("curve123 {0} ", toString(transform(ctm, pathConstructionRenderInfo.SegmentData)));
                    break;
                case PathConstructionRenderInfo.CURVE_13:
                    Console.Write("curve13 {0} ", toString(transform(ctm, pathConstructionRenderInfo.SegmentData)));
                    break;
                case PathConstructionRenderInfo.CURVE_23:
                    Console.Write("curve23 {0} ", toString(transform(ctm, pathConstructionRenderInfo.SegmentData)));
                    break;
                case PathConstructionRenderInfo.LINETO:
                    Console.Write("line to {0} ", toString(transform(ctm, pathConstructionRenderInfo.SegmentData)));
                    break;
                case PathConstructionRenderInfo.RECT:
                    Console.Write("rectangle {0} ", toString(transform(ctm, expandRectangleCoordinates(pathConstructionRenderInfo.SegmentData))));
                    break;
            }
        }
        Console.WriteLine();

        pathInfos.Clear();
        return null;
    }

    String toString(IList<float> coordinates)
    {
        StringBuilder result = new StringBuilder();
        result.Append("[ ");
        for (int i = 0; i < coordinates.Count; i++)
        {
            result.Append(coordinates[i]);
            result.Append(' ');
        }
        result.Append(']');
        return result.ToString();
    }

    List<float> transform(Matrix ctm, IList<float> coordinates)
    {
        List<float> result = new List<float>();
        for (int i = 0; i + 1 < coordinates.Count; i += 2)
        {
            Vector vector = new Vector(coordinates[i], coordinates[i + 1], 1);
            vector = vector.Cross(ctm);
            result.Add(vector[Vector.I1]);
            result.Add(vector[Vector.I2]);
        }
        return result;
    }

    List<float> expandRectangleCoordinates(IList<float> rectangle)
    {
        if (rectangle.Count < 4)
            return new List<float>();
        return new List<float>
        {
                rectangle[0], rectangle[1],
                rectangle[0] + rectangle[2], rectangle[1],
                rectangle[0] + rectangle[2], rectangle[1] + rectangle[3],
                rectangle[0], rectangle[1] + rectangle[3]
        };
    }

    String toString(BaseColor baseColor)
    {
        if (baseColor == null)
            return "DEFAULT";
        return String.Format("{0},{1},{2}", baseColor.R, baseColor.G, baseColor.B);
    }

    GraphicsState getGraphicsState(PathPaintingRenderInfo renderInfo)
    {
        System.Reflection.FieldInfo gsField = typeof(PathPaintingRenderInfo).GetField("gs", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
        return (GraphicsState) gsField.GetValue(renderInfo);
    }

    public void ClipPath(int rule)
    {
    }

    List<PathConstructionRenderInfo> pathInfos = new List<PathConstructionRenderInfo>();
}

The required reflection required to retrieve the GraphicsState takes place in the method getGraphicsState.

Using the ExtRenderListener class

using (var pdfReader = new PdfReader([FILE TO PARSE]))
{
    IExtRenderListener extRenderListener = new ExtRenderListener();
    // Loop through each page of the document
    for (var page = 1; page <= pdfReader.NumberOfPages; page++)
    {
        Console.Write("\nPage {0}\n====\n", page);
        PdfReaderContentParser parser = new PdfReaderContentParser(pdfReader);
        parser.ProcessContent(page, extRenderListener);
    }
}

Output

For the PDF that former question was about, this code returns

Page 1
====
STROKE (0,0,0) the path rectangle [ 88,3 693,69 227,77 693,69 227,77 788 88,3 788 ] 
STROKE (0,0,0) the path rectangle [ 227,77 693,69 367,24 693,69 367,24 788 227,77 788 ] 
STROKE (0,0,0) the path rectangle [ 367,23 693,69 506,7 693,69 506,7 788 367,23 788 ] 
FILL (255,0,0) the path rectangle [ 229,77 695,69 365,37 695,69 365,37 786,09 229,77 786,09 ] 
STROKE (DEFAULT) the path move to [ 228 810 ] line to [ 338 810 ]

(As you can observe in the coordinate outputs, my current locale uses a comma as decimal separator...)



标签: c# itext