/* -*- mode: C++; tab-width: 4; c-basic-offset: 4; -*- */ /* AbiWord * Copyright (C) 2002 Francis James Franklin * Copyright (C) 2001-2002 AbiSource, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include "ut_debugmsg.h" #include "ut_assert.h" #include "ut_exception.h" #include "ut_string.h" #include "ut_bytebuf.h" #include "ut_base64.h" #include "ut_hash.h" #include "ut_units.h" #include "ut_wctomb.h" #include "ut_path.h" #include "ut_math.h" #include "ut_misc.h" #include "ut_string_class.h" #include "xap_App.h" #include "xap_EncodingManager.h" #include "pt_Types.h" #include "pl_Listener.h" #include "pd_Document.h" #include "pd_Style.h" #include "pp_AttrProp.h" #include "pp_Property.h" #include "pp_PropertyMap.h" #include "px_ChangeRecord.h" #include "px_CR_Object.h" #include "px_CR_Span.h" #include "px_CR_Strux.h" #include "fd_Field.h" #include "fl_AutoNum.h" #include "ie_impexp_HTML.h" #include "ie_exp_HTML.h" #ifdef HTML_DIALOG_OPTIONS #include "xap_Dialog_Id.h" #include "xap_DialogFactory.h" #endif #ifdef HTML_TABLES_SUPPORTED #include "ie_Table.h" #endif #ifndef IE_MIME_XHTML #define IE_MIME_XHTML "application/xhtml+xml" #endif #ifndef IE_MIME_HTML #define IE_MIME_HTML "text/html" #endif #ifndef IE_MIME_CSS #define IE_MIME_CSS "text/css" #endif /*****************************************************************/ /*****************************************************************/ IE_Exp_HTML_Sniffer::IE_Exp_HTML_Sniffer () #ifdef HTML_NAMED_CONSTRUCTORS : IE_ExpSniffer(IE_IMPEXPNAME_XHTML) #endif { // } bool IE_Exp_HTML_Sniffer::recognizeSuffix (const char * szSuffix) { return (!UT_stricmp (szSuffix, ".xhtml") || !(UT_stricmp (szSuffix, ".html")) || !(UT_stricmp (szSuffix, ".htm"))); } UT_Error IE_Exp_HTML_Sniffer::constructExporter (PD_Document * pDocument, IE_Exp ** ppie) { IE_Exp_HTML * p = new IE_Exp_HTML(pDocument); *ppie = p; return UT_OK; } bool IE_Exp_HTML_Sniffer::getDlgLabels (const char ** pszDesc, const char ** pszSuffixList, IEFileType * ft) { *pszDesc = "XHTML 1.0 (.html)"; *pszSuffixList = "*.html"; *ft = getFileType (); return true; } #ifdef HTML_ENABLE_HTML4 // HTML 4 IE_Exp_HTML4_Sniffer::IE_Exp_HTML4_Sniffer () #ifdef HTML_NAMED_CONSTRUCTORS : IE_ExpSniffer(IE_IMPEXPNAME_HTML) #endif { // } bool IE_Exp_HTML4_Sniffer::recognizeSuffix (const char * szSuffix) { return (!(UT_stricmp (szSuffix, ".html")) || !(UT_stricmp (szSuffix, ".htm"))); } UT_Error IE_Exp_HTML4_Sniffer::constructExporter (PD_Document * pDocument, IE_Exp ** ppie) { IE_Exp_HTML * p = new IE_Exp_HTML(pDocument); if (p) p->set_HTML4 (); *ppie = p; return UT_OK; } bool IE_Exp_HTML4_Sniffer::getDlgLabels (const char ** pszDesc, const char ** pszSuffixList, IEFileType * ft) { *pszDesc = "HTML 4.0 (.html, .htm)"; *pszSuffixList = "*.html; *.htm"; *ft = getFileType (); return true; } #endif /* HTML_ENABLE_HTML4 */ #ifdef HTML_ENABLE_PHTML // XHTML w/ PHP instructions for AbiWord Web Docs IE_Exp_PHTML_Sniffer::IE_Exp_PHTML_Sniffer () #ifdef HTML_NAMED_CONSTRUCTORS : IE_ExpSniffer(IE_IMPEXPNAME_PHTML) #endif { // } bool IE_Exp_PHTML_Sniffer::recognizeSuffix (const char * szSuffix) { return (!(UT_stricmp (szSuffix, ".phtml"))); } UT_Error IE_Exp_PHTML_Sniffer::constructExporter (PD_Document * pDocument, IE_Exp ** ppie) { IE_Exp_HTML * p = new IE_Exp_HTML(pDocument); if (p) p->set_PHTML (); *ppie = p; return UT_OK; } bool IE_Exp_PHTML_Sniffer::getDlgLabels(const char ** pszDesc, const char ** pszSuffixList, IEFileType * ft) { *pszDesc = "XHTML+PHP (.phtml)"; *pszSuffixList = "*.phtml"; *ft = getFileType(); return true; } #endif /* HTML_ENABLE_PHTML */ #ifdef HTML_ENABLE_MHTML // Multipart HTML: http://www.rfc-editor.org/rfc/rfc2557.txt IE_Exp_MHTML_Sniffer::IE_Exp_MHTML_Sniffer () #ifdef HTML_NAMED_CONSTRUCTORS : IE_ExpSniffer(IE_IMPEXPNAME_MHTML) #endif { // } bool IE_Exp_MHTML_Sniffer::recognizeSuffix (const char * szSuffix) { return (!(UT_stricmp (szSuffix, ".mht"))); } UT_Error IE_Exp_MHTML_Sniffer::constructExporter (PD_Document * pDocument, IE_Exp ** ppie) { IE_Exp_HTML * p = new IE_Exp_HTML(pDocument); if (p) p->set_MHTML (); *ppie = p; return UT_OK; } bool IE_Exp_MHTML_Sniffer::getDlgLabels(const char ** pszDesc, const char ** pszSuffixList, IEFileType * ft) { *pszDesc = "Multipart HTML (.mht)"; *pszSuffixList = "*.mht"; *ft = getFileType(); return true; } #endif /* HTML_ENABLE_MHTML */ /*****************************************************************/ /*****************************************************************/ /* TODO: is there a better way to do this? */ static UT_UTF8String s_string_to_url (UT_String & str) { UT_UTF8String url; static const char hex[16] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' }; char buf[4]; buf[0] = '%'; buf[3] = 0; const char * ptr = str.c_str (); while (*ptr) { bool isValidPunctuation = false; switch (*ptr) { case '-': // TODO: any others? case '_': case '.': isValidPunctuation = true; break; default: break; } unsigned char u = (unsigned char) *ptr; if (!isalnum (static_cast(u)) && !isValidPunctuation) { buf[1] = hex[(u >> 4) & 0x0f]; buf[2] = hex[ u & 0x0f]; url += buf; } else { buf[2] = (char) *ptr; url += (buf + 2); } ptr++; } return url; } static const char * s_prop_list[] = { "background-color", "transparent", "color", 0, "font-family", 0, "font-size", "medium", "font-style", "normal", "font-variant", "normal", "font-weight", "normal", "height", "auto", "lang", 0, "margin-bottom", "0pt", "margin-left", "0pt", "margin-right", "0pt", "margin-top", "0pt", "orphans", "2", "text-align", 0, "text-decoration", "none", "text-indent", "0in", "vertical-align", "baseline", "widows", "2", "width" "auto", 0, 0 }; static const UT_uint32 s_PropListLen = NrElements(s_prop_list) - 2; /* don't include the zeros */ /*! This function returns true if the given property is a valid CSS property. It is based on the list in pp_Property.cpp, and, as such, is quite brittle. prop_default may be zero on return, indicating that the default is not fixed */ static bool is_CSS (const char * prop_name, const char ** prop_default = 0) { if ( prop_name == 0) return false; if (*prop_name == 0) return false; bool bCSS = false; for (UT_uint32 i = 0; i < s_PropListLen; i += 2) if (!UT_strcmp (prop_name, s_prop_list[i])) { if (prop_default) *prop_default = s_prop_list[i+1]; bCSS = true; break; } return bCSS; } /*! This function copies a string to a new string, removing all the white space in the process. */ static char * s_removeWhiteSpace (const char * text, UT_UTF8String & utf8str) { utf8str = ""; if (text) { char buf[2]; // ick! [TODO ??] buf[1] = 0; const char * ptr = text; while (*ptr) { if (isspace ((int) ((unsigned char) *ptr))) { buf[0] = '_'; } else { buf[0] = *ptr; } utf8str += buf; ptr++; } } return 0; } /*****************************************************************/ /*****************************************************************/ #define IS_TRANSPARENT_COLOR(c) (!UT_strcmp(c, "transparent")) #define BT_NORMAL 1 #define BT_HEADING1 2 #define BT_HEADING2 3 #define BT_HEADING3 4 #define BT_BLOCKTEXT 5 #define BT_PLAINTEXT 6 #define BT_NUMBEREDLIST 7 #define BT_BULLETLIST 8 class s_HTML_Listener; class s_StyleTree : public PL_Listener { private: PD_Document * m_pDocument; // root element of tree only (atm, anyway) s_StyleTree * m_parent; s_StyleTree ** m_list; UT_uint32 m_count; UT_uint32 m_max; bool m_bInUse; UT_UTF8String m_style_name; UT_UTF8String m_class_name; UT_UTF8String m_class_list; PD_Style * m_style; UT_UTF8Hash m_map; s_StyleTree (s_StyleTree * parent, const char * name, PD_Style * style); public: s_StyleTree (PD_Document * pDocument); ~s_StyleTree (); private: bool add (const char * style_name, PD_Style * style); public: bool add (const char * style_name, PD_Document * pDoc); private: void inUse (); public: const s_StyleTree * findAndUse (const char * style_name); const s_StyleTree * find (const char * style_name) const; const s_StyleTree * find (PD_Style * style) const; bool descends (const char * style_name) const; void print (s_HTML_Listener * listener) const; const s_StyleTree * operator[] (UT_uint32 i) const { return (i < m_count) ? m_list[i] : 0; } UT_uint32 count () const { return m_count; } const UT_UTF8String & style_name () const { return m_style_name; } const UT_UTF8String & class_name () const { return m_class_name; } const UT_UTF8String & class_list () const { return m_class_list; } const UT_UTF8String * lookup (const UT_UTF8String & prop_name); private: void styleCheck (PT_AttrPropIndex api); public: /* implementation of PL_Listener */ bool populate (PL_StruxFmtHandle sfh, const PX_ChangeRecord * pcr); bool populateStrux (PL_StruxDocHandle sdh, const PX_ChangeRecord * pcr, PL_StruxFmtHandle * psfh); bool change (PL_StruxFmtHandle sfh, const PX_ChangeRecord * pcr); bool insertStrux (PL_StruxFmtHandle sfh, const PX_ChangeRecord * pcr, PL_StruxDocHandle sdh, PL_ListenerId lid, void (*pfnBindHandles) (PL_StruxDocHandle sdhNew, PL_ListenerId lid, PL_StruxFmtHandle sfhNew)); bool signal (UT_uint32 iSignal); }; class s_HTML_Listener : public PL_Listener { public: s_HTML_Listener (PD_Document * pDocument, IE_Exp_HTML * pie, bool bClipBoard, bool bTemplateBody, const XAP_Exp_HTMLOptions * exp_opt, s_StyleTree * style_tree); ~s_HTML_Listener (); bool populate (PL_StruxFmtHandle sfh, const PX_ChangeRecord * pcr); bool populateStrux (PL_StruxDocHandle sdh, const PX_ChangeRecord * pcr, PL_StruxFmtHandle * psfh); bool change (PL_StruxFmtHandle sfh, const PX_ChangeRecord * pcr); bool insertStrux (PL_StruxFmtHandle sfh, const PX_ChangeRecord * pcr, PL_StruxDocHandle sdh, PL_ListenerId lid, void (*pfnBindHandles) (PL_StruxDocHandle sdhNew, PL_ListenerId lid, PL_StruxFmtHandle sfhNew)); bool signal (UT_uint32 iSignal); void startEmbeddedStrux(void); private: void _outputBegin (PT_AttrPropIndex api); void _outputEnd (); bool _openStyleSheet (UT_UTF8String & css_path); void _closeStyleSheet (); void _outputStyles (const PP_AttrProp * pAP); void _openSection (PT_AttrPropIndex api); void _closeSection (void); void _openTag (PT_AttrPropIndex api, PL_StruxDocHandle sdh); void _closeTag (void); void _closeSpan (void); void _openSpan (PT_AttrPropIndex api); #ifdef HTML_TABLES_SUPPORTED void _openTable (PT_AttrPropIndex api); void _closeTable (); void _openRow (PT_AttrPropIndex api); void _openCell (PT_AttrPropIndex api); void _closeCell (); #endif void _outputData (const UT_UCSChar * p, UT_uint32 length); bool _inherits (const char * style, const char * from); void _storeStyles (void); void _writeImage (const UT_ByteBuf * pByteBuf, const UT_String & imagedir, const UT_String & filename); void _writeImageBase64 (const UT_ByteBuf * pByteBuf); void _handleImage (PT_AttrPropIndex api); void _handlePendingImages (); void _handleField (const PX_ChangeRecord_Object * pcro, PT_AttrPropIndex api); void _handleHyperlink (PT_AttrPropIndex api); void _handleBookmark (PT_AttrPropIndex api); #ifdef HTML_META_SUPPORTED void _handleMetaTag (const char * key, UT_UTF8String & value); void _handleMeta (); #endif PD_Document * m_pDocument; IE_Exp_HTML * m_pie; bool m_bClipBoard; bool m_bTemplateBody; const XAP_Exp_HTMLOptions * m_exp_opt; s_StyleTree * m_style_tree; inline bool get_HTML4 () const { return m_exp_opt->bIs4; } inline bool get_PHTML () const { return m_exp_opt->bIsAbiWebDoc; } inline bool get_Declare_XML () const { return m_exp_opt->bDeclareXML && !m_exp_opt->bIs4; } inline bool get_Allow_AWML () const { return m_exp_opt->bAllowAWML && !m_exp_opt->bIs4; } inline bool get_Embed_CSS () const { return m_exp_opt->bEmbedCSS; } inline bool get_Embed_Images () const { return m_exp_opt->bEmbedImages; } inline bool get_Multipart () const { return m_exp_opt->bMultipart; } bool m_bInSection; bool m_bInBlock; bool m_bInTList; bool m_bInTListItem; bool m_bInSpan; bool m_bNextIsSpace; bool m_bWroteText; bool m_bFirstWrite; bool m_bQuotedPrintable; #ifdef HTML_TABLES_SUPPORTED ie_Table m_TableHelper; #endif // Need to look up proper type, and place to stick #defines... UT_uint32 m_iBlockType; // BT_* UT_uint32 m_iListDepth; // 0 corresponds to not in a list UT_NumberStack m_utsListType; UT_uint32 m_iImgCnt; UT_Wctomb m_wmctomb; enum WhiteSpace { ws_None = 0, ws_Pre = 1, ws_Post = 2, ws_Both = 3 }; /* low-level; these may use m_utf8_0 but not m_utf8_1 */ void tagRaw (UT_UTF8String & content); void tagNewIndent (UT_uint32 extra = 0); void tagOpenClose (const UT_UTF8String & content, bool suppress, WhiteSpace ws = ws_Both); void tagOpen (UT_uint32 tagID, const UT_UTF8String & content, WhiteSpace ws = ws_Both); void tagClose (UT_uint32 tagID, const UT_UTF8String & content, WhiteSpace ws = ws_Both); void tagClose (UT_uint32 tagID); void tagOpenBroken (const UT_UTF8String & content, WhiteSpace ws = ws_Pre); void tagCloseBroken (const UT_UTF8String & content, bool suppress, WhiteSpace ws = ws_Post); UT_uint32 tagTop (); void tagPop (); void tagPI (const char * target, const UT_UTF8String & content); void tagComment (const UT_UTF8String & content); void tagCommentOpen (); void tagCommentClose (); void styleIndent (); public: void styleOpen (const UT_UTF8String & rule); void styleClose (); void styleNameValue (const char * name, const UT_UTF8String & value); private: void styleText (const UT_UTF8String & content); void textTrusted (const UT_UTF8String & text); void textUntrusted (const char * text); void multiHeader (const UT_UTF8String & title); void multiBoundary (bool end = false); void multiField (const char * name, const UT_UTF8String & value); void multiBreak (); /* for emulation of lists using tables: */ fl_AutoNum * tlistLookup (UT_uint32 listid); void tlistNumber (fl_AutoNum * list, PL_StruxDocHandle sdh); void tlistPush (); void tlistPushItem (const XML_Char * szListID, const XML_Char * szMarginLeft, PL_StruxDocHandle sdh); void tlistPop (); void tlistPopItem (); UT_uint32 listDepth (); UT_uint32 listType (); void listPush (UT_uint32 type, const char * ClassName); void listPop (); void listPopToDepth (UT_uint32 depth); bool compareStyle (const char * key, const char * value); void _fillColWidthsVector(); void _setCellWidthInches(void); /* temporary strings; use with extreme caution */ UT_UTF8String m_utf8_0; // low-level UT_UTF8String m_utf8_1; // intermediate UT_UTF8String m_utf8_span; // span tag-string cache UT_UTF8String m_utf8_style; // current block style const s_StyleTree * m_StyleTreeInline; // current inline style tree, if any const s_StyleTree * m_StyleTreeBlock; // current block style tree, if any const s_StyleTree * m_StyleTreeBody; // document default style tree, if any const PP_AttrProp * m_pAPStyles; UT_UTF8String m_utf8_css_path; // Multipart HTML: cache for content location UT_NumberStack m_tagStack; UT_uint32 m_styleIndent; FILE * m_fdCSS; UT_uint32 m_tlistIndent; UT_uint32 m_tlistListID; List_Type m_tlistType; UT_StringPtrMap m_SavedURLs; bool m_bIgnoreTillEnd; PT_DocPosition m_iEmbedStartPos; double m_dPageWidthInches; double m_dSecLeftMarginInches; double m_dSecRightMarginInches; double m_dCellWidthInches; UT_Vector m_vecDWidths; }; /*****************************************************************/ /*****************************************************************/ bool s_HTML_Listener::compareStyle (const char * key, const char * value) { /* require both key & value to be non-empty strings */ if (( key == 0) || ( value == 0)) return false; if ((*key == 0) || (*value == 0)) return false; UT_UTF8String css_name(key); const UT_UTF8String * css_value = 0; if (m_StyleTreeInline) css_value = const_cast(m_StyleTreeInline)->lookup (css_name); if (m_StyleTreeBlock && !css_value) css_value = const_cast(m_StyleTreeBlock)->lookup (css_name); if (m_StyleTreeBody && !css_value) css_value = const_cast(m_StyleTreeBody)->lookup (css_name); if (!css_value) return false; return (*css_value == value); } void s_HTML_Listener::tagRaw (UT_UTF8String & content) { #ifdef HTML_ENABLE_MHTML if (m_bQuotedPrintable) content.escapeMIME (); #endif // fputs (content.utf8_str (), stdout); m_pie->write (content.utf8_str (), content.byteLength ()); } void s_HTML_Listener::tagNewIndent (UT_uint32 extra) { m_utf8_0 = ""; UT_uint32 depth = m_tagStack.getDepth () + extra; UT_uint32 i; // MSVC DOES NOT SUPPORT CURRENT for SCOPING RULES!!! for (i = 0; i < (depth >> 3); i++) m_utf8_0 += "\t"; for (i = 0; i < (depth & 7); i++) m_utf8_0 += " "; } /* NOTE: We terminate each line with a \r\n sequence to make IE think * that our XHTML is really HTML. This is a stupid IE bug. * Sorry. */ void s_HTML_Listener::tagOpenClose (const UT_UTF8String & content, bool suppress, WhiteSpace ws) { if (ws & ws_Pre) tagNewIndent (); else m_utf8_0 = ""; m_utf8_0 += "<"; m_utf8_0 += content; if (suppress) m_utf8_0 += ">"; else m_utf8_0 += " />"; if (ws & ws_Post) m_utf8_0 += "\r\n"; tagRaw (m_utf8_0); } void s_HTML_Listener::tagOpen (UT_uint32 tagID, const UT_UTF8String & content, WhiteSpace ws) { if (ws & ws_Pre) tagNewIndent (); else m_utf8_0 = ""; m_utf8_0 += "<"; m_utf8_0 += content; m_utf8_0 += ">"; if (ws & ws_Post) m_utf8_0 += "\r\n"; tagRaw (m_utf8_0); m_tagStack.push (tagID); } void s_HTML_Listener::tagClose (UT_uint32 tagID, const UT_UTF8String & content, WhiteSpace ws) { tagClose (tagID); if (ws & ws_Pre) tagNewIndent (); else m_utf8_0 = ""; m_utf8_0 += ""; if (ws & ws_Post) m_utf8_0 += "\r\n"; tagRaw (m_utf8_0); } void s_HTML_Listener::tagClose (UT_uint32 tagID) { UT_uint32 i = 0; m_tagStack.pop ((UT_sint32*)&i); if (i == tagID) return; UT_DEBUGMSG(("WARNING: possible tag mis-match in XHTML output!\n")); } /* use with *extreme* caution! (this is used by images with data-URLs) */ void s_HTML_Listener::tagOpenBroken (const UT_UTF8String & content, WhiteSpace ws) { if (ws & ws_Pre) { tagNewIndent (); m_utf8_0 += "<"; } else m_utf8_0 = "<"; m_utf8_0 += content; tagRaw (m_utf8_0); } /* use with *extreme* caution! (this is used by images with data-URLs) */ void s_HTML_Listener::tagCloseBroken (const UT_UTF8String & content, bool suppress, WhiteSpace ws) { m_utf8_0 = content; if (suppress) m_utf8_0 += " >"; else m_utf8_0 += " />"; if (ws & ws_Post) m_utf8_0 += "\r\n"; tagRaw (m_utf8_0); } UT_uint32 s_HTML_Listener::tagTop () { UT_sint32 i = 0; if (m_tagStack.viewTop (i)) return (UT_uint32)i; return 0; } void s_HTML_Listener::tagPop () { switch (tagTop ()) { case TT_TD: { m_utf8_1 = "td"; tagClose (TT_TD, m_utf8_1); } break; case TT_TR: { m_utf8_1 = "tr"; tagClose (TT_TR, m_utf8_1); } break; case TT_TBODY: { m_utf8_1 = "tbody"; tagClose (TT_TBODY, m_utf8_1); } break; case TT_TABLE: { m_utf8_1 = "table"; tagClose (TT_TABLE, m_utf8_1); } break; case TT_DIV: { m_utf8_1 = "div"; tagClose (TT_DIV, m_utf8_1); } break; case TT_P: { m_utf8_1 = "p"; tagClose(TT_P,m_utf8_1); } break; case TT_H1: { m_utf8_1 = "h1"; tagClose(TT_H1,m_utf8_1); } break; case TT_H2: { m_utf8_1 = "h2"; tagClose(TT_H2,m_utf8_1); } break; case TT_H3: { m_utf8_1 = "h3"; tagClose(TT_H3,m_utf8_1); } break; case TT_H4: { m_utf8_1 = "h4"; tagClose(TT_H4,m_utf8_1); } break; case TT_SPAN: { m_utf8_1 = "span"; tagClose (TT_SPAN, m_utf8_1); } break; default: { UT_DEBUGMSG(("tagPop: unhandled tag closure! %d\n",tagTop())); m_utf8_1 ="error - not handled"; } break; } } void s_HTML_Listener::tagPI (const char * target, const UT_UTF8String & content) { tagNewIndent (); m_utf8_0 += "\r\n"; tagRaw (m_utf8_0); } void s_HTML_Listener::tagComment (const UT_UTF8String & content) { tagNewIndent (); m_utf8_0 += "\r\n"; tagRaw (m_utf8_0); } void s_HTML_Listener::tagCommentOpen () { tagNewIndent (); m_utf8_0 += "\r\n"; tagRaw (m_utf8_0); } void s_HTML_Listener::styleIndent () { m_utf8_0 = ""; for (UT_uint32 i = 0; i < m_styleIndent; i++) m_utf8_0 += "\t"; } void s_HTML_Listener::styleOpen (const UT_UTF8String & rule) { styleIndent (); m_utf8_0 += rule; m_utf8_0 += " {\r\n"; if (m_fdCSS) fwrite (m_utf8_0.utf8_str (), 1, m_utf8_0.byteLength (), m_fdCSS); else tagRaw (m_utf8_0); m_styleIndent++; } void s_HTML_Listener::styleClose () { if (m_styleIndent == 0) { UT_DEBUGMSG(("WARNING: CSS style group over-closing!\n")); return; } m_styleIndent--; styleIndent (); m_utf8_0 += "}\r\n"; if (m_fdCSS) fwrite (m_utf8_0.utf8_str (), 1, m_utf8_0.byteLength (), m_fdCSS); else tagRaw (m_utf8_0); } void s_HTML_Listener::styleNameValue (const char * name, const UT_UTF8String & value) { styleIndent (); m_utf8_0 += name; m_utf8_0 += ": "; m_utf8_0 += value; m_utf8_0 += ";\r\n"; if (m_fdCSS) fwrite (m_utf8_0.utf8_str (), 1, m_utf8_0.byteLength (), m_fdCSS); else tagRaw (m_utf8_0); } void s_HTML_Listener::styleText (const UT_UTF8String & content) { if (m_fdCSS) fwrite (content.utf8_str (), 1, content.byteLength (), m_fdCSS); else { m_utf8_0 = content; tagRaw (m_utf8_0); } } void s_HTML_Listener::textTrusted (const UT_UTF8String & text) { if (text.byteLength ()) { m_utf8_0 = text; tagRaw (m_utf8_0); m_bWroteText = true; } } void s_HTML_Listener::textUntrusted (const char * text) { if ( text == 0) return; if (*text == 0) return; m_utf8_0 = ""; char buf[2]; buf[1] = 0; const char * ptr = text; while (*ptr) { if ((*ptr & 0x7f) == *ptr) // ASCII { switch (*ptr) { case '<': m_utf8_0 += "<"; break; case '>': m_utf8_0 += ">"; break; case '&': m_utf8_0 += "&"; break; default: buf[0] = *ptr; m_utf8_0 += buf; break; } } /* TODO: translate non-ASCII characters */ ptr++; } if (m_utf8_0.byteLength ()) tagRaw (m_utf8_0); } static const char * s_boundary = "AbiWord_multipart_boundary____________"; void s_HTML_Listener::multiHeader (const UT_UTF8String & title) { m_utf8_1 = ""; multiField ("From", m_utf8_1); multiField ("Subject", title); time_t tim = time (NULL); struct tm * pTime = localtime (&tim); char timestr[64]; strftime (timestr, 63, "%a, %d %b %Y %H:%M:%S +0100", pTime); // hmm, hard-code time zone timestr[63] = 0; m_utf8_1 = timestr; multiField ("Date", m_utf8_1); m_utf8_1 = "1.0"; multiField ("MIME-Version", m_utf8_1); m_utf8_1 = "multipart/related;\r\n\tboundary=\""; m_utf8_1 += s_boundary; m_utf8_1 += "\";\r\n\ttype=\""; if (get_HTML4 ()) m_utf8_1 += IE_MIME_HTML; else m_utf8_1 += IE_MIME_XHTML; m_utf8_1 += "\""; multiField ("Content-Type", m_utf8_1); multiBoundary (); if (get_HTML4 ()) m_utf8_1 = IE_MIME_HTML; else m_utf8_1 = IE_MIME_XHTML; m_utf8_1 += "; charset=\"UTF-8\""; multiField ("Content-Type", m_utf8_1); m_utf8_1 = "quoted-printable"; multiField ("Content-Transfer-Encoding", m_utf8_1); multiBreak (); m_bQuotedPrintable = true; } void s_HTML_Listener::multiBoundary (bool end) { m_utf8_0 = "\r\n--"; m_utf8_0 += s_boundary; if (end) m_utf8_0 += "--\r\n"; else m_utf8_0 += "\r\n"; // fputs (m_utf8_0.utf8_str (), stdout); m_pie->write (m_utf8_0.utf8_str (), m_utf8_0.byteLength ()); } void s_HTML_Listener::multiField (const char * name, const UT_UTF8String & value) { m_utf8_0 = name; m_utf8_0 += ": "; m_utf8_0 += value; m_utf8_0 += "\r\n"; // fputs (m_utf8_0.utf8_str (), stdout); m_pie->write (m_utf8_0.utf8_str (), m_utf8_0.byteLength ()); } void s_HTML_Listener::multiBreak () { m_utf8_0 = "\r\n"; // fputs (m_utf8_0.utf8_str (), stdout); m_pie->write (m_utf8_0.utf8_str (), m_utf8_0.byteLength ()); } /* intermediate methods */ static const char * s_DTD_XHTML = "!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\""; static const char * s_DTD_HTML4 = "!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\""; static const char * s_Delimiter = "================================================================================ "; static const char * s_Header[3] = { "This HTML file was created by AbiWord. ", "AbiWord is a free, Open Source word processor. ", "More information about AbiWord is available at http://www.abisource.com " }; void s_HTML_Listener::_outputBegin (PT_AttrPropIndex api) { if (m_bTemplateBody) { m_bFirstWrite = false; return; } UT_UTF8String titleProp; #ifdef HTML_META_SUPPORTED m_pDocument->getMetaDataProp (PD_META_KEY_TITLE, titleProp); if (titleProp.byteLength () == 0 && m_pie->getFileName () != NULL) titleProp = UT_basename(m_pie->getFileName ()); #else titleProp = UT_basename(m_pie->getFileName ()); #endif if (get_Multipart ()) multiHeader (titleProp); /* print XML header */ if (!get_HTML4 ()) { if (get_Declare_XML ()) { m_utf8_1 = "version=\"1.0\""; tagPI ("xml", m_utf8_1); } m_utf8_1 = s_DTD_XHTML; tagOpenClose (m_utf8_1, true); } else { m_utf8_1 = s_DTD_HTML4; tagOpenClose (m_utf8_1, true); } /* open root element, i.e. ; namespace it if XHTML */ m_utf8_1 = "html"; if (!get_HTML4 ()) { m_utf8_1 += " xmlns=\"http://www.w3.org/1999/xhtml\""; if (get_Allow_AWML ()) m_utf8_1 += " xmlns:awml=\"http://www.abisource.com/awml.dtd\""; } tagOpen (TT_HTML, m_utf8_1); /* start section of HTML document */ m_utf8_1 = "head"; tagOpen (TT_HEAD, m_utf8_1); /* print header comment * we insert them that let, because IE6 expect to find root within * 6 lines. */ const UT_UTF8String delimiter(s_Delimiter); tagComment (delimiter); for (UT_uint32 hdri = 0; hdri < 3; hdri++) { m_utf8_1 = s_Header[hdri]; tagComment (m_utf8_1); } tagComment (delimiter); /* we add a meta tag describing the document's charset as UTF-8 * even with XHTML because Safari and Camino fail to recognize * charset. This still validate W3C. */ m_utf8_1 = "meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\""; tagOpenClose (m_utf8_1, get_HTML4 ()); /* set page's title in browser */ m_utf8_1 = "title"; tagOpen (TT_TITLE, m_utf8_1, ws_Pre); #ifdef HTML_META_SUPPORTED textTrusted (titleProp.escapeXML ()); // TODO: back-port this method? #else textUntrusted (titleProp.utf8_str ()); #endif tagClose (TT_TITLE, m_utf8_1, ws_Post); #ifdef HTML_META_SUPPORTED /* write out our metadata properties */ _handleMeta (); #endif if (!get_PHTML ()) { const PP_AttrProp * pAP = 0; bool bHaveProp = m_pDocument->getAttrProp (api, &pAP); if (bHaveProp && pAP) { _outputStyles (pAP); if (!get_Embed_CSS ()) m_pAPStyles = pAP; } } if (get_PHTML ()) { m_utf8_1 = "\r\n include($DOCUMENT_ROOT.'/x-header.php');\r\n "; tagPI ("php", m_utf8_1); } /* end section of HTML document */ m_utf8_1 = "head"; tagClose (TT_HEAD, m_utf8_1); /* start section of HTML document */ m_utf8_1 = "body"; tagOpen (TT_BODY, m_utf8_1); if (get_PHTML ()) { m_utf8_1 = "\r\n include($DOCUMENT_ROOT.'/x-page-begin.php');\r\n "; tagPI ("php", m_utf8_1); } m_bFirstWrite = false; } void s_HTML_Listener::_outputEnd () { if (m_bInBlock) _closeTag (); while (true) { UT_uint32 top = tagTop (); if ((top == TT_BODY) || !top) break; tagPop (); } if (m_bTemplateBody) return; if (get_PHTML ()) { m_utf8_1 = "\r\n include($DOCUMENT_ROOT.'/x-page-end.php');\r\n "; tagPI ("php", m_utf8_1); } /* end section of HTML document */ m_utf8_1 = "body"; tagClose (TT_BODY, m_utf8_1); /* end section of HTML document */ m_utf8_1 = "html"; tagClose (TT_HTML, m_utf8_1); if (get_Multipart ()) { m_bQuotedPrintable = false; if (m_pAPStyles) { _outputStyles (m_pAPStyles); m_bQuotedPrintable = false; } _handlePendingImages (); multiBoundary (true); } } bool s_HTML_Listener::_openStyleSheet (UT_UTF8String & css_path) { UT_String imagebasedir = UT_basename (m_pie->getFileName ()); imagebasedir += "_files"; UT_String imagedir = m_pie->getFileName (); imagedir += "_files"; m_pDocument->getApp()->makeDirectory (imagedir.c_str (), 0750); imagedir += "/style.css"; if (m_utf8_css_path.byteLength ()) // Multipart HTML: style-sheet segment { multiBoundary (); m_utf8_1 = IE_MIME_CSS; m_utf8_1 += "; charset=\"UTF-8\""; multiField ("Content-Type", m_utf8_1); multiField ("Content-Location", m_utf8_css_path); m_utf8_1 = "quoted-printable"; multiField ("Content-Transfer-Encoding", m_utf8_1); multiBreak (); m_bQuotedPrintable = true; } else if (!get_Multipart ()) { m_fdCSS = fopen (imagedir.c_str (), "wb"); if (m_fdCSS == NULL) return false; } css_path = s_string_to_url (imagebasedir); css_path += "/style.css"; return true; } void s_HTML_Listener::_closeStyleSheet () { if (m_fdCSS) fclose (m_fdCSS); m_fdCSS = 0; } void s_HTML_Listener::_outputStyles (const PP_AttrProp * pAP) { /* some cascading style rules */ const XML_Char * szName = 0; const XML_Char * szValue = 0; if (get_Embed_CSS ()) { m_utf8_1 = "style type=\"text/css\""; tagOpen (TT_STYLE, m_utf8_1); tagCommentOpen (); } else { UT_UTF8String css_path; if (!_openStyleSheet (css_path)) return; if (!get_Multipart () || (m_utf8_css_path.byteLength () == 0)) { m_utf8_1 = "link href=\""; m_utf8_1 += css_path; m_utf8_1 += "\" rel=\"stylesheet\" type=\"text/css\""; tagOpenClose (m_utf8_1, get_HTML4 ()); if (get_Multipart ()) { m_utf8_css_path = css_path; return; } } /* first line of style sheet is an encoding declaration */ m_utf8_1 = "@charset \"UTF-8\";\r\n\r\n"; styleText (m_utf8_1); } /* global page styles refer to the tag */ PD_Style * pStyle = 0; m_pDocument->getStyle ("Normal", &pStyle); if (pAP && pStyle) { /* Add normal styles to any descendent of the body for global effect * * (I think @ rules are supposed to precede non-@ rules) */ m_utf8_1 = "@media print"; styleOpen (m_utf8_1); m_utf8_1 = "body"; styleOpen (m_utf8_1); szValue = PP_evalProperty ("page-margin-top", 0, 0, pAP, m_pDocument, true); m_utf8_1 = static_cast(szValue); styleNameValue ("padding-top", m_utf8_1); szValue = PP_evalProperty ("page-margin-bottom", 0, 0, pAP, m_pDocument, true); m_utf8_1 = static_cast(szValue); styleNameValue ("padding-bottom", m_utf8_1); szValue = PP_evalProperty ("page-margin-left", 0, 0, pAP, m_pDocument, true); m_utf8_1 = static_cast(szValue); styleNameValue ("padding-left", m_utf8_1); szValue = PP_evalProperty ("page-margin-right", 0, 0, pAP, m_pDocument, true); m_utf8_1 = static_cast(szValue); styleNameValue ("padding-right", m_utf8_1); styleClose (); // end of: body { } styleClose (); // end of: @media print { } m_utf8_1 = "body"; styleOpen (m_utf8_1); for (UT_uint32 i = 0; i < pStyle->getPropertyCount (); i++) { pStyle->getNthProperty (i, szName, szValue); if (( szName == 0) || ( szValue == 0)) continue; // paranoid? moi? if ((*szName == 0) || (*szValue == 0)) continue; if (strstr (szName, "margin")) continue; if (!is_CSS (reinterpret_cast(szName))) continue; if (UT_strcmp (szName, "font-family") == 0) { if ((UT_strcmp (szValue, "serif") == 0) || (UT_strcmp (szValue, "sans-serif") == 0) || (UT_strcmp (szValue, "cursive") == 0) || (UT_strcmp (szValue, "fantasy") == 0) || (UT_strcmp (szValue, "monospace") == 0)) { m_utf8_1 = static_cast(szValue); } else { m_utf8_1 = "'"; m_utf8_1 += static_cast(szValue); m_utf8_1 += "'"; } } else if (UT_strcmp (szName, "color") == 0) { if (IS_TRANSPARENT_COLOR (szValue)) continue; m_utf8_1 = "#"; m_utf8_1 += static_cast(szValue); } else m_utf8_1 = static_cast(szValue); styleNameValue (szName, m_utf8_1); } szValue = PP_evalProperty ("background-color", 0, 0, pAP, m_pDocument, true); if(!IS_TRANSPARENT_COLOR (szValue)) { m_utf8_1 = "#"; m_utf8_1 += static_cast(szValue); styleNameValue ("background-color", m_utf8_1); } styleClose (); // end of: body { } #ifdef HTML_TABLES_SUPPORTED m_utf8_1 = "table"; styleOpen (m_utf8_1); m_utf8_1 = "100%"; styleNameValue ("width", m_utf8_1); styleClose (); // end of: table { } m_utf8_1 = "td"; styleOpen (m_utf8_1); m_utf8_1 = "collapse"; styleNameValue ("border-collapse", m_utf8_1); m_utf8_1 = "left"; styleNameValue ("text-align", m_utf8_1); m_utf8_1 = "top"; styleNameValue ("vertical-align", m_utf8_1); styleClose (); // end of: td { } #endif /* HTML_TABLES_SUPPORTED */ } m_style_tree->print (this); if (get_Embed_CSS ()) { tagCommentClose (); m_utf8_1 = "style"; tagClose (TT_STYLE, m_utf8_1); } else _closeStyleSheet (); } /*! * This closes open section tags and starts new one for embedded struxes */ void s_HTML_Listener::startEmbeddedStrux(void) { if (m_bInSection) _closeSection (); m_utf8_1 = "div"; tagOpen (TT_DIV, m_utf8_1); m_bInSection = true; } void s_HTML_Listener::_openSection (PT_AttrPropIndex api) { if (m_bFirstWrite) _outputBegin (api); if (m_bInSection) _closeSection (); m_utf8_1 = "div"; tagOpen (TT_DIV, m_utf8_1); m_dPageWidthInches = m_pDocument->m_docPageSize.Width(DIM_IN); const PP_AttrProp* pSectionAP = NULL; m_pDocument->getAttrProp(api, &pSectionAP); const char* pszLeftMargin = NULL; const char* pszRightMargin = NULL; pSectionAP->getProperty("page-margin-left", (const XML_Char *&)pszLeftMargin); pSectionAP->getProperty("page-margin-right", (const XML_Char *&)pszRightMargin); if(pszLeftMargin && pszLeftMargin[0]) { m_dSecLeftMarginInches = UT_convertToInches(pszLeftMargin); } else { m_dSecLeftMarginInches = 1.0; } if(pszRightMargin && pszRightMargin[0]) { m_dSecRightMarginInches = UT_convertToInches(pszLeftMargin); } else { m_dSecRightMarginInches = 1.0; } m_bInSection = true; } void s_HTML_Listener::_closeSection (void) { if (m_bInSection && (tagTop () == TT_DIV)) { m_utf8_1 = "div"; tagClose (TT_DIV, m_utf8_1); } m_bInSection = false; } /*! This function returns true if the name of the PD_Style which style is based on, without whitespace, is the same as `from`, and otherwise returns false. */ bool s_HTML_Listener::_inherits (const char * style, const char * from) { if ((style == 0) || (from == 0)) return false; bool bret = false; PD_Style * pStyle = 0; if (m_pDocument->getStyle (style, &pStyle)) if (pStyle) { PD_Style * pBasedOn = pStyle->getBasedOn (); if (pBasedOn) { /* The name of the style is stored in the PT_NAME_ATTRIBUTE_NAME * attribute within the style */ const XML_Char * szName = 0; pBasedOn->getAttribute (PT_NAME_ATTRIBUTE_NAME, szName); if (szName) { /* careful!! */ s_removeWhiteSpace (static_cast(szName), m_utf8_0); if (m_utf8_0.utf8_str ()) bret = (UT_strcmp (from, m_utf8_0.utf8_str ()) == 0); } } } return bret; } fl_AutoNum * s_HTML_Listener::tlistLookup (UT_uint32 listid) { fl_AutoNum * pMatch = 0; fl_AutoNum * pAutoNum = 0; for (UT_uint32 k = 0; (m_pDocument->enumLists (k, &pAutoNum)); k++) if (pAutoNum) if (pAutoNum->getID () == listid) { pMatch = pAutoNum; break; } return pMatch; } void s_HTML_Listener::tlistNumber (fl_AutoNum * list, PL_StruxDocHandle sdh) { fl_AutoNum * parent = list->getParent (); if (parent) { tlistNumber (parent, list->getParentItem ()); textUntrusted ("."); } UT_uint32 value = list->getValue (sdh); switch (m_tlistType) { default: case NUMBERED_LIST: { char buf[16]; sprintf (buf, "%lu", static_cast(value)); textUntrusted (buf); } break; case LOWERCASE_LIST: textUntrusted (fl_AutoNum::dec2ascii (value - 1, 97)); break; case UPPERCASE_LIST: textUntrusted (fl_AutoNum::dec2ascii (value - 1, 65)); break; case LOWERROMAN_LIST: textUntrusted (fl_AutoNum::dec2roman (value, true)); break; case UPPERROMAN_LIST: textUntrusted (fl_AutoNum::dec2roman (value, false)); break; } } void s_HTML_Listener::tlistPush () { if (m_bInTList) tlistPop (); /* list item, of any depth, is represented as a table with one * row and two columns: */ m_utf8_1 = "table cols=\"2\" width=\"100%\""; tagOpen (TT_TABLE, m_utf8_1); if (IS_BULLETED_LIST_TYPE (m_tlistType)) { m_utf8_1 = "tr"; tagOpen (TT_TR, m_utf8_1); unsigned long indent = (m_tlistIndent > 18) ? (m_tlistIndent - 9) : 9; char buf[16]; sprintf (buf, "%lu", indent); m_utf8_1 = "td width=\""; m_utf8_1 += buf; m_utf8_1 += "\""; tagOpen (TT_TD, m_utf8_1, ws_Pre); m_utf8_1 = " "; textTrusted (m_utf8_1); m_utf8_1 = "td"; tagClose (TT_TD, m_utf8_1, ws_Post); tagOpen (TT_TD, m_utf8_1); m_utf8_1 = "ul style=\"list-style-type: "; switch (m_tlistType) { case BULLETED_LIST: case TRIANGLE_LIST: case IMPLIES_LIST: case HAND_LIST: m_utf8_1 += "disc"; break; case DASHED_LIST: case DIAMOND_LIST: case TICK_LIST: case HEART_LIST: m_utf8_1 += "circle"; break; case SQUARE_LIST: case STAR_LIST: case BOX_LIST: default: m_utf8_1 += "square"; break; } m_utf8_1 += "\""; tagOpen (TT_UL, m_utf8_1); } m_bInTList = true; } void s_HTML_Listener::tlistPushItem (const XML_Char * szListID, const XML_Char * szMarginLeft, PL_StruxDocHandle sdh) { if (m_bInTListItem) tlistPopItem (); if (szListID == 0) return; unsigned long listid; if (sscanf (szListID, "%lu", &listid) != 1) return; fl_AutoNum * list = tlistLookup (listid); if (list == 0) { UT_DEBUGMSG(("list lookup failed!\n")); return; } unsigned long indent = 18; if (szMarginLeft) { double dpts = UT_convertToPoints (szMarginLeft); if (dpts > 18) indent = static_cast(dpts); } if (m_bInTList) if ((indent != static_cast(m_tlistIndent)) || (listid != static_cast(m_tlistListID))) { /* different list or a change in indentation... * need to start a new table */ tlistPop (); } if (!m_bInTList) { m_tlistListID = static_cast(listid); m_tlistIndent = static_cast(indent); m_tlistType = list->getType (); tlistPush (); } m_bInTListItem = true; if (IS_BULLETED_LIST_TYPE (m_tlistType)) { /* by this point we're sitting in an unordered list; * new list item will be created on return... */ return; } m_utf8_1 = "tr"; tagOpen (TT_TR, m_utf8_1); char buf[16]; sprintf (buf, "%lu", indent); m_utf8_1 = "td width=\""; m_utf8_1 += buf; m_utf8_1 += "\" style=\"text-align: right\" align=\"right\" valign=\"top\""; /* the left being the number/bullet point... * (generate text/tags/etc. to represent the number/bullet point) */ tagOpen (TT_TD, m_utf8_1, ws_Pre); tlistNumber (list, sdh); m_utf8_1 = "td"; tagClose (TT_TD, m_utf8_1, ws_Post); /* and the right column being the list item text/tags/etc. * new table column will be created on return... */ } void s_HTML_Listener::tlistPop () { if (!m_bInTList) return; if (m_bInTListItem) tlistPopItem (); if (IS_BULLETED_LIST_TYPE (m_tlistType)) { if (tagTop () == TT_UL) { m_utf8_1 = "ul"; tagClose (TT_UL, m_utf8_1); } if (tagTop () == TT_TD) { m_utf8_1 = "td"; tagClose (TT_TD, m_utf8_1); } if (tagTop () == TT_TR) { m_utf8_1 = "tr"; tagClose (TT_TR, m_utf8_1); } } if (tagTop () == TT_TABLE) { m_utf8_1 = "table"; tagClose (TT_TABLE, m_utf8_1); } m_bInTList = false; } void s_HTML_Listener::tlistPopItem () { if (!m_bInTListItem) return; if (IS_BULLETED_LIST_TYPE (m_tlistType)) { if (tagTop () == TT_LI) { m_utf8_1 = "li"; tagClose (TT_LI, m_utf8_1, ws_Post); } } else { if (tagTop () == TT_TD) { m_utf8_1 = "td"; tagClose (TT_TD, m_utf8_1, ws_Post); } if (tagTop () == TT_TR) { m_utf8_1 = "tr"; tagClose (TT_TR, m_utf8_1); } } m_bInTListItem = false; } UT_uint32 s_HTML_Listener::listDepth () { return static_cast(m_utsListType.getDepth ()); } UT_uint32 s_HTML_Listener::listType () { UT_sint32 i = 0; m_utsListType.viewTop (i); return (UT_uint32)i; } void s_HTML_Listener::listPush (UT_uint32 type, const char * ClassName) { if (tagTop () == TT_LI) { m_utf8_1 = "li"; tagClose (TT_LI, m_utf8_1, ws_Post); } UT_uint32 tagID; if (type == BT_BULLETLIST) { tagID = TT_UL; m_utf8_1 = "ul"; } else { tagID = TT_OL; m_utf8_1 = "ol"; } tagOpen (tagID, m_utf8_1); m_utsListType.push (type); } void s_HTML_Listener::listPop () { if (tagTop () == TT_LI) { m_utf8_1 = "li"; tagClose (TT_LI, m_utf8_1, ws_Post); } UT_uint32 type = 0; m_utsListType.pop ((UT_sint32*)&type); UT_uint32 tagID; if (type == BT_BULLETLIST) { tagID = TT_UL; m_utf8_1 = "ul"; } else { tagID = TT_OL; m_utf8_1 = "ol"; } tagClose (tagID, m_utf8_1); } void s_HTML_Listener::listPopToDepth (UT_uint32 depth) { if (listDepth () <= depth) return; UT_uint32 count = listDepth () - depth; for (UT_uint32 i = 0; i < count; i++) listPop (); } void s_HTML_Listener::_openTag (PT_AttrPropIndex api, PL_StruxDocHandle sdh) { if (m_bFirstWrite) _openSection (api); if (!m_bInSection) return; m_StyleTreeInline = 0; m_StyleTreeBlock = 0; if (m_bInBlock) { if (tagTop () == TT_A) { m_utf8_1 = "a"; tagClose (TT_A, m_utf8_1, ws_None); } if (tagTop () != TT_LI) _closeTag (); } m_bWroteText = false; const XML_Char * szDefault = "Normal"; // TODO: should be/is a #define somewhere? const PP_AttrProp * pAP = 0; bool bHaveProp = m_pDocument->getAttrProp (api, &pAP); if (!bHaveProp || (pAP == 0)) //

