1#include <Debug.h>
2#include "PDFWriter.h"
3#include "Bookmark.h"
4#include "Scanner.h"
5#include "Report.h"
6
7
8Bookmark::Definition::Definition(int level, BFont* font, bool expanded)
9	: fLevel(level)
10	, fFont(*font)
11	, fExpanded(expanded)
12{
13}
14
15
16bool Bookmark::Definition::Matches(font_family* family, font_style* style, float size) const
17{
18	font_family family0;
19	font_style  style0;
20
21	if (fFont.Size() != size) return false;
22
23	fFont.GetFamilyAndStyle(&family0, &style0);
24
25	return strcmp(family0, *family) == 0 &&
26		strcmp(style0, *style) == 0;
27}
28
29
30Bookmark::Bookmark(PDFWriter* writer)
31	: fWriter(writer)
32{
33	for (int i = 0; i < kMaxBookmarkLevels; i ++) {
34		fLevels[i] = 0;
35	}
36}
37
38
39Bookmark::Definition* Bookmark::Find(BFont* font) const
40{
41	font_family family;
42	font_style  style;
43	float       size;
44
45	font->GetFamilyAndStyle(&family, &style);
46	size = font->Size();
47
48	for (int i = 0; i < fDefinitions.CountItems(); i++) {
49		Definition* definition = fDefinitions.ItemAt(i);
50		if (definition->Matches(&family, &style, size)) {
51			return definition;
52		}
53	}
54	return NULL;
55}
56
57
58void Bookmark::AddDefinition(int level, BFont* font, bool expanded)
59{
60	ASSERT(1 <= level && level <= kMaxBookmarkLevels);
61	if (Find(font) == NULL) {
62		fDefinitions.AddItem(new Definition(level, font, expanded));
63	}
64}
65
66
67void Bookmark::AddBookmark(BPoint start, float height, const char* text, BFont* font)
68{
69	Definition* definition = Find(font);
70	if (definition != NULL) {
71		fOutlines.AddItem(new Outline(start, height, text, definition));
72	}
73}
74
75
76int Bookmark::AscendingByStart(const Outline** a, const Outline** b) {
77	return (int)((*b)->Start().y - (*a)->Start().y);
78}
79
80
81void Bookmark::CreateBookmarks() {
82	char optList[256];
83	fOutlines.SortItems(AscendingByStart);
84
85	for (int i = 0; i < fOutlines.CountItems(); i++) {
86		BString ucs2;
87
88		Outline* o = fOutlines.ItemAt(i);
89		REPORT(kInfo, fWriter->fPage, "Bookmark '%s' at level %d", o->Text(), o->Level());
90
91		sprintf(optList, "type=fixed left=%f top=%f", o->Start().x, o->Start().y + o->Height());
92		PDF_set_parameter(fWriter->fPdf, "bookmarkdest", optList);
93
94		fWriter->ToPDFUnicode(o->Text(), ucs2);
95
96		int open = o->Expanded() ? 1 : 0;
97		int bookmark = PDF_add_bookmark(fWriter->fPdf, ucs2.String(), fLevels[o->Level()-1], open);
98
99		if (bookmark < 0) bookmark = 0;
100
101		for (int i = o->Level(); i < kMaxBookmarkLevels; i ++) {
102			fLevels[i] = bookmark;
103		}
104	}
105	// reset to default
106	PDF_set_parameter(fWriter->fPdf, "bookmarkdest", "type=fitwindow");
107
108	fOutlines.MakeEmpty();
109}
110
111// Reads bookmark definitions from file
112
113/*
114File Format: Definition.
115Line comment starts with '#'.
116
117Definition = Version { Font }.
118Version    = "Bookmarks" "2.0".
119Font       = Level Family Style Size Expanded.
120Level      = int.
121Family     = String.
122Style      = String.
123Size       = float.
124Expanded   = "expanded" | "collapsed". // new in version 2.0, version 1.0 defaults to collapsed
125String     = '"' string '"'.
126*/
127
128bool Bookmark::Exists(const char* f, const char* s) const {
129	font_family family;
130	font_style  style;
131	uint32      flags;
132	int32       nFamilies;
133	int32       nStyles;
134
135	nFamilies = count_font_families();
136
137	for (int32 i = 0; i < nFamilies; i ++) {
138		if (get_font_family(i, &family, &flags) == B_OK && strcmp(f, family) == 0) {
139			nStyles = count_font_styles(family);
140			for (int32 j = 0; j < nStyles; j++) {
141				if (get_font_style(family, j, &style, &flags) == B_OK && strcmp(s, style) == 0) {
142					return true;
143				}
144			}
145		}
146	}
147	return false;
148}
149
150
151bool Bookmark::Read(const char* name) {
152	Scanner scnr(name);
153	if (scnr.InitCheck() == B_OK) {
154		BString s; float version; bool ok;
155		ok = scnr.ReadName(&s) && scnr.ReadFloat(&version);
156		if (!ok || strcmp(s.String(), "Bookmarks") != 0 || (version != 1.0 && version != 2.0) ) {
157			REPORT(kError, 0, "Bookmarks (line %d, column %d): '%s' not a bookmarks file or wrong version!", scnr.Line(), scnr.Column(), name);
158			return false;
159		}
160
161		while (!scnr.IsEOF()) {
162			float   level, size;
163			bool    expanded = false;
164			BString family, style, expand;
165			if (!(scnr.ReadFloat(&level) && level >= 1.0 && level <= 10.0)) {
166				REPORT(kError, 0, "Bookmarks (line %d, column %d): Invalid level", scnr.Line(), scnr.Column());
167				return false;
168			}
169			if (!scnr.ReadString(&family)) {
170				REPORT(kError, 0, "Bookmarks (line %d, column %d): Invalid font family", scnr.Line(), scnr.Column());
171				return false;
172			}
173			if (!scnr.ReadString(&style)) {
174				REPORT(kError, 0, "Bookmarks (line %d, column %d): Invalid font style", scnr.Line(), scnr.Column());
175				return false;
176			}
177			if (!scnr.ReadFloat(&size)) {
178				REPORT(kError, 0, "Bookmarks (line %d, column %d): Invalid font size", scnr.Line(), scnr.Column());
179				return false;
180			}
181			if (version == 2.0) {
182				if (!scnr.ReadName(&expand) || (strcmp(expand.String(), "expanded") != 0 && strcmp(expand.String(), "collapsed") != 0)) {
183					REPORT(kError, 0, "Bookmarks (line %d, column %d): Invalid expanded value", scnr.Line(), scnr.Column());
184					return false;
185				}
186				expanded = strcmp(expand.String(), "expanded") == 0;
187			}
188
189			if (Exists(family.String(), style.String())) {
190				BFont font;
191				font.SetFamilyAndStyle(family.String(), style.String());
192				font.SetSize(size);
193
194				AddDefinition((int)level, &font, expanded);
195			} else {
196				REPORT(kWarning, 0, "Bookmarks (line %d, column %d): Font %s-%s not available!", scnr.Line(), scnr.Column(), family.String(), style.String());
197			}
198
199			scnr.SkipSpaces();
200		}
201		return true;
202	} else {
203		REPORT(kError, 0, "Bookmarks: Could not open bookmarks file '%d'", name);
204	}
205	return false;
206}
207