【问题标题】:How to extract the color of a rectangle in a PDF, with iText如何使用 iText 提取 PDF 中矩形的颜色
【发布时间】:2017-01-18 20:28:21
【问题描述】:

我正在尝试使用 iText 提取 PDF 中矩形的颜色。 以下是 PDF 页面的全部内容:

这是使用 iText 提取的页面内容:

q
BT
36 806 Td
0 -18 Td
/F1 12 Tf
(Option 1:)Tj
0 0 Td
0 -94.31 Td
ET
Q
q
Q
q
2 J
0 G
0.5 w
88.3 693.69 139.47 94.31 re
S
0.5 w
227.77 693.69 139.47 94.31 re
S
0.5 w
367.23 693.69 139.47 94.31 re
S
Q
BT
1 0 0 1 90.3 774 Tm
/F1 12 Tf
(A rectangle:)Tj
ET
q 1.13 0 0 1.13 229.77 695.69 cm /Xf1 Do Q
BT
1 0 0 1 369.23 774 Tm
/F1 12 Tf
(The rectangle is scaled)Tj
1 0 0 1 369.23 762 Tm
(to fit inside the cell, you)Tj
1 0 0 1 369.23 750 Tm
(see a padding.)Tj
ET
228 810 m
338 810 l
S

但是,我无法从该代码中提取一些内容,我说的是红色,如果我生成相同的 PDF 但使用另一种颜色而不是红色,页面内容没有任何变化(代码如上所示)。

所以,我的问题是,如何使用 iText 库中的一些方法或属性来提取该颜色。

我正在使用 iText 5.5.9,这是我用来生成 PDF 示例的代码示例:

感谢您提供的任何帮助!


这是我用来生成 PDF 的代码:

String dest = "C:\\TestCreation.pdf";
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(dest));
document.open();

document.add(new Paragraph("Option 1:"));
PdfPTable table = new PdfPTable(3);
table.addCell("A rectangle:");
PdfTemplate template = writer.getDirectContent().createTemplate(120, 80);
template.setColorFill(BaseColor.RED);
template.rectangle(0, 0, 120, 80);
template.fill();
writer.releaseTemplate(template);
table.addCell(Image.getInstance(template));
table.addCell("The rectangle is scaled to fit inside the cell, you see a padding.");
document.add(table);

PdfContentByte cb = writer.getDirectContent();
cb.moveTo(228, 810);
cb.lineTo(338, 810);
cb.stroke();
document.close();

您可以在这里看到 PDF 文件: PDF example

这是我用来获取页面内容的行代码: String pageContent = new String(reader.getPageContent(1));

我一直在查看所有阅读器对象,我能够找到矩形,但找不到它的颜色:

【问题讨论】:

  • (与我下面的评论相关)如果您想要更详细的答案,您能否提供:(1)您正在使用的 PDF 的在线版本(2)用于生成内容的代码溪流。谢谢
  • 谢谢 Alex,我刚刚添加了示例代码和文件。
  • ^^ 对于代码,您能否提供用于生成页面内容提取的代码?对我来说,问题来自于数据的提取方式;)
  • 我能够定位矩形的框尺寸,但不能定位它的颜色,并且我已经探索了所有阅读器对象。我猜可能颜色不是 RGB 格式
  • 你可以试试 getPageResources 吗?

标签: java pdf itext


【解决方案1】:

要查找矩形的颜色,您可能需要浏览 PDF 流的 /Annots 部分。在这里,您只是探索 /Contents,其中不包括 Rect 实体的颜色等信息。

我希望它会有所帮助:)

【讨论】:

  • 事实证明,它不是注释,而是从 Contents 引用的表单 XObject。
【解决方案2】:

您的代码显示了它,这就是您创建矩形并添加它的方式:

PdfTemplate template = writer.getDirectContent().createTemplate(120, 80);
template.setColorFill(BaseColor.RED);
template.rectangle(0, 0, 120, 80);
template.fill();
writer.releaseTemplate(template);
table.addCell(Image.getInstance(template));