with no style attribute, and no properties either { listPopToDepth (0); m_utf8_1 = "p"; tagOpen (TT_P, m_utf8_1, ws_Pre); m_utf8_style = szDefault; m_iBlockType = BT_NORMAL; m_bInBlock = true; return; } UT_uint32 tagID = TT_OTHER; bool tagPending = false; const XML_Char * szValue = 0; const XML_Char * szLevel = 0; const XML_Char * szListID = 0; const XML_Char * szStyleType = 0; /* This is the point at which we differentiate between different * types of tags in HTML. We do a sequence of checks on the "style" * and other useful attributes of the current block. First we check * if we are in a block which has a named "style" or which contains * list information. * * Weaknesses in this code include the mutability of our stored * document state. I've had it happen where the representation of * lists in the abiword format changes, which tends to break this * code. */ bool have_style = pAP->getAttribute (PT_STYLE_ATTRIBUTE_NAME, szValue); bool have_listID = pAP->getAttribute (PT_LISTID_ATTRIBUTE_NAME, szListID); bool zero_listID = true; if (have_listID) zero_listID = (UT_strcmp (szListID, "0") == 0); /* A nonzero "listid" attribute value indicates that we * are in a list item, so we need to process it, HTML-style. */ /* Specify a default style name for this list item if it * doesn't already have one. */ if (!have_style) szValue = szDefault; m_utf8_style = szValue; m_StyleTreeBlock = m_style_tree->find (szValue); if (!zero_listID) { /* Desired list type (numbered / bullet) */ if (!pAP->getProperty ("list-style", szStyleType)) szStyleType = szValue; if (UT_strcmp (static_cast(szStyleType), "Bullet List") == 0) { m_iBlockType = BT_BULLETLIST; } else { m_iBlockType = BT_NUMBEREDLIST; } #ifndef HTML_PSEUDO_LISTS /* Find out how deeply nested this list item is. */ pAP->getAttribute ("level", szLevel); m_iListDepth = atoi (static_cast(szLevel)); /* TODO: why can m_iListDepth be zero sometimes ?? (numbered headings?) */ if (m_iListDepth == 0) m_iListDepth = 1; /* Rise to desired list depth if currently too deep */ listPopToDepth (m_iListDepth); /* current list & desired list have same depth but different types... * pop one and add new below */ if ((m_iListDepth == listDepth ()) && (m_iBlockType != listType ())) { listPop (); } /* If our list is getting deeper, we need to start a nested list. * Assumption: list can only increase in depth by 1. */ if (m_iListDepth > listDepth ()) { listPush (m_iBlockType, szValue); } else if (tagTop () == TT_LI) { m_utf8_1 = "li"; tagClose (TT_LI, m_utf8_1, ws_Post); } tagID = TT_LI; tagPending = true; m_utf8_1 = "li"; if (m_StyleTreeBlock) if (m_StyleTreeBlock->class_list().byteLength ()) { m_utf8_1 += " class=\""; m_utf8_1 += m_StyleTreeBlock->class_list (); m_utf8_1 += "\""; } #else const XML_Char * szP_MarginLeft = 0; pAP->getProperty ("margin-left", szP_MarginLeft); tlistPushItem (szListID, szP_MarginLeft, sdh); if (IS_BULLETED_LIST_TYPE (m_tlistType)) { tagID = TT_LI; m_utf8_1 = "li"; } else { tagID = TT_TD; m_utf8_1 = "td"; } tagPending = true; #endif } else if (have_style) { if (m_bInTList) tlistPop (); listPopToDepth (0); const s_StyleTree * tree = m_StyleTreeBlock; bool bAddAWMLStyle = false; if (get_Allow_AWML () && !get_HTML4 ()) bAddAWMLStyle = true; if ((UT_strcmp (static_cast(szValue), "Heading 1") == 0) || (UT_strcmp (static_cast(szValue), "Numbered Heading 1") == 0)) { m_iBlockType = BT_HEADING1; tagID = TT_H1; tagPending = true; m_utf8_1 = "h1"; if (UT_strcmp (static_cast(szValue), "Heading 1") == 0) bAddAWMLStyle = false; } else if ((UT_strcmp (static_cast(szValue), "Heading 2") == 0) || (UT_strcmp (static_cast(szValue), "Numbered Heading 2") == 0)) { m_iBlockType = BT_HEADING2; tagID = TT_H2; tagPending = true; m_utf8_1 = "h2"; if (UT_strcmp (static_cast(szValue), "Heading 2") == 0) bAddAWMLStyle = false; } else if ((UT_strcmp (static_cast(szValue), "Heading 3") == 0) || (UT_strcmp (static_cast(szValue), "Numbered Heading 3") == 0)) { m_iBlockType = BT_HEADING3; tagID = TT_H3; tagPending = true; m_utf8_1 = "h3"; if (UT_strcmp (static_cast(szValue), "Heading 3") == 0) bAddAWMLStyle = false; } else if (UT_strcmp (static_cast(szValue), "Block Text") == 0) { m_iBlockType = BT_BLOCKTEXT; tagID = TT_BLOCKQUOTE; tagPending = true; m_utf8_1 = "blockquote"; bAddAWMLStyle = false; } else if (UT_strcmp (static_cast(szValue), "Plain Text") == 0) { m_iBlockType = BT_PLAINTEXT; tagID = TT_PRE; tagPending = true; m_utf8_1 = "pre"; bAddAWMLStyle = false; } else if (UT_strcmp (static_cast(szValue), "Normal") == 0) { m_iBlockType = BT_NORMAL; tagID = TT_P; tagPending = true; m_utf8_1 = "p"; bAddAWMLStyle = false; } else if (tree == 0) // hmm... { m_iBlockType = BT_NORMAL; tagID = TT_P; tagPending = true; m_utf8_1 = "p"; } else if (tree->descends ("Heading 1")) { m_iBlockType = BT_HEADING1; tagID = TT_H1; tagPending = true; m_utf8_1 = "h1"; } else if (tree->descends ("Heading 2")) { m_iBlockType = BT_HEADING2; tagID = TT_H2; tagPending = true; m_utf8_1 = "h2"; } else if (tree->descends ("Heading 3")) { m_iBlockType = BT_HEADING3; tagID = TT_H3; tagPending = true; m_utf8_1 = "h3"; } else if (tree->descends ("Block Text")) { m_iBlockType = BT_BLOCKTEXT; tagID = TT_BLOCKQUOTE; tagPending = true; m_utf8_1 = "blockquote"; } else if (tree->descends ("Plain Text")) { m_iBlockType = BT_PLAINTEXT; tagID = TT_PRE; tagPending = true; m_utf8_1 = "pre"; } else if (tree->descends ("Normal")) { m_iBlockType = BT_NORMAL; tagID = TT_P; tagPending = true; m_utf8_1 = "p"; } else { m_iBlockType = BT_NORMAL; tagID = TT_P; tagPending = true; m_utf8_1 = "p"; } if (tree) if (tree->class_list().byteLength ()) { m_utf8_1 += " class=\""; m_utf8_1 += tree->class_list (); m_utf8_1 += "\""; } if (bAddAWMLStyle) { m_utf8_1 += " awml:style=\""; m_utf8_1 += szValue; m_utf8_1 += "\""; } } else // not a list, no style { if (m_bInTList) tlistPop (); listPopToDepth (0); m_iBlockType = BT_NORMAL; tagID = TT_P; tagPending = true; m_utf8_1 = "p"; } if (!tagPending) { UT_DEBUGMSG(("WARNING: unexpected!\n")); return; } const XML_Char * szP_TextAlign = 0; const XML_Char * szP_MarginBottom = 0; const XML_Char * szP_MarginTop = 0; const XML_Char * szP_MarginLeft = 0; const XML_Char * szP_MarginRight = 0; const XML_Char * szP_TextIndent = 0; const XML_Char * szP_DomDir = 0; pAP->getProperty ("text-align", szP_TextAlign); pAP->getProperty ("margin-bottom", szP_MarginBottom); pAP->getProperty ("margin-top", szP_MarginTop); pAP->getProperty ("margin-right", szP_MarginRight); /* NOTE: For both "margin-left" and "text-indent" for lists, * Abi's behaviour and HTML's do not match. * Furthermore, it seems like all blocks have a "margin-left" * and "text-indent", even if they are zero, which adds * significant clutter. These are all manually taken care of * below. I think that the computation of these attributes * needs to be rethought. - John */ if ((tagID != TT_LI) && (tagID != TT_TD)) { if (pAP->getProperty ("margin-left", szP_MarginLeft)) if (strstr (szP_MarginLeft, "0.0000")) szP_MarginLeft = 0; if (pAP->getProperty ("text-indent", szP_TextIndent)) if (strstr (szP_TextIndent, "0.0000")) szP_TextIndent = 0; } pAP->getProperty ("dom-dir", szP_DomDir); if (szP_DomDir) // any reason why this can't be used with

or
 ??
		{
			m_utf8_1 += " dir=\"";
			m_utf8_1 += szP_DomDir;
			m_utf8_1 += "\"";
		}

	bool validProp = (szP_TextAlign || szP_MarginBottom || szP_MarginTop ||
					  szP_MarginLeft || szP_MarginRight || szP_TextIndent);

	/* Assumption: never get property set with block text, plain text. Probably true...
	 */
	if ((m_iBlockType != BT_PLAINTEXT) &&
		(m_iBlockType != BT_BLOCKTEXT) && validProp)
		{
			m_utf8_1 += " style=\"";

			bool first = true;

			if (szP_TextAlign)
				{
					if (!first) m_utf8_1 += "; ";
					m_utf8_1 += "text-align: ";
					m_utf8_1 += szP_TextAlign;
					first = false;
				}
			if (szP_MarginBottom)
				{
					if (!first) m_utf8_1 += "; ";
					m_utf8_1 += "margin-bottom: ";
					m_utf8_1 += szP_MarginBottom;
					first = false;
				}
			if (szP_MarginTop)
				{
					if (!first) m_utf8_1 += "; ";
					m_utf8_1 += "margin-top: ";
					m_utf8_1 += szP_MarginTop;
					first = false;
				}
			if (szP_MarginRight)
				{
					if (!first) m_utf8_1 += "; ";
					m_utf8_1 += "margin-right: ";
					m_utf8_1 += szP_MarginRight;
					first = false;
				}
			if (szP_MarginLeft)
				{
					if (!first) m_utf8_1 += "; ";
					m_utf8_1 += "margin-left: ";
					m_utf8_1 += szP_MarginLeft;
					first = false;
				}
			if (szP_TextIndent)
				{
					if (!first) m_utf8_1 += "; ";
					m_utf8_1 += "text-indent: ";
					m_utf8_1 += szP_TextIndent;
					first = false;
				}

			m_utf8_1 += "\"";
		}

	tagOpen (tagID, m_utf8_1, ws_Pre);

	m_bInBlock = true;
}

