1 /* poppler-private.cc: qt interface to poppler
2  * Copyright (C) 2005, Net Integration Technologies, Inc.
3  * Copyright (C) 2006, 2011 by Albert Astals Cid <aacid@kde.org>
4  * Copyright (C) 2008, 2010, 2011 by Pino Toscano <pino@kde.org>
5  * Inspired on code by
6  * Copyright (C) 2004 by Albert Astals Cid <tsdgeos@terra.es>
7  * Copyright (C) 2004 by Enrico Ros <eros.kde@email.it>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2, or (at your option)
12  * any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
22  */
23 
24 #include "poppler-private.h"
25 
26 #include <QtCore/QByteArray>
27 #include <QtCore/QDebug>
28 #include <QtCore/QVariant>
29 
30 #include <Link.h>
31 #include <Outline.h>
32 #include <UnicodeMap.h>
33 
34 namespace Poppler {
35 
36 namespace Debug {
37 
qDebugDebugFunction(const QString & message,const QVariant &)38     void qDebugDebugFunction(const QString &message, const QVariant & /*closure*/)
39     {
40         qDebug() << message;
41     }
42 
43     PopplerDebugFunc debugFunction = qDebugDebugFunction;
44     QVariant debugClosure;
45 
46 }
47 
48     static UnicodeMap *utf8Map = 0;
49 
setDebugErrorFunction(PopplerDebugFunc function,const QVariant & closure)50     void setDebugErrorFunction(PopplerDebugFunc function, const QVariant &closure)
51     {
52         Debug::debugFunction = function ? function : Debug::qDebugDebugFunction;
53         Debug::debugClosure = closure;
54     }
55 
qt4ErrorFunction(int pos,char * msg,va_list args)56     void qt4ErrorFunction(int pos, char *msg, va_list args)
57     {
58         QString emsg;
59         char buffer[1024]; // should be big enough
60 
61         if (pos >= 0)
62         {
63             emsg = QString::fromLatin1("Error (%1): ").arg(pos);
64         }
65         else
66         {
67             emsg = QString::fromLatin1("Error: ");
68         }
69         qvsnprintf(buffer, sizeof(buffer) - 1, msg, args);
70         emsg += QString::fromAscii(buffer);
71         (*Debug::debugFunction)(emsg, Debug::debugClosure);
72     }
73 
unicodeToQString(Unicode * u,int len)74     QString unicodeToQString(Unicode* u, int len) {
75         if (!utf8Map)
76         {
77                 GooString enc("UTF-8");
78                 utf8Map = globalParams->getUnicodeMap(&enc);
79                 utf8Map->incRefCnt();
80         }
81 
82         // ignore the last character if it is 0x0
83         if ((len > 0) && (u[len - 1] == 0))
84         {
85             --len;
86         }
87 
88         GooString convertedStr;
89         for (int i = 0; i < len; ++i)
90         {
91             char buf[8];
92             const int n = utf8Map->mapUnicode(u[i], buf, sizeof(buf));
93             convertedStr.append(buf, n);
94         }
95 
96         return QString::fromUtf8(convertedStr.getCString(), convertedStr.getLength());
97     }
98 
UnicodeParsedString(GooString * s1)99     QString UnicodeParsedString(GooString *s1) {
100         if ( !s1 || s1->getLength() == 0 )
101             return QString();
102 
103         GBool isUnicode;
104         int i;
105         Unicode u;
106         QString result;
107         if ( ( s1->getChar(0) & 0xff ) == 0xfe && ( s1->getLength() > 1 && ( s1->getChar(1) & 0xff ) == 0xff ) )
108         {
109             isUnicode = gTrue;
110             i = 2;
111             result.reserve( ( s1->getLength() - 2 ) / 2 );
112         }
113         else
114         {
115             isUnicode = gFalse;
116             i = 0;
117             result.reserve( s1->getLength() );
118         }
119         while ( i < s1->getLength() )
120         {
121             if ( isUnicode )
122             {
123                 u = ( ( s1->getChar(i) & 0xff ) << 8 ) | ( s1->getChar(i+1) & 0xff );
124                 i += 2;
125             }
126             else
127             {
128                 u = s1->getChar(i) & 0xff;
129                 ++i;
130             }
131             result += QChar( u );
132         }
133         return result;
134     }
135 
QStringToUnicodeGooString(const QString & s)136     GooString *QStringToUnicodeGooString(const QString &s) {
137         int len = s.length() * 2 + 2;
138         char *cstring = (char *)gmallocn(len, sizeof(char));
139         cstring[0] = 0xfe;
140         cstring[1] = 0xff;
141         for (int i = 0; i < s.length(); ++i)
142         {
143             cstring[2+i*2] = s.at(i).row();
144             cstring[3+i*2] = s.at(i).cell();
145         }
146         GooString *ret = new GooString(cstring, len);
147         gfree(cstring);
148         return ret;
149     }
150 
QStringToGooString(const QString & s)151     GooString *QStringToGooString(const QString &s) {
152         int len = s.length();
153         char *cstring = (char *)gmallocn(s.length(), sizeof(char));
154         for (int i = 0; i < len; ++i)
155             cstring[i] = s.at(i).unicode();
156         GooString *ret = new GooString(cstring, len);
157         gfree(cstring);
158         return ret;
159     }
160 
linkActionToTocItem(::LinkAction * a,DocumentData * doc,QDomElement * e)161     void linkActionToTocItem( ::LinkAction * a, DocumentData * doc, QDomElement * e )
162     {
163         if ( !a || !e )
164             return;
165 
166         switch ( a->getKind() )
167         {
168             case actionGoTo:
169             {
170                 // page number is contained/referenced in a LinkGoTo
171                 LinkGoTo * g = static_cast< LinkGoTo * >( a );
172                 LinkDest * destination = g->getDest();
173                 if ( !destination && g->getNamedDest() )
174                 {
175                     // no 'destination' but an internal 'named reference'. we could
176                     // get the destination for the page now, but it's VERY time consuming,
177                     // so better storing the reference and provide the viewport on demand
178                     GooString *s = g->getNamedDest();
179                     QChar *charArray = new QChar[s->getLength()];
180                     for (int i = 0; i < s->getLength(); ++i) charArray[i] = QChar(s->getCString()[i]);
181                     QString aux(charArray, s->getLength());
182                     e->setAttribute( "DestinationName", aux );
183                     delete[] charArray;
184                 }
185                 else if ( destination && destination->isOk() )
186                 {
187                     LinkDestinationData ldd(destination, NULL, doc, false);
188                     e->setAttribute( "Destination", LinkDestination(ldd).toString() );
189                 }
190                 break;
191             }
192             case actionGoToR:
193             {
194                 // page number is contained/referenced in a LinkGoToR
195                 LinkGoToR * g = static_cast< LinkGoToR * >( a );
196                 LinkDest * destination = g->getDest();
197                 if ( !destination && g->getNamedDest() )
198                 {
199                     // no 'destination' but an internal 'named reference'. we could
200                     // get the destination for the page now, but it's VERY time consuming,
201                     // so better storing the reference and provide the viewport on demand
202                     GooString *s = g->getNamedDest();
203                     QChar *charArray = new QChar[s->getLength()];
204                     for (int i = 0; i < s->getLength(); ++i) charArray[i] = QChar(s->getCString()[i]);
205                     QString aux(charArray, s->getLength());
206                     e->setAttribute( "DestinationName", aux );
207                     delete[] charArray;
208                 }
209                 else if ( destination && destination->isOk() )
210                 {
211                     LinkDestinationData ldd(destination, NULL, doc, g->getFileName() != 0);
212                     e->setAttribute( "Destination", LinkDestination(ldd).toString() );
213                 }
214                 e->setAttribute( "ExternalFileName", g->getFileName()->getCString() );
215                 break;
216             }
217             case actionURI:
218             {
219                 LinkURI * u = static_cast< LinkURI * >( a );
220                 e->setAttribute( "DestinationURI", u->getURI()->getCString() );
221             }
222             default: ;
223         }
224     }
225 
~DocumentData()226     DocumentData::~DocumentData()
227     {
228         qDeleteAll(m_embeddedFiles);
229         delete (OptContentModel *)m_optContentModel;
230         delete doc;
231         delete m_outputDev;
232         delete m_fontInfoIterator;
233 
234         count --;
235         if ( count == 0 )
236         {
237             utf8Map = 0;
238             delete globalParams;
239         }
240       }
241 
init(GooString * ownerPassword,GooString * userPassword)242     void DocumentData::init(GooString *ownerPassword, GooString *userPassword)
243     {
244         m_fontInfoIterator = 0;
245         m_backend = Document::SplashBackend;
246         m_outputDev = 0;
247         paperColor = Qt::white;
248         m_hints = 0;
249         m_optContentModel = 0;
250         // It might be more appropriate to delete these in PDFDoc
251         delete ownerPassword;
252         delete userPassword;
253 
254         if ( count == 0 )
255         {
256             utf8Map = 0;
257             globalParams = new GlobalParams();
258             setErrorFunction(qt4ErrorFunction);
259         }
260         count ++;
261     }
262 
263 
addTocChildren(QDomDocument * docSyn,QDomNode * parent,GooList * items)264     void DocumentData::addTocChildren( QDomDocument * docSyn, QDomNode * parent, GooList * items )
265     {
266         int numItems = items->getLength();
267         for ( int i = 0; i < numItems; ++i )
268         {
269             // iterate over every object in 'items'
270             OutlineItem * outlineItem = (OutlineItem *)items->get( i );
271 
272             // 1. create element using outlineItem's title as tagName
273             QString name;
274             Unicode * uniChar = outlineItem->getTitle();
275             int titleLength = outlineItem->getTitleLength();
276             name = unicodeToQString(uniChar, titleLength);
277             if ( name.isEmpty() )
278                 continue;
279 
280             QDomElement item = docSyn->createElement( name );
281             parent->appendChild( item );
282 
283             // 2. find the page the link refers to
284             ::LinkAction * a = outlineItem->getAction();
285             linkActionToTocItem( a, this, &item );
286 
287             item.setAttribute( "Open", QVariant( (bool)outlineItem->isOpen() ).toString() );
288 
289             // 3. recursively descend over children
290             outlineItem->open();
291             GooList * children = outlineItem->getKids();
292             if ( children )
293                 addTocChildren( docSyn, &item, children );
294         }
295     }
296 
297 }
298