diff --git a/Common/3dParty/html/gumbo.pri b/Common/3dParty/html/gumbo.pri index 52446e7e60..8f8b255001 100644 --- a/Common/3dParty/html/gumbo.pri +++ b/Common/3dParty/html/gumbo.pri @@ -7,4 +7,5 @@ core_windows:INCLUDEPATH += $$PWD/gumbo-parser/visualc/include HEADERS += $$files($$PWD/gumbo-parser/src/*.h, true) \ $$PWD/htmltoxhtml.h -SOURCES += $$files($$PWD/gumbo-parser/src/*.c, true) +SOURCES += $$files($$PWD/gumbo-parser/src/*.c, true) \ + $$PWD/htmltoxhtml.cpp diff --git a/Common/3dParty/html/htmltoxhtml.cpp b/Common/3dParty/html/htmltoxhtml.cpp new file mode 100644 index 0000000000..5b1b9d2d6c --- /dev/null +++ b/Common/3dParty/html/htmltoxhtml.cpp @@ -0,0 +1,657 @@ +#include "htmltoxhtml.h" + +#include +#include +#include +#include + +#include "gumbo-parser/src/gumbo.h" +#include "../../../DesktopEditor/common/File.h" +#include "../../../DesktopEditor/common/Directory.h" +#include "../../../DesktopEditor/common/StringBuilder.h" +#include "../../../UnicodeConverter/UnicodeConverter.h" +#include "../../../HtmlFile2/src/StringFinder.h" + +namespace HTML +{ +#if defined(CreateDirectory) +#undef CreateDirectory +#endif + +static std::string nonbreaking_inline = "|a|abbr|acronym|b|bdo|big|cite|code|dfn|em|font|i|img|kbd|nobr|s|small|span|strike|strong|sub|sup|tt|"; +static std::string empty_tags = "|area|base|basefont|bgsound|br|command|col|embed|event-source|frame|hr|image|img|input|keygen|link|menuitem|meta|param|source|spacer|track|wbr|"; +static std::string preserve_whitespace = "|pre|textarea|script|style|"; +static std::string special_handling = "|html|body|"; +static std::string treat_like_inline = "|p|"; + +static std::vector html_tags = {"div","span","a","img","p","h1","h2","h3","h4","h5","h6", + "ul", "ol", "li","td","tr","table","thead","tbody","tfoot","th", + "br","form","input","button","section","nav","header","footer", + "main","figure","figcaption","strong","em","i", "b", "u","pre", + "code","blockquote","hr","script","link","meta","style","title", + "head","body","html","legend","optgroup","option","select","dl", + "dt","dd","time","data","abbr","address","area","base","bdi", + "bdo","cite","col","iframe","video","source","track","textarea", + "label","fieldset","colgroup","del","ins","details","summary", + "dialog","embed","kbd","map","mark","menu","meter","object", + "output","param","progress","q","samp","small","sub","sup","var", + "wbr","acronym","applet","article","aside","audio","basefont", + "bgsound","big","blink","canvas","caption","center","command", + "comment","datalist","dfn","dir","font","frame","frameset", + "hgroup","isindex","keygen","marquee","nobr","noembed","noframes", + "noscript","plaintext","rp","rt","ruby","s","strike","tt","xmp"}; + +static std::vector unchecked_nodes_new = {"svg"}; + +static void replace_all(std::string& s, const std::string& s1, const std::string& s2) +{ + size_t pos = s.find(s1); + while(pos != std::string::npos) + { + s.replace(pos, s1.length(), s2); + pos = s.find(s1, pos + s2.length()); + } +} + +static bool NodeIsUnprocessed(const std::string& sTagName) +{ + return "xml" == sTagName; +} + +static bool IsUnckeckedNodes(const std::string& sValue) +{ + return unchecked_nodes_new.end() != std::find(unchecked_nodes_new.begin(), unchecked_nodes_new.end(), sValue); +} + +static std::string Base64ToString(const std::string& sContent, const std::string& sCharset) +{ + std::string sRes; + int nSrcLen = (int)sContent.length(); + int nDecodeLen = NSBase64::Base64DecodeGetRequiredLength(nSrcLen); + BYTE* pData = new BYTE[nDecodeLen]; + if (TRUE == NSBase64::Base64Decode(sContent.c_str(), nSrcLen, pData, &nDecodeLen)) + { + std::wstring sConvert; + if(!sCharset.empty() && NSStringFinder::Equals("utf-8", sCharset)) + { + NSUnicodeConverter::CUnicodeConverter oConverter; + sConvert = oConverter.toUnicode(reinterpret_cast(pData), (unsigned)nDecodeLen, sCharset.data()); + } + sRes = sConvert.empty() ? std::string(reinterpret_cast(pData), nDecodeLen) : U_TO_UTF8(sConvert); + } + RELEASEARRAYOBJECTS(pData); + return sRes; +} + +static std::string QuotedPrintableDecode(const std::string& sContent, std::string& sCharset) +{ + NSStringUtils::CStringBuilderA sRes; + size_t ip = 0; + size_t i = sContent.find('='); + + if(i == 0) + { + size_t nIgnore = 12; + std::string charset = sContent.substr(0, nIgnore); + if(charset == "=00=00=FE=FF") + sCharset = "UTF-32BE"; + else if(charset == "=FF=FE=00=00") + sCharset = "UTF-32LE"; + else if(charset == "=2B=2F=76=38" || charset == "=2B=2F=76=39" || + charset == "=2B=2F=76=2B" || charset == "=2B=2F=76=2F") + sCharset = "UTF-7"; + else if(charset == "=DD=73=66=73") + sCharset = "UTF-EBCDIC"; + else if(charset == "=84=31=95=33") + sCharset = "GB-18030"; + else + { + nIgnore -= 3; + charset.erase(nIgnore); + if(charset == "=EF=BB=BF") + sCharset = "UTF-8"; + else if(charset == "=F7=64=4C") + sCharset = "UTF-1"; + else if(charset == "=0E=FE=FF") + sCharset = "SCSU"; + else if(charset == "=FB=EE=28") + sCharset = "BOCU-1"; + else + { + nIgnore -= 3; + charset.erase(nIgnore); + if(charset == "=FE=FF") + sCharset = "UTF-16BE"; + else if(charset == "=FF=FE") + sCharset = "UTF-16LE"; + else + nIgnore -= 6; + } + } + + ip = nIgnore; + i = sContent.find('=', ip); + } + + while(i != std::string::npos && i + 2 < sContent.length()) + { + sRes.WriteString(sContent.c_str() + ip, i - ip); + std::string str = sContent.substr(i + 1, 2); + if(str.front() == '\n' || str.front() == '\r') + { + char ch = str[1]; + if(ch != '\n' && ch != '\r') + sRes.WriteString(&ch, 1); + } + else + { + char* err; + char ch = (int)strtol(str.data(), &err, 16); + if(*err) + sRes.WriteString('=' + str); + else + sRes.WriteString(&ch, 1); + } + ip = i + 3; + i = sContent.find('=', ip); + } + if(ip != std::string::npos) + sRes.WriteString(sContent.c_str() + ip); + return sRes.GetData(); +} + +static std::string mhtTohtml(const std::string& sFileContent); + +static void ReadMht(const std::string& sMhtContent, std::map& sRes, NSStringUtils::CStringBuilderA& oRes) +{ + size_t unContentPosition = 0, unCharsetBegin = 0, unCharsetEnd = std::string::npos; + + NSStringFinder::TFoundedData oData; + + // Content-Type + oData = NSStringFinder::FindProperty(sMhtContent, "content-type", {":"}, {";", "\\n", "\\r"}); + const std::string sContentType{oData.m_sValue}; + + if (sContentType.empty()) + return; + + if (NSStringFinder::Equals(sContentType, "multipart/alternative")) + { + oRes.WriteString(mhtTohtml(sMhtContent.substr(oData.m_unEndPosition, sMhtContent.length() - oData.m_unEndPosition))); + return; + } + + unContentPosition = std::max(unContentPosition, oData.m_unEndPosition); + unCharsetBegin = oData.m_unEndPosition; + + // name +// std::string sName = NSStringFinder::FindProperty(sMhtContent, "name", {"="}, {";", "\\n", "\\r"}, 0, unLastPosition); +// unContentPosition = std::max(unContentPosition, unLastPosition); + + // Content-Location + oData = NSStringFinder::FindProperty(sMhtContent, "content-location", {":"}, {";", "\\n", "\\r"}); + std::string sContentLocation{oData.m_sValue}; + + if (!oData.Empty()) + unContentPosition = std::max(unContentPosition, oData.m_unEndPosition); + + // Content-ID + oData = NSStringFinder::FindProperty(sMhtContent, "content-id", {":"}, {";", "\\n", "\\r"}); + std::string sContentID{oData.m_sValue}; + + if (!oData.Empty()) + { + unContentPosition = std::max(unContentPosition, oData.m_unEndPosition); + unCharsetEnd = std::min(unCharsetEnd, oData.m_unBeginPosition); + NSStringFinder::CutInside(sContentID, "<", ">"); + } + + if (sContentLocation.empty() && !sContentID.empty()) + sContentLocation = "cid:" + sContentID; + + // Content-Transfer-Encoding + oData = NSStringFinder::FindProperty(sMhtContent, "content-transfer-encoding", {":"}, {";", "\\n", "\\r"}); + const std::string sContentEncoding{oData.m_sValue}; + + if (!oData.Empty()) + { + unContentPosition = std::max(unContentPosition, oData.m_unEndPosition); + unCharsetEnd = std::min(unCharsetEnd, oData.m_unBeginPosition); + } + + // charset + std::string sCharset = "utf-8"; + + if (std::string::npos != unCharsetEnd && unCharsetBegin < unCharsetEnd) + { + sCharset = NSStringFinder::FindProperty(sMhtContent.substr(unCharsetBegin, unCharsetEnd - unCharsetBegin), "charset", {"="}, {";", "\\n", "\\r"}).m_sValue; + NSStringFinder::CutInside(sCharset, "\""); + } + + // Content + std::string sContent = sMhtContent.substr(unContentPosition, sMhtContent.length() - unContentPosition); + +// std::wstring sExtention = NSFile::GetFileExtention(UTF8_TO_U(sName)); +// std::transform(sExtention.begin(), sExtention.end(), sExtention.begin(), tolower); + // Основной документ + if (NSStringFinder::Equals(sContentType, "multipart/alternative")) + oRes.WriteString(mhtTohtml(sContent)); + else if ((NSStringFinder::Find(sContentType, "text") /*&& (sExtention.empty() || NSStringFinder::EqualOf(sExtention, {L"htm", L"html", L"xhtml", L"css"}))*/) + || (NSStringFinder::Equals(sContentType, "application/octet-stream") && NSStringFinder::Find(sContentLocation, "css"))) + { + // Стили заключаются в тэг "); + } + // Картинки + else if ((NSStringFinder::Find(sContentType, "image") /*|| NSStringFinder::Equals(sExtention, L"gif")*/ || NSStringFinder::Equals(sContentType, "application/octet-stream")) && + NSStringFinder::Equals(sContentEncoding, "base64")) + { +// if (NSStringFinder::Equals(sExtention, L"ico") || NSStringFinder::Find(sContentType, "ico")) +// sContentType = "image/jpg"; +// else if(NSStringFinder::Equals(sExtention, L"gif")) +// sContentType = "image/gif"; + int nSrcLen = (int)sContent.length(); + int nDecodeLen = NSBase64::Base64DecodeGetRequiredLength(nSrcLen); + BYTE* pData = new BYTE[nDecodeLen]; + if (TRUE == NSBase64::Base64Decode(sContent.c_str(), nSrcLen, pData, &nDecodeLen)) + sRes.insert(std::make_pair(sContentLocation, "data:" + sContentType + ";base64," + sContent)); + RELEASEARRAYOBJECTS(pData); + } +} + +static std::string mhtTohtml(const std::string& sFileContent) +{ + std::map sRes; + NSStringUtils::CStringBuilderA oRes; + + // Поиск boundary + NSStringFinder::TFoundedData oData{NSStringFinder::FindProperty(sFileContent, "boundary", {"="}, {"\\r", "\\n", "\""})}; + + size_t nFound{oData.m_unEndPosition}; + std::string sBoundary{oData.m_sValue}; + + if (sBoundary.empty()) + { + size_t nFoundEnd = sFileContent.length(); + nFound = 0; + ReadMht(sFileContent.substr(nFound, nFoundEnd), sRes, oRes); + return oRes.GetData(); + } + + NSStringFinder::CutInside(sBoundary, "\""); + + size_t nFoundEnd{nFound}; + + sBoundary = "--" + sBoundary; + size_t nBoundaryLength = sBoundary.length(); + + nFound = sFileContent.find(sBoundary, nFound) + nBoundaryLength; + + // Цикл по boundary + while(nFound != std::string::npos) + { + nFoundEnd = sFileContent.find(sBoundary, nFound + nBoundaryLength); + if(nFoundEnd == std::string::npos) + break; + + ReadMht(sFileContent.substr(nFound, nFoundEnd - nFound), sRes, oRes); + + nFound = sFileContent.find(sBoundary, nFoundEnd); + } + + std::string sFile = oRes.GetData(); + for(const std::pair& item : sRes) + { + std::string sName = item.first; + size_t found = sFile.find(sName); + size_t sfound = sName.rfind('/'); + if(found == std::string::npos && sfound != std::string::npos) + found = sFile.find(sName.erase(0, sfound + 1)); + while(found != std::string::npos) + { + size_t fq = sFile.find_last_of("\"\'>=", found); + + if (std::string::npos == fq) + break; + + char ch = sFile[fq]; + if(ch != '\"' && ch != '\'') + fq++; + size_t tq = sFile.find_first_of("\"\'<> ", found) + 1; + + if (std::string::npos == tq) + break; + + if(sFile[tq] != '\"' && sFile[tq] != '\'') + tq--; + if(ch != '>') + { + std::string is = '\"' + item.second + '\"'; + sFile.replace(fq, tq - fq, is); + found = sFile.find(sName, fq + is.length()); + } + else + found = sFile.find(sName, tq); + } + } + + return sFile; +} + +// Заменяет сущности &,<,> в text +static void substitute_xml_entities_into_text(std::string& text) +{ + // replacing & must come first + replace_all(text, "&", "&"); + replace_all(text, "<", "<"); + replace_all(text, ">", ">"); +} + +// After running through Gumbo, the values of type "" are replaced with the corresponding code '0x01' +// Since the attribute value does not use control characters (value <= 0x09), +// then just delete them, otherwise XmlUtils::CXmlLiteReader crashes on them. +// bug#73486 +static void remove_control_symbols(std::string& text) +{ + std::string::iterator itFound = std::find_if(text.begin(), text.end(), [](unsigned char chValue){ return chValue <= 0x09; }); + + while (itFound != text.end()) + { + itFound = text.erase(itFound); + itFound = std::find_if(itFound, text.end(), [](unsigned char chValue){ return chValue <= 0x09; }); + } +} + +// Заменяет сущности " в text +static void substitute_xml_entities_into_attributes(std::string& text) +{ + remove_control_symbols(text); + substitute_xml_entities_into_text(text); + replace_all(text, "\"", """); +} + +static std::string handle_unknown_tag(GumboStringPiece* text) +{ + if (text->data == NULL) + return ""; + GumboStringPiece gsp = *text; + gumbo_tag_from_original_text(&gsp); + std::string sAtr = std::string(gsp.data, gsp.length); + size_t found = sAtr.find_first_of("-'+,./=?;!*#@$_%<>&;\"\'()[]{}"); + while(found != std::string::npos) + { + sAtr.erase(found, 1); + found = sAtr.find_first_of("-'+,./=?;!*#@$_%<>&;\"\'()[]{}", found); + } + return sAtr; +} + +static std::string get_tag_name(GumboNode* node) +{ + std::string tagname = (node->type == GUMBO_NODE_DOCUMENT ? "document" : gumbo_normalized_tagname(node->v.element.tag)); + if (tagname.empty()) + tagname = handle_unknown_tag(&node->v.element.original_tag); + return tagname; +} + +static void build_doctype(GumboNode* node, NSStringUtils::CStringBuilderA& oBuilder) +{ + if (node->v.document.has_doctype) + { + oBuilder.WriteString("v.document.name); + std::string pi(node->v.document.public_identifier); + remove_control_symbols(pi); + if ((node->v.document.public_identifier != NULL) && !pi.empty()) + { + oBuilder.WriteString(" PUBLIC \""); + oBuilder.WriteString(pi); + oBuilder.WriteString("\" \""); + oBuilder.WriteString(node->v.document.system_identifier); + oBuilder.WriteString("\""); + } + oBuilder.WriteString(">"); + } +} + +static void build_attributes(const GumboVector* attribs, NSStringUtils::CStringBuilderA& atts) +{ + std::vector arrRepeat; + for (size_t i = 0; i < attribs->length; ++i) + { + GumboAttribute* at = static_cast(attribs->data[i]); + std::string sVal(at->value); + std::string sName(at->name); + + remove_control_symbols(sVal); + remove_control_symbols(sName); + + atts.WriteString(" "); + + bool bCheck = false; + size_t nBad = sName.find_first_of("+,.=?#%<>&;\"\'()[]{}"); + while(nBad != std::string::npos) + { + sName.erase(nBad, 1); + nBad = sName.find_first_of("+,.=?#%<>&;\"\'()[]{}", nBad); + if(sName.empty()) + break; + bCheck = true; + } + if(sName.empty()) + continue; + while(sName.front() >= '0' && sName.front() <= '9') + { + sName.erase(0, 1); + if(sName.empty()) + break; + bCheck = true; + } + if(bCheck) + { + GumboAttribute* check = gumbo_get_attribute(attribs, sName.c_str()); + if(check || std::find(arrRepeat.begin(), arrRepeat.end(), sName) != arrRepeat.end()) + continue; + else + arrRepeat.push_back(sName); + } + + if(sName.empty()) + continue; + atts.WriteString(sName); + + // determine original quote character used if it exists + std::string qs ="\""; + atts.WriteString("="); + atts.WriteString(qs); + substitute_xml_entities_into_attributes(sVal); + atts.WriteString(sVal); + atts.WriteString(qs); + } +} + +static void prettyprint(GumboNode* node, NSStringUtils::CStringBuilderA& oBuilder, bool bCheckValidNode = true); + +static void prettyprint_contents(GumboNode* node, NSStringUtils::CStringBuilderA& contents, bool bCheckValidNode) +{ + std::string key = "|" + get_tag_name(node) + "|"; + bool keep_whitespace = preserve_whitespace.find(key) != std::string::npos; + bool is_inline = nonbreaking_inline.find(key) != std::string::npos; + bool is_like_inline = treat_like_inline.find(key) != std::string::npos; + + GumboVector* children = &node->v.element.children; + + for (size_t i = 0; i < children->length; i++) + { + GumboNode* child = static_cast (children->data[i]); + + if (child->type == GUMBO_NODE_TEXT) + { + std::string val(child->v.text.text); + remove_control_symbols(val); + substitute_xml_entities_into_text(val); + + // Избавление от FF + size_t found = val.find_first_of("\014"); + while(found != std::string::npos) + { + val.erase(found, 1); + found = val.find_first_of("\014", found); + } + + contents.WriteString(val); + } + else if ((child->type == GUMBO_NODE_ELEMENT) || (child->type == GUMBO_NODE_TEMPLATE)) + prettyprint(child, contents, bCheckValidNode); + else if (child->type == GUMBO_NODE_WHITESPACE) + { + if (keep_whitespace || is_inline || is_like_inline) + contents.WriteString(child->v.text.text); + } + else if (child->type != GUMBO_NODE_COMMENT) + { + // Сообщение об ошибке + // Does this actually exist: (child->type == GUMBO_NODE_CDATA) + // fprintf(stderr, "unknown element of type: %d\n", child->type); + } + } +} + +static void prettyprint(GumboNode* node, NSStringUtils::CStringBuilderA& oBuilder, bool bCheckValidNode) +{ + // special case the document node + if (node->type == GUMBO_NODE_DOCUMENT) + { + build_doctype(node, oBuilder); + prettyprint_contents(node, oBuilder, bCheckValidNode); + return; + } + + std::string tagname = get_tag_name(node); + remove_control_symbols(tagname); + + if (NodeIsUnprocessed(tagname)) + return; + + if (bCheckValidNode) + bCheckValidNode = !IsUnckeckedNodes(tagname); + + if (bCheckValidNode && html_tags.end() == std::find(html_tags.begin(), html_tags.end(), tagname)) + { + prettyprint_contents(node, oBuilder, bCheckValidNode); + return; + } + + std::string close = ""; + std::string closeTag = ""; + std::string key = "|" + tagname + "|"; + bool is_empty_tag = empty_tags.find(key) != std::string::npos; + + // determine closing tag type + if (is_empty_tag) + close = "/"; + else + closeTag = ""; + + // build results + oBuilder.WriteString("<" + tagname); + + // build attr string + const GumboVector* attribs = &node->v.element.attributes; + build_attributes(attribs, oBuilder); + oBuilder.WriteString(close + ">"); + + // prettyprint your contents + prettyprint_contents(node, oBuilder, bCheckValidNode); + oBuilder.WriteString(closeTag); +} + +std::wstring htmlToXhtml(std::string& sFileContent, bool bNeedConvert) +{ + if (bNeedConvert) + { // Определение кодировки + std::string sEncoding = NSStringFinder::FindProperty(sFileContent, "charset", {"="}, {";", "\\n", "\\r", " ", "\"", "'"}).m_sValue; + + if (sEncoding.empty()) + sEncoding = NSStringFinder::FindProperty(sFileContent, "encoding", {"="}, {";", "\\n", "\\r", " "}).m_sValue; + + if (!sEncoding.empty() && !NSStringFinder::Equals("utf-8", sEncoding)) + { + NSUnicodeConverter::CUnicodeConverter oConverter; + sFileContent = U_TO_UTF8(oConverter.toUnicode(sFileContent, sEncoding.c_str())); + } + } + + // Избавляемся от лишних символов до <... + boost::regex oRegex("<[a-zA-Z]"); + boost::match_results oResult; + + if (boost::regex_search(sFileContent, oResult, oRegex)) + sFileContent.erase(0, oResult.position()); + + //Избавление от + while (NSStringFinder::RemoveEmptyTag(sFileContent, "a")); + //Избавление от + while (NSStringFinder::RemoveEmptyTag(sFileContent, "title")); + //Избавление от <script ... /> + while (NSStringFinder::RemoveEmptyTag(sFileContent, "script")); + + // Gumbo + GumboOptions options = kGumboDefaultOptions; + GumboOutput* output = gumbo_parse_with_options(&options, sFileContent.data(), sFileContent.length()); + + // prettyprint + NSStringUtils::CStringBuilderA oBuilder; + prettyprint(output->document, oBuilder); + + // Конвертирование из string utf8 в wstring + return UTF8_TO_U(oBuilder.GetData()); +} + +std::wstring mhtToXhtml(std::string& sFileContent) +{ + sFileContent = mhtTohtml(sFileContent); + + // Gumbo + GumboOptions options = kGumboDefaultOptions; + GumboOutput* output = gumbo_parse_with_options(&options, sFileContent.data(), sFileContent.length()); + + // prettyprint + NSStringUtils::CStringBuilderA oBuilder; + prettyprint(output->document, oBuilder); + + // Конвертирование из string utf8 в wstring + return UTF8_TO_U(oBuilder.GetData()); +} +} diff --git a/Common/3dParty/html/htmltoxhtml.h b/Common/3dParty/html/htmltoxhtml.h index 71e78b520f..29527b48e7 100644 --- a/Common/3dParty/html/htmltoxhtml.h +++ b/Common/3dParty/html/htmltoxhtml.h @@ -2,657 +2,11 @@ #define HTMLTOXHTML_H #include <string> -#include <map> -#include <cctype> -#include <vector> -#include <algorithm> -#include "gumbo-parser/src/gumbo.h" -#include "../../../DesktopEditor/common/File.h" -#include "../../../DesktopEditor/common/Directory.h" -#include "../../../DesktopEditor/common/StringBuilder.h" -#include "../../../UnicodeConverter/UnicodeConverter.h" -#include "../../../HtmlFile2/src/StringFinder.h" - -#if defined(CreateDirectory) -#undef CreateDirectory -#endif - -static std::string nonbreaking_inline = "|a|abbr|acronym|b|bdo|big|cite|code|dfn|em|font|i|img|kbd|nobr|s|small|span|strike|strong|sub|sup|tt|"; -static std::string empty_tags = "|area|base|basefont|bgsound|br|command|col|embed|event-source|frame|hr|image|img|input|keygen|link|menuitem|meta|param|source|spacer|track|wbr|"; -static std::string preserve_whitespace = "|pre|textarea|script|style|"; -static std::string special_handling = "|html|body|"; -static std::string treat_like_inline = "|p|"; - -static std::vector<std::string> html_tags = {"div","span","a","img","p","h1","h2","h3","h4","h5","h6", - "ul", "ol", "li","td","tr","table","thead","tbody","tfoot","th", - "br","form","input","button","section","nav","header","footer", - "main","figure","figcaption","strong","em","i", "b", "u","pre", - "code","blockquote","hr","script","link","meta","style","title", - "head","body","html","legend","optgroup","option","select","dl", - "dt","dd","time","data","abbr","address","area","base","bdi", - "bdo","cite","col","iframe","video","source","track","textarea", - "label","fieldset","colgroup","del","ins","details","summary", - "dialog","embed","kbd","map","mark","menu","meter","object", - "output","param","progress","q","samp","small","sub","sup","var", - "wbr","acronym","applet","article","aside","audio","basefont", - "bgsound","big","blink","canvas","caption","center","command", - "comment","datalist","dfn","dir","font","frame","frameset", - "hgroup","isindex","keygen","marquee","nobr","noembed","noframes", - "noscript","plaintext","rp","rt","ruby","s","strike","tt","xmp"}; - -static std::vector<std::string> unchecked_nodes_new = {"svg"}; - -static void prettyprint(GumboNode*, NSStringUtils::CStringBuilderA& oBuilder, bool bCheckValidNode = true); -static std::string mhtTohtml(const std::string &sFileContent); - -// Заменяет в строке s все символы s1 на s2 -static void replace_all(std::string& s, const std::string& s1, const std::string& s2) +namespace HTML { - size_t pos = s.find(s1); - while(pos != std::string::npos) - { - s.replace(pos, s1.length(), s2); - pos = s.find(s1, pos + s2.length()); - } -} - -static bool NodeIsUnprocessed(const std::string& wsTagName) -{ - return "xml" == wsTagName; -} - -static bool IsUnckeckedNodes(const std::string& sValue) -{ - return unchecked_nodes_new.end() != std::find(unchecked_nodes_new.begin(), unchecked_nodes_new.end(), sValue); -} - -static std::wstring htmlToXhtml(std::string& sFileContent, bool bNeedConvert) -{ - if (bNeedConvert) - { // Определение кодировки - std::string sEncoding = NSStringFinder::FindProperty(sFileContent, "charset", {"="}, {";", "\\n", "\\r", " ", "\"", "'"}).m_sValue; - - if (sEncoding.empty()) - sEncoding = NSStringFinder::FindProperty(sFileContent, "encoding", {"="}, {";", "\\n", "\\r", " "}).m_sValue; - - if (!sEncoding.empty() && !NSStringFinder::Equals("utf-8", sEncoding)) - { - NSUnicodeConverter::CUnicodeConverter oConverter; - sFileContent = U_TO_UTF8(oConverter.toUnicode(sFileContent, sEncoding.c_str())); - } - } - - // Избавляемся от лишних символов до <... - boost::regex oRegex("<[a-zA-Z]"); - boost::match_results<typename std::string::const_iterator> oResult; - - if (boost::regex_search(sFileContent, oResult, oRegex)) - sFileContent.erase(0, oResult.position()); - - //Избавление от <a ... /> - while (NSStringFinder::RemoveEmptyTag(sFileContent, "a")); - //Избавление от <title ... /> - while (NSStringFinder::RemoveEmptyTag(sFileContent, "title")); - //Избавление от <script ... /> - while (NSStringFinder::RemoveEmptyTag(sFileContent, "script")); - - // Gumbo - GumboOptions options = kGumboDefaultOptions; - GumboOutput* output = gumbo_parse_with_options(&options, sFileContent.data(), sFileContent.length()); - - // prettyprint - NSStringUtils::CStringBuilderA oBuilder; - prettyprint(output->document, oBuilder); - - // Конвертирование из string utf8 в wstring - return UTF8_TO_U(oBuilder.GetData()); -} - -static std::string Base64ToString(const std::string& sContent, const std::string& sCharset) -{ - std::string sRes; - int nSrcLen = (int)sContent.length(); - int nDecodeLen = NSBase64::Base64DecodeGetRequiredLength(nSrcLen); - BYTE* pData = new BYTE[nDecodeLen]; - if (TRUE == NSBase64::Base64Decode(sContent.c_str(), nSrcLen, pData, &nDecodeLen)) - { - std::wstring sConvert; - if(!sCharset.empty() && NSStringFinder::Equals<std::string>("utf-8", sCharset)) - { - NSUnicodeConverter::CUnicodeConverter oConverter; - sConvert = oConverter.toUnicode(reinterpret_cast<char *>(pData), (unsigned)nDecodeLen, sCharset.data()); - } - sRes = sConvert.empty() ? std::string(reinterpret_cast<char *>(pData), nDecodeLen) : U_TO_UTF8(sConvert); - } - RELEASEARRAYOBJECTS(pData); - return sRes; -} - -static std::string QuotedPrintableDecode(const std::string& sContent, std::string& sCharset) -{ - NSStringUtils::CStringBuilderA sRes; - size_t ip = 0; - size_t i = sContent.find('='); - - if(i == 0) - { - size_t nIgnore = 12; - std::string charset = sContent.substr(0, nIgnore); - if(charset == "=00=00=FE=FF") - sCharset = "UTF-32BE"; - else if(charset == "=FF=FE=00=00") - sCharset = "UTF-32LE"; - else if(charset == "=2B=2F=76=38" || charset == "=2B=2F=76=39" || - charset == "=2B=2F=76=2B" || charset == "=2B=2F=76=2F") - sCharset = "UTF-7"; - else if(charset == "=DD=73=66=73") - sCharset = "UTF-EBCDIC"; - else if(charset == "=84=31=95=33") - sCharset = "GB-18030"; - else - { - nIgnore -= 3; - charset.erase(nIgnore); - if(charset == "=EF=BB=BF") - sCharset = "UTF-8"; - else if(charset == "=F7=64=4C") - sCharset = "UTF-1"; - else if(charset == "=0E=FE=FF") - sCharset = "SCSU"; - else if(charset == "=FB=EE=28") - sCharset = "BOCU-1"; - else - { - nIgnore -= 3; - charset.erase(nIgnore); - if(charset == "=FE=FF") - sCharset = "UTF-16BE"; - else if(charset == "=FF=FE") - sCharset = "UTF-16LE"; - else - nIgnore -= 6; - } - } - - ip = nIgnore; - i = sContent.find('=', ip); - } - - while(i != std::string::npos && i + 2 < sContent.length()) - { - sRes.WriteString(sContent.c_str() + ip, i - ip); - std::string str = sContent.substr(i + 1, 2); - if(str.front() == '\n' || str.front() == '\r') - { - char ch = str[1]; - if(ch != '\n' && ch != '\r') - sRes.WriteString(&ch, 1); - } - else - { - char* err; - char ch = (int)strtol(str.data(), &err, 16); - if(*err) - sRes.WriteString('=' + str); - else - sRes.WriteString(&ch, 1); - } - ip = i + 3; - i = sContent.find('=', ip); - } - if(ip != std::string::npos) - sRes.WriteString(sContent.c_str() + ip); - return sRes.GetData(); -} - -static void ReadMht(const std::string& sMhtContent, std::map<std::string, std::string>& sRes, NSStringUtils::CStringBuilderA& oRes) -{ - size_t unContentPosition = 0, unCharsetBegin = 0, unCharsetEnd = std::string::npos; - - NSStringFinder::TFoundedData<char> oData; - - // Content-Type - oData = NSStringFinder::FindProperty(sMhtContent, "content-type", {":"}, {";", "\\n", "\\r"}); - const std::string sContentType{oData.m_sValue}; - - if (sContentType.empty()) - return; - - if (NSStringFinder::Equals(sContentType, "multipart/alternative")) - { - oRes.WriteString(mhtTohtml(sMhtContent.substr(oData.m_unEndPosition, sMhtContent.length() - oData.m_unEndPosition))); - return; - } - - unContentPosition = std::max(unContentPosition, oData.m_unEndPosition); - unCharsetBegin = oData.m_unEndPosition; - - // name -// std::string sName = NSStringFinder::FindProperty(sMhtContent, "name", {"="}, {";", "\\n", "\\r"}, 0, unLastPosition); -// unContentPosition = std::max(unContentPosition, unLastPosition); - - // Content-Location - oData = NSStringFinder::FindProperty(sMhtContent, "content-location", {":"}, {";", "\\n", "\\r"}); - std::string sContentLocation{oData.m_sValue}; - - if (!oData.Empty()) - unContentPosition = std::max(unContentPosition, oData.m_unEndPosition); - - // Content-ID - oData = NSStringFinder::FindProperty(sMhtContent, "content-id", {":"}, {";", "\\n", "\\r"}); - std::string sContentID{oData.m_sValue}; - - if (!oData.Empty()) - { - unContentPosition = std::max(unContentPosition, oData.m_unEndPosition); - unCharsetEnd = std::min(unCharsetEnd, oData.m_unBeginPosition); - NSStringFinder::CutInside<std::string>(sContentID, "<", ">"); - } - - if (sContentLocation.empty() && !sContentID.empty()) - sContentLocation = "cid:" + sContentID; - - // Content-Transfer-Encoding - oData = NSStringFinder::FindProperty(sMhtContent, "content-transfer-encoding", {":"}, {";", "\\n", "\\r"}); - const std::string sContentEncoding{oData.m_sValue}; - - if (!oData.Empty()) - { - unContentPosition = std::max(unContentPosition, oData.m_unEndPosition); - unCharsetEnd = std::min(unCharsetEnd, oData.m_unBeginPosition); - } - - // charset - std::string sCharset = "utf-8"; - - if (std::string::npos != unCharsetEnd && unCharsetBegin < unCharsetEnd) - { - sCharset = NSStringFinder::FindProperty(sMhtContent.substr(unCharsetBegin, unCharsetEnd - unCharsetBegin), "charset", {"="}, {";", "\\n", "\\r"}).m_sValue; - NSStringFinder::CutInside<std::string>(sCharset, "\""); - } - - // Content - std::string sContent = sMhtContent.substr(unContentPosition, sMhtContent.length() - unContentPosition); - -// std::wstring sExtention = NSFile::GetFileExtention(UTF8_TO_U(sName)); -// std::transform(sExtention.begin(), sExtention.end(), sExtention.begin(), tolower); - // Основной документ - if (NSStringFinder::Equals(sContentType, "multipart/alternative")) - oRes.WriteString(mhtTohtml(sContent)); - else if ((NSStringFinder::Find(sContentType, "text") /*&& (sExtention.empty() || NSStringFinder::EqualOf(sExtention, {L"htm", L"html", L"xhtml", L"css"}))*/) - || (NSStringFinder::Equals(sContentType, "application/octet-stream") && NSStringFinder::Find(sContentLocation, "css"))) - { - // Стили заключаются в тэг <style> - const bool bAddTagStyle = NSStringFinder::Equals(sContentType, "text/css") /*|| NSStringFinder::Equals(sExtention, L"css")*/ || NSStringFinder::Find(sContentLocation, "css"); - - if (bAddTagStyle) - oRes.WriteString("<style>"); - - if (NSStringFinder::Equals(sContentEncoding, "base64")) - sContent = Base64ToString(sContent, sCharset); - else if (NSStringFinder::EqualOf(sContentEncoding, {"8bit", "7bit"}) || sContentEncoding.empty()) - { - if (!NSStringFinder::Equals(sCharset, "utf-8") && !sCharset.empty()) - { - NSUnicodeConverter::CUnicodeConverter oConverter; - sContent = U_TO_UTF8(oConverter.toUnicode(sContent, sCharset.data())); - } - } - else if (NSStringFinder::Equals(sContentEncoding, "quoted-printable")) - { - sContent = QuotedPrintableDecode(sContent, sCharset); - if (!NSStringFinder::Equals(sCharset, "utf-8") && !sCharset.empty()) - { - NSUnicodeConverter::CUnicodeConverter oConverter; - sContent = U_TO_UTF8(oConverter.toUnicode(sContent, sCharset.data())); - } - } - - if (NSStringFinder::Equals(sContentType, "text/html")) - sContent = U_TO_UTF8(htmlToXhtml(sContent, false)); - - oRes.WriteString(sContent); - - if(bAddTagStyle) - oRes.WriteString("</style>"); - } - // Картинки - else if ((NSStringFinder::Find(sContentType, "image") /*|| NSStringFinder::Equals(sExtention, L"gif")*/ || NSStringFinder::Equals(sContentType, "application/octet-stream")) && - NSStringFinder::Equals(sContentEncoding, "base64")) - { -// if (NSStringFinder::Equals(sExtention, L"ico") || NSStringFinder::Find(sContentType, "ico")) -// sContentType = "image/jpg"; -// else if(NSStringFinder::Equals(sExtention, L"gif")) -// sContentType = "image/gif"; - int nSrcLen = (int)sContent.length(); - int nDecodeLen = NSBase64::Base64DecodeGetRequiredLength(nSrcLen); - BYTE* pData = new BYTE[nDecodeLen]; - if (TRUE == NSBase64::Base64Decode(sContent.c_str(), nSrcLen, pData, &nDecodeLen)) - sRes.insert(std::make_pair(sContentLocation, "data:" + sContentType + ";base64," + sContent)); - RELEASEARRAYOBJECTS(pData); - } -} - -static std::string mhtTohtml(const std::string& sFileContent) -{ - std::map<std::string, std::string> sRes; - NSStringUtils::CStringBuilderA oRes; - - // Поиск boundary - NSStringFinder::TFoundedData<char> oData{NSStringFinder::FindProperty(sFileContent, "boundary", {"="}, {"\\r", "\\n", "\""})}; - - size_t nFound{oData.m_unEndPosition}; - std::string sBoundary{oData.m_sValue}; - - if (sBoundary.empty()) - { - size_t nFoundEnd = sFileContent.length(); - nFound = 0; - ReadMht(sFileContent.substr(nFound, nFoundEnd), sRes, oRes); - return oRes.GetData(); - } - - NSStringFinder::CutInside<std::string>(sBoundary, "\""); - - size_t nFoundEnd{nFound}; - - sBoundary = "--" + sBoundary; - size_t nBoundaryLength = sBoundary.length(); - - nFound = sFileContent.find(sBoundary, nFound) + nBoundaryLength; - - // Цикл по boundary - while(nFound != std::string::npos) - { - nFoundEnd = sFileContent.find(sBoundary, nFound + nBoundaryLength); - if(nFoundEnd == std::string::npos) - break; - - ReadMht(sFileContent.substr(nFound, nFoundEnd - nFound), sRes, oRes); - - nFound = sFileContent.find(sBoundary, nFoundEnd); - } - - std::string sFile = oRes.GetData(); - for(const std::pair<std::string, std::string>& item : sRes) - { - std::string sName = item.first; - size_t found = sFile.find(sName); - size_t sfound = sName.rfind('/'); - if(found == std::string::npos && sfound != std::string::npos) - found = sFile.find(sName.erase(0, sfound + 1)); - while(found != std::string::npos) - { - size_t fq = sFile.find_last_of("\"\'>=", found); - - if (std::string::npos == fq) - break; - - char ch = sFile[fq]; - if(ch != '\"' && ch != '\'') - fq++; - size_t tq = sFile.find_first_of("\"\'<> ", found) + 1; - - if (std::string::npos == tq) - break; - - if(sFile[tq] != '\"' && sFile[tq] != '\'') - tq--; - if(ch != '>') - { - std::string is = '\"' + item.second + '\"'; - sFile.replace(fq, tq - fq, is); - found = sFile.find(sName, fq + is.length()); - } - else - found = sFile.find(sName, tq); - } - } - - return sFile; -} - -static std::wstring mhtToXhtml(std::string& sFileContent) -{ - sFileContent = mhtTohtml(sFileContent); - - // Gumbo - GumboOptions options = kGumboDefaultOptions; - GumboOutput* output = gumbo_parse_with_options(&options, sFileContent.data(), sFileContent.length()); - - // prettyprint - NSStringUtils::CStringBuilderA oBuilder; - prettyprint(output->document, oBuilder); - - // Конвертирование из string utf8 в wstring - return UTF8_TO_U(oBuilder.GetData()); -} - -// Заменяет сущности &,<,> в text -static void substitute_xml_entities_into_text(std::string& text) -{ - // replacing & must come first - replace_all(text, "&", "&"); - replace_all(text, "<", "<"); - replace_all(text, ">", ">"); -} - -// After running through Gumbo, the values of type "" are replaced with the corresponding code '0x01' -// Since the attribute value does not use control characters (value <= 0x09), -// then just delete them, otherwise XmlUtils::CXmlLiteReader crashes on them. -// bug#73486 -static void remove_control_symbols(std::string& text) -{ - std::string::iterator itFound = std::find_if(text.begin(), text.end(), [](unsigned char chValue){ return chValue <= 0x09; }); - - while (itFound != text.end()) - { - itFound = text.erase(itFound); - itFound = std::find_if(itFound, text.end(), [](unsigned char chValue){ return chValue <= 0x09; }); - } -} - -// Заменяет сущности " в text -static void substitute_xml_entities_into_attributes(std::string& text) -{ - remove_control_symbols(text); - substitute_xml_entities_into_text(text); - replace_all(text, "\"", """); -} - -static std::string handle_unknown_tag(GumboStringPiece* text) -{ - if (text->data == NULL) - return ""; - GumboStringPiece gsp = *text; - gumbo_tag_from_original_text(&gsp); - std::string sAtr = std::string(gsp.data, gsp.length); - size_t found = sAtr.find_first_of("-'+,./=?;!*#@$_%<>&;\"\'()[]{}"); - while(found != std::string::npos) - { - sAtr.erase(found, 1); - found = sAtr.find_first_of("-'+,./=?;!*#@$_%<>&;\"\'()[]{}", found); - } - return sAtr; -} - -static std::string get_tag_name(GumboNode* node) -{ - std::string tagname = (node->type == GUMBO_NODE_DOCUMENT ? "document" : gumbo_normalized_tagname(node->v.element.tag)); - if (tagname.empty()) - tagname = handle_unknown_tag(&node->v.element.original_tag); - return tagname; -} - -static void build_doctype(GumboNode* node, NSStringUtils::CStringBuilderA& oBuilder) -{ - if (node->v.document.has_doctype) - { - oBuilder.WriteString("<!DOCTYPE "); - oBuilder.WriteString(node->v.document.name); - std::string pi(node->v.document.public_identifier); - remove_control_symbols(pi); - if ((node->v.document.public_identifier != NULL) && !pi.empty()) - { - oBuilder.WriteString(" PUBLIC \""); - oBuilder.WriteString(pi); - oBuilder.WriteString("\" \""); - oBuilder.WriteString(node->v.document.system_identifier); - oBuilder.WriteString("\""); - } - oBuilder.WriteString(">"); - } -} - -static void build_attributes(const GumboVector* attribs, NSStringUtils::CStringBuilderA& atts) -{ - std::vector<std::string> arrRepeat; - for (size_t i = 0; i < attribs->length; ++i) - { - GumboAttribute* at = static_cast<GumboAttribute*>(attribs->data[i]); - std::string sVal(at->value); - std::string sName(at->name); - - remove_control_symbols(sVal); - remove_control_symbols(sName); - - atts.WriteString(" "); - - bool bCheck = false; - size_t nBad = sName.find_first_of("+,.=?#%<>&;\"\'()[]{}"); - while(nBad != std::string::npos) - { - sName.erase(nBad, 1); - nBad = sName.find_first_of("+,.=?#%<>&;\"\'()[]{}", nBad); - if(sName.empty()) - break; - bCheck = true; - } - if(sName.empty()) - continue; - while(sName.front() >= '0' && sName.front() <= '9') - { - sName.erase(0, 1); - if(sName.empty()) - break; - bCheck = true; - } - if(bCheck) - { - GumboAttribute* check = gumbo_get_attribute(attribs, sName.c_str()); - if(check || std::find(arrRepeat.begin(), arrRepeat.end(), sName) != arrRepeat.end()) - continue; - else - arrRepeat.push_back(sName); - } - - if(sName.empty()) - continue; - atts.WriteString(sName); - - // determine original quote character used if it exists - std::string qs ="\""; - atts.WriteString("="); - atts.WriteString(qs); - substitute_xml_entities_into_attributes(sVal); - atts.WriteString(sVal); - atts.WriteString(qs); - } -} - -static void prettyprint_contents(GumboNode* node, NSStringUtils::CStringBuilderA& contents, bool bCheckValidNode) -{ - std::string key = "|" + get_tag_name(node) + "|"; - bool keep_whitespace = preserve_whitespace.find(key) != std::string::npos; - bool is_inline = nonbreaking_inline.find(key) != std::string::npos; - bool is_like_inline = treat_like_inline.find(key) != std::string::npos; - - GumboVector* children = &node->v.element.children; - - for (size_t i = 0; i < children->length; i++) - { - GumboNode* child = static_cast<GumboNode*> (children->data[i]); - - if (child->type == GUMBO_NODE_TEXT) - { - std::string val(child->v.text.text); - remove_control_symbols(val); - substitute_xml_entities_into_text(val); - - // Избавление от FF - size_t found = val.find_first_of("\014"); - while(found != std::string::npos) - { - val.erase(found, 1); - found = val.find_first_of("\014", found); - } - - contents.WriteString(val); - } - else if ((child->type == GUMBO_NODE_ELEMENT) || (child->type == GUMBO_NODE_TEMPLATE)) - prettyprint(child, contents, bCheckValidNode); - else if (child->type == GUMBO_NODE_WHITESPACE) - { - if (keep_whitespace || is_inline || is_like_inline) - contents.WriteString(child->v.text.text); - } - else if (child->type != GUMBO_NODE_COMMENT) - { - // Сообщение об ошибке - // Does this actually exist: (child->type == GUMBO_NODE_CDATA) - // fprintf(stderr, "unknown element of type: %d\n", child->type); - } - } -} - -static void prettyprint(GumboNode* node, NSStringUtils::CStringBuilderA& oBuilder, bool bCheckValidNode) -{ - // special case the document node - if (node->type == GUMBO_NODE_DOCUMENT) - { - build_doctype(node, oBuilder); - prettyprint_contents(node, oBuilder, bCheckValidNode); - return; - } - - std::string tagname = get_tag_name(node); - remove_control_symbols(tagname); - - if (NodeIsUnprocessed(tagname)) - return; - - if (bCheckValidNode) - bCheckValidNode = !IsUnckeckedNodes(tagname); - - if (bCheckValidNode && html_tags.end() == std::find(html_tags.begin(), html_tags.end(), tagname)) - { - prettyprint_contents(node, oBuilder, bCheckValidNode); - return; - } - - std::string close = ""; - std::string closeTag = ""; - std::string key = "|" + tagname + "|"; - bool is_empty_tag = empty_tags.find(key) != std::string::npos; - - // determine closing tag type - if (is_empty_tag) - close = "/"; - else - closeTag = "</" + tagname + ">"; - - // build results - oBuilder.WriteString("<" + tagname); - - // build attr string - const GumboVector* attribs = &node->v.element.attributes; - build_attributes(attribs, oBuilder); - oBuilder.WriteString(close + ">"); - - // prettyprint your contents - prettyprint_contents(node, oBuilder, bCheckValidNode); - oBuilder.WriteString(closeTag); +std::wstring htmlToXhtml(std::string& sFileContent, bool bNeedConvert); +std::wstring mhtToXhtml(std::string& sFileContent); } #endif // HTMLTOXHTML_H diff --git a/HtmlFile2/Common.h b/HtmlFile2/Common.h index 203d33d645..47870ff45f 100644 --- a/HtmlFile2/Common.h +++ b/HtmlFile2/Common.h @@ -2,10 +2,15 @@ #define COMMON_H #include <string> +#include "../../DesktopEditor/common/StringBuilder.h" namespace HTML { -void replace_all(std::wstring& s, const std::wstring& s1, const std::wstring& s2) +#define MAX_STRING_BLOCK_SIZE (size_t)10485760 + +using XmlString = NSStringUtils::CStringBuilder; + +inline void replace_all(std::wstring& s, const std::wstring& s1, const std::wstring& s2) { size_t pos = s.find(s1); size_t l = s2.length(); @@ -17,7 +22,7 @@ void replace_all(std::wstring& s, const std::wstring& s1, const std::wstring& s2 } } -std::wstring EncodeXmlString(const std::wstring& s) +inline std::wstring EncodeXmlString(const std::wstring& s) { std::wstring sRes = s; @@ -32,6 +37,28 @@ std::wstring EncodeXmlString(const std::wstring& s) return sRes; } + +inline void WriteToStringBuilder(NSStringUtils::CStringBuilder& oSrcStringBuilder, NSStringUtils::CStringBuilder& oDstStringBuilder) +{ + if (oSrcStringBuilder.GetCurSize() < MAX_STRING_BLOCK_SIZE) + { + oDstStringBuilder.Write(oSrcStringBuilder); + return; + } + + size_t ulSize = oSrcStringBuilder.GetCurSize(); + size_t ulCurrentBlockSize = 0, ulPosition = 0; + + while (ulSize > 0) + { + ulCurrentBlockSize = std::min(ulSize, MAX_STRING_BLOCK_SIZE); + oDstStringBuilder.WriteString(oSrcStringBuilder.GetSubData(ulPosition, ulCurrentBlockSize)); + + ulSize -= ulCurrentBlockSize; + ulPosition += ulCurrentBlockSize; + } +} + } #endif // COMMON_H diff --git a/HtmlFile2/HTMLReader.cpp b/HtmlFile2/HTMLReader.cpp index c197e29245..153192f8f0 100644 --- a/HtmlFile2/HTMLReader.cpp +++ b/HtmlFile2/HTMLReader.cpp @@ -4,12 +4,17 @@ #include "../DesktopEditor/common/File.h" #include "../DesktopEditor/common/Path.h" -#include "../Common/3dParty/html/gumbo-parser/src/gumbo.h" +#include "../Common/3dParty/html/htmltoxhtml.h" #include "Common.h" #include "Interpretators/OOXMLInterpretator.h" #include "Tags/OOXMLTags.h" +#include "../../Common/3dParty/html/gumbo-parser/src/gumbo.h" +#include "../src/StringFinder.h" + +#include <boost/tuple/tuple.hpp> + namespace HTML { #define HTML_TAG(tag) GUMBO_TAG_##tag @@ -170,20 +175,24 @@ CHTMLReader::CHTMLReader() pInterpretator->SetCSSCalculator(&m_oCSSCalculator); m_pInterpretator = pInterpretator; - m_mTags[HTML_TAG(A)] = std::make_shared<TAnchor <COOXMLInterpretator>>(pInterpretator); - m_mTags[HTML_TAG(ABBR)] = std::make_shared<TAnchor <COOXMLInterpretator>>(pInterpretator); - m_mTags[HTML_TAG(BR)] = std::make_shared<TBreak <COOXMLInterpretator>>(pInterpretator); - m_mTags[HTML_TAG(DIV)] = std::make_shared<TDivision <COOXMLInterpretator>>(pInterpretator); - m_mTags[HTML_TAG(IMG)] = std::make_shared<TImage <COOXMLInterpretator>>(pInterpretator, &m_oLightReader); - m_mTags[HTML_TAG(FONT)] = std::make_shared<TFont <COOXMLInterpretator>>(pInterpretator); - m_mTags[HTML_TAG(INPUT)] = std::make_shared<TInput <COOXMLInterpretator>>(pInterpretator); - m_mTags[HTML_TAG(BASEFONT)] = std::make_shared<TBaseFont <COOXMLInterpretator>>(pInterpretator); - m_mTags[HTML_TAG(BLOCKQUOTE)] = std::make_shared<TBlockquote <COOXMLInterpretator>>(pInterpretator); - m_mTags[HTML_TAG(HR)] = std::make_shared<THorizontalRule<COOXMLInterpretator>>(pInterpretator); - m_mTags[HTML_TAG(UL)] = std::make_shared<TList <COOXMLInterpretator>>(pInterpretator); - m_mTags[HTML_TAG(LI)] = std::make_shared<TListElement <COOXMLInterpretator>>(pInterpretator); + m_mTags[HTML_TAG(A)] = std::make_shared<CAnchor <COOXMLInterpretator>>(pInterpretator); + m_mTags[HTML_TAG(ABBR)] = std::make_shared<CAnchor <COOXMLInterpretator>>(pInterpretator); + m_mTags[HTML_TAG(BR)] = std::make_shared<CBreak <COOXMLInterpretator>>(pInterpretator); + m_mTags[HTML_TAG(DIV)] = std::make_shared<CDivision <COOXMLInterpretator>>(pInterpretator); + m_mTags[HTML_TAG(IMG)] = std::make_shared<CImage <COOXMLInterpretator>>(pInterpretator); + m_mTags[HTML_TAG(FONT)] = std::make_shared<CFont <COOXMLInterpretator>>(pInterpretator); + m_mTags[HTML_TAG(INPUT)] = std::make_shared<CInput <COOXMLInterpretator>>(pInterpretator); + m_mTags[HTML_TAG(BASEFONT)] = std::make_shared<CBaseFont <COOXMLInterpretator>>(pInterpretator); + m_mTags[HTML_TAG(BLOCKQUOTE)] = std::make_shared<CBlockquote <COOXMLInterpretator>>(pInterpretator); + m_mTags[HTML_TAG(HR)] = std::make_shared<CHorizontalRule<COOXMLInterpretator>>(pInterpretator); + m_mTags[HTML_TAG(UL)] = std::make_shared<CList <COOXMLInterpretator>>(pInterpretator); + m_mTags[HTML_TAG(LI)] = std::make_shared<CListElement <COOXMLInterpretator>>(pInterpretator); + m_mTags[HTML_TAG(CAPTION)] = std::make_shared<CCaption <COOXMLInterpretator>>(pInterpretator); + m_mTags[HTML_TAG(TABLE)] = std::make_shared<CTable <COOXMLInterpretator>>(pInterpretator); + m_mTags[HTML_TAG(TR)] = std::make_shared<CTableRow <COOXMLInterpretator>>(pInterpretator); + m_mTags[HTML_TAG(TD)] = std::make_shared<CTableCell <COOXMLInterpretator>>(pInterpretator); - std::shared_ptr<ITag> oIgnoredTag{std::make_shared<TEmptyTag>()}; + std::shared_ptr<ITag> oIgnoredTag{std::make_shared<CEmptyTag>()}; m_mTags[HTML_TAG(B)] = oIgnoredTag; m_mTags[HTML_TAG(I)] = oIgnoredTag; @@ -273,7 +282,7 @@ bool CHTMLReader::HTML2XHTML(const std::wstring& wsFileName) sFileContent.replace(nFind, nFindEnd - nFind, "1.0"); } - // const std::wstring sRes{htmlToXhtml(sFileContent, bNeedConvert)}; + const std::wstring sRes{htmlToXhtml(sFileContent, bNeedConvert)}; #ifdef SAVE_NORMALIZED_HTML #if 1 == SAVE_NORMALIZED_HTML @@ -286,7 +295,7 @@ bool CHTMLReader::HTML2XHTML(const std::wstring& wsFileName) #endif #endif - return m_oLightReader.FromString(UTF8_TO_U(sFileContent)); + return m_oLightReader.FromString(sRes); } void CHTMLReader::ReadStyle() @@ -446,7 +455,7 @@ bool CHTMLReader::ReadStream(std::vector<NSCSS::CNode>& arSelectors, bool bInser if (!bInsertEmptyP) return false; - m_pInterpretator->WriteEmptyParagraph(true, true); + m_pInterpretator->WriteEmptyParagraph(); return true; // WriteEmptyParagraph(sSelectors, oTS); @@ -470,7 +479,7 @@ bool CHTMLReader::ReadStream(std::vector<NSCSS::CNode>& arSelectors, bool bInser } while(m_oLightReader.ReadNextSiblingNode2(nDeath)); if (!bResult && bInsertEmptyP) - m_pInterpretator->WriteEmptyParagraph(true, true); + m_pInterpretator->WriteEmptyParagraph(); return bResult; } @@ -561,11 +570,16 @@ bool CHTMLReader::ReadInside(std::vector<NSCSS::CNode>& arSelectors) break; } case HTML_TAG(IMG): - case HTML_TAG(SVG): + { bResult = ReadDefaultTag(HTML_TAG(IMG), arSelectors); break; } + case HTML_TAG(SVG): + { + bResult = ReadSVG(arSelectors); + break; + } case HTML_TAG(INS): case HTML_TAG(U): { @@ -803,6 +817,16 @@ bool CHTMLReader::ReadAnchor(std::vector<NSCSS::CNode>& arSelectors) return true; } +bool CHTMLReader::ReadSVG(const std::vector<NSCSS::CNode>& arSelectors) +{ + if (!m_mTags[HTML_TAG(IMAGE)]->Open(arSelectors, &m_oLightReader)) + return false; + + m_mTags[HTML_TAG(IMAGE)]->Close(arSelectors); + + return true; +} + bool CHTMLReader::ReadTable(std::vector<NSCSS::CNode>& arSelectors) { if(m_oLightReader.IsEmptyNode()) @@ -811,46 +835,44 @@ bool CHTMLReader::ReadTable(std::vector<NSCSS::CNode>& arSelectors) // if (HTML_TAG(TABLE) == m_oState.m_eLastElement) // WriteEmptyParagraph(oXml, true); - CTable oTable; - CTextSettings oTextSettings{oTS}; - oTextSettings.sPStyle.clear(); + CStorageTable oTable; - NSCSS::CCompiledStyle *pStyle = sSelectors.back().m_pCompiledStyle; + NSCSS::CCompiledStyle *pStyle = arSelectors.back().m_pCompiledStyle; //Table styles std::wstring wsFrame; + std::wstring wsValue; - for (const std::pair<std::wstring, std::wstring>& oArgument : sSelectors.back().m_mAttributes) + if (arSelectors.back().GetAttributeValue(L"border", wsValue)) { - if (L"border" == oArgument.first) + const int nWidth = NSStringFinder::ToInt(wsValue); + + if (0 < nWidth) { - const int nWidth = NSStringFinder::ToInt(oArgument.second); + oTable.SetRules(L"all"); - if (0 < nWidth) + if (pStyle->m_oBorder.Empty()) { - oTable.SetRules(L"all"); - - if (!pStyle->m_oBorder.Empty()) - continue; - pStyle->m_oBorder.SetStyle(L"outset", 0, true); pStyle->m_oBorder.SetWidth(nWidth, NSCSS::UnitMeasure::Point, 0, true); pStyle->m_oBorder.SetColor(L"auto", 0, true); } - else if (pStyle->m_oBorder.Empty()) - { - pStyle->m_oBorder.SetNone(0, true); - oTable.SetRules(L"none"); - } } - else if (L"cellpadding" == oArgument.first) - pStyle->m_oPadding.SetValues(oArgument.second + L"px", 0, true); - else if (L"rules" == oArgument.first) - oTable.SetRules(oArgument.second); - else if (L"frame" == oArgument.first) - wsFrame = oArgument.second; + else if (pStyle->m_oBorder.Empty()) + { + pStyle->m_oBorder.SetNone(0, true); + oTable.SetRules(L"none"); + } } + if (arSelectors.back().GetAttributeValue(L"cellpadding", wsValue)) + pStyle->m_oPadding.SetValues(wsValue + L"px", 0, true); + + if (arSelectors.back().GetAttributeValue(L"rules", wsValue)) + oTable.SetRules(wsValue); + + arSelectors.back().GetAttributeValue(L"frame", wsFrame); + if (!wsFrame.empty() && pStyle->m_oBorder.Empty()) { #define SetDefaultBorderSide(side) \ @@ -892,8 +914,8 @@ bool CHTMLReader::ReadTable(std::vector<NSCSS::CNode>& arSelectors) if (pStyle->m_oBorder.GetCollapse() == NSCSS::NSProperties::BorderCollapse::Collapse) oTable.SetCellSpacing(0); - else if (sSelectors.back().m_mAttributes.end() != sSelectors.back().m_mAttributes.find(L"cellspacing")) - oTable.SetCellSpacing(NSStringFinder::ToInt(sSelectors.back().m_mAttributes[L"cellspacing"])); + else if (arSelectors.back().GetAttributeValue(L"cellspacing", wsValue)) + oTable.SetCellSpacing(NSStringFinder::ToInt(wsValue)); else if (pStyle->m_oBorder.GetCollapse() == NSCSS::NSProperties::BorderCollapse::Separate) oTable.SetCellSpacing(15); @@ -908,29 +930,270 @@ bool CHTMLReader::ReadTable(std::vector<NSCSS::CNode>& arSelectors) while(m_oLightReader.ReadNextSiblingNode(nDeath)) { const std::wstring sName = m_oLightReader.GetName(); - GetSubClass(oXml, sSelectors); + GetSubClass(arSelectors); if(sName == L"caption") - ParseTableCaption(oTable, sSelectors, oTS); + ReadTableCaption(oTable, arSelectors); if(sName == L"thead") - ParseTableRows(oTable, sSelectors, oTextSettings, ERowParseMode::ParseModeHeader); + ReadTableRows(oTable, arSelectors, ERowParseMode::Header); if(sName == L"tbody") - ParseTableRows(oTable, sSelectors, oTextSettings, ERowParseMode::ParseModeBody); + ReadTableRows(oTable, arSelectors, ERowParseMode::Body); else if(sName == L"tfoot") - ParseTableRows(oTable, sSelectors, oTextSettings, ERowParseMode::ParseModeFoother); + ReadTableRows(oTable, arSelectors, ERowParseMode::Foother); else if (sName == L"colgroup") - ParseTableColspan(oTable); + ReadTableColspan(oTable); arSelectors.pop_back(); } oTable.Shorten(); oTable.CompleteTable(); - oTable.ConvertToOOXML(*oXml); + // oTable.ConvertToOOXML(*oXml); + + if (!m_mTags[HTML_TAG(TABLE)]->Open(arSelectors, &oTable)) + return false; + + #define CONVERT_ROWS(rows, parse_mode)\ + {\ + const std::vector<CStorageTableRow*> arRows{rows};\ + \ + for (UINT unRow = 0; unRow < arRows.size(); ++unRow)\ + {\ + if (!m_mTags[HTML_TAG(TR)]->Open(arSelectors, boost::tuple<CStorageTableRow&, const CStorageTable&>(*arRows[unRow], oTable)))\ + continue;\ + \ + const std::vector<CStorageTableCell*>& arCells{arRows[unRow]->GetCells()};\ + ERowPosition eRowPosition{ERowPosition::Middle};\ + \ + if (0 == unRow)\ + eRowPosition = ERowPosition::First;\ + else if (arRows.size() - 1 == unRow)\ + eRowPosition = ERowPosition::Last;\ + \ + for (UINT unCol = 0; unCol < arCells.size(); ++unCol)\ + {\ + m_mTags[HTML_TAG(TD)]->Open(arSelectors, boost::tuple<CStorageTableCell&, const CStorageTable&, UINT, ERowParseMode, ERowPosition>(*arCells[unCol], oTable, unCol, parse_mode, eRowPosition));\ + m_mTags[HTML_TAG(TD)]->Close(arSelectors);\ + }\ + \ + m_mTags[HTML_TAG(TR)]->Close(arSelectors);\ + }} + + for (const std::vector<CStorageTableRow*>& arHeader : oTable.GetHeaders()) + CONVERT_ROWS(arHeader, ERowParseMode::Header) + + CONVERT_ROWS(oTable.GetRows(), ERowParseMode::Body) + CONVERT_ROWS(oTable.GetFoothers(), ERowParseMode::Foother) + + m_mTags[HTML_TAG(TABLE)]->Close(arSelectors); return true; } +void CHTMLReader::ReadTableCaption(CStorageTable& oTable, std::vector<NSCSS::CNode>& arSelectors) +{ + if (nullptr == m_pInterpretator) + return; + + GetSubClass(arSelectors); + m_pInterpretator->SetDataOutput(oTable.GetCaptionData()); + + arSelectors.back().m_pCompiledStyle->m_oDisplay.SetVAlign(L"center", arSelectors.size()); + + ReadDefaultTag(HTML_TAG(CAPTION), arSelectors); + + m_pInterpretator->RevertDataOutput(); + arSelectors.pop_back(); +} + +void CalculateCellStyles(TTableCellStyle* pCellStyle, std::vector<NSCSS::CNode>& arSelectors) +{ + if (NULL == pCellStyle) + return; + + pCellStyle->m_wsVAlign = arSelectors.back().m_pCompiledStyle->m_oDisplay.GetVAlign().ToWString(); + pCellStyle->m_wsHAlign = arSelectors.back().m_pCompiledStyle->m_oDisplay.GetHAlign().ToWString(); + pCellStyle->m_oBackground = arSelectors.back().m_pCompiledStyle->m_oBackground.GetColor(); + pCellStyle->m_oHeight = arSelectors.back().m_pCompiledStyle->m_oDisplay.GetHeight(); + pCellStyle->m_oWidth = arSelectors.back().m_pCompiledStyle->m_oDisplay.GetWidth(); + pCellStyle->m_oPadding = arSelectors.back().m_pCompiledStyle->m_oPadding; + pCellStyle->m_oBorder = arSelectors.back().m_pCompiledStyle->m_oBorder; + + if (pCellStyle->m_wsHAlign.empty()) + pCellStyle->m_wsHAlign = arSelectors.back().m_pCompiledStyle->m_oText.GetAlign().ToWString(); +} + +struct TRowspanElement +{ + UINT m_unRowSpan; + UINT m_unColumnIndex; + const CStorageTableCell* m_pCell; + + TRowspanElement(UINT unRowSpan, UINT unColumnIndex, const CStorageTableCell* pCell) + : m_unRowSpan(unRowSpan), m_unColumnIndex(unColumnIndex), m_pCell(pCell) + {} +}; + +void CHTMLReader::ReadTableRows(CStorageTable& oTable, std::vector<NSCSS::CNode>& arSelectors, ERowParseMode eMode) +{ + std::vector<TRowspanElement> arRowspanElements; + std::vector<CStorageTableRow*> arRows; + + int nDeath = m_oLightReader.GetDepth(); + while (m_oLightReader.ReadNextSiblingNode(nDeath)) + { + if (L"tr" != m_oLightReader.GetName()) + continue; + + GetSubClass(arSelectors); + + CStorageTableRow *pRow = new CStorageTableRow(); + + for (std::vector<TRowspanElement>::iterator itElement = arRowspanElements.begin(); itElement < arRowspanElements.end();) + { + pRow->InsertCell(CStorageTableCell::CreateEmpty(itElement->m_pCell->GetColspan(), true, itElement->m_pCell->GetStyles()), itElement->m_unColumnIndex); + + itElement->m_unRowSpan--; + if (1 == itElement->m_unRowSpan) + itElement = arRowspanElements.erase(itElement); + else + ++itElement; + } + + UINT unColumnIndex = 0; + int nTrDepth = m_oLightReader.GetDepth(); + while (m_oLightReader.ReadNextSiblingNode(nTrDepth)) + { + CStorageTableCell *pCell = new CStorageTableCell(); + + if (NULL == pCell) + continue; + + GetSubClass(arSelectors); + + std::vector<NSCSS::CNode> arNewSelectors{(std::vector<NSCSS::CNode>::const_iterator)std::find_if(arSelectors.begin(), arSelectors.end(), [](const NSCSS::CNode& oNode){ return L"table" == oNode.m_wsName; }), arSelectors.cend()}; + + CalculateCellStyles(pCell->GetStyles(), arNewSelectors); + + std::wstring wsValue; + + if (arSelectors.back().GetAttributeValue(L"colspan", wsValue)) + pCell->SetColspan(NSStringFinder::ToInt(wsValue, 1), pRow->GetIndex()); + + if (arSelectors.back().GetAttributeValue(L"rowspan", wsValue)) + { + pCell->SetRowspan(NSStringFinder::ToInt(wsValue, 1)); + + if (1 != pCell->GetRowspan()) + arRowspanElements.push_back({pCell->GetRowspan(), unColumnIndex, pCell}); + } + + // Читаем th. Ячейка заголовка таблицы. Выравнивание посередине. Выделяется полужирным + if(m_oLightReader.GetName() == L"th") + { + if (pCell->GetStyles()->m_wsHAlign.empty()) + arSelectors.back().m_pCompiledStyle->m_oText.SetAlign(L"center", arSelectors.size()); + + arSelectors.back().m_pCompiledStyle->m_oFont.SetWeight(L"bold", arSelectors.size()); + + m_pInterpretator->SetDataOutput(pCell->GetData()); + ReadStream(arSelectors, true); + m_pInterpretator->RevertDataOutput(); + } + // Читаем td. Ячейка таблицы + else if(m_oLightReader.GetName() == L"td") + { + m_pInterpretator->SetDataOutput(pCell->GetData()); + ReadStream(arSelectors, true); + m_pInterpretator->RevertDataOutput(); + } + + if (pRow->GetIndex() == MAXCOLUMNSINTABLE - 1) + { + while (m_oLightReader.ReadNextSiblingNode(nTrDepth)) + { + if (L"td" != m_oLightReader.GetName() && L"th" != m_oLightReader.GetName()) + continue; + + GetSubClass(arSelectors); + m_pInterpretator->SetDataOutput(pCell->GetData()); + ReadStream(arSelectors); + m_pInterpretator->RevertDataOutput(); + arSelectors.pop_back(); + } + } + + pRow->AddCell(pCell); + arSelectors.pop_back(); + + ++unColumnIndex; + + if (pRow->GetIndex() == MAXCOLUMNSINTABLE) + break; + } + + arSelectors.pop_back(); + arRows.push_back(pRow); + } + + oTable.AddRows(arRows, eMode); +} + +void CHTMLReader::ReadTableColspan(CStorageTable& oTable) +{ + std::vector<NSCSS::CNode> arNodes; + GetSubClass(arNodes); + + CTableColgroup *pColgroup = new CTableColgroup(arNodes.back()); + + if (NULL == pColgroup) + return; + + oTable.AddColgroup(pColgroup); + + const int nDeath = m_oLightReader.GetDepth(); + if (!m_oLightReader.IsEmptyNode() && m_oLightReader.ReadNextSiblingNode2(nDeath)) + { + do + { + if (L"col" != m_oLightReader.GetName()) + continue; + + GetSubClass(arNodes); + + CTableCol *pCol = new CTableCol(arNodes.back()); + + if (NULL == pCol) + { + arNodes.pop_back(); + continue; + } + + CalculateCellStyles(pCol->GetStyle(), arNodes); + arNodes.pop_back(); + + if (NULL == pCol) + continue; + + pColgroup->AddCol(pCol); + } while(m_oLightReader.ReadNextSiblingNode2(nDeath)); + } + + if(pColgroup->Empty()) + { + std::map<std::wstring, std::wstring>::const_iterator itFound = arNodes.begin()->m_mAttributes.find(L"span"); + + CTableCol *pCol = new CTableCol((arNodes.begin()->m_mAttributes.cend() != itFound) ? NSStringFinder::ToInt(itFound->second, 1) : 1); + + if (NULL == pCol) + return; + + CalculateCellStyles(pCol->GetStyle(), arNodes); + + pColgroup->AddCol(pCol); + } +} + bool CHTMLReader::ReadEmptyTag(UINT unTag, const std::vector<NSCSS::CNode>& arSelectors) { if (!m_mTags[unTag]->Open(arSelectors)) diff --git a/HtmlFile2/HTMLReader.h b/HtmlFile2/HTMLReader.h index 439d54bcd5..8bacf599d0 100644 --- a/HtmlFile2/HTMLReader.h +++ b/HtmlFile2/HTMLReader.h @@ -6,6 +6,7 @@ #include "Interpretators/HTMLInterpretator.h" #include "Tags/HTMLTags.h" +#include "Table.h" namespace HTML { @@ -51,10 +52,15 @@ private: bool ReadText(std::vector<NSCSS::CNode>& arSelectors); bool ReadAnchor(std::vector<NSCSS::CNode>& arSelectors); - bool ReadTable(std::vector<NSCSS::CNode>& arSelectors); + bool ReadSVG(const std::vector<NSCSS::CNode>& arSelectors); bool ReadEmptyTag(UINT unTag, const std::vector<NSCSS::CNode>& arSelectors); bool ReadDefaultTag(UINT unTag, std::vector<NSCSS::CNode>& arSelectors); + bool ReadTable(std::vector<NSCSS::CNode>& arSelectors); + void ReadTableCaption(CStorageTable& oTable, std::vector<NSCSS::CNode>& arSelectors); + void ReadTableRows(CStorageTable& oTable, std::vector<NSCSS::CNode>& arSelectors, ERowParseMode eMode); + void ReadTableColspan(CStorageTable& oTable); + void GetSubClass(std::vector<NSCSS::CNode>& arSelectors); }; } diff --git a/HtmlFile2/HtmlFile2.pro b/HtmlFile2/HtmlFile2.pro index 46b671dfa4..8ff371f1dd 100644 --- a/HtmlFile2/HtmlFile2.pro +++ b/HtmlFile2/HtmlFile2.pro @@ -13,6 +13,7 @@ DEFINES += CSSCALCULATOR_LIBRARY_STATIC DEFINES += CSS_CALCULATOR_WITH_XHTML DEFINES += DISABLE_RUBY_SUPPORT DEFINES += MDCONVERTER_DECL_EXPORT +DEFINES += HTML_NORMALIZER_DECL_EXPORT CORE_ROOT_DIR = $$PWD/.. PWD_ROOT_DIR = $$PWD @@ -34,6 +35,7 @@ ADD_DEPENDENCY(kernel, UnicodeConverter, graphics, kernel_network) SOURCES += htmlfile2.cpp \ ./Interpretators/OOXMLInterpretator.cpp \ HTMLReader.cpp \ + Table.cpp \ Tags/OOXMLTags.cpp HEADERS += htmlfile2.h \ @@ -45,4 +47,5 @@ HEADERS += htmlfile2.h \ HTMLParams.h \ HTMLReader.h \ ./Tags/HTMLTags.h \ + Table.h \ Tags/OOXMLTags.h diff --git a/HtmlFile2/Interpretators/HTMLInterpretator.h b/HtmlFile2/Interpretators/HTMLInterpretator.h index 2f54d4f5d8..dfbd3e7067 100644 --- a/HtmlFile2/Interpretators/HTMLInterpretator.h +++ b/HtmlFile2/Interpretators/HTMLInterpretator.h @@ -1,8 +1,9 @@ #ifndef HTMLINTERPRETATOR_H #define HTMLINTERPRETATOR_H -#include "../Common/3dParty/html/css/src/CNode.h" +#include "../../Common/3dParty/html/css/src/CNode.h" #include "HTMLParams.h" +#include "../Common.h" namespace HTML { @@ -22,6 +23,9 @@ public: virtual void BeginBlock() = 0; virtual void EndBlock(bool bAddBlock) = 0; + virtual void SetDataOutput(XmlString* pOutputData) = 0; // Задаем место вывода для интерпретатора + virtual void RevertDataOutput() = 0; // Возвращаем место вывода к исходному + #ifdef _DEBUG virtual void PrintData() = 0; #endif diff --git a/HtmlFile2/Interpretators/OOXMLInterpretator.cpp b/HtmlFile2/Interpretators/OOXMLInterpretator.cpp index 40bd090292..49292db5ab 100644 --- a/HtmlFile2/Interpretators/OOXMLInterpretator.cpp +++ b/HtmlFile2/Interpretators/OOXMLInterpretator.cpp @@ -6,7 +6,7 @@ #include "../../DesktopEditor/common/Directory.h" #include "../../DesktopEditor/common/SystemUtils.h" -#include "../../DesktopEditor/graphics/pro/Graphics.h" +#include "../../DesktopEditor/graphics/pro/Fonts.h" #include "../src/Languages.h" @@ -36,15 +36,12 @@ namespace HTML #define DEFAULT_IMAGE_HEIGHT 304800 inline bool ElementInTable(const std::vector<NSCSS::CNode>& arSelectors); -inline void WriteToStringBuilder(NSStringUtils::CStringBuilder& oSrcStringBuilder, NSStringUtils::CStringBuilder& oDstStringBuilder); inline bool ElementInHeader(const std::vector<NSCSS::CNode> arSelsectors); inline std::wstring StandardizeHeaderId(const std::wstring& wsHeader); inline int CalculateFontChange(const std::vector<NSCSS::CNode>& arSelectors); inline UINT GetFontSizeLevel(UINT unFontSize); inline UINT GetFontSizeByLevel(UINT unLevel); inline void ReplaceSpaces(std::wstring& wsValue); -inline void replace_all(std::wstring& s, const std::wstring& s1, const std::wstring& s2); -inline std::wstring EncodeXmlString(std::wstring wsString); COOXMLInterpretator::COOXMLInterpretator() : m_nFootnoteId(1), m_nHyperlinkId(1), m_nNumberingId(1), m_nId(1), m_nShapeId(1), m_bWasDivs(false), m_pFonts(nullptr) @@ -346,7 +343,7 @@ void COOXMLInterpretator::End(const std::wstring& wsDst) } // webSettings.xml - if (!m_bWasDivs) + if (m_bWasDivs) m_oWebSettings.WriteString(L"</w:divs>"); m_oWebSettings.WriteString(L"</w:webSettings>"); @@ -424,9 +421,6 @@ void COOXMLInterpretator::CloseT() void COOXMLInterpretator::BeginBlock() { - m_arStates.top().m_pCurrentDocument = new XmlString(); - //Сохранить состояние - SaveState(); m_arStates.top().CreateNewCurrentDocument(); @@ -450,6 +444,19 @@ void COOXMLInterpretator::EndBlock(bool bAddBlock) RollBackState(); } +void COOXMLInterpretator::SetDataOutput(XmlString* pOutputData) +{ + SaveState(); + m_arStates.top().m_pCurrentDocument = pOutputData; + m_arStates.top().m_bRemoveCurrentDocument = false; +} + +void COOXMLInterpretator::RevertDataOutput() +{ + CloseP(); + RollBackState(); +} + void COOXMLInterpretator::SaveState() { m_arStates.push(m_arStates.top()); @@ -1167,27 +1174,6 @@ inline bool ElementInTable(const std::vector<NSCSS::CNode>& arSelectors) return arSelectors.crend() != std::find_if(arSelectors.crbegin(), arSelectors.crend(), [](const NSCSS::CNode& oNode) { return L"table" == oNode.m_wsName; }); } -inline void WriteToStringBuilder(NSStringUtils::CStringBuilder& oSrcStringBuilder, NSStringUtils::CStringBuilder& oDstStringBuilder) -{ - if (oSrcStringBuilder.GetCurSize() < MAX_STRING_BLOCK_SIZE) - { - oDstStringBuilder.Write(oSrcStringBuilder); - return; - } - - size_t ulSize = oSrcStringBuilder.GetCurSize(); - size_t ulCurrentBlockSize = 0, ulPosition = 0; - - while (ulSize > 0) - { - ulCurrentBlockSize = std::min(ulSize, MAX_STRING_BLOCK_SIZE); - oDstStringBuilder.WriteString(oSrcStringBuilder.GetSubData(ulPosition, ulCurrentBlockSize)); - - ulSize -= ulCurrentBlockSize; - ulPosition += ulCurrentBlockSize; - } -} - inline bool ElementInHeader(const std::vector<NSCSS::CNode> arSelsectors) { for (const NSCSS::CNode& oNode : arSelsectors) @@ -1365,30 +1351,4 @@ inline void ReplaceSpaces(std::wstring& wsValue) itBegin = std::find_if(itBegin + 1, wsValue.cend(), [](wchar_t wchValue){ return std::iswspace(wchValue) && 0xa0 != wchValue; }); } } - -inline void ReplaceAll(std::wstring& s, const std::wstring& s1, const std::wstring& s2) -{ - size_t pos = s.find(s1); - size_t l = s2.length(); - while (pos != std::string::npos) - { - if (!(s1 == L"&" && s2 == L"&" && s.length() > pos + 4 && s[pos] == L'&' && s[pos + 1] == L'a' && s[pos + 2] == L'm' && s[pos + 3] == L'p' && s[pos + 4] == L';')) - s.replace(pos, s1.length(), s2); - pos = s.find(s1, pos + l); - } -} - -inline std::wstring EncodeXmlString(std::wstring wsString) -{ - ReplaceAll(wsString, L"&", L"&"); - ReplaceAll(wsString, L"<", L"<"); - ReplaceAll(wsString, L">", L">"); - ReplaceAll(wsString, L"\"", L"""); - ReplaceAll(wsString, L"\'", L"'"); - ReplaceAll(wsString, L"\n", L" "); - ReplaceAll(wsString, L"\r", L" "); - ReplaceAll(wsString, L"\t", L" "); - - return wsString; -} } diff --git a/HtmlFile2/Interpretators/OOXMLInterpretator.h b/HtmlFile2/Interpretators/OOXMLInterpretator.h index 597d65c41b..fd5987cb3e 100644 --- a/HtmlFile2/Interpretators/OOXMLInterpretator.h +++ b/HtmlFile2/Interpretators/OOXMLInterpretator.h @@ -2,7 +2,6 @@ #define OOXMLINTERPRETATOR_H // #include "HTMLInterpretator.h" -#include "../../DesktopEditor/common/StringBuilder.h" #include "../../Common/3dParty/html/css/src/xhtml/CDocumentStyle.h" #include "../../Common/3dParty/html/css/src/CCssCalculator.h" @@ -13,8 +12,6 @@ namespace NSFonts { class IApplicationFonts; } namespace HTML { -using XmlString = NSStringUtils::CStringBuilder; - struct TImageData { UINT m_unWidth; @@ -135,6 +132,9 @@ public: void BeginBlock() override; void EndBlock(bool bAddBlock) override; + void SetDataOutput(XmlString* pOutputData) override; + void RevertDataOutput() override; + void SaveState(); void RollBackState(); diff --git a/HtmlFile2/Table.cpp b/HtmlFile2/Table.cpp new file mode 100644 index 0000000000..873660e47f --- /dev/null +++ b/HtmlFile2/Table.cpp @@ -0,0 +1,703 @@ +#include "Table.h" + +#include "Common.h" +#include "src/StringFinder.h" + +namespace HTML +{ +#define MAX_STRING_BLOCK_SIZE (size_t)10485760 + +#define RELEASE_VECTOR_PTR(vector_object, object_type) \ + for (object_type* pElement : vector_object) \ + RELEASEOBJECT(pElement) + +#define FIRST_ELEMENT 0x00000001 +#define LAST_ELEMENT 0x00000002 +#define MID_ELEMENT 0x00000004 + +#define PARSE_MODE_HEADER 0x00000100 +#define PARSE_MODE_BODY 0x00000200 +#define PARSE_MODE_FOOTHER 0x00000400 + +#define COL_POSITION_MASK 0x0000000F +#define ROW_POSITION_MASK 0x000000F0 +#define PARSE_MODE_MASK 0x00000F00 + +#define DEFAULT_PAGE_WIDTH 12240 // Значение в Twips +#define DEFAULT_PAGE_HEIGHT 15840 // Значение в Twips + +TTableRowStyle::TTableRowStyle() + : m_unMaxIndex(0), m_unMaxHeight(0), m_bIsHeader(false) +{} + +bool TTableRowStyle::Empty() const +{ + return 0 == m_unMaxHeight && false == m_bIsHeader; +} + +TTableCellStyle::TTableCellStyle() +{} + +bool TTableCellStyle::Empty() +{ + return m_oWidth.Empty() && m_oHeight.Empty() && m_oBorder.Empty() && m_oPadding.Empty() && m_wsVAlign.empty() && m_wsVAlign.empty(); +} + +void TTableCellStyle::Copy(const TTableCellStyle* pTableCellStyle) +{ + if (NULL == pTableCellStyle) + return; + + m_oWidth = pTableCellStyle->m_oWidth; + m_oHeight = pTableCellStyle->m_oHeight; + m_oBorder = pTableCellStyle->m_oBorder; + m_oPadding = pTableCellStyle->m_oPadding; + m_oBackground = pTableCellStyle->m_oBackground; + + m_wsHAlign = pTableCellStyle->m_wsHAlign; + m_wsVAlign = pTableCellStyle->m_wsVAlign; +} + +TTableCellStyle& TTableCellStyle::operator+=(const TTableCellStyle* pCellStyle) +{ + if (NULL == pCellStyle) + return *this; + + m_oWidth += pCellStyle->m_oWidth; + m_oHeight += pCellStyle->m_oHeight; + m_oBorder += pCellStyle->m_oBorder; + m_oPadding += pCellStyle->m_oPadding; + m_oBackground += pCellStyle->m_oBackground; + + if (m_wsHAlign.empty()) + m_wsHAlign = pCellStyle->m_wsHAlign; + + if (m_wsVAlign.empty()) + m_wsVAlign = pCellStyle->m_wsVAlign; + + return *this; +} + +CStorageTableCell::CStorageTableCell() + : m_unColspan(1), m_unRowSpan(1), m_bIsMerged(false), m_bIsEmpty(false) +{} + +CStorageTableCell::CStorageTableCell(UINT unColspan, UINT unRowspan, bool bIsMerged, bool bIsEmpty) + : m_unColspan(unColspan), m_unRowSpan(unRowspan), m_bIsMerged(bIsMerged), + m_bIsEmpty(bIsEmpty) +{} + +CStorageTableCell::CStorageTableCell(CStorageTableCell& oCell) + : m_unColspan(oCell.m_unColspan), m_unRowSpan(oCell.m_unRowSpan), m_bIsMerged(oCell.m_bIsMerged), + m_bIsEmpty(oCell.m_bIsEmpty), m_oStyles(oCell.m_oStyles) +{ + WriteToStringBuilder(oCell.m_oData, m_oData); +} + +bool CStorageTableCell::Empty() const +{ + return m_bIsEmpty; +} + +bool CStorageTableCell::Merged() const +{ + return m_bIsMerged; +} + +CStorageTableCell* CStorageTableCell::Copy() +{ + return new CStorageTableCell(*this); +} + +CStorageTableCell* CStorageTableCell::CreateEmpty(UINT unColspan, bool m_bIsMerged, const TTableCellStyle* pStyle) +{ + CStorageTableCell *pCell = new CStorageTableCell(unColspan, 1, m_bIsMerged, true); + + pCell->m_oStyles.Copy(pStyle); + + return pCell; +} + +CStorageTableCell* CStorageTableCell::CreateEmpty(const TTableCellStyle* pStyle) +{ + CStorageTableCell *pCell = new CStorageTableCell(1, 1, false, true); + + pCell->m_oStyles.Copy(pStyle); + + return pCell; +} + +void CStorageTableCell::SetColspan(UINT unColspan, UINT unCurrentIndex) +{ + if (MAXCOLUMNSINTABLE - 1 != unCurrentIndex) + m_unColspan = std::min(MAXCOLUMNSINTABLE - 1 - unCurrentIndex, unColspan); + else + m_unColspan = 1; +} + +UINT CStorageTableCell::GetColspan() const +{ + return m_unColspan; +} + +void CStorageTableCell::SetRowspan(UINT unRowspan) +{ + m_unRowSpan = unRowspan; +} + +UINT CStorageTableCell::GetRowspan() const +{ + return m_unRowSpan; +} + +NSStringUtils::CStringBuilder* CStorageTableCell::GetData() +{ + return &m_oData; +} + +const TTableCellStyle* CStorageTableCell::GetStyles() const +{ + return &m_oStyles; +} + +TTableCellStyle* CStorageTableCell::GetStyles() +{ + return &m_oStyles; +} + +void CStorageTableCell::SetWidth(const NSCSS::NSProperties::CDigit& oWidth) +{ + m_oStyles.m_oWidth = oWidth; +} + +void CStorageTableCell::SetHeight(const NSCSS::NSProperties::CDigit& oHeight) +{ + m_oStyles.m_oHeight = oHeight; +} + +UINT CStorageTableCell::GetWidth() const +{ + return m_oStyles.m_oWidth.ToInt(NSCSS::Twips, DEFAULT_PAGE_WIDTH); +} + +UINT CStorageTableCell::GetHeight() const +{ + return m_oStyles.m_oHeight.ToInt(NSCSS::Twips, DEFAULT_PAGE_HEIGHT); +} + +void CStorageTableCell::SetBorder(const NSCSS::NSProperties::CBorder& oBorder) +{ + m_oStyles.m_oBorder = oBorder; +} + +void CStorageTableCell::ClearTopBorder() +{ + m_oStyles.m_oBorder.SetTopSide(L"none", 0, true); +} + +void CStorageTableCell::ClearLeftBorder() +{ + m_oStyles.m_oBorder.SetLeftSide(L"none", 0, true); +} + +void CStorageTableCell::ClearBottomBorder() +{ + m_oStyles.m_oBorder.SetBottomSide(L"none", 0, true); +} + +void CStorageTableCell::ClearRightBorder() +{ + m_oStyles.m_oBorder.SetRightSide(L"none", 0, true); +} + +void CStorageTableCell::SetPadding(const NSCSS::NSProperties::CIndent& oPadding) +{ + m_oStyles.m_oPadding = oPadding; +} + +void CStorageTableCell::SetHAlign(const std::wstring& wsAlign) +{ + m_oStyles.m_wsHAlign = wsAlign; +} + +void CStorageTableCell::SetVAlign(const std::wstring& wsAlign) +{ + m_oStyles.m_wsVAlign = wsAlign; +} + +void CStorageTableCell::SetBackground(const NSCSS::NSProperties::CColor& oColor) +{ + m_oStyles.m_oBackground = oColor; +} + +CStorageTableRow::CStorageTableRow() +{} + +CStorageTableRow::~CStorageTableRow() +{ + for (CStorageTableCell* pCell : m_arCells) + RELEASEOBJECT(pCell); +} + +void CStorageTableRow::AddCell(CStorageTableCell* pCell) +{ + InsertCell(pCell, -1); +} + +void CStorageTableRow::InsertCell(CStorageTableCell* pCell, int nPosition) +{ + if (NULL == pCell) + return; + + if (nPosition < 0) + { + std::vector<CStorageTableCell*>::iterator itFoundEmpty = std::find_if(m_arCells.begin(), m_arCells.end(), [](CStorageTableCell* pCell) { return pCell->Empty() && !pCell->Merged(); }); + + if (m_arCells.end() != itFoundEmpty) + { + --m_oStyles.m_unMaxIndex; + delete *itFoundEmpty; + *itFoundEmpty = pCell; + + if (1 != pCell->GetColspan()) + { + ++itFoundEmpty; + UINT unColspan = pCell->GetColspan() - 1; + + while (m_arCells.end() != itFoundEmpty && (*itFoundEmpty)->Empty() && unColspan > 0) + { + --m_oStyles.m_unMaxIndex; + --unColspan; + delete (*itFoundEmpty); + itFoundEmpty = m_arCells.erase(itFoundEmpty); + } + + if (unColspan != 0) + pCell->SetColspan(pCell->GetColspan() - unColspan, MAXCOLUMNSINTABLE); + } + } + else + m_arCells.push_back(pCell); + } + else if (nPosition >= m_arCells.size()) + { + const UINT unMissingCount = nPosition - m_arCells.size(); + + for (UINT unIndex = 0; unIndex < unMissingCount; ++unIndex) + m_arCells.push_back(CStorageTableCell::CreateEmpty()); + + m_oStyles.m_unMaxIndex += unMissingCount; + + m_arCells.push_back(pCell); + } + else if (m_arCells[nPosition]->Empty()) + { + delete m_arCells[nPosition]; + --m_oStyles.m_unMaxIndex; + m_arCells[nPosition] = pCell; + + if (1 != pCell->GetColspan()) + { + ++nPosition; + UINT unDeleteCount = pCell->GetColspan() - 1; + while (nPosition < m_arCells.size() && m_arCells[nPosition]->Empty() && !m_arCells[nPosition]->Merged() && unDeleteCount > 0) + { + delete m_arCells[nPosition]; + --m_oStyles.m_unMaxIndex; + m_arCells.erase(m_arCells.begin() + nPosition); + --unDeleteCount; + } + + if (0 != unDeleteCount) + pCell->SetColspan(pCell->GetColspan() - unDeleteCount, MAXCOLUMNSINTABLE); + } + } + else + m_arCells.insert(m_arCells.begin() + nPosition, pCell); + + m_oStyles.m_unMaxIndex += pCell->GetColspan(); + + if (1 == pCell->GetColspan() && 1 == pCell->GetRowspan()) + m_oStyles.m_unMaxHeight = std::max(m_oStyles.m_unMaxHeight, pCell->GetHeight()); +} + +UINT CStorageTableRow::GetIndex() const +{ + return m_oStyles.m_unMaxIndex; +} + +UINT CStorageTableRow::GetCount() const +{ + return m_arCells.size(); +} + +CStorageTableCell* CStorageTableRow::operator[](UINT unIndex) +{ + if (unIndex >= m_arCells.size()) + return NULL; + + return m_arCells[unIndex]; +} + +bool CStorageTableRow::Empty() const +{ + return m_arCells.empty(); +} + +const TTableRowStyle& CStorageTableRow::GetStyles() const +{ + return m_oStyles; +} + +const std::vector<CStorageTableCell*>& CStorageTableRow::GetCells() const +{ + return m_arCells; +} + +CTableCol::CTableCol(UINT unSpan) + : m_unSpan(unSpan) +{} + +CTableCol::CTableCol(const NSCSS::CNode& oTableColNode) + : m_unSpan(1) +{ + m_unSpan = NSStringFinder::ToInt(oTableColNode.GetAttributeValue(L"span")); +} + +UINT CTableCol::GetSpan() const +{ + return m_unSpan; +} + +TTableCellStyle* CTableCol::GetStyle() +{ + return &m_oStyle; +} + +const TTableCellStyle* CTableCol::GetStyle() const +{ + return &m_oStyle; +} + +CTableColgroup::CTableColgroup(NSCSS::CNode& oTableColgroupNode) + : m_unWidth(0) +{ + m_unWidth = NSStringFinder::ToInt(oTableColgroupNode.GetAttributeValue(L"width"), 0); +} + +CTableColgroup::~CTableColgroup() +{ + RELEASE_VECTOR_PTR(m_arCols, CTableCol) +} + +bool CTableColgroup::Empty() const +{ + return m_arCols.empty(); +} + +void CTableColgroup::AddCol(CTableCol* pCol) +{ + if (NULL != pCol) + m_arCols.push_back(pCol); +} + +const std::vector<CTableCol*>& CTableColgroup::GetCols() const +{ + return m_arCols; +} + +TTableStyles::TTableStyles() + : m_nCellSpacing(-1), m_enRules(None) +{} + +bool TTableStyles::Empty() const +{ + return m_oPadding.Empty() && m_oMargin.Empty() && m_oBorder.Empty() && m_oWidth.Empty() && -1 == m_nCellSpacing && m_wsAlign.empty(); +} + +CStorageTable::CStorageTable() + : m_unMaxColumns(0) +{} + +CStorageTable::~CStorageTable() +{ + for (std::vector<CStorageTableRow*>& arHeaders : m_arHeaders) + RELEASE_VECTOR_PTR(arHeaders, CStorageTableRow) + + RELEASE_VECTOR_PTR(m_arFoother, CStorageTableRow) + RELEASE_VECTOR_PTR(m_arRows, CStorageTableRow) + RELEASE_VECTOR_PTR(m_arColgroups, CTableColgroup) +} + +CStorageTableRow* CStorageTable::operator[](UINT unIndex) +{ + if (unIndex < m_arRows.size()) + return m_arRows[unIndex]; + + return NULL; +} + +bool CStorageTable::Empty() const +{ + return m_arHeaders.empty() && m_arRows.empty() && m_arFoother.empty(); +} + +bool CStorageTable::HaveCaption() +{ + return 0 != m_oCaption.GetCurSize(); +} + +bool CStorageTable::HaveColgroups() const +{ + return !m_arColgroups.empty(); +} + +UINT CStorageTable::GetRowCount() const +{ + return m_arRows.size(); +} + +const TTableStyles& CStorageTable::GetTableStyles() const +{ + return m_oStyles; +} + +const TTableCellStyle* CStorageTable::GetColStyle(UINT unColumnNumber) const +{ + if (m_arColgroups.empty()) + return NULL; + + UINT unCurrentNumber = 0; + + for (const CTableColgroup* pColgroup : m_arColgroups) + { + for (const CTableCol* pCol : pColgroup->GetCols()) + { + unCurrentNumber += pCol->GetSpan(); + + if (unCurrentNumber >= unColumnNumber) + return pCol->GetStyle(); + } + } + + return NULL; +} + +void CStorageTable::AddRows(std::vector<CStorageTableRow*>& arRows, ERowParseMode eParseMode) +{ + if (arRows.empty()) + return; + + if (ERowParseMode::Foother == eParseMode && !m_arFoother.empty()) + eParseMode = ERowParseMode::Header; + + if (ERowParseMode::Header == eParseMode) + m_arHeaders.push_back({}); + + for (CStorageTableRow* pRow : arRows) + AddRow(pRow, eParseMode); +} + +void CStorageTable::AddRow(CStorageTableRow* pRow, ERowParseMode eParseMode) +{ + if (NULL == pRow) + return; + + for (UINT unIndex = 0; unIndex < pRow->GetCount(); ++unIndex) + { + if (unIndex >= m_arMinColspan.size()) + m_arMinColspan.push_back((*pRow)[unIndex]->GetColspan()); + else if ((*pRow)[unIndex]->GetColspan() < m_arMinColspan[unIndex]) + m_arMinColspan[unIndex] = (*pRow)[unIndex]->GetColspan(); + } + + switch (eParseMode) + { + default: + case ERowParseMode::Body: + { + m_arRows.push_back(pRow); + break; + } + case ERowParseMode::Header: + { + if (m_arHeaders.empty()) + m_arHeaders.push_back({}); + + m_arHeaders.back().push_back(pRow); + break; + } + case ERowParseMode::Foother: + { + m_arFoother.push_back(pRow); + break; + } + } +} + +NSStringUtils::CStringBuilder* CStorageTable::GetCaptionData() +{ + return &m_oCaption; +} + +void CStorageTable::SetPadding(const NSCSS::NSProperties::CIndent& oPadding) +{ + m_oStyles.m_oPadding = oPadding; +} + +const NSCSS::NSProperties::CIndent& CStorageTable::GetPadding() const +{ + return m_oStyles.m_oPadding; +} + +void CStorageTable::SetMargin(const NSCSS::NSProperties::CIndent& oMargin) +{ + m_oStyles.m_oMargin = oMargin; +} + +void CStorageTable::SetBorder(const NSCSS::NSProperties::CBorder& oBorder) +{ + m_oStyles.m_oBorder = oBorder; +} + +void CStorageTable::SetWidth(const NSCSS::NSProperties::CDigit& oWidth) +{ + m_oStyles.m_oWidth = oWidth; +} + +void CStorageTable::SetCellSpacing(int nCellSpacing) +{ + m_oStyles.m_nCellSpacing = nCellSpacing; +} + +void CStorageTable::SetAlign(const std::wstring& wsValue) +{ + m_oStyles.m_wsAlign = wsValue; +} + +void CStorageTable::SetRules(const std::wstring& wsValue) +{ + if (wsValue.empty()) + return; + + if (NSStringFinder::Equals(wsValue, L"all")) + m_oStyles.m_enRules = TTableStyles::ETableRules::All; + else if (NSStringFinder::Equals(wsValue, L"groups")) + m_oStyles.m_enRules = TTableStyles::ETableRules::Groups; + else if (NSStringFinder::Equals(wsValue, L"cols")) + m_oStyles.m_enRules = TTableStyles::ETableRules::Cols; + else if (NSStringFinder::Equals(wsValue, L"none")) + m_oStyles.m_enRules = TTableStyles::ETableRules::None; + else if (NSStringFinder::Equals(wsValue, L"rows")) + m_oStyles.m_enRules = TTableStyles::ETableRules::Rows; +} + +void CStorageTable::AddColgroup(CTableColgroup* pElement) +{ + if (NULL != pElement) + m_arColgroups.push_back(pElement); +} + +void CStorageTable::RecalculateMaxColumns() +{ + for (const CStorageTableRow* pRow : m_arRows) + m_unMaxColumns = std::max(m_unMaxColumns, pRow->GetIndex()); +} + +void CStorageTable::Shorten() +{ + UINT unIndex = 0; + CStorageTableCell* pCell = NULL; + + UINT unMaxIndex = 0; //Максимальный индекс без учета строк, где имеется только 1 ячейка + + for (const CStorageTableRow* pRow : m_arRows) + { + if (1 < pRow->GetCount()) + unMaxIndex = std::max(unMaxIndex, pRow->GetIndex()); + } + + while (unIndex < m_arMinColspan.size()) + { + for (CStorageTableRow* pRow : m_arRows) + { + if (0 != unMaxIndex && 1 == pRow->GetCount() && pRow->GetIndex() > unMaxIndex) + { + pCell = (*pRow)[unIndex]; + + if (NULL == pCell) + continue; + + pCell->SetColspan(unMaxIndex, MAXCOLUMNSINTABLE); + continue; + } + + if (1 == m_arMinColspan[unIndex]) + break; + + pCell = (*pRow)[unIndex]; + + if (NULL == pCell) + continue; + + if (1 < pCell->GetColspan() && pCell->GetColspan() > m_arMinColspan[unIndex]) + { + pCell->SetColspan(m_arMinColspan[unIndex], MAXCOLUMNSINTABLE); + continue; + } + + if ((*pRow)[unIndex]->GetColspan() == m_arMinColspan[unIndex] + 1) + (*pRow)[unIndex]->SetColspan(2, MAXCOLUMNSINTABLE); + else if ((*pRow)[unIndex]->GetColspan() > m_arMinColspan[unIndex]) + (*pRow)[unIndex]->SetColspan((*pRow)[unIndex]->GetColspan() - m_arMinColspan[unIndex], MAXCOLUMNSINTABLE); + } + + ++unIndex; + } +} + +void CStorageTable::CompleteTable() +{ + UINT unMaxIndex = 0; + + for (CStorageTableRow* pRow : m_arRows) + unMaxIndex = std::max(unMaxIndex, pRow->GetIndex()); + + for (CStorageTableRow* pRow : m_arRows) + { + if (NULL == pRow || 0 == pRow->GetCount()) + continue; + + for (UINT unIndex = pRow->GetIndex(); unIndex < unMaxIndex; ++unIndex) + pRow->InsertCell(CStorageTableCell::CreateEmpty(), unIndex); + } + + RecalculateMaxColumns(); +} + +const std::vector<std::vector<CStorageTableRow*>>& CStorageTable::GetHeaders() const +{ + return m_arHeaders; +} + +const std::vector<CStorageTableRow*>& CStorageTable::GetFoothers() const +{ + return m_arFoother; +} + +const std::vector<CStorageTableRow*>& CStorageTable::GetRows() const +{ + return m_arRows; +} + +const std::vector<CTableColgroup*> CStorageTable::GetColgroups() const +{ + return m_arColgroups; +} + +UINT CStorageTable::GetMaxColumns() const +{ + return m_unMaxColumns; +} + +} diff --git a/HtmlFile2/Table.h b/HtmlFile2/Table.h new file mode 100644 index 0000000000..fb4b3f0afe --- /dev/null +++ b/HtmlFile2/Table.h @@ -0,0 +1,254 @@ +#ifndef TABLE_H +#define TABLE_H + +#include "../DesktopEditor/common/StringBuilder.h" +#include "../Common/3dParty/html/css/src/StyleProperties.h" +#include "../Common/3dParty/html/css/src/CNode.h" + +#include <vector> + +namespace HTML +{ +#define MAXCOLUMNSINTABLE 63 +#define MAXROWSINTABLE 32767 + +enum class ERowParseMode +{ + Header, + Body, + Foother +}; + +enum class ERowPosition +{ + First, + Middle, + Last +}; + +struct TTableRowStyle +{ + UINT m_unMaxIndex; + UINT m_unMaxHeight; + bool m_bIsHeader; + + TTableRowStyle(); + + bool Empty() const; +}; + +struct TTableCellStyle +{ + NSCSS::NSProperties::CDigit m_oWidth; + NSCSS::NSProperties::CDigit m_oHeight; + NSCSS::NSProperties::CBorder m_oBorder; + NSCSS::NSProperties::CIndent m_oPadding; + NSCSS::NSProperties::CColor m_oBackground; + + std::wstring m_wsHAlign; + std::wstring m_wsVAlign; + + TTableCellStyle(); + + bool Empty(); + void Copy(const TTableCellStyle* pTableCellStyle); + + TTableCellStyle& operator+=(const TTableCellStyle* pCellStyle); +}; + +class CStorageTable; + +class CStorageTableCell +{ +public: + CStorageTableCell(); + CStorageTableCell(UINT unColspan, UINT unRowspan, bool bIsMerged, bool bIsEmpty); + CStorageTableCell(CStorageTableCell& oCell); + + bool Empty() const; + bool Merged() const; + + CStorageTableCell* Copy(); + + static CStorageTableCell* CreateEmpty(UINT unColspan = 1, bool m_bIsMerged = false, const TTableCellStyle* pStyle = NULL); + static CStorageTableCell* CreateEmpty(const TTableCellStyle* pStyle); + + void SetColspan(UINT unColspan, UINT unCurrentIndex); + UINT GetColspan() const; + + void SetRowspan(UINT unRowspan); + UINT GetRowspan() const; + + NSStringUtils::CStringBuilder* GetData(); + + const TTableCellStyle* GetStyles() const; + TTableCellStyle* GetStyles(); + + void SetWidth(const NSCSS::NSProperties::CDigit& oWidth); + void SetHeight(const NSCSS::NSProperties::CDigit& oHeight); + + UINT GetWidth() const; + UINT GetHeight() const; + + void SetBorder(const NSCSS::NSProperties::CBorder& oBorder); + + void ClearTopBorder(); + void ClearLeftBorder(); + void ClearBottomBorder(); + void ClearRightBorder(); + + void SetPadding(const NSCSS::NSProperties::CIndent& oPadding); + void SetHAlign(const std::wstring& wsAlign); + void SetVAlign(const std::wstring& wsAlign); + void SetBackground(const NSCSS::NSProperties::CColor& oColor); +private: + UINT m_unColspan; + UINT m_unRowSpan; + + bool m_bIsMerged; + bool m_bIsEmpty; + + TTableCellStyle m_oStyles; + NSStringUtils::CStringBuilder m_oData; +}; + +class CStorageTableRow +{ +public: + CStorageTableRow(); + ~CStorageTableRow(); + + void AddCell(CStorageTableCell* pCell); + void InsertCell(CStorageTableCell *pCell, int nPosition); + + UINT GetIndex() const; + UINT GetCount() const; + + CStorageTableCell* operator[](UINT unIndex); + + bool Empty() const; + const TTableRowStyle& GetStyles() const; + const std::vector<CStorageTableCell*>& GetCells() const; +private: + TTableRowStyle m_oStyles; + std::vector<CStorageTableCell*> m_arCells; +}; + +class CTableCol +{ +public: + CTableCol(UINT unSpan); + CTableCol(const NSCSS::CNode& oTableColNode); + + UINT GetSpan() const; + TTableCellStyle* GetStyle(); + const TTableCellStyle* GetStyle() const; +private: + UINT m_unSpan; + TTableCellStyle m_oStyle; +}; + +class CTableColgroup +{ +public: + CTableColgroup(NSCSS::CNode& oTableColgroupNode); + ~CTableColgroup(); + + bool Empty() const; + + void AddCol(CTableCol* pCol); + + const std::vector<CTableCol*>& GetCols() const; +private: + std::vector<CTableCol*> m_arCols; + UINT m_unWidth; +}; + +//Необходимые стили таблицы +struct TTableStyles +{ + NSCSS::NSProperties::CIndent m_oPadding; + NSCSS::NSProperties::CIndent m_oMargin; + NSCSS::NSProperties::CBorder m_oBorder; + NSCSS::NSProperties::CDigit m_oWidth; + + int m_nCellSpacing; + + std::wstring m_wsAlign; + + enum ETableRules + { + All, + Groups, + Cols, + None, + Rows + } m_enRules; + + TTableStyles(); + + bool Empty() const; +}; + +class CStorageTable +{ +public: + CStorageTable(); + ~CStorageTable(); + + CStorageTableRow* operator[](UINT unIndex); + + bool Empty() const; + bool HaveCaption(); + bool HaveColgroups() const; + UINT GetRowCount() const; + const TTableStyles& GetTableStyles() const; + const TTableCellStyle* GetColStyle(UINT unColumnNumber) const; + + void AddRows(std::vector<CStorageTableRow*>& m_arRows, ERowParseMode eParseMode = ERowParseMode::Body); + void AddRow(CStorageTableRow* pRow, ERowParseMode eParseMode = ERowParseMode::Body); + + NSStringUtils::CStringBuilder* GetCaptionData(); + + void SetPadding(const NSCSS::NSProperties::CIndent& oPadding); + const NSCSS::NSProperties::CIndent& GetPadding() const; + + void SetMargin(const NSCSS::NSProperties::CIndent& oMargin); + void SetBorder(const NSCSS::NSProperties::CBorder& oBorder); + + void SetWidth(const NSCSS::NSProperties::CDigit& oWidth); + void SetCellSpacing(int nCellSpacing); + void SetAlign(const std::wstring& wsValue); + void SetRules(const std::wstring& wsValue); + + void AddColgroup(CTableColgroup* pElement); + + void RecalculateMaxColumns(); + void Shorten(); + void CompleteTable(); + + //TODO:: переделать на const std::vector<const T*> Get...() const; + const std::vector<std::vector<CStorageTableRow*>>& GetHeaders() const; + const std::vector<CStorageTableRow*>& GetFoothers() const; + const std::vector<CStorageTableRow*>& GetRows() const; + const std::vector<CTableColgroup*> GetColgroups() const; + + UINT GetMaxColumns() const; +private: + std::vector<std::vector<CStorageTableRow*>> m_arHeaders; + std::vector<CStorageTableRow*> m_arFoother; + std::vector<CStorageTableRow*> m_arRows; + + std::vector<UINT> m_arMinColspan; + + NSStringUtils::CStringBuilder m_oCaption; + + std::vector<CTableColgroup*> m_arColgroups; + + TTableStyles m_oStyles; + + UINT m_unMaxColumns; +}; +} + +#endif // TABLE_H diff --git a/HtmlFile2/Tags/HTMLTags.h b/HtmlFile2/Tags/HTMLTags.h index cb6ab95336..fe568d2e63 100644 --- a/HtmlFile2/Tags/HTMLTags.h +++ b/HtmlFile2/Tags/HTMLTags.h @@ -2,20 +2,23 @@ #define HTMLTAGS_H #include "../Common/3dParty/html/css/src/CNode.h" +#include <boost/any.hpp> namespace HTML { -struct ITag +class ITag { +public: virtual ~ITag() = default; - virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors) = 0; + virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData = boost::any()) = 0; virtual void Close(const std::vector<NSCSS::CNode>& arSelectors) = 0; }; -struct TEmptyTag : public ITag +class CEmptyTag : public ITag { - virtual ~TEmptyTag() = default; - virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors) +public: + virtual ~CEmptyTag() = default; + virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData = boost::any()) { return true; }; @@ -23,15 +26,16 @@ struct TEmptyTag : public ITag }; template<class T> -struct TTag : public ITag +class CTag : public ITag { +protected: T* m_pInterpretator; - - TTag(T* pInterpretator) +public: + CTag(T* pInterpretator) : m_pInterpretator(pInterpretator) {} - virtual ~TTag() = default; - virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors) = 0; + virtual ~CTag() = default; + virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData = boost::any()) = 0; virtual void Close(const std::vector<NSCSS::CNode>& arSelectors) = 0; bool ValidInterpretator() const @@ -42,37 +46,43 @@ struct TTag : public ITag #define CREATE_TAG(tag_name)\ template<class T>\ -struct tag_name : public TTag<T>\ +class tag_name : public CTag<T>\ {\ +public:\ virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors) = 0;\ virtual void Close(const std::vector<NSCSS::CNode>& arSelectors) = 0;\ } -CREATE_TAG(TAnchor); -CREATE_TAG(TAbbr); -CREATE_TAG(TBold); -CREATE_TAG(TBidirectional); -CREATE_TAG(TBreak); -CREATE_TAG(TCenter); -CREATE_TAG(TItalic); -CREATE_TAG(TKbd); -CREATE_TAG(TStrike); -CREATE_TAG(TUnderline); -CREATE_TAG(TMark); -CREATE_TAG(TQuotation); -CREATE_TAG(TSup); -CREATE_TAG(TSpan); -CREATE_TAG(TDD); -CREATE_TAG(TPreformatted); -CREATE_TAG(THeader); -CREATE_TAG(TDivision); -CREATE_TAG(TImage); -CREATE_TAG(TFont); -CREATE_TAG(TInput); -CREATE_TAG(TBaseFont); -CREATE_TAG(TBlockquote); -CREATE_TAG(THorizontalRule); -CREATE_TAG(TList); -CREATE_TAG(TListElement); +CREATE_TAG(CAnchor); +CREATE_TAG(CAbbr); +CREATE_TAG(CBold); +CREATE_TAG(CBidirectional); +CREATE_TAG(CBreak); +CREATE_TAG(CCenter); +CREATE_TAG(CItalic); +CREATE_TAG(CKbd); +CREATE_TAG(CStrike); +CREATE_TAG(CUnderline); +CREATE_TAG(CMark); +CREATE_TAG(CQuotation); +CREATE_TAG(CSup); +CREATE_TAG(CSpan); +CREATE_TAG(CDD); +CREATE_TAG(CPreformatted); +CREATE_TAG(CHeader); +CREATE_TAG(CDivision); +CREATE_TAG(CImage); +CREATE_TAG(CFont); +CREATE_TAG(CInput); +CREATE_TAG(CBaseFont); +CREATE_TAG(CBlockquote); +CREATE_TAG(CHorizontalRule); +CREATE_TAG(CList); +CREATE_TAG(CListElement); +CREATE_TAG(CCaption); +CREATE_TAG(CTable); +CREATE_TAG(CTableRow); +CREATE_TAG(CTableCell); + } #endif // HTMLTAGS_H diff --git a/HtmlFile2/Tags/OOXMLTags.cpp b/HtmlFile2/Tags/OOXMLTags.cpp index e8f14aea91..4a2d527afd 100644 --- a/HtmlFile2/Tags/OOXMLTags.cpp +++ b/HtmlFile2/Tags/OOXMLTags.cpp @@ -1,5 +1,7 @@ #include "OOXMLTags.h" + #include "../src/StringFinder.h" +#include "../Table.h" #include "../../Common/Network/FileTransporter/include/FileTransporter.h" @@ -11,8 +13,13 @@ #include "../../DesktopEditor/common/ProcessEnv.h" #include "../../DesktopEditor/common/Path.h" +#include <boost/tuple/tuple.hpp> + namespace HTML { +#define DEFAULT_PAGE_WIDTH 12240 // Значение в Twips +#define DEFAULT_PAGE_HEIGHT 15840 // Значение в Twips + inline bool ElementInTable(const std::vector<NSCSS::CNode>& arSelectors); inline bool NotValidExtension(const std::wstring& sExtention); @@ -27,11 +34,11 @@ bool UpdateImageData(const std::wstring& wsImagePath, TImageData& oImageData); const static double HTML_FONTS[7] = {7.5, 10, 12, 13.5, 18, 24, 36}; -TAnchor<COOXMLInterpretator>::TAnchor(COOXMLInterpretator* pInterpretator) - : TTag(pInterpretator) +CAnchor<COOXMLInterpretator>::CAnchor(COOXMLInterpretator* pInterpretator) + : CTag(pInterpretator) {} -bool TAnchor<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelectors) +bool CAnchor<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData) { if (!ValidInterpretator()) return false; @@ -65,7 +72,7 @@ bool TAnchor<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelec return true; } -void TAnchor<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& arSelectors) +void CAnchor<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& arSelectors) { if (!ValidInterpretator()) return; @@ -100,11 +107,11 @@ void TAnchor<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& arSele m_pInterpretator->CloseExternalHyperlink(); } -TAbbr<COOXMLInterpretator>::TAbbr(COOXMLInterpretator* pInterpretator) - : TTag(pInterpretator) +CAbbr<COOXMLInterpretator>::CAbbr(COOXMLInterpretator* pInterpretator) + : CTag(pInterpretator) {} -bool TAbbr<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelectors) +bool CAbbr<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData) { if (!ValidInterpretator()) return false; @@ -126,7 +133,7 @@ bool TAbbr<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelecto return true; } -void TAbbr<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& arSelectors) +void CAbbr<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& arSelectors) { if (!ValidInterpretator()) return; @@ -134,11 +141,11 @@ void TAbbr<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& arSelect m_pInterpretator->GetCurrentDocument().WriteString(L"<w:r><w:fldChar w:fldCharType=\"end\"/></w:r>"); } -TBreak<COOXMLInterpretator>::TBreak(COOXMLInterpretator* pInterpretator) - : TTag(pInterpretator) +CBreak<COOXMLInterpretator>::CBreak(COOXMLInterpretator* pInterpretator) + : CTag(pInterpretator) {} -bool TBreak<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelectors) +bool CBreak<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData) { if (!ValidInterpretator()) return false; @@ -148,14 +155,14 @@ bool TBreak<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelect return true; } -void TBreak<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& arSelectors) +void CBreak<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& arSelectors) {} -TDivision<COOXMLInterpretator>::TDivision(COOXMLInterpretator* pInterpretator) - : TTag(pInterpretator) +CDivision<COOXMLInterpretator>::CDivision(COOXMLInterpretator* pInterpretator) + : CTag(pInterpretator) {} -bool TDivision<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelectors) +bool CDivision<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData) { if (!ValidInterpretator()) return false; @@ -191,7 +198,7 @@ bool TDivision<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSel return true; } -void TDivision<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& arSelectors) +void CDivision<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& arSelectors) { if (!ValidInterpretator() || m_arFootnoteIDs.empty()) return; @@ -205,23 +212,28 @@ void TDivision<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& arSe m_arFootnoteIDs.pop(); } -TImage<COOXMLInterpretator>::TImage(COOXMLInterpretator* pInterpretator, XmlUtils::CXmlLiteReader* pXmlReader) - : TTag(pInterpretator), m_pXmlReader(pXmlReader) +CImage<COOXMLInterpretator>::CImage(COOXMLInterpretator* pInterpretator) + : CTag(pInterpretator) {} -bool TImage<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelectors) +bool CImage<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData) { if (!ValidInterpretator()) return false; if (L"svg" == arSelectors.back().m_wsName) { - if (nullptr == m_pXmlReader) + if (oExtraData.empty() || typeid(XmlUtils::CXmlLiteReader*) != oExtraData.type()) + return false; + + XmlUtils::CXmlLiteReader *pXmlReader{boost::any_cast<XmlUtils::CXmlLiteReader*>(oExtraData)}; + + if (nullptr == pXmlReader) return false; const std::wstring wsImagePath{m_pInterpretator->GetMediaDir() + L'i' + std::to_wstring(m_arrImages.size()) + L".png"}; - if (!ReadSVG(m_pXmlReader->GetOuterXml(), m_pInterpretator->GetFonts(), m_pInterpretator->GetTempDir(), wsImagePath)) + if (!ReadSVG(pXmlReader->GetOuterXml(), m_pInterpretator->GetFonts(), m_pInterpretator->GetTempDir(), wsImagePath)) return false; TImageData oNewImageData; @@ -366,11 +378,11 @@ bool TImage<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelect } // Проверка на повтор - std::vector<std::wstring>::iterator nFind = std::find(m_arrImages.begin(), m_arrImages.end(), wsSrc); + const std::vector<std::wstring>::const_iterator nFind = std::find(m_arrImages.cbegin(), m_arrImages.cend(), wsSrc); if (nFind != m_arrImages.end()) { bRes = true; - nImageId = nFind - m_arrImages.begin(); + nImageId = nFind - m_arrImages.cbegin(); } } @@ -426,14 +438,14 @@ bool TImage<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelect return true; } -void TImage<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& arSelectors) +void CImage<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& arSelectors) {} -TFont<COOXMLInterpretator>::TFont(COOXMLInterpretator* pInterpretator) - : TTag(pInterpretator) +CFont<COOXMLInterpretator>::CFont(COOXMLInterpretator* pInterpretator) + : CTag(pInterpretator) {} -bool TFont<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelectors) +bool CFont<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData) { if (!ValidInterpretator()) return false; @@ -451,12 +463,12 @@ bool TFont<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelecto int nSize = 3; if(!wsValue.empty()) { - // if(wsValue.front() == L'+') - // nSize += NSStringFinder::ToInt(wsValue.substr(1)); - // else if(wsValue.front() == L'-') - // nSize -= NSStringFinder::ToInt(wsValue.substr(1)); - // else - // nSize = NSStringFinder::ToInt(wsValue); + if(wsValue.front() == L'+') + nSize += NSStringFinder::ToInt(wsValue.substr(1)); + else if(wsValue.front() == L'-') + nSize -= NSStringFinder::ToInt(wsValue.substr(1)); + else + nSize = NSStringFinder::ToInt(wsValue); } if (nSize < 1 || nSize > 7) @@ -468,14 +480,14 @@ bool TFont<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelecto return true; } -void TFont<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& arSelectors) +void CFont<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& arSelectors) {} -TInput<COOXMLInterpretator>::TInput(COOXMLInterpretator* pInterpretator) - : TTag(pInterpretator) +CInput<COOXMLInterpretator>::CInput(COOXMLInterpretator* pInterpretator) + : CTag(pInterpretator) {} -bool TInput<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelectors) +bool CInput<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData) { if (!ValidInterpretator()) return false; @@ -504,14 +516,14 @@ bool TInput<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelect return true; } -void TInput<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& arSelectors) +void CInput<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& arSelectors) {} -TBaseFont<COOXMLInterpretator>::TBaseFont(COOXMLInterpretator* pInterpretator) - : TTag(pInterpretator) +CBaseFont<COOXMLInterpretator>::CBaseFont(COOXMLInterpretator* pInterpretator) + : CTag(pInterpretator) {} -bool TBaseFont<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelectors) +bool CBaseFont<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData) { if (!ValidInterpretator()) return false; @@ -549,14 +561,14 @@ bool TBaseFont<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSel return true; } -void TBaseFont<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& arSelectors) +void CBaseFont<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& arSelectors) {} -TBlockquote<COOXMLInterpretator>::TBlockquote(COOXMLInterpretator* pInterpretator) - : TTag(pInterpretator) +CBlockquote<COOXMLInterpretator>::CBlockquote(COOXMLInterpretator* pInterpretator) + : CTag(pInterpretator) {} -bool TBlockquote<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelectors) +bool CBlockquote<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData) { if (!ValidInterpretator()) return false; @@ -625,7 +637,7 @@ bool TBlockquote<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arS return true; } -void TBlockquote<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& arSelectors) +void CBlockquote<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& arSelectors) { if (!ValidInterpretator()) return; @@ -633,11 +645,11 @@ void TBlockquote<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& ar m_pInterpretator->RollBackDivId(); } -THorizontalRule<COOXMLInterpretator>::THorizontalRule(COOXMLInterpretator* pInterpretator) - : TTag(pInterpretator), m_unShapeId(1) +CHorizontalRule<COOXMLInterpretator>::CHorizontalRule(COOXMLInterpretator* pInterpretator) + : CTag(pInterpretator), m_unShapeId(1) {} -bool THorizontalRule<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelectors) +bool CHorizontalRule<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData) { if (!ValidInterpretator()) return false; @@ -733,14 +745,14 @@ bool THorizontalRule<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& return true; } -void THorizontalRule<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& arSelectors) +void CHorizontalRule<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& arSelectors) {} -TList<COOXMLInterpretator>::TList(COOXMLInterpretator* pInterpretator) - : TTag(pInterpretator), m_unNumberingId(1) +CList<COOXMLInterpretator>::CList(COOXMLInterpretator* pInterpretator) + : CTag(pInterpretator), m_unNumberingId(1) {} -bool TList<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelectors) +bool CList<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData) { if (!ValidInterpretator()) return false; @@ -781,7 +793,7 @@ bool TList<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelecto return true; } -void TList<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& arSelectors) +void CList<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& arSelectors) { if (!ValidInterpretator()) return; @@ -789,16 +801,16 @@ void TList<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& arSelect m_pInterpretator->CloseP(); } -TListElement<COOXMLInterpretator>::TListElement(COOXMLInterpretator* pInterpretator) - : TTag(pInterpretator) +CListElement<COOXMLInterpretator>::CListElement(COOXMLInterpretator* pInterpretator) + : CTag(pInterpretator) {} -bool TListElement<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelectors) +bool CListElement<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData) { return ValidInterpretator(); } -void TListElement<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& arSelectors) +void CListElement<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& arSelectors) { if (!ValidInterpretator()) return; @@ -1055,4 +1067,408 @@ bool UpdateImageData(const std::wstring& wsImagePath, TImageData& oImageData) return true; } + +CCaption<COOXMLInterpretator>::CCaption(COOXMLInterpretator* pInterpretator) + : CTag(pInterpretator) +{} + +bool CCaption<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData) +{ + if (!ValidInterpretator()) + return false; + + m_pInterpretator->WritePPr(arSelectors); + + return true; +} + +void CCaption<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& arSelectors) +{ + if (!ValidInterpretator()) + return; + + m_pInterpretator->CloseP(); +} + +std::wstring CreateBorders(const NSCSS::NSProperties::CBorder& oBorder, const NSCSS::NSProperties::CIndent* pPadding = NULL, bool bAddIntermediateLines = false, TTableStyles::ETableRules enTableRule = TTableStyles::ETableRules::None) +{ + std::wstring wsTable; + + if (oBorder.EqualSides() && (NULL == pPadding || pPadding->Equals())) + { + const std::wstring wsBorderStyle = NSCSS::CDocumentStyle::CalculateBorderStyle(oBorder.GetLeftBorder(), ((NULL == pPadding) ? NULL : (&(pPadding->GetLeft())))); + + wsTable = L"<w:top " + wsBorderStyle + L"/>" + L"<w:left " + wsBorderStyle + L"/>" + + L"<w:bottom " + wsBorderStyle + L"/>" + L"<w:right " + wsBorderStyle + L"/>"; + } + else + { + if (oBorder.GetTopBorder().Valid()) + wsTable += L"<w:top " + NSCSS::CDocumentStyle::CalculateBorderStyle(oBorder.GetTopBorder(), ((NULL == pPadding) ? NULL : (&(pPadding->GetTop())))) + L"/>"; + + if (oBorder.GetLeftBorder().Valid()) + wsTable += L"<w:left " + NSCSS::CDocumentStyle::CalculateBorderStyle(oBorder.GetLeftBorder(), ((NULL == pPadding) ? NULL : (&(pPadding->GetLeft())))) + L"/>"; + + if (oBorder.GetBottomBorder().Valid()) + wsTable += L"<w:bottom " + NSCSS::CDocumentStyle::CalculateBorderStyle(oBorder.GetBottomBorder(), ((NULL == pPadding) ? NULL : (&(pPadding->GetBottom())))) + L"/>"; + + if (oBorder.GetRightBorder().Valid()) + wsTable += L"<w:right " + NSCSS::CDocumentStyle::CalculateBorderStyle(oBorder.GetRightBorder(), ((NULL == pPadding) ? NULL : (&(pPadding->GetRight())))) + L"/>"; + } + + if (!bAddIntermediateLines) + return wsTable; + + if (TTableStyles::ETableRules::Rows == enTableRule || TTableStyles::ETableRules::All == enTableRule) + { + NSCSS::NSProperties::CBorderSide oNewSide(oBorder.GetBottomBorder()); + oNewSide.SetWidth(L"1pt", 0, true); + + wsTable += L"<w:insideH " + NSCSS::CDocumentStyle::CalculateBorderStyle(oNewSide) + L"/>"; + } + + if (TTableStyles::ETableRules::Cols == enTableRule || TTableStyles::ETableRules::All == enTableRule) + { + NSCSS::NSProperties::CBorderSide oNewSide(oBorder.GetRightBorder()); + oNewSide.SetWidth(L"1pt", 0, true); + + wsTable += L"<w:insideV " + NSCSS::CDocumentStyle::CalculateBorderStyle(oNewSide) + L"/>"; + } + + return wsTable; +} + +std::wstring CreateBorders(const std::wstring& wsStyle, UINT unSize, UINT unSpace, const std::wstring& wsAuto) +{ + const std::wstring wsBodyBorder{L"w:val=\"" + wsStyle + L"\" w:sz=\"" + std::to_wstring(unSize) + L"\" w:space=\"" + std::to_wstring(unSpace) + L"\" w:color=\"" + wsAuto + L"\""}; + + return L"<w:top " + wsBodyBorder + L"/>" + + L"<w:left " + wsBodyBorder + L"/>" + + L"<w:bottom " + wsBodyBorder + L"/>" + + L"<w:right " + wsBodyBorder + L"/>"; +} + +#define CreateOutsetBorders(enType) CreateBorders(L"outset", 6, 0, L"auto", enType) + +std::wstring CreateDefaultBorder(std::wstring wsSideName) +{ + std::transform(wsSideName.begin(), wsSideName.end(), wsSideName.begin(), std::towlower); + return L"<w:" + wsSideName + L" w:val=\"single\" w:sz=\"1\" w:space=\"0\" w:color=\"auto\"/>"; +} + +CTable<COOXMLInterpretator>::CTable(COOXMLInterpretator* pInterpretator) + : CTag(pInterpretator) +{} + +bool CTable<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData) +{ + if (!ValidInterpretator() || oExtraData.empty() || typeid(CStorageTable*) != oExtraData.type()) + return false; + + CStorageTable* pStorageTable{boost::any_cast<CStorageTable*>(oExtraData)}; + + if (pStorageTable->Empty()) + return false; + + XmlString& oCurrentDocument{m_pInterpretator->GetCurrentDocument()}; + + oCurrentDocument.WriteNodeBegin(L"w:tbl"); + oCurrentDocument.WriteNodeBegin(L"w:tblPr"); + + const TTableStyles& oTableStyles{pStorageTable->GetTableStyles()}; + + if (!oTableStyles.m_oWidth.Empty() && !oTableStyles.m_oWidth.Zero()) + { + if (NSCSS::UnitMeasure::Percent == oTableStyles.m_oWidth.GetUnitMeasure()) + oCurrentDocument += L"<w:tblW w:w=\"" + std::to_wstring(oTableStyles.m_oWidth.ToInt(NSCSS::UnitMeasure::Percent, 5000)) + L"\" w:type=\"pct\"/>"; + else + oCurrentDocument += L"<w:tblW w:w=\"" + std::to_wstring(oTableStyles.m_oWidth.ToInt(NSCSS::UnitMeasure::Twips)) + L"\" w:type=\"dxa\"/>"; + } + else + oCurrentDocument += L"<w:tblW w:w=\"0\" w:type=\"auto\"/>"; + + if (!oTableStyles.m_oMargin.GetLeft().Empty() && !oTableStyles.m_oMargin.GetLeft().Zero()) + { + if (NSCSS::UnitMeasure::Percent == oTableStyles.m_oMargin.GetLeft().GetUnitMeasure()) + oCurrentDocument += L"<w:tblInd w:w=\"" + std::to_wstring(oTableStyles.m_oMargin.GetLeft().ToInt(NSCSS::UnitMeasure::Percent, 5000)) + L"\" w:type=\"pct\"/>"; + else + oCurrentDocument += L"<w:tblInd w:w=\"" + std::to_wstring(oTableStyles.m_oMargin.GetLeft().ToInt(NSCSS::UnitMeasure::Twips)) + L"\" w:type=\"dxa\"/>"; + } + + if (!oTableStyles.m_wsAlign.empty()) + oCurrentDocument += L"<w:jc w:val=\"" + oTableStyles.m_wsAlign + L"\"/>"; + + if (0 < oTableStyles.m_nCellSpacing && oTableStyles.m_oBorder.GetCollapse() != NSCSS::NSProperties::BorderCollapse::Collapse) + oCurrentDocument += L"<w:tblCellSpacing w:w=\"" + std::to_wstring(oTableStyles.m_nCellSpacing) + L"\" w:type=\"dxa\"/>"; + + if (!oTableStyles.m_oBorder.Empty() && !oTableStyles.m_oBorder.Zero()) + oCurrentDocument += L"<w:tblBorders>" + CreateBorders(oTableStyles.m_oBorder, NULL, true, (TTableStyles::ETableRules::Groups == oTableStyles.m_enRules && !pStorageTable->GetColgroups().empty()) ? TTableStyles::ETableRules::Cols : oTableStyles.m_enRules) + L"</w:tblBorders>"; + + if (!oTableStyles.m_oPadding.Empty() && !oTableStyles.m_oPadding.Zero()) + { + const int nTopPadding = std::max(0, oTableStyles.m_oPadding.GetTop() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_HEIGHT)); + const int nLeftPadding = std::max(0, oTableStyles.m_oPadding.GetLeft() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_WIDTH )); + const int nBottomPadding = std::max(0, oTableStyles.m_oPadding.GetBottom().ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_HEIGHT)); + const int nRightPadding = std::max(0, oTableStyles.m_oPadding.GetRight() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_WIDTH )); + + oCurrentDocument.WriteNodeBegin(L"w:tblCellMar"); + + if (0 != nTopPadding) + oCurrentDocument += L"<w:top w:w=\"" + std::to_wstring(nTopPadding) + L"\" w:type=\"dxa\"/>"; + + if (0 != nLeftPadding) + oCurrentDocument += L"<w:left w:w=\"" + std::to_wstring(nLeftPadding) + L"\" w:type=\"dxa\"/>"; + + if (0 != nBottomPadding) + oCurrentDocument += L"<w:bottom w:w=\"" + std::to_wstring(nBottomPadding) + L"\" w:type=\"dxa\"/>"; + + if (0 != nRightPadding) + oCurrentDocument += L"<w:right w:w=\"" + std::to_wstring(nRightPadding) + L"\" w:type=\"dxa\"/>"; + + oCurrentDocument.WriteNodeEnd(L"w:tblCellMar"); + } + else + oCurrentDocument += L"<w:tblCellMar><w:top w:w=\"15\" w:type=\"dxa\"/><w:left w:w=\"15\" w:type=\"dxa\"/><w:bottom w:w=\"15\" w:type=\"dxa\"/><w:right w:w=\"15\" w:type=\"dxa\"/></w:tblCellMar>"; + + oCurrentDocument += L"<w:tblLook w:val=\"04A0\" w:noVBand=\"1\" w:noHBand=\"0\" w:lastColumn=\"0\" w:firstColumn=\"1\" w:lastRow=\"0\" w:firstRow=\"1\"/>"; + oCurrentDocument.WriteNodeEnd(L"w:tblPr"); + + if (pStorageTable->HaveCaption()) + { + oCurrentDocument.WriteNodeBegin(L"w:tr"); + oCurrentDocument.WriteNodeBegin(L"w:tc"); + oCurrentDocument.WriteNodeBegin(L"w:tcPr"); + oCurrentDocument += L"<w:tcW w:w=\"0\" w:type=\"auto\"/>"; + oCurrentDocument += L"<w:gridSpan w:val=\"" + std::to_wstring(pStorageTable->GetMaxColumns()) + L"\"/>"; + oCurrentDocument += L"<w:tcBorders><w:top w:val=\"nil\"/><w:left w:val=\"nil\"/><w:bottom w:val=\"nil\"/><w:right w:val=\"nil\"/></w:tcBorders>"; + oCurrentDocument += L"<w:vAlign w:val=\"center\"/>"; + oCurrentDocument += L"<w:hideMark/>"; + oCurrentDocument.WriteNodeEnd(L"w:tcPr"); + WriteToStringBuilder(*(pStorageTable->GetCaptionData()), oCurrentDocument); + oCurrentDocument.WriteNodeEnd(L"w:tc"); + oCurrentDocument.WriteNodeEnd(L"w:tr"); + } + + return true; +} + +void CTable<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& arSelectors) +{ + if (!ValidInterpretator()) + return; + + m_pInterpretator->GetCurrentDocument().WriteNodeEnd(L"w:tbl"); +} + +CTableRow<COOXMLInterpretator>::CTableRow(COOXMLInterpretator* pInterpretator) + : CTag(pInterpretator) +{} + +bool CTableRow<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData) +{ + using DataForRow = boost::tuple<CStorageTableRow&, const CStorageTable&>; + + if (!ValidInterpretator() || oExtraData.empty() || typeid(DataForRow) != oExtraData.type()) + return false; + + const DataForRow& oDataForRow(boost::any_cast<DataForRow>(oExtraData)); + CStorageTableRow& oStorageTableRow{boost::get<0>(oDataForRow)}; + + if (oStorageTableRow.Empty()) + return false; + + XmlString& oCurrentDocument{m_pInterpretator->GetCurrentDocument()}; + + oCurrentDocument.WriteNodeBegin(L"w:tr"); + + const TTableStyles& oTableStyles{boost::get<1>(oDataForRow).GetTableStyles()}; + const TTableRowStyle oTableRowStyles{oStorageTableRow.GetStyles()}; + + if (!oTableRowStyles.Empty() || 0 < oTableStyles.m_nCellSpacing) + { + oCurrentDocument.WriteNodeBegin(L"w:trPr"); + + if (oTableRowStyles.m_bIsHeader) + oCurrentDocument += L"<w:tblHeader/>"; + + if (0 < oTableRowStyles.m_unMaxHeight) + oCurrentDocument += L"<w:trHeight w:val=\"" + std::to_wstring(oTableRowStyles.m_unMaxHeight) + L"\"/>"; + + if (0 < oTableStyles.m_nCellSpacing) + oCurrentDocument += L"<w:tblCellSpacing w:w=\"" + std::to_wstring(oTableStyles.m_nCellSpacing) + L"\" w:type=\"dxa\"/>"; + + oCurrentDocument.WriteNodeEnd(L"w:trPr"); + } + + return true; +} + +void HTML::CTableRow<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& arSelectors) +{ + if (!ValidInterpretator()) + return; + + m_pInterpretator->GetCurrentDocument().WriteNodeEnd(L"w:tr"); +} + +CTableCell<COOXMLInterpretator>::CTableCell(COOXMLInterpretator* pInterpretator) + : CTag(pInterpretator) +{} + +std::wstring CalculateSidesToClean(UINT unColumnNumber, const std::vector<CTableColgroup*>& arColgroups, UINT unMaxColumns) +{ + if (arColgroups.empty()) + return std::wstring(); + + UINT unCurrentNumber = 0; + + for (const CTableColgroup* pColgroup : arColgroups) + { + for (const CTableCol* pCol : pColgroup->GetCols()) + { + if (unCurrentNumber + 1 == unCurrentNumber) + return (1 != pCol->GetSpan()) ? L"<w:right w:val=\"nil\"/>" : std::wstring(); + + unCurrentNumber += pCol->GetSpan(); + + if (unColumnNumber == unCurrentNumber) + return (1 != pCol->GetSpan()) ? L"<w:left w:val=\"nil\"/>" : std::wstring(); + else if (unColumnNumber < unCurrentNumber) + return std::wstring((1 != unColumnNumber) ? L"<w:left w:val=\"nil\"/>" : L"") + std::wstring((unMaxColumns != unColumnNumber) ? L"<w:right w:val=\"nil\"/>" : L""); + } + } + + return std::wstring(); +} + +bool CTableCell<COOXMLInterpretator>::Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData) +{ + using DataForCell = boost::tuple<CStorageTableCell&, const CStorageTable&, UINT, ERowParseMode, ERowPosition>; + + if (!ValidInterpretator() || oExtraData.empty() || typeid(DataForCell) != oExtraData.type()) + return false; + + const DataForCell& oDataForCell{boost::any_cast<const DataForCell>(oExtraData)}; + CStorageTableCell& oStorageTableCell{boost::get<0>(oDataForCell)}; + + XmlString& oCurrentDocument{m_pInterpretator->GetCurrentDocument()}; + + oCurrentDocument.WriteNodeBegin(L"w:tc"); + oCurrentDocument.WriteNodeBegin(L"w:tcPr"); + + const ERowParseMode eParseMode{boost::get<3>(oDataForCell)}; + + if (ERowParseMode::Header == eParseMode) + oCurrentDocument += L"<w:tblHeader/>"; + + TTableCellStyle oCellStyle(*oStorageTableCell.GetStyles()); + + const CStorageTable& oStorageTable{boost::get<1>(oDataForCell)}; + const UINT unColumnNumber{boost::get<2>(oDataForCell)}; + const TTableCellStyle* pColStyle{oStorageTable.GetColStyle(unColumnNumber)}; + + if (NULL != pColStyle) + oCellStyle += pColStyle; + + if (!oCellStyle.m_oWidth.Empty()) + { + if (NSCSS::UnitMeasure::Percent == oCellStyle.m_oWidth.GetUnitMeasure()) + oCurrentDocument += L"<w:tcW w:w=\"" + std::to_wstring(oCellStyle.m_oWidth.ToInt(NSCSS::UnitMeasure::Percent, 5000)) + L"\" w:type=\"pct\"/>"; + else + { + if (!oCellStyle.m_oWidth.Zero()) + { + int nWidth; + if (NSCSS::UnitMeasure::None != oCellStyle.m_oWidth.GetUnitMeasure()) + nWidth = oCellStyle.m_oWidth.ToInt(NSCSS::UnitMeasure::Twips); + else + nWidth = static_cast<int>(NSCSS::CUnitMeasureConverter::ConvertPx(oCellStyle.m_oWidth.ToDouble(), NSCSS::UnitMeasure::Twips, 96) + 0.5); + + oCurrentDocument += L"<w:tcW w:w=\"" + std::to_wstring(nWidth) + L"\" w:type=\"dxa\"/>"; + } + else + oCurrentDocument += L"<w:tcW w:w=\"6\" w:type=\"dxa\"/>"; + } + } + else + oCurrentDocument += L"<w:tcW w:w=\"0\" w:type=\"auto\"/>"; + + if (1 != oStorageTableCell.GetColspan()) + oCurrentDocument += L"<w:gridSpan w:val=\"" + std::to_wstring(oStorageTableCell.GetColspan()) + L"\"/>"; + + if (oStorageTableCell.Merged()) + oCurrentDocument += L"<w:vMerge w:val=\"continue\"/>"; + else if (1 < oStorageTableCell.GetRowspan()) + oCurrentDocument += L"<w:vMerge w:val=\"restart\"/>"; + + const TTableStyles oTableStyles{oStorageTable.GetTableStyles()}; + + if (!oCellStyle.m_oBorder.Empty() && !oCellStyle.m_oBorder.Zero() /*&& oCellStyle.m_oBorder != oTableStyles.m_oBorder*/) + oCurrentDocument += L"<w:tcBorders>" + CreateBorders(oCellStyle.m_oBorder, &oCellStyle.m_oPadding) + L"</w:tcBorders>"; + else if (TTableStyles::ETableRules::Groups == oTableStyles.m_enRules) + { + std::wstring wsBorders; + + if (oStorageTable.HaveColgroups()) + wsBorders += CalculateSidesToClean(unColumnNumber, oStorageTable.GetColgroups(), oStorageTable.GetMaxColumns()); + + const ERowPosition eRowPosition{boost::get<4>(oDataForCell)}; + + if (ERowParseMode::Header == eParseMode && ERowPosition::Last == eRowPosition) + wsBorders += CreateDefaultBorder(L"bottom"); + else if (ERowParseMode::Foother == eParseMode && ERowPosition::First == eRowPosition) + wsBorders += CreateDefaultBorder(L"top"); + + if (!wsBorders.empty()) + oCurrentDocument += L"<w:tcBorders>" + wsBorders + L"</w:tcBorders>"; + } + + if (!oCellStyle.m_oBackground.Empty()) + { + const std::wstring wsShdFill{(NSCSS::NSProperties::ColorNone == oCellStyle.m_oBackground.GetType()) ? L"auto" : oCellStyle.m_oBackground.ToWString()}; + oCurrentDocument += L"<w:shd w:val=\"clear\" w:color=\"auto\" w:fill=\"" + wsShdFill + L"\"/>"; + } + + if (!oCellStyle.m_wsVAlign.empty()) + oCurrentDocument += L"<w:vAlign w:val=\"" + oCellStyle.m_wsVAlign + L"\"/>"; + else + oCurrentDocument += L"<w:vAlign w:val=\"center\"/>"; + + if (!oCellStyle.m_oPadding.Empty() && oTableStyles.m_oPadding != oCellStyle.m_oPadding) + { + const int nTopPadding = std::max(oTableStyles.m_oPadding.GetTop() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_HEIGHT), + oCellStyle .m_oPadding.GetTop() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_HEIGHT)); + const int nLeftPadding = std::max(oTableStyles.m_oPadding.GetLeft() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_WIDTH), + oCellStyle .m_oPadding.GetLeft() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_WIDTH)); + const int nBottomPadding = std::max(oTableStyles.m_oPadding.GetBottom().ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_HEIGHT), + oCellStyle .m_oPadding.GetBottom().ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_HEIGHT)); + const int nRightPadding = std::max(oTableStyles.m_oPadding.GetRight() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_WIDTH), + oCellStyle .m_oPadding.GetRight() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_WIDTH)); + + oCurrentDocument += L"<w:tcMar>" + "<w:top w:w=\"" + std::to_wstring(nTopPadding) + L"\" w:type=\"dxa\"/>" + "<w:left w:w=\"" + std::to_wstring(nLeftPadding) + L"\" w:type=\"dxa\"/>" + "<w:bottom w:w=\"" + std::to_wstring(nBottomPadding) + L"\" w:type=\"dxa\"/>" + "<w:right w:w=\"" + std::to_wstring(nRightPadding) + L"\" w:type=\"dxa\"/>" + "</w:tcMar>"; + } + + oCurrentDocument += L"<w:hideMark/>"; + oCurrentDocument.WriteNodeEnd(L"w:tcPr"); + + if (0 != oStorageTableCell.GetData()->GetCurSize()) + WriteToStringBuilder(*oStorageTableCell.GetData(), oCurrentDocument); + else + m_pInterpretator->WriteEmptyParagraph(); + + return true; +} + + void HTML::CTableCell<COOXMLInterpretator>::Close(const std::vector<NSCSS::CNode>& arSelectors) + { + if (!ValidInterpretator()) + return; + + m_pInterpretator->GetCurrentDocument().WriteNodeEnd(L"w:tc"); + } } diff --git a/HtmlFile2/Tags/OOXMLTags.h b/HtmlFile2/Tags/OOXMLTags.h index 368abeaf4d..72f5c9c026 100644 --- a/HtmlFile2/Tags/OOXMLTags.h +++ b/HtmlFile2/Tags/OOXMLTags.h @@ -5,114 +5,154 @@ #include "../Interpretators/OOXMLInterpretator.h" #include <stack> -namespace XmlUtils { class CXmlLiteReader; } - namespace HTML { template<> -struct TAnchor<COOXMLInterpretator> : public TTag<COOXMLInterpretator> +class CAnchor<COOXMLInterpretator> : public CTag<COOXMLInterpretator> { - TAnchor(COOXMLInterpretator* pInterpretator); - virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors) override; +public: + CAnchor(COOXMLInterpretator* pInterpretator); + virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData = boost::any()) override; virtual void Close(const std::vector<NSCSS::CNode>& arSelectors) override; }; template<> -struct TAbbr<COOXMLInterpretator> : public TTag<COOXMLInterpretator> +class CAbbr<COOXMLInterpretator> : public CTag<COOXMLInterpretator> { - TAbbr(COOXMLInterpretator* pInterpretator); - virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors) override; +public: + CAbbr(COOXMLInterpretator* pInterpretator); + virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData = boost::any()) override; virtual void Close(const std::vector<NSCSS::CNode>& arSelectors) override; }; template<> -struct TBreak<COOXMLInterpretator> : public TTag<COOXMLInterpretator> +class CBreak<COOXMLInterpretator> : public CTag<COOXMLInterpretator> { - TBreak(COOXMLInterpretator* pInterpretator); - virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors) override; +public: + CBreak(COOXMLInterpretator* pInterpretator); + virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData = boost::any()) override; virtual void Close(const std::vector<NSCSS::CNode>& arSelectors) override; }; template<> -struct TDivision<COOXMLInterpretator> : public TTag<COOXMLInterpretator> +class CDivision<COOXMLInterpretator> : public CTag<COOXMLInterpretator> { std::stack<UINT> m_arFootnoteIDs; - - TDivision(COOXMLInterpretator* pInterpretator); - virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors) override; +public: + CDivision(COOXMLInterpretator* pInterpretator); + virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData = boost::any()) override; virtual void Close(const std::vector<NSCSS::CNode>& arSelectors) override; }; template<> -struct TImage<COOXMLInterpretator> : public TTag<COOXMLInterpretator> +class CImage<COOXMLInterpretator> : public CTag<COOXMLInterpretator> { std::vector<std::wstring> m_arrImages; - XmlUtils::CXmlLiteReader* m_pXmlReader; //TODO:: подумать как можно иначе получить текс в svg теге - - TImage(COOXMLInterpretator* pInterpretator, XmlUtils::CXmlLiteReader* pXmlReader); - virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors) override; +public: + CImage(COOXMLInterpretator* pInterpretator); + virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData = boost::any()) override; virtual void Close(const std::vector<NSCSS::CNode>& arSelectors) override; }; template<> -struct TFont<COOXMLInterpretator> : public TTag<COOXMLInterpretator> +class CFont<COOXMLInterpretator> : public CTag<COOXMLInterpretator> { - TFont(COOXMLInterpretator* pInterpretator); - virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors) override; +public: + CFont(COOXMLInterpretator* pInterpretator); + virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData = boost::any()) override; virtual void Close(const std::vector<NSCSS::CNode>& arSelectors) override; }; template<> -struct TInput<COOXMLInterpretator> : public TTag<COOXMLInterpretator> +class CInput<COOXMLInterpretator> : public CTag<COOXMLInterpretator> { - TInput(COOXMLInterpretator* pInterpretator); - virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors) override; +public: + CInput(COOXMLInterpretator* pInterpretator); + virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData = boost::any()) override; virtual void Close(const std::vector<NSCSS::CNode>& arSelectors) override; }; template<> -struct TBaseFont<COOXMLInterpretator> : public TTag<COOXMLInterpretator> +class CBaseFont<COOXMLInterpretator> : public CTag<COOXMLInterpretator> { - TBaseFont(COOXMLInterpretator* pInterpretator); - virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors) override; +public: + CBaseFont(COOXMLInterpretator* pInterpretator); + virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData = boost::any()) override; virtual void Close(const std::vector<NSCSS::CNode>& arSelectors) override; }; template<> -struct TBlockquote<COOXMLInterpretator> : public TTag<COOXMLInterpretator> +class CBlockquote<COOXMLInterpretator> : public CTag<COOXMLInterpretator> { std::map<std::wstring, UINT> m_mDivs; - - TBlockquote(COOXMLInterpretator* pInterpretator); - virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors) override; +public: + CBlockquote(COOXMLInterpretator* pInterpretator); + virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData = boost::any()) override; virtual void Close(const std::vector<NSCSS::CNode>& arSelectors) override; }; template<> -struct THorizontalRule<COOXMLInterpretator> : public TTag<COOXMLInterpretator> +class CHorizontalRule<COOXMLInterpretator> : public CTag<COOXMLInterpretator> { UINT m_unShapeId; - - THorizontalRule(COOXMLInterpretator* pInterpretator); - virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors) override; +public: + CHorizontalRule(COOXMLInterpretator* pInterpretator); + virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData = boost::any()) override; virtual void Close(const std::vector<NSCSS::CNode>& arSelectors) override; }; template<> -struct TList<COOXMLInterpretator> : public TTag<COOXMLInterpretator> +class CList<COOXMLInterpretator> : public CTag<COOXMLInterpretator> { UINT m_unNumberingId; - - TList(COOXMLInterpretator* pInterpretator); - virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors) override; +public: + CList(COOXMLInterpretator* pInterpretator); + virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData = boost::any()) override; virtual void Close(const std::vector<NSCSS::CNode>& arSelectors) override; }; template<> -struct TListElement<COOXMLInterpretator> : public TTag<COOXMLInterpretator> +class CListElement<COOXMLInterpretator> : public CTag<COOXMLInterpretator> { - TListElement(COOXMLInterpretator* pInterpretator); - virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors) override; +public: + CListElement(COOXMLInterpretator* pInterpretator); + virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData = boost::any()) override; + virtual void Close(const std::vector<NSCSS::CNode>& arSelectors) override; +}; + +template<> +class CCaption<COOXMLInterpretator> : public CTag<COOXMLInterpretator> +{ +public: + CCaption(COOXMLInterpretator* pInterpretator); + virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData = boost::any()) override; + virtual void Close(const std::vector<NSCSS::CNode>& arSelectors) override; +}; + +template<> +class CTable<COOXMLInterpretator> : public CTag<COOXMLInterpretator> +{ +public: + CTable(COOXMLInterpretator* pInterpretator); + virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData = boost::any()) override; + virtual void Close(const std::vector<NSCSS::CNode>& arSelectors) override; +}; + +template<> +class CTableRow<COOXMLInterpretator> : public CTag<COOXMLInterpretator> +{ +public: + CTableRow(COOXMLInterpretator* pInterpretator); + virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData = boost::any()) override; + virtual void Close(const std::vector<NSCSS::CNode>& arSelectors) override; +}; + +template<> +class CTableCell<COOXMLInterpretator> : public CTag<COOXMLInterpretator> +{ +public: + CTableCell(COOXMLInterpretator* pInterpretator); + virtual bool Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData = boost::any()) override; virtual void Close(const std::vector<NSCSS::CNode>& arSelectors) override; }; } diff --git a/HtmlFile2/src/StringFinder.h b/HtmlFile2/src/StringFinder.h index a89720cd5f..16563288d8 100644 --- a/HtmlFile2/src/StringFinder.h +++ b/HtmlFile2/src/StringFinder.h @@ -60,23 +60,23 @@ namespace NSStringFinder return sValue; } - std::string FindProperty(const std::string& sString, const std::string& sProperty, const std::string& sDelimiter, const std::string& sEnding, const size_t& unStarting, size_t& unEndPosition) + inline std::string FindProperty(const std::string& sString, const std::string& sProperty, const std::string& sDelimiter, const std::string& sEnding, const size_t& unStarting, size_t& unEndPosition) { return FindPropertyTemplate<char>(sString, sProperty, sDelimiter, sEnding, unStarting, unEndPosition); } - std::wstring FindProperty(const std::wstring& wsString, const std::wstring& wsProperty, const std::wstring& wsDelimiter, const std::wstring& wsEnding, const size_t& unStarting, size_t& unEndPosition) + inline std::wstring FindProperty(const std::wstring& wsString, const std::wstring& wsProperty, const std::wstring& wsDelimiter, const std::wstring& wsEnding, const size_t& unStarting, size_t& unEndPosition) { return FindPropertyTemplate<wchar_t>(wsString, wsProperty, wsDelimiter, wsEnding, unStarting, unEndPosition); } - std::string FindProperty(const std::string& sString, const std::string& sProperty, const std::string& sDelimiter, const std::string& sEnding, const size_t& unStarting = 0) + inline std::string FindProperty(const std::string& sString, const std::string& sProperty, const std::string& sDelimiter, const std::string& sEnding, const size_t& unStarting = 0) { size_t unEndPosition = 0; return FindPropertyTemplate<char>(sString, sProperty, sDelimiter, sEnding, unStarting, unEndPosition); } - std::wstring FindProperty(const std::wstring& wsString, const std::wstring& wsProperty, const std::wstring& wsDelimiter, const std::wstring& wsEnding, const size_t& unStarting = 0) + inline std::wstring FindProperty(const std::wstring& wsString, const std::wstring& wsProperty, const std::wstring& wsDelimiter, const std::wstring& wsEnding, const size_t& unStarting = 0) { size_t unEndPosition = 0; return FindPropertyTemplate<wchar_t>(wsString, wsProperty, wsDelimiter, wsEnding, unStarting, unEndPosition); @@ -130,12 +130,12 @@ namespace NSStringFinder return oData; } - TFoundedData<char> FindProperty(const std::string& sString, const std::string& sProperty, const std::vector<std::string>& arDelimiters, const std::vector<std::string>& arEndings, const size_t& unStarting = 0) + inline TFoundedData<char> FindProperty(const std::string& sString, const std::string& sProperty, const std::vector<std::string>& arDelimiters, const std::vector<std::string>& arEndings, const size_t& unStarting = 0) { return FindPropertyTemplate<char>(sString, sProperty, arDelimiters, arEndings, unStarting); } - TFoundedData<wchar_t> FindProperty(const std::wstring& wsString, const std::wstring& wsProperty, const std::vector<std::wstring>& arDelimiters, const std::vector<std::wstring>& arEndings, const size_t& unStarting = 0) + inline TFoundedData<wchar_t> FindProperty(const std::wstring& wsString, const std::wstring& wsProperty, const std::vector<std::wstring>& arDelimiters, const std::vector<std::wstring>& arEndings, const size_t& unStarting = 0) { return FindPropertyTemplate<wchar_t>(wsString, wsProperty, arDelimiters, arEndings, unStarting); } @@ -154,12 +154,12 @@ namespace NSStringFinder return true; } - bool RemoveEmptyTag(std::string& sValue, const std::string& sTagName, size_t unStart = 0) + inline bool RemoveEmptyTag(std::string& sValue, const std::string& sTagName, size_t unStart = 0) { return RemoveEmptyTagTemplate<char>(sValue, sTagName, unStart); } - bool RemoveEmptyTag(std::wstring& sValue, const std::wstring& sTagName, size_t unStart = 0) + inline bool RemoveEmptyTag(std::wstring& sValue, const std::wstring& sTagName, size_t unStart = 0) { return RemoveEmptyTagTemplate<wchar_t>(sValue, sTagName, unStart); } @@ -223,7 +223,7 @@ namespace NSStringFinder return !boost::algorithm::ifind_first(sString, sValue).empty(); } - int ToInt(const std::wstring& oValue, int nMinValue = 0) + inline int ToInt(const std::wstring& oValue, int nMinValue = 0) { boost::wregex oRegex(LR"((-?\.\d+|-?\d+(\.\d+)?))"); @@ -237,7 +237,7 @@ namespace NSStringFinder return std::max(nMinValue, nValue); } - int ToDouble(const std::wstring& oValue, double dMinValue = 0.) + inline int ToDouble(const std::wstring& oValue, double dMinValue = 0.) { boost::wregex oRegex(LR"((-?\.\d+|-?\d+(\.\d+)?))");