source: wkhtmltox/trunk/fuentes/src/lib/outline.cc @ 51

Last change on this file since 51 was 51, checked in by mabarracus, 4 years ago

wip

File size: 10.6 KB
Line 
1// -*- mode: c++; tab-width: 4; indent-tabs-mode: t; eval: (progn (c-set-style "stroustrup") (c-set-offset 'innamespace 0)); -*-
2// vi:set ts=4 sts=4 sw=4 noet :
3//
4// Copyright 2010 wkhtmltopdf authors
5//
6// This file is part of wkhtmltopdf.
7//
8// wkhtmltopdf is free software: you can redistribute it and/or modify
9// it under the terms of the GNU Lesser General Public License as published by
10// the Free Software Foundation, either version 3 of the License, or
11// (at your option) any later version.
12//
13// wkhtmltopdf is distributed in the hope that it will be useful,
14// but WITHOUT ANY WARRANTY; without even the implied warranty of
15// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16// GNU General Public License for more details.
17//
18// You should have received a copy of the GNU Lesser General Public License
19// along with wkhtmltopdf.  If not, see <http://www.gnu.org/licenses/>.
20
21
22#include "outline_p.hh"
23#include <fstream>
24#include <iostream>
25using namespace std;
26#ifdef __EXTENSIVE_WKHTMLTOPDF_QT_HACK__
27
28namespace wkhtmltopdf {
29/*!
30  \file outline_p.hh
31  \brief Defines the classes OutlinePrivate and OutlineItem
32*/
33
34/*!
35  \class OutlineItem
36  \brief Class describing an item in the outline
37*/
38
39OutlineItem::OutlineItem():
40        parent(NULL), page(-1), display(true), forwardLinks(false), backLinks(false) {}
41
42/*!
43  \brief Recursivily delete the subtree
44*/
45OutlineItem::~OutlineItem() {
46        foreach (OutlineItem * i, children)
47                delete i;
48}
49
50void OutlineItem::fillAnchors(const OutlineItem * other,
51                                                          int & anchorCounter,
52                                                          QVector<QPair<QWebElement, QString> > & local,
53                                                          QHash<QString, QWebElement> & anchors) {
54        if (!other ||
55                other->children.size() != children.size() ||
56                other->document != document ||
57                other->value != value ||
58                other->display != display) other=0;
59
60        if (other) {
61                anchor = other->anchor;
62                tocAnchor = other->anchor;
63        } else {
64                anchor = QString("__WKANCHOR_")+QString::number(anchorCounter++,36);
65                tocAnchor = QString("__WKANCHOR_")+QString::number(anchorCounter++,36);
66        }
67
68        if (forwardLinks)
69                anchors[anchor] = element;
70        if (backLinks)
71                local.push_back( QPair<QWebElement, QString>(element, tocAnchor) );
72
73        for (int i=0; i < children.size(); ++i)
74                children[i]->fillAnchors(other?other->children[i]:0, anchorCounter, local, anchors);
75}
76
77bool OutlineItem::differentFrom(const OutlineItem * other) const {
78        if (other->children.size() != children.size() ||
79                other->page != page ||
80                other->document != document ||
81                other->value != value ||
82                other->display != display) return true;
83
84        for (int i=0; i < children.size(); ++i)
85                if (children[i]->differentFrom(other->children[i]))
86                        return true;
87        return false;
88}
89
90
91/*!
92  \class OutlinePrivate
93  \brief Class providing implementation details of Outline
94*/
95
96OutlinePrivate::OutlinePrivate(const settings::PdfGlobal & s):
97        settings(s), pageCount(0), anchorCounter(0) {
98}
99
100OutlinePrivate::~OutlinePrivate() {
101        foreach (OutlineItem * i, documentOutlines)
102                delete i;
103}
104
105void OutlinePrivate::fillChildAnchors(OutlineItem * item, QHash<QString, QWebElement> & anchors) {
106        foreach (OutlineItem * i, item->children) {
107                if (i->anchor.isEmpty()) continue;
108                anchors[i->anchor] = i->element;
109                fillChildAnchors(i, anchors);
110        }
111}
112
113void OutlinePrivate::outlineChildren(OutlineItem * item, QPrinter * printer, int level) {
114        if (level + 1 > settings.outlineDepth) return;
115        foreach (OutlineItem * i, item->children) {
116                printer->beginSectionOutline(i->value, i->anchor);
117                outlineChildren(i, printer, level+1);
118                printer->endSectionOutline();
119        }
120}
121
122QString escape(QString str) {
123        return str.replace('&',"&amp;")
124                .replace('<',"&lt;")
125                .replace('>',"&gt;")
126                .replace('"',"&quot;")
127                .replace('\'',"&apos;");
128}
129
130void OutlinePrivate::dumpChildren(QTextStream & stream, const QList<OutlineItem *> & items, int level) const {
131        foreach (OutlineItem * item, items) {
132                for (int i=0; i < level; ++i) stream << "  ";
133                stream << "<item title=\"" << escape(item->value) << "\" page=\"" << (item->page + prefixSum[item->document]+ settings.pageOffset) << "\" link=\"" << escape(item->anchor) << "\" backLink=\"" << escape(item->tocAnchor) << "\"";
134                if (item->children.empty())
135                        stream << "/>" << endl;
136                else {
137                        stream << ">" << endl;
138                        dumpChildren(stream, item->children, level+1);
139                        for (int i=0; i < level; ++i) stream << "  ";
140                        stream << "</item>" << endl;
141                }
142        }
143}
144
145void OutlinePrivate::buildPrefixSum() {
146        prefixSum.clear();
147        prefixSum.push_back(0);
148        foreach (int x, documentPages)
149                prefixSum.push_back( prefixSum.back() + x);
150}
151
152void Outline::dump(QTextStream & stream) const {
153        d->buildPrefixSum();
154        stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl;
155        stream << "<outline xmlns=\"http://wkhtmltopdf.org/outline\">" << endl;
156        d->dumpChildren(stream, d->documentOutlines, 1);
157        stream << "</outline>" << endl;
158}
159
160/*!
161  \file outline.hh
162  \brief Defines the Outline class
163*/
164
165/*!
166  \class Outline
167  \brief Class responsible for building and keeping an outline of a document.
168*/
169
170/*!
171  \brief Construct a new outline class
172  \param settings The settings to use
173*/
174Outline::Outline(const settings::PdfGlobal & settings): d(new OutlinePrivate(settings)) {}
175Outline::~Outline() {delete d;}
176
177
178/*!
179  \brief Replace a webpage in the outline
180  \param document The number of the webpage
181  \param name The name of the webpage
182  \param wp A webprinter for the page
183  \param frame The frame containing the webpage
184*/
185bool Outline::replaceWebPage(int document,
186                                                         const QString & name,
187                                                         QWebPrinter & wp,
188                                                         QWebFrame * frame,
189                                                         const settings::PdfObject & ps,
190                                                         QVector<QPair<QWebElement, QString> > & local,
191                                                         QHash<QString, QWebElement> & anchors) {
192        QMap< QPair<int, QPair<qreal,qreal> >, QWebElement> headings;
193        foreach (const QWebElement & e, frame->findAllElements("h1,h2,h3,h4,h5,h6,h7,h8,h9")) {
194                QPair<int, QRectF> location = wp.elementLocation(e);
195                headings[ qMakePair(location.first, qMakePair(location.second.y(), location.second.x()) ) ] = e;
196        }
197
198        //This heuristic is a little strange, it tries to create a real tree,
199        //even though someone puts a h5 below a h1 or stuff like that
200        //The way this is handled is having a level stack, indicating what h-tags
201        //a level level in the tree currently represents
202        QVector<uint> levelStack;
203        levelStack.push_back(0);
204        OutlineItem * root = new OutlineItem();
205        root->page = 0;
206        root->document = document;
207        root->value = name;
208        root->display = true;
209
210        OutlineItem * old = root;
211        for (QMap< QPair<int, QPair<qreal,qreal> >, QWebElement>::iterator i = headings.begin();
212                i != headings.end(); ++i) {
213                const QWebElement & element = i.value();
214                uint level = element.tagName().mid(1).toInt();
215                QString value = element.toPlainText().replace("\n", " ").trimmed();
216                if (i.key().first == -1 || value == "") continue;
217                OutlineItem * item = new OutlineItem();
218                item->page = i.key().first;
219                item->document = document;
220                item->value = value;
221                item->element = element;
222                item->forwardLinks = ps.toc.forwardLinks;
223                item->backLinks = ps.toc.backLinks;
224
225                while (levelStack.back() >= level) {
226                        old = old->parent;
227                        levelStack.pop_back();
228                }
229                item->parent = old;
230                old->children.push_back(item);
231                old = item;
232                levelStack.push_back(level);
233        }
234
235        root->fillAnchors(d->documentOutlines[document], d->anchorCounter, local, anchors);
236        bool changed=d->documentOutlines[document]->differentFrom(root);
237        delete d->documentOutlines[document];
238        d->documentOutlines[document] = root;
239
240        if (d->documentPages[document] != wp.pageCount()) {
241                d->pageCount -= d->documentPages[document];
242                d->documentPages[document] = wp.pageCount();
243                d->pageCount += d->documentPages[document];
244                changed=true;
245        }
246        return changed;
247}
248
249/*!
250  \brief Add a new webpage to the outline
251  \param name The name of the webpage
252  \param wp A webprinter for the page
253  \param frame The frame containing the webpage
254*/
255void Outline::addWebPage(const QString & name, QWebPrinter & wp, QWebFrame * frame, const settings::PdfObject & ps,
256                                                 QVector<QPair<QWebElement, QString> > & local,
257                                                 QHash<QString, QWebElement> & anchors) {
258        Q_UNUSED(name);
259        addEmptyWebPage();
260        replaceWebPage(d->documentOutlines.size()-1, name, wp, frame, ps, local, anchors);
261}
262
263
264void Outline::addEmptyWebPage() {
265        OutlineItem * root = new OutlineItem();
266        root->page = 0;
267        root->document = d->documentPages.size();
268        root->value = "";
269        root->display = true;
270        d->documentOutlines.push_back(root);
271        d->pageCount += 1;
272        d->documentPages.push_back(1);
273}
274
275void OutlinePrivate::buildHFCache(OutlineItem * i, int level) {
276        buildPrefixSum();
277        if (level >= hfCache.size()) return;
278        foreach (OutlineItem * j, i->children) {
279                int page = j->page + prefixSum[j->document];
280                while (hfCache[level].size() < page)
281                        hfCache[level].push_back(hfCache[level].back());
282                if (hfCache[level].size() == page)
283                        hfCache[level].push_back(j);
284                buildHFCache(j, level+1);
285        }
286}
287
288
289/*!
290  \brief Fill in header footer parameters for a given page
291  \param page The page to fill in for
292  \param parms The structure to fill
293 */
294void Outline::fillHeaderFooterParms(int page, QHash<QString, QString> & parms, const settings::PdfObject & ps) {
295        //Build hfcache
296        if (d->hfCache.size() == 0) {
297                for (int i=0; i < 3; ++i) {
298                        QList< OutlineItem *> x;
299                        x.push_back(NULL);
300                        d->hfCache.push_back(x);
301                }
302                foreach (OutlineItem * i, d->documentOutlines)
303                        d->buildHFCache(i, 0);
304        }
305        for (int i=0; i < 3; ++i)
306                while (d->hfCache[i].size() <= page)
307                        d->hfCache[i].push_back(d->hfCache[i].back());
308
309        int off = d->settings.pageOffset;
310        typedef QPair<QString,QString> SP;
311        foreach (const SP & rep, ps.replacements)
312                parms[rep.first] = rep.second;
313
314        parms["frompage"] = QString::number(off+1);
315        parms["topage"] = QString::number(off+d->pageCount);
316        parms["page" ] = QString::number(page+off);
317        parms["webpage"] = ps.page;
318        parms["section" ] = d->hfCache[0][page]?d->hfCache[0][page]->value:QString("");
319        parms["subsection" ] = d->hfCache[1][page]?d->hfCache[1][page]->value:QString("");
320        parms["subsubsection" ] = d->hfCache[2][page]?d->hfCache[2][page]->value:QString("");
321}
322
323/*!
324  \brief Fill in anchor as to add to a given document
325  \param doc The 0 indexed document number (in order of addWebPage)
326  \param anchors The structure to fill
327*/
328void Outline::fillAnchors(int doc, QHash<QString, QWebElement> & anchors) {
329  if (doc < 0 || doc >= d->documentOutlines.size()) return;
330  d->fillChildAnchors( d->documentOutlines[doc], anchors );
331}
332
333/*!
334  \brief return the number of pages in the outlined document
335*/
336int Outline::pageCount() {
337        return d->pageCount;
338}
339
340/*!
341  \brief Print the document outline to a given printer
342  \param printer The printer to print to
343*/
344void Outline::printOutline(QPrinter * printer) {
345        if (!d->settings.outline) return;
346        foreach (OutlineItem * i, d->documentOutlines)
347                d->outlineChildren(i, printer, 0);
348}
349
350}
351#endif //__EXTENSIVE_WKHTMLTOPDF_QT_HACK__
Note: See TracBrowser for help on using the repository browser.