#include #include #include #include #include #include #include #include "htmlfile2.h" #include "../Common/3dParty/html/htmltoxhtml.h" #include "../Common/3dParty/html/css/src/CCssCalculator.h" #include "../Common/3dParty/html/css/src/xhtml/CDocumentStyle.h" #include "../Common/FileDownloader/FileDownloader.h" #include "../DesktopEditor/common/Base64.h" #include "../DesktopEditor/common/SystemUtils.h" #include "../DesktopEditor/common/StringBuilder.h" #include "../DesktopEditor/common/File.h" #include "../DesktopEditor/common/Directory.h" #include "../DesktopEditor/common/Path.h" #include "../DesktopEditor/xml/include/xmlutils.h" #include "../DesktopEditor/raster/BgraFrame.h" #include "../DesktopEditor/graphics/pro/Fonts.h" #include "../DesktopEditor/graphics/pro/Graphics.h" #ifndef VALUE2STR #define VALUE_TO_STRING(x) #x #define VALUE2STR(x) VALUE_TO_STRING(x) #endif // Ячейка таблицы struct CTc { int i; int j; std::wstring sGridSpan = L"1"; CTc(int _i, int _j, std::wstring sColspan) { i = _i; j = _j; sGridSpan = sColspan; } }; // Настройки текста struct CTextSettings { bool bBdo; // Реверс текста bool bPre; // Сохранение форматирования (Сохранение пробелов, табуляций, переносов строк) int nLi; // Уровень списка CTextSettings(bool _bBdo, bool _bPre, int _nLi) { bBdo = _bBdo; bPre = _bPre; nLi = _nLi; } }; bool operator==(const CTc& c1, const CTc& c2) { return (c1.i == c2.i && c1.j == c2.j && c1.sGridSpan == c2.sGridSpan); } class CHtmlFile2_Private { public: XmlUtils::CXmlLiteReader m_oLightReader; // SAX Reader NSCSS::CCssCalculator m_oStylesCalculator; // Css калькулятор NSCSS::CDocumentStyle m_oXmlStyle; // Ooxml стиль std::wstring m_sTmp; // Temp папка для конфертации html в xhtml std::wstring m_sSrc; // Директория источника std::wstring m_sDst; // Директория назначения std::wstring m_sBase; // Полный базовый адрес std::map> m_sSrcs; // Имена обрабатываемых файлов (имя файла, имя перекрестной ссылки) private: int m_nImageId; // ID картинки int m_nFootnoteId; // ID сноски int m_nHyperlinkId; // ID ссылки int m_nStyleId; // ID стиля int m_nCrossId; // ID перекрестной ссылки int m_nNumberingId; // ID списка NSStringUtils::CStringBuilder m_oStylesXml; // styles.xml NSStringUtils::CStringBuilder m_oDocXmlRels; // document.xml.rels NSStringUtils::CStringBuilder m_oDocXml; // document.xml NSStringUtils::CStringBuilder m_oNoteXml; // footnotes.xml NSStringUtils::CStringBuilder m_oNumberXml; // numbering.xml public: CHtmlFile2_Private() { m_nImageId = 1; m_nFootnoteId = 1; m_nHyperlinkId = 1; m_nStyleId = 1; m_nCrossId = 1; m_nNumberingId = 1; } ~CHtmlFile2_Private() { m_oLightReader .Clear(); m_oStylesCalculator.Clear(); m_oXmlStyle .Clear(); m_sSrcs .clear(); m_oStylesXml .Clear(); m_oDocXmlRels .Clear(); m_oDocXml .Clear(); m_oNoteXml .Clear(); m_oNumberXml .Clear(); } // Проверяет наличие тэга html bool isHtml() { if(!m_oLightReader.ReadNextNode()) return false; if(m_oLightReader.GetName() != L"html") return false; return true; } void CreateDocxEmpty(CHtmlParams* oParams) { // Создаем пустые папки std::wstring strDirectory = m_sDst; // rels std::wstring pathRels = strDirectory + L"/_rels"; NSDirectory::CreateDirectory(pathRels); // docProps std::wstring pathDocProps = strDirectory + L"/docProps"; NSDirectory::CreateDirectory(pathDocProps); // word std::wstring pathWord = strDirectory + L"/word"; NSDirectory::CreateDirectory(pathWord); // documentRels std::wstring pathWordRels = pathWord + L"/_rels"; NSDirectory::CreateDirectory(pathWordRels); // media std::wstring pathMedia = pathWord + L"/media"; NSDirectory::CreateDirectory(pathMedia); // theme std::wstring pathTheme = pathWord + L"/theme"; NSDirectory::CreateDirectory(pathTheme); // theme1.xml std::wstring sTheme = L""; NSFile::CFileBinary oThemeWriter; if (oThemeWriter.CreateFileW(pathTheme + L"/theme1.xml")) { oThemeWriter.WriteStringUTF8(sTheme); oThemeWriter.CloseFile(); } // app.xml std::wstring sApplication = NSSystemUtils::GetEnvVariable(NSSystemUtils::gc_EnvApplicationName); if (sApplication.empty()) sApplication = NSSystemUtils::gc_EnvApplicationNameDefault; #if defined(INTVER) std::string sVersion = VALUE2STR(INTVER); #endif sApplication += L"/"; sApplication += UTF8_TO_U(sVersion); std::wstring sApp = L""; sApp += sApplication; sApp += L"0falsefalsefalsefalse"; NSFile::CFileBinary oAppWriter; if (oAppWriter.CreateFileW(pathDocProps + L"/app.xml")) { oAppWriter.WriteStringUTF8(sApp); oAppWriter.CloseFile(); } // .rels std::wstring sRels = L""; NSFile::CFileBinary oRelsWriter; if (oRelsWriter.CreateFileW(pathRels + L"/.rels")) { oRelsWriter.WriteStringUTF8(sRels); oRelsWriter.CloseFile(); } // [Content_Types].xml std::wstring sContent = L""; NSFile::CFileBinary oContentWriter; if (oContentWriter.CreateFileW(strDirectory + L"/[Content_Types].xml")) { oContentWriter.WriteStringUTF8(sContent); oContentWriter.CloseFile(); } // footnotes.xml.rels NSFile::CFileBinary oFootRelsWriter; if (oFootRelsWriter.CreateFileW(pathWordRels + L"/footnotes.xml.rels")) { oFootRelsWriter.WriteStringUTF8(L""); oFootRelsWriter.CloseFile(); } // fontTable.xml std::wstring sFontTable = L""; NSFile::CFileBinary oFontTableWriter; if (oFontTableWriter.CreateFileW(pathWord + L"/fontTable.xml")) { oFontTableWriter.WriteStringUTF8(sFontTable); oFontTableWriter.CloseFile(); } // settings.xml std::wstring sSettings = L""; NSFile::CFileBinary oSettingsWriter; if (oSettingsWriter.CreateFileW(pathWord + L"/settings.xml")) { oSettingsWriter.WriteStringUTF8(sSettings); oSettingsWriter.CloseFile(); } // webSettings.xml std::wstring sWebSettings = L""; NSFile::CFileBinary oWebWriter; if (oWebWriter.CreateFileW(pathWord + L"/webSettings.xml")) { oWebWriter.WriteStringUTF8(sWebSettings); oWebWriter.CloseFile(); } // numbering.xml NSStringUtils::CStringBuilder oNumbering; // Маркированный список m_oNumberXml += L""; // core.xml std::wstring sCore = L""; if(oParams != NULL) { if(!oParams->m_sBookTitle.empty()) { sCore += L""; sCore += oParams->m_sBookTitle; sCore += L""; } if(!oParams->m_sAuthors.empty()) { sCore += L""; sCore += oParams->m_sAuthors; sCore += L""; } if(!oParams->m_sGenres.empty()) { sCore += L""; sCore += oParams->m_sGenres; sCore += L""; } if(!oParams->m_sDate.empty()) { sCore += L""; sCore += oParams->m_sDate; sCore += L""; } if(!oParams->m_sDescription.empty()) { sCore += L""; sCore += oParams->m_sDescription; sCore += L""; } } sCore += L""; NSFile::CFileBinary oCoreWriter; if (oCoreWriter.CreateFileW(pathDocProps + L"/core.xml")) { oCoreWriter.WriteStringUTF8(sCore); oCoreWriter.CloseFile(); } // Начала файлов m_oDocXmlRels += L""; m_oDocXmlRels += L""; m_oDocXmlRels += L""; m_oDocXmlRels += L""; m_oDocXmlRels += L""; m_oDocXmlRels += L""; m_oDocXmlRels += L""; m_oDocXmlRels += L""; m_oDocXml += L""; m_oNoteXml += L""; m_oNoteXml += L""; m_oStylesXml += L""; // m_oStylesXml += L""; // m_oStylesXml += L""; // Стили по умолчанию // Нормальный стиль, от которого базируются m_oStylesXml += L""; // Маркированный список m_oStylesXml += L""; // Заголовки // m_oStylesXml += L""; // Текст: p, div // m_oStylesXml += L""; // m_oStylesXml += L""; // Ссылки m_oStylesXml += L""; // Таблицы m_oStylesXml += L""; // Сноски m_oStylesXml.WriteString(L""); } bool readSrc(const std::wstring& sFileName) { // Читаем html if(!isHtml()) return false; if(m_oLightReader.IsEmptyNode()) return true; int nDeath = m_oLightReader.GetDepth(); while(m_oLightReader.ReadNextSiblingNode(nDeath)) { std::wstring sName = m_oLightReader.GetName(); if(sName == L"head") readHead(); else if(sName == L"body") readBody(sFileName); } return true; } void write() { m_oDocXmlRels.WriteString(L""); NSFile::CFileBinary oRelsWriter; if (oRelsWriter.CreateFileW(m_sDst + L"/word/_rels/document.xml.rels")) { oRelsWriter.WriteStringUTF8(m_oDocXmlRels.GetData()); oRelsWriter.CloseFile(); } m_oDocXml.WriteString(L""); NSFile::CFileBinary oDocumentWriter; if (oDocumentWriter.CreateFileW(m_sDst + L"/word/document.xml")) { oDocumentWriter.WriteStringUTF8(m_oDocXml.GetData()); oDocumentWriter.CloseFile(); } m_oNoteXml.WriteString(L""); NSFile::CFileBinary oFootnotesWriter; if (oFootnotesWriter.CreateFileW(m_sDst + L"/word/footnotes.xml")) { oFootnotesWriter.WriteStringUTF8(m_oNoteXml.GetData()); oFootnotesWriter.CloseFile(); } // styles.xml m_oStylesXml.WriteString(L""); NSFile::CFileBinary oStylesWriter; if (oStylesWriter.CreateFileW(m_sDst + L"/word/styles.xml")) { oStylesWriter.WriteStringUTF8(m_oStylesXml.GetData()); oStylesWriter.CloseFile(); } // numbering.xml // Маркированный список m_oNumberXml.WriteString(L""); // Нумерованный список for(int i = 1; i < m_nNumberingId; i++) { m_oNumberXml.WriteString(L""); } m_oNumberXml.WriteString(L""); NSFile::CFileBinary oNumberingWriter; if (oNumberingWriter.CreateFileW(m_sDst + L"/word/numbering.xml")) { oNumberingWriter.WriteStringUTF8(m_oNumberXml.GetData()); oNumberingWriter.CloseFile(); } } void htmlXhtml(const std::wstring& sSrc) { NSFile::CFileBinary oXhtmlWriter; if (oXhtmlWriter.CreateFileW(m_sTmp + L"/res.xhtml")) { // htmlToXhtml возвращает текст файла в кодировке UTF-8 oXhtmlWriter.WriteStringUTF8(htmlToXhtml(sSrc)); oXhtmlWriter.CloseFile(); } } void readStyle() { if(m_oLightReader.IsEmptyNode()) return; int nDeath = m_oLightReader.GetDepth(); while(m_oLightReader.ReadNextSiblingNode(nDeath)) { std::wstring sName = m_oLightReader.GetName(); // Стиль по ссылке if(sName == L"link") { while(m_oLightReader.MoveToNextAttribute()) { if(m_oLightReader.GetName() == L"href") { std::wstring sRef = m_oLightReader.GetText(); // Если это css файл, то поведение аналогично тэгу style // Кроме функции получения стилей std::wstring sType = NSFile::GetFileExtention(sRef); if(sType == L"css") m_oStylesCalculator.AddStylesFromFile(m_sSrc + L"/" + sRef); } } m_oLightReader.MoveToElement(); } // тэг style содержит стили для styles.xml else if(sName == L"style") { std::string sText = m_oLightReader.GetText2A(); m_oStylesCalculator.AddStyles(sText); } readStyle(); } } private: std::vector GetSubClass(const std::vector& sSelectors, std::wstring& sNote) { std::vector sSubClass(sSelectors); NSCSS::CNode oNode; oNode.m_sName = m_oLightReader.GetName(); if(oNode.m_sName == L"#text") return sSubClass; // Стиль по атрибуту while(m_oLightReader.MoveToNextAttribute()) { std::wstring sAName = m_oLightReader.GetName(); if(sAName == L"class") oNode.m_sClass = m_oLightReader.GetText(); else if(sAName == L"id") oNode.m_sId = m_oLightReader.GetText(); else if(sAName == L"style") oNode.m_sStyle = m_oLightReader.GetText(); else if(sAName == L"title") sNote = m_oLightReader.GetText(); } m_oLightReader.MoveToElement(); sSubClass.push_back(oNode); return sSubClass; } std::wstring GetStyle(std::vector sSelectors, bool bP) { NSCSS::CNode oChild = sSelectors.back(); sSelectors.pop_back(); NSCSS::CCompiledStyle oStyle = m_oStylesCalculator.GetCompiledStyle(oChild, sSelectors); if(bP) m_oXmlStyle.WritePStyle(oStyle); else m_oXmlStyle.WriteRStyle(oStyle); std::wstring sRes = m_oXmlStyle.GetId(); std::wstring sStyle = m_oXmlStyle.GetStyle(); m_oStylesXml.WriteString(sStyle); return sRes; } void readHead() { if(m_oLightReader.IsEmptyNode()) return; int nDeath = m_oLightReader.GetDepth(); while(m_oLightReader.ReadNextSiblingNode(nDeath)) { std::wstring sName = m_oLightReader.GetName(); // Базовый адрес if(sName == L"base") { while(m_oLightReader.MoveToNextAttribute()) if(m_oLightReader.GetName() == L"href") m_sBase = m_oLightReader.GetText(); m_oLightReader.MoveToElement(); } } } void readBody(const std::wstring& sFileName) { bool bWasP = true; bool bWasPPr = false; std::vector sSelectors; std::wstring sEmptyNote; sSelectors = GetSubClass(sSelectors, sEmptyNote); CTextSettings oTS { false, false, -1 }; auto it = m_sSrcs.find(sFileName); if(it != m_sSrcs.end()) { for(const std::wstring& sId : it->second) { std::wstring sCrossId = std::to_wstring(m_nCrossId++); m_oDocXml.WriteString(L""); } } m_oDocXml.WriteString(L""); readStream(&m_oDocXml, sSelectors, L"", oTS, bWasP, bWasPPr); m_oDocXml.WriteString(L""); } void readStream(NSStringUtils::CStringBuilder* oXml, const std::vector& sSelectors, std::wstring sStyle, const CTextSettings& oTS, bool& bWasP, bool& bWasPPr) { if(m_oLightReader.IsEmptyNode()) return; int nDeath = m_oLightReader.GetDepth(); while(m_oLightReader.ReadNextSiblingNode2(nDeath)) { std::wstring sNote; std::vector sSubClass = GetSubClass(sSelectors, sNote); std::wstring sName = m_oLightReader.GetName(); if(sName == L"#text") { std::wstring sPStyle = L""; if(!bWasPPr) { oXml->WriteString(L"WriteString(sPStyle); oXml->WriteString(L"\"/>"); bWasPPr = true; } oXml->WriteString(L"WriteString(sRStyle); oXml->WriteString(L"\"/>"); oXml->WriteString(sStyle); oXml->WriteString(L""); std::wstring sText = m_oLightReader.GetText(); auto end = sText.end(); if(oTS.bBdo) std::reverse(sText.begin(), sText.end()); if(oTS.bPre) { size_t nAfter = sText.find(L'\n'); while(nAfter != std::wstring::npos) { std::wstring sSubText = sText.substr(0, nAfter); sText.erase(0, sSubText.length() + 1); oXml->WriteEncodeXmlString(sSubText); oXml->WriteString(L"WriteString(sPStyle); oXml->WriteString(L"\"/>WriteString(sRStyle); oXml->WriteString(L"\"/>"); oXml->WriteString(sStyle); oXml->WriteString(L""); nAfter = sText.find(L'\n'); } end = sText.end(); } else end = std::unique(sText.begin(), sText.end(), [] (wchar_t l, wchar_t r) { return std::iswspace(l) && std::iswspace(r); }); sText = std::wstring(sText.begin(), end); oXml->WriteEncodeXmlString(sText); oXml->WriteString(L""); bWasP = false; continue; } // Ссылки // Области ссылки if(sName == L"a" || sName == L"area") readA(oXml, sSubClass, sStyle, oTS, bWasP, bWasPPr); // Полужирный текст // Акцентированный текст else if(sName == L"b" || sName == L"strong") readStream(oXml, sSubClass, sStyle + L"", oTS, bWasP, bWasPPr); // Направление текста else if(sName == L"bdo") { std::wstring sDir = L""; while(m_oLightReader.MoveToNextAttribute()) if(m_oLightReader.GetName() == L"dir") sDir = m_oLightReader.GetText(); m_oLightReader.MoveToElement(); CTextSettings oTSBdo { sDir == L"rtl", oTS.bPre, oTS.nLi }; readStream(oXml, sSubClass, sStyle, oTSBdo, bWasP, bWasPPr); } // Отмена направления текста else if(sName == L"bdi") { CTextSettings oTSBdo { false, oTS.bPre, oTS.nLi }; readStream(oXml, sSubClass, sStyle, oTSBdo, bWasP, bWasPPr); } // Увеличивает размер шрифта else if(sName == L"big") readStream(oXml, sSubClass, sStyle + L"", oTS, bWasP, bWasPPr); // Перенос строки else if(sName == L"br") oXml->WriteString(L""); // Цитата, обычно выделяется курсивом // Новый термин, обычно выделяется курсивом // Акцентированный текст // Курсивный текст // Переменная, обычно выделяется курсивом else if(sName == L"cite" || sName == L"dfn" || sName == L"em" || sName == L"i" || sName == L"var") readStream(oXml, sSubClass, sStyle + L"", oTS, bWasP, bWasPPr); // Код // Моноширинный шрифт, например, Consolas // Результат скрипта else if(sName == L"code" || sName == L"kbd" || sName == L"samp") readStream(oXml, sSubClass, sStyle + L"", oTS, bWasP, bWasPPr); // Зачеркнутый текст else if(sName == L"del" || sName == L"s") readStream(oXml, sSubClass, sStyle + L"", oTS, bWasP, bWasPPr); // Картинки else if(sName == L"img") { readImage(oXml, sStyle); bWasP = false; bWasPPr = false; } // Подчеркнутый else if(sName == L"ins") readStream(oXml, sSubClass, sStyle + L"", oTS, bWasP, bWasPPr); // Выделенный текст, обычно выделяется желтым else if(sName == L"mark") readStream(oXml, sSubClass, sStyle + L"", oTS, bWasP, bWasPPr); // Цитата, выделенная кавычками, обычно выделяется курсивом else if(sName == L"q") { oXml->WriteString(L"WriteString(sStyle); oXml->WriteString(L"\"/>"); oXml->WriteString(sStyle); oXml->WriteString(L"""); readStream(oXml, sSubClass, sStyle + L"", oTS, bWasP, bWasPPr); oXml->WriteString(L"WriteString(sStyle); oXml->WriteString(L"\"/>"); oXml->WriteString(sStyle); oXml->WriteString(L"""); bWasP = false; } // Текст верхнего регистра else if(sName == L"rt" || sName == L"sup") readStream(oXml, sSubClass, sStyle + L"", oTS, bWasP, bWasPPr); // Уменьшает размер шрифта else if(sName == L"small") readStream(oXml, sSubClass, sStyle + L"", oTS, bWasP, bWasPPr); // Текст нижнего регистра else if(sName == L"sub") readStream(oXml, sSubClass, sStyle + L"", oTS, bWasP, bWasPPr); // Векторная картинка else if(sName == L"svg") { readSVG(oXml); bWasP = false; } // Игнорируются тэги выполняющие скрипт else if(sName == L"template" || sName == L"canvas" || sName == L"video" || sName == L"math" || sName == L"rp" || sName == L"command" || sName == L"iframe" || sName == L"embed" || sName == L"wbr" || sName == L"audio" || sName == L"bgsound" || sName == L"applet" || sName == L"blink" || sName == L"keygen"|| sName == L"script") continue; // Без нового абзаца else if(sName == L"datalist" || sName == L"button" || sName == L"label" || sName == L"data" || sName == L"object" || sName == L"noscript" || sName == L"output" || sName == L"input" || sName == L"time" || sName == L"ruby" || sName == L"progress" || sName == L"hgroup" || sName == L"meter" || sName == L"span" || sName == L"font" || sName == L"basefont" || sName == L"center" || sName == L"acronym"|| sName == L"abbr") readStream(oXml, sSubClass, sStyle, oTS, bWasP, bWasPPr); // С нового абзаца else { if(!bWasP) { auto it = std::find_if(sSubClass.begin(), sSubClass.end(), [](const NSCSS::CNode& item){ return item.m_sName == L"a"; }); if(it != sSubClass.end()) oXml->WriteString(L""); oXml->WriteString(L""); if(it != sSubClass.end()) oXml->WriteString(L""); bWasP = true; bWasPPr = false; } // Адрес if(sName == L"address") readStream(oXml, sSubClass, sStyle + L"", oTS, bWasP, bWasPPr); // С нового абзаца else if(sName == L"article" || sName == L"header" || sName == L"div" || sName == L"blockquote" || sName == L"main" || sName == L"summary" || sName == L"footer" || sName == L"nav" || sName == L"figcaption" || sName == L"form" || sName == L"details" || sName == L"option" || sName == L"dd" || sName == L"fieldset" || sName == L"p" || sName == L"section" || sName == L"figure" || sName == L"dl" || sName == L"legend" || sName == L"map" || sName == L"dt" || sName == L"aside" || sName == L"h1" || sName == L"h2" || sName == L"h3" || sName == L"h4" || sName == L"h5" || sName == L"h6") readStream(oXml, sSubClass, sStyle, oTS, bWasP, bWasPPr); // Горизонтальная линия else if(sName == L"hr") { oXml->WriteString(L""); bWasP = false; bWasPPr = true; } // Меню // Маркированный список else if(sName == L"menu" || sName == L"ul" || sName == L"select") readLi(oXml, sSubClass, sStyle, oTS, bWasP, bWasPPr, true); // Нумерованный список else if(sName == L"ol") readLi(oXml, sSubClass, sStyle, oTS, bWasP, bWasPPr, false); // Предварительно форматированный текст else if(sName == L"pre") { CTextSettings oTSPre { oTS.bBdo, true, oTS.nLi }; readStream(oXml, sSubClass, sStyle + L"", oTSPre, bWasP, bWasPPr); } // Таблицы else if(sName == L"table") { auto it = std::find_if(sSubClass.begin(), sSubClass.end(), [](const NSCSS::CNode& item){ return item.m_sName == L"a"; }); if(it != sSubClass.end()) oXml->WriteString(L""); oXml->WriteString(L""); bWasP = false; bWasPPr = false; readTable(oXml, sSubClass, sStyle, oTS, bWasP, bWasPPr); oXml->WriteString(L""); if(it != sSubClass.end()) oXml->WriteString(L""); bWasP = true; bWasPPr = false; } // Текст с границами else if(sName == L"textarea") { oXml->WriteString(L""); bWasPPr = true; readStream(oXml, sSubClass, sStyle, oTS, bWasP, bWasPPr); } // Неизвестный тэг. Выделять ли его абзацем? else readStream(oXml, sSubClass, sStyle, oTS, bWasP, bWasPPr); readNote(oXml, sNote); sNote = L""; if(!bWasP) { auto it = std::find_if(sSubClass.begin(), sSubClass.end(), [](const NSCSS::CNode& item){ return item.m_sName == L"a"; }); if(it != sSubClass.end()) oXml->WriteString(L""); oXml->WriteString(L""); if(it != sSubClass.end()) oXml->WriteString(L""); bWasP = true; bWasPPr = false; } } readNote(oXml, sNote); } } int readTr (NSStringUtils::CStringBuilder* oXml, const std::vector& sSelectors, std::wstring sStyle, const CTextSettings& oTS, bool& bWasP, bool& bWasPPr) { std::vector mTable; int nGridCol = 0; int nDeath = m_oLightReader.GetDepth(); int i = 1; // Строка while(m_oLightReader.ReadNextSiblingNode(nDeath)) { // tr - строки в таблице if(m_oLightReader.GetName() != L"tr") continue; int nTrDeath = m_oLightReader.GetDepth(); if(m_oLightReader.IsEmptyNode() || !m_oLightReader.ReadNextSiblingNode(nTrDeath)) continue; int j = 1; // Столбец oXml->WriteString(L""); do { int nColspan = 1; int nRowspan = 1; while(m_oLightReader.MoveToNextAttribute()) { if(m_oLightReader.GetName() == L"colspan") nColspan = stoi(m_oLightReader.GetText()); else if(m_oLightReader.GetName() == L"rowspan") nRowspan = stoi(m_oLightReader.GetText()); } m_oLightReader.MoveToElement(); // Вставляем ячейки до auto it1 = std::find_if(mTable.begin(), mTable.end(), [i, j](const CTc& item){ return item.i == i && item.j == j; }); auto it2 = std::find_if(mTable.begin(), mTable.end(), [j] (const CTc& item){ return item.i == 0 && item.j == j; }); while(it1 != mTable.end() || it2 != mTable.end()) { oXml->WriteString(L"sGridSpan : it2->sGridSpan); oXml->WriteString(sCol); oXml->WriteString(L"\"/>"); j += stoi(sCol); it1 = std::find_if(mTable.begin(), mTable.end(), [i, j](const CTc& item){ return item.i == i && item.j == j; }); it2 = std::find_if(mTable.begin(), mTable.end(), [j] (const CTc& item){ return item.i == 0 && item.j == j; }); } oXml->WriteString(L""); if(nRowspan != 1) { oXml->WriteString(L""); std::wstring sColspan = std::to_wstring(nColspan); if(nRowspan == 0) mTable.push_back({0, j, sColspan}); else for(int k = i + 1; k < i + nRowspan; k++) mTable.push_back({k, j, sColspan}); } if(nColspan != 1) { oXml->WriteString(L"WriteString(std::to_wstring(nColspan)); oXml->WriteString(L"\"/>"); j += nColspan - 1; } oXml->WriteString(L""); std::wstring sEmptyNote; std::vector sSubClass = GetSubClass(sSelectors, sEmptyNote); // Читаем th. Ячейка заголовка таблицы. Выравнивание посередине. Выделяется полужирным if(m_oLightReader.GetName() == L"th") { oXml->WriteString(L""); bWasP = false; bWasPPr = true; readStream(oXml, sSubClass, sStyle + L"", oTS, bWasP, bWasPPr); } // Читаем td. Ячейка таблицы. Выравнивание вправо else if(m_oLightReader.GetName() == L"td") { oXml->WriteString(L""); bWasP = false; bWasPPr = true; readStream(oXml, sSubClass, sStyle, oTS, bWasP, bWasPPr); } oXml->WriteString(L""); j++; // Вставляем ячейки после it1 = std::find_if(mTable.begin(), mTable.end(), [i, j](const CTc& item){ return item.i == i && item.j == j; }); it2 = std::find_if(mTable.begin(), mTable.end(), [j] (const CTc& item){ return item.i == 0 && item.j == j; }); while(it1 != mTable.end() || it2 != mTable.end()) { oXml->WriteString(L"sGridSpan : it2->sGridSpan); oXml->WriteString(sCol); oXml->WriteString(L"\"/>"); j += stoi(sCol); it1 = std::find_if(mTable.begin(), mTable.end(), [i, j](const CTc& item){ return item.i == i && item.j == j; }); it2 = std::find_if(mTable.begin(), mTable.end(), [j] (const CTc& item){ return item.i == 0 && item.j == j; }); } } while(m_oLightReader.ReadNextSiblingNode(nTrDeath)); oXml->WriteString(L""); if(--j > nGridCol) nGridCol = j; i++; } return nGridCol; } void readTable (NSStringUtils::CStringBuilder* oXml, const std::vector& sSelectors, std::wstring sStyle, const CTextSettings& oTS, bool& bWasP, bool& bWasPPr) { if(m_oLightReader.IsEmptyNode()) return; NSStringUtils::CStringBuilder oCaption; NSStringUtils::CStringBuilder oHead; NSStringUtils::CStringBuilder oBody; NSStringUtils::CStringBuilder oFoot; int nGridCol = 0; int nDeath = m_oLightReader.GetDepth(); while(m_oLightReader.ReadNextSiblingNode(nDeath)) { int n = 0; std::wstring sName = m_oLightReader.GetName(); if(sName == L"caption") { oCaption.WriteString(L""); bWasP = false; bWasPPr = true; readStream(&oCaption, sSelectors, sStyle, oTS, bWasP, bWasPPr); oCaption.WriteString(L""); bWasP = false; bWasPPr = false; } if(sName == L"thead") n = readTr(&oHead, sSelectors, sStyle, oTS, bWasP, bWasPPr); else if(sName == L"tbody") n = readTr(&oBody, sSelectors, sStyle, oTS, bWasP, bWasPPr); else if(sName == L"tfoot") n = readTr(&oFoot, sSelectors, sStyle, oTS, bWasP, bWasPPr); if(n > nGridCol) nGridCol = n; } // Заголовок таблицы oXml->WriteString(oCaption.GetData()); // Начало таблицы oXml->WriteString(L""); // "; // Размеры таблицы /* std::wstring sGridCol = L""; if(nGridCol != 0) sGridCol = std::to_wstring((int)(9570.0 / (double)nGridCol)); *oXml += L""; for(int i = 0; i < nGridCol; i++) { *oXml += L""; } *oXml += L""; */ // Конец таблицы oXml->WriteString(oHead.GetData()); oXml->WriteString(oBody.GetData()); oXml->WriteString(oFoot.GetData()); oXml->WriteString(L""); // Пустая строка после таблицы, чтобы следующий текст не приклеивался oXml->WriteString(L""); } void readLi (NSStringUtils::CStringBuilder* oXml, const std::vector& sSelectors, std::wstring sStyle, const CTextSettings& oTS, bool& bWasP, bool& bWasPPr, bool bType) { if(m_oLightReader.IsEmptyNode()) return; // Нумерованный список if(!bType) { m_oNumberXml.WriteString(L""); } int nDeath = m_oLightReader.GetDepth(); while(m_oLightReader.ReadNextSiblingNode(nDeath)) { std::wstring sName = m_oLightReader.GetName(); if(sName == L"optgroup") { while(m_oLightReader.MoveToNextAttribute()) { if(m_oLightReader.GetName() == L"label") { if(!bWasP) { auto it = std::find_if(sSelectors.begin(), sSelectors.end(), [](const NSCSS::CNode& item){ return item.m_sName == L"a"; }); if(it != sSelectors.end()) oXml->WriteString(L""); oXml->WriteString(L""); if(it != sSelectors.end()) oXml->WriteString(L""); bWasP = true; bWasPPr = false; } std::wstring sPStyle = L""; if(!bWasPPr) { oXml->WriteString(L"WriteString(sPStyle); oXml->WriteString(L"\"/>"); bWasPPr = true; } oXml->WriteString(L"WriteString(sRStyle); oXml->WriteString(L"\"/>"); oXml->WriteString(sStyle); oXml->WriteString(L""); oXml->WriteEncodeXmlString(m_oLightReader.GetText()); oXml->WriteString(L""); auto it = std::find_if(sSelectors.begin(), sSelectors.end(), [](const NSCSS::CNode& item){ return item.m_sName == L"a"; }); if(it != sSelectors.end()) oXml->WriteString(L""); oXml->WriteString(L""); if(it != sSelectors.end()) oXml->WriteString(L""); bWasP = true; bWasPPr = false; } } m_oLightReader.MoveToElement(); readLi(oXml, sSelectors, sStyle, oTS, bWasP, bWasPPr, true); continue; } if(sName != L"li" && sName != L"option") { readStream(oXml, sSelectors, sStyle, oTS, bWasP, bWasPPr); continue; } if(!bWasP) { auto it = std::find_if(sSelectors.begin(), sSelectors.end(), [](const NSCSS::CNode& item){ return item.m_sName == L"a"; }); if(it != sSelectors.end()) oXml->WriteString(L""); oXml->WriteString(L""); if(it != sSelectors.end()) oXml->WriteString(L""); bWasP = true; bWasPPr = false; } CTextSettings oTSLi { oTS.bBdo, oTS.bPre, oTS.nLi + 1 }; oXml->WriteString(L"WriteString(std::to_wstring(oTSLi.nLi)); oXml->WriteString(L"\"/>WriteString(bType ? L"1" : std::to_wstring(m_nNumberingId)); oXml->WriteString(L"\"/>"); bWasP = true; bWasPPr = true; readStream(oXml, sSelectors, sStyle, oTSLi, bWasP, bWasPPr); if(!bWasP) { auto it = std::find_if(sSelectors.begin(), sSelectors.end(), [](const NSCSS::CNode& item){ return item.m_sName == L"a"; }); if(it != sSelectors.end()) oXml->WriteString(L""); oXml->WriteString(L""); if(it != sSelectors.end()) oXml->WriteString(L""); bWasP = true; bWasPPr = false; } } } void readA (NSStringUtils::CStringBuilder* oXml, const std::vector& sSelectors, std::wstring sStyle, const CTextSettings& oTS, bool& bWasP, bool& bWasPPr) { std::wstring sRef; std::wstring sTitle; std::wstring sAlt; bool bCross = false; while(m_oLightReader.MoveToNextAttribute()) { std::wstring sName = m_oLightReader.GetName(); if(sName == L"href") { sRef = m_oLightReader.GetText(); if(sRef.length() > 1) { if(sRef.front() == L'#') { bCross = true; sRef = sRef.substr(1); continue; } } size_t nSrc = sRef.rfind(L"/"); if(nSrc == std::wstring::npos) nSrc = 0; else nSrc++; size_t nLen = sRef.rfind(L"html"); if(nLen == std::wstring::npos) continue; else nLen += 4; std::wstring sFileName = sRef.substr(nSrc, nLen - nSrc); auto it = m_sSrcs.find(sFileName); if(it != m_sSrcs.end()) { bCross = true; sRef = L"cHyp" + std::to_wstring(m_nHyperlinkId++); it->second.push_back(sRef); } } else if(sName == L"title") sTitle = m_oLightReader.GetText(); else if(sName == L"name") { std::wstring sCrossId = std::to_wstring(m_nCrossId++); oXml->WriteString(L"WriteString(sCrossId); oXml->WriteString(L"\" w:name=\""); oXml->WriteString(m_oLightReader.GetText()); oXml->WriteString(L"\"/>WriteString(sCrossId); oXml->WriteString(L"\"/>"); } else if(sName == L"alt") sAlt = m_oLightReader.GetText(); } m_oLightReader.MoveToElement(); if(sTitle.empty()) sTitle = sRef; // Перекрестная ссылка внутри файла if(bCross) { oXml->WriteString(L"WriteString(sRef); } // Внешняя ссылка else { // Пишем рельсы m_oDocXmlRels.WriteString(L""); // Пишем в document.xml oXml->WriteString(L"WriteEncodeXmlString(sTitle); oXml->WriteString(L"\" r:id=\"rHyp"); oXml->WriteString(std::to_wstring(m_nHyperlinkId++)); } oXml->WriteString(L"\">"); bWasP = true; if(m_oLightReader.IsEmptyNode()) { oXml->WriteString(L"WriteString(sRStyle); oXml->WriteString(L"\"/>"); oXml->WriteString(sStyle + L""); oXml->WriteString(L""); oXml->WriteString(sAlt); oXml->WriteString(L""); } else readStream(oXml, sSelectors, sStyle + L"", oTS, bWasP, bWasPPr); oXml->WriteString(L""); bWasP = false; } void readNote(NSStringUtils::CStringBuilder* oXml, const std::wstring& sNote) { if(sNote.empty()) return; oXml->WriteString(L"WriteString(std::to_wstring(m_nFootnoteId)); oXml->WriteString(L"\"/>"); m_oNoteXml.WriteString(L""); m_oNoteXml.WriteEncodeXmlString(sNote); m_oNoteXml.WriteString(L""); } void readImage (NSStringUtils::CStringBuilder* oXml, const std::wstring& sStyle) { std::wstring sAlt = L""; bool bRes = false; while(m_oLightReader.MoveToNextAttribute()) { if(m_oLightReader.GetName() == L"alt") { sAlt = m_oLightReader.GetText(); continue; } else if(m_oLightReader.GetName() != L"src") continue; std::wstring sSrcM = m_oLightReader.GetText(); std::wstring sImageName = L""; std::wstring sImageId = std::to_wstring(m_nImageId); size_t nLen = (sSrcM.length() > 4 ? 4 : 0); // Картинка Base64 if(sSrcM.substr(0, nLen) == L"data") { size_t nBase = sSrcM.find(L"/", nLen); if(nBase == std::wstring::npos) continue; nBase++; size_t nEndBase = sSrcM.find(L";", nBase); if(nEndBase == std::wstring::npos) continue; std::wstring sType = sSrcM.substr(nBase, nEndBase - nBase); sImageName = sImageId + L"." + sType; NSFile::CFileBinary oImageWriter; if(oImageWriter.CreateFileW(m_sDst + L"/word/media/i" + sImageName)) { nBase = sSrcM.find(L"base64", nLen); if(nBase == std::wstring::npos) continue; std::string sBase64 = m_oLightReader.GetTextA().substr(nBase + 7); int nSrcLen = (int)sBase64.length(); int nDecodeLen = NSBase64::Base64DecodeGetRequiredLength(nSrcLen); if(nDecodeLen == 0) continue; BYTE* pImageData = new BYTE[nDecodeLen]; if (TRUE == NSBase64::Base64Decode(sBase64.c_str(), nSrcLen, pImageData, &nDecodeLen)) { oImageWriter.WriteFile(pImageData, (DWORD)nDecodeLen); bRes = true; } RELEASEARRAYOBJECTS(pImageData); oImageWriter.CloseFile(); } } // Картинка в сети else if(sSrcM.substr(0, nLen) == L"http" || !m_sBase.empty()) { std::wstring sExtention = NSFile::GetFileExtention(sSrcM); if(sExtention != L"bmp" && sExtention != L"svg" && sExtention != L"jfif" && sExtention != L"wmf" && sExtention != L"gif" && sExtention != L"jpe" && sExtention != L"png" && sExtention != L"jpeg" && sExtention != L"jpg" ) continue; sImageName = NSFile::GetFileName(sSrcM); sImageName.erase(std::remove_if(sImageName.begin(), sImageName.end(), [] (wchar_t ch) { return std::iswspace(ch); }), sImageName.end()); CFileDownloader oDownloadImg(m_sBase + sSrcM, false); oDownloadImg.SetFilePath(m_sDst + L"/word/media/i" + sImageName); bRes = oDownloadImg.DownloadSync(); } // Картинка по относительному пути else { sImageName = NSFile::GetFileName(sSrcM); sImageName.erase(std::remove_if(sImageName.begin(), sImageName.end(), [] (wchar_t ch) { return std::iswspace(ch); }), sImageName.end()); std::wstring sExtention = NSFile::GetFileExtention(sSrcM); if(sExtention != L"bmp" && sExtention != L"svg" && sExtention != L"jfif" && sExtention != L"wmf" && sExtention != L"gif" && sExtention != L"jpe" && sExtention != L"png" && sExtention != L"jpeg" && sExtention != L"jpg" ) continue; bRes = NSFile::CFileBinary::Copy(m_sSrc + L"/" + sSrcM, m_sDst + L"/word/media/i" + sImageName); if(!bRes) bRes = NSFile::CFileBinary::Copy(m_sSrc + L"/" + NSFile::GetFileName(sSrcM), m_sDst + L"/word/media/i" + sImageName); } if(bRes) ImageRels(oXml, sImageId, L"i" + sImageName); } m_oLightReader.MoveToElement(); if(!bRes) { oXml->WriteString(L""); oXml->WriteString(sStyle); oXml->WriteString(L""); oXml->WriteEncodeXmlString(sAlt); oXml->WriteString(L""); } } void ImageRels (NSStringUtils::CStringBuilder* oXml, const std::wstring& sImageId, const std::wstring& sImageName) { m_nImageId++; // Прописать рельсы m_oDocXmlRels.WriteString(L""); // Получаем размеры картинки CBgraFrame oBgraFrame; oBgraFrame.OpenFile(m_sDst + L"/word/media/" + sImageName); int nHy = oBgraFrame.get_Height(); int nWx = oBgraFrame.get_Width(); if(nWx > nHy) { int nW = nWx * 9525; nW = (nW > 7000000 ? 7000000 : nW); nHy = (int)((double)nHy * (double)nW / (double)nWx); nWx = nW; } else { int nH = nHy * 9525; nH = (nH > 9000000 ? 9000000 : nH); int nW = (int)((double)nWx * (double)nH / (double)nHy); if(nW > 7000000) { nW = 7000000; nHy = (int)((double)nHy * (double)nW / (double)nWx); } else nHy = nH; nWx = nW; } // Пишем в document.xml oXml->WriteString(L"WriteString(std::to_wstring(nWx)); oXml->WriteString(L"\" cy=\""); oXml->WriteString(std::to_wstring(nHy)); oXml->WriteString(L"\"/>WriteString(sImageId); oXml->WriteString(L"\" name=\"\"/>WriteString(sImageId); oXml->WriteString(L"\" name=\"\"/>WriteString(sImageId); oXml->WriteString(L"\"/>WriteString(std::to_wstring(nWx)); oXml->WriteString(L"\" cy=\""); oXml->WriteString(std::to_wstring(nHy)); oXml->WriteString(L"\"/>"); } void readSVG (NSStringUtils::CStringBuilder* oXml) { // Сохранить как .svg картинку NSStringUtils::CStringBuilder oSVG; bool bNeedXmlns = true; oSVG.WriteString(L""); std::wstring sSVG = m_oLightReader.GetInnerXml(); size_t nRef = sSVG.find(L"image"); while(nRef != std::wstring::npos) { size_t nHRef = sSVG.find(L"href", nRef); if(nHRef == std::wstring::npos) break; nHRef += 6; if(sSVG.compare(nHRef, 4, L"http") == 0) { nRef = sSVG.find(L"image", nRef + 5); continue; } size_t nHRefLen = sSVG.find(L"\"", nHRef); if(nHRefLen == std::wstring::npos) break; std::wstring sImageName = sSVG.substr(nHRef, nHRefLen - nHRef); bool bRes = NSFile::CFileBinary::Copy(m_sSrc + L"/" + sImageName, m_sDst + L"/word/media/" + NSFile::GetFileName(sImageName)); if(!bRes) bRes = NSFile::CFileBinary::Copy(m_sSrc + L"/" + NSFile::GetFileName(sImageName), m_sDst + L"/word/media/" + NSFile::GetFileName(sImageName)); if(bRes) sSVG.replace(nHRef, nHRefLen - nHRef, NSFile::GetFileName(sImageName)); nRef = sSVG.find(L"image", nRef + 5); } oSVG.WriteString(sSVG); oSVG.WriteString(L""); std::wstring sImageId = std::to_wstring(m_nImageId); NSFile::CFileBinary oSVGWriter; std::wstring sImageFile = m_sDst + L"/word/media/" + sImageId + L".svg"; if (oSVGWriter.CreateFileW(sImageFile)) { oSVGWriter.WriteStringUTF8(oSVG.GetData()); oSVGWriter.CloseFile(); } // Конвертация из svg в png NSFonts::IApplicationFonts* pFonts = NSFonts::NSApplication::Create(); MetaFile::IMetaFile* pMetafile = MetaFile::Create(pFonts); bool bLoad = pMetafile->LoadFromFile(sImageFile.data()); if(bLoad) { std::wstring sPngFile = m_sDst + L"/word/media/" + sImageId + L".png"; pMetafile->ConvertToRaster(sPngFile.data(), 4, 1000); } pMetafile->Release(); pFonts->Release(); ImageRels(oXml, sImageId, sImageId + L".png"); } }; CHtmlFile2::CHtmlFile2() { m_internal = new CHtmlFile2_Private(); } CHtmlFile2::~CHtmlFile2() { RELEASEOBJECT(m_internal); } bool CHtmlFile2::IsHtmlFile(const std::wstring& sFile) { m_internal->htmlXhtml(sFile); // Открывает файл на проверку if (!m_internal->m_oLightReader.FromFile(m_internal->m_sTmp + L"/res.xhtml")) return false; // Читаем html if(!m_internal->isHtml()) return false; return true; } void CHtmlFile2::SetTmpDirectory(const std::wstring& sFolder) { m_internal->m_sTmp = sFolder; } HRESULT CHtmlFile2::Open(const std::wstring& sSrc, const std::wstring& sDst, CHtmlParams* oParams) { if(!m_internal->m_oLightReader.IsValid()) if(!IsHtmlFile(sSrc)) return S_FALSE; m_internal->m_sSrc = NSSystemPath::GetDirectoryName(sSrc); m_internal->m_sDst = sDst; m_internal->CreateDocxEmpty(oParams); m_internal->readStyle(); // Переходим в начало if(!m_internal->m_oLightReader.MoveToStart()) return S_FALSE; if(!m_internal->readSrc(NSFile::GetFileName(sSrc))) return S_FALSE; m_internal->write(); NSFile::CFileBinary::Remove(m_internal->m_sTmp + L"/res.xhtml"); return S_OK; } HRESULT CHtmlFile2::OpenBatch(const std::vector& sSrc, const std::wstring& sDst, CHtmlParams* oParams) { m_internal->m_sDst = sDst; m_internal->CreateDocxEmpty(oParams); for(const std::wstring& sS : sSrc) m_internal->m_sSrcs.insert(std::make_pair(NSFile::GetFileName(sS), std::vector())); for(const std::wstring& sS : sSrc) { #ifdef _DEBUG std::wcout << NSFile::GetFileName(sS) << std::endl; #endif m_internal->m_sSrc = NSSystemPath::GetDirectoryName(sS); if(!IsHtmlFile(sS)) return S_FALSE; m_internal->readStyle(); // Переходим в начало if(!m_internal->m_oLightReader.MoveToStart()) return S_FALSE; if(!m_internal->readSrc(NSFile::GetFileName(sS))) return S_FALSE; NSFile::CFileBinary::Remove(m_internal->m_sTmp + L"/res.xhtml"); m_internal->m_oLightReader.Clear(); m_internal->m_oStylesCalculator.Clear(); m_internal->m_sBase = L""; } m_internal->write(); return S_OK; }