0

I'd like to change zoom level in outlines using iText7. Note that this has probably changed compared to the way it was done in iText 5.

By trial and error I've come up with the code:

List<PdfOutline> outlines = pdfDoc.getOutlines(true).getAllChildren();

for (int i = 0; i < outlines.size(); i++) {

    PdfOutline outline = outlines.get(i);
    PdfDictionary content = outline.getContent();
    PdfDictionary pdfDictionary = (PdfDictionary) content.get(PdfName.A);
    if (pdfDictionary != null) {
        PdfArray arr = (PdfArray) pdfDictionary.get(PdfName.D);
        if (arr.size() == 5) { // for XYZ zoom type
            PdfName xyz = (PdfName) arr.get(1);
            arr.set(3, new PdfNumber(2_000));
            arr.set(4, new PdfNumber(2_000));
        }
    }  

EDIT

The problem is that the above code doesn't seem to work as the resulting pdf is saved but there are no changes in zoom level.

UPDATE I've come up with a different solution (inspired by different question at SO):

PdfNameTree destsTree = document.getCatalog().getNameTree(PdfName.Dests);
PdfOutline outline = document.getOutlines(false);
if (outline != null) {
    walkOutlines(outline, destsTree.getNames(), document);
}      

private static void walkOutlines(PdfOutline outline, Map<String, PdfObject> names,
            PdfDocument document) {

    if (outline.getDestination() != null) {

        int pageNumber = document.getPageNumber(
                (PdfDictionary) outline.getDestination().getDestinationPage(names));

        float height = document.getPage(pageNumber).getPageSize().getHeight();

        outline.setOpen(false);
        outline.addDestination(PdfExplicitRemoteGoToDestination.createXYZ(
                pageNumber, 0F, height, ZOOM_LEVEL));

    }
    for (PdfOutline child : outline.getAllChildren()) {
        walkOutlines(child, names, document);
    }
}
menteith
  • 596
  • 14
  • 51
  • 1) Please provide a [mre] or a [Short, Self Contained, Correct Example](http://www.sscce.org). 2) "The problem is that the above code doesn't seem to work." How so? Any exceptions thrown? It doesn't compile? – yur Nov 07 '19 at 09:11
  • 1
    I added explanation as to why does it mean that it fails to work. This is minimal example. I see no pointing in writing `public static void main...` etc. – menteith Nov 07 '19 at 10:19
  • Are you working in append mode? That might require marking some indirect objects as updated. Can you share your example PDF? There might be something special about your PDF that makes the change disappear. – mkl Nov 11 '19 at 14:02
  • @mkl I did but now I don't. Also I've come up with a different solution which seems to work. Care to have a look? – menteith Nov 11 '19 at 17:15
  • Can you share your example PDF? – mkl Nov 11 '19 at 19:01
  • @mkl Yes, I can but I don't want make it public. How can I then share it with you? – menteith Nov 11 '19 at 19:10
  • There is an email address in my profile here. Just send it there. – mkl Nov 11 '19 at 19:12

2 Answers2

0

So something like this?

    PdfDocument pdfDoc = new PdfDocument(new PdfReader(SRC), new PdfWriter(DEST));                                                   
    pdfDoc.getCatalog().setOpenAction(PdfExplicitDestination.createXYZ(pdfDoc.getPage(1), 0,                                                       pdfDoc.getPage(1).getPageSize().getHeight(), 0.75f)); 
    pdfDoc.close();

https://itextpdf.com/en/resources/faq/technical-support/itext-7/how-can-i-set-zoom-level-pdf-using-itext-7

  • This does not change zoom level for outlines. `You can use setOpenActionto open a document with a default display magnification level. ` – menteith Nov 07 '19 at 17:34
0

Your original code

The problem is that the above code doesn't seem to work as the resulting pdf is saved but there are no changes in zoom level.

Trying your original code I cannot confirm this, the zoom level and the y coordinate (why do you change that, too?) of the title page outline are changed!

The other outlines are not changed, though, for two reasons:

  • Your original code only iterates over the top level outlines (the immediate catalog outlines children), so only the top level outline entries can be changed at all.

  • Your original code assumes the outlines to have an GoTo Action (A) wrapping an explicit XYZ Destination (D mapping to a 5 element array).

    This in case of your document only is true for the title page, all other outlines immediately contain a non-explicit, named Destination (Dest mapping to a name).

Your alternate code

Your alternate code does solve the issues, in particular

  • it recursively also visits all ancestors of the top level outlines, so all outline entries can be changed; and
  • it uses iText classes to identify and evaluate all kinds of destinations, be they wrapped in an action or not, be they explicit or named, ...

In Adobe Reader the result appears to work as desired, but looking into it one sees that there are issues:

  • The title page outline (which was the only one of your outlines with an Action entry) now has both the old Action entry and a new Destination entry. This strictly speaking is forbidden by the specification; thus, you should remove existing Action entries.
  • The created destination arrays contain the destination page (the first array entry) as an integer page number. This is only allowed for destinations in Remote GoTo Actions. As your new destinations are clearly non-Remote and also non-Actions, this only works because Adobe Reader is very lax. Instead of the page number you should use a page object, and you shouldn't use PdfExplicitRemoteGoToDestination to start with.

Improving your alternate code

To fix the issues above, change

outline.addDestination(PdfExplicitRemoteGoToDestination.createXYZ(
        pageNumber, 0F, height, ZOOM_LEVEL));

to

outline.getContent().remove(PdfName.A);
outline.addDestination(PdfExplicitDestination.createXYZ(
        document.GetPage(pageNumber), 0F, height, ZOOM_LEVEL));

Strictly speaking your code can be improved further.

  • You use the PageSize but for the visible page area you should use the CropBox.
  • And you use 0F, height as coordinates of the top left corner of the target view. This assumes the origin of the coordinate system to be the lower left corner of the page You should use box.getLeft(), box.getTop() instead with box being the crop box of the page in question.

In case of your example document, though, media box and crop box coincide, and the origin indeed is the lower left corner. Thus, for that document you don't need that improvement.

Community
  • 1
  • 1
mkl
  • 90,588
  • 15
  • 125
  • 265