Create local link in rotated PdfPCell in iTextShar

2019-05-11 17:24发布

问题:

I'm trying to put a link to another page in my pdf using iTextSharp. link in rotated cell is not working. other cells work as expected:

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();

I'm using 'iTextSharp 5.5.8'. I've tried with Chunk.SetAction PdfAction.GotoLocalPage and Chunk.SetLocalGoto. Nothing works for me

Thank you.

回答1:

Actually iText(Sharp) did create a Link annotation for the anchor in the rotated cell, too, but its coordinates are completely wrong:

/Rect[-0.003 0 53.34 12]

These coordinates even are partially off-page which might explain peculiar behavior of some PDF viewers.

The cause

(I analyzed the iText Java code which has the same issues, because I'm more at home with Java. The matching iTextSharp C# code is very similar, though.)

The cause for this is that the PdfDocument code processing a PdfChunk assumes that the current coordinate system is the original user space coordinate system initialized with the MediaBox data. Thus, it uses the current coordinates without any transformation to generate local Link annotations:

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))

Unfortunately, though, cell rotation is implemented by means of a user coordinate system change, e.g. a rotation by 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))

Thus, the PdfDocument code above called for a rotated anchor chunk will position at a generally completely wrong position.

By the way, this does not only concern local Link annotations but all kinds of annotations generated for a chunk. A particularly evil case is that of a generic tag: if a page event listener reacts to a GenericTag event, it can use the coordinates for some drawing action during the call but not as coordinates relative to the MediaBox.


A fix for this most likely requires signaling any coordinate system changes to the PdfDocument and updating the code there to take this information into account when using coordinates for purposes not influenced by those transformations. In particular the GenericTag event should be extended to receive both the transformed and the original coordinates.

I would propose leaving this fix to iText development.



回答2:

It would have been useful to include the code that is not working for you, so people can focus on the specific issue(s).

I have verified creating local links in general, when both link and destination are in a table (i.e. PdfPCell). This code works as expected:

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);