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