2

I want to add a bookmark pointing to a specific page within a document. Adding bookmarks from other PDF files I'm merging with code similar to that below works fine, but when I copied it to add custom bookmarks to non-bookmarked items it fails:

var bookmarks = new ArrayList();
var writer = new PdfCopy(document, memorystream);
// ...
var uni = new Hashtable();
uni.Add("Action", "GoTo");
uni.Add("Title", "Awesome Unicorn pic");
uni.Add("Page", "8 XYZ 0 0 0");
bookmarks.Add(uni);
// ...
writer.Outlines = bookmarks;

But apparently ("Page", "8 XYZ 0 0 0") does not reference Page 8 but rather Section 8 or something like that. Is there an alternative Action I could use to point to an arbitrary page? Or some other method?

Joel Peltonen
  • 13,025
  • 6
  • 64
  • 100

1 Answers1

3

Looks like the PDF coordinate system messed with my feeble human brain. Turns out that ("Page", "8 XYZ 0 0 0"); actually does reference page 8, but "XYZ 0 0 0" does not reference the top left point on a page, but rather the bottom left point. So when clicked, a bookmark like this unexpectedly takes you to page two. Awesome.

The code below works as expected, because it gets the height of the first page and uses that to link to the top of the page. The code is gathered from different places around my source, so it's not very "together" but still, it works.

var bookmarks = new ArrayList();
var rdr = new PdfReader(first);
var doc = new Document(rdr.GetPageSizeWithRotation(1));
var wri = new PdfCopy(doc, memorystream);
var temp = wri.GetImportedPage(rdr, 1); // get 1st page
var h = temp.Height; // get height of 1st page

// Add first item to bookmarks.
var test = new Hashtable();
test.Add("Action", "GoTo");
test.Add("Title", "Page1 0 H 0");
test.Add("Page", "1 XYZ 0 "+h+" 0"); // use height of 1st page
bookmarks.Add(test);

// Do your worst and afterwards set the bookmarks to Outline. So yeah.
wri.Outlines = bookmarks;
Joel Peltonen
  • 13,025
  • 6
  • 64
  • 100
  • 1
    *"XYZ 0 0 0" does not reference the top left point on a page, but rather the bottom left point.* - this is fairly common in PDFs. Actually you choose the coordinate origin by means of your mediabox and cropbox entries for the page in question, and fairly often these boxes start out at 0,0 and from there go right and up. – mkl Oct 15 '13 at 08:15
  • Thinking back this seems logical in a traditional mathematical coordinate system. I am just so used to working with systems like Java 2D and HTML/JS/CSS where the origo (0,0) is at the top left that I got confused. – Joel Peltonen Oct 15 '13 at 08:22
  • This apparently is also the reason why merging PDFs always break internal links completely; because they don't point to a named position but rather to a point on a page. Sigh. – Joel Peltonen Oct 21 '13 at 07:22
  • 1
    @Nenotlep -- am I missing something? Why not just **.Add("Page", "1")**? Why explicitly specify the "top left" of the page? Just wondering – BeemerGuy Apr 07 '18 at 00:55
  • @BeemerGuy honestly, I can't remember. It could be that I never tried that. I am very lucky in that I don't have that code anymore and I don't even work in the same company so I won't test it. BUT if I was to guess, `Add("Page", "1")` translates to `Add("Page", "1 XYZ 0 0 0");` And that is wrong, because it actually takes you to page 2, because `0 0 0` references the very bottom of page 1 and so it scrolls so that you actually see page 2. – Joel Peltonen Apr 11 '18 at 06:54