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 }