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