KDocTools
xslt.cpp
Go to the documentation of this file.
00001 #include "xslt.h" 00002 00003 #include <libxslt/xsltconfig.h> 00004 #include <libxslt/xsltInternals.h> 00005 #include <libxslt/transform.h> 00006 #include <libxslt/xsltutils.h> 00007 #include <libxml/xmlIO.h> 00008 #include <libxml/parserInternals.h> 00009 #include <libxml/catalog.h> 00010 #include <QtCore/QDate> 00011 #include <QtCore/QDir> 00012 #include <QtCore/QRegExp> 00013 #include <assert.h> 00014 #include <QtCore/QTextCodec> 00015 #include <stdlib.h> 00016 #include <stdarg.h> 00017 00018 #ifdef Q_OS_WIN 00019 #include <config-kdoctools.h> 00020 #include <QtCore/QCoreApplication> 00021 #include <QtCore/QDebug> 00022 #include <QtCore/QHash> 00023 #endif 00024 00025 #if !defined( SIMPLE_XSLT ) 00026 extern HelpProtocol *slave; 00027 #define INFO( x ) if (slave) slave->infoMessage(x); 00028 #else 00029 #define INFO( x ) 00030 #endif 00031 00032 int writeToQString(void * context, const char * buffer, int len) 00033 { 00034 QString *t = (QString*)context; 00035 *t += QString::fromUtf8(buffer, len); 00036 return len; 00037 } 00038 00039 int closeQString(void * context) { 00040 QString *t = (QString*)context; 00041 *t += '\n'; 00042 return 0; 00043 } 00044 00045 #if defined (SIMPLE_XSLT) && defined(Q_WS_WIN) 00046 00047 #define MAX_PATHS 64 00048 xmlExternalEntityLoader defaultEntityLoader = NULL; 00049 static xmlChar *paths[MAX_PATHS + 1]; 00050 static int nbpaths = 0; 00051 static QHash<QString,QString> replaceURLList; 00052 00053 /* 00054 * Entity loading control and customization. 00055 * taken from xsltproc.c 00056 */ 00057 static xmlParserInputPtr xsltprocExternalEntityLoader(const char *_URL, const char *ID,xmlParserCtxtPtr ctxt) 00058 { 00059 xmlParserInputPtr ret; 00060 warningSAXFunc warning = NULL; 00061 00062 // use local available dtd versions instead of fetching it everytime from the internet 00063 QString url = QLatin1String(_URL); 00064 QHash<QString, QString>::const_iterator i; 00065 for(i = replaceURLList.constBegin(); i != replaceURLList.constEnd(); i++) 00066 { 00067 if (url.startsWith(i.key())) 00068 { 00069 url.replace(i.key(),i.value()); 00070 qDebug() << "converted" << _URL << "to" << url; 00071 } 00072 } 00073 char URL[1024]; 00074 strcpy(URL,url.toLatin1().constData()); 00075 00076 const char *lastsegment = URL; 00077 const char *iter = URL; 00078 00079 if (nbpaths > 0) { 00080 while (*iter != 0) { 00081 if (*iter == '/') 00082 lastsegment = iter + 1; 00083 iter++; 00084 } 00085 } 00086 00087 if ((ctxt != NULL) && (ctxt->sax != NULL)) { 00088 warning = ctxt->sax->warning; 00089 ctxt->sax->warning = NULL; 00090 } 00091 00092 if (defaultEntityLoader != NULL) { 00093 ret = defaultEntityLoader(URL, ID, ctxt); 00094 if (ret != NULL) { 00095 if (warning != NULL) 00096 ctxt->sax->warning = warning; 00097 qDebug() << "Loaded URL=\"" << URL << "\" ID=\"" << ID << "\""; 00098 return(ret); 00099 } 00100 } 00101 for (int i = 0;i < nbpaths;i++) { 00102 xmlChar *newURL; 00103 00104 newURL = xmlStrdup((const xmlChar *) paths[i]); 00105 newURL = xmlStrcat(newURL, (const xmlChar *) "/"); 00106 newURL = xmlStrcat(newURL, (const xmlChar *) lastsegment); 00107 if (newURL != NULL) { 00108 ret = defaultEntityLoader((const char *)newURL, ID, ctxt); 00109 if (ret != NULL) { 00110 if (warning != NULL) 00111 ctxt->sax->warning = warning; 00112 qDebug() << "Loaded URL=\"" << newURL << "\" ID=\"" << ID << "\""; 00113 xmlFree(newURL); 00114 return(ret); 00115 } 00116 xmlFree(newURL); 00117 } 00118 } 00119 if (warning != NULL) { 00120 ctxt->sax->warning = warning; 00121 if (URL != NULL) 00122 warning(ctxt, "failed to load external entity \"%s\"\n", URL); 00123 else if (ID != NULL) 00124 warning(ctxt, "failed to load external entity \"%s\"\n", ID); 00125 } 00126 return(NULL); 00127 } 00128 #endif 00129 00130 QString transform( const QString &pat, const QString& tss, 00131 const QVector<const char *> ¶ms ) 00132 { 00133 QString parsed; 00134 00135 INFO(i18n("Parsing stylesheet")); 00136 #if defined (SIMPLE_XSLT) && defined(Q_WS_WIN) 00137 // prepare use of local available dtd versions instead of fetching everytime from the internet 00138 // this approach is url based 00139 if (!defaultEntityLoader) { 00140 defaultEntityLoader = xmlGetExternalEntityLoader(); 00141 xmlSetExternalEntityLoader(xsltprocExternalEntityLoader); 00142 00143 replaceURLList[QLatin1String("http://www.oasis-open.org/docbook/xml/4.2")] = QString("file:///%1").arg(DOCBOOK_XML_CURRDTD); 00144 } 00145 #endif 00146 00147 xsltStylesheetPtr style_sheet = 00148 xsltParseStylesheetFile((const xmlChar *)tss.toLatin1().data()); 00149 00150 if ( !style_sheet ) { 00151 return parsed; 00152 } 00153 if (style_sheet->indent == 1) 00154 xmlIndentTreeOutput = 1; 00155 else 00156 xmlIndentTreeOutput = 0; 00157 00158 INFO(i18n("Parsing document")); 00159 00160 xmlDocPtr doc = xmlParseFile( pat.toLatin1() ); 00161 xsltTransformContextPtr ctxt; 00162 00163 ctxt = xsltNewTransformContext(style_sheet, doc); 00164 if (ctxt == NULL) 00165 return parsed; 00166 00167 INFO(i18n("Applying stylesheet")); 00168 QVector<const char *> p = params; 00169 p.append( NULL ); 00170 xmlDocPtr res = xsltApplyStylesheet(style_sheet, doc, const_cast<const char **>(&p[0])); 00171 xmlFreeDoc(doc); 00172 if (res != NULL) { 00173 xmlOutputBufferPtr outp = xmlOutputBufferCreateIO(writeToQString, (xmlOutputCloseCallback)closeQString, &parsed, 0); 00174 outp->written = 0; 00175 INFO(i18n("Writing document")); 00176 xsltSaveResultTo ( outp, res, style_sheet ); 00177 xmlOutputBufferFlush(outp); 00178 xmlFreeDoc(res); 00179 } 00180 xsltFreeStylesheet(style_sheet); 00181 00182 if (parsed.isEmpty()) 00183 parsed = ' '; // avoid error message 00184 return parsed; 00185 } 00186 00187 /* 00188 xmlParserInputPtr meinExternalEntityLoader(const char *URL, const char *ID, 00189 xmlParserCtxtPtr ctxt) { 00190 xmlParserInputPtr ret = NULL; 00191 00192 // fprintf(stderr, "loading %s %s %s\n", URL, ID, ctxt->directory); 00193 00194 if (URL == NULL) { 00195 if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL)) 00196 ctxt->sax->warning(ctxt, 00197 "failed to load external entity \"%s\"\n", ID); 00198 return(NULL); 00199 } 00200 if (!qstrcmp(ID, "-//OASIS//DTD DocBook XML V4.1.2//EN")) 00201 URL = "docbook/xml-dtd-4.1.2/docbookx.dtd"; 00202 if (!qstrcmp(ID, "-//OASIS//DTD XML DocBook V4.1.2//EN")) 00203 URL = "docbook/xml-dtd-4.1.2/docbookx.dtd"; 00204 00205 QString file; 00206 if (KStandardDirs::exists( QDir::currentPath() + "/" + URL ) ) 00207 file = QDir::currentPath() + "/" + URL; 00208 else 00209 file = locate("dtd", URL); 00210 00211 ret = xmlNewInputFromFile(ctxt, file.toLatin1().constData()); 00212 if (ret == NULL) { 00213 if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL)) 00214 ctxt->sax->warning(ctxt, 00215 00216 "failed to load external entity \"%s\"\n", URL); 00217 } 00218 return(ret); 00219 } 00220 */ 00221 00222 QString splitOut(const QString &parsed, int index) 00223 { 00224 int start_index = index + 1; 00225 while (parsed.at(start_index - 1) != '>') start_index++; 00226 00227 int inside = 0; 00228 00229 QString filedata; 00230 00231 while (true) { 00232 int endindex = parsed.indexOf("</FILENAME>", index); 00233 int startindex = parsed.indexOf("<FILENAME ", index) + 1; 00234 00235 // kDebug() << "FILENAME " << startindex << " " << endindex << " " << inside << " " << parsed.mid(startindex + 18, 15)<< " " << parsed.length(); 00236 00237 if (startindex > 0) { 00238 if (startindex < endindex) { 00239 // kDebug() << "finding another"; 00240 index = startindex + 8; 00241 inside++; 00242 } else { 00243 index = endindex + 8; 00244 inside--; 00245 } 00246 } else { 00247 inside--; 00248 index = endindex + 1; 00249 } 00250 00251 if (inside == 0) { 00252 filedata = parsed.mid(start_index, endindex - start_index); 00253 break; 00254 } 00255 00256 } 00257 00258 index = filedata.indexOf("<FILENAME "); 00259 00260 if (index > 0) { 00261 int endindex = filedata.lastIndexOf("</FILENAME>"); 00262 while (filedata.at(endindex) != '>') endindex++; 00263 endindex++; 00264 filedata = filedata.left(index) + filedata.mid(endindex); 00265 } 00266 00267 // filedata.replace(QRegExp(">"), "\n>"); 00268 return filedata; 00269 } 00270 00271 QByteArray fromUnicode( const QString &data ) 00272 { 00273 #ifdef Q_WS_WIN 00274 return data.toUtf8(); 00275 #else 00276 QTextCodec *locale = QTextCodec::codecForLocale(); 00277 QByteArray result; 00278 char buffer[30000]; 00279 uint buffer_len = 0; 00280 uint len = 0; 00281 int offset = 0; 00282 const int part_len = 5000; 00283 00284 QString part; 00285 00286 while ( offset < data.length() ) 00287 { 00288 part = data.mid( offset, part_len ); 00289 QByteArray test = locale->fromUnicode( part ); 00290 if ( locale->toUnicode( test ) == part ) { 00291 result += test; 00292 offset += part_len; 00293 continue; 00294 } 00295 len = part.length(); 00296 buffer_len = 0; 00297 for ( uint i = 0; i < len; i++ ) { 00298 QByteArray test = locale->fromUnicode( part.mid( i, 1 ) ); 00299 if ( locale->toUnicode( test ) == part.mid( i, 1 ) ) { 00300 if (buffer_len + test.length() + 1 > sizeof(buffer)) 00301 break; 00302 strcpy( buffer + buffer_len, test.data() ); 00303 buffer_len += test.length(); 00304 } else { 00305 QString res; 00306 res.sprintf( "&#%d;", part.at( i ).unicode() ); 00307 test = locale->fromUnicode( res ); 00308 if (buffer_len + test.length() + 1 > sizeof(buffer)) 00309 break; 00310 strcpy( buffer + buffer_len, test.data() ); 00311 buffer_len += test.length(); 00312 } 00313 } 00314 result += QByteArray( buffer, buffer_len + 1); 00315 offset += part_len; 00316 } 00317 return result; 00318 #endif 00319 } 00320 00321 void replaceCharsetHeader( QString &output ) 00322 { 00323 QString name; 00324 #ifdef Q_WS_WIN 00325 name = "utf-8"; 00326 // may be required for all xml output 00327 if (output.contains("<table-of-contents>")) 00328 output.replace( QString( "<?xml version=\"1.0\"?>" ), 00329 QString( "<?xml version=\"1.0\" encoding=\"%1\"?>").arg( name ) ); 00330 #else 00331 name = QTextCodec::codecForLocale()->name(); 00332 name.replace( QString( "ISO " ), "iso-" ); 00333 output.replace( QString( "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">" ), 00334 QString( "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%1\">" ).arg( name ) ); 00335 #endif 00336 }
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Wed May 2 2012 18:46:26 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Wed May 2 2012 18:46:26 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.