/* This file is part of the KDE project Copyright (C) 2001, 2002, 2004 Nicolas GOUTTE This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include "ExportFilter.h" #include "ExportCss.h" TQString HtmlCssWorker::escapeCssIdentifier(const TQString& strText) const { // Reference: section 4.1.3 of the CSS2 recommendation // However most HTML user agents support this section only in a restrictive way, so we cannot use any CSS escape // NOTE: we do not guarantee anymore that the style name is unique! (### FIXME) TQString strReturn; // Taken in the restrictive way, an identifier can only start with a letter. const TQChar qch0(strText[0]); if (((int)qch0<'a' || (int)qch0>'z') && ((int)qch0<'A' || (int)qch0>'Z')) { // Not a letter, so we have to add a prefix strReturn+="kWoRd_"; // The curious spelling is for allowing a HTML import to identfy it and to remove it. // The processing of the character itself is done below } for (uint i=0; i='a') && (ch<='z')) || ((ch>='A') && (ch<='Z')) || ((ch>='0') && (ch<='9')) || (ch=='-') || (ch=='_')) // The underscore is allowed by the CSS2 errata { // Normal allowed characters (without any problem) strReturn+=qch; } else if ((ch<=' ') || (ch>=128 && ch<=160)) // space (breaking or not) and control characters { // CSS2 would allow to escape it but not any HTML user agent supports this strReturn+='_'; } else if ((ch>=161) && (getCodec()->canEncode(qch))) { // Any Unicode character greater or egual to 161 is allowed // except if it cannot be written in the encoding strReturn+=qch; } else // if ch >= 33 && ch <=127 with holes (or not in encoding) { // Either CSS2 does not allow this character unescaped or it is not in the encoding // but a CSS escape would break some HTML user agents (e.g. Mozilla 1.4) // So we have to do our own incompatible cooking. :-( strReturn+="--"; // start our private escape strReturn+=TQString::number(ch,16); strReturn+="--"; // end our private escape } } return strReturn; } TQString HtmlCssWorker::textFormatToCss(const TextFormatting& formatOrigin, const TextFormatting& formatData, const bool force) const { // TODO: as this method comes from the AbiWord filter, // TODO: verify that it is working for HTML // TODO: rename variable formatData TQString strElement; // TODO: rename this variable // Font name TQString fontName = formatData.fontName; if (!fontName.isEmpty() && (force || (formatOrigin.fontName!=formatData.fontName))) { strElement+="font-family: "; if (fontName.tqfind(' ')==-1) strElement+=escapeHtmlText(fontName); else { // If the font name contains a space, it should be quoted. strElement+='\''; strElement+=escapeHtmlText(fontName); strElement+='\''; } // ### TODO: add alternative font names strElement+="; "; } if (force || (formatOrigin.italic!=formatData.italic)) { // Font style strElement+="font-style: "; if ( formatData.italic ) { strElement+="italic"; } else { strElement+="normal"; } strElement+="; "; } if (force || ((formatOrigin.weight>=75)!=(formatData.weight>=75))) { strElement+="font-weight: "; if ( formatData.weight >= 75 ) { strElement+="bold"; } else { strElement+="normal"; } strElement+="; "; } if (force || (formatOrigin.fontSize!=formatData.fontSize)) { const int size=formatData.fontSize; if (size>0) { // We use absolute font sizes. strElement+="font-size: "; strElement+=TQString::number(size,10); strElement+="pt; "; } } if (force || (formatOrigin.fgColor!=formatData.fgColor)) { if ( formatData.fgColor.isValid() ) { // Give colour strElement+="color: "; strElement+=formatData.fgColor.name(); strElement+="; "; } } if (force || (formatOrigin.bgColor!=formatData.bgColor)) { if ( formatData.bgColor.isValid() ) { // Give background colour strElement+="background-color: "; strElement+=formatData.bgColor.name(); strElement+="; "; } } if (force || (formatOrigin.underline!=formatData.underline) || (formatOrigin.strikeout!=formatData.strikeout)) { strElement+="text-decoration: "; if ( formatData.underline ) { strElement+="underline"; } else if ( formatData.strikeout ) { strElement+="line-through"; } else { strElement+="none"; } strElement+="; "; } if (force || (formatOrigin.fontAttribute!=formatData.fontAttribute)) { bool smallcaps=false; strElement+="text-transform: "; if ( formatData.fontAttribute=="uppercase" ) { strElement+="uppercase"; } else if ( formatData.fontAttribute=="lowercase" ) { strElement+="lowercase"; } else if ( formatData.fontAttribute=="smallcaps" ) { strElement+="none"; smallcaps=true; } else { strElement+="none"; } strElement+="; "; // ### TODO: mostly issuing font-variant is not necessary. strElement+="font-variant:"; if (smallcaps) strElement+="small-caps"; else strElement+="normal"; strElement+="; "; } // TODO: As this is the last property, do not put a semi-colon return strElement; } TQString HtmlCssWorker::getStartOfListOpeningTag(const CounterData::Style typeList, bool& ordered) { TQString strResult; switch (typeList) { case CounterData::STYLE_CUSTOMBULLET: // We cannot keep the custom type/style default: { ordered=false; strResult="
    \n"; break; } case CounterData::STYLE_NONE: { ordered=false; strResult="
      \n"; break; } case CounterData::STYLE_CIRCLEBULLET: { ordered=false; strResult="
        \n"; break; } case CounterData::STYLE_SQUAREBULLET: { ordered=false; strResult="
          \n"; break; } case CounterData::STYLE_DISCBULLET: { ordered=false; strResult="
            \n"; break; } case CounterData::STYLE_NUM: { ordered=true; strResult="
              \n"; break; } case CounterData::STYLE_ALPHAB_L: { ordered=true; strResult="
                \n"; break; } case CounterData::STYLE_ALPHAB_U: { ordered=true; strResult="
                  \n"; break; } case CounterData::STYLE_ROM_NUM_L: { ordered=true; strResult="
                    \n"; break; } case CounterData::STYLE_ROM_NUM_U: { ordered=true; strResult="
                      \n"; break; } case CounterData::STYLE_CUSTOM: { // We cannot keep the custom type/style ordered=true; strResult="
                        \n"; break; } } return strResult; } TQString HtmlCssWorker::layoutToCss(const LayoutData& layoutOrigin, const LayoutData& tqlayout, const bool force) const { TQString strLayout; if (force || (layoutOrigin.tqalignment!=tqlayout.tqalignment)) { if ( (tqlayout.tqalignment=="left") || (tqlayout.tqalignment== "right") || (tqlayout.tqalignment=="center") || (tqlayout.tqalignment=="justify")) { strLayout += TQString("text-align:%1; ").tqarg(tqlayout.tqalignment); } else if ( tqlayout.tqalignment=="auto") { // Do nothing, the user-agent should be more intelligent than us. } else { kdWarning(30503) << "Unknown tqalignment: " << tqlayout.tqalignment << endl; } } if ((tqlayout.indentLeft>=0.0) && (force || (layoutOrigin.indentLeft!=tqlayout.indentLeft))) { strLayout += TQString("margin-left:%1pt; ").tqarg(tqlayout.indentLeft); } if ((tqlayout.indentRight>=0.0) && (force || (layoutOrigin.indentRight!=tqlayout.indentRight))) { strLayout += TQString("margin-right:%1pt; ").tqarg(tqlayout.indentRight); } if (force || (layoutOrigin.indentLeft!=tqlayout.indentLeft)) { strLayout += TQString("text-indent:%1pt; ").tqarg(tqlayout.indentFirst); } if ((tqlayout.marginBottom>=0.0) && ( force || ( layoutOrigin.marginBottom != tqlayout.marginBottom ) ) ) { strLayout += TQString("margin-bottom:%1pt; ").tqarg(tqlayout.marginBottom); } if ((tqlayout.marginTop>=0.0) && ( force || ( layoutOrigin.marginTop != tqlayout.marginTop ) ) ) { strLayout += TQString("margin-top:%1pt; ").tqarg(tqlayout.marginTop); } if (force || ( layoutOrigin.lineSpacingType != tqlayout.lineSpacingType ) || ( layoutOrigin.lineSpacing != tqlayout.lineSpacing ) ) { switch ( tqlayout.lineSpacingType ) { case LayoutData::LS_CUSTOM: { // ### TODO: CSS 2 does not known "at-least". #if 0 // We have a custom line spacing (in points) const TQString height ( TQString::number(tqlayout.lineSpacing) ); // ### TODO: rounding? strLayout += "style:line-spacing:"; strLayout += height; strLayout += "pt; "; #endif break; } case LayoutData::LS_SINGLE: { strLayout += "line-height:normal; "; // One break; } case LayoutData::LS_ONEANDHALF: { strLayout += "line-height:150%; "; // One-and-half break; } case LayoutData::LS_DOUBLE: { strLayout += "line-height:200%; "; // Two break; } case LayoutData::LS_MULTIPLE: { const TQString mult ( TQString::number( tqRound( tqlayout.lineSpacing * 100 ) ) ); strLayout += "line-height:"; strLayout += mult; strLayout += "%; "; break; } case LayoutData::LS_FIXED: { // We have a fixed line height (in points) const TQString height ( TQString::number(tqlayout.lineSpacing) ); // ### TODO: rounding? strLayout += "line-height:"; strLayout += height; strLayout += "pt; "; break; } case LayoutData::LS_ATLEAST: { // ### TODO: CSS 2 does not known "at-least". // ### TODO: however draft CCS3 (module 'line') has 'line-stacking-strategy' to tweak this behaviour // We have a at-least line height (in points) const TQString height ( TQString::number(tqlayout.lineSpacing) ); // ### TODO: rounding? strLayout += "line-height:"; strLayout += height; strLayout += "pt; "; break; } default: { kdWarning(30503) << "Unsupported lineSpacingType: " << tqlayout.lineSpacingType << " (Ignoring!)" << endl; break; } } } // TODO: Konqueror/KHTML does not support "text-shadow" if (!force && ( layoutOrigin.shadowDirection == tqlayout.shadowDirection ) && ( layoutOrigin.shadowDistance == tqlayout.shadowDistance ) ) { // Do nothing! } else if ((!tqlayout.shadowDirection) || (!tqlayout.shadowDistance)) { strLayout += "text-shadow:"; strLayout+="none; "; } else { double xDistance,yDistance; const double distance=tqlayout.shadowDistance; switch (tqlayout.shadowDirection) { case 1: // SD_LEFT_UP { xDistance= (-distance); yDistance= (-distance); break; } case 2: // SD_UP { xDistance= 0; yDistance= (-distance); break; } case 3: // SD_RIGHT_UP { xDistance= (distance); yDistance= (-distance); break; } case 4: // SD_RIGHT { xDistance= (distance); yDistance= 0; break; } case 5: // SD_RIGHT_BOTTOM { xDistance= (distance); yDistance= (distance); break; } case 6: // SD_BOTTOM { xDistance= 0; yDistance= (distance); break; } case 7: // SD_LEFT_BOTTOM { xDistance= (-distance); yDistance= (distance); break; } case 8: // SD_LEFT { xDistance= (distance); yDistance= 0; break; } default: { xDistance=0; yDistance=0; break; } } if ( (!xDistance) && (!yDistance) ) { strLayout += "text-shadow:"; strLayout+="none; "; } else { strLayout += "text-shadow:"; strLayout+=TQString("%1 %2pt %3pt; ").tqarg(tqlayout.shadowColor.name()) .tqarg(xDistance,0,'f',0).tqarg(yDistance,0,'f',0); // We do not want any scientific notation or any decimal } } // TODO: borders // This must remain last, as the last property does not have a semi-colon strLayout+=textFormatToCss(layoutOrigin.formatData.text, tqlayout.formatData.text,force); return strLayout; } void HtmlCssWorker::openParagraph(const TQString& strTag, const LayoutData& tqlayout, TQChar::Direction direction) { const LayoutData& styleLayout=m_styleMap[tqlayout.styleName]; *m_streamOut << '<' << strTag; // Opening elements *m_streamOut << " class=\"" << escapeCssIdentifier(tqlayout.styleName); *m_streamOut << "\""; TQString strStyle=layoutToCss(styleLayout,tqlayout,false); if (!strStyle.isEmpty()) { *m_streamOut << " style=\"" << strStyle; if (direction == TQChar::DirRLE) { *m_streamOut << "direction: rtl; tqunicode-bidi: embed; "; } else if (direction == TQChar::DirRLO) { *m_streamOut << "direction: rtl; tqunicode-bidi: override; "; } *m_streamOut<< "\""; } *m_streamOut << ">"; if ( 1==tqlayout.formatData.text.verticalAlignment ) { *m_streamOut << ""; //Subscript } else if ( 2==tqlayout.formatData.text.verticalAlignment ) { *m_streamOut << ""; //Superscript } if ( tqlayout.tqalignment == "center" ) *m_streamOut << "
                        "; } void HtmlCssWorker::closeParagraph(const TQString& strTag, const LayoutData& tqlayout) { if ( 2==tqlayout.formatData.text.verticalAlignment ) { *m_streamOut << ""; //Superscript } else if ( 1==tqlayout.formatData.text.verticalAlignment ) { *m_streamOut << ""; //Subscript } if ( tqlayout.tqalignment == "center" ) *m_streamOut << "
                        "; *m_streamOut << "\n"; } void HtmlCssWorker::openSpan(const FormatData& formatOrigin, const FormatData& format) { *m_streamOut << ""; // close span opening tag if ( 1==format.text.verticalAlignment ) { *m_streamOut << ""; //Subscript } else if ( 2==format.text.verticalAlignment ) { *m_streamOut << ""; //Superscript } } void HtmlCssWorker::closeSpan(const FormatData& formatOrigin, const FormatData& format) { if ( 2==format.text.verticalAlignment ) { *m_streamOut << ""; //Superscript } else if ( 1==format.text.verticalAlignment ) { *m_streamOut << ""; //Subscript } *m_streamOut << ""; } bool HtmlCssWorker::doFullPaperFormat(const int format, const double width, const double height, const int orientation) { TQString strWidth, strHeight, strUnits; KWEFUtil::GetNativePaperFormat(format, strWidth, strHeight, strUnits); if ((strWidth.isEmpty())||(strHeight.isEmpty())||(strUnits.isEmpty())) { // page format is unknown, so we need the size information strUnits="pt"; strWidth=TQString::number(width); strHeight=TQString::number(height); } if (orientation==1) { // Landscape, so we must swap the sizes TQString strTemp(strWidth); strWidth=strHeight; strHeight=strTemp; } m_strPageSize="size: "; m_strPageSize+=strWidth; m_strPageSize+=strUnits; m_strPageSize+=" "; m_strPageSize+=strHeight; m_strPageSize+=strUnits; m_strPageSize+=";"; return true; } bool HtmlCssWorker::doFullPaperBorders (const double top, const double left, const double bottom, const double right) { m_strPaperBorders=" margin-top: "; m_strPaperBorders+=TQString::number(top); m_strPaperBorders+="pt;\n"; m_strPaperBorders+=" margin-left: "; m_strPaperBorders+=TQString::number(left); m_strPaperBorders+="pt;\n"; m_strPaperBorders+=" margin-bottom: "; m_strPaperBorders+=TQString::number(bottom); m_strPaperBorders+="pt;\n"; m_strPaperBorders+=" margin-right: "; m_strPaperBorders+=TQString::number(right); m_strPaperBorders+="pt;\n"; return true; } bool HtmlCssWorker::doOpenStyles(void) { *m_streamOut << "\n"; return true; }