bookmarks (bookmarks.cpp) – this example shows how to create bookmarks and link anchors, which can help with navigation through the document.

    1 /*
    2  * (c) 2013-2017 http://www.litePDF.cz
    3  *
    4  * The example code is supplied "AS IS". It disclaims all warranties, expressed
    5  * or implied, including, without limitation, the warranties of merchantability
    6  * and of fitness for any purpose. It assumes no liability for direct, indirect,
    7  * incidental, special, exemplary, or consequential damages, which may result
    8  * from the use of the code, even if advised of the possibility of such damage.
    9  *
   10  * Permission is hereby granted to use, copy, modify, and distribute this
   11  * source code, or portions hereof, for any purpose, without fee.
   12  */ 
   13 
   14 #include <windows.h>
   15 #include <stdio.h>
   16 #include <stdlib.h>
   17 #include <string.h>
   18 #include <string>
   19 #include <vector>
   20 
   21 #include "share/litePDF.h"
   22 
   23 static const wchar_t *bookmark_tree[] = {
   24    L"1",
   25    L"\t1.1",
   26    L"\t1.2",
   27    L"/\t\t1.2.1 (italic)",
   28    L"*\t\t1.2.2 (bold)",
   29    L"\t\t1.2.3 (normal)",
   30    L"/*\t1.3 (italic and bold)",
   31    L"2",
   32    L"3",
   33    L"\t3.1",
   34    L"\t\t3.1.1",
   35    L"\t3.2",
   36    L"+\t\t3.2.1",
   37    L"\t\t3.2.2",
   38    L"\t\t3.2.3",
   39    L"\t3.3",
   40    L"\t\t3.3.1",
   41    L"4",
   42    L"\t4.1",
   43    NULL
   44 };
   45 
   46 static int drawTextLineWithFont(HDC hDC,
   47                                 HFONT hFont,
   48                                 int left,
   49                                 int top,
   50                                 const wchar_t *text)
   51 {
   52    HGDIOBJ oldFnt;
   53    int textLen = wcslen(text);
   54    SIZE sz;
   55 
   56    oldFnt = SelectObject(hDC, hFont);
   57 
   58    TextOutW(hDC, left, top, text, textLen);
   59 
   60    if (GetTextExtentPointW(hDC, text, textLen, &sz)) {
   61       top += sz.cy;
   62    } else {
   63       top += 10;
   64    }
   65 
   66    SelectObject(hDC, oldFnt);
   67 
   68    // add also extra line spacing
   69    return top + 2;
   70 }
   71 
   72 class BookmarkData
   73 {
   74  public:
   75    unsigned int level;
   76    std::wstring title;
   77    unsigned int fontStyle;
   78    int pageIndex;
   79    int left_mm;
   80    int top_mm;
   81 
   82    BookmarkData(unsigned int pLevel,
   83                 const std::wstring &pTtitle,
   84                 unsigned int pFontStyle,
   85                 int pPageIndex,
   86                 int pLeft_mm,
   87                 int pTop_mm)
   88    {
   89       level = pLevel;
   90       title = pTtitle;
   91       fontStyle = pFontStyle;
   92       pageIndex = pPageIndex;
   93       left_mm = pLeft_mm;
   94       top_mm = pTop_mm;
   95    }
   96 };
   97 
   98 int main(void)
   99 {
  100    int res = 0;
  101 
  102    using namespace std;
  103    using namespace litePDF;
  104 
  105    try {
  106       TLitePDF litePDF;
  107       COLORREF colors[4] = { RGB(128, 0, 0), RGB(0, 128, 0), RGB(0, 0, 128), RGB(0, 0, 0) };
  108       int ii, jj, sz, pageIndex = 0, colorIndex = 0, linkPageIndex = -1, linkY = -1, top = 100;
  109       const wchar_t *linkText = NULL;
  110       unsigned int bookmarkLevels[3] = { 0, 0, 0 }; // only down to level 3 is generated
  111       unsigned int lastBookmarkLevel = 0;
  112       vector<BookmarkData> bookmarks;
  113       HDC hDC = NULL;
  114 
  115       // Prepare fonts
  116       HFONT headingFonts[3] = {NULL, NULL, NULL}, normalFont;
  117       LOGFONTA lf = {0, };
  118       lf.lfHeight = -2970 / 80; // ~80 lines per page of the normal text
  119       strcpy(lf.lfFaceName, "Helvetica");
  120 
  121       normalFont = CreateFontIndirect(&lf);
  122 
  123       lf.lfUnderline = TRUE;
  124       lf.lfItalic = TRUE;
  125       lf.lfHeight *= 1.3;
  126       headingFonts[2] = CreateFontIndirect(&lf);
  127       lf.lfWeight = FW_BOLD;
  128       lf.lfItalic = FALSE;
  129       lf.lfHeight *= 1.3;
  130       headingFonts[1] = CreateFontIndirect(&lf);
  131       lf.lfHeight *= 1.3;
  132       headingFonts[0] = CreateFontIndirect(&lf);
  133 
  134       // begin memory-based PDF file
  135       litePDF.CreateMemDocument();
  136 
  137       for (ii = 0; bookmark_tree[ii]; ii++) {
  138          const wchar_t *line = bookmark_tree[ii];
  139          unsigned int fontStyle = LitePDFBookmarkFlag_None, bookmarkLevel = 0;
  140          bool rememberLink = false;
  141 
  142          jj = 0;
  143          while (line[jj]) {
  144             if (line[jj] == '/') {
  145                fontStyle |= LitePDFBookmarkFlag_Italic;
  146             } else if (line[jj] == L'*') {
  147                fontStyle |= LitePDFBookmarkFlag_Bold;
  148             } else if (line[jj] == L'+') {
  149                rememberLink = TRUE;
  150             } else if (line[jj] == L'\t') {
  151                bookmarkLevel++;
  152             } else {
  153                break;
  154             }
  155 
  156             jj++;
  157          }
  158 
  159          if (bookmarkLevel >= 3) {
  160             throw TLitePDFException(ERROR_INVALID_PARAMETER, "Bookmark data inconsistent, bookmark level too large");
  161          }
  162 
  163          if (hDC == NULL || top + 500 >= 2970) {
  164             if (hDC) {
  165                // finish current page drawing first
  166                litePDF.FinishPage(hDC);
  167                pageIndex++;
  168             }
  169 
  170             // add a new page to it, with large-enough pixel scale
  171             hDC = litePDF.AddPage(litePDF.MMToUnit(210), litePDF.MMToUnit(297), 2100, 2970, LitePDFDrawFlag_SubstituteFonts);
  172             SetTextColor(hDC, RGB(0,0,0));
  173             top = 100;
  174          }
  175 
  176          // remember the first link chapter information
  177          if (rememberLink && !linkText) {
  178             linkPageIndex = pageIndex;
  179             linkText = line + jj;
  180             linkY = top * 297 / 2970;
  181          }
  182 
  183          wstring heading = L"Heading " + wstring(line + jj);
  184 
  185          // cannot create bookmarks when drawing, thus just remember the place of it
  186          bookmarks.push_back(BookmarkData(bookmarkLevel, heading,
  187             fontStyle, pageIndex, 10, top * 297 / 2970));
  188 
  189          if (heading.find(L" (") < heading.size()) {
  190             heading.erase(heading.find(L" ("));
  191          }
  192 
  193          top = drawTextLineWithFont(hDC, headingFonts[bookmarkLevel], 120 + bookmarkLevel * 40, top, heading.c_str());
  194 
  195          // draw up to 5 lines of the text
  196          for (jj = 0; jj < 3 + (rand() % 3); jj++) {
  197             top = drawTextLineWithFont(hDC, normalFont, 100, top, L"Chapter inner text line.");
  198          }
  199 
  200          // extra space between chapters
  201          top += 100;
  202       }
  203 
  204       // finish drawing
  205       if (hDC) {
  206          litePDF.FinishPage(hDC);
  207       }
  208 
  209       lastBookmarkLevel = 0;
  210 
  211       // add bookmarks, when the drawing is finally finished and all pages are available
  212       sz = bookmarks.size();
  213       for (ii = 0; ii < sz; ii++) {
  214          const BookmarkData &bkmk = bookmarks[ii];
  215 
  216          if (bkmk.level == 0) {
  217             bookmarkLevels[0] = litePDF.CreateBookmarkRoot(bkmk.title.c_str(), bkmk.fontStyle,
  218                colors[colorIndex % 4], bkmk.pageIndex, bkmk.left_mm, bkmk.top_mm);
  219             lastBookmarkLevel = 0;
  220             colorIndex++;
  221          } else if (lastBookmarkLevel + 1 == bkmk.level) {
  222             bookmarkLevels[bkmk.level] = litePDF.CreateBookmarkChild(bookmarkLevels[lastBookmarkLevel],
  223                bkmk.title.c_str(), bkmk.fontStyle, RGB(0,0,0), bkmk.pageIndex, bkmk.left_mm, bkmk.top_mm);
  224          } else {
  225             bookmarkLevels[bkmk.level] = litePDF.CreateBookmarkSibling(bookmarkLevels[bkmk.level],
  226                bkmk.title.c_str(), bkmk.fontStyle, RGB(0,0,0), bkmk.pageIndex, bkmk.left_mm, bkmk.top_mm);
  227          }
  228 
  229          lastBookmarkLevel = bkmk.level;
  230       }
  231 
  232       if (linkPageIndex != -1) {
  233          unsigned int resourceID;
  234          wstring annotationText = L"Go to Chapter " + wstring(linkText);
  235 
  236          hDC = litePDF.AddResource(35, 6, 350, 60, LitePDFDrawFlag_SubstituteFonts);
  237          SetTextColor(hDC, RGB(0, 128, 255));
  238          drawTextLineWithFont(hDC, normalFont, 0, 0, annotationText.c_str());
  239          resourceID = litePDF.FinishResource(hDC);
  240 
  241          litePDF.CreateLinkAnnotation(0, 100, 30, 35, 6, LitePDFAnnotationFlag_None, resourceID,
  242             linkPageIndex, 10, linkY, annotationText.c_str());
  243       }
  244 
  245       litePDF.SaveToFile("bookmarks-1.pdf");
  246 
  247       // close the document
  248       litePDF.Close();
  249 
  250       // free allocated fonts
  251       DeleteObject(normalFont);
  252       DeleteObject(headingFonts[0]);
  253       DeleteObject(headingFonts[1]);
  254       DeleteObject(headingFonts[2]);
  255    } catch (TLitePDFException &ex) {
  256       fprintf (stderr, "litePDF Exception: %x: %s\n", ex.getCode(), ex.getMessage());
  257       res = 1;
  258    }
  259 
  260    return res;
  261 }
TOPlist