iText PdfTemplate 生成 PDF 表单 XObject。表单 XObject 又是一个 PDF 内容流,它是任何图形对象序列(包括路径对象、文本对象和采样图像)的自包含描述(ISO 32000 的第 8.10.1 节-1),即一个单独的绘图指令流,其内容可以从任何其他内容流中引用。

对于您的页面内容流,这是包含表单 XObject 的行:

q 1.13 0 0 1.13 229.77 695.69 cm /Xf1 Do Q

(将变换矩阵操作拉伸1.13并移动一点,然后绘制XObject Xf1,然后重置变换矩阵。)

那个XObject Xf1的内容流是这样的:

1 0 0 rg
0 0 120 80 re
f

即它将非描边颜色设置为 RGB 红色,在原点定义一个 120x80 的矩形,然后填充它。


这是我用来获取页面内容的行代码:

String pageContent = new String(reader.getPageContent(1));

该行不足以获取所有内容详细信息:

  1. 它只返回立即 页面内容,而不返回来自立即内容中使用的表单XObjects 和模式的详细说明。经常会发现其直接页面内容仅引用一个或多个表单 XObjects 的 PDF。

  2. 尽管看起来页面内容是二进制的,而不是文本的。一旦使用了具有非标准编码的字体,PDF 字符串内容在您的 Java 字符串中就毫无意义,或者(取决于您的标准编码)甚至被破坏。

应该使用 iText 解析器框架,例如像这样:

ExtRenderListener extRenderListener = new ExtRenderListener()
{
    @Override
    public void beginTextBlock()                        {   }
    @Override
    public void renderText(TextRenderInfo renderInfo)   {   }
    @Override
    public void endTextBlock()                          {   }
    @Override
    public void renderImage(ImageRenderInfo renderInfo) {   }

    @Override
    public void modifyPath(PathConstructionRenderInfo renderInfo)
    {
        pathInfos.add(renderInfo);
    }

    @Override
    public Path renderPath(PathPaintingRenderInfo renderInfo)
    {
        GraphicsState graphicsState;
        try
        {
            graphicsState = getGraphicsState(renderInfo);
        }
        catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e)
        {
            e.printStackTrace();
            return null;
        }

        Matrix ctm = graphicsState.getCtm();

        if ((renderInfo.getOperation() & PathPaintingRenderInfo.FILL) != 0)
        {
            System.out.printf("FILL (%s) ", toString(graphicsState.getFillColor()));
            if ((renderInfo.getOperation() & PathPaintingRenderInfo.STROKE) != 0)
                System.out.print("and ");
        }
        if ((renderInfo.getOperation() & PathPaintingRenderInfo.STROKE) != 0)
        {
            System.out.printf("STROKE (%s) ", toString(graphicsState.getStrokeColor()));
        }

        System.out.print("the path ");

        for (PathConstructionRenderInfo pathConstructionRenderInfo : pathInfos)
        {
            switch (pathConstructionRenderInfo.getOperation())
            {
            case PathConstructionRenderInfo.MOVETO:
                System.out.printf("move to %s ", transform(ctm, pathConstructionRenderInfo.getSegmentData()));
                break;
            case PathConstructionRenderInfo.CLOSE:
                System.out.printf("close %s ", transform(ctm, pathConstructionRenderInfo.getSegmentData()));
                break;
            case PathConstructionRenderInfo.CURVE_123:
                System.out.printf("curve123 %s ", transform(ctm, pathConstructionRenderInfo.getSegmentData()));
                break;
            case PathConstructionRenderInfo.CURVE_13:
                System.out.printf("curve13 %s ", transform(ctm, pathConstructionRenderInfo.getSegmentData()));
                break;
            case PathConstructionRenderInfo.CURVE_23:
                System.out.printf("curve23 %s ", transform(ctm, pathConstructionRenderInfo.getSegmentData()));
                break;
            case PathConstructionRenderInfo.LINETO:
                System.out.printf("line to %s ", transform(ctm, pathConstructionRenderInfo.getSegmentData()));
                break;
            case PathConstructionRenderInfo.RECT:
                System.out.printf("rectangle %s ", transform(ctm, expandRectangleCoordinates(pathConstructionRenderInfo.getSegmentData())));
                break;
            }
        }
        System.out.println();

        pathInfos.clear();
        return null;
    }

    @Override
    public void clipPath(int rule)
    {
    }

    List<Float> transform(Matrix ctm, List<Float> coordinates)
    {
        List<Float> result = new ArrayList<>();
        for (int i = 0; i + 1 < coordinates.size(); i += 2)
        {
            Vector vector = new Vector(coordinates.get(i), coordinates.get(i + 1), 1);
            vector = vector.cross(ctm);
            result.add(vector.get(Vector.I1));
            result.add(vector.get(Vector.I2));
        }
        return result;
    }

    List<Float> expandRectangleCoordinates(List<Float> rectangle)
    {
        if (rectangle.size() < 4)
            return Collections.emptyList();
        return Arrays.asList(
                rectangle.get(0), rectangle.get(1),
                rectangle.get(0) + rectangle.get(2), rectangle.get(1),
                rectangle.get(0) + rectangle.get(2), rectangle.get(1) + rectangle.get(3),
                rectangle.get(0), rectangle.get(1) + rectangle.get(3)
                );
    }

    String toString(BaseColor baseColor)
    {
        if (baseColor == null)
            return "DEFAULT";
        return String.format("%s,%s,%s", baseColor.getRed(), baseColor.getGreen(), baseColor.getBlue());
    }

    GraphicsState getGraphicsState(PathPaintingRenderInfo renderInfo) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException
    {
        Field gsField = PathPaintingRenderInfo.class.getDeclaredField("gs");
        gsField.setAccessible(true);
        return (GraphicsState) gsField.get(renderInfo);
    }
    
    final List<PathConstructionRenderInfo> pathInfos = new ArrayList<>();
};

