#include "Converter2OOXML.h" #include #include "../../../DesktopEditor/common/File.h" #include "../../../DesktopEditor/common/Directory.h" #include "../../../DesktopEditor/common/SystemUtils.h" #include "../../../DesktopEditor/raster/BgraFrame.h" #include "../../../OfficeUtils/src/OfficeUtils.h" #include "../../../DesktopEditor/graphics/pro/Graphics.h" #include "../Paragraph/ParaText.h" #include "../Paragraph/CtrlTable.h" #include "../Paragraph/CtrlEqEdit.h" #include "../Paragraph/CtrlShapeArc.h" #include "../Paragraph/CtrlShapePolygon.h" #include "../Paragraph/CtrlShapeCurve.h" #include "../HWPElements/HWPRecordParaShape.h" #include "../HWPElements/HWPRecordCharShape.h" #include "Transform.h" #define PARA_SPACING_SCALE 0.85 #define SPACING_SCALE_MS_WORD 1.21 namespace HWP { enum class EShapeObjectType { Arc, Curve, Ellipse, Line, Ole, Picture, Polygon, Rectangle, Unknown }; EShapeObjectType GetShapeObjectType(const std::wstring& wsID) { if (L"cra$" == wsID) return EShapeObjectType::Arc; if (L"ruc$" == wsID) return EShapeObjectType::Curve; if (L"lle$" == wsID) return EShapeObjectType::Ellipse; if (L"nil$" == wsID || L"loc$" == wsID) return EShapeObjectType::Line; if (L"elo$" == wsID) return EShapeObjectType::Ole; if (L"cip$" == wsID) return EShapeObjectType::Picture; if (L"lop$" == wsID) return EShapeObjectType::Polygon; if (L"cer$" == wsID) return EShapeObjectType::Rectangle; return EShapeObjectType::Unknown; } CConverter2OOXML::CConverter2OOXML() : m_pContext(nullptr), m_ushShapeCount(0), m_ushPageCount(1), m_ushTableCount(0), m_ushEquationCount(0), m_ushBookmarkCount(0) {} CConverter2OOXML::~CConverter2OOXML() {} void CConverter2OOXML::SetContext(CWriterContext* pContext) { m_pContext = pContext; } void CConverter2OOXML::SetTempDirectory(const HWP_STRING& sTempDirectory) { m_sTempDirectory = sTempDirectory; m_oOleConverter.SetTempDir(m_sTempDirectory); } void CConverter2OOXML::CreateEmptyFiles() { NSDirectory::CreateDirectory(m_sTempDirectory + L"/_rels"); NSDirectory::CreateDirectory(m_sTempDirectory + L"/docProps"); NSDirectory::CreateDirectory(m_sTempDirectory + L"/word"); NSDirectory::CreateDirectory(m_sTempDirectory + L"/word/_rels"); NSDirectory::CreateDirectory(m_sTempDirectory + L"/word/media"); NSDirectory::CreateDirectory(m_sTempDirectory + L"/word/theme"); // theme1.xml std::wstring wsTheme = L""; NSFile::CFileBinary oThemeWriter; if (oThemeWriter.CreateFileW(m_sTempDirectory + L"/word/theme/theme1.xml")) { oThemeWriter.WriteStringUTF8(wsTheme); oThemeWriter.CloseFile(); } // app.xml std::wstring wsApplication = NSSystemUtils::GetEnvVariable(NSSystemUtils::gc_EnvApplicationName); if (wsApplication.empty()) wsApplication = NSSystemUtils::gc_EnvApplicationNameDefault; std::wstring wsApp = L""; wsApp += wsApplication; wsApp += L"0falsefalsefalsefalse"; NSFile::CFileBinary oAppWriter; if (oAppWriter.CreateFileW(m_sTempDirectory + L"/docProps/app.xml")) { oAppWriter.WriteStringUTF8(wsApp); oAppWriter.CloseFile(); } // .rels std::wstring wsRels = L""; NSFile::CFileBinary oRelsWriter; if (oRelsWriter.CreateFileW(m_sTempDirectory + L"/_rels/.rels")) { oRelsWriter.WriteStringUTF8(wsRels); oRelsWriter.CloseFile(); } // fontTable.xml std::wstring wsFontTable = L""; NSFile::CFileBinary oFontTableWriter; if (oFontTableWriter.CreateFileW(m_sTempDirectory + L"/word/fontTable.xml")) { oFontTableWriter.WriteStringUTF8(wsFontTable); oFontTableWriter.CloseFile(); } // settings.xml std::wstring wsSettings = L""; NSFile::CFileBinary oSettingsWriter; if (oSettingsWriter.CreateFileW(m_sTempDirectory + L"/word/settings.xml")) { oSettingsWriter.WriteStringUTF8(wsSettings); oSettingsWriter.CloseFile(); } // core.xml std::wstring wsCore = L""; wsCore += L""; NSFile::CFileBinary oCoreWriter; if (oCoreWriter.CreateFileW(m_sTempDirectory + L"/docProps/core.xml")) { oCoreWriter.WriteStringUTF8(wsCore); oCoreWriter.CloseFile(); } FillDefaultData(); } void CConverter2OOXML::FillDefaultData() { // Заполняем начало файлов AddRelationship(L"styles", L"styles.xml"); AddRelationship(L"settings", L"settings.xml"); AddRelationship(L"webSettings", L"webSettings.xml"); AddRelationship(L"fontTable", L"fontTable.xml"); AddRelationship(L"theme", L"theme/theme1.xml"); AddContentType(L"document.xml", L"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"); AddContentType(L"styles.xml", L"application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"); AddContentType(L"settings.xml", L"application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml"); AddContentType(L"webSettings.xml", L"application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml"); AddContentType(L"fontTable.xml", L"application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml"); AddContentType(L"theme/theme1.xml", L"application/vnd.openxmlformats-officedocument.theme+xml"); m_oNoteXmlRels.WriteString(L""); m_oDocXml .WriteString(L""); m_oWebSettings.WriteString(L""); } void CConverter2OOXML::Close() { // Дописываем концы файлов m_oDocXml.WriteString(L""); NSFile::CFileBinary oDocumentWriter; if (oDocumentWriter.CreateFileW(m_sTempDirectory + L"/word/document.xml")) { oDocumentWriter.WriteStringUTF8(m_oDocXml.GetData()); oDocumentWriter.CloseFile(); } if (m_oFootnoteConverter.SaveToFile(m_sTempDirectory + FILE_SEPARATOR_STR + L"word" + FILE_SEPARATOR_STR)) { AddRelationship(L"footnotes", L"footnotes.xml"); AddRelationship(L"endnotes", L"endnotes.xml"); AddContentType(L"footnotes.xml", L"application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml"); AddContentType(L"endnotes.xml", L"application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml"); } // styles.xml m_oStyleConverter.SaveToFile(m_sTempDirectory + FILE_SEPARATOR_STR + L"word" + FILE_SEPARATOR_STR); // numbering.xml if (m_oNumberingConverter.SaveToFile(m_sTempDirectory + FILE_SEPARATOR_STR + L"word" + FILE_SEPARATOR_STR)) { AddRelationship(L"numbering", L"numbering.xml"); AddContentType(L"numbering.xml", L"application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml"); } // webSettings.xml m_oWebSettings.WriteString(L""); NSFile::CFileBinary oWebSettingsWriter; if (oWebSettingsWriter.CreateFileW(m_sTempDirectory + L"/word/webSettings.xml")) { oWebSettingsWriter.WriteStringUTF8(m_oWebSettings.GetData()); oWebSettingsWriter.CloseFile(); } // [Content_Types].xml NSFile::CFileBinary oContentTypeWriter; if (oContentTypeWriter.CreateFileW(m_sTempDirectory + L"/[Content_Types].xml")) { oContentTypeWriter.WriteStringUTF8(L""); oContentTypeWriter.WriteStringUTF8(L""); for (const TContentType& oContentType : m_arDefaultContentType) oContentTypeWriter.WriteStringUTF8(L""); for (const TContentType& oContentType : m_arContentTypes) oContentTypeWriter.WriteStringUTF8(L""); oContentTypeWriter.WriteStringUTF8(L""); oContentTypeWriter.WriteStringUTF8(L""); oContentTypeWriter.WriteStringUTF8(L""); oContentTypeWriter.CloseFile(); } NSFile::CFileBinary oRelsWriter; if (oRelsWriter.CreateFileW(m_sTempDirectory + L"/word/_rels/document.xml.rels")) { oRelsWriter.WriteStringUTF8(L""); for (const TRelationship& oRelationship : m_arRelationships) { oRelsWriter.WriteStringUTF8(L""); else oRelsWriter.WriteStringUTF8(L"/>"); } oRelsWriter.WriteStringUTF8(L""); oRelsWriter.CloseFile(); } } void CConverter2OOXML::Convert() { if (nullptr == m_pContext) return; TConversionState oState; std::vector arSetcions{m_pContext->GetSections()}; for (const CHWPSection* pSection : arSetcions) { const bool bIsLastSection = pSection == arSetcions.back(); for (const CHWPPargraph *pPara : pSection->GetParagraphs()) WriteParagraph(pPara, m_oDocXml, oState); if (!bIsLastSection) m_oDocXml.WriteString(L""); WriteSectionSettings(oState); if (!bIsLastSection) m_oDocXml.WriteString(L""); ++oState.m_ushSecdIndex; } } bool CConverter2OOXML::IsRasterFormat(const HWP_STRING& sFormat) { return L"png" == sFormat || L"jpg" == sFormat || L"jpeg" == sFormat || L"bmp" == sFormat; } void CConverter2OOXML::WriteCharacter(const CCtrlCharacter* pCharacter, short shParaShapeID, short shParaStyleID, NSStringUtils::CStringBuilder& oBuilder, TConversionState& oState) { if (nullptr == pCharacter) return; switch (pCharacter->GetType()) { case ECtrlCharType::PARAGRAPH_BREAK: { if (!oState.m_bOpenedP) OpenParagraph(shParaShapeID, shParaStyleID, oBuilder, oState); oState.m_bOpenedP = false; oBuilder.WriteString(L""); break; } case ECtrlCharType::LINE_BREAK: { oState.m_eBreakType = TConversionState::EBreakType::TextWrapping; break; } case ECtrlCharType::HARD_HYPHEN: case ECtrlCharType::HARD_SPACE: break; } } void CConverter2OOXML::WriteShape(const CCtrlGeneralShape* pShape, short shParaShapeID, short shParaStyleID, NSStringUtils::CStringBuilder& oBuilder, TConversionState& oState) { if (nullptr == pShape || oState.m_bInTextBox) return; switch (pShape->GetShapeType()) { case EShapeType::Arc: case EShapeType::Ellipse: case EShapeType::Rect: case EShapeType::Line: case EShapeType::Polygon: case EShapeType::Curve: { WriteGeometryShape(pShape, shParaShapeID, shParaStyleID, oBuilder, oState); break; } case EShapeType::Pic: { WritePicture((const CCtrlShapePic*)pShape, shParaShapeID, shParaStyleID, oBuilder, oState); break; } case EShapeType::EqEdit: { WriteEqEditShape((const CCtrlEqEdit*)pShape, shParaShapeID, shParaStyleID, oBuilder, oState); break; } case EShapeType::Ole: { WriteOleShape((const CCtrlShapeOle*)pShape, shParaShapeID, shParaStyleID, oBuilder, oState); break; } case EShapeType::Video: { WriteVideo((const CCtrlShapeVideo*)pShape, shParaShapeID, shParaStyleID, oBuilder, oState); break; } case EShapeType::GeneralShape: case EShapeType::ConnectLine: case EShapeType::Container: case EShapeType::TextArt: break; } } void CConverter2OOXML::WriteNote(const CCtrlNote* pNote, short shParaShapeID, NSStringUtils::CStringBuilder& oBuilder, TConversionState& oState) { oBuilder.WriteString(L""); CRunnerStyle oRunnerStyle; oRunnerStyle.SetVerticalAlign(EVerticalAlignRun::Superscript); WriteRunnerStyle(oState.m_ushLastCharShapeId, oBuilder, oState, oRunnerStyle); oBuilder.WriteString(m_oFootnoteConverter.CreateNote((const CCtrlNote*)pNote, *this)); oBuilder.WriteString(L""); } void CConverter2OOXML::WriteField(const CCtrlField* pShape, short shParaShapeID, short shParaStyleID, NSStringUtils::CStringBuilder& oBuilder, TConversionState& oState) { if (nullptr == pShape) return; switch (pShape->GetType()) { case EFieldType::Hyperlink: { HWP_STRING wsHref = pShape->GetStringParam(L"Path"); if(wsHref.empty()) { HWP_STRING sCommand = pShape->GetStringParam(L"Command"); if (sCommand.empty()) sCommand = pShape->GetCommand(); sCommand = sCommand.substr(0, sCommand.find(L';')); size_t unFound = sCommand.find(L'\\'); while (HWP_STRING::npos != unFound) { sCommand.erase(unFound, 1); unFound = sCommand.find(L'\\', unFound); } wsHref = sCommand; } if (wsHref.empty()) break; const HWP_STRING wsID = AddRelationship(L"hyperlink", wsHref); OpenParagraph(shParaShapeID, shParaStyleID, oBuilder, oState); oBuilder.WriteString(L""); oState.m_mOpenField.insert(std::make_pair(pShape->GetInstanceID(), pShape)); break; } case EFieldType::HyperlinkClosing: { oBuilder.WriteString(L""); break; } case EFieldType::Bookmark: { oBuilder.WriteString(L"GetStringParam(L"bookmarkname")); oBuilder.WriteString(L"\"/>"); oState.m_arOpenedBookmarks.push(m_ushBookmarkCount++); oState.m_mOpenField.insert(std::make_pair(pShape->GetInstanceID(), pShape)); break; } case EFieldType::BookmarkClosing: { if (oState.m_arOpenedBookmarks.empty()) break; oBuilder.WriteString(L""); oState.m_arOpenedBookmarks.pop(); break; } //TODO:: как-будто хочется определить тип закрывающей field на этапе парса hwpx case EFieldType::Unknown: { std::map::const_iterator itFound = oState.m_mOpenField.find(pShape->GetInstanceID()); if (oState.m_mOpenField.cend() != itFound) { switch (itFound->second->GetType()) { case EFieldType::Hyperlink: { oBuilder.WriteString(L""); break; } case EFieldType::Bookmark: { if (oState.m_arOpenedBookmarks.empty()) break; oBuilder.WriteString(L""); oState.m_arOpenedBookmarks.pop(); break; } default: break; } } break; } } } void CConverter2OOXML::WriteCaption(const CCtrlCommon* pCtrlCommon, NSStringUtils::CStringBuilder& oBuilder, TConversionState& oState) { if (nullptr == pCtrlCommon || !pCtrlCommon->HaveCaption()) return; for (const CCapParagraph* pCapParagraph : pCtrlCommon->GetCaptionParas()) WriteParagraph(pCapParagraph, oBuilder, oState); } void CConverter2OOXML::WriteParagraph(const CHWPPargraph* pParagraph, NSStringUtils::CStringBuilder& oBuilder, TConversionState& oState) { if (nullptr == pParagraph) return; CloseParagraph(oBuilder, oState); if (0 < pParagraph->GetBreakType()) { if ((0x04 == (pParagraph->GetBreakType() & 0x04)) && (0 < oState.m_unParaIndex)) { oState.m_eBreakType = TConversionState::EBreakType::Page; // oBuilder.WriteString(L""); ++m_ushPageCount; } else if (0x08 == (pParagraph->GetBreakType() & 0x08)) oState.m_eBreakType = TConversionState::EBreakType::Column; // oBuilder.WriteString(L""); } ++oState.m_unParaIndex; for (const CCtrl* pCtrl : pParagraph->GetCtrls()) { switch (pCtrl->GetCtrlType()) { case ECtrlObjectType::ParaText: { WriteText((const CParaText*)pCtrl, pParagraph->GetRangeTags(), pParagraph->GetShapeID(), pParagraph->GetStyleID(), oBuilder, oState); // WriteText(((const CParaText*)pCtrl)->GetText(), pParagraph->GetShapeID(), ((const CParaText*)pCtrl)->GetCharShapeID(), oBuilder, oState); break; } case ECtrlObjectType::Character: { WriteCharacter((const CCtrlCharacter*)pCtrl, pParagraph->GetShapeID(), pParagraph->GetStyleID(), oBuilder, oState); break; } case ECtrlObjectType::Shape: { WriteShape((const CCtrlGeneralShape*)pCtrl, pParagraph->GetShapeID(), pParagraph->GetStyleID(), oBuilder, oState); break; } case ECtrlObjectType::Table: { WriteTable((const CCtrlTable*)pCtrl, pParagraph->GetShapeID(), pParagraph->GetStyleID(), oBuilder, oState); break; } case ECtrlObjectType::Note: { WriteNote((const CCtrlNote*)pCtrl, pParagraph->GetShapeID(), oBuilder, oState); break; } case ECtrlObjectType::SectionDef: { oState.m_pSectionDef = (const CCtrlSectionDef*)pCtrl; break; } case ECtrlObjectType::ColumnDef: { oState.m_pColumnDef = (const CCtrlColumnDef*)(pCtrl); break; } case ECtrlObjectType::AutoNumber: { WriteAutoNumber((const CCtrlAutoNumber*)pCtrl, pParagraph->GetShapeID(), pParagraph->GetStyleID(), oState.m_ushLastCharShapeId, oBuilder, oState); break; } case ECtrlObjectType::Field: { WriteField((const CCtrlField*)pCtrl, pParagraph->GetShapeID(), pParagraph->GetStyleID(), oBuilder, oState); break; } case ECtrlObjectType::HeadFoot: { if (EHanType::HWPX == m_pContext->GetType()) oState.m_arCtrlsHeadFoot.push_back((const CCtrlHeadFoot*)pCtrl); break; } default: break; } } CloseParagraph(oBuilder, oState); } void CConverter2OOXML::WriteParagraphProperties(short shParaShapeID, short shParaStyleID, NSStringUtils::CStringBuilder& oBuilder, TConversionState& oState) { oBuilder.WriteString(L""); WriteParaShapeProperties(shParaShapeID, shParaStyleID, oBuilder, oState); oBuilder.WriteString(L""); } void CConverter2OOXML::WriteParaShapeProperties(short shParaShapeID, short shParaStyleID, NSStringUtils::CStringBuilder& oBuilder, TConversionState& oState) { if (nullptr == m_pContext) return; const std::wstring wsStyleId = m_oStyleConverter.CreateStyle(shParaShapeID, shParaStyleID, *m_pContext, oState); if (!wsStyleId.empty()) { oBuilder.WriteString(L""); } if (m_oStyleConverter.GetLastParaShapeId() != shParaShapeID) m_oStyleConverter.WriteDifferenceParagraphStyles(m_oStyleConverter.GetLastParaShapeId(), shParaShapeID, *m_pContext, oBuilder); const CHWPRecordParaShape* pParaShape= dynamic_cast(m_pContext->GetParaShape(shParaShapeID)); if (nullptr == pParaShape) return; switch (pParaShape->GetHeadingType()) { case EHeadingType::NUMBER: case EHeadingType::BULLET: { const int nNumId = m_oNumberingConverter.CreateNumbering(dynamic_cast(m_pContext->GetNumbering(pParaShape->GetHeadingIdRef())), pParaShape->GetHeadingType()); if (0 == nNumId) break; oBuilder.WriteString(L""); oBuilder.WriteString(L"GetHeadingLevel()) + L"\"/>"); oBuilder.WriteString(L""); oBuilder.WriteString(L""); break; } case EHeadingType::OUTLINE: case EHeadingType::NONE: break; } } void CConverter2OOXML::WriteTable(const CCtrlTable* pTable, short shParaShapeID, short shParaStyleID, NSStringUtils::CStringBuilder& oBuilder, TConversionState& oState) { if (nullptr == pTable || pTable->Empty()) return; CloseParagraph(oBuilder, oState); ++m_ushTableCount; oBuilder.WriteString(L""); WriteTableProperties(pTable, shParaShapeID, shParaStyleID, oBuilder, oState); std::vector>> m_arrCells(pTable->GetRows()); for (unsigned int unRowIndex = 0; unRowIndex < pTable->GetRows(); ++unRowIndex) { m_arrCells[unRowIndex].resize(pTable->GetCols()); for (unsigned int unColIndex = 0; unColIndex < pTable->GetCols(); ++unColIndex) m_arrCells[unRowIndex][unColIndex] = std::make_pair(ECellCreator::NOT_CREATED, nullptr); } for (unsigned int unCellIndex = 0; unCellIndex < pTable->GetCountCells(); ++unCellIndex) { const CTblCell* pCell = pTable->GetCell(unCellIndex); if (nullptr == pCell || pCell->GetRowAddr() >= pTable->GetRows() || pCell->GetColAddr() >= pTable->GetCols()) continue; m_arrCells[pCell->GetRowAddr()][pCell->GetColAddr()] = std::make_pair(ECellCreator::FILE, pCell); } for (unsigned int unRowIndex = 0; unRowIndex < pTable->GetRows(); ++unRowIndex) { for (unsigned int unColIndex = 0; unColIndex < pTable->GetCols(); ++unColIndex) { std::pair oValue = m_arrCells[unRowIndex][unColIndex]; if (ECellCreator::NOT_CREATED == oValue.first) continue; if (ECellCreator::FILE == oValue.first) { if (1 != oValue.second->GetRowSpan()) { for (unsigned int unIndex = 1; unIndex < oValue.second->GetRowSpan() && unRowIndex + unIndex < pTable->GetRows(); ++unIndex) m_arrCells[unRowIndex + unIndex][unColIndex] = std::make_pair(ECellCreator::EMPTY, oValue.second); } } unColIndex += oValue.second->GetColSpan() - 1; } } //TODO:: в случаях, когда есть пустые столбцы необходимо добавить возможность удаления данных столбцов // Например для матрицы 3x2, у которой значения есть только в 2x2, необходимо удалить последний столбец for (unsigned int unRowIndex = 0; unRowIndex < pTable->GetRows(); ++unRowIndex) { oBuilder.WriteString(L""); int nHeight = 0; for (unsigned int unColIndex = 0; unColIndex < pTable->GetCols(); ++unColIndex) { std::pair oValue = m_arrCells[unRowIndex][unColIndex]; if (ECellCreator::FILE == oValue.first && 1 == oValue.second->GetRowSpan()) nHeight = (std::max)(nHeight, oValue.second->GetHeight()); } oBuilder.WriteString(L""); oBuilder.WriteString(L""); oBuilder.WriteString(L""); for (unsigned int unColIndex = 0; unColIndex < pTable->GetCols(); ++unColIndex) { std::pair oValue = m_arrCells[unRowIndex][unColIndex]; WriteCell(oValue.second, oBuilder, oState, oValue.first); } oBuilder.WriteString(L""); } oBuilder.WriteString(L""); } void CConverter2OOXML::WriteTableProperties(const CCtrlTable* pTable, short shParaShapeID, short shParaStyleID, NSStringUtils::CStringBuilder& oBuilder, TConversionState& oState) { if (nullptr == pTable) return; oBuilder.WriteString(L""); oBuilder.WriteString(L""); WriteParaShapeProperties(shParaShapeID, shParaStyleID, oBuilder, oState); const CHWPRecordBorderFill* pBorderFill = nullptr; if (nullptr != m_pContext) pBorderFill = dynamic_cast(m_pContext->GetBorderFill(pTable->GetBorderFillID())); if (nullptr == pBorderFill) { oBuilder.WriteString(L""); return; } oBuilder.WriteString(L""); } void CConverter2OOXML::WriteCell(const CTblCell* pCell, NSStringUtils::CStringBuilder& oBuilder, TConversionState& oState, ECellCreator eCellCreator) { if (nullptr == pCell) return; oBuilder.WriteString(L""); oBuilder.WriteString(L""); oBuilder.WriteString(L"GetWidth())) + L"\" w:type=\"dxa\"/>"); if (1 != pCell->GetColSpan()) oBuilder.WriteString(L"GetColSpan()) + L"\"/>"); if (1 != pCell->GetRowSpan()) oBuilder.WriteString(L""); WriteCellProperties(pCell->GetBorderFillID(), oBuilder); switch(pCell->GetVertAlign()) { case EVertAlign::TOP: oBuilder.WriteString(L""); break; case EVertAlign::CENTER: oBuilder.WriteString(L""); break; case EVertAlign::BOTTOM: oBuilder.WriteString(L""); break; case EVertAlign::INSIDE: case EVertAlign::OUTSIDE: break; } oBuilder.WriteString(L""); if (ECellCreator::FILE == eCellCreator && !pCell->GetParagraphs().empty()) { for (const CHWPPargraph* pParagraph : pCell->GetParagraphs()) { NSStringUtils::CStringBuilder oCellBuilder; TConversionState oCellState; WriteParagraph(pParagraph, oCellBuilder, oCellState); if (0 == oCellBuilder.GetCurSize()) { OpenParagraph(pParagraph->GetShapeID(), pParagraph->GetStyleID(), oBuilder, oCellState); CloseParagraph(oBuilder, oCellState); } else oBuilder.Write(oCellBuilder); } } else oBuilder.WriteString(L""); oBuilder.WriteString(L""); } void CConverter2OOXML::WriteCellProperties(short shBorderFillID, NSStringUtils::CStringBuilder& oBuilder) { const CHWPRecordBorderFill* pBorderFill = nullptr; if (nullptr != m_pContext) pBorderFill = dynamic_cast(m_pContext->GetBorderFill(shBorderFillID)); if (nullptr == pBorderFill) return; if (nullptr != pBorderFill->GetFill() && pBorderFill->GetFill()->ColorFill()) oBuilder.WriteString(L"GetFill()->GetFaceColor()) + L"\"/>"); oBuilder.WriteString(L""); WriteBorder(pBorderFill->GetTopBorder(), L"top", oBuilder); WriteBorder(pBorderFill->GetLeftBorder(), L"left", oBuilder); WriteBorder(pBorderFill->GetBottomBorder(), L"bottom", oBuilder); WriteBorder(pBorderFill->GetRightBorder(), L"right", oBuilder); oBuilder.WriteString(L""); } void CConverter2OOXML::WriteBorder(const TBorder& oBorder, const HWP_STRING& sBorderName, NSStringUtils::CStringBuilder& oBuilder) { if (0x00 == oBorder.m_chWidth || sBorderName.empty()) return; HWP_STRING sType; //TODO:: проверить стиль линий switch(oBorder.m_eStyle) { case ELineStyle2::NONE: oBuilder.WriteString(L""); return; case ELineStyle2::SOLID: sType = L"single"; break; case ELineStyle2::DASH: sType = L"dashed"; break; case ELineStyle2::DOT: sType = L"dotted"; break; case ELineStyle2::DASH_DOT: sType = L"dotDash"; break; case ELineStyle2::DASH_DOT_DOT: sType = L"dotDotDash"; break; case ELineStyle2::LONG_DASH: sType = L"dashed"; break; case ELineStyle2::CIRCLE: sType = L"dashed"; break; case ELineStyle2::DOUBLE_SLIM: sType = L"thickThinMediumGap"; break; case ELineStyle2::SLIM_THICK: sType = L"thickThinMediumGap"; break; case ELineStyle2::THICK_SLIM: sType = L"thickThinMediumGap"; break; case ELineStyle2::SLIM_THICK_SLIM: sType = L"thickThinMediumGap"; break; break; } oBuilder.WriteString(L""); } VECTOR ArcToBezier(const TPoint& oStart, const TPoint& oEnd, const TPoint& oCenter) { const double dRadiusX = std::abs(oStart.m_nX - oCenter.m_nX); const double dRadiusY = std::abs(oStart.m_nY - oCenter.m_nY); // Вычисление углов double dStartAngle = std::atan2(oStart.m_nY - oCenter.m_nY, oStart.m_nX - oCenter.m_nX); double dEndAngle = std::atan2(oEnd.m_nY - oCenter.m_nY, oEnd.m_nX - oCenter.m_nX); if (dEndAngle < dStartAngle) std::swap(dStartAngle, dEndAngle); TPoint oControl1{static_cast(oStart.m_nX + dRadiusX * std::cos(dStartAngle)), static_cast(oStart.m_nY + dRadiusY * std::sin(dStartAngle))}; TPoint oControl2{static_cast(oEnd.m_nX - dRadiusX * std::cos(dEndAngle )), static_cast(oEnd.m_nY + dRadiusY * std::sin(dEndAngle ))}; return {oStart, oControl1, oControl2, oEnd}; } void CConverter2OOXML::WriteGeometryShape(const CCtrlGeneralShape* pGeneralShape, short shParaShapeID, short shParaStyleID, NSStringUtils::CStringBuilder& oBuilder, TConversionState& oState) { if (nullptr == pGeneralShape) return; EShapeObjectType eShapeType = GetShapeObjectType(pGeneralShape->GetID()); if (EShapeObjectType::Unknown == eShapeType) return; ++m_ushShapeCount; WriteCaption((const CCtrlCommon*)pGeneralShape, oBuilder, oState); OpenParagraph(shParaShapeID, shParaStyleID, oBuilder, oState); const int nWidth = Transform::HWPUINT2OOXML(pGeneralShape->GetWidth()); const int nHeight = Transform::HWPUINT2OOXML(pGeneralShape->GetHeight()); const std::wstring wsWidth = std::to_wstring(nWidth); const std::wstring wsHeight = std::to_wstring(nHeight); oBuilder.WriteString(L""); oBuilder.WriteString(L""); OpenDrawingNode(pGeneralShape, oBuilder); oBuilder.WriteString(L""); oBuilder.WriteString(L""); oBuilder.WriteString(L""); oBuilder.WriteString(L""); oBuilder.WriteString(L""); oBuilder.WriteString(L""); switch (eShapeType) { case EShapeObjectType::Arc: { const CCtrlShapeArc *pShapeArc = (const CCtrlShapeArc*)pGeneralShape; VECTOR arBezierPoints{ArcToBezier({Transform::HWPUINT2OOXML(pShapeArc->GetFirstPoint() .m_nX), Transform::HWPUINT2OOXML(pShapeArc->GetFirstPoint() .m_nY)}, {Transform::HWPUINT2OOXML(pShapeArc->GetSecondPoint().m_nX), Transform::HWPUINT2OOXML(pShapeArc->GetSecondPoint().m_nY)}, {Transform::HWPUINT2OOXML(pShapeArc->GetCenterPoint().m_nX), Transform::HWPUINT2OOXML(pShapeArc->GetCenterPoint().m_nY)})}; oBuilder.WriteString(L""); oBuilder.WriteString(L""); oBuilder.WriteString(L""); oBuilder.WriteString(L""); oBuilder.WriteString(L""); oBuilder.WriteString(L""); oBuilder.WriteString(L""); oBuilder.WriteString(L""); break; } case EShapeObjectType::Ellipse: { oBuilder.WriteString(L""); break; } case EShapeObjectType::Line: { oBuilder.WriteString(L""); break; } case EShapeObjectType::Rectangle: { oBuilder.WriteString(L""); break; } case EShapeObjectType::Polygon: { VECTOR arPoints{((const CCtrlShapePolygon*)pGeneralShape)->GetPoints()}; if (2 > arPoints.size()) break; oBuilder.WriteString(L""); oBuilder.WriteString(L""); for (unsigned short ushIndex = 1; ushIndex < arPoints.size(); ++ushIndex) oBuilder.WriteString(L""); oBuilder.WriteString(L""); break; } case EShapeObjectType::Curve: { VECTOR arPoints{((const CCtrlShapeCurve*)pGeneralShape)->GetPoints()}; if (2 > arPoints.size()) break; VECTOR arSegmentType{((const CCtrlShapeCurve*)pGeneralShape)->GetSegmentsType()}; oBuilder.WriteString(L""); oBuilder.WriteString(L""); for (unsigned short ushIndex = 0; ushIndex < arSegmentType.size(); ++ushIndex) { if (0x01 == arSegmentType[ushIndex]) { oBuilder.WriteString(L""); oBuilder.WriteString(L""); oBuilder.WriteString(L""); oBuilder.WriteString(L""); oBuilder.WriteString(L""); ushIndex += 2; } else oBuilder.WriteString(L""); } oBuilder.WriteString(L""); break; } case EShapeObjectType::Ole: case EShapeObjectType::Picture: case EShapeObjectType::Unknown: break; } const CFill *pFill = pGeneralShape->GetFill(); if (nullptr == pFill || pFill->NoneFill()) oBuilder.WriteString(L""); else if (pFill->ColorFill()) oBuilder.WriteString(L"GetFaceColor()) + L"\"/>"); else if (pFill->ImageFill()) { const HWP_STRING sPictureId = SavePicture(pFill->GetBinItemID()); if (!sPictureId.empty()) oBuilder.WriteString(L""); else oBuilder.WriteString(L""); } WriteLineSettings(pGeneralShape, oBuilder); oBuilder.WriteString(L""); unsigned int nCountParagraphs = pGeneralShape->GetCountParagraphs(); if (0 < nCountParagraphs) { oBuilder.WriteString(L""); TConversionState oShapeState; oShapeState.m_bInTextBox = true; for (unsigned int unParaIndex = 0; unParaIndex < nCountParagraphs; ++unParaIndex) WriteParagraph(pGeneralShape->GetParagraphs(unParaIndex), oBuilder, oShapeState); oBuilder.WriteString(L""); } oBuilder.WriteString(L""); oBuilder.WriteString(L""); CloseDrawingNode(pGeneralShape, oBuilder); oBuilder.WriteString(L""); } void CConverter2OOXML::WriteEqEditShape(const CCtrlEqEdit* pEqEditShape, short shParaShapeID, short shParaStyleID, NSStringUtils::CStringBuilder& oBuilder, TConversionState& oState) { //TODO:: добавить конвертацию eqn формулы в ooxml ++m_ushEquationCount; WriteCaption((const CCtrlCommon*)pEqEditShape, oBuilder, oState); OpenParagraph(shParaShapeID, shParaStyleID, oBuilder, oState); oBuilder.WriteString(L""); oBuilder.WriteString(L""); oBuilder.WriteEncodeXmlString(pEqEditShape->GetEqn()); oBuilder.WriteString(L""); } void CConverter2OOXML::WriteOleShape(const CCtrlShapeOle* pOleShape, short shParaShapeID, short shParaStyleID, NSStringUtils::CStringBuilder& oBuilder, TConversionState& oState) { //TODO:: добавить конвертацию hwp ole -> ooxml chart //TODO:: необходимо добавить поддержку формата "Hwp Document File Formats - Charts" (для случаев, когда нет ooxml представления) // Пока можем вытащить лишь ooxml представление данных // Реализовать с использованием pole.h? if (nullptr == m_pContext) return; CHWPStream oBuffer; HWP_STRING sFileName; if (!m_pContext->GetBinBytes(pOleShape->GetBinDataID(), oBuffer, sFileName)) return; m_oOleConverter.CreateChart(oBuffer); const unsigned int unChartIndex = m_oOleConverter.GetChartsCount(); if (0 == unChartIndex) return; ++m_ushShapeCount; WriteCaption((const CCtrlCommon*)pOleShape, oBuilder, oState); const std::wstring wsWidth = std::to_wstring(Transform::HWPUINT2OOXML(pOleShape->GetWidth())); const std::wstring wsHeight = std::to_wstring(Transform::HWPUINT2OOXML(pOleShape->GetHeight())); const std::wstring wsRelID = AddRelationship(L"chart", L"charts/chart" + std::to_wstring(unChartIndex) + L".xml"); OpenParagraph(shParaShapeID, shParaStyleID, oBuilder, oState); oBuilder.WriteString(L""); OpenDrawingNode(pOleShape, oBuilder); oBuilder.WriteString(L""); oBuilder.WriteString(L""); oBuilder.WriteString(L""); oBuilder.WriteString(L""); CloseDrawingNode(pOleShape, oBuilder); oBuilder.WriteString(L""); AddContentType(L"charts/chart" + std::to_wstring(unChartIndex) + L".xml", L"application/vnd.openxmlformats-officedocument.drawingml.chart+xml"); AddContentType(L"charts/style" + std::to_wstring(unChartIndex) + L".xml", L"application/vnd.ms-office.chartstyle+xml"); AddContentType(L"charts/colors" + std::to_wstring(unChartIndex) + L".xml", L"application/vnd.ms-office.chartcolorstyle+xml"); } void CConverter2OOXML::WriteSectionSettings(TConversionState& oState) { m_oDocXml.WriteString(L""); if (nullptr != oState.m_pSectionDef) { std::vector arCtrlsHeadFoot; if (EHanType::HWP == m_pContext->GetType()) arCtrlsHeadFoot = oState.m_pSectionDef->GetHeaderFooters(); else if (EHanType::HWPX == m_pContext->GetType()) { arCtrlsHeadFoot = oState.m_arCtrlsHeadFoot; oState.m_arCtrlsHeadFoot.clear(); } for (const CCtrlHeadFoot* pCtrlHeadFoot : arCtrlsHeadFoot) { const std::wstring wsID = m_oFootnoteConverter.CreateHeadOrFoot((const CCtrlHeadFoot*)pCtrlHeadFoot, *this); if (!wsID.empty() && wsID.length() > 6) { const std::wstring wsType = wsID.substr(0, 6); AddContentType(wsID, L"application/vnd.openxmlformats-officedocument.wordprocessingml." + wsType + L"+xml"); m_oDocXml.WriteString(L""); } } } const CPage *pPage = (nullptr != oState.m_pSectionDef) ? oState.m_pSectionDef->GetPage() : nullptr; if (nullptr == pPage) { //DEFAULT_VALUE m_oDocXml.WriteString(L""); m_oDocXml.WriteString(L""); } else { m_oDocXml.WriteString(L"GetWidth())) + L"\" w:h=\"" + std::to_wstring(Transform::HWPUINT2Twips(pPage->GetHeight())) + L"\"/>"); m_oDocXml.WriteString(L"GetMarginTop())) + L"\" w:right=\"" + std::to_wstring(Transform::HWPUINT2Twips(pPage->GetMarginRight())) + L"\" w:bottom=\"" + std::to_wstring(Transform::HWPUINT2Twips(pPage->GetMarginBottom())) + L"\" w:left=\"" + std::to_wstring(Transform::HWPUINT2Twips(pPage->GetMarginRight())) + L"\" w:header=\"" + std::to_wstring(Transform::HWPUINT2Twips(pPage->GetMarginHeader())) + L"\" w:footer=\"" + std::to_wstring(Transform::HWPUINT2Twips(pPage->GetMarginFooter())) + L"\" w:gutter=\"" + std::to_wstring(Transform::HWPUINT2Twips(pPage->GetMarginGutter())) + L"\"/>"); } if (nullptr != oState.m_pColumnDef && 1 < oState.m_pColumnDef->GetColCount()) { //TODO:: Добавить поддержку остальный свойств m_oDocXml.WriteString(L"GetColCount()) + L"\" w:space=\"454\""); if (ELineStyle2::NONE != oState.m_pColumnDef->GetColLineStyle()) m_oDocXml.WriteString(L" w:sep=\"true\""); m_oDocXml.WriteString(L"/>"); } else m_oDocXml.WriteString(L""); m_oDocXml.WriteString(L""); } void CConverter2OOXML::WritePicture(const CCtrlShapePic* pCtrlPic, short shParaShapeId, short shParaStyleID, NSStringUtils::CStringBuilder& oBuilder, TConversionState& oState) { if (nullptr == pCtrlPic) return; HWP_STRING sPictureID = SavePicture(pCtrlPic->GetBinDataID()); if (sPictureID.empty()) return; ++m_ushShapeCount; WriteCaption((const CCtrlCommon*)pCtrlPic, oBuilder, oState); OpenParagraph(shParaShapeId, shParaStyleID, oBuilder, oState); oBuilder.WriteString(L""); OpenDrawingNode(pCtrlPic, oBuilder, pCtrlPic->GetImageRectWidth(), pCtrlPic->GetIMageRectHeight()); oBuilder.WriteString(L""); oBuilder.WriteString(L""); oBuilder.WriteString(L""); oBuilder.WriteString(L""); oBuilder.WriteString(L""); oBuilder.WriteString(L""); oBuilder.WriteString(L"GetFinalWidth())) + L"\" cy=\"" + std::to_wstring(Transform::HWPUINT2OOXML(pCtrlPic->GetFinalHeight())) + L"\"/>"); oBuilder.WriteString(L""); WriteBorderSettings(pCtrlPic, oBuilder); oBuilder.WriteString(L""); CloseDrawingNode(pCtrlPic, oBuilder); oBuilder.WriteString(L""); } void CConverter2OOXML::WriteVideo(const CCtrlShapeVideo* pCtrlVideo, short shParaShapeId, short shParaStyleID, NSStringUtils::CStringBuilder& oBuilder, TConversionState& oState) { if (nullptr == pCtrlVideo || 1 != pCtrlVideo->GetVideoType()) return; HWP_STRING sPictureID = SavePicture(pCtrlVideo->GetThumnailBinID()); if (sPictureID.empty()) return; ++m_ushShapeCount; WriteCaption((const CCtrlCommon*)pCtrlVideo, oBuilder, oState); OpenParagraph(shParaShapeId, shParaStyleID, oBuilder, oState); oBuilder.WriteString(L""); OpenDrawingNode(pCtrlVideo, oBuilder); oBuilder.WriteString(L""); oBuilder.WriteString(L""); oBuilder.WriteString(L""); oBuilder.WriteString(L"GetDesc() + L"\">"); oBuilder.WriteString(L"GetWebUrl()) + L"\"/>"); oBuilder.WriteString(L""); oBuilder.WriteString(L""); oBuilder.WriteString(L""); const int nIFrameWidth = (int)((double)Transform::HWPUINT2OOXML(pCtrlVideo->GetWidth()) / 22860.); const int nIFrameHeight = (int)((double)Transform::HWPUINT2OOXML(pCtrlVideo->GetHeight()) / 22860.); oBuilder.WriteString(L""); oBuilder.WriteString(L"GetWebUrl() + L"\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen=\"\" title=\"\" sandbox=\"allow-scripts allow-same-origin allow-popups\">"); oBuilder.WriteString(L"\" h=\"" + std::to_wstring(nIFrameWidth) + L"\" w=\"" + std::to_wstring(nIFrameHeight) + L"\"/>"); oBuilder.WriteString(L""); oBuilder.WriteString(L"GetFinalWidth())) + L"\" cy=\"" + std::to_wstring(Transform::HWPUINT2OOXML(pCtrlVideo->GetFinalHeight())) + L"\"/>"); oBuilder.WriteString(L""); CloseDrawingNode(pCtrlVideo, oBuilder); oBuilder.WriteString(L""); } bool CConverter2OOXML::SaveSVGFile(const HWP_STRING& sSVG, HWP_STRING& sFileName) { if (sSVG.empty()) return false; NSFonts::IApplicationFonts* pFonts = NSFonts::NSApplication::Create(); pFonts->Initialize(); MetaFile::IMetaFile* pSvgReader = MetaFile::Create(pFonts); if (!pSvgReader->LoadFromString(sSVG)) { RELEASEINTERFACE(pSvgReader); RELEASEINTERFACE(pFonts); return false; } NSGraphics::IGraphicsRenderer* pGrRenderer = NSGraphics::Create(); pGrRenderer->SetFontManager(pSvgReader->get_FontManager()); double dX, dY, dW, dH; pSvgReader->GetBounds(&dX, &dY, &dW, &dH); if (dW < 0) dW = -dW; if (dH < 0) dH = -dH; double dOneMaxSize = (double)1000.; if (dW > dH && dW > dOneMaxSize) { dH *= (dOneMaxSize / dW); dW = dOneMaxSize; } else if (dH > dW && dH > dOneMaxSize) { dW *= (dOneMaxSize / dH); dH = dOneMaxSize; } int nWidth = static_cast(dW + 0.5); int nHeight = static_cast(dH + 0.5); double dWidth = 25.4 * nWidth / 96; double dHeight = 25.4 * nHeight / 96; unsigned char* pBgraData = (unsigned char*)malloc(nWidth * nHeight * 4); if (!pBgraData) { double dKoef = 2000.0 / (nWidth > nHeight ? nWidth : nHeight); nWidth = (int)(dKoef * nWidth); nHeight = (int)(dKoef * nHeight); dWidth = 25.4 * nWidth / 96; dHeight = 25.4 * nHeight / 96; pBgraData = (unsigned char*)malloc(nWidth * nHeight * 4); } if (!pBgraData) return false; unsigned int alfa = 0xffffff; //дефолтный тон должен быть прозрачным, а не белым //memset(pBgraData, 0xff, nWidth * nHeight * 4); for (int i = 0; i < nWidth * nHeight; i++) { ((unsigned int*)pBgraData)[i] = alfa; } CBgraFrame oFrame; oFrame.put_Data(pBgraData); oFrame.put_Width(nWidth); oFrame.put_Height(nHeight); oFrame.put_Stride(-4 * nWidth); pGrRenderer->CreateFromBgraFrame(&oFrame); pGrRenderer->SetSwapRGB(false); pGrRenderer->put_Width(dWidth); pGrRenderer->put_Height(dHeight); pSvgReader->SetTempDirectory(m_sTempDirectory); pSvgReader->DrawOnRenderer(pGrRenderer, 0, 0, dWidth, dHeight); sFileName = sFileName.substr(0, sFileName.find(L'.')); sFileName += L".png"; oFrame.SaveFile(m_sTempDirectory + L"/word/media/" + sFileName, 4); oFrame.put_Data(NULL); RELEASEINTERFACE(pGrRenderer); if (pBgraData) free(pBgraData); RELEASEINTERFACE(pSvgReader); RELEASEINTERFACE(pFonts); return true; } HWP_STRING CConverter2OOXML::SavePicture(const HWP_STRING& sBinItemId) { if (nullptr == m_pContext) return HWP_STRING(); //TODO:: добавить поддержку устновки размеров изображения из свойств шейпа CHWPStream oBuffer; HWP_STRING sFileName; if (!m_pContext->GetBinBytes(sBinItemId, oBuffer, sFileName)) return HWP_STRING(); oBuffer.MoveToStart(); if (IsRasterFormat(NSFile::GetFileExtention(sFileName))) { NSFile::CFileBinary oFile; oFile.CreateFileW(m_sTempDirectory + L"/word/media/" + sFileName); if (!oFile.WriteFile((unsigned char*)oBuffer.GetCurPtr(), oBuffer.GetSize())) { oFile.CloseFile(); return HWP_STRING(); } oFile.CloseFile(); } else if (L"svg" == NSFile::GetFileExtention(sFileName)) { std::string sSVG(oBuffer.GetCurPtr(), oBuffer.GetSize()); if (!SaveSVGFile(UTF8_TO_U(sSVG), sFileName)) return HWP_STRING(); } AddContentType(L"media/" + sFileName, L"image/" + NSFile::GetFileExtention(sFileName)); return AddRelationship(L"image", L"media/" + sFileName); } void CConverter2OOXML::WriteRunnerStyle(short shCharShapeID, NSStringUtils::CStringBuilder& oBuilder, TConversionState& oState, const CRunnerStyle& sExternStyles) { if (nullptr == m_pContext) return; oBuilder.WriteString(L""); if (m_oStyleConverter.GetLastCharShapeId() != shCharShapeID) m_oStyleConverter.WriteDifferenceRunnerStyles(m_oStyleConverter.GetLastCharShapeId(), shCharShapeID, *m_pContext, oBuilder); const CHWPRecordCharShape* pCharShape = dynamic_cast(m_pContext->GetCharShape(shCharShapeID)); if (nullptr != pCharShape) { WriteTextBorderStyle(pCharShape->GetBorderFillID(), oBuilder, oState); oState.m_ushLastCharShapeId = shCharShapeID; } m_oStyleConverter.WriteRunnerProperties(sExternStyles, oBuilder); oBuilder.WriteString(L""); switch(oState.m_eBreakType) { case TConversionState::EBreakType::Page: oBuilder.WriteString(L""); break; case TConversionState::EBreakType::Column: oBuilder.WriteString(L""); break; case TConversionState::EBreakType::TextWrapping: oBuilder.WriteString(L""); break; case TConversionState::EBreakType::None: break; } oState.m_eBreakType = TConversionState::EBreakType::None; } void CConverter2OOXML::WriteTextBorderStyle(short shBorderFillId, NSStringUtils::CStringBuilder& oBuilder, TConversionState& oState) { const CHWPRecordBorderFill* pBorderFill = dynamic_cast(m_pContext->GetBorderFill(shBorderFillId)); if (nullptr == pBorderFill) return; TBorder oBorder{pBorderFill->GetLeftBorder()}; if (ELineStyle2::NONE == oBorder.m_eStyle) return; WriteBorder(pBorderFill->GetLeftBorder(), L"bdr", oBuilder); } void CConverter2OOXML::OpenDrawingNode(const CCtrlObjElement* pCtrlShape, NSStringUtils::CStringBuilder& oBuilder, int nWidth, int nHeight) { if (nullptr == pCtrlShape) return; oBuilder.WriteString(L""); if (pCtrlShape->GetTreatAsChar()) { oBuilder.WriteString(L"GetTopOutMargin() / 10)) + L"\" distB=\"" + std::to_wstring(Transform::HWPUINT2OOXML(pCtrlShape->GetBottomOutMargin() / 10)) + L"\" distL=\"" + std::to_wstring(Transform::HWPUINT2OOXML(pCtrlShape->GetLeftOutMargin() / 10)) + L"\" distR=\"" + std::to_wstring(Transform::HWPUINT2OOXML(pCtrlShape->GetRightOutMargin() / 10)) + L"\">"); WriteShapeExtent(pCtrlShape, oBuilder); } else { oBuilder.WriteString(L"GetTextWrap() ? L"1" : L"0")) + L"\" relativeHeight=\"" + std::to_wstring(pCtrlShape->GetZOrder()) + L"\" distT=\"" + std::to_wstring(Transform::HWPUINT2OOXML(pCtrlShape->GetTopOutMargin() / 10)) + L"\" distB=\"" + std::to_wstring(Transform::HWPUINT2OOXML(pCtrlShape->GetBottomOutMargin() / 10)) + L"\" distL=\"" + std::to_wstring(Transform::HWPUINT2OOXML(pCtrlShape->GetLeftOutMargin() / 10)) + L"\" distR=\"" + std::to_wstring(Transform::HWPUINT2OOXML(pCtrlShape->GetRightOutMargin() / 10)) + L"\" simplePos=\"0\" locked=\"0\" layoutInCell=\"1\" allowOverlap=\"1\">"); WriteShapePosition(pCtrlShape, oBuilder); WriteShapeExtent(pCtrlShape, oBuilder, nWidth, nHeight); WriteShapeWrapMode(pCtrlShape, oBuilder); } WriteShapeProperty(pCtrlShape, oBuilder); } void CConverter2OOXML::CloseDrawingNode(const CCtrlObjElement* pCtrlShape, NSStringUtils::CStringBuilder& oBuilder) { if (nullptr == pCtrlShape) return; if (pCtrlShape->GetTreatAsChar()) oBuilder.WriteString(L""); else { oBuilder.WriteString(L"00"); oBuilder.WriteString(L""); } oBuilder.WriteString(L""); } HWP_STRING GetVRelativeFrom(EVRelTo eRelTo) { switch (eRelTo) { case EVRelTo::PARA: return L"paragraph"; case EVRelTo::PAPER: case EVRelTo::PAGE: return L"page"; } } HWP_STRING GetHRelativeFrom(EHRelTo eRelTo) { switch (eRelTo) { case EHRelTo::PAPER: case EHRelTo::PAGE: return L"page"; case EHRelTo::COLUMN: return L"column"; case EHRelTo::PARA: return L"character"; } } void CConverter2OOXML::WriteShapeProperty(const CCtrlCommon* pCtrlShape, NSStringUtils::CStringBuilder& oBuilder) { if (nullptr == pCtrlShape) return; oBuilder.WriteString(L"GetDesc()); oBuilder.WriteString(L"\"/>"); oBuilder.WriteString(L""); } void CConverter2OOXML::WriteShapePosition(const CCtrlCommon* pCtrlShape, NSStringUtils::CStringBuilder& oBuilder) { if (nullptr == pCtrlShape) return; oBuilder.WriteString(L""); oBuilder.WriteString(L"GetHorzRelTo()) + L"\">" + std::to_wstring(Transform::HWPUINT2OOXML(pCtrlShape->GetHorzOffset())) + L""); oBuilder.WriteString(L"GetVertRelTo()) + L"\">" + std::to_wstring(Transform::HWPUINT2OOXML(pCtrlShape->GetVertOffset())) + L""); } void CConverter2OOXML::WriteShapeExtent(const CCtrlObjElement* pCtrlShape, NSStringUtils::CStringBuilder& oBuilder, int nWidth, int nHeight) { if (nullptr == pCtrlShape) return; double dScaleX = 1., dScaleY = 1.; const int nOrgWidth{pCtrlShape->GetOrgWidth()}, nOrgHeight{pCtrlShape->GetOrgHeight()}; const int nCurWidth{pCtrlShape->GetCurWidth()}, nCurHeight{pCtrlShape->GetCurHeight()}; if (0 != nCurWidth && 0 != nOrgWidth) dScaleX = (double)nCurWidth / (double)nOrgWidth; if (0 != nCurHeight && 0 != nOrgHeight) dScaleY = (double)nCurHeight / (double)nOrgHeight; if (0 != pCtrlShape->GetWidth()) dScaleX *= (double)pCtrlShape->GetWidth() / (double)nCurWidth; if (0 != pCtrlShape->GetHeight()) dScaleY *= (double)pCtrlShape->GetHeight() / (double)nCurHeight; int nFinalWidth {pCtrlShape->GetWidth() }; int nFinalHeight{pCtrlShape->GetHeight()}; if (0 != nWidth) dScaleX *= (double)nWidth / (double)nFinalWidth; if (0 != nHeight) dScaleY *= (double)nHeight / (double)nFinalHeight; nFinalWidth = ceil((double)nFinalWidth * dScaleX); nFinalHeight = ceil((double)nFinalHeight * dScaleY); nFinalWidth -= (pCtrlShape->GetLeftInMargin() + pCtrlShape->GetRightInMargin()); nFinalHeight -= (pCtrlShape->GetTopInMargin() + pCtrlShape->GetBottomInMargin()); oBuilder.WriteString(L""); oBuilder.WriteString(L""); } void CConverter2OOXML::WriteShapeWrapMode(const CCtrlCommon* pCtrlShape, NSStringUtils::CStringBuilder& oBuilder) { if (nullptr == pCtrlShape) return; switch (pCtrlShape->GetTextWrap()) { case ETextWrap::SQUARE: { oBuilder.WriteString(L"GetTextFlow()) { case 0x0: default: oBuilder.WriteString(L"bothSides"); break; case 0x1: oBuilder.WriteString(L"left"); break; case 0x2: oBuilder.WriteString(L"right"); break; case 0x3: oBuilder.WriteString(L"largest"); break; } oBuilder.WriteString(L"\"/>"); break; } case ETextWrap::TOP_AND_BOTTOM: { oBuilder.WriteString(L""); break; } case ETextWrap::BEHIND_TEXT: case ETextWrap::IN_FRONT_OF_TEXT: { oBuilder.WriteString(L""); break; } } } void CConverter2OOXML::OpenParagraph(short shParaShapeID, short shParaStyleID, NSStringUtils::CStringBuilder& oBuilder, TConversionState& oState) { if (oState.m_bOpenedP) return; oBuilder.WriteString(L""); oState.m_bOpenedP = true; WriteParagraphProperties(shParaShapeID, shParaStyleID, oBuilder, oState); } void CConverter2OOXML::CloseParagraph(NSStringUtils::CStringBuilder& oBuilder, TConversionState& oState) { if (!oState.m_bOpenedP) return; oBuilder.WriteString(L""); oState.m_bOpenedP = false; } void CConverter2OOXML::WriteText(const CParaText* pParaText, const std::vector& arRangeTags, short shParaShapeID, short shParaStyleID, NSStringUtils::CStringBuilder& oBuilder, TConversionState& oState) { if (nullptr == pParaText) return; if (arRangeTags.empty()) { WriteText(pParaText->GetText(), shParaShapeID, shParaStyleID, pParaText->GetCharShapeID(), oBuilder, oState); return; } HWP_STRING wsText = pParaText->GetText(); int nParaTextPosition = pParaText->GetStartIDx(); int unStartText = 0; TColor *pHighlightColor = nullptr; for (size_t unTextPosition = 0; unTextPosition < wsText.length(); ++unTextPosition) { for (const TRangeTag& oRangeTag : arRangeTags) { if (unTextPosition + nParaTextPosition == oRangeTag.m_nStartPos) { WriteText(wsText.substr(unStartText, unTextPosition - unStartText), shParaShapeID, shParaStyleID, pParaText->GetCharShapeID(), oBuilder, oState); unStartText = unTextPosition; switch (oRangeTag.m_chType) { case 0x02: //highlight { pHighlightColor = new TColor{(unsigned char)oRangeTag.m_arData[0], (unsigned char)oRangeTag.m_arData[1], (unsigned char)oRangeTag.m_arData[2]}; break; } default: break; } } else if (unTextPosition + nParaTextPosition == oRangeTag.m_nEndPos) { CRunnerStyle oRunnerStyle; oRunnerStyle.SetHighlight(NormalizeHighlightColor(*pHighlightColor)); WriteText(wsText.substr(unStartText, unTextPosition - unStartText), shParaShapeID, shParaStyleID, pParaText->GetCharShapeID(), oBuilder, oState, oRunnerStyle); unStartText = unTextPosition; switch (oRangeTag.m_chType) { case 0x02: //highlight { if (nullptr != pHighlightColor) { delete pHighlightColor; pHighlightColor = nullptr; } break; } default: break; } } } } if (unStartText < wsText.length()) WriteText(wsText.substr(unStartText), shParaShapeID, shParaStyleID, pParaText->GetCharShapeID(), oBuilder, oState); } std::vector SplitText(const std::wstring& wsText) { if (wsText.empty()) return std::vector{L""}; std::vector arTexts; std::wstring wsCurrentText; bool bWasLetter = iswalpha(wsText[0]); wsCurrentText.push_back(wsText[0]); for (unsigned int unIndex = 1; unIndex < wsText.length(); ++unIndex) { bool bCurrentIsLetter = iswalnum(wsText[unIndex]); if (bCurrentIsLetter != bWasLetter) { arTexts.push_back(wsCurrentText); wsCurrentText.clear(); bWasLetter = bCurrentIsLetter; } wsCurrentText.push_back(wsText[unIndex]); } if (!wsCurrentText.empty()) arTexts.push_back(wsCurrentText); return arTexts; } void CConverter2OOXML::WriteText(const HWP_STRING& wsText, short shParaShapeID, short shParaStyleID, short shCharShapeID, NSStringUtils::CStringBuilder& oBuilder, TConversionState& oState, const CRunnerStyle& oExternalStyle) { OpenParagraph(shParaShapeID, shParaStyleID, oBuilder, oState); if (wsText.empty()) { oBuilder.WriteString(L""); WriteRunnerStyle(shCharShapeID, oBuilder, oState, oExternalStyle); oBuilder.WriteString(L""); return; } oBuilder.WriteString(L""); WriteRunnerStyle(shCharShapeID, oBuilder, oState, oExternalStyle); oBuilder.WriteString(L""); else oBuilder.WriteString(L">"); if (bNeedAddSpace) oBuilder.WriteString(L" "); oBuilder.WriteEncodeXmlString(wsText); oBuilder.WriteString(L""); return; for (const std::wstring& wsTextElement : SplitText(wsText)) { oBuilder.WriteString(L""); WriteRunnerStyle(shCharShapeID, oBuilder, oState, oExternalStyle); if (!wsTextElement.empty()) { oBuilder.WriteString(L""); oBuilder.WriteEncodeXmlString(wsTextElement); oBuilder.WriteString(L""); } oBuilder.WriteString(L""); } } void CConverter2OOXML::WriteLineSettings(const CCtrlGeneralShape* pCtrlGeneralShape, NSStringUtils::CStringBuilder& oBuilder) { if (nullptr == pCtrlGeneralShape) return; WriteLineSettings(pCtrlGeneralShape->GetLineStyle(), pCtrlGeneralShape->GetLineColor(), pCtrlGeneralShape->GetLineThick(), 1, oBuilder); } void CConverter2OOXML::WriteLineSettings(ELineStyle2 eLineStyle, int nColor, int nThick, HWP_BYTE nCompoundLineType, NSStringUtils::CStringBuilder& oBuilder) { if (ELineStyle2::NONE == eLineStyle) { oBuilder.WriteString(L""); return; } if (0 == nThick) nThick = 100; oBuilder.WriteString(L""); oBuilder.WriteString(L""); switch (eLineStyle) { case ELineStyle2::DASH: { oBuilder.WriteString(L""); break; } case ELineStyle2::DOT: { oBuilder.WriteString(L""); break; } case ELineStyle2::DASH_DOT: { oBuilder.WriteString(L""); break; } case ELineStyle2::DASH_DOT_DOT: { oBuilder.WriteString(L""); break; } case ELineStyle2::LONG_DASH: { oBuilder.WriteString(L""); break; } case ELineStyle2::CIRCLE: { oBuilder.WriteString(L""); break; } default: break; } switch (nCompoundLineType) { case 0x00: { oBuilder.WriteString(L""); break; } case 0x01: { oBuilder.WriteString(L""); break; } } oBuilder.WriteString(L""); } void CConverter2OOXML::WriteBorderSettings(const CCtrlShapePic* pCtrlPic, NSStringUtils::CStringBuilder& oBuilder) { if (nullptr == pCtrlPic) return; WriteLineSettings(pCtrlPic->GetBorderLineStyle(), pCtrlPic->GetBorderColor(), pCtrlPic->GetBorderThick(), pCtrlPic->GetBorderCompoundLineType(), oBuilder); } void CConverter2OOXML::WriteAutoNumber(const CCtrlAutoNumber* pAutoNumber, short shParaShapeID, short shParaStyleID, short shCharShapeID, NSStringUtils::CStringBuilder& oBuilder, TConversionState& oState) { if (nullptr == pAutoNumber) return; unsigned short ushValue = 0; HWP_STRING wsType; //TODO:: лучше перейти не на ручной подсчет, а на автоматический в word (но там есть свои проблемы) switch (pAutoNumber->GetNumType()) { case ENumType::PAGE: { OpenParagraph(shParaShapeID, shParaStyleID, oBuilder, oState); oBuilder.WriteString(L"1"); return; } case ENumType::TOTAL_PAGE: ushValue = m_ushPageCount; break; case ENumType::FOOTNOTE: { OpenParagraph(shParaShapeID, shParaStyleID, oBuilder, oState); oBuilder.WriteString(L""); CRunnerStyle oRunnerStyle; oRunnerStyle.SetVerticalAlign(EVerticalAlignRun::Superscript); WriteRunnerStyle(shCharShapeID, oBuilder, oState, oRunnerStyle); oBuilder.WriteString(L""); return; } case ENumType::ENDNOTE: { OpenParagraph(shParaShapeID, shParaStyleID, oBuilder, oState); oBuilder.WriteString(L""); CRunnerStyle oRunnerStyle; oRunnerStyle.SetVerticalAlign(EVerticalAlignRun::Superscript); WriteRunnerStyle(shCharShapeID, oBuilder, oState, oRunnerStyle); oBuilder.WriteString(L""); return; } case ENumType::FIGURE: { wsType = L"Figure"; ushValue = m_ushShapeCount; break; } case ENumType::TABLE: { wsType = L"Table"; ushValue = m_ushTableCount; break; } case ENumType::EQUATION: { wsType = L"Equation"; ushValue = m_ushEquationCount; break; } } if (0 == ushValue) return; if (!wsType.empty()) oBuilder.WriteString(L""); WriteText(std::to_wstring(ushValue), shParaShapeID, shParaStyleID, shCharShapeID, oBuilder, oState); if (!wsType.empty()) oBuilder.WriteString(L""); } HWP_STRING CConverter2OOXML::AddRelationship(const HWP_STRING& wsType, const HWP_STRING& wsTarget) { if (wsType.empty() || wsTarget.empty()) return HWP_STRING(); VECTOR::const_iterator itFound = std::find_if(m_arRelationships.cbegin(), m_arRelationships.cend(), [wsTarget](const TRelationship& oRelationship){ return wsTarget == oRelationship.m_wsTarget; }); if (m_arRelationships.cend() != itFound) return itFound->m_wsID; if (L"hyperlink" == wsType) { NSStringUtils::CStringBuilder oBuilder; oBuilder.WriteEncodeXmlString(wsTarget); m_arRelationships.push_back({L"rId" + std::to_wstring(m_arRelationships.size() + 1), wsType, oBuilder.GetData()}); } else m_arRelationships.push_back({L"rId" + std::to_wstring(m_arRelationships.size() + 1), wsType, wsTarget}); return m_arRelationships.back().m_wsID; } void CConverter2OOXML::AddContentType(const HWP_STRING& wsName, const HWP_STRING& wsType) { if (wsName.empty() || wsType.empty()) return; VECTOR::const_iterator itFound = std::find_if(m_arContentTypes.cbegin(), m_arContentTypes.cend(), [wsName](const TContentType& oContentType){ return wsName == oContentType.m_wsName; }); if (m_arContentTypes.cend() != itFound) return; AddDefaultContentType(NSFile::GetFileExtention(wsName)); m_arContentTypes.push_back({wsName, wsType}); } void CConverter2OOXML::AddDefaultContentType(const HWP_STRING& wsName) { if (wsName.empty()) return; VECTOR::const_iterator itFound = std::find_if(m_arDefaultContentType.cbegin(), m_arDefaultContentType.cend(), [wsName](const TContentType& oContentType){ return wsName == oContentType.m_wsName; }); if (m_arDefaultContentType.cend() != itFound) return; if (IsRasterFormat(wsName)) m_arDefaultContentType.push_back({wsName, L"image/" + wsName}); else if (L"xml" == wsName) m_arDefaultContentType.push_back({wsName, L"application/xml"}); } bool CConverter2OOXML::ConvertToFile(const HWP_STRING& sFilePath) { if (nullptr == m_pContext || sFilePath.empty()) return false; CreateEmptyFiles(); Convert(); Close(); COfficeUtils oZip; oZip.CompressFileOrDirectory(m_sTempDirectory, sFilePath); return true; } bool CConverter2OOXML::ConvertToDir(const HWP_STRING& sDirectoryPath) { if (nullptr == m_pContext || sDirectoryPath.empty()) return false; HWP_STRING sCurrentTempDir{m_sTempDirectory}; SetTempDirectory(sDirectoryPath); CreateEmptyFiles(); Convert(); Close(); SetTempDirectory(sCurrentTempDir); return true; } HWP_STRING CConverter2OOXML::GetTempDirectory() const { return m_sTempDirectory; } }