/*************************************************************************** copyright : (C) 2003-2006 by Robby Stephenson email : robby@periapsis.org ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of version 2 of the GNU General Public License as * * published by the Free Software Foundation; * * * ***************************************************************************/ #include "htmlexporter.h" #include "xslthandler.h" #include "tellicoxmlexporter.h" #include "../document.h" #include "../collection.h" #include "../filehandler.h" #include "../imagefactory.h" #include "../latin1literal.h" #include "../tellico_kernel.h" #include "../tellico_utils.h" #include "../progressmanager.h" #include "../core/tellico_config.h" #include "../tellico_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern "C" { #include #include } using Tellico::Export::HTMLExporter; HTMLExporter::HTMLExporter() : Tellico::Export::Exporter(), m_handler(0), m_printHeaders(true), m_printGrouped(false), m_exportEntryFiles(false), m_cancelled(false), m_parseDOM(true), m_checkCreateDir(true), m_imageWidth(0), m_imageHeight(0), m_widget(0), m_xsltFile(TQString::fromLatin1("tellico2html.xsl")) { } HTMLExporter::HTMLExporter(Data::CollPtr coll_) : Tellico::Export::Exporter(coll_), m_handler(0), m_printHeaders(true), m_printGrouped(false), m_exportEntryFiles(false), m_cancelled(false), m_parseDOM(true), m_checkCreateDir(true), m_imageWidth(0), m_imageHeight(0), m_widget(0), m_xsltFile(TQString::fromLatin1("tellico2html.xsl")) { } HTMLExporter::~HTMLExporter() { delete m_handler; m_handler = 0; } TQString HTMLExporter::formatString() const { return i18n("HTML"); } TQString HTMLExporter::fileFilter() const { return i18n("*.html|HTML Files (*.html)") + TQChar('\n') + i18n("*|All Files"); } void HTMLExporter::reset() { // since the ExportUTF8 option may have changed, need to delete handler delete m_handler; m_handler = 0; m_files.clear(); m_links.clear(); m_copiedFiles.clear(); } bool HTMLExporter::exec() { if(url().isEmpty() || !url().isValid()) { kdWarning() << "HTMLExporter::exec() - trying to export to invalid URL" << endl; return false; } // check file exists first // if we're not forcing, ask use bool force = (options() & Export::ExportForce) || FileHandler::queryExists(url()); if(!force) { return false; } if(!m_parseDOM) { return FileHandler::writeTextURL(url(), text(), options() & Export::ExportUTF8, force); } m_cancelled = false; // TODO: maybe need label? if(options() & ExportProgress) { ProgressItem& item = ProgressManager::self()->newProgressItem(this, TQString(), true); item.setTotalSteps(100); connect(&item, TQT_SIGNAL(signalCancelled(ProgressItem*)), TQT_SLOT(slotCancel())); } // ok if not ExportProgress, no worries ProgressItem::Done done(this); htmlDocPtr htmlDoc = htmlParseDoc(reinterpret_cast(text().utf8().data()), NULL); xmlNodePtr root = xmlDocGetRootElement(htmlDoc); if(root == 0) { myDebug() << "HTMLExporter::exec() - no root" << endl; return false; } parseDOM(root); if(m_cancelled) { return true; // intentionally cancelled } ProgressManager::self()->setProgress(this, 15); xmlChar* c; int bytes; htmlDocDumpMemory(htmlDoc, &c, &bytes); TQString allText; if(bytes > 0) { allText = TQString::fromUtf8(reinterpret_cast(c), bytes); xmlFree(c); } if(m_cancelled) { return true; // intentionally cancelled } ProgressManager::self()->setProgress(this, 20); bool success = FileHandler::writeTextURL(url(), allText, options() & Export::ExportUTF8, force); success &= copyFiles() && (!m_exportEntryFiles || writeEntryFiles()); return success; } bool HTMLExporter::loadXSLTFile() { TQString xsltfile = locate("appdata", m_xsltFile); if(xsltfile.isNull()) { myDebug() << "HTMLExporter::loadXSLTFile() - no xslt file for " << m_xsltFile << endl; return false; } KURL u; u.setPath(xsltfile); // do NOT do namespace processing, it messes up the XSL declaration since // TQDom thinks there are no elements in the Tellico namespace and as a result // removes the namespace declaration TQDomDocument dom = FileHandler::readXMLFile(u, false); if(dom.isNull()) { myDebug() << "HTMLExporter::loadXSLTFile() - error loading xslt file: " << xsltfile << endl; return false; } // notes about utf-8 encoding: // all params should be passed to XSLTHandler in utf8 // input string to XSLTHandler should be in utf-8, EVEN IF DOM STRING SAYS OTHERWISE // the stylesheet prints utf-8 by default, if using locale encoding, need // to change the encoding attribute on the xsl:output element if(!(options() & Export::ExportUTF8)) { XSLTHandler::setLocaleEncoding(dom); } delete m_handler; m_handler = new XSLTHandler(dom, TQFile::encodeName(xsltfile), true /*translate*/); if(!m_handler->isValid()) { delete m_handler; m_handler = 0; return false; } if(m_exportEntryFiles) { // export entries to same place as all the other date files m_handler->addStringParam("entrydir", TQFile::encodeName(fileDir().fileName())+ '/'); // be sure to link all the entries m_handler->addParam("link-entries", "true()"); } if(!m_collectionURL.isEmpty()) { TQString s = TQString::fromLatin1("../") + m_collectionURL.fileName(); m_handler->addStringParam("collection-file", s.utf8()); } // look for a file that gets installed to know the installation directory // if parseDOM, that means we want the locations to be the actual location // otherwise, we assume it'll be relative if(m_parseDOM && m_dataDir.isEmpty()) { m_dataDir = KGlobal::dirs()->findResourceDir("appdata", TQString::fromLatin1("pics/tellico.png")); } else if(!m_parseDOM) { m_dataDir.truncate(0); } if(!m_dataDir.isEmpty()) { m_handler->addStringParam("datadir", TQFile::encodeName(m_dataDir)); } setFormattingOptions(collection()); return m_handler->isValid(); } TQString HTMLExporter::text() { if((!m_handler || !m_handler->isValid()) && !loadXSLTFile()) { kdWarning() << "HTMLExporter::text() - error loading xslt file: " << m_xsltFile << endl; return TQString(); } Data::CollPtr coll = collection(); if(!coll) { myDebug() << "HTMLExporter::text() - no collection pointer!" << endl; return TQString(); } if(m_groupBy.isEmpty()) { m_printGrouped = false; // can't group if no groups exist } GUI::CursorSaver cs; writeImages(coll); // now grab the XML TellicoXMLExporter exporter(coll); exporter.setURL(url()); exporter.setEntries(entries()); exporter.setIncludeGroups(m_printGrouped); // yes, this should be in utf8, always exporter.setOptions(options() | Export::ExportUTF8 | Export::ExportImages); TQDomDocument output = exporter.exportXML(); #if 0 TQFile f(TQString::fromLatin1("/tmp/test.xml")); if(f.open(IO_WriteOnly)) { TQTextStream t(&f); t << output.toString(); } f.close(); #endif TQString text = m_handler->applyStylesheet(output.toString()); #if 0 TQFile f2(TQString::fromLatin1("/tmp/test.html")); if(f2.open(IO_WriteOnly)) { TQTextStream t(&f2); t << text; // t << "\n\n-------------------------------------------------------\n\n"; // t << Tellico::i18nReplace(text); } f2.close(); #endif // the XSLT file gets translated instead // return Tellico::i18nReplace(text); return text; } void HTMLExporter::setFormattingOptions(Data::CollPtr coll) { TQString file = Kernel::self()->URL().fileName(); if(file != i18n("Untitled")) { m_handler->addStringParam("filename", TQFile::encodeName(file)); } m_handler->addStringParam("cdate", KGlobal::locale()->formatDate(TQDate::currentDate()).utf8()); m_handler->addParam("show-headers", m_printHeaders ? "true()" : "false()"); m_handler->addParam("group-entries", m_printGrouped ? "true()" : "false()"); TQStringList sortTitles; if(!m_sort1.isEmpty()) { sortTitles << m_sort1; } if(!m_sort2.isEmpty()) { sortTitles << m_sort2; } // the third sort column may be same as first if(!m_sort3.isEmpty() && sortTitles.findIndex(m_sort3) == -1) { sortTitles << m_sort3; } if(sortTitles.count() > 0) { m_handler->addStringParam("sort-name1", coll->fieldNameByTitle(sortTitles[0]).utf8()); if(sortTitles.count() > 1) { m_handler->addStringParam("sort-name2", coll->fieldNameByTitle(sortTitles[1]).utf8()); if(sortTitles.count() > 2) { m_handler->addStringParam("sort-name3", coll->fieldNameByTitle(sortTitles[2]).utf8()); } } } // no longer showing "sorted by..." since the column headers are clickable // but still use "grouped by" TQString sortString; if(m_printGrouped) { TQString s; // if more than one, then it's the People pseudo-group if(m_groupBy.count() > 1) { s = i18n("People"); } else { s = coll->fieldTitleByName(m_groupBy[0]); } sortString = i18n("(grouped by %1)").arg(s); TQString groupFields; for(TQStringList::ConstIterator it = m_groupBy.begin(); it != m_groupBy.end(); ++it) { Data::FieldPtr f = coll->fieldByName(*it); if(!f) { continue; } if(f->flags() & Data::Field::AllowMultiple) { groupFields += TQString::fromLatin1("tc:") + *it + TQString::fromLatin1("s/tc:") + *it; } else { groupFields += TQString::fromLatin1("tc:") + *it; } int ncols = 0; if(f->type() == Data::Field::Table) { bool ok; ncols = Tellico::toUInt(f->property(TQString::fromLatin1("columns")), &ok); if(!ok) { ncols = 1; } } if(ncols > 1) { groupFields += TQString::fromLatin1("/tc:column[1]"); } if(*it != m_groupBy.last()) { groupFields += '|'; } } // myDebug() << groupFields << endl; m_handler->addStringParam("group-fields", groupFields.utf8()); m_handler->addStringParam("sort-title", sortString.utf8()); } TQString pageTitle = coll->title(); pageTitle += TQChar(' ') + sortString; m_handler->addStringParam("page-title", pageTitle.utf8()); TQStringList showFields; for(TQStringList::ConstIterator it = m_columns.begin(); it != m_columns.end(); ++it) { showFields << coll->fieldNameByTitle(*it); } m_handler->addStringParam("column-names", showFields.join(TQChar(' ')).utf8()); if(m_imageWidth > 0 && m_imageHeight > 0) { m_handler->addParam("image-width", TQCString().setNum(m_imageWidth)); m_handler->addParam("image-height", TQCString().setNum(m_imageHeight)); } // add system colors to stylesheet const int type = coll->type(); m_handler->addStringParam("font", TQString(Config::templateFont(type).family()).latin1()); m_handler->addStringParam("fontsize", TQCString().setNum(Config::templateFont(type).pointSize())); m_handler->addStringParam("bgcolor", TQString(Config::templateBaseColor(type).name()).latin1()); m_handler->addStringParam("fgcolor", TQString(Config::templateTextColor(type).name()).latin1()); m_handler->addStringParam("color1", TQString(Config::templateHighlightedTextColor(type).name()).latin1()); m_handler->addStringParam("color2", TQString(Config::templateHighlightedBaseColor(type).name()).latin1()); // add locale code to stylesheet (for sorting) m_handler->addStringParam("lang", KGlobal::locale()->languagesTwoAlpha().first().utf8()); } void HTMLExporter::writeImages(Data::CollPtr coll_) { // keep track of which image fields to write, this is for field names StringSet imageFields; for(TQStringList::ConstIterator it = m_columns.begin(); it != m_columns.end(); ++it) { if(coll_->fieldByTitle(*it)->type() == Data::Field::Image) { imageFields.add(*it); } } // all the images potentially used in the HTML export need to be written to disk // if we're exporting entry files, then we'll certainly want all the image fields written // if we're not exporting to a file, then we might be exporting an entry template file // and so we need to write all of them too. if(m_exportEntryFiles || url().isEmpty()) { // add all image fields to string list Data::FieldVec fields = coll_->imageFields(); for(Data::FieldVec::Iterator fieldIt = fields.begin(); fieldIt != fields.end(); ++fieldIt) { imageFields.add(fieldIt->name()); } } // all of them are going to get written to tmp file bool useTemp = url().isEmpty(); KURL imgDir; TQString imgDirRelative; // really some convoluted logic here // basically, four cases. 1) we're writing to a tmp file, for printing probably // so then write all the images to the tmp directory, 2) we're exporting to HTML, and // this is the main collection file, in which case m_parseDOM is always true; // 3) we're exporting HTML, and this is the first entry file, for which parseDOM is true // and exportEntryFiles is false. Then the image file will get copied in copyFiles() and is // probably an image in the entry template. 4) we're exporting HTML, and this is not the // first entry file, in which case, we want to refer directly to the target dir if(useTemp) { // everything goes in the tmp dir imgDir.setPath(ImageFactory::tempDir()); imgDirRelative = imgDir.path(); } else if(m_parseDOM) { imgDir = fileDir(); // copy to fileDir imgDirRelative = Data::Document::self()->allImagesOnDisk() ? ImageFactory::dataDir() : ImageFactory::tempDir(); createDir(); } else { imgDir = fileDir(); imgDirRelative = KURL::relativeURL(url(), imgDir); createDir(); } m_handler->addStringParam("imgdir", TQFile::encodeName(imgDirRelative)); int count = 0; const int processCount = 100; // process after every 100 events TQStringList fieldsList = imageFields.toList(); StringSet imageSet; // track which images are written for(TQStringList::ConstIterator fieldName = fieldsList.begin(); fieldName != fieldsList.end(); ++fieldName) { for(Data::EntryVec::ConstIterator entryIt = entries().begin(); entryIt != entries().end(); ++entryIt) { TQString id = entryIt->field(*fieldName); // if no id or is already writen, continue if(id.isEmpty() || imageSet.has(id)) { continue; } imageSet.add(id); // try writing bool success = useTemp ? ImageFactory::writeCachedImage(id, ImageFactory::TempDir) : ImageFactory::writeImage(id, imgDir, true); if(!success) { kdWarning() << "HTMLExporter::writeImages() - unable to write image file: " << imgDir.path() << id << endl; } if(++count == processCount) { kapp->processEvents(); count = 0; } } } } TQWidget* HTMLExporter::widget(TQWidget* parent_, const char* name_/*=0*/) { if(m_widget && TQT_BASE_OBJECT(m_widget->parent()) == TQT_BASE_OBJECT(parent_)) { return m_widget; } m_widget = new TQWidget(parent_, name_); TQVBoxLayout* l = new TQVBoxLayout(m_widget); TQGroupBox* box = new TQGroupBox(1, Qt::Horizontal, i18n("HTML Options"), m_widget); l->addWidget(box); m_checkPrintHeaders = new TQCheckBox(i18n("Print field headers"), box); TQWhatsThis::add(m_checkPrintHeaders, i18n("If checked, the field names will be " "printed as table headers.")); m_checkPrintHeaders->setChecked(m_printHeaders); m_checkPrintGrouped = new TQCheckBox(i18n("Group the entries"), box); TQWhatsThis::add(m_checkPrintGrouped, i18n("If checked, the entries will be grouped by " "the selected field.")); m_checkPrintGrouped->setChecked(m_printGrouped); m_checkExportEntryFiles = new TQCheckBox(i18n("Export individual entry files"), box); TQWhatsThis::add(m_checkExportEntryFiles, i18n("If checked, individual files will be created for each entry.")); m_checkExportEntryFiles->setChecked(m_exportEntryFiles); l->addStretch(1); return m_widget; } void HTMLExporter::readOptions(KConfig* config_) { KConfigGroup exportConfig(config_, TQString::fromLatin1("ExportOptions - %1").arg(formatString())); m_printHeaders = exportConfig.readBoolEntry("Print Field Headers", m_printHeaders); m_printGrouped = exportConfig.readBoolEntry("Print Grouped", m_printGrouped); m_exportEntryFiles = exportConfig.readBoolEntry("Export Entry Files", m_exportEntryFiles); // read current entry export template m_entryXSLTFile = Config::templateName(collection()->type()); m_entryXSLTFile = locate("appdata", TQString::fromLatin1("entry-templates/") + m_entryXSLTFile + TQString::fromLatin1(".xsl")); } void HTMLExporter::saveOptions(KConfig* config_) { KConfigGroup cfg(config_, TQString::fromLatin1("ExportOptions - %1").arg(formatString())); m_printHeaders = m_checkPrintHeaders->isChecked(); cfg.writeEntry("Print Field Headers", m_printHeaders); m_printGrouped = m_checkPrintGrouped->isChecked(); cfg.writeEntry("Print Grouped", m_printGrouped); m_exportEntryFiles = m_checkExportEntryFiles->isChecked(); cfg.writeEntry("Export Entry Files", m_exportEntryFiles); } void HTMLExporter::setXSLTFile(const TQString& filename_) { if(m_xsltFile == filename_) { return; } m_xsltFile = filename_; m_xsltFilePath = TQString(); reset(); } KURL HTMLExporter::fileDir() const { if(url().isEmpty()) { return KURL(); } KURL fileDir = url(); // cd to directory of target URL fileDir.cd(TQString::fromLatin1("..")); fileDir.addPath(fileDirName()); return fileDir; } TQString HTMLExporter::fileDirName() const { if(!m_collectionURL.isEmpty()) { return TQString::fromLatin1("/"); } return url().fileName().section('.', 0, 0) + TQString::fromLatin1("_files/"); } // how ugly is this? const xmlChar* HTMLExporter::handleLink(const xmlChar* link_) { return reinterpret_cast(tqstrdup(handleLink(TQString::fromUtf8(reinterpret_cast(link_))).utf8())); } TQString HTMLExporter::handleLink(const TQString& link_) { if(m_links.contains(link_)) { return m_links[link_]; } // assume that if the link_ is not relative, then we don't need to copy it if(!KURL::isRelativeURL(link_)) { return link_; } if(m_xsltFilePath.isEmpty()) { m_xsltFilePath = locate("appdata", m_xsltFile); if(m_xsltFilePath.isNull()) { kdWarning() << "HTMLExporter::handleLink() - no xslt file for " << m_xsltFile << endl; } } KURL u; u.setPath(m_xsltFilePath); u = KURL(u, link_); // one of the "quirks" of the html export is that img src urls are set to point to // the tmpDir() when exporting entry files from a collection, but those images // don't actually exist, and they get copied in writeImages() instead. // so we only need to keep track of the url if it exists const bool exists = KIO::NetAccess::exists(u, false, 0); if(exists) { m_files.append(u); } // if we're exporting entry files, we want pics/ to // go in pics/ const bool isPic = link_.startsWith(m_dataDir + TQString::fromLatin1("pics/")); TQString midDir; if(m_exportEntryFiles && isPic) { midDir = TQString::fromLatin1("pics/"); } // pictures are special since they might not exist when the HTML is exported, since they might get copied later // on the other hand, don't change the file location if it doesn't exist if(isPic || exists) { m_links.insert(link_, fileDirName() + midDir + u.fileName()); } else { m_links.insert(link_, link_); } return m_links[link_]; } const xmlChar* HTMLExporter::analyzeInternalCSS(const xmlChar* str_) { return reinterpret_cast(tqstrdup(analyzeInternalCSS(TQString::fromUtf8(reinterpret_cast(str_))).utf8())); } TQString HTMLExporter::analyzeInternalCSS(const TQString& str_) { TQString str = str_; int start = 0; int end = 0; const TQString url = TQString::fromLatin1("url("); for(int pos = str.find(url); pos >= 0; pos = str.find(url, pos+1)) { pos += 4; // url( if(str[pos] == '"' || str[pos] == '\'') { ++pos; } start = pos; pos = str.find(')', start); end = pos; if(str[pos-1] == '"' || str[pos-1] == '\'') { --end; } str.replace(start, end-start, handleLink(str.mid(start, end-start))); } return str; } void HTMLExporter::createDir() { if(!m_checkCreateDir) { return; } KURL dir = fileDir(); if(dir.isEmpty()) { myDebug() << "HTMLExporter::createDir() - called on empty URL!" << endl; return; } if(KIO::NetAccess::exists(dir, false, 0)) { m_checkCreateDir = false; } else { m_checkCreateDir = !KIO::NetAccess::mkdir(dir, m_widget); } } bool HTMLExporter::copyFiles() { if(m_files.isEmpty()) { return true; } const uint start = 20; const uint maxProgress = m_exportEntryFiles ? 40 : 80; const uint stepSize = TQMAX(1, m_files.count()/maxProgress); uint j = 0; createDir(); KURL target; for(KURL::List::ConstIterator it = m_files.begin(); it != m_files.end() && !m_cancelled; ++it, ++j) { if(m_copiedFiles.has((*it).url())) { continue; } if(target.isEmpty()) { target = fileDir(); } target.setFileName((*it).fileName()); bool success = KIO::NetAccess::file_copy(*it, target, -1, true /* overwrite */, false /* resume */, m_widget); if(success) { m_copiedFiles.add((*it).url()); } else { kdWarning() << "HTMLExporter::copyFiles() - can't copy " << target << endl; kdWarning() << KIO::NetAccess::lastErrorString() << endl; } if(j%stepSize == 0) { if(options() & ExportProgress) { ProgressManager::self()->setProgress(this, TQMIN(start+j/stepSize, 99)); } kapp->processEvents(); } } return true; } bool HTMLExporter::writeEntryFiles() { if(m_entryXSLTFile.isEmpty()) { kdWarning() << "HTMLExporter::writeEntryFiles() - no entry XSLT file" << endl; return false; } const uint start = 60; const uint stepSize = TQMAX(1, entries().count()/40); uint j = 0; // now worry about actually exporting entry files // I can't reliable encode a string as a URI, so I'm punting, and I'll just replace everything but // a-zA-Z0-9 with an underscore. This MUST match the filename template in tellico2html.xsl // the id is used so uniqueness is guaranteed const TQRegExp badChars(TQString::fromLatin1("[^-a-zA-Z0-9]")); bool formatted = options() & Export::ExportFormatted; KURL outputFile = fileDir(); GUI::CursorSaver cs(TQt::waitCursor); HTMLExporter exporter(collection()); long opt = options() | Export::ExportForce; opt &= ~ExportProgress; exporter.setOptions(opt); exporter.setXSLTFile(m_entryXSLTFile); exporter.setCollectionURL(url()); bool parseDOM = true; const TQString title = TQString::fromLatin1("title"); const TQString html = TQString::fromLatin1(".html"); bool multipleTitles = collection()->fieldByName(title)->flags() & Data::Field::AllowMultiple; Data::EntryVec entries = this->entries(); // not const since the pointer has to be copied for(Data::EntryVecIt entryIt = entries.begin(); entryIt != entries.end() && !m_cancelled; ++entryIt, ++j) { TQString file = entryIt->field(title, formatted); // but only use the first title if it has multiple if(multipleTitles) { file = file.section(';', 0, 0); } file.replace(badChars, TQChar('_')); file += TQChar('-') + TQString::number(entryIt->id()) + html; outputFile.setFileName(file); exporter.setEntries(Data::EntryVec(entryIt)); exporter.setURL(outputFile); exporter.exec(); // no longer need to parse DOM if(parseDOM) { parseDOM = false; exporter.setParseDOM(false); // this is rather stupid, but I'm too lazy to figure out the better way // since we parsed the DOM for the first entry file to grab any // images used in the template, need to resave it so the image links // get written correctly exporter.exec(); } if(j%stepSize == 0) { if(options() & ExportProgress) { ProgressManager::self()->setProgress(this, TQMIN(start+j/stepSize, 99)); } kapp->processEvents(); } } // the images in "pics/" are special data images, copy them always // since the entry files may refer to them, but we don't know that TQStringList dataImages; dataImages << TQString::fromLatin1("checkmark.png"); for(uint i = 1; i <= 10; ++i) { dataImages << TQString::fromLatin1("stars%1.png").arg(i); } KURL dataDir; dataDir.setPath(KGlobal::dirs()->findResourceDir("appdata", TQString::fromLatin1("pics/tellico.png")) + "pics/"); KURL target = fileDir(); target.addPath(TQString::fromLatin1("pics/")); KIO::NetAccess::mkdir(target, m_widget); for(TQStringList::ConstIterator it = dataImages.begin(); it != dataImages.end(); ++it) { dataDir.setFileName(*it); target.setFileName(*it); KIO::NetAccess::copy(dataDir, target, m_widget); } return true; } void HTMLExporter::slotCancel() { m_cancelled = true; } void HTMLExporter::parseDOM(xmlNode* node_) { if(node_ == 0) { myDebug() << "HTMLExporter::parseDOM() - no node" << endl; return; } bool parseChildren = true; if(node_->type == XML_ELEMENT_NODE) { const TQCString nodeName = TQCString(reinterpret_cast(node_->name)).upper(); xmlElement* elem = reinterpret_cast(node_); // to speed up things, check now for nodename if(nodeName == "IMG" || nodeName == "SCRIPT" || nodeName == "LINK") { for(xmlAttribute* attr = elem->attributes; attr; attr = reinterpret_cast(attr->next)) { TQCString attrName = TQCString(reinterpret_cast(attr->name)).upper(); if( (attrName == "SRC" && (nodeName == "IMG" || nodeName == "SCRIPT")) || (attrName == "HREF" && nodeName == "LINK")) { /* (attrName == "BACKGROUND" && (nodeName == "BODY" || nodeName == "TABLE" || nodeName == "TH" || nodeName == "TD"))) */ xmlChar* value = xmlGetProp(node_, attr->name); if(value) { xmlSetProp(node_, attr->name, handleLink(value)); xmlFree(value); } // each node only has one significant attribute, so break now break; } } } else if(nodeName == "STYLE") { // if the first child is a CDATA, use it, otherwise replace complete node xmlNode* nodeToReplace = node_; xmlNode* child = node_->children; if(child && child->type == XML_CDATA_SECTION_NODE) { nodeToReplace = child; } xmlChar* value = xmlNodeGetContent(nodeToReplace); if(value) { xmlNodeSetContent(nodeToReplace, analyzeInternalCSS(value)); xmlFree(value); } // no longer need to parse child text nodes parseChildren = false; } } if(parseChildren) { xmlNode* child = node_->children; while(child) { parseDOM(child); child = child->next; } } } #include "htmlexporter.moc"