void s_HTML_Listener::_closeTag (void)
{
	if (!m_bInBlock) return;

	if (m_bInSpan) _closeSpan ();
	
	if (tagTop () == TT_A)
		{
			m_utf8_1 = "a";
			tagClose (TT_A, m_utf8_1, ws_None);
		}
	if (m_iBlockType == BT_NORMAL)
		{
#ifdef HTML_EMPTY_PARA_LF
			if (!m_bWroteText) // TODO: is this really ideal?
				{
					m_utf8_1 = "br";
					tagOpenClose (m_utf8_1, get_HTML4 (), ws_None);
				}
#endif /* HTML_EMPTY_PARA_LF */
			if (tagTop () == TT_P)
				{
					m_utf8_1 = "p";
					tagClose (TT_P, m_utf8_1, ws_Post);
				}
		}
	else if (m_iBlockType == BT_HEADING1) 
		{
			if (tagTop () == TT_H1)
				{
					m_utf8_1 = "h1";
					tagClose (TT_H1, m_utf8_1, ws_Post);
				}
		}
	else if (m_iBlockType == BT_HEADING2)
		{
			if (tagTop () == TT_H2)
				{
					m_utf8_1 = "h2";
					tagClose (TT_H2, m_utf8_1, ws_Post);
				}
		}
	else if (m_iBlockType == BT_HEADING3)
		{
			if (tagTop () == TT_H3)
				{
					m_utf8_1 = "h3";
					tagClose (TT_H3, m_utf8_1, ws_Post);
				}
		}
	else if (m_iBlockType == BT_BLOCKTEXT)
		{
			if (tagTop () == TT_BLOCKQUOTE)
				{
					m_utf8_1 = "blockquote";
					tagClose (TT_BLOCKQUOTE, m_utf8_1, ws_Post);
				}
		}
	else if (m_iBlockType == BT_PLAINTEXT)
		{
			if (tagTop () == TT_PRE)
				{
					m_utf8_1 = "pre";
					tagClose (TT_PRE, m_utf8_1, ws_Post);
				}
		}
	else if (m_bInTListItem)
		{
			/* we're in a table-list item
			 */
			tlistPopItem ();
		}
	else if (m_iBlockType == BT_NUMBEREDLIST || m_iBlockType == BT_BULLETLIST)
		{	
			/* do nothing, lists are handled differently, as they have multiple tags */ 
		}
 	else
		{
			UT_DEBUGMSG(("(defaulting to 

)\n")); if (tagTop () == TT_P) { m_utf8_1 = "p"; tagClose (TT_P, m_utf8_1, ws_Post); } } m_bInBlock = false; } void s_HTML_Listener::_openSpan (PT_AttrPropIndex api) { if (m_bFirstWrite) _openTag (api, 0); if (!m_bInBlock) return; m_StyleTreeInline = 0; const PP_AttrProp * pAP = 0; bool bHaveProp = (api ? (m_pDocument->getAttrProp (api, &pAP)) : false); if (!bHaveProp || (pAP == 0)) { if (m_bInSpan) _closeSpan (); return; } const XML_Char * szA_Style = 0; bool have_style = pAP->getAttribute (PT_STYLE_ATTRIBUTE_NAME, szA_Style); if (have_style) if (m_utf8_style == szA_Style) // inline style is block style; ignore have_style = false; const s_StyleTree * tree = 0; if (have_style) tree = m_style_tree->find (szA_Style); m_StyleTreeInline = tree; const XML_Char * szP_FontWeight = 0; const XML_Char * szP_FontStyle = 0; const XML_Char * szP_FontSize = 0; const XML_Char * szP_FontFamily = 0; const XML_Char * szP_TextDecoration = 0; const XML_Char * szP_TextPosition = 0; const XML_Char * szP_Color = 0; const XML_Char * szP_BgColor = 0; const XML_Char * szP_Lang = 0; pAP->getProperty ("font-weight", szP_FontWeight); pAP->getProperty ("font-style", szP_FontStyle); pAP->getProperty ("font-size", szP_FontSize); pAP->getProperty ("font-family", szP_FontFamily); pAP->getProperty ("text-decoration", szP_TextDecoration); pAP->getProperty ("text-position", szP_TextPosition); pAP->getProperty ("color", szP_Color); pAP->getProperty ("bgcolor", szP_BgColor); pAP->getProperty ("lang", szP_Lang); bool first = true; m_utf8_1 = "span style=\""; /* TODO: this bold/italic check needs re-thought */ if (szP_FontWeight) if (UT_strcmp (szP_FontWeight, "bold") == 0) if (!compareStyle ("font-weight", "bold")) { if (!first) m_utf8_1 += "; "; m_utf8_1 += "font-weight: bold"; first = false; } if (szP_FontStyle) if (UT_strcmp (szP_FontStyle, "italic") == 0) if (!compareStyle ("font-style", "italic")) { if (!first) m_utf8_1 += "; "; m_utf8_1 += "font-style: italic"; first = false; } if (szP_Lang) if (!compareStyle ("font-style", szP_Lang)) { if (!first) m_utf8_1 += "; "; m_utf8_1 += "lang: "; m_utf8_1 += szP_Lang; first = false; } if (szP_FontSize) { char * old_locale = setlocale (LC_NUMERIC, "C"); char buf[16]; sprintf (buf, "%g", UT_convertToPoints (szP_FontSize)); setlocale (LC_NUMERIC, old_locale); m_utf8_0 = buf; m_utf8_0 += "pt"; if (!compareStyle ("font-size", m_utf8_0.utf8_str ())) { if (!first) m_utf8_1 += "; "; m_utf8_1 += "font-size: "; m_utf8_1 += m_utf8_0; first = false; } } if (szP_FontFamily) { if ((UT_strcmp (szP_FontFamily, "serif") == 0) || (UT_strcmp (szP_FontFamily, "sans-serif") == 0) || (UT_strcmp (szP_FontFamily, "cursive") == 0) || (UT_strcmp (szP_FontFamily, "fantasy") == 0) || (UT_strcmp (szP_FontFamily, "monospace") == 0)) { m_utf8_0 = static_cast(szP_FontFamily); } else { m_utf8_0 = "'"; m_utf8_0 += static_cast(szP_FontFamily); m_utf8_0 += "'"; } if (!compareStyle ("font-family", m_utf8_0.utf8_str ())) { if (!first) m_utf8_1 += "; "; m_utf8_1 += "font-family: "; m_utf8_1 += m_utf8_0; first = false; } } if (szP_TextDecoration) { bool bUnderline = (strstr (szP_TextDecoration, "underline") != NULL); bool bLineThrough = (strstr (szP_TextDecoration, "line-through") != NULL); bool bOverline = (strstr (szP_TextDecoration, "overline") != NULL); if (bUnderline || bLineThrough || bOverline) { m_utf8_0 = ""; if (bUnderline) m_utf8_0 += "underline"; if (bLineThrough) { if (bUnderline) m_utf8_0 += ", "; m_utf8_0 += "line-through"; } if (bOverline) { if (bUnderline || bLineThrough) m_utf8_0 += ", "; m_utf8_0 += "overline"; } if (!compareStyle ("text-decoration", m_utf8_0.utf8_str ())) { if (!first) m_utf8_1 += "; "; m_utf8_1 += "text-decoration: "; m_utf8_1 += m_utf8_0; first = false; } } } if (szP_TextPosition) { if (UT_strcmp (szP_TextPosition, "superscript") == 0) { if (!compareStyle ("vertical-align", "superscript")) { if (!first) m_utf8_1 += "; "; m_utf8_1 += "vertical-align: superscript"; first = false; } } else if (UT_strcmp (szP_TextPosition, "subscript") == 0) { if (!compareStyle ("vertical-align", "subscript")) { if (!first) m_utf8_1 += "; "; m_utf8_1 += "vertical-align: subscript"; first = false; } } } if (szP_Color) if (!IS_TRANSPARENT_COLOR (szP_Color)) { m_utf8_0 = "#"; m_utf8_0 += szP_Color; if (!compareStyle ("color", m_utf8_0.utf8_str ())) { if (!first) m_utf8_1 += "; "; m_utf8_1 += "color: "; m_utf8_1 += m_utf8_0; first = false; } } if (szP_BgColor) if (!IS_TRANSPARENT_COLOR (szP_BgColor)) { m_utf8_0 = "#"; m_utf8_0 += szP_BgColor; if (!compareStyle ("background", m_utf8_0.utf8_str ())) { if (!first) m_utf8_1 += "; "; m_utf8_1 += "background: "; m_utf8_1 += m_utf8_0; first = false; } } bool bInSpan = false; if (first) { /* no style elements specified */ m_utf8_1 = "span"; } else { m_utf8_1 += "\""; bInSpan = true; } if (tree) if (tree->class_list().byteLength ()) { m_utf8_1 += " class=\""; m_utf8_1 += tree->class_list (); m_utf8_1 += "\""; bInSpan = true; } /* if the dir-override is set, or dir is 'rtl' or 'ltr', we will output * the dir property; however, this property cannot be within a style * sheet, so anything that needs to be added to this code and belongs * withing a style property must be above us; further it should be noted * that there is a good chance that the html browser will not handle it * correctly. For instance IE will take dir=rtl as an indication that * the span should have rtl placement on a line, but it will ignore this * value when printing the actual span. */ const XML_Char * szP_DirOverride = 0; pAP->getProperty ("dir-override", szP_DirOverride); if (szP_DirOverride) if ((*szP_DirOverride == 'l') || (*szP_DirOverride == 'r')) { m_utf8_1 += " dir=\""; m_utf8_1 += szP_DirOverride; m_utf8_1 += "\""; bInSpan = true; } if (bInSpan) { if (m_bInSpan) { if (m_utf8_span == m_utf8_1) return; // this span same as last... m_utf8_span = m_utf8_1; _closeSpan (); } else m_utf8_span = m_utf8_1; tagOpen (TT_SPAN, m_utf8_span, ws_None); m_bInSpan = true; } else if (m_bInSpan) _closeSpan (); } void s_HTML_Listener::_closeSpan () { if (tagTop () == TT_A) { m_utf8_1 = "a"; tagClose (TT_A, m_utf8_1, ws_None); } if (tagTop () == TT_SPAN) { m_utf8_1 = "span"; tagClose (TT_SPAN, m_utf8_1, ws_None); } m_bInSpan = false; } #ifdef HTML_TABLES_SUPPORTED void s_HTML_Listener::_fillColWidthsVector(void) { // // Positioned columns controls // const char * pszColumnProps = m_TableHelper.getTableProp("table-column-props"); UT_sint32 nCols = m_TableHelper.getNumCols (); UT_DEBUGMSG(("Number columns in table %d \n",nCols)); if(m_vecDWidths.getItemCount() > 0) { UT_VECTOR_PURGEALL(double *,m_vecDWidths); m_vecDWidths.clear(); } if(pszColumnProps && *pszColumnProps) { /* These will be properties applied to all columns. To start with, just the widths of each column are specifed. These are translated to layout units. The format of the string of properties is: table-column-props:1.2in/3.0in/1.3in/; So we read back in pszColumnProps 1.2in/3.0in/1.3in/ The "/" characters will be used to delineate different column entries. As new properties for each column are defined these will be delineated with "_" characters. But we'll cross that bridge later. */ UT_DEBUGMSG(("table-column-props: %s \n",pszColumnProps)); UT_String sProps = pszColumnProps; UT_sint32 sizes = sProps.size(); UT_sint32 i =0; UT_sint32 j =0; while(i < sizes) { for (j=i; (ji && sProps[j] == '/') { UT_String sSub = sProps.substr(i,(j-i)); i = j + 1; double * pDWidth = new double; *pDWidth = UT_convertToInches(sSub.c_str()); m_vecDWidths.addItem(reinterpret_cast(pDWidth)); } } } // // automatic column widths set to total width divided by nCols // else { // double total = m_dPageWidthInches - m_dSecLeftMarginInches - m_dSecRightMarginInches; UT_sint32 nCols = m_TableHelper.getNumCols (); double totWidth = m_dPageWidthInches - m_dSecLeftMarginInches - m_dSecRightMarginInches; double colWidth = totWidth/nCols; UT_sint32 i = 0; for(i =0; i< nCols; i++) { double * pDWidth = new double; *pDWidth = colWidth; m_vecDWidths.addItem(reinterpret_cast(pDWidth)); } } } void s_HTML_Listener::_openTable (PT_AttrPropIndex api) { if (m_bFirstWrite) _openSection (api); if (!m_bInSection) return; if (m_bInBlock) _closeTag (); const PP_AttrProp * pAP = NULL; bool bHaveProp = m_pDocument->getAttrProp (api,&pAP); if (!bHaveProp || (pAP == 0)) return; UT_sint32 cellPadding = 1; const char * prop = m_TableHelper.getTableProp ("table-line-thickness"); UT_sint32 border = (prop ? atoi (prop) : 1); UT_UTF8String border_default = "1px"; if (prop) border_default = prop; #if 0 const XML_Char * pszTableColSpacing = 0; const XML_Char * pszTableRowSpacing = 0; const XML_Char * pszLeftOffset = 0; const XML_Char * pszTopOffset = 0; const XML_Char * pszRightOffset = 0; const XML_Char * pszBottomOffset = 0; pSectionAP->getProperty ("table-col-spacing", pszTableColSpacing); pSectionAP->getProperty ("table-row-spacing", pszTableRowSpacing); pSectionAP->getProperty ("cell-margin-left", pszLeftOffset); pSectionAP->getProperty ("cell-margin-top", pszTopOffset); pSectionAP->getProperty ("cell-margin-right", pszRightOffset); pSectionAP->getProperty ("cell-margin-bottom", pszBottomOffset); #endif UT_UTF8String styles; const char * pszBgColor = m_TableHelper.getTableProp ("bgcolor"); if (pszBgColor == NULL) pszBgColor = m_TableHelper.getTableProp ("background-color"); if (pszBgColor) { if (styles.byteLength ()) styles += "; "; styles += "background-color: "; UT_HashColor color; const char * hash = color.setHashIfValid (pszBgColor); if (hash) styles += hash; else styles += pszBgColor; } const char * pszBorderColor = NULL; pszBorderColor = m_TableHelper.getTableProp ("color"); if (pszBorderColor) { if (styles.byteLength ()) styles += "; "; styles += "color: "; UT_HashColor color; const char * hash = color.setHashIfValid (pszBorderColor); if (hash) styles += hash; else styles += pszBorderColor; } pszBorderColor = m_TableHelper.getTableProp ("bot-color"); if (pszBorderColor) { if (styles.byteLength ()) styles += "; "; styles += "border-bottom-color: "; UT_HashColor color; const char * hash = color.setHashIfValid (pszBorderColor); if (hash) styles += hash; else styles += pszBorderColor; } pszBorderColor = m_TableHelper.getTableProp ("left-color"); if (pszBorderColor) { if (styles.byteLength ()) styles += "; "; styles += "border-left-color: "; UT_HashColor color; const char * hash = color.setHashIfValid (pszBorderColor); if (hash) styles += hash; else styles += pszBorderColor; } pszBorderColor = m_TableHelper.getTableProp ("right-color"); if (pszBorderColor) { if (styles.byteLength ()) styles += "; "; styles += "border-right-color: "; UT_HashColor color; const char * hash = color.setHashIfValid (pszBorderColor); if (hash) styles += hash; else styles += pszBorderColor; } pszBorderColor = m_TableHelper.getTableProp ("top-color"); if (pszBorderColor) { if (styles.byteLength ()) styles += "; "; styles += "border-top-color: "; UT_HashColor color; const char * hash = color.setHashIfValid (pszBorderColor); if (hash) styles += hash; else styles += pszBorderColor; } const char * pszBorderStyle = NULL; pszBorderStyle = m_TableHelper.getTableProp ("bot-style"); if (pszBorderStyle) { if (styles.byteLength ()) styles += "; "; styles += "border-bottom-style: "; styles += PP_PropertyMap::linestyle_for_CSS (pszBorderStyle); } pszBorderStyle = m_TableHelper.getTableProp ("left-style"); if (pszBorderStyle) { if (styles.byteLength ()) styles += "; "; styles += "border-left-style: "; styles += PP_PropertyMap::linestyle_for_CSS (pszBorderStyle); } pszBorderStyle = m_TableHelper.getTableProp ("right-style"); if (pszBorderStyle) { if (styles.byteLength ()) styles += "; "; styles += "border-right-style: "; styles += PP_PropertyMap::linestyle_for_CSS (pszBorderStyle); } pszBorderStyle = m_TableHelper.getTableProp ("top-style"); if (pszBorderStyle) { if (styles.byteLength ()) styles += "; "; styles += "border-top-style: "; styles += PP_PropertyMap::linestyle_for_CSS (pszBorderStyle); } const char * pszBorderWidth = NULL; if (styles.byteLength ()) styles += "; "; pszBorderWidth = m_TableHelper.getTableProp ("bot-thickness"); styles += "border-bottom-width: "; if (pszBorderWidth) styles += pszBorderWidth; else styles += border_default; pszBorderWidth = m_TableHelper.getTableProp ("left-thickness"); styles += "; border-left-width: "; if (pszBorderWidth) styles += pszBorderWidth; else styles += border_default; pszBorderWidth = m_TableHelper.getTableProp ("right-thickness"); styles += "; border-right-width: "; if (pszBorderWidth) styles += pszBorderWidth; else styles += border_default; pszBorderWidth = m_TableHelper.getTableProp ("top-thickness"); styles += "; border-top-width: "; if (pszBorderWidth) styles += pszBorderWidth; else styles += border_default; m_utf8_1 = "table cellpadding=\""; m_utf8_1 += UT_UTF8String_sprintf ("%d\" border=\"%d", cellPadding, border); m_utf8_1 += "\" rules=\"all\" style=\""; m_utf8_1 += styles; m_utf8_1 += "\""; int nCols = m_TableHelper.getNumCols (); double totWidth = m_dPageWidthInches - m_dSecLeftMarginInches - m_dSecRightMarginInches; float colWidth = 100.0 / static_cast(nCols); tagOpen (TT_TABLE, m_utf8_1); _fillColWidthsVector(); UT_sint32 i = 0; if(m_vecDWidths.getItemCount() > 0) { for(i = 0; i< static_cast(m_vecDWidths.getItemCount());i++) { double * pDWidth = reinterpret_cast(m_vecDWidths.getNthItem(i)); double percent = 100.0*(*pDWidth/totWidth); char * old_locale = setlocale (LC_NUMERIC, "C"); m_utf8_1 = "colgroup width=\""; m_utf8_1 += UT_UTF8String_sprintf ("%f%%\" span=\"%d", percent,1); m_utf8_1 += "\""; UT_DEBUGMSG(("Output width def %s \n",m_utf8_1.utf8_str())); setlocale (LC_NUMERIC, old_locale); tagOpenClose (m_utf8_1, false); m_utf8_1.clear(); } } else { tagOpen (TT_TABLE, m_utf8_1); char * old_locale = setlocale (LC_NUMERIC, "C"); m_utf8_1 = "colgroup width=\""; m_utf8_1 += UT_UTF8String_sprintf ("%f%%\" span=\"%d", colWidth, nCols); m_utf8_1 += "\""; setlocale (LC_NUMERIC, old_locale); tagOpenClose (m_utf8_1, false); } m_utf8_1 = "tbody style=\"border: inherit\""; tagOpen (TT_TBODY, m_utf8_1); } void s_HTML_Listener::_closeTable () { m_utf8_1 = "tbody"; tagClose (TT_TBODY, m_utf8_1); m_utf8_1 = "table"; tagClose (TT_TABLE, m_utf8_1); UT_VECTOR_PURGEALL(double *,m_vecDWidths); m_vecDWidths.clear(); if(m_TableHelper.getNestDepth() > 0) { _fillColWidthsVector(); _setCellWidthInches(); } } void s_HTML_Listener::_setCellWidthInches(void) { UT_sint32 left = m_TableHelper.getLeft (); UT_sint32 right = m_TableHelper.getRight (); double tot = 0; UT_sint32 i =0; UT_ASSERT(m_vecDWidths.size() >= (right-1)); for(i=left; i(m_vecDWidths.getNthItem(i)); } m_dCellWidthInches = tot; } void s_HTML_Listener::_openRow (PT_AttrPropIndex api) { if (tagTop () == TT_TR) { m_utf8_1 = "tr"; tagClose (TT_TR, m_utf8_1); } if (tagTop () != TT_TBODY) { _openTable (api); } m_utf8_1 = "tr style=\"border: inherit\""; tagOpen (TT_TR, m_utf8_1); } void s_HTML_Listener::_openCell (PT_AttrPropIndex api) { if (m_bFirstWrite) _openSection (api); if (!m_bInSection) return; if (m_TableHelper.getNestDepth () < 1) _openTable(api); const PP_AttrProp * pAP = NULL; bool bHaveProp = m_pDocument->getAttrProp (api, &pAP); _setCellWidthInches(); if (bHaveProp && pAP) { UT_sint32 rowspan = m_TableHelper.getBot () - m_TableHelper.getTop (); UT_sint32 colspan = m_TableHelper.getRight () - m_TableHelper.getLeft (); if ((tagTop () != TT_TR) || (m_TableHelper.getLeft () == 0)) // beginning of a new row _openRow (api); UT_UTF8String styles; const char * pszBgColor = m_TableHelper.getCellProp ("bgcolor"); if (pszBgColor == NULL) pszBgColor = m_TableHelper.getCellProp ("background-color"); if (pszBgColor) { if (styles.byteLength ()) styles += "; "; styles += "background-color: "; UT_HashColor color; const char * hash = color.setHashIfValid (pszBgColor); if (hash) styles += hash; else styles += pszBgColor; } const char * pszBorderColor = NULL; pszBorderColor = m_TableHelper.getCellProp ("color"); if (pszBorderColor) { if (styles.byteLength ()) styles += "; "; styles += "color: "; UT_HashColor color; const char * hash = color.setHashIfValid (pszBorderColor); if (hash) styles += hash; else styles += pszBorderColor; } pszBorderColor = m_TableHelper.getCellProp ("bot-color"); if (pszBorderColor) { if (styles.byteLength ()) styles += "; "; styles += "border-bottom-color: "; UT_HashColor color; const char * hash = color.setHashIfValid (pszBorderColor); if (hash) styles += hash; else styles += pszBorderColor; } pszBorderColor = m_TableHelper.getCellProp ("left-color"); if (pszBorderColor) { if (styles.byteLength ()) styles += "; "; styles += "border-left-color: "; UT_HashColor color; const char * hash = color.setHashIfValid (pszBorderColor); if (hash) styles += hash; else styles += pszBorderColor; } pszBorderColor = m_TableHelper.getCellProp ("right-color"); if (pszBorderColor) { if (styles.byteLength ()) styles += "; "; styles += "border-right-color: "; UT_HashColor color; const char * hash = color.setHashIfValid (pszBorderColor); if (hash) styles += hash; else styles += pszBorderColor; } pszBorderColor = m_TableHelper.getCellProp ("top-color"); if (pszBorderColor) { if (styles.byteLength ()) styles += "; "; styles += "border-top-color: "; UT_HashColor color; const char * hash = color.setHashIfValid (pszBorderColor); if (hash) styles += hash; else styles += pszBorderColor; } const char * pszBorderStyle = NULL; pszBorderStyle = m_TableHelper.getCellProp ("bot-style"); if (pszBorderStyle) { if (styles.byteLength ()) styles += "; "; styles += "border-bottom-style: "; styles += PP_PropertyMap::linestyle_for_CSS (pszBorderStyle); } pszBorderStyle = m_TableHelper.getCellProp ("left-style"); if (pszBorderStyle) { if (styles.byteLength ()) styles += "; "; styles += "border-left-style: "; styles += PP_PropertyMap::linestyle_for_CSS (pszBorderStyle); } pszBorderStyle = m_TableHelper.getCellProp ("right-style"); if (pszBorderStyle) { if (styles.byteLength ()) styles += "; "; styles += "border-right-style: "; styles += PP_PropertyMap::linestyle_for_CSS (pszBorderStyle); } pszBorderStyle = m_TableHelper.getCellProp ("top-style"); if (pszBorderStyle) { if (styles.byteLength ()) styles += "; "; styles += "border-top-style: "; styles += PP_PropertyMap::linestyle_for_CSS (pszBorderStyle); } const char * pszBorderWidth = NULL; pszBorderWidth = m_TableHelper.getCellProp ("bot-thickness"); if (pszBorderWidth) { if (styles.byteLength ()) styles += "; "; styles += "border-bottom-width: "; styles += pszBorderWidth; } pszBorderWidth = m_TableHelper.getCellProp ("left-thickness"); if (pszBorderWidth) { if (styles.byteLength ()) styles += "; "; styles += "border-left-width: "; styles += pszBorderWidth; } pszBorderWidth = m_TableHelper.getCellProp ("right-thickness"); if (pszBorderWidth) { if (styles.byteLength ()) styles += "; "; styles += "border-right-width: "; styles += pszBorderWidth; } pszBorderWidth = m_TableHelper.getCellProp ("top-thickness"); if (pszBorderWidth) { if (styles.byteLength ()) styles += "; "; styles += "border-top-width: "; styles += pszBorderWidth; } m_utf8_1 = "td"; if (styles.byteLength ()) { m_utf8_1 += " style=\""; m_utf8_1 += styles; m_utf8_1 += "\""; } if (rowspan > 1) { m_utf8_1 += " rowspan=\""; m_utf8_1 += UT_UTF8String_sprintf ("%d", rowspan); m_utf8_1 += "\""; } if (colspan > 1) { m_utf8_1 += " colspan=\""; m_utf8_1 += UT_UTF8String_sprintf ("%d", colspan); m_utf8_1 += "\""; } tagOpen (TT_TD, m_utf8_1); } } void s_HTML_Listener::_closeCell () { if (m_TableHelper.getNestDepth () < 1) return; m_utf8_1 = "td"; tagClose (TT_TD, m_utf8_1); if (m_TableHelper.getNumCols () == m_TableHelper.getRight ()) // logical end of a row { m_utf8_1 = "tr"; tagClose (TT_TR, m_utf8_1); } } #endif /* HTML_TABLES_SUPPORTED */ void s_HTML_Listener::_outputData (const UT_UCSChar * data, UT_uint32 length) { if (!m_bInBlock) return; m_utf8_1 = ""; bool prev_space = false; const UT_UCSChar * ucs_ptr = data; for (UT_uint32 i = 0; i < length; i++) { bool space = false; switch (*ucs_ptr) { case UCS_FF: // page break, convert to line break case UCS_LF: /* LF -- representing a Forced-Line-Break */ if (m_utf8_1.byteLength ()) textTrusted (m_utf8_1); m_utf8_1 = "br"; tagOpenClose (m_utf8_1, get_HTML4 (), ws_None); m_utf8_1 = ""; break; case UCS_LQUOTE: case UCS_RQUOTE: /* Smart quotes get translated back into normal quotes */ m_utf8_1 += "'"; break; case UCS_LDBLQUOTE: m_utf8_1 += "“"; break; case UCS_RDBLQUOTE: m_utf8_1 += "”"; break; case UCS_EN_DASH: // TODO: isn't there a better way? case UCS_EM_DASH: m_utf8_1 += "-"; break; default: if ((*ucs_ptr & 0x007f) == *ucs_ptr) // ASCII { char c = static_cast(*ucs_ptr & 0x007f); if (isspace (static_cast(static_cast(c)))) { if (prev_space) m_utf8_1 += " "; else #ifdef HTML_UCS4 m_utf8_1.appendUCS4 (ucs_ptr, 1); #else m_utf8_1.append (ucs_ptr, 1); #endif space = true; } else switch (c) { case '<': m_utf8_1 += "<"; break; case '>': m_utf8_1 += ">"; break; case '&': m_utf8_1 += "&"; break; default: #ifdef HTML_UCS4 m_utf8_1.appendUCS4 (ucs_ptr, 1); #else m_utf8_1.append (ucs_ptr, 1); #endif break; } } #ifdef HTML_UCS4 else m_utf8_1.appendUCS4 (ucs_ptr, 1); // !ASCII, just append... ?? #else else m_utf8_1.append (ucs_ptr, 1); // !ASCII, just append... ?? #endif break; } prev_space = space; ucs_ptr++; } if (m_utf8_1.byteLength ()) textTrusted (m_utf8_1); } s_HTML_Listener::s_HTML_Listener (PD_Document * pDocument, IE_Exp_HTML * pie, bool bClipBoard, bool bTemplateBody, const XAP_Exp_HTMLOptions * exp_opt, s_StyleTree * style_tree) : m_pDocument (pDocument), m_pie(pie), m_bClipBoard(bClipBoard), m_bTemplateBody(bTemplateBody), m_exp_opt(exp_opt), m_style_tree(style_tree), m_bInSection(false), m_bInBlock(false), m_bInTList(false), m_bInTListItem(false), m_bInSpan(false), m_bNextIsSpace(false), m_bWroteText(false), m_bFirstWrite(true), m_bQuotedPrintable(false), #ifdef HTML_TABLES_SUPPORTED m_TableHelper(pDocument), #endif /* HTML_TABLES_SUPPORTED */ m_iBlockType(0), m_iListDepth(0), m_iImgCnt(0), m_StyleTreeInline(0), m_StyleTreeBlock(0), m_StyleTreeBody(0), m_pAPStyles(0), m_styleIndent(0), m_fdCSS(0), m_tlistIndent(0), m_tlistListID(0), m_bIgnoreTillEnd(false), m_iEmbedStartPos(0), m_dPageWidthInches(0.0), m_dSecLeftMarginInches(0.0), m_dSecRightMarginInches(0.0), m_dCellWidthInches(0.0) { m_StyleTreeBody = m_style_tree->find ("Normal"); } s_HTML_Listener::~s_HTML_Listener() { _closeTag (); listPopToDepth (0); _closeSection (); _outputEnd (); UT_VECTOR_PURGEALL(double *,m_vecDWidths); } /* dataid is the raw string with the data ID * imagedir is the name of the directory in which we'll write the image * filename is the name of the file to which we'll write the image * url is the URL which we'll use */ void s_HTML_Listener::_writeImage (const UT_ByteBuf * pByteBuf, const UT_String & imagedir, const UT_String & filename) { /* hmm, bit lazy this - attempt to create directory whether or not * it exists already... if it does, well hey. if this fails to * create a directory then fopen() will fail as well, so no biggie */ m_pDocument->getApp()->makeDirectory (imagedir.c_str (), 0750); UT_String path(imagedir); path += "/"; path += filename; FILE * out = fopen (path.c_str (), "wb+"); if (out) { fwrite (pByteBuf->getPointer (0), sizeof (UT_Byte), pByteBuf->getLength (), out); fclose (out); } } void s_HTML_Listener::_writeImageBase64 (const UT_ByteBuf * pByteBuf) { char buffer[75]; char * bufptr = 0; size_t buflen; size_t imglen = pByteBuf->getLength (); const char * imgptr = reinterpret_cast(pByteBuf->getPointer (0)); buffer[0] = '\r'; buffer[1] = '\n'; while (imglen) { buflen = 72; bufptr = buffer + 2; UT_UTF8_Base64Encode (bufptr, buflen, imgptr, imglen); *bufptr = 0; m_utf8_1 = buffer; textTrusted (m_utf8_1); } } void s_HTML_Listener::_handleImage (PT_AttrPropIndex api) { const PP_AttrProp * pAP = 0; bool bHaveProp = m_pDocument->getAttrProp (api, &pAP); if (!bHaveProp || (pAP == 0)) return; const XML_Char * szDataID = 0; pAP->getAttribute ("dataid", szDataID); if (szDataID == 0) return; const char * szName = 0; const char * szMimeType = 0; const UT_ByteBuf * pByteBuf = 0; UT_uint32 k = 0; while (m_pDocument->enumDataItems (k, 0, &szName, &pByteBuf, reinterpret_cast(&szMimeType))) { k++; if (szName == 0) continue; if (UT_strcmp (szDataID, szName) == 0) break; szName = 0; szMimeType = 0; pByteBuf = 0; } if ((pByteBuf == 0) || (szMimeType == 0)) return; // ?? if (UT_strcmp (szMimeType, "image/png") != 0) { UT_DEBUGMSG(("Object not of MIME type image/png - ignoring...\n")); return; } const char * dataid = UT_basename (static_cast(szDataID)); const char * suffix = dataid + strlen (dataid); const char * suffid = suffix; const char * ptr = 0; /* Question: What does the DataID look like for images pasted * from the clipboard? */ ptr = suffix; while (ptr > dataid) if (*--ptr == '_') { suffix = ptr; suffid = suffix; break; } ptr = suffix; while (ptr > dataid) if (*--ptr == '.') { suffix = ptr; // break; } if (dataid == suffix) return; /* hmm; who knows what locale the system uses */ UT_String imagebasedir = "clipboard"; if (m_pie->getFileName ()) imagebasedir = UT_basename (m_pie->getFileName ()); imagebasedir += "_files"; UT_String imagedir = m_pie->getFileName (); imagedir += "_files"; UT_String filename(dataid,suffix-dataid); filename += suffid; filename += ".png"; UT_UTF8String url; url += s_string_to_url (imagebasedir); url += "/"; url += s_string_to_url (filename); if (get_Multipart ()) { UT_UTF8String * save_url = new UT_UTF8String(url); if (save_url == 0) return; if (!m_SavedURLs.insert (szDataID, save_url)) // arg. failed. skip image { DELETEP(save_url); return; } } /* szDataID is the raw string with the data ID * imagedir is the name of the directory in which we'll write the image * filename is the name of the file to which we'll write the image * url is the URL which we'll use */ if (!get_Embed_Images () && !get_Multipart ()) { _writeImage (pByteBuf, imagedir, filename); } m_utf8_1 = "img"; const XML_Char * szWidth = 0; pAP->getProperty ("width", szWidth); double dWidth = UT_convertToInches(szWidth); double total = 0; if(m_TableHelper.getNestDepth() > 0) { total = m_dCellWidthInches; } else { total = m_dPageWidthInches - m_dSecLeftMarginInches - m_dSecRightMarginInches; } double percent = 100.0*dWidth/total; if(percent > 100.) { percent = 100.0; } UT_UTF8String tmp; UT_DEBUGMSG(("Width of Image %s \n",szWidth)); if (szWidth) { m_utf8_1 += " width=\""; tmp = UT_UTF8String_sprintf("%f%%",percent,1); m_utf8_1 += tmp; m_utf8_1 += "\""; } if (!get_Embed_Images () || get_Multipart ()) { m_utf8_1 += " src=\""; m_utf8_1 += url; m_utf8_1 += "\""; tagOpenClose (m_utf8_1, get_HTML4 (), ws_None); return; } m_utf8_1 += " src=\"data:image/png;base64,"; tagOpenBroken (m_utf8_1, ws_None); _writeImageBase64 (pByteBuf); m_utf8_1 = "\""; tagCloseBroken (m_utf8_1, get_HTML4 (), ws_None); } void s_HTML_Listener::_handlePendingImages () { UT_StringPtrMap::UT_Cursor cursor (&m_SavedURLs); const void * val = 0; for (val = cursor.first (); cursor.is_valid (); val = cursor.next ()) { const char * dataid = cursor.key().c_str (); const UT_UTF8String * saved_url = reinterpret_cast(val); UT_UTF8String * url = const_cast(saved_url); const char * szName = 0; const char * szMimeType = 0; const UT_ByteBuf * pByteBuf = 0; UT_uint32 k = 0; while (m_pDocument->enumDataItems (k, 0, &szName, &pByteBuf, reinterpret_cast(&szMimeType))) { k++; if (szName == 0) continue; if (UT_strcmp (dataid, szName) == 0) break; szName = 0; szMimeType = 0; pByteBuf = 0; } if (pByteBuf) // this should always be found, but just in case... { multiBoundary (); m_utf8_1 = "image/png"; multiField ("Content-Type", m_utf8_1); m_utf8_1 = "base64"; multiField ("Content-Transfer-Encoding", m_utf8_1); multiField ("Content-Location", *url); _writeImageBase64 (pByteBuf); multiBreak (); } DELETEP(url); } m_SavedURLs.clear (); } void s_HTML_Listener::_handleField (const PX_ChangeRecord_Object * pcro, PT_AttrPropIndex api) { const PP_AttrProp * pAP = 0; bool bHaveProp = m_pDocument->getAttrProp (api, &pAP); if (!bHaveProp || (pAP == 0)) return; const XML_Char * szType = 0; pAP->getAttribute ("type", szType); if (szType == 0) return; fd_Field * field = pcro->getField (); if (UT_strcmp (szType, "list_label") != 0) { m_utf8_1 = "span"; m_utf8_1 += " class=\"ABI_FIELD_"; m_utf8_1 += szType; m_utf8_1 += "\""; tagOpen (TT_SPAN, m_utf8_1, ws_None); textUntrusted (field->getValue ()); m_utf8_1 = "span"; tagClose (TT_SPAN, m_utf8_1, ws_None); } } void s_HTML_Listener::_handleHyperlink (PT_AttrPropIndex api) { m_utf8_1 = "a"; if (tagTop () == TT_A) { tagClose (TT_A, m_utf8_1, ws_None); } const PP_AttrProp * pAP = 0; bool bHaveProp = (api ? (m_pDocument->getAttrProp (api, &pAP)) : false); if (!bHaveProp || (pAP == 0)) return; const XML_Char * szHRef = 0; pAP->getAttribute ("xlink:href", szHRef); if (szHRef) // trust this to be a valid URL?? { m_utf8_1 += " href=\""; m_utf8_1 += szHRef; m_utf8_1 += "\""; tagOpen (TT_A, m_utf8_1, ws_None); } } void s_HTML_Listener::_handleBookmark (PT_AttrPropIndex api) { m_utf8_1 = "a"; if (tagTop () == TT_A) { tagClose (TT_A, m_utf8_1, ws_None); } const PP_AttrProp * pAP = 0; bool bHaveProp = (api ? (m_pDocument->getAttrProp (api, &pAP)) : false); if (!bHaveProp || (pAP == 0)) return; const XML_Char * szType = 0; pAP->getAttribute ("type", szType); if (szType == 0) return; // ?? if (UT_XML_stricmp (szType, "start") == 0) { const XML_Char * szName = 0; pAP->getAttribute ("name", szName); if (szName) { m_utf8_1 += " name=\""; m_utf8_1 += szName; m_utf8_1 += "\""; if (!get_HTML4 ()) { m_utf8_1 += " id=\""; m_utf8_1 += szName; m_utf8_1 += "\""; } tagOpen (TT_A, m_utf8_1, ws_None); } } } #ifdef HTML_META_SUPPORTED void s_HTML_Listener::_handleMetaTag (const char * key, UT_UTF8String & value) { m_utf8_1 = "meta name=\""; m_utf8_1 += key; m_utf8_1 += "\" content=\""; m_utf8_1 += value.escapeXML (); m_utf8_1 += "\""; tagOpenClose (m_utf8_1, get_HTML4 ()); } void s_HTML_Listener::_handleMeta () { UT_UTF8String metaProp; if (m_pDocument->getMetaDataProp (PD_META_KEY_TITLE, metaProp) && metaProp.size ()) _handleMetaTag ("Title", metaProp); if (m_pDocument->getMetaDataProp (PD_META_KEY_CREATOR, metaProp) && metaProp.size ()) _handleMetaTag ("Author", metaProp); if (m_pDocument->getMetaDataProp (PD_META_KEY_KEYWORDS, metaProp) && metaProp.size ()) _handleMetaTag ("Keywords", metaProp); if (m_pDocument->getMetaDataProp (PD_META_KEY_SUBJECT, metaProp) && metaProp.size ()) _handleMetaTag ("Subject", metaProp); #if 0 // now generically dump all of our data to meta stuff const void * val = NULL ; for ( val = cursor.first(); cursor.is_valid(); val = cursor.next () ) { if ( val ) { UT_String *stringval = static_cast(val); if(stringval->size () > 0) { _handleMetaTag(cursor.key().c_str(), stringval->c_str())); } } } #endif } #endif /* HTML_META_SUPPORTED */ bool s_HTML_Listener::populate (PL_StruxFmtHandle /*sfh*/, const PX_ChangeRecord * pcr) { if (m_bFirstWrite && m_bClipBoard) { _openSection (0); _openTag (0, 0); } if(m_bIgnoreTillEnd) { return true; } switch (pcr->getType ()) { case PX_ChangeRecord::PXT_InsertSpan: { const PX_ChangeRecord_Span * pcrs = 0; pcrs = static_cast(pcr); PT_AttrPropIndex api = pcr->getIndexAP (); _openSpan (api); PT_BufIndex bi = pcrs->getBufIndex (); _outputData (m_pDocument->getPointer (bi), pcrs->getLength ()); // don't _closeSpan (); - leave open in case of identical sequences return true; } case PX_ChangeRecord::PXT_InsertObject: { if (m_bInSpan) _closeSpan (); m_bWroteText = true; const PX_ChangeRecord_Object * pcro = 0; pcro = static_cast(pcr); PT_AttrPropIndex api = pcr->getIndexAP (); switch (pcro->getObjectType ()) { case PTO_Image: _handleImage (api); return true; case PTO_Field: _handleField (pcro, api); return true; case PTO_Hyperlink: _handleHyperlink (api); return true; case PTO_Bookmark: _handleBookmark (api); return true; default: UT_DEBUGMSG(("WARNING: ie_exp_HTML.cpp: unhandled object type!\n")); return false; } } case PX_ChangeRecord::PXT_InsertFmtMark: return true; default: UT_DEBUGMSG(("WARNING: ie_exp_HTML.cpp: unhandled record type!\n")); return false; } } bool s_HTML_Listener::populateStrux (PL_StruxDocHandle sdh, const PX_ChangeRecord * pcr, PL_StruxFmtHandle * psfh) { UT_ASSERT(pcr->getType() == PX_ChangeRecord::PXT_InsertStrux); *psfh = 0; // we don't need it. const PX_ChangeRecord_Strux * pcrx = static_cast(pcr); PT_AttrPropIndex api = pcr->getIndexAP (); switch (pcrx->getStruxType ()) { case PTX_SectionHdrFtr: case PTX_Section: { if(m_bIgnoreTillEnd) { return true; } if (m_bInBlock) _closeTag (); // possible problem with lists?? _openSection (api); return true; } case PTX_Block: { if(m_bIgnoreTillEnd) { return true; } if (m_bFirstWrite && m_bClipBoard) _openSection (0); _openTag (api, sdh); return true; } #ifdef HTML_TABLES_SUPPORTED case PTX_SectionTable: { if(m_bIgnoreTillEnd) { return true; } if (m_bFirstWrite && m_bClipBoard) _openSection (0); m_TableHelper.OpenTable(sdh,pcr->getIndexAP()) ; _closeSpan(); _closeTag(); _openTable(pcr->getIndexAP()); return true; } case PTX_SectionCell: { if(m_bIgnoreTillEnd) { return true; } if(m_TableHelper.getNestDepth() <1) { m_TableHelper.OpenTable(sdh,pcr->getIndexAP()) ; _closeSpan(); _closeTag(); _openTable(pcr->getIndexAP()); } m_TableHelper.OpenCell(pcr->getIndexAP()) ; _closeSpan(); _closeTag(); _openCell(pcr->getIndexAP()); return true; } case PTX_EndTable: { if(m_bIgnoreTillEnd) { return true; } _closeTag(); m_TableHelper.CloseTable(); _closeTable(); return true; } case PTX_EndCell: { if(m_bIgnoreTillEnd) { return true; } _closeTag(); _closeCell(); if(m_TableHelper.getNestDepth() <1) { return true; } m_TableHelper.CloseCell(); return true; } #endif /* HTML_TABLES_SUPPORTED */ case PTX_SectionFootnote: case PTX_SectionEndnote: { m_iEmbedStartPos = pcrx->getPosition() + 1; m_bIgnoreTillEnd = true; return true; } case PTX_EndFootnote: case PTX_EndEndnote: { PD_DocumentRange * pDocRange = new PD_DocumentRange(m_pDocument, m_iEmbedStartPos, pcrx->getPosition() -1); if(pcrx->getStruxType () == PTX_EndFootnote) { m_pie->addFootnote(pDocRange); } else { m_pie->addEndnote(pDocRange); } m_bIgnoreTillEnd = false; return true; } #if 0 case PTX_EndFrame: case PTX_EndMarginnote: case PTX_EndFootnote: case PTX_SectionFrame: case PTX_SectionMarginnote: case PTX_SectionFootnote: case PTX_EndEndnote: #endif default: UT_DEBUGMSG(("WARNING: ie_exp_HTML.cpp: unhandled strux type!\n")); return false; } } /*****************************************************************/ /*****************************************************************/ bool s_HTML_Listener::change (PL_StruxFmtHandle /*sfh*/, const PX_ChangeRecord * /*pcr*/) { UT_ASSERT(0); // this function is not used. return false; } bool s_HTML_Listener::insertStrux (PL_StruxFmtHandle /*sfh*/, const PX_ChangeRecord * /*pcr*/, PL_StruxDocHandle /*sdh*/, PL_ListenerId /* lid */, void (* /*pfnBindHandles*/)(PL_StruxDocHandle /* sdhNew */, PL_ListenerId /* lid */, PL_StruxFmtHandle /* sfhNew */)) { UT_ASSERT(0); // this function is not used. return false; } bool s_HTML_Listener::signal (UT_uint32 /* iSignal */) { UT_ASSERT(UT_SHOULD_NOT_HAPPEN); return false; } /*****************************************************************/ /*****************************************************************/ s_StyleTree::s_StyleTree (s_StyleTree * parent, const char * style_name, PD_Style * style) : m_pDocument(0), m_parent(parent), m_list(0), m_count(0), m_max(0), m_bInUse(false), m_style_name(style_name), m_class_name(style_name), m_class_list(style_name), m_style(style) { if ((m_style_name == "Heading 1") || (m_style_name == "Heading 2") || (m_style_name == "Heading 3") || (m_style_name == "Normal")) { m_class_name = ""; m_class_list = ""; } else { s_removeWhiteSpace (style_name, m_class_name); m_class_list = m_class_name; } if (parent->class_list () != "") { m_class_list += " "; m_class_list += parent->class_list (); } UT_uint32 j = 0; const XML_Char * szName = 0; const XML_Char * szValue = 0; UT_UTF8String name; UT_UTF8String value; while (style->getNthProperty (j++, szName, szValue)) { name = szName; value = szValue; /* map property names to CSS equivalents */ if (name == "text-position") { name = "vertical-align"; } else if (name == "bgcolor") { name = "background-color"; } else if (!is_CSS (szName)) continue; /* map property values to CSS equivalents */ if (name == "font-family") { if (!((value == "serif") || (value == "sans-serif") || (value == "cursive") || (value == "fantasy") || (value == "monospace"))) { value = "'"; value += szValue; value += "'"; } } else if ((name == "color") || (name == "background-color")) { if (value != "transparent") { value = "#"; value += szValue; } } const UT_UTF8String * cascade_value = lookup (name); if (cascade_value) if (value == *cascade_value) continue; m_map.ins (name, value); } } s_StyleTree::s_StyleTree (PD_Document * pDocument) : m_pDocument(pDocument), m_parent(0), m_list(0), m_count(0), m_max(0), m_bInUse(false), m_style_name("None"), m_class_name(""), m_class_list(""), m_style(0) { if (!m_map.ins (s_prop_list)) { UT_DEBUGMSG(("failed to add CSS defaults to map in root style-tree element!\n")); } } s_StyleTree::~s_StyleTree () { for (UT_uint32 i = 0; i < m_count; i++) { DELETEP(m_list[i]); } FREEP (m_list); } bool s_StyleTree::add (const char * style_name, PD_Style * style) { if (m_list == 0) { m_list = reinterpret_cast(malloc (8 * sizeof (s_StyleTree *))); if (m_list == 0) return false; m_max = 8; } if (m_count == m_max) { s_StyleTree ** more = 0; more = reinterpret_cast(realloc (m_list, (m_max + 8) * sizeof (s_StyleTree *))); if (more == 0) return false; m_list = more; m_max += 8; } s_StyleTree * tree = 0; UT_TRY { tree = new s_StyleTree(this,style_name,style); } UT_CATCH(UT_CATCH_ANY) { tree = 0; } if (tree == 0) return false; m_list[m_count++] = tree; return true; } bool s_StyleTree::add (const char * style_name, PD_Document * pDoc) { if ((pDoc == 0) || (style_name == 0) || (*style_name == 0)) return false; if (m_parent) return m_parent->add (style_name, pDoc); if (find (style_name)) return true; PD_Style * style = 0; pDoc->getStyle (style_name, &style); if (!style) return false; s_StyleTree * parent = 0; PD_Style * basis = style->getBasedOn (); if (basis) { parent = const_cast(find (basis)); if (parent == 0) { const XML_Char * basis_name = 0; basis->getAttribute (PT_NAME_ATTRIBUTE_NAME, basis_name); if (!basis_name) return false; if (!add (basis_name, pDoc)) return false; parent = const_cast(find (basis)); } } else parent = this; if (!parent) { UT_ASSERT_NOT_REACHED(); return false; } return parent->add (style_name, style); } void s_StyleTree::inUse () { m_bInUse = true; if (m_parent) m_parent->inUse (); } const s_StyleTree * s_StyleTree::findAndUse (const char * style_name) { const s_StyleTree * style_tree = find (style_name); if (style_tree) { s_StyleTree * tree = const_cast(style_tree); tree->inUse (); } return style_tree; } const s_StyleTree * s_StyleTree::find (const char * style_name) const { if (m_style_name == style_name) return this; const s_StyleTree * tree = 0; for (UT_uint32 i = 0; i < m_count; i++) { tree = m_list[i]->find (style_name); if (tree) break; } return tree; } const s_StyleTree * s_StyleTree::find (PD_Style * style) const { const XML_Char * style_name = 0; style->getAttribute (PT_NAME_ATTRIBUTE_NAME, style_name); if (!style_name) return false; return find (style_name); } bool s_StyleTree::descends (const char * style_name) const { if (m_parent == 0) return false; if (m_style_name == style_name) return true; return m_parent->descends (style_name); } void s_StyleTree::print (s_HTML_Listener * listener) const { if (!m_bInUse) return; if (strstr (m_style_name.utf8_str (), "List")) return; if (m_parent) { UT_UTF8String selector("*."); if (m_class_name.byteLength ()) { selector += m_class_name; } else { if (m_style_name == "Normal") selector = "p, h1, h2, h3, li"; else if (m_style_name == "Heading 1") selector = "h1"; else if (m_style_name == "Heading 2") selector = "h2"; else if (m_style_name == "Heading 3") selector = "h3"; } listener->styleOpen (selector); UT_uint32 count = m_map.count (); for (UT_uint32 index = 0; index < count; index++) { const UT_UTF8String * key = 0; const UT_UTF8String * value = 0; m_map.pair (index, key, value); listener->styleNameValue (key->utf8_str (), *value); } listener->styleClose (); } for (UT_uint32 i = 0; i < m_count; i++) { m_list[i]->print (listener); } } const UT_UTF8String * s_StyleTree::lookup (const UT_UTF8String & prop_name) { const UT_UTF8String * prop_value = 0; prop_value = m_map[prop_name]; if (!prop_value && m_parent) prop_value = m_parent->lookup (prop_name); return prop_value; } void s_StyleTree::styleCheck (PT_AttrPropIndex api) { const PP_AttrProp * pAP = 0; bool bHaveProp = (api ? (m_pDocument->getAttrProp (api, &pAP)) : false); if (bHaveProp && pAP) { const XML_Char * szStyle = NULL; bool have_style = pAP->getAttribute (PT_STYLE_ATTRIBUTE_NAME, szStyle); if (have_style && szStyle) { findAndUse (szStyle); } } } bool s_StyleTree::populate (PL_StruxFmtHandle /*sfh*/, const PX_ChangeRecord * pcr) { switch (pcr->getType ()) { case PX_ChangeRecord::PXT_InsertSpan: styleCheck (pcr->getIndexAP ()); break; default: break; } return true; } bool s_StyleTree::populateStrux (PL_StruxDocHandle /*sdh*/, const PX_ChangeRecord * pcr, PL_StruxFmtHandle * psfh) { UT_ASSERT(pcr->getType() == PX_ChangeRecord::PXT_InsertStrux); *psfh = 0; // we don't need it. const PX_ChangeRecord_Strux * pcrx = static_cast(pcr); switch (pcrx->getStruxType ()) { case PTX_Block: styleCheck (pcr->getIndexAP ()); break; default: break; } return true; } bool s_StyleTree::change (PL_StruxFmtHandle /*sfh*/, const PX_ChangeRecord * /*pcr*/) { UT_ASSERT(0); // this function is not used. return false; } bool s_StyleTree::insertStrux (PL_StruxFmtHandle /*sfh*/, const PX_ChangeRecord * /*pcr*/, PL_StruxDocHandle /*sdh*/, PL_ListenerId /* lid */, void (* /*pfnBindHandles*/)(PL_StruxDocHandle /* sdhNew */, PL_ListenerId /* lid */, PL_StruxFmtHandle /* sfhNew */)) { UT_ASSERT(0); // this function is not used. return false; } bool s_StyleTree::signal (UT_uint32 /* iSignal */) { UT_ASSERT(UT_SHOULD_NOT_HAPPEN); return false; } /*****************************************************************/ /*****************************************************************/ #ifdef TH_SKIP_REST #undef TH_SKIP_REST #endif #define TH_SKIP_REST 1 #ifdef TH_SKIP_THIS #undef TH_SKIP_THIS #endif #define TH_SKIP_THIS 2 class ABI_EXPORT s_TemplateHandler : public UT_XML::ExpertListener { public: s_TemplateHandler (PD_Document * pDocument, IE_Exp_HTML * pie); ~s_TemplateHandler (); /* Implementation of ExpertListener */ void StartElement (const XML_Char * name, const XML_Char ** atts); void EndElement (const XML_Char * name); void CharData (const XML_Char * buffer, int length); void ProcessingInstruction (const XML_Char * target, const XML_Char * data); void Comment (const XML_Char * data); void StartCdataSection (); void EndCdataSection (); void Default (const XML_Char * buffer, int length); private: void _handleMetaTag (const char * key, UT_UTF8String & value); void _handleMeta (); bool echo () const; bool condition (const XML_Char * data) const; PD_Document * m_pDocument; IE_Exp_HTML * m_pie; bool m_cdata; bool m_empty; UT_UTF8String m_utf8; UT_UTF8String m_root; UT_UTF8Hash m_hash; UT_NumberStack m_mode; }; s_TemplateHandler::s_TemplateHandler (PD_Document * pDocument, IE_Exp_HTML * pie) : m_pDocument(pDocument), m_pie(pie), m_cdata(false), m_empty(false) { const UT_UTF8String * prop = m_pie->getProperty ("href-prefix"); if (prop) m_root = *prop; } s_TemplateHandler::~s_TemplateHandler () { // } void s_TemplateHandler::StartElement (const XML_Char * name, const XML_Char ** atts) { if (!echo ()) return; if (m_empty) { m_pie->write (">", 1); m_empty = false; } m_utf8 = "<"; m_utf8 += name; if (atts) { const XML_Char ** attr = atts; UT_UTF8String tmp; while (*attr) { bool href = ( (strcmp (*attr, "href") == 0) || ((strcmp (*attr, "src" ) == 0) && (strcmp (name, "img") == 0)) ); m_utf8 += " "; m_utf8 += *attr++; m_utf8 += "=\""; if (href && (**attr == '$')) { tmp = m_root; tmp += (*attr++ + 1); } else { tmp = *attr++; } tmp.escapeXML (); m_utf8 += tmp; m_utf8 += "\""; } } m_pie->write (m_utf8.utf8_str (), m_utf8.byteLength ()); m_empty = true; } void s_TemplateHandler::EndElement (const XML_Char * name) { if (!echo ()) return; if (m_empty) { m_pie->write (" />", 3); m_empty = false; } else { m_utf8 = ""; m_pie->write (m_utf8.utf8_str (), m_utf8.byteLength ()); } } void s_TemplateHandler::CharData (const XML_Char * buffer, int length) { if (!echo ()) return; if (m_empty) { m_pie->write (">", 1); m_empty = false; } if (m_cdata) { m_pie->write (buffer, length); return; } m_utf8 = buffer; m_utf8.escapeXML (); m_pie->write (m_utf8.utf8_str (), m_utf8.byteLength ()); } void s_TemplateHandler::ProcessingInstruction (const XML_Char * target, const XML_Char * data) { bool bAbiXHTML = (strncmp (target, "abi-xhtml-", 10) == 0); if (!bAbiXHTML && !echo ()) return; if (m_empty) { m_pie->write (">", 1); m_empty = false; } if (!bAbiXHTML) { /* processing instruction not relevant to this - could be PHP, etc. */ m_utf8 = ""; m_pie->write (m_utf8.utf8_str (), m_utf8.byteLength ()); return; } m_utf8 = target + 10; if ((m_utf8 == "insert") && echo ()) { m_utf8 = data; if (m_utf8 == "title") { #ifdef HTML_META_SUPPORTED m_utf8 = ""; m_pDocument->getMetaDataProp (PD_META_KEY_TITLE, m_utf8); if (m_utf8.byteLength () == 0) m_utf8 = m_pie->getFileName (); m_utf8.escapeXML (); m_pie->write (m_utf8.utf8_str (), m_utf8.byteLength ()); #endif /* HTML_META_SUPPORTED */ } else if (m_utf8 == "creator") { #ifdef HTML_META_SUPPORTED m_utf8 = ""; m_pDocument->getMetaDataProp (PD_META_KEY_CREATOR, m_utf8); if (m_utf8.byteLength ()) { m_utf8.escapeXML (); m_pie->write (m_utf8.utf8_str (), m_utf8.byteLength ()); } #endif /* HTML_META_SUPPORTED */ } else if (m_utf8 == "meta") { #ifdef HTML_META_SUPPORTED _handleMeta (); #endif /* HTML_META_SUPPORTED */ } else if (m_utf8 == "body") { m_pie->_writeDocument (false, true); } } else if ((m_utf8 == "comment-replace") && echo ()) { m_hash.clear (); m_hash.parse_attributes (data); const UT_UTF8String * sz_property = m_hash["property"]; const UT_UTF8String * sz_comment = m_hash["comment"]; if (sz_property && sz_comment) { #ifdef HTML_META_SUPPORTED UT_UTF8String creator = ""; #endif /* HTML_META_SUPPORTED */ const UT_UTF8String * prop = 0; if (*sz_property == "meta::creator") { #ifdef HTML_META_SUPPORTED m_pDocument->getMetaDataProp (PD_META_KEY_CREATOR, creator); if (creator.byteLength ()) prop = &creator; #endif /* HTML_META_SUPPORTED */ } else { prop = m_pie->getProperty (sz_property->utf8_str ()); } if (prop) { const UT_UTF8String DD("$$"); m_utf8 = *sz_comment; m_utf8.escape (DD, *prop); m_pie->write ("", 3); } } } else if ((m_utf8 == "menuitem") && echo ()) { m_hash.clear (); m_hash.parse_attributes (data); const UT_UTF8String * sz_property = m_hash["property"]; const UT_UTF8String * sz_class = m_hash["class"]; const UT_UTF8String * sz_href = m_hash["href"]; const UT_UTF8String * sz_label = m_hash["label"]; if (sz_property && sz_class && sz_href && sz_label) { const char * href = sz_href->utf8_str (); if (*href == '$') { m_utf8 = m_root; m_utf8 += href + 1; m_hash.ins ("href", m_utf8); sz_href = m_hash["href"]; } const UT_UTF8String * prop = m_pie->getProperty (sz_property->utf8_str ()); bool ne = (prop ? (*prop != *sz_class) : true); m_utf8 = "
"; m_utf8 += *sz_label; m_utf8 += "
"; if (ne) { m_utf8 += "
"; } m_utf8 += ""; m_pie->write (m_utf8.utf8_str (), m_utf8.byteLength ()); } } else if (m_utf8 == "if") { if (echo ()) { if (condition (data)) m_mode.push (0); else m_mode.push (TH_SKIP_THIS); } else { m_mode.push (TH_SKIP_REST); } } else if (m_mode.getDepth ()) { UT_sint32 mode; m_mode.viewTop (mode); if (m_utf8 == "elif") { if (mode == TH_SKIP_THIS) { if (condition (data)) { m_mode.pop (); m_mode.push (0); } } else if (mode != TH_SKIP_REST) { m_mode.pop (); m_mode.push (TH_SKIP_REST); } } else if (m_utf8 == "else") { if (mode == TH_SKIP_THIS) { if (condition (data)) { m_mode.pop (); m_mode.push (0); } } else if (mode != TH_SKIP_REST) { m_mode.pop (); m_mode.push (TH_SKIP_REST); } } else if (m_utf8 == "fi") { m_mode.pop (); } } } #ifdef HTML_META_SUPPORTED void s_TemplateHandler::_handleMetaTag (const char * key, UT_UTF8String & value) { m_utf8 = "\r\n"; m_pie->write (m_utf8.utf8_str (), m_utf8.byteLength ()); } void s_TemplateHandler::_handleMeta () { UT_UTF8String metaProp = "\r\n"; m_pie->write (metaProp.utf8_str (), metaProp.byteLength ()); if (m_pDocument->getMetaDataProp (PD_META_KEY_CREATOR, metaProp) && metaProp.size ()) _handleMetaTag ("Author", metaProp); if (m_pDocument->getMetaDataProp (PD_META_KEY_KEYWORDS, metaProp) && metaProp.size ()) _handleMetaTag ("Keywords", metaProp); if (m_pDocument->getMetaDataProp (PD_META_KEY_SUBJECT, metaProp) && metaProp.size ()) _handleMetaTag ("Subject", metaProp); } #endif /* HTML_META_SUPPORTED */ bool s_TemplateHandler::echo () const { if (!m_mode.getDepth ()) return true; UT_sint32 mode; m_mode.viewTop (mode); return (mode == 0); } bool s_TemplateHandler::condition (const XML_Char * data) const { const char * eq = strstr (data, "=="); const char * ne = strstr (data, "!="); if (!eq && !ne) return false; if (eq && ne) { if (eq < ne) ne = 0; else eq = 0; } UT_UTF8String var; const char * value = NULL; if (eq) { var.assign (data, eq - data); value = eq + 2; } else { var.assign (data, ne - data); value = ne + 2; } const UT_UTF8String * prop = m_pie->getProperty (var.utf8_str ()); bool match; if (prop) match = (*prop == value); else match = (*value == 0); return (eq ? match : !match); } void s_TemplateHandler::Comment (const XML_Char * data) { if (!echo ()) return; if (m_empty) { m_pie->write (">", 1); m_empty = false; } m_pie->write ("", 3); } void s_TemplateHandler::StartCdataSection () { if (!echo ()) return; if (m_empty) { m_pie->write (">", 1); m_empty = false; } m_pie->write ("write (">", 1); m_empty = false; } m_pie->write ("]]>", 3); m_cdata = false; } void s_TemplateHandler::Default (const XML_Char * buffer, int length) { // do nothing } /*****************************************************************/ /*****************************************************************/ IE_Exp_HTML::IE_Exp_HTML (PD_Document * pDocument) : IE_Exp(pDocument), m_style_tree(new s_StyleTree(pDocument)), m_bSuppressDialog(false) { m_exp_opt.bIs4 = false; m_exp_opt.bIsAbiWebDoc = false; m_exp_opt.bDeclareXML = true; m_exp_opt.bAllowAWML = true; m_exp_opt.bEmbedCSS = true; m_exp_opt.bEmbedImages = false; m_exp_opt.bMultipart = false; m_error = UT_OK; #ifdef HTML_DIALOG_OPTIONS XAP_Dialog_HTMLOptions::getHTMLDefaults (&m_exp_opt, pDocument->getApp ()); #endif } IE_Exp_HTML::~IE_Exp_HTML () { DELETEP(m_style_tree); UT_VECTOR_PURGEALL(PD_DocumentRange *,m_vecFootnotes); UT_VECTOR_PURGEALL(PD_DocumentRange *,m_vecEndnotes); // } void IE_Exp_HTML::_buildStyleTree () { const PD_Style * p_pds = 0; const XML_Char * szStyleName = 0; for (size_t n = 0; getDoc()->enumStyles (n, &szStyleName, &p_pds); n++) { if (p_pds == 0) continue; PT_AttrPropIndex api = p_pds->getIndexAP (); const PP_AttrProp * pAP_style = 0; bool bHaveProp = getDoc()->getAttrProp (api, &pAP_style); if (bHaveProp && pAP_style /* && p_pds->isUsed () */) // can't trust ->isUsed() :-( { m_style_tree->add (szStyleName, getDoc ()); } } if (getDocRange ()) // clipboard getDoc()->tellListenerSubset (m_style_tree, getDocRange ()); else getDoc()->tellListener (m_style_tree); } UT_Error IE_Exp_HTML::_writeDocument () { _buildStyleTree (); if (getDocRange () != NULL) // ClipBoard { m_exp_opt.bEmbedImages = true; return _writeDocument (true, false); } /* Export options: html4 yes | no whether to write HTML4 or XHTML php-includes yes | no whether to add use as template for output href-prefix use as prefix for template href attributes marked with initial '$' */ const UT_UTF8String * prop = 0; prop = getProperty ("html4"); if (prop) m_exp_opt.bIs4 = UT_parseBool (prop->utf8_str (), m_exp_opt.bIs4); prop = getProperty ("php-includes"); if (prop) m_exp_opt.bIsAbiWebDoc = UT_parseBool (prop->utf8_str (), m_exp_opt.bIsAbiWebDoc); prop = getProperty ("declare-xml"); if (prop) m_exp_opt.bDeclareXML = UT_parseBool (prop->utf8_str (), m_exp_opt.bDeclareXML); prop = getProperty ("use-awml"); if (prop) m_exp_opt.bAllowAWML = UT_parseBool (prop->utf8_str (), m_exp_opt.bAllowAWML); prop = getProperty ("embed-css"); if (prop) m_exp_opt.bEmbedCSS = UT_parseBool (prop->utf8_str (), m_exp_opt.bEmbedCSS); prop = getProperty ("embed-images"); if (prop) m_exp_opt.bEmbedImages = UT_parseBool (prop->utf8_str (), m_exp_opt.bEmbedImages); prop = getProperty ("html-template"); if (!prop) return _writeDocument (false, false); /* template mode... */ m_exp_opt.bIs4 = false; UT_UTF8String declaration; if (m_exp_opt.bDeclareXML) declaration += "\r\n"; declaration += "<"; declaration += s_DTD_XHTML; declaration += ">\r\n"; write (declaration.utf8_str (), declaration.byteLength ()); s_TemplateHandler TH(getDoc(),this); UT_XML parser; parser.setExpertListener (&TH); UT_Error err = parser.parse (prop->utf8_str ()); return err; } UT_Error IE_Exp_HTML::_writeDocument (bool bClipBoard, bool bTemplateBody) { s_HTML_Listener * pListener = new s_HTML_Listener(getDoc(),this,bClipBoard,bTemplateBody,&m_exp_opt,m_style_tree); if (pListener == 0) return UT_IE_NOMEMORY; PL_Listener * pL = static_cast(pListener); bool okay = true; if (bClipBoard) { okay = getDoc()->tellListenerSubset (pL, getDocRange ()); } else okay = getDoc()->tellListener (pL); UT_sint32 i = 0; // // Output footnotes // for(i=0; i< getNumFootnotes(); i++) { PD_DocumentRange * pDocRange = reinterpret_cast(m_vecFootnotes.getNthItem(i)); pListener->startEmbeddedStrux(); okay = getDoc()->tellListenerSubset(pL,pDocRange); } // // Output Endnotes // for(i=0; i< getNumEndnotes(); i++) { PD_DocumentRange * pDocRange = reinterpret_cast(m_vecEndnotes.getNthItem(i)); pListener->startEmbeddedStrux(); okay = getDoc()->tellListenerSubset(pL,pDocRange); } DELETEP(pListener); if ((m_error == UT_OK) && (okay == true)) return UT_OK; return UT_IE_COULDNOTWRITE; } bool IE_Exp_HTML::_openFile (const char * szFilename) { #ifdef HTML_DIALOG_OPTIONS XAP_Frame * pFrame = getDoc()->getApp()->getLastFocussedFrame (); if (m_bSuppressDialog || !pFrame) return IE_Exp::_openFile (szFilename); /* run the dialog */ XAP_Dialog_Id id = XAP_DIALOG_ID_HTMLOPTIONS; XAP_DialogFactory * pDialogFactory = static_cast(getDoc()->getApp()->getDialogFactory ()); XAP_Dialog_HTMLOptions * pDialog = static_cast(pDialogFactory->requestDialog (id)); UT_return_val_if_fail (pDialog, false); pDialog->setHTMLOptions (&m_exp_opt, getDoc()->getApp ()); pDialog->runModal (pFrame); /* extract what they did */ bool bSave = pDialog->shouldSave (); pDialogFactory->releaseDialog (pDialog); if (!bSave) { _cancelExport (); return false; } #endif return IE_Exp::_openFile (szFilename); } void IE_Exp_HTML::addFootnote(PD_DocumentRange * pDocRange) { m_vecFootnotes.addItem(reinterpret_cast(pDocRange)); } void IE_Exp_HTML::addEndnote(PD_DocumentRange * pDocRange) { m_vecEndnotes.addItem(reinterpret_cast(pDocRange)); } UT_sint32 IE_Exp_HTML::getNumFootnotes(void) { return static_cast(m_vecFootnotes.getItemCount()); } UT_sint32 IE_Exp_HTML::getNumEndnotes(void) { return static_cast(m_vecEndnotes.getItemCount()); }