您的代码显示了它,这就是您创建矩形并添加它的方式:
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));
该行不足以获取所有内容详细信息:
-
它只返回立即 页面内容,而不返回来自立即内容中使用的表单XObjects 和模式的详细说明。经常会发现其直接页面内容仅引用一个或多个表单 XObjects 的 PDF。
-
尽管看起来页面内容是二进制的,而不是文本的。一旦使用了具有非标准编码的字体,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”。