#include "htmlfile2.h" #include #include #include #include #include #include #include #include #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/Network/FileTransporter/include/FileTransporter.h" #include "../DesktopEditor/common/Types.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/common/ProcessEnv.h" #include "../DesktopEditor/xml/include/xmlutils.h" #include "../DesktopEditor/raster/BgraFrame.h" #include "../DesktopEditor/graphics/pro/Fonts.h" #include "../DesktopEditor/graphics/pro/Graphics.h" #include "htmlfile2.h" #include #ifndef VALUE2STR #define VALUE_TO_STRING(x) #x #define VALUE2STR(x) VALUE_TO_STRING(x) #endif std::wstring rStyle = L" a area b strong bdo bdi big br center cite dfn em i var code kbd samp tt del s font img ins u mark q rt sup small sub svg input basefont button label data object noscript output abbr time ruby progress hgroup meter span acronym "; //struct CTree //{ // NSCSS::CNode m_oNode; // std::vector m_arrChild; //}; // Ячейка таблицы struct CTc { int i; int j; std::wstring sGridSpan = L"1"; CTc(int _i, int _j, const std::wstring& sColspan) : i(_i), j(_j), sGridSpan(sColspan) {} bool operator==(const CTc& c2) { return (i == c2.i && j == c2.j && sGridSpan == c2.sGridSpan); } }; // Настройки текста struct CTextSettings { bool bBdo; // Реверс текста bool bPre; // Сохранение форматирования (Сохранение пробелов, табуляций, переносов строк) int nLi; // Уровень списка std::wstring sRStyle; // w:rStyle std::wstring sPStyle; // w:pStyle CTextSettings(bool _bBdo, bool _bPre, int _nLi, const std::wstring& _sRStyle, const std::wstring& _sPStyle) : bBdo(_bBdo), bPre(_bPre), nLi(_nLi), sRStyle(_sRStyle), sPStyle(_sPStyle) {} CTextSettings(const CTextSettings& oTS) : bBdo(oTS.bBdo), bPre(oTS.bPre), nLi(oTS.nLi), sRStyle(oTS.sRStyle), sPStyle(oTS.sPStyle) {} }; 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(); 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); } } std::wstring EncodeXmlString(const std::wstring& s) { std::wstring sRes = s; replace_all(sRes, L"&", L"&"); replace_all(sRes, L"<", L"<"); replace_all(sRes, L">", L">"); replace_all(sRes, L"\"", L"""); replace_all(sRes, L"\'", L"'"); replace_all(sRes, L"\n", L" "); replace_all(sRes, L"\r", L" "); replace_all(sRes, L"\t", L" "); return sRes; } 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 папка std::wstring m_sSrc; // Директория источника std::wstring m_sDst; // Директория назначения std::wstring m_sBase; // Полный базовый адрес NSCSS::CTree m_oTree; // Дерево body html-файла private: int m_nFootnoteId; // ID сноски int m_nHyperlinkId; // 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_oNoteXmlRels; // footnotes.xml.rels NSStringUtils::CStringBuilder m_oDocXml; // document.xml NSStringUtils::CStringBuilder m_oNoteXml; // footnotes.xml NSStringUtils::CStringBuilder m_oNumberXml; // numbering.xml bool m_bInP; // открыт? bool m_bWasPStyle; // записан? bool m_bWasSpace; // Был пробел? std::vector m_arrImages; // Картинки std::map m_mFootnotes; // Сноски public: CHtmlFile2_Private() : m_nFootnoteId(1), m_nHyperlinkId(1), m_nCrossId(1), m_nNumberingId(1), m_bInP(false), m_bWasPStyle(false), m_bWasSpace(false) { //Установим размер исходного и нового окна для Css калькулятора (должны быть одинаковые единицы измерения (желательно пункты)) //Это нужно для масштабирования некоторых значений m_oStylesCalculator.SetSizeSourceWindow(NSCSS::CSizeWindow(4940 * (1366 * (25.4 / m_oStylesCalculator.GetDpi())), 0)); m_oStylesCalculator.SetSizeDeviceWindow(NSCSS::CSizeWindow(4940, 0)); } ~CHtmlFile2_Private() { m_oLightReader .Clear(); m_oStylesCalculator.Clear(); m_oXmlStyle .Clear(); m_oStylesXml .Clear(); m_oDocXmlRels .Clear(); m_oNoteXmlRels .Clear(); m_oDocXml .Clear(); m_oNoteXml .Clear(); m_oNumberXml .Clear(); } // Проверяет наличие тэга html bool isHtml() { return (m_oLightReader.ReadNextNode() ? m_oLightReader.GetName() == L"html" : false); } // Создаёт основу docx void CreateDocxEmpty(CHtmlParams* oParams) { // Создаем пустые папки NSDirectory::CreateDirectory(m_sDst + L"/_rels"); NSDirectory::CreateDirectory(m_sDst + L"/docProps"); NSDirectory::CreateDirectory(m_sDst + L"/word"); NSDirectory::CreateDirectory(m_sDst + L"/word/_rels"); NSDirectory::CreateDirectory(m_sDst + L"/word/media"); NSDirectory::CreateDirectory(m_sDst + L"/word/theme"); // theme1.xml std::wstring sTheme = L""; NSFile::CFileBinary oThemeWriter; if (oThemeWriter.CreateFileW(m_sDst + L"/word/theme/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 std::wstring sApp = L""; sApp += sApplication + L"/" + UTF8_TO_U(sVersion); sApp += L"0falsefalsefalsefalse"; NSFile::CFileBinary oAppWriter; if (oAppWriter.CreateFileW(m_sDst + L"/docProps/app.xml")) { oAppWriter.WriteStringUTF8(sApp); oAppWriter.CloseFile(); } // .rels std::wstring sRels = L""; NSFile::CFileBinary oRelsWriter; if (oRelsWriter.CreateFileW(m_sDst + L"/_rels/.rels")) { oRelsWriter.WriteStringUTF8(sRels); oRelsWriter.CloseFile(); } // [Content_Types].xml std::wstring sContent = L""; NSFile::CFileBinary oContentWriter; if (oContentWriter.CreateFileW(m_sDst + L"/[Content_Types].xml")) { oContentWriter.WriteStringUTF8(sContent); oContentWriter.CloseFile(); } // fontTable.xml std::wstring sFontTable = L""; NSFile::CFileBinary oFontTableWriter; if (oFontTableWriter.CreateFileW(m_sDst + L"/word/fontTable.xml")) { oFontTableWriter.WriteStringUTF8(sFontTable); oFontTableWriter.CloseFile(); } // settings.xml std::wstring sSettings = L""; NSFile::CFileBinary oSettingsWriter; if (oSettingsWriter.CreateFileW(m_sDst + L"/word/settings.xml")) { oSettingsWriter.WriteStringUTF8(sSettings); oSettingsWriter.CloseFile(); } // webSettings.xml std::wstring sWebSettings = L""; NSFile::CFileBinary oWebWriter; if (oWebWriter.CreateFileW(m_sDst + L"/word/webSettings.xml")) { oWebWriter.WriteStringUTF8(sWebSettings); oWebWriter.CloseFile(); } // numbering.xml // Маркированный список m_oNumberXml += L""; // core.xml std::wstring sCore = L""; if(oParams) { if(!oParams->m_sBookTitle.empty()) { sCore += L""; sCore += EncodeXmlString(oParams->m_sBookTitle); sCore += L""; } if(!oParams->m_sAuthors.empty()) { sCore += L""; sCore += EncodeXmlString(oParams->m_sAuthors); sCore += L""; } if(!oParams->m_sGenres.empty()) { sCore += L""; sCore += EncodeXmlString(oParams->m_sGenres); sCore += L""; } if(!oParams->m_sDate.empty()) { sCore += L""; sCore += EncodeXmlString(oParams->m_sDate); sCore += L""; } if(!oParams->m_sDescription.empty()) { sCore += L""; sCore += EncodeXmlString(oParams->m_sDescription); sCore += L""; } } sCore += L""; NSFile::CFileBinary oCoreWriter; if (oCoreWriter.CreateFileW(m_sDst + L"/docProps/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_oNoteXmlRels += L""; m_oDocXml += L""; m_oNoteXml += L""; m_oNoteXml += L""; m_oStylesXml += L""; // docDefaults по умолчанию if(oParams && !oParams->m_sdocDefaults.empty()) m_oStylesXml += oParams->m_sdocDefaults; else m_oStylesXml += L""; // normal по умолчанию if(oParams && !oParams->m_sNormal.empty()) m_oStylesXml += oParams->m_sNormal; else m_oStylesXml += L""; // Маркированный список m_oStylesXml += L""; // Ссылки m_oStylesXml += L""; // Таблицы m_oStylesXml += L""; // Сноски m_oStylesXml += L""; } // Читает файл void readSrc() { // Читаем html m_oLightReader.ReadNextNode(); 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(); } } // Дописывает концы docx 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_oNoteXmlRels.WriteString(L""); if (oRelsWriter.CreateFileW(m_sDst + L"/word/_rels/footnotes.xml.rels")) { oRelsWriter.WriteStringUTF8(m_oNoteXmlRels.GetData()); oRelsWriter.CloseFile(); } if (m_bInP) m_oDocXml.WriteString(L""); 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(); } } // Конвертирует html в xhtml bool htmlXhtml(const std::wstring& sSrc) { BYTE* pData; DWORD nLength; if (!NSFile::CFileBinary::ReadAllBytes(sSrc, &pData, nLength)) return false; std::string sFileContent = XmlUtils::GetUtf8FromFileContent(pData, nLength); bool bNeedConvert = true; if (nLength > 4) { if (pData[0] == 0xFF && pData[1] == 0xFE && !(pData[2] == 0x00 && pData[3] == 0x00)) bNeedConvert = false; if (pData[0] == 0xFE && pData[1] == 0xFF) bNeedConvert = false; if (pData[0] == 0xFF && pData[1] == 0xFE && pData[2] == 0x00 && pData[3] == 0x00) bNeedConvert = false; if (pData[0] == 0 && pData[1] == 0 && pData[2] == 0xFE && pData[3] == 0xFF) bNeedConvert = false; } RELEASEARRAYOBJECTS(pData); size_t nFind = sFileContent.find("version=\""); if(nFind != std::string::npos) { nFind += 9; size_t nFindEnd = sFileContent.find("\"", nFind); if(nFindEnd != std::string::npos) sFileContent.replace(nFind, nFindEnd - nFind, "1.0"); } std::wstring sRes = htmlToXhtml(sFileContent, bNeedConvert); /* NSFile::CFileBinary oWriter; if (oWriter.CreateFileW(m_sTmp + L"/res.html")) { oWriter.WriteStringUTF8(sRes); oWriter.CloseFile(); } */ return m_oLightReader.FromString(sRes); } // Конвертирует mht в xhtml bool mhtXhtml(const std::wstring& sSrc) { NSFile::CFileBinary file; if (!file.OpenFile(sSrc)) return false; unsigned char* buffer = new unsigned char[4096]; if (!buffer) { file.CloseFile(); return false; } DWORD dwReadBytes = 0; file.ReadFile(buffer, 4096, dwReadBytes); file.CloseFile(); std::string xml_string = XmlUtils::GetUtf8FromFileContent(buffer, dwReadBytes); bool bRes = false; if (std::string::npos != xml_string.find("Content-Type: multipart/related")) { BYTE* pData; DWORD nLength; if (!NSFile::CFileBinary::ReadAllBytes(sSrc, &pData, nLength)) return false; std::string sFileContent = XmlUtils::GetUtf8FromFileContent(pData, nLength); RELEASEARRAYOBJECTS(pData); std::wstring sRes = mhtToXhtml(sFileContent); /* NSFile::CFileBinary oWriter; if (oWriter.CreateFileW(m_sTmp + L"/res.html")) { oWriter.WriteStringUTF8(sRes); oWriter.CloseFile(); } */ bRes = m_oLightReader.FromString(sRes); } else bRes = htmlXhtml(sSrc); RELEASEARRAYOBJECTS(buffer); return bRes; } // Читает стили 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"body") readStyle2(m_oTree); else { // Стиль по ссылке if(sName == L"link") { while(m_oLightReader.MoveToNextAttribute()) { if(m_oLightReader.GetName() != L"href") continue; std::wstring sRef = m_oLightReader.GetText(); if(NSFile::GetFileExtention(sRef) != L"css") continue; std::wstring sFName = NSFile::GetFileName(sRef); // Стиль в сети if(sRef.substr(0, 4) == L"http") { sFName = m_sTmp + L'/' + sFName; NSNetwork::NSFileTransport::CFileDownloader oDownloadStyle(sRef, false); oDownloadStyle.SetFilePath(sFName); if(oDownloadStyle.DownloadSync()) { m_oStylesCalculator.AddStylesFromFile(sFName); NSFile::CFileBinary::Remove(sFName); } } else { m_oStylesCalculator.AddStylesFromFile(m_sSrc + L'/' + sFName); m_oStylesCalculator.AddStylesFromFile(m_sSrc + L'/' + sRef); } } m_oLightReader.MoveToElement(); } // тэг style содержит стили для styles.xml else if(sName == L"style") m_oStylesCalculator.AddStyles(m_oLightReader.GetText2()); else readStyle(); } } } void readStyle2(NSCSS::CTree& oTree) { std::wstring sName = m_oLightReader.GetName(); // Стиль по ссылке if(sName == L"link") { while(m_oLightReader.MoveToNextAttribute()) { if(m_oLightReader.GetName() != L"href") continue; std::wstring sRef = m_oLightReader.GetText(); if(NSFile::GetFileExtention(sRef) != L"css") continue; std::wstring sFName = NSFile::GetFileName(sRef); // Стиль в сети if(sRef.substr(0, 4) == L"http") { sFName = m_sTmp + L'/' + sFName; NSNetwork::NSFileTransport::CFileDownloader oDownloadStyle(sRef, false); oDownloadStyle.SetFilePath(sFName); if(oDownloadStyle.DownloadSync()) { m_oStylesCalculator.AddStylesFromFile(sFName); NSFile::CFileBinary::Remove(sFName); } } else { m_oStylesCalculator.AddStylesFromFile(m_sSrc + L'/' + sFName); m_oStylesCalculator.AddStylesFromFile(m_sSrc + L'/' + sRef); } } m_oLightReader.MoveToElement(); } // тэг style содержит стили для styles.xml else if(sName == L"style") m_oStylesCalculator.AddStyles(m_oLightReader.GetText2()); oTree.m_oNode.m_wsName = sName; // Стиль по атрибуту while(m_oLightReader.MoveToNextAttribute()) { std::wstring sNameA = m_oLightReader.GetName(); if(sNameA == L"class") oTree.m_oNode.m_wsClass = m_oLightReader.GetText(); else if(sNameA == L"id") oTree.m_oNode.m_wsId = m_oLightReader.GetText(); else if(sNameA == L"style") oTree.m_oNode.m_wsStyle += m_oLightReader.GetText(); else oTree.m_oNode.m_mAttributes[sNameA] = m_oLightReader.GetText(); } m_oLightReader.MoveToElement(); int nDeath = m_oLightReader.GetDepth(); while(m_oLightReader.ReadNextSiblingNode(nDeath)) { if(!m_oLightReader.IsEmptyNode()) { NSCSS::CTree oChildTree; readStyle2(oChildTree); oTree.m_arrChild.push_back(oChildTree); } } } void PageBreakBefore() { m_oDocXml.WriteString(L""); } private: std::wstring GetSubClass(NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors) { NSCSS::CNode oNode; std::wstring sNote; oNode.m_wsName = m_oLightReader.GetName(); // Стиль по атрибуту while(m_oLightReader.MoveToNextAttribute()) { std::wstring sName = m_oLightReader.GetName(); if(sName == L"class") oNode.m_wsClass = m_oLightReader.GetText(); else if(sName == L"id") { oNode.m_wsId = m_oLightReader.GetText(); std::wstring sCrossId = std::to_wstring(m_nCrossId++); oXml->WriteString(L"WriteString(sCrossId); oXml->WriteString(L"\" w:name=\""); oXml->WriteEncodeXmlString(oNode.m_wsId); oXml->WriteString(L"\"/>WriteString(sCrossId); oXml->WriteString(L"\"/>"); } else if(sName == L"style") oNode.m_wsStyle += m_oLightReader.GetText(); else if(sName == L"title") sNote = m_oLightReader.GetText(); else oNode.m_mAttributes[sName] = m_oLightReader.GetText(); } m_oLightReader.MoveToElement(); sSelectors.push_back(oNode); return sNote; } std::wstring GetStyle(const NSCSS::CCompiledStyle& oStyle, bool bP) { // NSCSS::CCompiledStyle oStyle = m_oStylesCalculator.GetCompiledStyle(sSelectors); bP ? m_oXmlStyle.WritePStyle(oStyle) : m_oXmlStyle.WriteRStyle(oStyle); m_oStylesXml.WriteString(m_oXmlStyle.GetStyle()); return m_oXmlStyle.GetIdAndClear(); } void readHead() { if(m_oLightReader.IsEmptyNode()) return; int nDeath = m_oLightReader.GetDepth(); while (m_oLightReader.ReadNextSiblingNode(nDeath)) { // Базовый адрес if (m_oLightReader.GetName() == L"base") { while (m_oLightReader.MoveToNextAttribute()) if (m_oLightReader.GetName() == L"href") m_sBase = m_oLightReader.GetText(); m_oLightReader.MoveToElement(); } } } void readBody() { std::vector sSelectors; sSelectors.push_back(NSCSS::CNode(L"html", L"", L"")); GetSubClass(&m_oDocXml, sSelectors); /* std::wstring sCrossId = std::to_wstring(m_nCrossId++); m_oDocXml.WriteString(L""); */ readStream(&m_oDocXml, sSelectors, { false, false, -1, L"", L"" }); } void readInside (NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, const CTextSettings& oTS, const std::wstring& sName) { if(sName == L"#text") { std::wstring sText = m_oLightReader.GetText(); size_t find = sText.find_first_not_of(L" \n\t\r"); if (find == std::wstring::npos) { m_bWasSpace = true; return; } else if (find != 1 || m_bWasSpace || sText.front() != L' ') sText.erase(0, find); std::wstring sPStyle = wrP(oXml, sSelectors, oTS); oXml->WriteString(L""); std::wstring sRStyle = wrR(oXml, sSelectors, oTS); oXml->WriteString(L""); std::wstring::iterator end; if(oTS.bBdo) std::reverse(sText.begin(), sText.end()); if (m_bWasSpace) { sText.insert(sText.begin(), L' '); m_bWasSpace = false; } if(oTS.bPre) { size_t nAfter = sText.find_first_of(L"\n\r"); while(nAfter != std::wstring::npos) { oXml->WriteEncodeXmlString(sText.c_str(), nAfter); oXml->WriteString(L""); if(!sPStyle.empty()) { oXml->WriteString(L"WriteString(sPStyle); oXml->WriteString(L"\"/>"); oXml->WriteString(oTS.sPStyle); oXml->WriteString(L""); } oXml->WriteString(L"WriteString(sRStyle); oXml->WriteString(L"\"/>"); oXml->WriteString(oTS.sRStyle); oXml->WriteString(L""); sText.erase(0, nAfter + 1); nAfter = sText.find_first_of(L"\n\r"); } 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""); return; } std::wstring sNote = GetSubClass(oXml, sSelectors); // Ссылка // Область ссылки if(sName == L"a" || sName == L"area") readA(oXml, sSelectors, oTS, sNote); // Полужирный текст // Акцентированный текст else if(sName == L"b" || sName == L"strong") { CTextSettings oTSR(oTS); oTSR.sRStyle += L""; readStream(oXml, sSelectors, oTSR); } // Направление текста else if(sName == L"bdo") { std::wstring sDir; while(m_oLightReader.MoveToNextAttribute()) if(m_oLightReader.GetName() == L"dir") sDir = m_oLightReader.GetText(); m_oLightReader.MoveToElement(); CTextSettings oTSBdo(oTS); oTSBdo.bBdo = (sDir == L"rtl"); readStream(oXml, sSelectors, oTSBdo); } // Отмена направления текста else if(sName == L"bdi") { CTextSettings oTSBdo(oTS); oTSBdo.bBdo = false; readStream(oXml, sSelectors, oTSBdo); } // Увеличивает размер шрифта else if(sName == L"big") { CTextSettings oTSR(oTS); oTSR.sRStyle += L""; readStream(oXml, sSelectors, oTSR); } // Перенос строки else if(sName == L"br") { wrP(oXml, sSelectors, oTS); oXml->WriteString(L""); NSCSS::CCompiledStyle oStyle = m_oStylesCalculator.GetCompiledStyle(sSelectors); if(oStyle.m_oText.GetAlign() == L"both") oXml->WriteString(L""); oXml->WriteString(L""); m_bWasSpace = false; } else if(sName == L"center") { CTextSettings oTSP(oTS); oTSP.sPStyle += L""; readStream(oXml, sSelectors, oTSP); } // Цитата, обычно выделяется курсивом // Новый термин, обычно выделяется курсивом // Акцентированный текст // Курсивный текст // Переменная, обычно выделяется курсивом else if(sName == L"cite" || sName == L"dfn" || sName == L"em" || sName == L"i" || sName == L"var") { CTextSettings oTSR(oTS); oTSR.sRStyle += L""; readStream(oXml, sSelectors, oTSR); } // Код // Моноширинный шрифт, например, Consolas // Результат скрипта else if(sName == L"code" || sName == L"kbd" || sName == L"samp" || sName == L"tt") { CTextSettings oTSR(oTS); oTSR.sRStyle += L""; readStream(oXml, sSelectors, oTSR); } // Зачеркнутый текст else if(sName == L"del" || sName == L"s") { CTextSettings oTSR(oTS); oTSR.sRStyle += L""; readStream(oXml, sSelectors, oTSR); } else if(sName == L"font") { while(m_oLightReader.MoveToNextAttribute()) { std::wstring sAName = m_oLightReader.GetName(); if(sAName == L"color") sSelectors.back().m_wsStyle += L"; color: " + m_oLightReader.GetText(); else if(sAName == L"face") sSelectors.back().m_wsStyle += L"; font-family: " + m_oLightReader.GetText(); else if(sAName == L"size") { int nSize = 3; std::wstring sSize = m_oLightReader.GetText(); if(!sSize.empty()) { if(sSize.front() == L'+') nSize += std::stoi(sSize.substr(1)); else if(sSize.front() == L'-') nSize -= std::stoi(sSize.substr(1)); else nSize = std::stoi(sSize); } sSize = nSize >= 1 && nSize <= 7 ? std::to_wstring(10 + nSize * 5) : L"22"; sSelectors.back().m_wsStyle += L"; font-size: " + sSize; } } m_oLightReader.MoveToElement(); readStream(oXml, sSelectors, oTS); } // Картинки else if(sName == L"img") readImage(oXml, sSelectors, oTS); // Подчеркнутый else if(sName == L"ins" || sName == L"u") { CTextSettings oTSR(oTS); oTSR.sRStyle += L""; readStream(oXml, sSelectors, oTSR); } // Выделенный текст, обычно выделяется желтым else if(sName == L"mark") { CTextSettings oTSR(oTS); oTSR.sRStyle += L""; readStream(oXml, sSelectors, oTSR); } // Цитата, выделенная кавычками, обычно выделяется курсивом else if(sName == L"q") { wrP(oXml, sSelectors, oTS); oXml->WriteString(L""); std::wstring sRStyle = wrR(oXml, sSelectors, oTS); oXml->WriteString(L"""); CTextSettings oTSR(oTS); oTSR.sRStyle += L""; readStream(oXml, sSelectors, oTSR); wrP(oXml, sSelectors, oTS); oXml->WriteString(L"WriteString(sRStyle); oXml->WriteString(L"\"/>"); oXml->WriteString(oTS.sRStyle); oXml->WriteString(L"""); } // Текст верхнего регистра else if(sName == L"rt" || sName == L"sup") { CTextSettings oTSR(oTS); oTSR.sRStyle += L""; readStream(oXml, sSelectors, oTSR); } // Уменьшает размер шрифта else if(sName == L"small") { CTextSettings oTSR(oTS); oTSR.sRStyle += L""; readStream(oXml, sSelectors, oTSR); } // Текст нижнего регистра else if(sName == L"sub") { CTextSettings oTSR(oTS); oTSR.sRStyle += L""; readStream(oXml, sSelectors, oTSR); } // Векторная картинка else if(sName == L"svg" || (sName.length() > 3 && sName.compare(sName.length() - 3, 3, L"svg") == 0)) { wrP(oXml, sSelectors, oTS); readSVG(oXml); } else if(sName == L"input") readInput(oXml, sSelectors, oTS); // Игнорируются тэги выполняющие скрипт 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" || sName == L"comment" || sName == L"title" || sName == L"style") { sSelectors.pop_back(); return; } else if (sName == L"span") { if (sSelectors.back().m_wsClass == L"MsoFootnoteReference") { sSelectors.pop_back(); return; } readStream(oXml, sSelectors, oTS); } // Без нового абзаца else if(sName == L"basefont" || sName == L"button" || sName == L"label" || sName == L"data" || sName == L"object" || sName == L"noscript" || sName == L"output" || sName == L"abbr" || sName == L"time" || sName == L"ruby" || sName == L"progress" || sName == L"hgroup" || sName == L"meter" || sName == L"acronym") readStream(oXml, sSelectors, oTS); // С нового абзаца else { if (m_bInP) { for (const NSCSS::CNode& item : sSelectors) if (item.m_wsName == L"a") oXml->WriteString(L""); oXml->WriteString(L""); m_bInP = false; } m_bWasSpace = false; // Адрес if(sName == L"address") { CTextSettings oTSR(oTS); oTSR.sRStyle += L""; readStream(oXml, sSelectors, oTSR); } // Определение термина, отступ от левого края else if(sName == L"dd") { CTextSettings oTSP(oTS); oTSP.sPStyle += L""; readStream(oXml, sSelectors, oTSP); } // aside возможно использовать для сносок в epub else if (sName == L"aside" || sName == L"div") { int bMsoFootnote = 0; std::wstring sFootnoteID; while (m_oLightReader.MoveToNextAttribute()) { std::wstring sAName = m_oLightReader.GetName(); std::wstring sAText = m_oLightReader.GetText(); if (sAName == L"epub:type" && sAText == L"footnote") bMsoFootnote++; else if (sAName == L"style" && sAText == L"mso-element:footnote") bMsoFootnote++; else if (sAName == L"id") { std::map::iterator it = m_mFootnotes.find(sAText); if (it != m_mFootnotes.end()) { bMsoFootnote++; sFootnoteID = it->second; } } } m_oLightReader.MoveToElement(); if (bMsoFootnote >= 2) { m_oNoteXml.WriteString(L""); readStream(&m_oNoteXml, sSelectors, oTS); m_oNoteXml.WriteString(L""); } else readStream(oXml, sSelectors, oTS); } // С нового абзаца else if(sName == L"article" || sName == L"header" || sName == L"blockquote" || sName == L"main" || sName == L"dir" || sName == L"summary" || sName == L"footer" || sName == L"nav" || sName == L"figcaption" || sName == L"form" || sName == L"details" || sName == L"option" || sName == L"dt" || sName == L"p" || sName == L"section" || sName == L"figure" || sName == L"dl" || sName == L"legend" || sName == L"map" || sName == L"h1" || sName == L"h2" || sName == L"h3" || sName == L"h4" || sName == L"h5" || sName == L"h6") readStream(oXml, sSelectors, oTS); // Горизонтальная линия else if(sName == L"hr") { bool bPrint = true; for (const NSCSS::CNode& item : sSelectors) { if (item.m_wsName == L"div" && item.m_wsStyle == L"mso-element:footnote-list") { bPrint = false; break; } } if (bPrint) oXml->WriteString(L""); } // Меню // Маркированный список else if(sName == L"menu" || sName == L"ul" || sName == L"select" || sName == L"datalist") readLi(oXml, sSelectors, oTS, true); // Нумерованный список else if(sName == L"ol") readLi(oXml, sSelectors, oTS, false); // Предварительно форматированный текст else if(sName == L"pre" || sName == L"xmp") { CTextSettings oTSPre(oTS); oTSPre.bPre = true; oTSPre.sRStyle += L""; oTSPre.sPStyle += L""; readStream(oXml, sSelectors, oTSPre); } // Таблицы else if(sName == L"table") readTable(oXml, sSelectors, oTS); // Текст с границами else if(sName == L"textarea" || sName == L"fieldset") { CTextSettings oTSP(oTS); oTSP.sPStyle += L""; readStream(oXml, sSelectors, oTSP); } // Неизвестный тэг. Выделять ли его абзацем? else readStream(oXml, sSelectors, oTS); readNote(oXml, sSelectors, sNote); sNote = L""; if (m_bInP) { for (const NSCSS::CNode& item : sSelectors) if (item.m_wsName == L"a") oXml->WriteString(L""); oXml->WriteString(L""); m_bInP = false; } m_bWasSpace = false; } readNote(oXml, sSelectors, sNote); sSelectors.pop_back(); } bool readStream (NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, const CTextSettings& oTS) { int nDeath = m_oLightReader.GetDepth(); if(m_oLightReader.IsEmptyNode() || !m_oLightReader.ReadNextSiblingNode2(nDeath)) return false; do { readInside(oXml, sSelectors, oTS, m_oLightReader.GetName()); } while(m_oLightReader.ReadNextSiblingNode2(nDeath)); return true; } void readTr (NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, const CTextSettings& oTS, const std::wstring& sBorders) { std::vector mTable; 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(); // Вставляем ячейки до std::vector::iterator it1 = std::find_if(mTable.begin(), mTable.end(), [i, j](const CTc& item){ return item.i == i && item.j == j; }); std::vector::iterator 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""); oXml->WriteString(!sBorders.empty() ? sBorders : L""); 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; }); } GetSubClass(oXml, sSelectors); oXml->WriteString(L""); NSCSS::CCompiledStyle oStyleSetting = m_oStylesCalculator.GetCompiledStyle({sSelectors.back()}, true); NSCSS::CCompiledStyle oStyle = m_oStylesCalculator.GetCompiledStyle({sSelectors.back()}, false); NSCSS::CCompiledStyle::StyleEquation(oStyle, oStyleSetting); int nWidth = oStyle.m_oDisplay.GetWidth().ToInt(NSCSS::UnitMeasure::Point, m_oStylesCalculator.GetSizeDeviceWindow().m_ushWidth); std::wstring wsType = L"dxa"; //Если ширина указана в %, то используем тип dxa, если же в других единицах измерения, то в pct #if 1 // проблема с regex в старом gcc (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52719) boost::wregex oWidthRegex(L"((width)+)[\\s]*:[\\s]*(.+%)"); bool bIsWidthPct = boost::regex_search(sSelectors.back().m_wsStyle, oWidthRegex); #else std::wregex oWidthRegex(L"((width)+)[\\s]*:[\\s]*(.+%)"); bool bIsWidthPct = std::regex_search(sSelectors.back().m_wsStyle, oWidthRegex); #endif if (bIsWidthPct) wsType = L"pct"; else { nWidth *= 10; // ограничиваем, примерно как в ms (22 inch) if (nWidth > 31680) nWidth = 31680; } //------------------------- if (nWidth > 0) oXml->WriteString(L""); else oXml->WriteString(L""); if(nColspan != 1) { oXml->WriteString(L"WriteString(std::to_wstring(nColspan)); oXml->WriteString(L"\"/>"); j += nColspan - 1; } oXml->WriteString(L""); oXml->WriteString(!sBorders.empty() ? sBorders : L""); 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}); } std::wstring wsVerticalAlign = oStyle.m_oDisplay.GetVAlign().ToWString(); if (!wsVerticalAlign.empty()) oXml->WriteString(L""); oXml->WriteString(L""); m_bWasPStyle = false; // Читаем th. Ячейка заголовка таблицы. Выравнивание посередине. Выделяется полужирным if(m_oLightReader.GetName() == L"th") { CTextSettings oTSR(oTS); oTSR.sRStyle += L""; readStream(oXml, sSelectors, oTSR); } // Читаем td. Ячейка таблицы. Выравнивание вправо else if(m_oLightReader.GetName() == L"td") readStream(oXml, sSelectors, oTS); sSelectors.pop_back(); if (m_bInP) { wrP(oXml, sSelectors, oTS); for (const NSCSS::CNode& item : sSelectors) if (item.m_wsName == L"a") oXml->WriteString(L""); oXml->WriteString(L""); m_bInP = false; } else if (oXml->GetSubData(oXml->GetCurSize() - 6) != L"") oXml->WriteString(L""); m_bWasSpace = false; 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""); oXml->WriteString(!sBorders.empty() ? sBorders : L""); 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""); i++; } } void readTable (NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, const CTextSettings& oTS) { if(m_oLightReader.IsEmptyNode()) return; NSStringUtils::CStringBuilder oHead; NSStringUtils::CStringBuilder oBody; NSStringUtils::CStringBuilder oFoot; NSCSS::CCompiledStyle oStyle = m_oStylesCalculator.GetCompiledStyle(sSelectors, false); if (oXml->GetSubData(oXml->GetCurSize() - 6) != L"") oXml->WriteString(L""); m_bWasSpace = false; // Начало таблицы std::wstring wsTable = L""; int nWidth = oStyle.m_oDisplay.GetWidth().ToInt(NSCSS::UnitMeasure::Point, m_oStylesCalculator.GetSizeDeviceWindow().m_ushWidth); std::wstring wsAlign = oStyle.m_oDisplay.GetHAlign().ToWString(); if (0 < nWidth) wsTable += L""; else if (m_oStylesCalculator.GetSizeDeviceWindow().m_ushWidth != 0) wsTable += L""; else wsTable += L""; if (wsAlign.empty()) { NSCSS::CNode oLastNode = sSelectors.back(); sSelectors.pop_back(); NSCSS::CCompiledStyle oTempSettingsStyle = m_oStylesCalculator.GetCompiledStyle(sSelectors, true); wsAlign = oTempSettingsStyle.m_oText.GetAlign().ToWString(); if (wsAlign.empty()) { NSCSS::CCompiledStyle oTempStyle = m_oStylesCalculator.GetCompiledStyle(sSelectors, false); wsAlign = oTempStyle.m_oText.GetAlign().ToWString(); } sSelectors.push_back(oLastNode); } if (!oStyle.m_oMargin.Empty() && (0 < oStyle.m_oMargin.GetTop().ToInt() || 0 < oStyle.m_oMargin.GetBottom().ToInt())) { wsTable += L""; if (0 < oStyle.m_oMargin.GetTop().ToInt()) wsTable += L"(oStyle.m_oMargin.GetTop().ToInt() * 10 + 0.5f)) + L"\" w:type=\"dxa\"/>"; // if (0 < oStyle.m_pMargin.GetLeftSide()) // wsTable += L"(oStyle.m_pMargin.GetLeftSide() * 10 + 0.5f)) + L"\" w:type=\"dxa\"/>"; if (0 < oStyle.m_oMargin.GetBottom().ToInt()) wsTable += L"(oStyle.m_oMargin.GetBottom().ToInt() * 10 + 0.5f)) + L"\" w:type=\"dxa\"/>"; // if (0 < oStyle.m_pMargin.GetRightSide()) // wsTable += L"(oStyle.m_pMargin.GetRightSide() * 10 + 0.5f)) + L"\" w:type=\"dxa\"/>"; wsTable += L""; } if (!wsAlign.empty()) wsTable += L""; wsTable += L""; wsTable += L""; // borders std::wstring sBorders; oStyle.m_oBorder.Unblock(); if (oStyle.m_oBorder.Empty()) { sBorders = L""; } else { if (oStyle.m_oBorder.EqualSides()) { std::wstring sColor = oStyle.m_oBorder.GetBottomBorder().GetColor().ToWString(); std::wstring sSz = oStyle.m_oBorder.GetBottomBorder().GetWidth().ToWString(); std::wstring sStyle = oStyle.m_oBorder.GetBottomBorder().GetStyle().ToWString(); sBorders = L"" + L"" + L"" + L"" + L"" + L""; } else { std::wstring sColorLeftSide = oStyle.m_oBorder.GetLeftBorder().GetColor().ToWString(); std::wstring sSzLeftSide = oStyle.m_oBorder.GetLeftBorder().GetWidth().ToWString(); std::wstring sStyleLeftSide = oStyle.m_oBorder.GetLeftBorder().GetStyle().ToWString(); std::wstring sColorTopSide = oStyle.m_oBorder.GetTopBorder().GetColor().ToWString(); std::wstring sSzTopSide = oStyle.m_oBorder.GetTopBorder().GetWidth().ToWString(); std::wstring sStyleTopSide = oStyle.m_oBorder.GetTopBorder().GetStyle().ToWString(); std::wstring sColorRightSide = oStyle.m_oBorder.GetRightBorder().GetColor().ToWString(); std::wstring sSzRightSide = oStyle.m_oBorder.GetRightBorder().GetWidth().ToWString(); std::wstring sStyleRightSide = oStyle.m_oBorder.GetRightBorder().GetStyle().ToWString(); std::wstring sColorBottomSide = oStyle.m_oBorder.GetBottomBorder().GetColor().ToWString(); std::wstring sSzBottomSide = oStyle.m_oBorder.GetBottomBorder().GetWidth().ToWString(); std::wstring sStyleBottomSide = oStyle.m_oBorder.GetBottomBorder().GetColor().ToWString(); sBorders = L"" + L"" + L"" + L""; } } oXml->WriteString(wsTable); /* NSCSS::CCompiledStyle oStyleSetting = m_oStylesCalculator.GetCompiledStyle(sSelectors, true); oStyle = m_oStylesCalculator.GetCompiledStyle(sSelectors); NSCSS::CCompiledStyle::StyleEquation(oStyle, oStyleSetting); m_oXmlStyle.WriteLitePStyle(oStyleSetting); std::wstring sPSettings = m_oXmlStyle.GetStyle(); m_oXmlStyle.Clear(); size_t nBdr = sPSettings.find(L""); if (nBdr != std::wstring::npos) { nBdr += 8; size_t nBdrEnd = sPSettings.find(L"", nBdr); if (nBdrEnd != std::wstring::npos) { sBorders = sPSettings.substr(nBdr, nBdrEnd - nBdr); size_t nSpace = sBorders.find(L"w:space=\""); while (nSpace != std::wstring::npos) { nSpace += 9; size_t nSpaceEnd = sBorders.find(L'\"', nSpace); sBorders.replace(nSpace, nSpaceEnd - nSpace, L"0"); nSpace = sBorders.find(L"w:space=\"", nSpace); } } } */ int nDeath = m_oLightReader.GetDepth(); while(m_oLightReader.ReadNextSiblingNode(nDeath)) { std::wstring sName = m_oLightReader.GetName(); GetSubClass(oXml, sSelectors); // Заголовок таблицы if(sName == L"caption") { size_t nHyp = 0; if (!m_bInP) { oXml->WriteString(L""); for (const NSCSS::CNode& item : sSelectors) { if (item.m_wsName == L"a") { oXml->WriteString(L""); nHyp++; } } m_bInP = true; m_bWasPStyle = false; } // Заголовок таблицы выравнивание посередине CTextSettings oTSP { oTS.bBdo, oTS.bPre, oTS.nLi, oTS.sRStyle, oTS.sPStyle + L"" }; readStream(oXml, sSelectors, oTSP); if (m_bInP) { for (size_t i = 0; i < nHyp; i++) oXml->WriteString(L""); oXml->WriteString(L""); m_bInP = false; m_bWasPStyle = false; } m_bWasSpace = false; } if(sName == L"thead") readTr(&oHead, sSelectors, oTS, sBorders); else if(sName == L"tbody") readTr(&oBody, sSelectors, oTS, sBorders); else if(sName == L"tfoot") readTr(&oFoot, sSelectors, oTS, sBorders); sSelectors.pop_back(); } // Конец таблицы oXml->WriteString(oHead.GetData()); oXml->WriteString(oBody.GetData()); oXml->WriteString(oFoot.GetData()); oXml->WriteString(L""); } void readInput (NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, const CTextSettings& oTS) { std::wstring sValue; std::wstring sAlt; std::wstring sType; while(m_oLightReader.MoveToNextAttribute()) { std::wstring sName = m_oLightReader.GetName(); if(sName == L"value") sValue = m_oLightReader.GetText(); else if(sName == L"alt") sAlt = m_oLightReader.GetText(); else if(sName == L"type") sType = m_oLightReader.GetText(); } m_oLightReader.MoveToElement(); if(sType == L"hidden") return; if(sValue.empty()) sValue = sAlt; if(!sValue.empty()) { wrP(oXml, sSelectors, oTS); oXml->WriteString(L""); wrR(oXml, sSelectors, oTS); oXml->WriteString(L""); oXml->WriteEncodeXmlString(sValue + L' '); oXml->WriteString(L""); } readStream(oXml, sSelectors, oTS); } void readLi (NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, const CTextSettings& oTS, bool bType) { if(m_oLightReader.IsEmptyNode()) return; std::wstring sStart = L"1"; while(m_oLightReader.MoveToNextAttribute()) if(m_oLightReader.GetName() == L"start") sStart = m_oLightReader.GetText(); m_oLightReader.MoveToElement(); int nDeath = m_oLightReader.GetDepth(); while(m_oLightReader.ReadNextSiblingNode(nDeath)) { std::wstring sName = m_oLightReader.GetName(); if (sName == L"optgroup") { GetSubClass(oXml, sSelectors); while(m_oLightReader.MoveToNextAttribute()) { if(m_oLightReader.GetName() != L"label") continue; if (m_bInP) { for (const NSCSS::CNode& item : sSelectors) if (item.m_wsName == L"a") oXml->WriteString(L""); oXml->WriteString(L""); m_bInP = false; } m_bWasSpace = false; wrP(oXml, sSelectors, oTS); oXml->WriteString(L""); wrR(oXml, sSelectors, oTS); oXml->WriteString(L""); oXml->WriteEncodeXmlString(m_oLightReader.GetText()); oXml->WriteString(L""); } m_oLightReader.MoveToElement(); readLi(oXml, sSelectors, oTS, true); sSelectors.pop_back(); continue; } if (sName != L"li" && sName != L"option") { readInside(oXml, sSelectors, oTS, sName); continue; } GetSubClass(oXml, sSelectors); while(m_oLightReader.MoveToNextAttribute()) if(m_oLightReader.GetName() == L"value") sStart = m_oLightReader.GetText(); m_oLightReader.MoveToElement(); if (m_bInP) { for (const NSCSS::CNode& item : sSelectors) if (item.m_wsName == L"a") oXml->WriteString(L""); oXml->WriteString(L""); m_bInP = false; } m_bWasSpace = false; CTextSettings oTSLiP(oTS); oTSLiP.nLi++; oTSLiP.sPStyle += L""; readStream(oXml, sSelectors, oTSLiP); if (m_bInP) { for (const NSCSS::CNode& item : sSelectors) if (item.m_wsName == L"a") oXml->WriteString(L""); oXml->WriteString(L""); m_bInP = false; } m_bWasSpace = false; sSelectors.pop_back(); } // Нумерованный список if(!bType) { m_oNumberXml.WriteString(L""); } } void readA (NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, const CTextSettings& oTS, std::wstring& sNote) { std::wstring sRef; std::wstring sAlt; bool bCross = false; std::wstring sFootnote; while(m_oLightReader.MoveToNextAttribute()) { std::wstring sName = m_oLightReader.GetName(); std::wstring sText = m_oLightReader.GetText(); if(sName == L"href") { sRef = sText; if(sRef.find('#') != std::wstring::npos) bCross = true; } 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(sText); oXml->WriteString(L"\"/>WriteString(sCrossId); oXml->WriteString(L"\"/>"); } else if(sName == L"alt") sAlt = sText; else if (sName == L"style" && sText.find(L"mso-footnote-id") != std::wstring::npos) sFootnote = sText.substr(sText.rfind(L':') + 1); else if (sName == L"epub:type" && sText.find(L"noteref")) sFootnote = L"href"; } m_oLightReader.MoveToElement(); if(sNote.empty()) sNote = sRef; if (bCross && sFootnote == L"href") sFootnote = sRef.substr(sRef.find('#') + 1); if (!m_bInP) { oXml->WriteString(L""); for (size_t i = 0; i < sSelectors.size() - 1; i++) if (sSelectors[i].m_wsName == L"a") oXml->WriteString(L""); m_bInP = true; m_bWasPStyle = false; } wrP(oXml, sSelectors, oTS); // Перекрестная ссылка внутри файла if(bCross) { oXml->WriteString(L"WriteString(NSFile::GetFileName(sRef)); else oXml->WriteEncodeXmlString(sRef.c_str() + nSharp + 1); } // Внешняя ссылка else { // Пишем рельсы NSStringUtils::CStringBuilder* oRelationshipXml = &m_oDocXmlRels; if (oXml == &m_oNoteXml) oRelationshipXml = &m_oNoteXmlRels; oRelationshipXml->WriteString(L"WriteString(std::to_wstring(m_nHyperlinkId)); oRelationshipXml->WriteString(L"\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink\" Target=\""); oRelationshipXml->WriteEncodeXmlString(sRef); oRelationshipXml->WriteString(L"\" TargetMode=\"External\"/>"); // Пишем в document.xml oXml->WriteString(L"WriteEncodeXmlString(sNote); oXml->WriteString(L"\" r:id=\"rHyp"); oXml->WriteString(std::to_wstring(m_nHyperlinkId++)); } oXml->WriteString(L"\">"); if(!readStream(oXml, sSelectors, oTS)) { oXml->WriteString(L""); wrR(oXml, sSelectors, oTS); oXml->WriteString(L""); oXml->WriteEncodeXmlString(sAlt); oXml->WriteString(L""); } if (m_bInP) { oXml->WriteString(L""); bool bFootnote = false; if (sSelectors.size() > 1) { const NSCSS::CNode& oNode = sSelectors[sSelectors.size() - 2]; bFootnote = oNode.m_wsName == L"p" && oNode.m_wsClass == L"MsoFootnoteText"; } // Сноска if (bCross && !sFootnote.empty()) { if (!bFootnote) { std::wstring sFootnoteID = std::to_wstring(m_nFootnoteId++); oXml->WriteString(L"WriteString(sFootnoteID); oXml->WriteString(L"\"/>"); m_mFootnotes.insert(std::make_pair(sFootnote, sFootnoteID)); } else oXml->WriteString(L""); } } sNote = L""; } bool readBase64 (const std::wstring& sSrcM, std::wstring& sExtention) { bool bRes = false; size_t nBase = sSrcM.find(L"/", 4); nBase++; size_t nEndBase = sSrcM.find(L";", nBase); if (nEndBase == std::wstring::npos) return bRes; sExtention = sSrcM.substr(nBase, nEndBase - nBase); if (sExtention == L"octet-stream") sExtention = L"jpg"; nBase = sSrcM.find(L"base64", nEndBase); if (nBase == std::wstring::npos) return bRes; NSFile::CFileBinary oImageWriter; std::wstring sImageName = std::to_wstring(m_arrImages.size()) + L'.' + sExtention; if (oImageWriter.CreateFileW(m_sDst + L"/word/media/i" + sImageName)) { std::string sSrc = U_TO_UTF8(sSrcM); std::string sBase64 = sSrc.substr(nBase + 7); int nSrcLen = (int)sBase64.length(); int nDecodeLen = NSBase64::Base64DecodeGetRequiredLength(nSrcLen); if (nDecodeLen != 0) { 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(); } return bRes; } bool CopyImage (const std::wstring& wscSrc, const std::wstring& wsDst, bool bIsAllowExternalLocalFiles) { bool bRes = false; bool bAllow = true; std::wstring wsSrc = wscSrc; if (!bIsAllowExternalLocalFiles) { wsSrc = NSSystemPath::NormalizePath(wsSrc); std::wstring wsStartSrc = NSSystemPath::NormalizePath(m_sSrc); bAllow = wsSrc.substr(0, wsStartSrc.length()) == wsStartSrc; } if (bAllow) bRes = NSFile::CFileBinary::Copy(wsSrc, wsDst); return bRes; } inline bool NotValidExtension(const std::wstring& sExtention) { return sExtention != L"bmp" && sExtention != L"emf" && sExtention != L"emz" && sExtention != L"eps" && sExtention != L"fpx" && sExtention != L"gif" && sExtention != L"jpe" && sExtention != L"jpeg" && sExtention != L"jpg" && sExtention != L"jfif" && sExtention != L"pct" && sExtention != L"pict" && sExtention != L"png" && sExtention != L"pntg" && sExtention != L"psd" && sExtention != L"qtif" && sExtention != L"sgi" && sExtention != L"svg" && sExtention != L"tga" && sExtention != L"tpic" && sExtention != L"tiff" && sExtention != L"tif" && sExtention != L"wmf" && sExtention != L"wmz"; } void ImageAlternative(NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, const CTextSettings& oTS, const std::wstring& wsAlt) { if (wsAlt.empty()) return; wrP(oXml, sSelectors, oTS); oXml->WriteString(L""); wrR(oXml, sSelectors, oTS); oXml->WriteString(L""); oXml->WriteEncodeXmlString(wsAlt); oXml->WriteString(L""); } void readImage (NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, const CTextSettings& oTS) { std::wstring wsAlt, sSrcM; bool bRes = false; while (m_oLightReader.MoveToNextAttribute()) { std::wstring wsName = m_oLightReader.GetName(); if (wsName == L"alt") wsAlt = m_oLightReader.GetText(); else if (wsName == L"src") sSrcM = m_oLightReader.GetText(); } m_oLightReader.MoveToElement(); if (sSrcM.empty()) { ImageAlternative(oXml, sSelectors, oTS, wsAlt); return; } bool bIsAllowExternalLocalFiles = true; if (NSProcessEnv::IsPresent(NSProcessEnv::Converter::gc_allowPrivateIP)) bIsAllowExternalLocalFiles = NSProcessEnv::GetBoolValue(NSProcessEnv::Converter::gc_allowPrivateIP); int nImageId = -1; std::wstring sImageSrc, sExtention; // Предполагаем картинку в Base64 if (sSrcM.length() > 4 && sSrcM.substr(0, 4) == L"data" && sSrcM.find(L"/", 4) != std::wstring::npos) bRes = readBase64(sSrcM, sExtention); if (!bRes) { // Проверка расширения sExtention = NSFile::GetFileExtention(sSrcM); std::transform(sExtention.begin(), sExtention.end(), sExtention.begin(), tolower); if (NotValidExtension(sExtention)) { ImageAlternative(oXml, sSelectors, oTS, wsAlt); return; } // Проверка на повтор std::vector::iterator nFind = std::find(m_arrImages.begin(), m_arrImages.end(), sSrcM); if (nFind != m_arrImages.end()) { bRes = true; nImageId = nFind - m_arrImages.begin(); } } if (!bRes) { sImageSrc = sSrcM; std::wstring wsDst = m_sDst + L"/word/media/i" + std::to_wstring(m_arrImages.size()) + L'.' + sExtention; // Предполагаем картинку по локальному пути if (!((!m_sBase.empty() && m_sBase.length() > 4 && m_sBase.substr(0, 4) == L"http") || (sSrcM.length() > 4 && sSrcM.substr(0, 4) == L"http"))) { if (!m_sBase.empty()) { if (!bRes) bRes = CopyImage(NSSystemPath::Combine(m_sBase, sSrcM), wsDst, bIsAllowExternalLocalFiles); if (!bRes) bRes = CopyImage(NSSystemPath::Combine(m_sSrc, m_sBase + sSrcM), wsDst, bIsAllowExternalLocalFiles); } if (!bRes) bRes = CopyImage(NSSystemPath::Combine(m_sSrc, sSrcM), wsDst, bIsAllowExternalLocalFiles); if (!bRes) bRes = CopyImage(m_sSrc + L"/" + NSFile::GetFileName(sSrcM), wsDst, bIsAllowExternalLocalFiles); if (!bRes) bRes = CopyImage(sSrcM, wsDst, bIsAllowExternalLocalFiles); } // Предполагаем картинку в сети else { // Проверка gc_allowNetworkRequest предполагается в kernel_network NSNetwork::NSFileTransport::CFileDownloader oDownloadImg(m_sBase + sSrcM, false); oDownloadImg.SetFilePath(wsDst); bRes = oDownloadImg.DownloadSync(); } } if (!bRes) ImageAlternative(oXml, sSelectors, oTS, wsAlt); else { wrP(oXml, sSelectors, oTS); ImageRels(oXml, nImageId, sImageSrc, sExtention); } } std::wstring wrP(NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, const CTextSettings& oTS) { if (!m_bInP) { oXml->WriteString(L""); for (const NSCSS::CNode& item : sSelectors) if (item.m_wsName == L"a") oXml->WriteString(L""); m_bInP = true; m_bWasPStyle = false; } if (m_bWasPStyle) return L""; oXml->WriteString(L"> temporary; size_t i = 0; while(i != sSelectors.size()) { if (sSelectors[i].Empty() && rStyle.find(L' ' + sSelectors[i].m_wsName + L' ') != std::wstring::npos) { temporary.push_back(std::make_pair(i, sSelectors[i])); sSelectors.erase(sSelectors.begin() + i); } else i++; } NSCSS::CCompiledStyle oStyleSetting = m_oStylesCalculator.GetCompiledStyle(sSelectors, true); NSCSS::CCompiledStyle oStyle = m_oStylesCalculator.GetCompiledStyle(sSelectors); NSCSS::CCompiledStyle::StyleEquation(oStyle, oStyleSetting); std::wstring sPStyle = GetStyle(oStyle, true); m_oXmlStyle.WriteLitePStyle(oStyleSetting); std::wstring sPSettings = m_oXmlStyle.GetStyle(); m_oXmlStyle.Clear(); for(int i = temporary.size() - 1; i >= 0; i--) sSelectors.insert(sSelectors.begin() + temporary[i].first, temporary[i].second); // Если в таблице, то игнориуются Paragraph Borders bool bInTable = false; for (const NSCSS::CNode& item : sSelectors) if (item.m_wsName == L"table") bInTable = true; if (bInTable) { size_t nBdr = sPSettings.find(L""); if (nBdr != std::wstring::npos) { size_t nBdrEnd = sPSettings.find(L"", nBdr); if (nBdrEnd != std::wstring::npos) sPSettings.erase(nBdr, nBdrEnd + 9 - nBdr); } } oXml->WriteString(sPStyle); oXml->WriteString(L"\"/>"); oXml->WriteString(oTS.sPStyle + L' ' + sPSettings); oXml->WriteString(L""); m_bWasPStyle = true; return sPStyle; } std::wstring wrR(NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, const CTextSettings& oTS) { if (!m_bInP) return L""; NSCSS::CCompiledStyle oStyleSetting = m_oStylesCalculator.GetCompiledStyle(sSelectors, true); NSCSS::CCompiledStyle oStyle = m_oStylesCalculator.GetCompiledStyle(sSelectors); NSCSS::CCompiledStyle::StyleEquation(oStyle, oStyleSetting); std::wstring sRStyle = GetStyle(oStyle, false); m_oXmlStyle.WriteLiteRStyle(oStyleSetting); const std::wstring sRSettings = m_oXmlStyle.GetStyle(); m_oXmlStyle.Clear(); oXml->WriteString(L"WriteString(sRStyle); oXml->WriteString(L"\"/>"); oXml->WriteString(oTS.sRStyle + L' ' + sRSettings); oXml->WriteString(L""); return sRStyle; } void ImageRels (NSStringUtils::CStringBuilder* oXml, int nImageId, const std::wstring& sImageSrc, const std::wstring& sExtention) { bool bNew = nImageId < 0; if (bNew) nImageId = m_arrImages.size(); std::wstring sImageId = std::to_wstring(nImageId); std::wstring sImageName = sImageId + L'.' + sExtention; CBgraFrame oBgraFrame; if (!oBgraFrame.OpenFile(m_sDst + L"/word/media/i" + sImageName)) return; // Прописать рельсы if (bNew) { m_arrImages.push_back(sImageSrc); m_oDocXmlRels.WriteString(L""); } // Получаем размеры картинки 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 > 8000000 ? 8000000 : 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 readNote (NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, const std::wstring& sNote) { if(sNote.empty()) return; if (!m_bInP) { oXml->WriteString(L""); for (const NSCSS::CNode& item : sSelectors) if (item.m_wsName == L"a") oXml->WriteString(L""); m_bInP = true; m_bWasPStyle = false; } 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 readSVG (NSStringUtils::CStringBuilder* oXml) { // Сохранить как .svg картинку NSStringUtils::CStringBuilder oSVG; oSVG.WriteString(L""); std::wstring sSVG = m_oLightReader.GetInnerXml(); size_t nRef = sSVG.find(L"image"); while (nRef != std::wstring::npos) { size_t nRefBegin = sSVG.rfind(L'<', nRef); if (nRefBegin != std::wstring::npos) { if (sSVG[nRefBegin + 1] == L'/') nRefBegin++; sSVG.erase(nRefBegin + 1, nRef - nRefBegin - 1); nRef = nRefBegin + 1; } size_t nRefEnd = sSVG.find(L'>', nRef); size_t nHRef = sSVG.find(L"href", nRef); if (nHRef == std::wstring::npos || nRefEnd == std::wstring::npos) break; nHRef += 6; if (nHRef > nRefEnd || 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); std::wstring sTIN(sImageName); sTIN.erase(std::remove_if(sTIN.begin(), sTIN.end(), [] (wchar_t ch) { return std::iswspace(ch) || (ch == L'^'); }), sTIN.end()); sTIN = NSFile::GetFileName(sTIN); bool bRes = NSFile::CFileBinary::Copy(m_sSrc + L"/" + sImageName, m_sDst + L"/word/media/" + sTIN); if(!bRes) bRes = NSFile::CFileBinary::Copy(m_sSrc + L"/" + NSFile::GetFileName(sImageName), m_sDst + L"/word/media/" + sTIN); if(bRes) sSVG.replace(nHRef, nHRefLen - nHRef, sTIN); nRef = sSVG.find(L"image", nRef + 5); } oSVG.WriteString(sSVG); oSVG.WriteString(L""); std::wstring sImageId = std::to_wstring(m_arrImages.size()); NSFile::CFileBinary oSVGWriter; std::wstring sImageFile = m_sDst + L"/word/media/i" + 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/i" + sImageId + L".png"; pMetafile->ConvertToRaster(sPngFile.data(), 4, 1000); } pMetafile->Release(); pFonts->Release(); ImageRels(oXml, -1, L"", L"png"); } }; CHtmlFile2::CHtmlFile2() { m_internal = new CHtmlFile2_Private(); } CHtmlFile2::~CHtmlFile2() { RELEASEOBJECT(m_internal); } bool CHtmlFile2::IsHtmlFile(const std::wstring& sFile) { // Конвертируем в xhtml if(!m_internal->htmlXhtml(sFile)) return false; // Читаем html return m_internal->isHtml(); } bool CHtmlFile2::IsMhtFile(const std::wstring& sFile) { // Конвертируем в xhtml if(!m_internal->mhtXhtml(sFile)) return false; // Читаем html return m_internal->isHtml(); } void CHtmlFile2::SetTmpDirectory(const std::wstring& sFolder) { m_internal->m_sTmp = sFolder; } HRESULT CHtmlFile2::OpenHtml(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(); m_internal->m_oStylesCalculator.SetBodyTree(m_internal->m_oTree); // Переходим в начало if(!m_internal->m_oLightReader.MoveToStart()) return S_FALSE; if(oParams && oParams->m_bNeedPageBreakBefore) m_internal->PageBreakBefore(); m_internal->readSrc(); m_internal->write(); return S_OK; } HRESULT CHtmlFile2::OpenMht(const std::wstring& sSrc, const std::wstring& sDst, CHtmlParams* oParams) { if(!m_internal->m_oLightReader.IsValid()) if(!IsMhtFile(sSrc)) return S_FALSE; m_internal->m_sSrc = NSSystemPath::GetDirectoryName(sSrc); m_internal->m_sDst = sDst; m_internal->CreateDocxEmpty(oParams); m_internal->readStyle(); m_internal->m_oStylesCalculator.SetBodyTree(m_internal->m_oTree); // Переходим в начало if(!m_internal->m_oLightReader.MoveToStart()) return S_FALSE; if(oParams && oParams->m_bNeedPageBreakBefore) m_internal->PageBreakBefore(); m_internal->readSrc(); m_internal->write(); return S_OK; } HRESULT CHtmlFile2::OpenBatchHtml(const std::vector& sSrc, const std::wstring& sDst, CHtmlParams* oParams) { m_internal->m_sDst = sDst; m_internal->CreateDocxEmpty(oParams); bool bFirst = true; 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)) continue; m_internal->readStyle(); m_internal->m_oStylesCalculator.SetBodyTree(m_internal->m_oTree); // Переходим в начало if(m_internal->m_oLightReader.MoveToStart()) { if(oParams && oParams->m_bNeedPageBreakBefore && !bFirst) m_internal->PageBreakBefore(); bFirst = false; m_internal->readSrc(); m_internal->m_oLightReader.Clear(); m_internal->m_sBase.clear(); } m_internal->m_oStylesCalculator.Clear(); } m_internal->write(); return S_OK; }