在 iTextSharp 的旋转 PdfPCell 中创建本地 link

Create local link in rotated PdfPCell in iTextSharp

我正在尝试使用 iTextSharp 将 link 放到我的 pdf 中的另一个页面。 link 在旋转的单元格中不起作用。其他单元按预期工作:

FileStream fs = new FileStream("TestPDF.pdf", FileMode.Create, FileAccess.Write, FileShare.None);
Document doc = new Document();
PdfWriter writer = PdfWriter.GetInstance(doc, fs);
doc.Open();

PdfPTable linkTable = new PdfPTable(2);
PdfPCell linkCell = new PdfPCell();

linkCell.HorizontalAlignment = Element.ALIGN_CENTER;
linkCell.Rotation = 90;
linkCell.FixedHeight = 70;

Anchor linkAnchor = new Anchor("Click here");
linkAnchor.Reference = "#target";
Paragraph linkPara = new Paragraph();
linkPara.Add(linkAnchor);
linkCell.AddElement(linkPara);
linkTable.AddCell(linkCell);

PdfPCell linkCell2 = new PdfPCell();
Anchor linkAnchor2 = new Anchor("Click here 2");
linkAnchor2.Reference = "#target";
Paragraph linkPara2 = new Paragraph();
linkPara2.Add(linkAnchor2);
linkCell2.AddElement(linkPara2);
linkTable.AddCell(linkCell2);

linkTable.AddCell(new PdfPCell(new Phrase("cell 3")));
linkTable.AddCell(new PdfPCell(new Phrase("cell 4")));
doc.Add(linkTable);

doc.NewPage();

Anchor destAnchor = new Anchor("top");
destAnchor.Name = "target";
PdfPTable destTable = new PdfPTable(1);
PdfPCell destCell = new PdfPCell();
Paragraph destPara = new Paragraph();
destPara.Add(destAnchor);
destCell.AddElement(destPara);
destTable.AddCell(destCell);
destTable.AddCell(new PdfPCell(new Phrase("cell 2")));
destTable.AddCell(new PdfPCell(new Phrase("cell 3")));
destTable.AddCell(new PdfPCell(new Phrase("cell 4")));
doc.Add(destTable);

doc.Close();

我正在使用 'iTextSharp 5.5.8'。我试过 Chunk.SetAction PdfAction.GotoLocalPage 和 Chunk.SetLocalGoto。什么都不适合我

谢谢。

包含对您不起作用的代码会很有用,这样人们就可以专注于特定问题。

当 link 和目的地都在 table 中(即 PdfPCell)时,我已经验证了通常创建本地 links。此代码按预期工作:

PdfPTable linkTable = new PdfPTable(2);
PdfPCell linkCell = new PdfPCell();
Anchor linkAnchor = new Anchor("Click here to go to top of next page.");
linkAnchor.Reference = "#target";
Paragraph linkPara = new Paragraph();
linkPara.Add(linkAnchor);
linkCell.AddElement(linkPara);
linkTable.AddCell(linkCell);
linkTable.AddCell(new PdfPCell(new Phrase("cell 2")));
linkTable.AddCell(new PdfPCell(new Phrase("cell 3")));
linkTable.AddCell(new PdfPCell(new Phrase("cell 4")));
doc.Add(linkTable);

doc.NewPage();

Anchor destAnchor = new Anchor("top");
destAnchor.Name = "target";
PdfPTable destTable = new PdfPTable(1);
PdfPCell destCell = new PdfPCell();
Paragraph destPara = new Paragraph();
destPara.Add(destAnchor);
destCell.AddElement(destPara);
destTable.AddCell(destCell);
destTable.AddCell(new PdfPCell(new Phrase("cell 2")));
destTable.AddCell(new PdfPCell(new Phrase("cell 3")));
destTable.AddCell(new PdfPCell(new Phrase("cell 4")));
doc.Add(destTable);

实际上 iText(Sharp) 也确实为旋转单元格中的锚点创建了一个 Link 注释,但是它的坐标完全错误:

/Rect[-0.003 0 53.34 12]

这些坐标甚至部分位于页面外,这可能解释了某些 PDF 查看器的特殊行为。

原因

(我分析了具有相同问题的 iText Java 代码,因为我更熟悉 Java。不过,匹配的 iTextSharp C# 代码非常相似。)

原因是 PdfDocument 代码处理 PdfChunk 假定当前坐标系是使用 初始化的原始用户 space 坐标系媒体框 数据。因此,它使用当前坐标而不进行任何变换来生成本地 Link 注释:

float xMarker = text.getXTLM();
float baseXMarker = xMarker;
float yMarker = text.getYTLM();
...
if (chunk.isAttribute(Chunk.LOCALGOTO)) {
    float subtract = lastBaseFactor;
    if (nextChunk != null && nextChunk.isAttribute(Chunk.LOCALGOTO))
        subtract = 0;
    if (nextChunk == null)
        subtract += hangingCorrection;
    localGoto((String)chunk.getAttribute(Chunk.LOCALGOTO), xMarker, yMarker, xMarker + width - subtract, yMarker + fontSize);
}

(PdfDocument.writeLineToContent(PdfLine, PdfContentByte, PdfContentByte, Object[], float))

不幸的是,单元格旋转是通过用户坐标系更改实现的,例如旋转 90°:

ct.setSimpleColumn(-0.003f, -0.001f, netWidth + 0.003f, calcHeight);
...
pivotY = cell.getTop() + yPos - currentMaxHeight + cell.getEffectivePaddingBottom();
switch (cell.getVerticalAlignment()) {
    case Element.ALIGN_BOTTOM:
        pivotX = cell.getLeft() + xPos + cell.getWidth() - cell.getEffectivePaddingRight();
        break;
    case Element.ALIGN_MIDDLE:
        pivotX = cell.getLeft() + xPos + (cell.getWidth() + cell.getEffectivePaddingLeft() - cell.getEffectivePaddingRight() + calcHeight) / 2;
        break;
    default: //top
        pivotX = cell.getLeft() + xPos + cell.getEffectivePaddingLeft() + calcHeight;
        break;
}
saveAndRotateCanvases(canvases, 0, 1, -1, 0, pivotX, pivotY);

(PdfPRow.writeCells(int, int, float, float, PdfContentByte[], boolean))

因此,上面调用旋转锚块的 PdfDocument 代码将定位在通常完全错误的位置。

顺便说一句,这不仅涉及本地 Link 注释,还涉及为块生成的各种注释。一个特别糟糕的情况是通用标记:如果页面事件侦听器对 GenericTag 事件做出反应,它可以在调用期间使用坐标进行某些绘图操作,但不能作为相对于 的坐标媒体框.


对此的修复很可能需要向 PdfDocument 发出任何坐标系更改的信号,并更新那里的代码以在将坐标用于不受这些转换影响的目的时考虑此信息。特别是应该扩展 GenericTag 事件以接收转换后的坐标和原始坐标。

我建议将此修复留给 iText 开发。