#include "OOXMLWriter.h" #include #include "../../DesktopEditor/common/File.h" #include "../../DesktopEditor/common/Directory.h" #include "../../DesktopEditor/common/SystemUtils.h" #include "../../DesktopEditor/graphics/pro/Fonts.h" #include "../src/Languages.h" namespace HTML { #ifndef VALUE2STR #define VALUE_TO_STRING(x) #x #define VALUE2STR(x) VALUE_TO_STRING(x) #endif #define MAX_STRING_BLOCK_SIZE (size_t)10485760 #define MAXCOLUMNSINTABLE 63 #define MAXROWSINTABLE 32767 #define DEFAULT_PAGE_WIDTH 12240 // Значение в Twips #define DEFAULT_PAGE_HEIGHT 15840 // Значение в Twips #define DEFAULT_LANGUAGE std::wstring(L"en-US") #define DEFAULT_FONT_FAMILY std::wstring(L"Times New Roman") #define DEFAULT_FONT_SIZE 24 #define DEFAULT_IMAGE_WIDTH 304800 #define DEFAULT_IMAGE_HEIGHT 304800 inline bool ElementInTable(const std::vector& arSelectors); inline bool ElementInHeader(const std::vector arSelsectors); inline std::wstring StandardizeHeaderId(const std::wstring& wsHeader); inline int CalculateFontChange(const std::vector& arSelectors); inline UINT GetFontSizeLevel(UINT unFontSize); inline UINT GetFontSizeByLevel(UINT unLevel); inline void ReplaceSpaces(std::wstring& wsValue); COOXMLWriter::COOXMLWriter(THTMLParameters* pHTMLParameters, NSCSS::CCssCalculator* pCSSCalculator) : m_pDstPath(nullptr), m_pTempDir(nullptr), m_pSrcPath(nullptr), m_pBasePath(nullptr), m_pCorePath(nullptr), m_pStylesCalculator(pCSSCalculator), m_pHTMLParameters(pHTMLParameters), m_nFootnoteId(1), m_nHyperlinkId(1), m_nListId(1), m_nElementId(1), m_bBanUpdatePageData(false), m_bWasDivs(false), m_pFonts(nullptr) { m_oPageData.SetWidth (DEFAULT_PAGE_WIDTH, NSCSS::UnitMeasure::Twips, 0, true); m_oPageData.SetHeight(DEFAULT_PAGE_HEIGHT, NSCSS::UnitMeasure::Twips, 0, true); m_oPageData.SetMargin(1440, NSCSS::UnitMeasure::Twips, 0, true); m_oPageData.SetFooter(720, NSCSS::UnitMeasure::Twips, 0, true); m_oPageData.SetHeader(720, NSCSS::UnitMeasure::Twips, 0, true); m_arStates.push(TState(nullptr)); m_arStates.top().m_pCurrentDocument = &m_oDocXml; } void COOXMLWriter::SetSrcDirectory(const std::wstring& wsPath) { m_pSrcPath = &wsPath; } void COOXMLWriter::SetDstDirectory(const std::wstring& wsPath) { m_pDstPath = &wsPath; } void COOXMLWriter::SetTempDirectory(const std::wstring& wsPath) { m_pTempDir = &wsPath; } void COOXMLWriter::SetBaseDirectory(const std::wstring& wsPath) { m_pBasePath = &wsPath; } void COOXMLWriter::SetCoreDirectory(const std::wstring& wsPath) { m_pCorePath = &wsPath; } void COOXMLWriter::Begin(const std::wstring& wsDst) { // Создаем пустые папки NSDirectory::CreateDirectory(wsDst + L"/_rels"); NSDirectory::CreateDirectory(wsDst + L"/docProps"); NSDirectory::CreateDirectory(wsDst + L"/word"); NSDirectory::CreateDirectory(wsDst + L"/word/_rels"); NSDirectory::CreateDirectory(wsDst + L"/word/media"); NSDirectory::CreateDirectory(wsDst + L"/word/theme"); // theme1.xml std::wstring sTheme = L""; NSFile::CFileBinary oThemeWriter; if (oThemeWriter.CreateFileW(wsDst + 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) const std::string sVersion = VALUE2STR(INTVER); #else const std::string sVersion{L"1.0"}; #endif std::wstring sApp = L""; sApp += sApplication + L"/" + UTF8_TO_U(sVersion); sApp += L"0falsefalsefalsefalse"; NSFile::CFileBinary oAppWriter; if (oAppWriter.CreateFileW(wsDst + L"/docProps/app.xml")) { oAppWriter.WriteStringUTF8(sApp); oAppWriter.CloseFile(); } // .rels std::wstring sRels = L""; NSFile::CFileBinary oRelsWriter; if (oRelsWriter.CreateFileW(wsDst + L"/_rels/.rels")) { oRelsWriter.WriteStringUTF8(sRels); oRelsWriter.CloseFile(); } // [Content_Types].xml std::wstring sContent = L""; NSFile::CFileBinary oContentWriter; if (oContentWriter.CreateFileW(wsDst + L"/[Content_Types].xml")) { oContentWriter.WriteStringUTF8(sContent); oContentWriter.CloseFile(); } // fontTable.xml std::wstring sFontTable = L""; NSFile::CFileBinary oFontTableWriter; if (oFontTableWriter.CreateFileW(wsDst + L"/word/fontTable.xml")) { oFontTableWriter.WriteStringUTF8(sFontTable); oFontTableWriter.CloseFile(); } // settings.xml std::wstring sSettings = L""; NSFile::CFileBinary oSettingsWriter; if (oSettingsWriter.CreateFileW(wsDst + L"/word/settings.xml")) { oSettingsWriter.WriteStringUTF8(sSettings); oSettingsWriter.CloseFile(); } // numbering.xml // Маркированный список m_oNumberXml += L""; std::wstring wsCurrentLanguage; // core.xml std::wstring sCore = L""; if(nullptr != m_pHTMLParameters) { if(!m_pHTMLParameters->m_sBookTitle.empty()) { sCore += L""; sCore += EncodeXmlString(m_pHTMLParameters->m_sBookTitle); sCore += L""; } if(!m_pHTMLParameters->m_sAuthors.empty()) { sCore += L""; sCore += EncodeXmlString(m_pHTMLParameters->m_sAuthors); sCore += L""; } if(!m_pHTMLParameters->m_sGenres.empty()) { sCore += L""; sCore += EncodeXmlString(m_pHTMLParameters->m_sGenres); sCore += L""; } if(!m_pHTMLParameters->m_sDate.empty()) { sCore += L""; sCore += EncodeXmlString(m_pHTMLParameters->m_sDate); sCore += L""; } if(!m_pHTMLParameters->m_sDescription.empty()) { sCore += L""; sCore += EncodeXmlString(m_pHTMLParameters->m_sDescription); sCore += L""; } if (!m_pHTMLParameters->m_sLanguage.empty()) { wsCurrentLanguage = IndentifyLanguage(m_pHTMLParameters->m_sLanguage); sCore += L""; sCore += wsCurrentLanguage; sCore += L""; } } sCore += L""; NSFile::CFileBinary oCoreWriter; if (oCoreWriter.CreateFileW(wsDst + 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""; m_oWebSettings += L""; m_nElementId += 7; // docDefaults по умолчанию if(m_pHTMLParameters && !m_pHTMLParameters->m_sdocDefaults.empty()) m_oStylesXml += m_pHTMLParameters->m_sdocDefaults; else { m_oStylesXml += L""; m_oStylesXml += L""; m_oStylesXml += L""; m_oStylesXml += L""; m_oStylesXml += L""; // m_oStylesXml += L""; } // normal по умолчанию if(m_pHTMLParameters && !m_pHTMLParameters->m_sNormal.empty()) m_oStylesXml += m_pHTMLParameters->m_sNormal; else { m_oStylesXml += L""; m_oStylesXml += L""; m_oStylesXml += L""; } // Маркированный список m_oStylesXml += L""; // Ссылки m_oStylesXml += L""; // Таблицы // m_oStylesXml += L""; // Сноски m_oStylesXml += L""; // Web стиль по-умолчанию m_oStylesXml += L""; } void COOXMLWriter::End(const std::wstring& wsDst) { m_oDocXmlRels.WriteString(L""); NSFile::CFileBinary oRelsWriter; if (oRelsWriter.CreateFileW(wsDst + L"/word/_rels/document.xml.rels")) { oRelsWriter.WriteStringUTF8(m_oDocXmlRels.GetData()); oRelsWriter.CloseFile(); } for (const std::pair& oFootnote : m_mFootnotes) { m_oNoteXml.WriteString(L""); m_oNoteXml.WriteEncodeXmlString(oFootnote.first); m_oNoteXml.WriteString(L""); } m_oNoteXmlRels.WriteString(L""); if (oRelsWriter.CreateFileW(wsDst + L"/word/_rels/footnotes.xml.rels")) { oRelsWriter.WriteStringUTF8(m_oNoteXmlRels.GetData()); oRelsWriter.CloseFile(); } CloseP(); m_oDocXml.WriteString(L""); m_oDocXml.WriteString(L""); m_oDocXml.WriteString(L""); NSFile::CFileBinary oDocumentWriter; if (oDocumentWriter.CreateFileW(wsDst + L"/word/document.xml")) { oDocumentWriter.WriteStringUTF8(m_oDocXml.GetData()); oDocumentWriter.CloseFile(); } m_oNoteXml.WriteString(L""); NSFile::CFileBinary oFootnotesWriter; if (oFootnotesWriter.CreateFileW(wsDst + L"/word/footnotes.xml")) { oFootnotesWriter.WriteStringUTF8(m_oNoteXml.GetData()); oFootnotesWriter.CloseFile(); } // styles.xml m_oStylesXml.WriteString(L""); NSFile::CFileBinary oStylesWriter; if (oStylesWriter.CreateFileW(wsDst + L"/word/styles.xml")) { oStylesWriter.WriteStringUTF8(m_oStylesXml.GetData()); oStylesWriter.CloseFile(); } // numbering.xml // Маркированный список m_oNumberXml.WriteString(L""); // Нумерованный список for(int i = 1; i < m_nListId; i++) { m_oNumberXml.WriteString(L""); } m_oNumberXml.WriteString(L""); NSFile::CFileBinary oNumberingWriter; if (oNumberingWriter.CreateFileW(wsDst + L"/word/numbering.xml")) { oNumberingWriter.WriteStringUTF8(m_oNumberXml.GetData()); oNumberingWriter.CloseFile(); } // webSettings.xml if (m_bWasDivs) m_oWebSettings.WriteString(L""); m_oWebSettings.WriteString(L""); NSFile::CFileBinary oWebSettingsWriter; if (oWebSettingsWriter.CreateFileW(wsDst + L"/word/webSettings.xml")) { oWebSettingsWriter.WriteStringUTF8(m_oWebSettings.GetData()); oWebSettingsWriter.CloseFile(); } } bool COOXMLWriter::OpenP() { if (m_arStates.top().m_bInP) return false; m_arStates.top().m_pCurrentDocument->WriteString(L""); m_arStates.top().m_bInP = true; m_arStates.top().m_bWasPStyle = false; return true; } bool COOXMLWriter::OpenR() { if (m_arStates.top().m_bInR) return false; OpenHyperlink(); m_arStates.top().m_pCurrentDocument->WriteString(L""); m_arStates.top().m_bInR = true; return true; } bool COOXMLWriter::OpenT() { if (m_arStates.top().m_bInT) return false; m_arStates.top().m_pCurrentDocument->WriteString(L""); m_arStates.top().m_bInT = true; return true; } void COOXMLWriter::OpenHyperlink() { if (m_arStates.top().m_bInHyperlink) return; if (!m_arStates.top().m_wsHref.empty()) { if (m_arStates.top().m_bISCrossHyperlink) OpenCrossHyperlink(m_arStates.top().m_wsHref); else OpenExternalHyperlink(m_arStates.top().m_wsHref, m_arStates.top().m_wsTooltip); m_arStates.top().m_bInHyperlink = true; } } void COOXMLWriter::CloseP() { m_arStates.top().m_bWasSpace = true; if (!m_arStates.top().m_bInP) return; CloseT(); CloseR(); CloseHyperlink(); m_arStates.top().m_pCurrentDocument->WriteString(L""); m_arStates.top().m_bInP = false; } void COOXMLWriter::CloseR() { if (!m_arStates.top().m_bInR) return; m_arStates.top().m_pCurrentDocument->WriteString(L""); m_arStates.top().m_bInR = false; } void COOXMLWriter::CloseT() { if (!m_arStates.top().m_bInT) return; m_arStates.top().m_pCurrentDocument->WriteString(L""); m_arStates.top().m_bInT = false; } void COOXMLWriter::CloseHyperlink() { if (!m_arStates.top().m_bInHyperlink) return; m_arStates.top().m_pCurrentDocument->WriteString(L""); m_arStates.top().m_bInHyperlink = false; // Сноска if (m_arStates.top().m_wsFootnote.empty()) return; if (!m_arStates.top().m_bIsFootnote) { std::wstring sFootnoteID = std::to_wstring(m_nFootnoteId++); OpenR(); m_arStates.top().m_pCurrentDocument->WriteString(L"WriteString(sFootnoteID); m_arStates.top().m_pCurrentDocument->WriteString(L"\"/>"); CloseR(); m_mFootnotes.insert(std::make_pair(m_arStates.top().m_wsFootnote, sFootnoteID)); } else { OpenR(); m_arStates.top().m_pCurrentDocument->WriteString(L""); CloseR(); } } void COOXMLWriter::BeginBlock() { CloseP(); SaveState(); m_arStates.top().CreateNewCurrentDocument(); } void COOXMLWriter::EndBlock(bool bAddBlock) { if (m_arStates.size() == 1) return; CloseP(); if (m_arStates.top().m_pCurrentDocument == &m_oDocXml) { RollBackState(); return; } if (bAddBlock) { XmlString *pXmlString{m_arStates.top().m_pCurrentDocument}; const bool bRemoveXmlString{m_arStates.top().m_bRemoveCurrentDocument}; m_arStates.top().m_bRemoveCurrentDocument = false; RollBackState(); WriteToStringBuilder(*pXmlString, *m_arStates.top().m_pCurrentDocument); if (bRemoveXmlString) delete pXmlString; } else RollBackState(); } void COOXMLWriter::SetDataOutput(XmlString* pOutputData) { SaveState(); m_arStates.top().m_pCurrentDocument = pOutputData; m_arStates.top().m_bRemoveCurrentDocument = false; } void COOXMLWriter::RevertDataOutput() { CloseP(); RollBackState(); } void COOXMLWriter::SaveState() { m_arStates.push(TState(m_arStates.top().m_pCurrentDocument)); } void COOXMLWriter::RollBackState() { if(m_arStates.size() > 1) m_arStates.pop(); } void COOXMLWriter::SetCurrentDocument(XmlString* pNewDocument) { SaveState(); m_arStates.top().m_pCurrentDocument = pNewDocument; m_arStates.top().m_bRemoveCurrentDocument = false; } void COOXMLWriter::Break(const std::vector& arSelectors) { if (m_arStates.top().m_bInP) { OpenR(); if(arSelectors.back().m_pCompiledStyle->m_oText.GetAlign() == L"both") m_arStates.top().m_pCurrentDocument->WriteString(L""); m_arStates.top().m_pCurrentDocument->WriteString(L""); CloseR(); } else WriteEmptyParagraph(); m_arStates.top().m_bWasSpace = true; } void COOXMLWriter::SetHyperlinkData(const std::wstring& wsRef, const std::wstring& wsTooltip, bool bIsCross, const std::wstring& wsFootnote, bool bIsFootnote) { CloseHyperlink(); //TODO:: подумать как лучше сделать работу с гиперссылками m_arStates.top().m_wsHref = wsRef; m_arStates.top().m_wsTooltip = wsTooltip; m_arStates.top().m_bISCrossHyperlink = bIsCross; m_arStates.top().m_wsFootnote = wsFootnote; m_arStates.top().m_bIsFootnote = bIsFootnote; } void COOXMLWriter::ClearHyperlinkData() { m_arStates.top().m_wsHref.clear(); m_arStates.top().m_wsTooltip.clear(); m_arStates.top().m_bISCrossHyperlink = false; m_arStates.top().m_wsFootnote.clear(); m_arStates.top().m_bIsFootnote = false; } void COOXMLWriter::PageBreak() { OpenP(); GetCurrentDocument()->WriteString(L""); CloseP(); } void COOXMLWriter::OpenCrossHyperlink(const std::wstring& wsRef) { m_arStates.top().m_pCurrentDocument->WriteString(L"WriteEncodeXmlString(AddAnchor(wsAnchorValue)); m_arStates.top().m_pCurrentDocument->WriteString(L"\">"); } void COOXMLWriter::OpenExternalHyperlink(const std::wstring& wsRef, const std::wstring& wsTooltip) { XmlString& oRelationshipXml(m_oDocXmlRels); if (*&m_arStates.top().m_pCurrentDocument == &m_oNoteXml) oRelationshipXml = m_oNoteXmlRels; oRelationshipXml.WriteString(L""); m_arStates.top().m_bInHyperlink = true; // Пишем в document.xml m_arStates.top().m_pCurrentDocument->WriteString(L"WriteEncodeXmlString(wsTooltip); m_arStates.top().m_pCurrentDocument->WriteString(L"\" r:id=\"rHyp"); m_arStates.top().m_pCurrentDocument->WriteString(std::to_wstring(m_nHyperlinkId++)); m_arStates.top().m_pCurrentDocument->WriteString(L"\">"); } void COOXMLWriter::CloseCrossHyperlink(const std::vector& arSelectors, std::wstring wsFootnote, const std::wstring& wsRef) { if (!m_arStates.top().m_bInP) return; CloseExternalHyperlink(); if (wsFootnote == L"href") wsFootnote = wsRef.substr(wsRef.find('#') + 1); bool bFootnote = false; if (arSelectors.size() > 1) { const NSCSS::CNode& oNode = arSelectors[arSelectors.size() - 2]; bFootnote = oNode.m_wsName == L"p" && oNode.m_wsClass == L"MsoFootnoteText"; } // Сноска if (!wsFootnote.empty()) { if (!bFootnote) { std::wstring sFootnoteID = std::to_wstring(m_nFootnoteId++); OpenR(); m_arStates.top().m_pCurrentDocument->WriteString(L"WriteString(sFootnoteID); m_arStates.top().m_pCurrentDocument->WriteString(L"\"/>"); CloseR(); m_mFootnotes.insert(std::make_pair(wsFootnote, sFootnoteID)); } else { OpenR(); m_arStates.top().m_pCurrentDocument->WriteString(L""); CloseR(); } } } void COOXMLWriter::CloseExternalHyperlink() { if (!m_arStates.top().m_bInP && !m_arStates.top().m_bInHyperlink) return; CloseT(); CloseR(); m_arStates.top().m_pCurrentDocument->WriteString(L""); m_arStates.top().m_bInHyperlink = false; } std::wstring COOXMLWriter::WritePPr(const std::vector& arSelectors) { OpenP(); if (m_arStates.top().m_bWasPStyle) return L""; std::wstring sPStyle = GetStyle(*arSelectors.back().m_pCompiledStyle, true); if (sPStyle.empty() && !ElementInTable(arSelectors)) sPStyle = L"normal-web"; std::wstring wsAnchor; for (std::vector::const_reverse_iterator itNode{arSelectors.crbegin()}; itNode < arSelectors.crend(); ++itNode) { if (itNode->m_wsId.empty()) continue; wsAnchor = itNode->m_wsId; break; } if (sPStyle.empty() && m_arDivId.empty() && wsAnchor.empty()) return L""; m_arStates.top().m_pCurrentDocument->WriteNodeBegin(L"w:pPr"); if (!sPStyle.empty()) { m_arStates.top().m_pCurrentDocument->WriteString(L"WriteString(sPStyle); m_arStates.top().m_pCurrentDocument->WriteString(L"\"/>"); } int nLiLevel{-1}; bool bNumberingLi{false}; bool bInTable{false}; for (const NSCSS::CNode& oNode : arSelectors) { if (L"ol" == oNode.m_wsName) bNumberingLi = true; else if (L"ul" == oNode.m_wsName) bNumberingLi = false; else if (L"table" == oNode.m_wsName) { bInTable = true; continue; } else continue; ++nLiLevel; } if (nLiLevel >= 0) m_arStates.top().m_pCurrentDocument->WriteString(L""); if (!m_arDivId.empty() && !bInTable) m_arStates.top().m_pCurrentDocument->WriteString(L""); m_arStates.top().m_pCurrentDocument->WriteNodeEnd(L"w:pPr"); m_arStates.top().m_bWasPStyle = true; if (!wsAnchor.empty()) WriteEmptyBookmark(wsAnchor); return sPStyle; } std::wstring COOXMLWriter::WriteRPr(XmlString& oXml, const std::vector& arSelectors) { if (!m_arStates.top().m_bInP) return L""; NSCSS::CCompiledStyle *pMainStyle{arSelectors.back().m_pCompiledStyle}; const std::wstring sRStyle = GetStyle(*pMainStyle, false); std::wstring wsFontSize; const int nCalculatedFontChange{CalculateFontChange(arSelectors)}; if (0 != nCalculatedFontChange) { int nFontSizeLevel{static_cast((pMainStyle->m_oFont.Empty()) ? 3 : GetFontSizeLevel(pMainStyle->m_oFont.GetSize().ToInt(NSCSS::Point) * 2))}; nFontSizeLevel += nCalculatedFontChange; const UINT unFontSize{GetFontSizeByLevel(nFontSizeLevel)}; wsFontSize += L""; } const std::wstring wsTextMode{pMainStyle->m_oDisplay.GetVAlign().ToWString()}; if (!sRStyle.empty() || (!wsTextMode.empty() && L"normal" != wsTextMode) || !wsFontSize.empty()) { oXml.WriteString(L""); if (!sRStyle.empty()) { oXml.WriteString(L""); } if (L"sub" == wsTextMode) oXml.WriteString(L""); else if (L"super" == wsTextMode) oXml.WriteString(L""); oXml.WriteString(wsFontSize); oXml.WriteString(L""); } return sRStyle; } bool COOXMLWriter::WriteText(std::wstring wsText, const std::vector& arSelectors) { if (wsText.empty()) return false; bool bBidirectional{false}, bPreformatted{false}, bQuotation{false}, bAddSpaces{true}, bMergedText{false}, bDeleted{false}; for (const NSCSS::CNode& oNode : arSelectors) { if (L"bdo" == oNode.m_wsName && L"rtl" == oNode.GetAttributeValue(L"dir")) bBidirectional = true; else if (L"bdi" == oNode.m_wsName) bBidirectional = false; else if (L"pre" == oNode.m_wsName || L"nobr" == oNode.m_wsName || L"xmp" == oNode.m_wsName) bPreformatted = true; else if (L"q" == oNode.m_wsName) bQuotation = true; else if (L"span" == oNode.m_wsName) bAddSpaces = false; else if (L"del" == oNode.m_wsName) bDeleted = true; } const NSCSS::CCompiledStyle* pCompiledStyle{arSelectors.back().m_pCompiledStyle}; if (!bPreformatted && nullptr != arSelectors.back().m_pCompiledStyle) { // TODO::поведение должно быть немного разное (реализовать) switch(pCompiledStyle->m_oDisplay.GetWhiteSpace().ToInt()) { case NSCSS::NSProperties::EWhiteSpace::Pre: case NSCSS::NSProperties::EWhiteSpace::Pre_Wrap: case NSCSS::NSProperties::EWhiteSpace::Pre_Line: bPreformatted = true; default: break; } } if (!bPreformatted && wsText.end() == std::find_if_not(wsText.begin(), wsText.end(), [](wchar_t wchChar){ return iswspace(wchChar) && 0xa0 != wchChar;})) return false; std::wstring wsHeaderId; if (ElementInHeader(arSelectors)) wsHeaderId = StandardizeHeaderId(wsText); if(bBidirectional) std::reverse(wsText.begin(), wsText.end()); const bool bInT = m_arStates.top().m_bInT; OpenP(); WritePPr(arSelectors); std::wstring wsCrossId; if (!wsHeaderId.empty()) WriteEmptyBookmark(wsHeaderId); if (bDeleted) GetCurrentDocument()->WriteString(L""); NSStringUtils::CStringBuilder oRPr; std::wstring sRStyle; if (OpenR()) { sRStyle = WriteRPr(oRPr, arSelectors); WriteToStringBuilder(oRPr, *m_arStates.top().m_pCurrentDocument); } if (bQuotation) m_arStates.top().m_pCurrentDocument->WriteString(L"""); if (!bPreformatted && bAddSpaces && m_arStates.top().m_bInP && !m_arStates.top().m_bInR && !iswspace(wsText.front()) && !iswpunct(wsText.front()) && !m_arStates.top().m_bWasSpace) { if (pCompiledStyle->m_oDisplay.GetVAlign().Empty() || L"normal" == pCompiledStyle->m_oDisplay.GetVAlign().ToWString()) WriteSpace(); } if(bPreformatted) { size_t unBegin = 0, unEnd = wsText.find_first_of(L"\n\r\t"); while (std::wstring::npos != unBegin) { if (OpenR()) WriteToStringBuilder(oRPr, *m_arStates.top().m_pCurrentDocument); if (!bDeleted) OpenT(); else GetCurrentDocument()->WriteString(L""); if (unEnd == std::wstring::npos) { m_arStates.top().m_pCurrentDocument->WriteEncodeXmlString(wsText.c_str() + unBegin, wsText.length() - unBegin); break; } if (unBegin != unEnd) { m_arStates.top().m_pCurrentDocument->WriteEncodeXmlString(wsText.c_str() + unBegin, unEnd - unBegin); CloseT(); } if (L'\n' == wsText[unEnd]) m_arStates.top().m_pCurrentDocument->WriteString(L""); else if (L'\t' == wsText[unEnd]) m_arStates.top().m_pCurrentDocument->WriteString(L""); unBegin = unEnd + 1; unEnd = wsText.find_first_of(L"\n\r\t", unBegin); } } else { ReplaceSpaces(wsText); if (!wsText.empty() && L'\t' == wsText[0]) { m_arStates.top().m_pCurrentDocument->WriteString(L""); wsText.erase(0, 1); } if (!wsText.empty() && std::iswspace(wsText.front()) && m_arStates.top().m_bWasSpace) wsText.erase(0, 1); if (!bDeleted) OpenT(); else GetCurrentDocument()->WriteString(L""); if (bMergedText && !m_arStates.top().m_bWasSpace && bInT && !bPreformatted) m_arStates.top().m_pCurrentDocument->WriteEncodeXmlString(L" "); if (!wsText.empty()) { m_arStates.top().m_bWasSpace = std::iswspace(wsText.back()); m_arStates.top().m_pCurrentDocument->WriteEncodeXmlString(wsText); } } if (bQuotation) m_arStates.top().m_pCurrentDocument->WriteString(L"""); if (!bDeleted) CloseT(); else GetCurrentDocument()->WriteString(L""); if (!bMergedText) CloseR(); if (bDeleted) GetCurrentDocument()->WriteString(L""); return true; } void COOXMLWriter::WriteEmptyParagraph(bool bVahish, bool bInP) { if (!bInP) m_arStates.top().m_pCurrentDocument->WriteString(L""); m_arStates.top().m_pCurrentDocument->WriteString(L""); if (bVahish) m_arStates.top().m_pCurrentDocument->WriteString(L""); m_arStates.top().m_pCurrentDocument->WriteString(L""); if (!bInP) m_arStates.top().m_pCurrentDocument->WriteString(L""); } void COOXMLWriter::WriteSpace() { OpenR(); m_arStates.top().m_pCurrentDocument->WriteString(L" "); CloseR(); m_arStates.top().m_bWasSpace = true; } void COOXMLWriter::WriteEmptyBookmark(const std::wstring& wsId) { const std::wstring wsCrossId{WriteBookmark(wsId)}; if (wsCrossId.empty()) return; m_arStates.top().m_pCurrentDocument->WriteString(L"WriteString(wsCrossId); m_arStates.top().m_pCurrentDocument->WriteString(L"\"/>"); } std::wstring COOXMLWriter::AddLiteBookmark() { return m_mBookmarks.insert(std::make_pair(L"Bookmark" + std::to_wstring(m_mBookmarks.size() + 1), m_mBookmarks.size() + 1)).first->first; } std::wstring COOXMLWriter::WriteBookmark(const std::wstring& wsId) { const std::wstring sCrossId = std::to_wstring(m_mBookmarks.size() + 1); std::wstring sName; if (m_mBookmarks.end() != m_mBookmarks.find(wsId)) sName = wsId + L"_" + std::to_wstring(++m_mBookmarks[wsId]); else { const anchors_map::const_iterator itFound{m_mAnchors.find(wsId)}; if (m_mAnchors.end() != itFound) sName = itFound->second; else sName = AddAnchor(wsId); m_mBookmarks.insert({wsId, 1}); } m_arStates.top().m_pCurrentDocument->WriteString(L"WriteString(sCrossId); m_arStates.top().m_pCurrentDocument->WriteString(L"\" w:name=\""); m_arStates.top().m_pCurrentDocument->WriteEncodeXmlString(sName); m_arStates.top().m_pCurrentDocument->WriteString(L"\"/>"); return sCrossId; } std::wstring COOXMLWriter::AddAnchor(const std::wstring& wsAnchorValue) { const anchors_map::iterator itFound = m_mAnchors.find(wsAnchorValue); if (m_mAnchors.end() != itFound) return itFound->second; const std::wstring wsAnchorId{L"anchor-" + std::to_wstring(m_mAnchors.size() + 1)}; m_mAnchors[wsAnchorValue] = wsAnchorId; return wsAnchorId; } void COOXMLWriter::WriteImage(const TImageData& oImageData, const std::wstring& wsId) { OpenR(); XmlString* pCurrentDocument{m_arStates.top().m_pCurrentDocument}; // Пишем в document.xml if (oImageData.ZeroSpaces()) { pCurrentDocument->WriteString(L"WriteString(std::to_wstring(oImageData.m_unWidth)); pCurrentDocument->WriteString(L"\" cy=\""); pCurrentDocument->WriteString(std::to_wstring(oImageData.m_unHeight)); pCurrentDocument->WriteString(L"\"/>WriteString(wsId); pCurrentDocument->WriteString(L"\" name=\"Picture " + wsId + L"\"/>WriteString(wsId); pCurrentDocument->WriteString(L"\" name=\"Picture " + wsId + L"\"/>WriteString(wsId); pCurrentDocument->WriteString(L"\"/>WriteString(std::to_wstring(oImageData.m_unWidth)); pCurrentDocument->WriteString(L"\" cy=\""); pCurrentDocument->WriteString(std::to_wstring(oImageData.m_unHeight)); pCurrentDocument->WriteString(L"\"/>"); } else { pCurrentDocument->WriteString(L""); pCurrentDocument->WriteString(L""); pCurrentDocument->WriteString(L""); pCurrentDocument->WriteString(L"" + oImageData.m_wsAlign + L""); pCurrentDocument->WriteString(L"0"); pCurrentDocument->WriteString(L""); pCurrentDocument->WriteString(L""); pCurrentDocument->WriteString(L""); pCurrentDocument->WriteString(L""); pCurrentDocument->WriteString(L""); pCurrentDocument->WriteString(L""); pCurrentDocument->WriteString(L""); pCurrentDocument->WriteString(L""); pCurrentDocument->WriteString(L""); pCurrentDocument->WriteString(L""); pCurrentDocument->WriteString(L""); pCurrentDocument->WriteString(L""); pCurrentDocument->WriteString(L""); pCurrentDocument->WriteString(L"00"); pCurrentDocument->WriteString(L""); } CloseR(); } void COOXMLWriter::WriteAlternativeImage(const std::wstring& wsAlt, const std::wstring& wsSrc, const TImageData& oImageData) { m_oDocXmlRels.WriteString(L""); const bool bOpenedP{OpenP()}; WriteEmptyImage((0 != oImageData.m_unWidth) ? oImageData.m_unWidth : DEFAULT_IMAGE_WIDTH, (0 != oImageData.m_unHeight) ? oImageData.m_unHeight : DEFAULT_IMAGE_HEIGHT, L"", wsAlt); if (bOpenedP) CloseP(); } void COOXMLWriter::WriteEmptyImage(int nWidth, int nHeight, const std::wstring& wsName, const std::wstring& wsDescr) { OpenR(); #define WRITE_ENCODE_ARGUMENT(argumentName, argumentValue) \ m_arStates.top().m_pCurrentDocument->WriteString(L" " + std::wstring(argumentName) + L"=\""); \ m_arStates.top().m_pCurrentDocument->WriteEncodeXmlString(argumentValue); \ m_arStates.top().m_pCurrentDocument->WriteString(L"\"") m_arStates.top().m_pCurrentDocument->WriteString(L""); m_arStates.top().m_pCurrentDocument->WriteString(L"WriteString(L"/>"); m_arStates.top().m_pCurrentDocument->WriteString(L""); m_arStates.top().m_pCurrentDocument->WriteString(L""); m_arStates.top().m_pCurrentDocument->WriteString(L"WriteString(L"/>"); m_arStates.top().m_pCurrentDocument->WriteString(L""); m_arStates.top().m_pCurrentDocument->WriteString(L""); m_arStates.top().m_pCurrentDocument->WriteString(L""); m_arStates.top().m_pCurrentDocument->WriteString(L""); CloseR(); } void COOXMLWriter::WriteImageRels(const std::wstring& wsImageId, const std::wstring& wsImageName) { m_oDocXmlRels.WriteString(L""); } std::wstring COOXMLWriter::GetStyle(const NSCSS::CCompiledStyle& oStyle, bool bParagraphStyle) { if (!((bParagraphStyle) ? m_oXmlStyle.WritePStyle(oStyle) : m_oXmlStyle.WriteRStyle(oStyle))) return std::wstring(); m_oStylesXml.WriteString(m_oXmlStyle.GetStyle()); return m_oXmlStyle.GetIdAndClear(); } void COOXMLWriter::UpdatePageStyle(const std::vector& arSelectors) { if (nullptr != m_pStylesCalculator) m_pStylesCalculator->CalculatePageStyle(m_oPageData, arSelectors); } void COOXMLWriter::SetBaseFont(const std::wstring& wsFontStyles) { if (nullptr != m_pStylesCalculator) m_pStylesCalculator->AddStyles(wsFontStyles); } void COOXMLWriter::SetDivId(const std::wstring& wsDivId) { m_arDivId.push(wsDivId); m_bWasDivs = true; } void COOXMLWriter::RollBackDivId() { if (!m_arDivId.empty()) m_arDivId.pop(); } void COOXMLWriter::IncreaseListId() { ++m_nListId; } int COOXMLWriter::GetListId() const { return m_nListId; } std::wstring COOXMLWriter::FindFootnote(const std::wstring& wsId) { const std::map::const_iterator itFound{m_mFootnotes.find(wsId)}; return (itFound != m_mFootnotes.cend()) ? itFound->second : std::wstring(); } void COOXMLWriter::OpenFootnote(const std::wstring& wsFootnoteID) { m_oNoteXml.WriteString(L""); } void COOXMLWriter::CloseFootnote() { m_oNoteXml.WriteString(L""); } XmlString& COOXMLWriter::GetStylesXml() { return m_oStylesXml; } XmlString& COOXMLWriter::GetDocRelsXml() { return m_oDocXmlRels; } XmlString& COOXMLWriter::GetNotesRelsXml() { return m_oNoteXmlRels; } XmlString& COOXMLWriter::GetDocumentXml() { return m_oDocXml; } XmlString& COOXMLWriter::GetNotesXml() { return m_oNoteXml; } XmlString& COOXMLWriter::GetNumberingXml() { return m_oNumberXml; } XmlString& COOXMLWriter::GetWebSettingsXml() { return m_oWebSettings; } XmlString* COOXMLWriter::GetCurrentDocument() const { return m_arStates.top().m_pCurrentDocument; } bool COOXMLWriter::SupportNestedTables() const { return true; } const NSCSS::NSProperties::CPage* COOXMLWriter::GetPageData() const { return &m_oPageData; } NSFonts::IApplicationFonts* COOXMLWriter::GetFonts() { if (nullptr == m_pFonts) { m_pFonts = NSFonts::NSApplication::Create(); if (NULL != m_pFonts) m_pFonts->Initialize(); } return m_pFonts; } std::wstring COOXMLWriter::GetMediaDir() const { return ((nullptr != m_pDstPath) ? *m_pDstPath : std::wstring()) + L"/word/media/"; } std::wstring COOXMLWriter::GetTempDir() const { return (nullptr != m_pTempDir) ? *m_pTempDir : std::wstring(); } std::wstring COOXMLWriter::GetSrcPath() const { return (nullptr != m_pSrcPath) ? *m_pSrcPath : std::wstring(); } std::wstring COOXMLWriter::GetBasePath() const { return (nullptr != m_pBasePath) ? *m_pBasePath : std::wstring(); } std::wstring COOXMLWriter::GetCorePath() const { return (nullptr != m_pCorePath) ? *m_pCorePath : std::wstring(); } inline bool ElementInTable(const std::vector& arSelectors) { return arSelectors.crend() != std::find_if(arSelectors.crbegin(), arSelectors.crend(), [](const NSCSS::CNode& oNode) { return L"table" == oNode.m_wsName; }); } inline bool ElementInHeader(const std::vector arSelsectors) { for (const NSCSS::CNode& oNode : arSelsectors) { if (2 == oNode.m_wsName.length() && L'h' != oNode.m_wsName[0] && (L'1' == oNode.m_wsName[1] || L'2' == oNode.m_wsName[1] || L'3' == oNode.m_wsName[1] || L'4' == oNode.m_wsName[1] || L'5' == oNode.m_wsName[1] || L'6' == oNode.m_wsName[1])) return true; } return false; } std::wstring StandardizeHeaderId(const std::wstring& wsHeader) { if (wsHeader.empty()) return std::wstring(); std::wstring result; result.reserve(wsHeader.size()); // Флаг, указывающий, был ли предыдущий символ дефисом bool prevWasHyphen = false; bool inWhitespaceSequence = false; wchar_t lowerC; for (wchar_t c : wsHeader) { // Приведение к нижнему регистру lowerC = std::tolower(c); // Проверяем, является ли символ буквой или цифрой if (std::iswalnum(lowerC)) { result.push_back(lowerC); prevWasHyphen = false; inWhitespaceSequence = false; } // Проверяем, является ли символ пробельным (пробел, табуляция и т.д.) else if (std::iswspace(lowerC)) { // Заменяем последовательности пробельных символов на один дефис if (!inWhitespaceSequence && !result.empty()) { result.push_back(L'-'); inWhitespaceSequence = true; } } // Проверяем, является ли символ дефисом или подчеркиванием else if (c == L'-' || c == L'_') { // Добавляем дефис, если предыдущий символ не был дефисом if (!prevWasHyphen && !result.empty()) { result.push_back(L'-'); prevWasHyphen = true; } inWhitespaceSequence = false; } // Все остальные символы (знаки препинания) пропускаем // Но если это буква в Unicode, мы можем её обработать else if (std::iswalpha(lowerC)) { // Для Unicode-символов, которые являются буквами result.push_back(lowerC); prevWasHyphen = false; inWhitespaceSequence = false; } // Остальные символы игнорируем } // Удаляем дефисы в начале и конце size_t start = 0; size_t end = result.length(); while (start < end && result[start] == L'-') ++start; while (end > start && result[end - 1] == L'-') --end; // Удаляем последовательные дефисы std::wstring finalResult; finalResult.reserve(end - start); bool lastWasHyphen = false; for (size_t i = start; i < end; i++) { if (result[i] == L'-') { if (!lastWasHyphen) { finalResult.push_back(L'-'); lastWasHyphen = true; } } else { finalResult.push_back(result[i]); lastWasHyphen = false; } } return finalResult; } inline int CalculateFontChange(const std::vector& arSelectors) { int nFontChange = 0; for (const NSCSS::CNode& oNode : arSelectors) { if (L"big" == oNode.m_wsName) ++nFontChange; else if (L"small" == oNode.m_wsName) --nFontChange; } return nFontChange; } inline UINT GetFontSizeLevel(UINT unFontSize) { if (unFontSize <= 18) return 1; else if (unFontSize <= 22) return 2; else if (unFontSize <= 26) return 3; else if (unFontSize <= 30) return 4; else if (unFontSize <= 40) return 5; else if (unFontSize <= 59) return 6; else return 7; } inline UINT GetFontSizeByLevel(UINT unLevel) { if (0 == unLevel) return 15; else if (unLevel > 7) return 72; switch (unLevel) { case 1: return 15; case 2: return 20; case 3: return 24; case 4: return 27; case 5: return 36; case 6: return 48; case 7: return 72; } return 24; } inline void ReplaceSpaces(std::wstring& wsValue) { // boost::wregex oRegex(L"\\s+"); // wsValue = boost::regex_replace(wsValue, oRegex, L" "); std::wstring::const_iterator itBegin = std::find_if(wsValue.cbegin(), wsValue.cend(), [](wchar_t wchValue){ return std::iswspace(wchValue) && 0xa0 != wchValue; }); std::wstring::const_iterator itEnd; while (wsValue.cend() != itBegin) { itEnd = std::find_if(itBegin, wsValue.cend(), [](wchar_t wchValue){ return !std::iswspace(wchValue) || 0xa0 == wchValue; }); wsValue.replace(itBegin, itEnd, L" "); itBegin = std::find_if(itBegin + 1, wsValue.cend(), [](wchar_t wchValue){ return std::iswspace(wchValue) && 0xa0 != wchValue; }); } } }