try (   InputStream resource = [RETRIEVE FILE TO PARSE AS INPUT STREAM])
{
    PdfReader pdfReader = new PdfReader(resource);

    for (int page = 1; page <= pdfReader.getNumberOfPages(); page++)
    {
        System.out.printf("\nPage %s\n====\n", page);

        PdfReaderContentParser parser = new PdfReaderContentParser(pdfReader);
        parser.processContent(page, extRenderListener);

    }
}

(ExtractPaths测试方法testExtractFromTestCreation)

对于您的示例文件,这会导致输出

Page 1
====
STROKE (0,0,0) the path rectangle [88.3, 693.69, 227.77, 693.69, 227.77, 788.0, 88.3, 788.0] 
STROKE (0,0,0) the path rectangle [227.77, 693.69, 367.24, 693.69, 367.24, 788.0, 227.77, 788.0] 
STROKE (0,0,0) the path rectangle [367.23, 693.69, 506.7, 693.69, 506.7, 788.0, 367.23, 788.0] 
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.0, 810.0] line to [338.0, 810.0] 

iText 将颜色值表示为字节 (0-255),而不是 PDF 使用的单位范围 (0.0 - 1.0)。因此,您会看到“(255,0,0)”,其中 PDF 选择了“1 0 0 rg”。

【讨论】:

  • 谢谢mkl!,但是我怎么能得到rg代码“1 0 0”,因为我在页面内容或阅读器对象树中看不到它,提前谢谢!
  • @rochastuff 我添加了一些代码来说明如何访问信息。
  • 顺便说一句,我打印的颜色假设它是 RGB(或灰度)。一般来说,必须查看实际的颜色类别并输出相应的字段。
  • 它就像一个冠军,感谢您的所有帮助@mkl!
  • 您可以找到代码in this answer的iTextSharp / C#端口。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-21
  • 2013-11-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多