From 6ae6c37ef1d0d4186d87e44a1c46e625b4a67c96 Mon Sep 17 00:00:00 2001 From: Kirill Polyakov Date: Tue, 25 Jun 2024 15:51:04 +0300 Subject: [PATCH 01/18] Added reading of custom fonts in svg --- .../3dParty/html/css/src/StaticFunctions.cpp | 4 +- .../3dParty/html/css/src/StyleProperties.cpp | 348 ++++++++---------- Common/3dParty/html/css/src/StyleProperties.h | 63 ++-- DesktopEditor/graphics/pro/metafile.pri | 2 + .../raster/Metafile/svg/CSvgFile.cpp | 16 + DesktopEditor/raster/Metafile/svg/CSvgFile.h | 14 +- .../raster/Metafile/svg/CSvgParser.cpp | 63 +++- .../raster/Metafile/svg/CSvgParser.h | 2 + .../Metafile/svg/SvgObjects/CContainer.h | 2 - .../raster/Metafile/svg/SvgObjects/CFont.cpp | 90 +++++ .../raster/Metafile/svg/SvgObjects/CFont.h | 53 +++ .../raster/Metafile/svg/SvgObjects/CPattern.h | 1 + .../raster/Metafile/svg/SvgObjects/CSymbol.h | 1 + .../raster/Metafile/svg/SvgObjects/CText.cpp | 33 +- .../raster/Metafile/svg/SvgObjects/CText.h | 2 + DesktopEditor/raster/Metafile/svg/SvgTypes.h | 1 + 16 files changed, 452 insertions(+), 243 deletions(-) create mode 100644 DesktopEditor/raster/Metafile/svg/SvgObjects/CFont.cpp create mode 100644 DesktopEditor/raster/Metafile/svg/SvgObjects/CFont.h diff --git a/Common/3dParty/html/css/src/StaticFunctions.cpp b/Common/3dParty/html/css/src/StaticFunctions.cpp index ae49a17bae..6eee504aa0 100644 --- a/Common/3dParty/html/css/src/StaticFunctions.cpp +++ b/Common/3dParty/html/css/src/StaticFunctions.cpp @@ -141,7 +141,9 @@ namespace NS_STATIC_FUNCTIONS while (std::wstring::npos != unEnd) { - arWords.emplace_back(wsLine.data() + unStart, unEnd - unStart + ((bWithSigns) ? 1 : 0)); + if (unStart != unEnd) + arWords.emplace_back(wsLine.data() + unStart, unEnd - unStart + ((bWithSigns) ? 1 : 0)); + unStart = wsLine.find_first_not_of(wsDelimiters, unEnd); unEnd = wsLine.find_first_of(wsDelimiters, unStart); } diff --git a/Common/3dParty/html/css/src/StyleProperties.cpp b/Common/3dParty/html/css/src/StyleProperties.cpp index 513d4d604b..99efa51952 100644 --- a/Common/3dParty/html/css/src/StyleProperties.cpp +++ b/Common/3dParty/html/css/src/StyleProperties.cpp @@ -459,37 +459,88 @@ namespace NSCSS return std::wstring(arTemp, 6); } - std::wstring CColor::CutURL(const std::wstring &wsValue) - { - if (wsValue.length() < 6) - return std::wstring(); + CColor::CColor() + : CValue(NULL, 0, false), m_oOpacity(1.) + {} - size_t unBegin = wsValue.find(L"(#"); - - if (std::wstring::npos == unBegin || unBegin < 3 || wsValue.length() - unBegin < 2) - return std::wstring(); - - std::wstring wsCopyValue(wsValue); - - std::transform(wsCopyValue.begin(), wsCopyValue.begin() + unBegin, wsCopyValue.begin(), std::towlower); - - if (std::wstring::npos == wsCopyValue.find(L"url(#")) - return std::wstring(); - - return wsCopyValue.substr(unBegin + 2, wsCopyValue.find(L')') - unBegin - 2); - } - void CColor::SetEmpty(unsigned int unLevel) { - m_oValue.Clear(); - m_oValue.m_enType = ColorEmpty; + Clear(); + m_enType = ColorEmpty; m_unLevel = unLevel; m_bImportant = false; } + + void CColor::SetRGB(unsigned char uchR, unsigned char uchG, unsigned char uchB) + { + Clear(); - CColor::CColor() - : CValue({}, 0, false), m_oOpacity(1.) - {} + m_oValue = new TRGB{uchR, uchG, uchB}; + + if (NULL == m_oValue) + return; + + m_enType = ColorRGB; + } + + void CColor::SetRGB(const TRGB &oRGB) + { + Clear(); + + m_oValue = new TRGB{oRGB}; + + if (NULL == m_oValue) + return; + + m_enType = ColorRGB; + } + + void CColor::SetHEX(const std::wstring &wsValue) + { + Clear(); + + if (6 != wsValue.length() && 3 != wsValue.length()) + return; + + if (6 == wsValue.length()) + m_oValue = new std::wstring(wsValue); + else + m_oValue = new std::wstring({wsValue[0], wsValue[0], wsValue[1], wsValue[1], wsValue[2], wsValue[2]}); + + if (NULL == m_oValue) + return; + + m_enType = ColorHEX; + } + + void CColor::SetUrl(const std::wstring &wsValue) + { + Clear(); + + if (wsValue.empty()) + return; + + CURL *pURL = new CURL(); + + if (NULL == pURL) + return; + + if (!pURL->SetValue(wsValue)) + { + delete pURL; + return; + } + + m_oValue = pURL; + m_enType = ColorUrl; + } + + void CColor::SetNone() + { + Clear(); + + m_enType = ColorNone; + } bool CColor::SetValue(const std::wstring &wsValue, unsigned int unLevel, bool bHardMode) { @@ -513,21 +564,21 @@ namespace NSCSS if (wsNewValue[0] == L'#') { - m_oValue.SetHEX(wsNewValue.substr(1, wsNewValue.length() - 1)); + SetHEX(wsNewValue.substr(1, wsNewValue.length() - 1)); m_unLevel = unLevel; m_bImportant = bImportant; return true; } else if (L"none" == wsNewValue) { - m_oValue.SetNone(); + SetNone(); m_unLevel = unLevel; m_bImportant = bImportant; return true; } else if (wsNewValue == L"transparent") { - m_oValue.SetNone(); + SetNone(); m_unLevel = unLevel; m_bImportant = bImportant; return true; @@ -558,7 +609,7 @@ namespace NSCSS if (255 < nGreen) nGreen = 255; if (255 < nBlue) nBlue = 255; - m_oValue.SetRGB(nRed, nGreen, nBlue); + SetRGB(nRed, nGreen, nBlue); if (wsNewValue.substr(0, 4) == L"rgba" && 4 == arValues.size()) m_oOpacity.SetValue(arValues[3], unLevel, bHardMode); @@ -570,9 +621,9 @@ namespace NSCSS if (5 <= wsNewValue.length()) { - m_oValue.SetUrl(CutURL(wsValue)); + SetUrl(wsValue); - if (m_oValue.m_enType == ColorUrl) + if (m_enType == ColorUrl) { m_unLevel = unLevel; m_bImportant = bImportant;; @@ -583,7 +634,7 @@ namespace NSCSS const std::map::const_iterator oHEX = NSConstValues::COLORS.find(wsNewValue); if (oHEX != NSConstValues::COLORS.end()) { - m_oValue.SetHEX(oHEX->second); + SetHEX(oHEX->second); m_unLevel = unLevel; m_bImportant = bImportant; return true; @@ -602,29 +653,53 @@ namespace NSCSS bool CColor::Empty() const { - return m_oValue.Empty(); + return ColorEmpty == m_enType; } bool CColor::None() const { - return ColorNone == m_oValue.m_enType; + return ColorNone == m_enType; } bool CColor::Url() const { - return ColorUrl == m_oValue.m_enType; + return ColorUrl == m_enType; } void CColor::Clear() { - m_oValue.Clear(); + switch (m_enType) + { + case ColorRGB: + { + TRGB *pRGB = static_cast(m_oValue); + RELEASEOBJECT(pRGB); + break; + } + case ColorHEX: + { + std::wstring* pValue = static_cast(m_oValue); + RELEASEOBJECT(pValue); + break; + } + case ColorUrl: + { + CURL *pURL = static_cast(m_oValue); + RELEASEOBJECT(pURL); + break; + } + default: + break; + } + + m_enType = ColorEmpty; m_unLevel = NULL; m_bImportant = false; } ColorType CColor::GetType() const { - return m_oValue.m_enType; + return m_enType; } double CColor::GetOpacity() const @@ -643,16 +718,16 @@ namespace NSCSS int CColor::ToInt() const { - switch(m_oValue.m_enType) + switch(m_enType) { case ColorRGB: { - TRGB* pRGB = static_cast(m_oValue.m_pColor); + TRGB* pRGB = static_cast(m_oValue); return RGB_TO_INT(pRGB->uchRed, pRGB->uchGreen, pRGB->uchBlue); } case ColorHEX: { - std::wstring *pValue = static_cast(m_oValue.m_pColor); + std::wstring *pValue = static_cast(m_oValue); TRGB oRGB = ConvertHEXtoRGB(*pValue); return RGB_TO_INT(oRGB.uchRed, oRGB.uchGreen, oRGB.uchBlue); } @@ -668,10 +743,11 @@ namespace NSCSS std::wstring CColor::ToWString() const { - switch(m_oValue.m_enType) + switch(m_enType) { - case ColorRGB: return ConvertRGBtoHEX(*static_cast(m_oValue.m_pColor)); - case ColorHEX: case ColorUrl: return *static_cast(m_oValue.m_pColor); + case ColorRGB: return ConvertRGBtoHEX(*static_cast(m_oValue)); + case ColorHEX: return *static_cast(m_oValue); + case ColorUrl: return static_cast(m_oValue)->GetValue(); default: return std::wstring(); } } @@ -683,10 +759,10 @@ namespace NSCSS TRGB oCurrentColor; - switch(m_oValue.m_enType) + switch(m_enType) { - case ColorRGB: oCurrentColor = *static_cast(m_oValue.m_pColor); break; - case ColorHEX: oCurrentColor = ConvertHEXtoRGB(*static_cast(m_oValue.m_pColor)); break; + case ColorRGB: oCurrentColor = *static_cast(m_oValue); break; + case ColorHEX: oCurrentColor = ConvertHEXtoRGB(*static_cast(m_oValue)); break; default: return L"none"; } @@ -710,10 +786,10 @@ namespace NSCSS TRGB CColor::ToRGB() const { - switch(m_oValue.m_enType) + switch(m_enType) { - case ColorRGB: return *static_cast(m_oValue.m_pColor); - case ColorHEX: return ConvertHEXtoRGB(*static_cast(m_oValue.m_pColor)); + case ColorRGB: return *static_cast(m_oValue); + case ColorHEX: return ConvertHEXtoRGB(*static_cast(m_oValue)); default: return TRGB(); } } @@ -2421,175 +2497,48 @@ namespace NSCSS m_oWeight == oFont.m_oWeight; } - CColorValue::CColorValue() - : m_enType(ColorEmpty), m_pColor(NULL) + CURL::CURL() {} - CColorValue::CColorValue(const CColorValue &oColorValue) - : m_enType() + bool CURL::Empty() const { - switch(oColorValue.m_enType) - { - case ColorRGB: SetRGB(*static_cast(oColorValue.m_pColor)); break; - case ColorHEX: SetHEX(*static_cast(oColorValue.m_pColor)); break; - case ColorUrl: SetUrl(*static_cast(oColorValue.m_pColor)); break; - default: m_enType = oColorValue.m_enType; break; - } - + return m_wsValue.empty(); } - CColorValue::~CColorValue() + bool CURL::LinkToId() const { - Clear(); + return m_wsValue.length() > 1 && L'#' == m_wsValue.front(); + } + + void CURL::Clear() + { + m_wsValue.clear(); } - void CColorValue::SetRGB(unsigned char uchR, unsigned char uchG, unsigned char uchB) + bool CURL::SetValue(const std::wstring &wsValue) { - Clear(); - - m_pColor = new TRGB{uchR, uchG, uchB}; - - if (NULL == m_pColor) - { - m_enType = ColorEmpty; - return; - } - - m_enType = ColorRGB; - } - - void CColorValue::SetRGB(const TRGB &oRGB) - { - Clear(); - - m_pColor = new TRGB{oRGB}; - - if (NULL == m_pColor) - { - m_enType = ColorEmpty; - return; - } - - m_enType = ColorRGB; - } - - void CColorValue::SetHEX(const std::wstring &wsValue) - { - Clear(); - - if (6 != wsValue.length() && 3 != wsValue.length()) - { - m_enType = ColorEmpty; - return; - } - - if (6 == wsValue.length()) - m_pColor = new std::wstring(wsValue); - else - m_pColor = new std::wstring({wsValue[0], wsValue[0], wsValue[1], wsValue[1], wsValue[2], wsValue[2]}); - - if (NULL == m_pColor) - { - m_enType = ColorEmpty; - return; - } - - m_enType = ColorHEX; - } - - void CColorValue::SetUrl(const std::wstring &wsValue) - { - Clear(); - - m_pColor = new std::wstring(wsValue); - - if (NULL == m_pColor || ((std::wstring*)m_pColor)->empty()) - { - m_enType = ColorEmpty; - return; - } - - m_enType = ColorUrl; - } - - void CColorValue::SetNone() - { - Clear(); - - m_enType = ColorNone; - } - - bool CColorValue::Empty() const - { - return ColorEmpty == m_enType; - } - - std::wstring CColorValue::GetColor() const - { - return *(std::wstring*)m_pColor; - } - - bool CColorValue::operator==(const CColorValue &oColorValue) const - { - if (m_enType != oColorValue.m_enType) + if (wsValue.empty()) return false; - if (ColorEmpty == m_enType || - ColorNone == m_enType) - return true; + std::wregex oRegex(L"url\\s*\\(\\s*(?:'|\"|)([#]?[^'\"()]+)(?:'|\"|)\\s*\\)"); + std::wsmatch oMatch; - switch (m_enType) - { - case ColorRGB: - return *static_cast(m_pColor) == *static_cast(oColorValue.m_pColor); - case ColorHEX: - case ColorUrl: - return *static_cast(m_pColor) == *static_cast(oColorValue.m_pColor); - default: - break; - } + if (!std::regex_search(wsValue.cbegin(), wsValue.cend(), oMatch, oRegex) || oMatch[1].str().empty()) + return false; - return false; + m_wsValue = oMatch[1].str(); + NS_STATIC_FUNCTIONS::RemoveSpaces(m_wsValue); + + return true; } - - CColorValue &CColorValue::operator=(const CColorValue &oColorValue) + + std::wstring CURL::GetValue() const { - switch(oColorValue.m_enType) - { - case ColorRGB: SetRGB(*static_cast(oColorValue.m_pColor)); break; - case ColorHEX: SetHEX(*static_cast(oColorValue.m_pColor)); break; - case ColorUrl: SetUrl(*static_cast(oColorValue.m_pColor)); break; - default: m_enType = oColorValue.m_enType; break; - } - - return *this; - } - - void CColorValue::Clear() - { - switch (m_enType) - { - case ColorRGB: - { - TRGB *pRGB = static_cast(m_pColor); - RELEASEOBJECT(pRGB); - break; - } - case ColorHEX: case ColorUrl: - { - std::wstring* pValue = static_cast(m_pColor); - RELEASEOBJECT(pValue); - break; - } - default: - break; - } - - m_enType = ColorEmpty; + return m_wsValue; } CEnum::CEnum() - : CValue(INT_MAX, 0, false){} + : CValue(INT_MAX, 0, false){} bool CEnum::SetValue(const std::wstring &wsValue, unsigned int unLevel, bool bHardMode) { @@ -2726,5 +2675,6 @@ namespace NSCSS { return m_oHeader; } + } } diff --git a/Common/3dParty/html/css/src/StyleProperties.h b/Common/3dParty/html/css/src/StyleProperties.h index 13ea54f67c..f424ceda99 100644 --- a/Common/3dParty/html/css/src/StyleProperties.h +++ b/Common/3dParty/html/css/src/StyleProperties.h @@ -21,6 +21,7 @@ namespace NSCSS friend class CDigit; friend class CColor; friend class CEnum; + friend class CURL; T m_oValue; unsigned int m_unLevel; @@ -180,6 +181,22 @@ namespace NSCSS bool operator==(const TRGB& oRGB) const; }; + class CURL + { + public: + CURL(); + + bool Empty() const; + bool LinkToId() const; + + void Clear(); + + bool SetValue(const std::wstring& wsValue); + std::wstring GetValue() const; + private: + std::wstring m_wsValue; + }; + typedef enum { ColorEmpty, @@ -189,39 +206,8 @@ namespace NSCSS ColorUrl } ColorType; - class Q_DECL_EXPORT CColorValue + class CColor : public CValue { - public: - CColorValue(); - CColorValue(const CColorValue& oColorValue); - ~CColorValue(); - - void SetRGB(unsigned char uchR, unsigned char uchG, unsigned char uchB); - void SetRGB(const TRGB& oRGB); - void SetHEX(const std::wstring& wsValue); - void SetUrl(const std::wstring& wsValue); - void SetNone(); - - void Clear(); - - bool Empty() const; - - ColorType m_enType; - void* m_pColor = NULL; - - std::wstring GetColor() const; - - bool operator==(const CColorValue& oColorValue) const; - CColorValue& operator= (const CColorValue& oColorValue); - }; - - class CColor : public CValue - { - CDigit m_oOpacity; - static TRGB ConvertHEXtoRGB(const std::wstring& wsValue); - static std::wstring ConvertRGBtoHEX(const TRGB& oValue); - static std::wstring CutURL(const std::wstring& wsValue); - void SetEmpty(unsigned int unLevel = 0); public: CColor(); @@ -242,6 +228,19 @@ namespace NSCSS std::wstring ToWString() const override; std::wstring EquateToColor(const std::vector>& arColors) const; TRGB ToRGB() const; + + static TRGB ConvertHEXtoRGB(const std::wstring& wsValue); + static std::wstring ConvertRGBtoHEX(const TRGB& oValue); + private: + CDigit m_oOpacity; + ColorType m_enType; + + void SetEmpty(unsigned int unLevel = 0); + void SetRGB(unsigned char uchR, unsigned char uchG, unsigned char uchB); + void SetRGB(const TRGB& oRGB); + void SetHEX(const std::wstring& wsValue); + void SetUrl(const std::wstring& wsValue); + void SetNone(); }; typedef enum diff --git a/DesktopEditor/graphics/pro/metafile.pri b/DesktopEditor/graphics/pro/metafile.pri index 176a1f8e4a..5817ead57a 100644 --- a/DesktopEditor/graphics/pro/metafile.pri +++ b/DesktopEditor/graphics/pro/metafile.pri @@ -116,6 +116,7 @@ METAFILE_PATH = $$PWD/../../raster/Metafile $$METAFILE_PATH/svg/SvgObjects/CText.h \ $$METAFILE_PATH/svg/SvgObjects/CUse.h \ $$METAFILE_PATH/svg/SvgObjects/CPolyline.h \ + $$METAFILE_PATH/svg/SvgObjects/CFont.h \ $$METAFILE_PATH/svg/SvgObjects/CStyle.h \ $$METAFILE_PATH/svg/SvgObjects/CObjectBase.h \ $$METAFILE_PATH/svg/SvgUtils.h @@ -139,6 +140,7 @@ METAFILE_PATH = $$PWD/../../raster/Metafile $$METAFILE_PATH/svg/SvgObjects/CText.cpp \ $$METAFILE_PATH/svg/SvgObjects/CUse.cpp \ $$METAFILE_PATH/svg/SvgObjects/CPolyline.cpp \ + $$METAFILE_PATH/svg/SvgObjects/CFont.cpp \ $$METAFILE_PATH/svg/SvgObjects/CObjectBase.cpp \ $$METAFILE_PATH/svg/SvgObjects/CStyle.cpp diff --git a/DesktopEditor/raster/Metafile/svg/CSvgFile.cpp b/DesktopEditor/raster/Metafile/svg/CSvgFile.cpp index 08a7727073..f6df395d0f 100644 --- a/DesktopEditor/raster/Metafile/svg/CSvgFile.cpp +++ b/DesktopEditor/raster/Metafile/svg/CSvgFile.cpp @@ -1,6 +1,7 @@ #include "CSvgFile.h" #include "SvgObjects/CContainer.h" +#include "SvgObjects/CFont.h" #define SVG_FILE_WIDTH 300 #define SVG_FILE_HEIGHT 150 @@ -132,6 +133,16 @@ SVG::CObject *CSvgFile::GetMarkedObject(const std::wstring &wsId) const return NULL; } +SVG::CFont *CSvgFile::GetFont(const std::wstring &wsFontFamily) const +{ + FontsFaceMap::const_iterator itFound = std::find_if(m_mFontsFace.cbegin(), m_mFontsFace.cend(), [&wsFontFamily](const std::pair& oValue){ return wsFontFamily == oValue.first; }); + + if (m_mFontsFace.cend() == itFound) + return NULL; + + return dynamic_cast(GetMarkedObject(itFound->second)); +} + std::wstring CSvgFile::GetWorkingDirectory() const { return m_wsWorkingDirectory; @@ -142,6 +153,11 @@ void CSvgFile::AddStyles(const std::wstring &wsStyles) m_oSvgCalculator.AddStyles(wsStyles); } +void CSvgFile::AddFontFace(const SVG::TFontArguments& oArguments, const std::wstring &wsId) +{ + m_mFontsFace.insert(std::make_pair(oArguments.m_wsFontFamily, wsId)); +} + bool CSvgFile::Draw(IRenderer *pRenderer, double dX, double dY, double dWidth, double dHeight) { if (NULL == pRenderer || m_oContainer.Empty()) diff --git a/DesktopEditor/raster/Metafile/svg/CSvgFile.h b/DesktopEditor/raster/Metafile/svg/CSvgFile.h index 1f2c71e60c..5f1af3e39f 100644 --- a/DesktopEditor/raster/Metafile/svg/CSvgFile.h +++ b/DesktopEditor/raster/Metafile/svg/CSvgFile.h @@ -9,6 +9,12 @@ #define SVG_DECL_IMPORT Q_DECL_IMPORT +namespace SVG +{ + struct TFontArguments; + class CFont; +} + class SVG_DECL_IMPORT CSvgFile { public: @@ -28,9 +34,12 @@ class SVG_DECL_IMPORT CSvgFile bool MarkObject(SVG::CObject* pObject); SVG::CObject* GetMarkedObject(const std::wstring& wsId) const; + SVG::CFont* GetFont(const std::wstring& wsFontFamily) const; + std::wstring GetWorkingDirectory() const; - + void AddStyles(const std::wstring& wsStyles); + void AddFontFace(const SVG::TFontArguments& oArguments, const std::wstring& wsId); bool Draw(IRenderer* pRenderer, double dX, double dY, double dWidth, double dHeight); private: @@ -44,6 +53,9 @@ class SVG_DECL_IMPORT CSvgFile MarkedMap m_mMarkedObjects; std::wstring m_wsWorkingDirectory; + + typedef std::map FontsFaceMap; + FontsFaceMap m_mFontsFace; }; #endif // CSVGFILE_H diff --git a/DesktopEditor/raster/Metafile/svg/CSvgParser.cpp b/DesktopEditor/raster/Metafile/svg/CSvgParser.cpp index 1872180bec..3faa22a463 100644 --- a/DesktopEditor/raster/Metafile/svg/CSvgParser.cpp +++ b/DesktopEditor/raster/Metafile/svg/CSvgParser.cpp @@ -6,7 +6,6 @@ #include "CSvgFile.h" -#include "SvgObjects/CContainer.h" #include "SvgObjects/CPolyline.h" #include "SvgObjects/CGradient.h" #include "SvgObjects/CClipPath.h" @@ -20,6 +19,7 @@ #include "SvgObjects/CRect.h" #include "SvgObjects/CLine.h" #include "SvgObjects/CPath.h" +#include "SvgObjects/CFont.h" #include "SvgObjects/CText.h" #include "SvgObjects/CMask.h" #include "SvgObjects/CUse.h" @@ -94,7 +94,7 @@ namespace SVG if (L"style" == wsElementName) { - pFile->AddStyles(oElement.GetText()); + ParseStyles(oElement.GetText(), pFile); return true; } @@ -115,6 +115,61 @@ namespace SVG return bScanResult; } + + void CSvgParser::ParseStyles(const std::wstring &wsStyles, CSvgFile *pFile) const + { + if (NULL == pFile) + return; + + pFile->AddStyles(wsStyles); + + std::wregex oRegex(L"@font-face\\s*(\\{[^}]*\\})"); + std::wsmatch oMatch; + std::wstring::const_iterator oSearchStart(wsStyles.cbegin()); + + while (std::regex_search(oSearchStart, wsStyles.cend(), oMatch, oRegex)) + { + if (oMatch[1].str().empty()) + continue; + + std::wstring wsValue{oMatch[1].str()}; + + std::wstring::const_iterator itStart = std::find_if(wsValue.cbegin(), wsValue.cend(), [](const wchar_t& wChar) { return !std::iswspace(wChar) && L'{' != wChar; }); + std::wstring::const_reverse_iterator itEnd = std::find_if(wsValue.crbegin(), wsValue.crend(), [](const wchar_t& wChar) { return !std::iswspace(wChar) && L'}' != wChar; }); + + if (wsValue.cend() != itStart && wsValue.crend() != itEnd) + wsValue = std::wstring(itStart, itEnd.base()); + + const std::vector arWords{NSCSS::NS_STATIC_FUNCTIONS::GetWordsW(wsValue, true, L":;")}; + + SvgURL oURL; + TFontArguments m_oArguments; + + for (unsigned int unIndex = 0; unIndex < arWords.size(); ++unIndex) + { + if (arWords[unIndex].length() > 3 && L"src" == arWords[unIndex].substr(0, 3) && L':' == arWords[unIndex].back() && + unIndex + 1 < arWords.size() && oURL.SetValue(arWords[++unIndex])) + { + continue; + } + else if (arWords[unIndex].length() > 11 && L"font-family" == arWords[unIndex].substr(0, 11) && L':' == arWords[unIndex].back() && + unIndex + 1 < arWords.size()) + { + const std::vector arFontFamily{NSCSS::NS_STATIC_FUNCTIONS::GetWordsW(arWords[++unIndex], false, L"\"\',;")}; + + if (arFontFamily.empty()) + continue; + + m_oArguments.m_wsFontFamily = arFontFamily.back(); + } + } + + if (!oURL.Empty() && !m_oArguments.m_wsFontFamily.empty()) + pFile->AddFontFace(m_oArguments, oURL.GetValue()); + + oSearchStart = oMatch.suffix().first; + } + } template bool CSvgParser::ReadObject(XmlUtils::CXmlNode &oElement, CContainer *pContainer, CSvgFile *pFile, CRenderedObject *pParent) const @@ -223,6 +278,10 @@ namespace SVG else RELEASEOBJECT(pObject); } + else if (L"font" == wsElementName) + { + pObject = new CFont(oElement); + } if (NULL != pObject) { diff --git a/DesktopEditor/raster/Metafile/svg/CSvgParser.h b/DesktopEditor/raster/Metafile/svg/CSvgParser.h index 060aecea5d..b0b298c3d6 100644 --- a/DesktopEditor/raster/Metafile/svg/CSvgParser.h +++ b/DesktopEditor/raster/Metafile/svg/CSvgParser.h @@ -2,6 +2,7 @@ #define CSVGPARSER_H #include "../../../common/Directory.h" +#include "../../../graphics/pro/Fonts.h" #include "../../../xml/include/xmlutils.h" #include "SvgObjects/CContainer.h" @@ -29,6 +30,7 @@ namespace SVG bool ReadChildrens(XmlUtils::CXmlNode& oElement, CContainer* pContainer, CSvgFile* pFile, CRenderedObject* pParent = NULL) const; bool ScanStyles(XmlUtils::CXmlNode& oElement, CSvgFile* pFile) const; + void ParseStyles(const std::wstring& wsStyles, CSvgFile *pFile) const; void UpdateStyles(CObject* pObject, CSvgFile* pFile) const; bool MarkObject(CObject* pObject, CSvgFile* pFile) const; diff --git a/DesktopEditor/raster/Metafile/svg/SvgObjects/CContainer.h b/DesktopEditor/raster/Metafile/svg/SvgObjects/CContainer.h index 4d57d48c2e..0b3502f464 100644 --- a/DesktopEditor/raster/Metafile/svg/SvgObjects/CContainer.h +++ b/DesktopEditor/raster/Metafile/svg/SvgObjects/CContainer.h @@ -3,8 +3,6 @@ #include "CObjectBase.h" -#include "../../../../graphics/pro/Fonts.h" - class CSvgFile; namespace SVG diff --git a/DesktopEditor/raster/Metafile/svg/SvgObjects/CFont.cpp b/DesktopEditor/raster/Metafile/svg/SvgObjects/CFont.cpp new file mode 100644 index 0000000000..589238874f --- /dev/null +++ b/DesktopEditor/raster/Metafile/svg/SvgObjects/CFont.cpp @@ -0,0 +1,90 @@ +#include "CFont.h" + +namespace SVG +{ + CGlyph::CGlyph(XmlUtils::CXmlNode &oNode) + : CPath(oNode) + { + std::wstring wsUnicode(oNode.GetAttribute(L"unicode")); + + if (!wsUnicode.empty()) + m_wchUnicode = wsUnicode[0]; + } + + wchar_t CGlyph::GetUnicode() const + { + return m_wchUnicode; + } + + CFontFace::CFontFace(XmlUtils::CXmlNode &oNode) + { + + } + + CFont::CFont(XmlUtils::CXmlNode &oNode) + : CAppliedObject(oNode), m_pMissingGlyph(NULL) + { + ParseGlyphs(oNode); + + m_oArguments.m_wsFontVariant = oNode.GetAttribute(L"font-variant"); + m_oArguments.m_wsFontStyle = oNode.GetAttribute(L"font-style"); + m_oArguments.m_wsFontWidght = oNode.GetAttribute(L"font-weight"); + } + + CFont::~CFont() + { + for (std::pair oElement : m_mGlyphs) + RELEASEOBJECT(oElement.second); + + RELEASEOBJECT(m_pMissingGlyph); + } + + void CFont::SetData(const std::map &mAttributes, unsigned short ushLevel, bool bHardMode) + { + } + + bool CFont::Apply(IRenderer *pRenderer, const CSvgFile *pFile, const TBounds &oObjectBounds) + { + return true; + } + + void CFont::ParseGlyphs(XmlUtils::CXmlNode &oNode) + { + std::vector arChilds; + + if (!oNode.GetChilds(arChilds) || arChilds.empty()) + return; + + for (XmlUtils::CXmlNode& oChild : arChilds) + { + if (L"glyph" == oChild.GetName()) + { + CGlyph *pGlyph = new CGlyph(oChild); + + if (NULL == pGlyph) + continue; + + if (m_mGlyphs.end() == m_mGlyphs.find(pGlyph->GetUnicode())) + m_mGlyphs.insert(std::make_pair(pGlyph->GetUnicode(), pGlyph)); + else + delete pGlyph; + } + else if (L"missing-glyph" == oChild.GetName()) + { + std::vector arMissingGlyphChilds; + if (!oChild.GetChilds(arMissingGlyphChilds) || arMissingGlyphChilds.empty()) + continue; + + for (XmlUtils::CXmlNode& oChildMissingGlyph : arMissingGlyphChilds) + { + if (L"path" == oChildMissingGlyph.GetName()) + { + m_pMissingGlyph = new CPath(oChildMissingGlyph); + if (NULL != m_pMissingGlyph) + break; + } + } + } + } + } +} diff --git a/DesktopEditor/raster/Metafile/svg/SvgObjects/CFont.h b/DesktopEditor/raster/Metafile/svg/SvgObjects/CFont.h new file mode 100644 index 0000000000..ea5242b380 --- /dev/null +++ b/DesktopEditor/raster/Metafile/svg/SvgObjects/CFont.h @@ -0,0 +1,53 @@ +#ifndef CFONT_H +#define CFONT_H + +#include "CPath.h" + +namespace SVG +{ + class CGlyph : public CPath + { + public: + CGlyph(XmlUtils::CXmlNode& oNode); + + wchar_t GetUnicode() const; + private: + wchar_t m_wchUnicode; + }; + + class CFontFace + { + public: + CFontFace(XmlUtils::CXmlNode& oNode); + private: + std::wstring m_wsSrcFaceName; + }; + + struct TFontArguments + { + std::wstring m_wsFontFamily; + std::wstring m_wsFontVariant; + std::wstring m_wsFontStyle; + std::wstring m_wsFontWidght; + }; + + class CFont : public CAppliedObject + { + public: + CFont(XmlUtils::CXmlNode& oNode); + ~CFont(); + + void SetData(const std::map &mAttributes, unsigned short ushLevel, bool bHardMode) override; + + bool Apply(IRenderer* pRenderer, const CSvgFile *pFile, const TBounds &oObjectBounds) override; + private: + void ParseGlyphs(XmlUtils::CXmlNode& oNode); + + TFontArguments m_oArguments; + + std::map m_mGlyphs; + CPath *m_pMissingGlyph; + }; +} + +#endif // CFONT_H diff --git a/DesktopEditor/raster/Metafile/svg/SvgObjects/CPattern.h b/DesktopEditor/raster/Metafile/svg/SvgObjects/CPattern.h index 2833568720..7ac81c88b8 100644 --- a/DesktopEditor/raster/Metafile/svg/SvgObjects/CPattern.h +++ b/DesktopEditor/raster/Metafile/svg/SvgObjects/CPattern.h @@ -2,6 +2,7 @@ #define CPATTERN_H #include "CContainer.h" +#include "../../../../graphics/pro/Fonts.h" namespace SVG { diff --git a/DesktopEditor/raster/Metafile/svg/SvgObjects/CSymbol.h b/DesktopEditor/raster/Metafile/svg/SvgObjects/CSymbol.h index f98f623ad4..7efe2b10fe 100644 --- a/DesktopEditor/raster/Metafile/svg/SvgObjects/CSymbol.h +++ b/DesktopEditor/raster/Metafile/svg/SvgObjects/CSymbol.h @@ -2,6 +2,7 @@ #define CSYMBOL_H #include "CContainer.h" +#include "../../../graphics/pro/Fonts.h" namespace SVG { diff --git a/DesktopEditor/raster/Metafile/svg/SvgObjects/CText.cpp b/DesktopEditor/raster/Metafile/svg/SvgObjects/CText.cpp index 9c6f1165fd..c324690132 100644 --- a/DesktopEditor/raster/Metafile/svg/SvgObjects/CText.cpp +++ b/DesktopEditor/raster/Metafile/svg/SvgObjects/CText.cpp @@ -152,12 +152,15 @@ namespace SVG double dX, dY; CalculatePosition(dX, dY); - ApplyFont(pRenderer, dX, dY); - - pRenderer->CommandDrawText(m_wsText, dX, dY, 0, 0); - - for (const CRenderedObject* pTSpan : m_arObjects) - pTSpan->Draw(pRenderer, pFile, oMode, pOtherStyles); + if (!UseExternalFont(pFile)) + { + ApplyFont(pRenderer, dX, dY); + + pRenderer->CommandDrawText(m_wsText, dX, dY, 0, 0); + + for (const CRenderedObject* pTSpan : m_arObjects) + pTSpan->Draw(pRenderer, pFile, oMode, pOtherStyles); + } EndPath(pRenderer, pFile, oOldMatrix, oMode, pOtherStyles); @@ -292,6 +295,24 @@ namespace SVG pRenderer->put_BrushColor1(m_oStyles.m_oFill.ToInt()); pRenderer->put_BrushAlpha1(255); } + + bool CTSpan::UseExternalFont(const CSvgFile *pFile) const + { + std::wstring wsFontFamily = DefaultFontFamily; + + if (!m_oFont.GetFamily().Empty()) + { + wsFontFamily = m_oFont.GetFamily().ToWString(); + CorrectFontFamily(wsFontFamily); + } + + CFont *pFont = pFile->GetFont(wsFontFamily); + + if (NULL == pFont) + return false; + + return true; + } TBounds CTSpan::GetBounds() const { diff --git a/DesktopEditor/raster/Metafile/svg/SvgObjects/CText.h b/DesktopEditor/raster/Metafile/svg/SvgObjects/CText.h index b67e5390fe..c0a15944ed 100644 --- a/DesktopEditor/raster/Metafile/svg/SvgObjects/CText.h +++ b/DesktopEditor/raster/Metafile/svg/SvgObjects/CText.h @@ -31,6 +31,8 @@ namespace SVG void ApplyStyle(IRenderer* pRenderer, const TSvgStyles* pStyles, const CSvgFile* pFile, int& nTypePath) const override; void ApplyFont(IRenderer* pRenderer, double& dX, double& dY) const; + bool UseExternalFont(const CSvgFile* pFile) const; + TBounds GetBounds() const override; double GetWidth() const; diff --git a/DesktopEditor/raster/Metafile/svg/SvgTypes.h b/DesktopEditor/raster/Metafile/svg/SvgTypes.h index 38ea443a6d..13a563466b 100644 --- a/DesktopEditor/raster/Metafile/svg/SvgTypes.h +++ b/DesktopEditor/raster/Metafile/svg/SvgTypes.h @@ -14,6 +14,7 @@ namespace SVG #define SvgDigit NSCSS::NSProperties::CDigit #define SvgString NSCSS::NSProperties::CString #define SvgColor NSCSS::NSProperties::CColor + #define SvgURL NSCSS::NSProperties::CURL #define SvgEnum NSCSS::NSProperties::CEnum #define SvgTransform NSCSS::NSProperties::CTransform From efa4433195232d7523a892360dead3f10cc4cbe9 Mon Sep 17 00:00:00 2001 From: Kirill Polyakov Date: Fri, 28 Jun 2024 20:58:19 +0300 Subject: [PATCH 02/18] Added rendering of custom fonts in svg --- .../raster/Metafile/svg/SvgObjects/CFont.cpp | 53 +++++++++++++++++++ .../raster/Metafile/svg/SvgObjects/CFont.h | 12 ++++- .../raster/Metafile/svg/SvgObjects/CText.cpp | 7 ++- .../raster/Metafile/svg/SvgObjects/CText.h | 2 +- 4 files changed, 69 insertions(+), 5 deletions(-) diff --git a/DesktopEditor/raster/Metafile/svg/SvgObjects/CFont.cpp b/DesktopEditor/raster/Metafile/svg/SvgObjects/CFont.cpp index 589238874f..276a106b15 100644 --- a/DesktopEditor/raster/Metafile/svg/SvgObjects/CFont.cpp +++ b/DesktopEditor/raster/Metafile/svg/SvgObjects/CFont.cpp @@ -9,6 +9,8 @@ namespace SVG if (!wsUnicode.empty()) m_wchUnicode = wsUnicode[0]; + + m_oHorizAdvX.SetValue(oNode.GetAttributeOrValue(L"horiz-adv-x")); } wchar_t CGlyph::GetUnicode() const @@ -29,6 +31,8 @@ namespace SVG m_oArguments.m_wsFontVariant = oNode.GetAttribute(L"font-variant"); m_oArguments.m_wsFontStyle = oNode.GetAttribute(L"font-style"); m_oArguments.m_wsFontWidght = oNode.GetAttribute(L"font-weight"); + + m_oHorizAdvX.SetValue(oNode.GetAttributeOrValue(L"horiz-adv-x")); } CFont::~CFont() @@ -45,6 +49,55 @@ namespace SVG bool CFont::Apply(IRenderer *pRenderer, const CSvgFile *pFile, const TBounds &oObjectBounds) { + return false; + } + + bool CFont::Draw(const std::wstring &wsText, double dX, double dY, IRenderer *pRenderer, const CSvgFile *pFile, CommandeMode oMode, const TSvgStyles *pStyles) const + { + if (NULL == pRenderer) + return false; + + double dM11, dM12, dM21, dM22, dRx, dRy; + + pRenderer->GetTransform(&dM11, &dM12, &dM21, &dM22, &dRx, &dRy); + + Aggplus::CMatrix oMatrix(dM11, dM12, dM21, dM22, dRx, dRy); + oMatrix.Translate(dX, dY); + + pRenderer->SetTransform(dM11, dM12, dM21, -dM22, oMatrix.tx(), oMatrix.ty()); + + MGlyphsMap::const_iterator itFound; + TBounds oGlyphBounds; + + for (wchar_t wchGlyph : wsText) + { + itFound = m_mGlyphs.find(wchGlyph); + + if (m_mGlyphs.cend() == itFound) + { + if (NULL == m_pMissingGlyph) + continue; + + m_pMissingGlyph->Draw(pRenderer, pFile, oMode, pStyles); + oMatrix.Translate(m_oHorizAdvX.ToDouble(NSCSS::Pixel), 0); + } + else + { + itFound->second->Draw(pRenderer, pFile, oMode, pStyles); + + if (!itFound->second->m_oHorizAdvX.Empty()) + oMatrix.Translate(itFound->second->m_oHorizAdvX.ToDouble(NSCSS::Pixel), 0); + else + oMatrix.Translate(m_oHorizAdvX.ToDouble(NSCSS::Pixel), 0); + } + + oMatrix.Translate(std::abs(oGlyphBounds.m_dRight - oGlyphBounds.m_dLeft), 0); + + pRenderer->SetTransform(dM11, dM12, dM21, -dM22, oMatrix.tx(), oMatrix.ty()); + } + + pRenderer->SetTransform(dM11, dM12, dM21, dM22, dRx, dRy); + return true; } diff --git a/DesktopEditor/raster/Metafile/svg/SvgObjects/CFont.h b/DesktopEditor/raster/Metafile/svg/SvgObjects/CFont.h index ea5242b380..bb1b1627ad 100644 --- a/DesktopEditor/raster/Metafile/svg/SvgObjects/CFont.h +++ b/DesktopEditor/raster/Metafile/svg/SvgObjects/CFont.h @@ -12,7 +12,10 @@ namespace SVG wchar_t GetUnicode() const; private: - wchar_t m_wchUnicode; + wchar_t m_wchUnicode; + SvgDigit m_oHorizAdvX; + + friend class CFont; }; class CFontFace @@ -40,13 +43,18 @@ namespace SVG void SetData(const std::map &mAttributes, unsigned short ushLevel, bool bHardMode) override; bool Apply(IRenderer* pRenderer, const CSvgFile *pFile, const TBounds &oObjectBounds) override; + bool Draw(const std::wstring& wsText, double dX, double dY, IRenderer* pRenderer, const CSvgFile *pFile, CommandeMode oMode = CommandeModeDraw, const TSvgStyles* pStyles = NULL) const; private: void ParseGlyphs(XmlUtils::CXmlNode& oNode); TFontArguments m_oArguments; - std::map m_mGlyphs; + typedef std::map MGlyphsMap; + + MGlyphsMap m_mGlyphs; CPath *m_pMissingGlyph; + + SvgDigit m_oHorizAdvX; }; } diff --git a/DesktopEditor/raster/Metafile/svg/SvgObjects/CText.cpp b/DesktopEditor/raster/Metafile/svg/SvgObjects/CText.cpp index c324690132..377b5d36a6 100644 --- a/DesktopEditor/raster/Metafile/svg/SvgObjects/CText.cpp +++ b/DesktopEditor/raster/Metafile/svg/SvgObjects/CText.cpp @@ -4,6 +4,7 @@ #include "../SvgUtils.h" #include "../CSvgFile.h" #include "CContainer.h" +#include "CFont.h" #include "CStyle.h" #ifndef MININT8 @@ -152,7 +153,7 @@ namespace SVG double dX, dY; CalculatePosition(dX, dY); - if (!UseExternalFont(pFile)) + if (!UseExternalFont(pFile, dX, dY, pRenderer, pFile, oMode, pOtherStyles)) { ApplyFont(pRenderer, dX, dY); @@ -296,7 +297,7 @@ namespace SVG pRenderer->put_BrushAlpha1(255); } - bool CTSpan::UseExternalFont(const CSvgFile *pFile) const + bool CTSpan::UseExternalFont(const CSvgFile *pFile, double dX, double dY, IRenderer *pRenderer, const CSvgFile *pFile, CommandeMode oMode, const TSvgStyles *pOtherStyles) const { std::wstring wsFontFamily = DefaultFontFamily; @@ -311,6 +312,8 @@ namespace SVG if (NULL == pFont) return false; + pFont->Draw(m_wsText, dX, dY, pRenderer, pFile, oMode, pOtherStyles); + return true; } diff --git a/DesktopEditor/raster/Metafile/svg/SvgObjects/CText.h b/DesktopEditor/raster/Metafile/svg/SvgObjects/CText.h index c0a15944ed..840807d01f 100644 --- a/DesktopEditor/raster/Metafile/svg/SvgObjects/CText.h +++ b/DesktopEditor/raster/Metafile/svg/SvgObjects/CText.h @@ -31,7 +31,7 @@ namespace SVG void ApplyStyle(IRenderer* pRenderer, const TSvgStyles* pStyles, const CSvgFile* pFile, int& nTypePath) const override; void ApplyFont(IRenderer* pRenderer, double& dX, double& dY) const; - bool UseExternalFont(const CSvgFile* pFile) const; + bool UseExternalFont(const CSvgFile* pFile, double dX, double dY, IRenderer* pRenderer, const CSvgFile* pFile, CommandeMode oMode = CommandeModeDraw, const TSvgStyles* pOtherStyles = NULL) const; TBounds GetBounds() const override; From 6eba82f43365b6b94b5cd3859e8dca19270ff335 Mon Sep 17 00:00:00 2001 From: Kirill Polyakov Date: Thu, 4 Jul 2024 18:00:06 +0300 Subject: [PATCH 03/18] Improvement in html to ooxml conversion of multi-level lists --- HtmlFile2/htmlfile2.cpp | 47 +++++++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/HtmlFile2/htmlfile2.cpp b/HtmlFile2/htmlfile2.cpp index 17fda94882..d41093a3e3 100644 --- a/HtmlFile2/htmlfile2.cpp +++ b/HtmlFile2/htmlfile2.cpp @@ -880,6 +880,24 @@ private: TTableStyles m_oStyles; }; +enum class EAbstructNumType +{ + Bullet, + CardinalText, + Chicago, + Decimal, + DecimalEnclosedCircle, + DecimalEnclosedFullstop, + DecimalEnclosedParen, + DecimalZero, + LowerLetter, + LowerRoman, + None, + OrdinalText, + UpperLetter, + UpperRoman +}; + void replace_all(std::wstring& s, const std::wstring& s1, const std::wstring& s2) { size_t pos = s.find(s1); @@ -2520,7 +2538,7 @@ private: int nDeath = m_oLightReader.GetDepth(); while(m_oLightReader.ReadNextSiblingNode(nDeath)) { - std::wstring sName = m_oLightReader.GetName(); + const std::wstring sName = m_oLightReader.GetName(); if (sName == L"optgroup") { GetSubClass(oXml, sSelectors); @@ -2556,9 +2574,24 @@ private: CloseP(oXml, sSelectors); CTextSettings oTSLiP(oTS); + + if (std::wstring::npos != oTS.sPStyle.find(L"")) + { + wrP(oXml, sSelectors, oTS); + CloseP(oXml, sSelectors); + oTSLiP.sPStyle.clear(); + } + oTSLiP.nLi++; + + const std::wstring wsOldPStyle{oTSLiP.sPStyle}; + oTSLiP.sPStyle += L""; + + wrP(oXml, sSelectors, oTSLiP); + oTSLiP.sPStyle = wsOldPStyle; + readStream(oXml, sSelectors, oTSLiP); CloseP(oXml, sSelectors); @@ -2574,21 +2607,21 @@ private: m_oNumberXml.WriteString(sStart); m_oNumberXml.WriteString(L"\"/>"); + m_oNumberXml.WriteString(L"\"/>"); } } From 74d7bbca7c739ea737d68548653343cd59a1d9fc Mon Sep 17 00:00:00 2001 From: Kirill Polyakov Date: Thu, 4 Jul 2024 18:00:32 +0300 Subject: [PATCH 04/18] Improvement in html to xml conversion of image size calculation --- HtmlFile2/htmlfile2.cpp | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/HtmlFile2/htmlfile2.cpp b/HtmlFile2/htmlfile2.cpp index d41093a3e3..c2c1fe02ca 100644 --- a/HtmlFile2/htmlfile2.cpp +++ b/HtmlFile2/htmlfile2.cpp @@ -2816,7 +2816,7 @@ private: 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, const std::wstring& wsSrc) + void ImageAlternative(NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, const CTextSettings& oTS, const std::wstring& wsAlt, const std::wstring& wsSrc, unsigned int unWidth, unsigned int unHeight) { if (wsAlt.empty()) { @@ -2853,19 +2853,43 @@ private: { std::wstring wsAlt, sSrcM; bool bRes = false; + unsigned int unWidth = 0, unHeight = 0; + while (m_oLightReader.MoveToNextAttribute()) { - std::wstring wsName = m_oLightReader.GetName(); + const std::wstring wsName = m_oLightReader.GetName(); if (wsName == L"alt") wsAlt = m_oLightReader.GetText(); else if (wsName == L"src") sSrcM = m_oLightReader.GetText(); + else if (wsName == L"width") + { + NSCSS::NSProperties::CDigit oDigit; + if (oDigit.SetValue(m_oLightReader.GetText())) + { + if (NSCSS::UnitMeasure::None == oDigit.GetUnitMeasure()) + unWidth = static_cast(NSCSS::CUnitMeasureConverter::ConvertPx(oDigit.ToDouble(), NSCSS::Inch, 96) * 914400); + else + unWidth = static_cast(oDigit.ToDouble(NSCSS::Inch) * 914400); + } + } + else if (wsName == L"height") + { + NSCSS::NSProperties::CDigit oDigit; + if (oDigit.SetValue(m_oLightReader.GetText())) + { + if (NSCSS::UnitMeasure::None == oDigit.GetUnitMeasure()) + unHeight = static_cast(NSCSS::CUnitMeasureConverter::ConvertPx(oDigit.ToDouble(), NSCSS::Inch, 96) * 914400 + 0.5); + else + unHeight = static_cast(oDigit.ToDouble(NSCSS::Inch) * 914400 + 0.5); + } + } } m_oLightReader.MoveToElement(); if (sSrcM.empty()) { - ImageAlternative(oXml, sSelectors, oTS, wsAlt, sSrcM); + ImageAlternative(oXml, sSelectors, oTS, wsAlt, sSrcM, unWidth, unHeight); return; } @@ -2896,7 +2920,7 @@ private: std::transform(sExtention.begin(), sExtention.end(), sExtention.begin(), tolower); if (NotValidExtension(sExtention)) { - ImageAlternative(oXml, sSelectors, oTS, wsAlt, sSrcM); + ImageAlternative(oXml, sSelectors, oTS, wsAlt, sSrcM, unWidth, unHeight); return; } @@ -2942,11 +2966,11 @@ private: } if (!bRes) - ImageAlternative(oXml, sSelectors, oTS, wsAlt, sSrcM); + ImageAlternative(oXml, sSelectors, oTS, wsAlt, sSrcM, unWidth, unHeight); else { wrP(oXml, sSelectors, oTS); - ImageRels(oXml, nImageId, sImageSrc, sExtention); + ImageRels(oXml, nImageId, sImageSrc, sExtention, unWidth, unHeight); } } @@ -3082,7 +3106,7 @@ private: pXml->WriteString(L""); } - void ImageRels (NSStringUtils::CStringBuilder* oXml, int nImageId, const std::wstring& sImageSrc, const std::wstring& sExtention) + void ImageRels (NSStringUtils::CStringBuilder* oXml, int nImageId, const std::wstring& sImageSrc, const std::wstring& sExtention, unsigned int unWidth = 0, unsigned int unHeight = 0) { bool bNew = nImageId < 0; if (bNew) @@ -3108,6 +3132,9 @@ private: m_oDocXmlRels.WriteString(L"\"/>"); } + if (0 != unWidth && 0 != unHeight) + return WriteImage(oXml, unWidth, unHeight, sImageId); + // Получаем размеры картинки int nHy = oBgraFrame.get_Height(); int nWx = oBgraFrame.get_Width(); From c7fc2bc3a750e877eb9d32b7e9f701102aacf398 Mon Sep 17 00:00:00 2001 From: Kirill Polyakov Date: Tue, 9 Jul 2024 16:44:32 +0300 Subject: [PATCH 05/18] The blockquote tag implementation has been added to the html to ooxml conversion --- HtmlFile2/htmlfile2.cpp | 89 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 79 insertions(+), 10 deletions(-) diff --git a/HtmlFile2/htmlfile2.cpp b/HtmlFile2/htmlfile2.cpp index c2c1fe02ca..556e9fa4ce 100644 --- a/HtmlFile2/htmlfile2.cpp +++ b/HtmlFile2/htmlfile2.cpp @@ -979,6 +979,7 @@ private: NSStringUtils::CStringBuilder m_oDocXml; // document.xml NSStringUtils::CStringBuilder m_oNoteXml; // footnotes.xml NSStringUtils::CStringBuilder m_oNumberXml; // numbering.xml + NSStringUtils::CStringBuilder m_oWebSettings; // webSettings.xml struct TState { @@ -997,6 +998,7 @@ private: std::vector m_arrImages; // Картинки std::map m_mFootnotes; // Сноски std::map m_mBookmarks; // Закладки + std::map m_mDivs; // Div элементы public: CHtmlFile2_Private() @@ -1019,6 +1021,7 @@ public: m_oDocXml .Clear(); m_oNoteXml .Clear(); m_oNumberXml .Clear(); + m_oWebSettings .Clear(); } // Проверяет наличие тэга html @@ -1100,15 +1103,6 @@ public: 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""; m_oNoteXml += L""; m_oStylesXml += L""; + m_oWebSettings += L""; m_nId += 7; @@ -1321,6 +1316,18 @@ public: oNumberingWriter.WriteStringUTF8(m_oNumberXml.GetData()); oNumberingWriter.CloseFile(); } + + // webSettings.xml + if (!m_mDivs.empty()) + m_oWebSettings.WriteString(L""); + + m_oWebSettings.WriteString(L""); + NSFile::CFileBinary oWebSettingsWriter; + if (oWebSettingsWriter.CreateFileW(m_sDst + L"/word/webSettings.xml")) + { + oWebSettingsWriter.WriteStringUTF8(m_oWebSettings.GetData()); + oWebSettingsWriter.CloseFile(); + } } // Конвертирует html в xhtml @@ -1692,6 +1699,56 @@ private: pXml->WriteString(L"\"/>"); } + std::wstring WriteDiv(NSStringUtils::CStringBuilder* pXml, std::vector& sSelectors, CTextSettings& oTS) + { + if (NULL == pXml || sSelectors.empty()) + return std::wstring(); + + const std::wstring wsKeyWord{sSelectors.back().m_wsName}; + + std::map::const_iterator itFound = m_mDivs.find(wsKeyWord); + + if (m_mDivs.end() != itFound) + return std::to_wstring(itFound->second); + + const std::wstring wsId{std::to_wstring(m_mDivs.size() + 1)}; + + if (m_mDivs.empty()) + pXml->WriteString(L""); + + m_oStylesCalculator.GetCompiledStyle(oTS.oPriorityStyle, sSelectors); + + INT nMarLeft = 720; + INT nMarRight = 720; + + if (!oTS.oPriorityStyle.m_oMargin.GetLeft().Empty() && !oTS.oPriorityStyle.m_oMargin.GetLeft().Zero()) + nMarLeft = oTS.oPriorityStyle.m_oMargin.GetLeft().ToInt(NSCSS::Twips, m_oPageData.GetWidth().ToInt(NSCSS::Twips)); + + if (!oTS.oPriorityStyle.m_oMargin.GetRight().Empty() && !oTS.oPriorityStyle.m_oMargin.GetRight().Zero()) + nMarRight = oTS.oPriorityStyle.m_oMargin.GetRight().ToInt(NSCSS::Twips, m_oPageData.GetWidth().ToInt(NSCSS::Twips)); + + if (L"blockquote" == wsKeyWord) + { + pXml->WriteString(L""); + pXml->WriteString(L""); + pXml->WriteString(L""); + pXml->WriteString(L""); + pXml->WriteString(L""); + pXml->WriteString(L""); + pXml->WriteString(L""); + pXml->WriteString(L""); + pXml->WriteString(L""); + pXml->WriteString(L""); + pXml->WriteString(L""); + pXml->WriteString(L""); + pXml->WriteString(L""); + } + + m_mDivs.insert(std::make_pair(wsKeyWord, m_mDivs.size() + 1)); + + return wsId; + } + std::wstring GetSubClass(NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors) { NSCSS::CNode oNode; @@ -2159,8 +2216,14 @@ private: else bResult = readStream(&oXmlData, sSelectors, oTS); } + else if (sName == L"blockquote") + { + CTextSettings oNewTS{oTS}; + oNewTS.sPStyle += L""; + bResult = readStream(&oXmlData, sSelectors, oNewTS); + } // С нового абзаца - else if(sName == L"article" || sName == L"header" || sName == L"blockquote" || sName == L"main" || sName == L"dir" || + else if(sName == L"article" || sName == L"header" || 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" || @@ -2998,6 +3061,12 @@ private: NSCSS::CCompiledStyle::StyleEquation(oStyle, oStyleSetting); + if (!oTS.oPriorityStyle.Empty()) + { + NSCSS::CCompiledStyle oPriorityStyle{oTS.oPriorityStyle}; + NSCSS::CCompiledStyle::StyleEquation(oPriorityStyle, oStyle); + } + std::wstring sPStyle = GetStyle(oStyle, true); if (sPStyle.empty() && oTS.sPStyle.empty()) From d4f3cd38010e9e7fd4201fc41e727a2a5782c782 Mon Sep 17 00:00:00 2001 From: Kirill Polyakov Date: Tue, 9 Jul 2024 16:44:50 +0300 Subject: [PATCH 06/18] Fix bugs in html to ooxml conversion --- HtmlFile2/htmlfile2.cpp | 90 +++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 58 deletions(-) diff --git a/HtmlFile2/htmlfile2.cpp b/HtmlFile2/htmlfile2.cpp index 556e9fa4ce..5eee9ae1d2 100644 --- a/HtmlFile2/htmlfile2.cpp +++ b/HtmlFile2/htmlfile2.cpp @@ -72,18 +72,21 @@ struct CTextSettings { bool bBdo; // Реверс текста bool bPre; // Сохранение форматирования (Сохранение пробелов, табуляций, переносов строк) + bool bQ; // Цитата bool bAddSpaces; // Добавлять пробелы перед текстом? bool bMergeText; // Объединять подяр идущий текст в 1? int nLi; // Уровень списка std::wstring sRStyle; // w:rStyle std::wstring sPStyle; // w:pStyle - CTextSettings(bool _bBdo, bool _bPre, bool _bAddSpaces, bool _bMergeText, int _nLi, const std::wstring& _sRStyle, const std::wstring& _sPStyle) : - bBdo(_bBdo), bPre(_bPre), bAddSpaces(_bAddSpaces), bMergeText(_bMergeText), nLi(_nLi), sRStyle(_sRStyle), sPStyle(_sPStyle) + NSCSS::CCompiledStyle oPriorityStyle; + + CTextSettings() + : bBdo(false), bPre(false), bQ(false), bAddSpaces(true), bMergeText(false), nLi(-1) {} CTextSettings(const CTextSettings& oTS) : - bBdo(oTS.bBdo), bPre(oTS.bPre), bAddSpaces(oTS.bAddSpaces), bMergeText(oTS.bMergeText), nLi(oTS.nLi), sRStyle(oTS.sRStyle), sPStyle(oTS.sPStyle) + bBdo(oTS.bBdo), bPre(oTS.bPre), bQ(oTS.bQ), bAddSpaces(oTS.bAddSpaces), bMergeText(oTS.bMergeText), nLi(oTS.nLi), sRStyle(oTS.sRStyle), sPStyle(oTS.sPStyle) {} void AddRStyle(const std::wstring& wsStyle) @@ -1824,7 +1827,7 @@ private: m_oDocXml.WriteString(L"\"/>"); */ - readStream(&m_oDocXml, sSelectors, { false, false, true, false, -1, L"", L"" }); + readStream(&m_oDocXml, sSelectors, {}); } bool readInside (NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, const CTextSettings& oTS, const std::wstring& sName) @@ -1858,8 +1861,14 @@ private: if (OpenR(oXml)) { sRStyle = wrRPr(oXml, sSelectors, oTS); + + if (oTS.bQ) + oXml->WriteString(L"""); + OpenT(oXml); } + else if (oTS.bQ) + oXml->WriteString(L"""); if(oTS.bBdo) std::reverse(sText.begin(), sText.end()); @@ -1916,6 +1925,9 @@ private: oXml->WriteEncodeXmlString(sText); + if (oTS.bQ) + oXml->WriteString(L"""); + if (!oTS.bMergeText) { CloseT(oXml); @@ -1942,11 +1954,7 @@ private: // Направление текста 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(); + const std::wstring sDir{GetArgumentValue(L"dir")}; CTextSettings oTSBdo(oTS); oTSBdo.bBdo = (sDir == L"rtl"); @@ -2076,26 +2084,9 @@ private: // Цитата, выделенная кавычками, обычно выделяется курсивом else if(sName == L"q") { - wrP(oXml, sSelectors, oTS); - oXml->WriteString(L""); - std::wstring sRStyle = wrRPr(oXml, sSelectors, oTS); - oXml->WriteString(L"""); - - CTextSettings oTSR(oTS); - oTSR.AddRStyle(L""); - readStream(oXml, sSelectors, oTSR); - - wrP(oXml, sSelectors, oTS); - oXml->WriteString(L""); - if (!sRStyle.empty()) - { - oXml->WriteString(L"WriteString(sRStyle); - oXml->WriteString(L"\"/>"); - oXml->WriteString(oTS.sRStyle); - oXml->WriteString(L""); - } - oXml->WriteString(L"""); + CTextSettings oTSQ(oTS); + oTSQ.bQ = true; + bResult = readStream(oXml, sSelectors, oTSQ); } // Текст верхнего регистра else if(sName == L"rt" || sName == L"sup") @@ -2881,35 +2872,18 @@ private: void ImageAlternative(NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, const CTextSettings& oTS, const std::wstring& wsAlt, const std::wstring& wsSrc, unsigned int unWidth, unsigned int unHeight) { - if (wsAlt.empty()) - { - //TODO:: реализовать отображение того, что картинку не удалось получить - if (wsSrc.empty()) - WriteEmptyParagraph(oXml, false, m_oState.m_bInP); - else - { - m_oDocXmlRels.WriteString(L""); + m_oDocXmlRels.WriteString(L""); - const bool bOpenedP{OpenP(oXml)}; + const bool bOpenedP{OpenP(oXml)}; - WriteEmptyImage(oXml, 304800, 304800); + WriteEmptyImage(oXml, 304800, 304800, L"", wsAlt); - if (bOpenedP) - CloseP(oXml, sSelectors); - } - return; - } - - wrP(oXml, sSelectors, oTS); - oXml->WriteString(L""); - wrRPr(oXml, sSelectors, oTS); - oXml->WriteString(L""); - oXml->WriteEncodeXmlString(wsAlt); - oXml->WriteString(L""); + if (bOpenedP) + CloseP(oXml, sSelectors); } void readImage (NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, const CTextSettings& oTS) @@ -3163,13 +3137,13 @@ private: pXml->WriteString(L"\"/>"); } - void WriteEmptyImage(NSStringUtils::CStringBuilder* pXml, int nWidth, int nHeight) + void WriteEmptyImage(NSStringUtils::CStringBuilder* pXml, int nWidth, int nHeight, const std::wstring& wsName = L"", const std::wstring& wsDescr = L"") { pXml->WriteString(L""); - pXml->WriteString(L""); + pXml->WriteString(L""); pXml->WriteString(L""); pXml->WriteString(L""); - pXml->WriteString(L""); + pXml->WriteString(L""); pXml->WriteString(L""); pXml->WriteString(L""); pXml->WriteString(L""); From f30aba8bf1d4a2b0b3c4660ad71b59d2d609e7b2 Mon Sep 17 00:00:00 2001 From: Kirill Polyakov Date: Wed, 10 Jul 2024 15:23:05 +0300 Subject: [PATCH 07/18] Added support for the rules attribute for tables in html to ooxml conversion --- .../3dParty/html/css/src/StyleProperties.cpp | 76 +++++++- Common/3dParty/html/css/src/StyleProperties.h | 22 ++- HtmlFile2/htmlfile2.cpp | 167 ++++++++++++++---- 3 files changed, 221 insertions(+), 44 deletions(-) diff --git a/Common/3dParty/html/css/src/StyleProperties.cpp b/Common/3dParty/html/css/src/StyleProperties.cpp index 99efa51952..b1ee0e668b 100644 --- a/Common/3dParty/html/css/src/StyleProperties.cpp +++ b/Common/3dParty/html/css/src/StyleProperties.cpp @@ -375,7 +375,7 @@ namespace NSCSS bool CDigit::SetValue(const std::wstring &wsValue, unsigned int unLevel, bool bHardMode) { - if (wsValue.empty() || (CHECK_CONDITIONS && !bHardMode) || unLevel < m_unLevel) + if (wsValue.empty() || (CHECK_CONDITIONS && !bHardMode)) return false; std::wstring wsNewValue = wsValue; @@ -432,6 +432,13 @@ namespace NSCSS uchBlue == oRGB.uchBlue; } + bool TRGB::operator!=(const TRGB& oRGB) const + { + return uchRed != oRGB.uchRed || + uchGreen != oRGB.uchGreen || + uchBlue != oRGB.uchBlue; + } + TRGB CColor::ConvertHEXtoRGB(const std::wstring &wsValue) { TRGB oRGB; @@ -459,6 +466,44 @@ namespace NSCSS return std::wstring(arTemp, 6); } + bool CColor::operator==(const CColor& oColor) const + { + if (m_enType != oColor.m_enType || m_oOpacity != oColor.m_oOpacity) + return false; + + switch(m_enType) + { + case ColorEmpty: + case ColorNone: + return true; + case ColorRGB: + return (*static_cast(m_oValue)) == (*static_cast(oColor.m_oValue)); + case ColorHEX: + return (*static_cast(m_oValue)) == (*static_cast(oColor.m_oValue)); + case ColorUrl: + return (*static_cast(m_oValue)) == (*static_cast(oColor.m_oValue)); + } + } + + bool CColor::operator!=(const CColor& oColor) const + { + if (m_enType != oColor.m_enType || m_oOpacity != oColor.m_oOpacity) + return true; + + switch(m_enType) + { + case ColorEmpty: + case ColorNone: + return false; + case ColorRGB: + return (*static_cast(m_oValue)) != (*static_cast(oColor.m_oValue)); + case ColorHEX: + return (*static_cast(m_oValue)) != (*static_cast(oColor.m_oValue)); + case ColorUrl: + return (*static_cast(m_oValue)) != (*static_cast(oColor.m_oValue)); + } + } + CColor::CColor() : CValue(NULL, 0, false), m_oOpacity(1.) {} @@ -1473,6 +1518,10 @@ namespace NSCSS : m_bBlock(false) {} + CBorderSide::CBorderSide(const CBorderSide& oBorderSide) + : m_oWidth(oBorderSide.m_oWidth), m_oStyle(oBorderSide.m_oStyle), m_oColor(oBorderSide.m_oColor), m_bBlock(oBorderSide.m_bBlock) + {} + void CBorderSide::Clear() { m_oWidth.Clear(); @@ -1620,6 +1669,13 @@ namespace NSCSS m_oColor == oBorderSide.m_oColor; } + bool CBorderSide::operator!=(const CBorderSide& oBorderSide) const + { + return m_oWidth != oBorderSide.m_oWidth || + m_oStyle != oBorderSide.m_oStyle || + m_oColor != oBorderSide.m_oColor; + } + CBorderSide &CBorderSide::operator =(const CBorderSide &oBorderSide) { m_oWidth = oBorderSide.m_oWidth; @@ -1865,6 +1921,14 @@ namespace NSCSS m_oBottom == oBorder.m_oBottom; } + bool CBorder::operator!=(const CBorder& oBorder) const + { + return m_oLeft != oBorder.m_oLeft || + m_oTop != oBorder.m_oTop || + m_oRight != oBorder.m_oRight || + m_oBottom != oBorder.m_oBottom; + } + CBorder &CBorder::operator =(const CBorder &oBorder) { m_oLeft = oBorder.m_oLeft; @@ -2537,6 +2601,16 @@ namespace NSCSS return m_wsValue; } + bool CURL::operator==(const CURL& oValue) const + { + return m_wsValue == oValue.m_wsValue; + } + + bool CURL::operator!=(const CURL& oValue) const + { + return m_wsValue != oValue.m_wsValue; + } + CEnum::CEnum() : CValue(INT_MAX, 0, false){} diff --git a/Common/3dParty/html/css/src/StyleProperties.h b/Common/3dParty/html/css/src/StyleProperties.h index f424ceda99..aec7b927ec 100644 --- a/Common/3dParty/html/css/src/StyleProperties.h +++ b/Common/3dParty/html/css/src/StyleProperties.h @@ -75,13 +75,13 @@ namespace NSCSS return *this; } - CValue& operator =(const T& oValue) + virtual CValue& operator =(const T& oValue) { //m_oValue = oValue.m_oValue; return *this; } - CValue& operator+=(const CValue& oValue) + virtual CValue& operator+=(const CValue& oValue) { if (m_unLevel > oValue.m_unLevel || (m_bImportant && !oValue.m_bImportant) || oValue.Empty()) return *this; @@ -93,10 +93,15 @@ namespace NSCSS return *this; } - bool operator==(const CValue& oValue) const + virtual bool operator==(const CValue& oValue) const { return m_oValue == oValue.m_oValue; } + + virtual bool operator!=(const CValue& oValue) const + { + return m_oValue != oValue.m_oValue; + } }; class CString : public CValue @@ -179,6 +184,7 @@ namespace NSCSS bool Empty() const; bool operator==(const TRGB& oRGB) const; + bool operator!=(const TRGB& oRGB) const; }; class CURL @@ -193,6 +199,9 @@ namespace NSCSS bool SetValue(const std::wstring& wsValue); std::wstring GetValue() const; + + bool operator==(const CURL& oValue) const; + bool operator!=(const CURL& oValue) const; private: std::wstring m_wsValue; }; @@ -231,6 +240,9 @@ namespace NSCSS static TRGB ConvertHEXtoRGB(const std::wstring& wsValue); static std::wstring ConvertRGBtoHEX(const TRGB& oValue); + + bool operator==(const CColor& oColor) const; + bool operator!=(const CColor& oColor) const; private: CDigit m_oOpacity; ColorType m_enType; @@ -426,6 +438,7 @@ namespace NSCSS { public: CBorderSide(); + CBorderSide(const CBorderSide& oBorderSide); void Clear(); @@ -451,6 +464,7 @@ namespace NSCSS CBorderSide& operator+=(const CBorderSide& oBorderSide); bool operator==(const CBorderSide& oBorderSide) const; + bool operator!=(const CBorderSide& oBorderSide) const; CBorderSide& operator =(const CBorderSide& oBorderSide); private: CDigit m_oWidth; @@ -521,7 +535,7 @@ namespace NSCSS CBorder& operator+=(const CBorder& oBorder); bool operator==(const CBorder& oBorder) const; - + bool operator!=(const CBorder& oBorder) const; CBorder& operator =(const CBorder& oBorder); private: CBorderSide m_oLeft; diff --git a/HtmlFile2/htmlfile2.cpp b/HtmlFile2/htmlfile2.cpp index 5eee9ae1d2..c16d3d9fa8 100644 --- a/HtmlFile2/htmlfile2.cpp +++ b/HtmlFile2/htmlfile2.cpp @@ -102,39 +102,6 @@ struct CTextSettings } }; -std::wstring CreateBorders(const NSCSS::NSProperties::CBorder& oBorder, const NSCSS::NSProperties::CIndent* pPadding = NULL) -{ - if (oBorder.EqualSides() && (NULL == pPadding || pPadding->Equals())) - { - const std::wstring wsBorderStyle = NSCSS::CDocumentStyle::CalculateBorderStyle(oBorder.GetLeftBorder(), ((NULL == pPadding) ? NULL : (&(pPadding->GetLeft())))); - - return L"" + - L"" + - L"" + - L""; - } - else - { - std::wstring wsTable; - - if (oBorder.GetTopBorder().Valid()) - wsTable += L"GetTop())))) + L"/>"; - - if (oBorder.GetLeftBorder().Valid()) - wsTable += L"GetLeft())))) + L"/>"; - - if (oBorder.GetBottomBorder().Valid()) - wsTable += L"GetBottom())))) + L"/>"; - - if (oBorder.GetRightBorder().Valid()) - wsTable += L"GetRight())))) + L"/>"; - - return wsTable; - } - - return L""; -} - void WriteEmptyParagraph(NSStringUtils::CStringBuilder* pXml, bool bVahish = false, bool bInP = false) { if (NULL == pXml) @@ -186,8 +153,17 @@ struct TTableStyles std::wstring m_wsAlign; + enum ETableRules + { + All, + Group, + Cols, + None, + Rows + } m_enRules; + TTableStyles() - : m_nCellSpacing(-1), m_bHaveBorderAttribute(false) + : m_nCellSpacing(-1), m_bHaveBorderAttribute(false), m_enRules(All) {} bool Empty() const @@ -196,6 +172,67 @@ struct TTableStyles } }; +std::wstring CreateBorders(const NSCSS::NSProperties::CBorder& oBorder, const NSCSS::NSProperties::CIndent* pPadding = NULL, bool bAddIntermediateLines = false, TTableStyles::ETableRules enTableRule = TTableStyles::ETableRules::None) +{ + std::wstring wsTable; + + if (oBorder.EqualSides() && (NULL == pPadding || pPadding->Equals())) + { + const std::wstring wsBorderStyle = NSCSS::CDocumentStyle::CalculateBorderStyle(oBorder.GetLeftBorder(), ((NULL == pPadding) ? NULL : (&(pPadding->GetLeft())))); + + wsTable = L"" + L"" + + L"" + L""; + } + else + { + + if (oBorder.GetTopBorder().Valid()) + wsTable += L"GetTop())))) + L"/>"; + + if (oBorder.GetLeftBorder().Valid()) + wsTable += L"GetLeft())))) + L"/>"; + + if (oBorder.GetBottomBorder().Valid()) + wsTable += L"GetBottom())))) + L"/>"; + + if (oBorder.GetRightBorder().Valid()) + wsTable += L"GetRight())))) + L"/>"; + } + + if (!bAddIntermediateLines) + return wsTable; + + if (TTableStyles::ETableRules::Rows == enTableRule || TTableStyles::ETableRules::All == enTableRule) + { + NSCSS::NSProperties::CBorderSide oNewSide(oBorder.GetBottomBorder()); + oNewSide.SetWidth(L"1pt", 0, true); + + wsTable += L""; + } + + if (TTableStyles::ETableRules::Cols == enTableRule || TTableStyles::ETableRules::All == enTableRule) + { + NSCSS::NSProperties::CBorderSide oNewSide(oBorder.GetRightBorder()); + oNewSide.SetWidth(L"1pt", 0, true); + + wsTable += L""; + } + + return wsTable; +} + +std::wstring CreateBorders(const std::wstring& wsStyle, UINT unSize, UINT unSpace, const std::wstring& wsAuto) +{ + const std::wstring wsBodyBorder{L"w:val=\"" + wsStyle + L"\" w:sz=\"" + std::to_wstring(unSize) + L"\" w:space=\"" + std::to_wstring(unSpace) + L"\" w:color=\"" + wsAuto + L"\""}; + + return L"" + + L"" + + L"" + + L""; +} + +#define CreateOutsetBorders(enType) CreateBorders(L"outset", 6, 0, L"auto", enType) + struct TTableRowStyle { UINT m_unMaxIndex; @@ -283,6 +320,15 @@ public: return pCell; } + static CTableCell* CreateEmpty(const TTableCellStyle* pStyle) + { + CTableCell *pCell = new CTableCell(1, 1, false, true); + + pCell->m_oStyles.Copy(pStyle); + + return pCell; + } + void SetMode(ERowParseMode eMode) { m_enMode = eMode; @@ -341,6 +387,26 @@ public: m_oStyles.m_oBorder = oBorder; } + void ClearTopBorder() + { + m_oStyles.m_oBorder.SetTopSide(L"none", 0, true); + } + + void ClearLeftBorder() + { + m_oStyles.m_oBorder.SetLeftSide(L"none", 0, true); + } + + void ClearBottomBorder() + { + m_oStyles.m_oBorder.SetBottomSide(L"none", 0, true); + } + + void ClearRightBorder() + { + m_oStyles.m_oBorder.SetRightSide(L"none", 0, true); + } + void SetPadding(const NSCSS::NSProperties::CIndent& oPadding) { m_oStyles.m_oPadding = oPadding; @@ -402,10 +468,10 @@ public: else if (1 < m_unRowSpan) oCell += L""; - if (!m_oStyles.m_oBorder.Zero() && !m_oStyles.m_oBorder.Empty()) + if (!m_oStyles.m_oBorder.Zero() && !m_oStyles.m_oBorder.Empty() && m_oStyles.m_oBorder != oTableStyles.m_oBorder) oCell += L"" + CreateBorders(m_oStyles.m_oBorder) + L""; - else if (oTableStyles.m_bHaveBorderAttribute) - oCell += L""; + // else if (oTableStyles.m_bHaveBorderAttribute) + // oCell += L"" + CreateOutsetBorders(oTableStyles.m_enRules) + L""; if (!m_oStyles.m_oBackground.Empty()) { @@ -564,7 +630,7 @@ public: return m_arCells.size(); } - std::wstring ConvertToOOXML(const TTableStyles& oTableStyles) + std::wstring ConvertToOOXML(const TTableStyles& oTableStyles) { if (m_arCells.empty()) return std::wstring(); @@ -699,6 +765,23 @@ public: m_oStyles.m_wsAlign = wsValue; } + void SetRules(const std::wstring& wsValue) + { + if (wsValue.empty()) + return; + + if (NSStringFinder::Equals(wsValue, L"all")) + m_oStyles.m_enRules = TTableStyles::ETableRules::All; + else if (NSStringFinder::Equals(wsValue, L"group")) + m_oStyles.m_enRules = TTableStyles::ETableRules::Group; + else if (NSStringFinder::Equals(wsValue, L"cols")) + m_oStyles.m_enRules = TTableStyles::ETableRules::Cols; + else if (NSStringFinder::Equals(wsValue, L"none")) + m_oStyles.m_enRules = TTableStyles::ETableRules::None; + else if (NSStringFinder::Equals(wsValue, L"rows")) + m_oStyles.m_enRules = TTableStyles::ETableRules::Rows; + } + void HaveBorderAttribute() { m_oStyles.m_bHaveBorderAttribute = true; @@ -780,6 +863,9 @@ public: for (CTableRow* pRow : m_arRows) { + if (NULL == pRow || 0 == pRow->GetCount()) + continue; + for (UINT unIndex = pRow->GetIndex(); unIndex < unMaxIndex; ++unIndex) pRow->InsertCell(CTableCell::CreateEmpty(), unIndex); } @@ -820,7 +906,7 @@ public: oTable += L""; if (!m_oStyles.m_oBorder.Empty() && !m_oStyles.m_oBorder.Zero()) - oTable += L"" + CreateBorders(m_oStyles.m_oBorder) + L""; + oTable += L"" + CreateBorders(m_oStyles.m_oBorder, NULL, true, m_oStyles.m_enRules) + L""; if (!m_oStyles.m_oPadding.Empty() && !m_oStyles.m_oPadding.Zero()) { @@ -2507,6 +2593,9 @@ private: if (sSelectors.back().m_mAttributes.end() != sSelectors.back().m_mAttributes.find(L"cellpadding")) oStyle.m_oPadding.SetValues(sSelectors.back().m_mAttributes[L"cellpadding"] + L"px", 0, true); + if (sSelectors.back().m_mAttributes.end() != sSelectors.back().m_mAttributes.find(L"rules")) + oTable.SetRules(sSelectors.back().m_mAttributes[L"rules"]); + if (oStyle.m_oBorder.GetCollapse() == NSCSS::NSProperties::BorderCollapse::Collapse) oTable.SetCellSpacing(0); else if (sSelectors.back().m_mAttributes.end() != sSelectors.back().m_mAttributes.find(L"cellspacing")) From 4bb3953171a2f5f32773daa1535ab13431030017 Mon Sep 17 00:00:00 2001 From: Kirill Polyakov Date: Fri, 12 Jul 2024 18:03:18 +0300 Subject: [PATCH 08/18] Added colgroup implementation for tables in html to ooxml conversion --- HtmlFile2/htmlfile2.cpp | 96 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 2 deletions(-) diff --git a/HtmlFile2/htmlfile2.cpp b/HtmlFile2/htmlfile2.cpp index c16d3d9fa8..b678480f43 100644 --- a/HtmlFile2/htmlfile2.cpp +++ b/HtmlFile2/htmlfile2.cpp @@ -48,6 +48,10 @@ #define SAVE_NORMALIZED_HTML 0 +#define RELEASE_VECTOR(vector_object, object_type) \ + for (object_type* pElement : vector_object) \ + RELEASEOBJECT(pElement) \ + 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 "; // Ячейка таблицы @@ -674,6 +678,57 @@ private: std::vector m_arCells; }; +class CTableCol +{ +public: + CTableCol(XmlUtils::CXmlLiteReader& oLiteReader) + : m_unSpan(1) + { + while (oLiteReader.MoveToNextAttribute()) + { + if (L"span" == oLiteReader.GetName()) + m_unSpan = NSStringFinder::ToInt(oLiteReader.GetText(), 1); + else if (L"style" == oLiteReader.GetName()) + m_wsStyle = oLiteReader.GetText(); + } + + oLiteReader.MoveToElement(); + } +private: + UINT m_unSpan; + std::wstring m_wsStyle; +}; + +class CTableColgroup +{ +public: + CTableColgroup(XmlUtils::CXmlLiteReader& oLiteReader) + : m_unWidth(0) + { + while (oLiteReader.MoveToNextAttribute()) + { + if (L"width" == oLiteReader.GetName()) + m_unWidth = NSStringFinder::ToInt(oLiteReader.GetText()); + } + + oLiteReader.MoveToElement(); + } + + ~CTableColgroup() + { + RELEASE_VECTOR(m_arCols, CTableCol) + } + + void AddCol(CTableCol* pCol) + { + if (NULL != pCol) + m_arCols.push_back(pCol); + } +private: + std::vector m_arCols; + UINT m_unWidth; +}; + class CTable { public: @@ -682,8 +737,8 @@ public: ~CTable() { - for (CTableRow* pRow : m_arRows) - RELEASEOBJECT(pRow); + RELEASE_VECTOR(m_arRows, CTableRow) + RELEASE_VECTOR(m_arColgroups, CTableColgroup) } CTableRow* operator[](UINT unIndex) @@ -782,6 +837,12 @@ public: m_oStyles.m_enRules = TTableStyles::ETableRules::Rows; } + void AddColgroup(CTableColgroup* pElement) + { + if (NULL != pElement) + m_arColgroups.push_back(pElement); + } + void HaveBorderAttribute() { m_oStyles.m_bHaveBorderAttribute = true; @@ -966,6 +1027,8 @@ private: NSStringUtils::CStringBuilder m_oCaption; + std::vector m_arColgroups; + TTableStyles m_oStyles; }; @@ -2452,6 +2515,33 @@ private: sSelectors.pop_back(); return; } + + void ParseTableColspan(CTable& oTable) + { + CTableColgroup *pColgroup = new CTableColgroup(m_oLightReader); + + if (NULL == pColgroup) + return; + + oTable.AddColgroup(pColgroup); + + const int nDeath = m_oLightReader.GetDepth(); + if (m_oLightReader.IsEmptyNode() || !m_oLightReader.ReadNextSiblingNode2(nDeath)) + return; + + do + { + if (L"col" != m_oLightReader.GetName()) + continue; + + CTableCol *pCol = new CTableCol(m_oLightReader); + + if (NULL == pCol) + continue; + + pColgroup->AddCol(pCol); + } while(m_oLightReader.ReadNextSiblingNode2(nDeath)); + } struct TRowspanElement { @@ -2624,6 +2714,8 @@ private: ParseTableRows(oTable, sSelectors, oTS, ERowParseMode::ParseModeBody); else if(sName == L"tfoot") ParseTableRows(oTable, sSelectors, oTS, ERowParseMode::ParseModeFoother); + else if (sName == L"colgroup") + ParseTableColspan(oTable); sSelectors.pop_back(); } From 70fe676300794cef628fd9762b3fa66d0ef7eb86 Mon Sep 17 00:00:00 2001 From: Kirill Polyakov Date: Tue, 16 Jul 2024 18:36:09 +0300 Subject: [PATCH 09/18] Improved colgroup support in html to ooxml conversion --- Common/3dParty/html/css/src/CNode.cpp | 2 +- Common/3dParty/html/css/src/CNode.h | 2 +- .../3dParty/html/css/src/StyleProperties.cpp | 42 +- Common/3dParty/html/css/src/StyleProperties.h | 3 + HtmlFile2/htmlfile2.cpp | 481 ++++++++++++------ 5 files changed, 364 insertions(+), 166 deletions(-) diff --git a/Common/3dParty/html/css/src/CNode.cpp b/Common/3dParty/html/css/src/CNode.cpp index 8b2cdc27b8..8073a6266e 100644 --- a/Common/3dParty/html/css/src/CNode.cpp +++ b/Common/3dParty/html/css/src/CNode.cpp @@ -5,7 +5,7 @@ namespace NSCSS CNode::CNode() {} - CNode::CNode(std::wstring wsName, std::wstring wsClass, std::wstring wsId) + CNode::CNode(const std::wstring& wsName, const std::wstring& wsClass, const std::wstring& wsId) : m_wsName(wsName), m_wsClass(wsClass), m_wsId(wsId) {} diff --git a/Common/3dParty/html/css/src/CNode.h b/Common/3dParty/html/css/src/CNode.h index a7c7de0539..57409a67a5 100644 --- a/Common/3dParty/html/css/src/CNode.h +++ b/Common/3dParty/html/css/src/CNode.h @@ -18,7 +18,7 @@ namespace NSCSS public: CNode(); - CNode(std::wstring wsName, std::wstring wsClass, std::wstring wsId); + CNode(const std::wstring& wsName, const std::wstring& wsClass, const std::wstring& wsId); bool Empty() const; diff --git a/Common/3dParty/html/css/src/StyleProperties.cpp b/Common/3dParty/html/css/src/StyleProperties.cpp index b1ee0e668b..9987204ed5 100644 --- a/Common/3dParty/html/css/src/StyleProperties.cpp +++ b/Common/3dParty/html/css/src/StyleProperties.cpp @@ -504,8 +504,48 @@ namespace NSCSS } } + CColor& CColor::operator =(const CColor& oColor) + { + m_enType = oColor.m_enType; + m_oOpacity = oColor.m_oOpacity; + + switch(m_enType) + { + case ColorEmpty: + case ColorNone: + break; + case ColorRGB: + { + m_oValue = new TRGB{(*static_cast(oColor.m_oValue))}; + break; + } + case ColorHEX: + { + m_oValue = new std::wstring(*static_cast(oColor.m_oValue)); + break; + } + case ColorUrl: + { + m_oValue = new CURL(*static_cast(oColor.m_oValue)); + break; + } + } + + return *this; + } + + CColor& CColor::operator+=(const CColor& oColor) + { + if (m_unLevel > oColor.m_unLevel || (m_bImportant && !oColor.m_bImportant) || oColor.Empty()) + return *this; + + *this = oColor; + + return *this; + } + CColor::CColor() - : CValue(NULL, 0, false), m_oOpacity(1.) + : CValue(NULL, 0, false), m_oOpacity(1.), m_enType(ColorEmpty) {} void CColor::SetEmpty(unsigned int unLevel) diff --git a/Common/3dParty/html/css/src/StyleProperties.h b/Common/3dParty/html/css/src/StyleProperties.h index aec7b927ec..dbede17a60 100644 --- a/Common/3dParty/html/css/src/StyleProperties.h +++ b/Common/3dParty/html/css/src/StyleProperties.h @@ -243,6 +243,9 @@ namespace NSCSS bool operator==(const CColor& oColor) const; bool operator!=(const CColor& oColor) const; + + CColor& operator =(const CColor& oColor); + CColor& operator+=(const CColor& oColor); private: CDigit m_oOpacity; ColorType m_enType; diff --git a/HtmlFile2/htmlfile2.cpp b/HtmlFile2/htmlfile2.cpp index b678480f43..bb4739b636 100644 --- a/HtmlFile2/htmlfile2.cpp +++ b/HtmlFile2/htmlfile2.cpp @@ -160,14 +160,14 @@ struct TTableStyles enum ETableRules { All, - Group, + Groups, Cols, None, Rows } m_enRules; TTableStyles() - : m_nCellSpacing(-1), m_bHaveBorderAttribute(false), m_enRules(All) + : m_nCellSpacing(-1), m_bHaveBorderAttribute(false), m_enRules(None) {} bool Empty() const @@ -189,7 +189,6 @@ std::wstring CreateBorders(const NSCSS::NSProperties::CBorder& oBorder, const NS } else { - if (oBorder.GetTopBorder().Valid()) wsTable += L"GetTop())))) + L"/>"; @@ -285,8 +284,30 @@ struct TTableCellStyle m_wsHAlign = pTableCellStyle->m_wsHAlign; m_wsVAlign = pTableCellStyle->m_wsVAlign; } + + TTableCellStyle& operator+=(const TTableCellStyle* pCellStyle) + { + if (NULL == pCellStyle) + return *this; + + m_oWidth += pCellStyle->m_oWidth; + m_oHeight += pCellStyle->m_oHeight; + m_oBorder += pCellStyle->m_oBorder; + m_oPadding += pCellStyle->m_oPadding; + m_oBackground += pCellStyle->m_oBackground; + + if (m_wsHAlign.empty()) + m_wsHAlign = pCellStyle->m_wsHAlign; + + if (m_wsVAlign.empty()) + m_wsVAlign = pCellStyle->m_wsVAlign; + + return *this; + } }; +class CTable; + class CTableCell { public: @@ -371,6 +392,11 @@ public: return &m_oStyles; } + TTableCellStyle* GetStyles() + { + return &m_oStyles; + } + void SetWidth(const NSCSS::NSProperties::CDigit& oWidth) { m_oStyles.m_oWidth = oWidth; @@ -431,95 +457,7 @@ public: m_oStyles.m_oBackground = oColor; } - std::wstring ConvertToOOXML(const TTableStyles& oTableStyles) - { - NSStringUtils::CStringBuilder oCell; - - oCell.WriteNodeBegin(L"w:tc"); - oCell.WriteNodeBegin(L"w:tcPr"); - - if (ParseModeHeader == m_enMode) - oCell += L""; - - if (!m_oStyles.m_oWidth.Empty()) - { - if (NSCSS::UnitMeasure::Percent == m_oStyles.m_oWidth.GetUnitMeasure()) - oCell += L""; - else - { - if (!m_oStyles.m_oWidth.Zero()) - { - int nWidth; - if (NSCSS::UnitMeasure::None != m_oStyles.m_oWidth.GetUnitMeasure()) - nWidth = m_oStyles.m_oWidth.ToInt(NSCSS::UnitMeasure::Twips); - else - nWidth = static_cast(NSCSS::CUnitMeasureConverter::ConvertPx(m_oStyles.m_oWidth.ToDouble(), NSCSS::UnitMeasure::Twips, 96) + 0.5); - - oCell += L""; - } - else - oCell += L""; - } - } - else - oCell += L""; - - if (1 != m_unColspan) - oCell += L""; - - if (m_bIsMerged) - oCell += L""; - else if (1 < m_unRowSpan) - oCell += L""; - - if (!m_oStyles.m_oBorder.Zero() && !m_oStyles.m_oBorder.Empty() && m_oStyles.m_oBorder != oTableStyles.m_oBorder) - oCell += L"" + CreateBorders(m_oStyles.m_oBorder) + L""; - // else if (oTableStyles.m_bHaveBorderAttribute) - // oCell += L"" + CreateOutsetBorders(oTableStyles.m_enRules) + L""; - - if (!m_oStyles.m_oBackground.Empty()) - { - const std::wstring wsShdFill{(NSCSS::NSProperties::ColorNone == m_oStyles.m_oBackground.GetType()) ? L"auto" : m_oStyles.m_oBackground.ToWString()}; - oCell += L""; - } - - if (!m_oStyles.m_wsVAlign.empty()) - oCell += L""; - else - oCell += L""; - - if (!m_oStyles.m_oPadding.Empty() && oTableStyles.m_oPadding != m_oStyles.m_oPadding) - { - const int nTopPadding = std::max(oTableStyles.m_oPadding.GetTop() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_HEIGHT), - m_oStyles .m_oPadding.GetTop() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_HEIGHT)); - const int nLeftPadding = std::max(oTableStyles.m_oPadding.GetLeft() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_WIDTH), - m_oStyles .m_oPadding.GetLeft() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_WIDTH)); - const int nBottomPadding = std::max(oTableStyles.m_oPadding.GetBottom().ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_HEIGHT), - m_oStyles .m_oPadding.GetBottom().ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_HEIGHT)); - const int nRightPadding = std::max(oTableStyles.m_oPadding.GetRight() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_WIDTH), - m_oStyles .m_oPadding.GetRight() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_WIDTH)); - - oCell += L"" - "" - "" - "" - "" - ""; - } - - oCell += L""; - oCell.WriteNodeEnd(L"w:tcPr"); - - if (0 != m_oData.GetCurSize()) - oCell += m_oData.GetData(); - else - WriteEmptyParagraph(&oCell); - - oCell.WriteNodeEnd(L"w:tc"); - - return oCell.GetData(); - } - + std::wstring ConvertToOOXML(const CTable& oTable, UINT unColumnNumber); private: UINT m_unColspan; UINT m_unRowSpan; @@ -634,37 +572,7 @@ public: return m_arCells.size(); } - std::wstring ConvertToOOXML(const TTableStyles& oTableStyles) - { - if (m_arCells.empty()) - return std::wstring(); - - NSStringUtils::CStringBuilder oRow; - oRow.WriteNodeBegin(L"w:tr"); - - if (!m_oStyles.Empty() || 0 < oTableStyles.m_nCellSpacing) - { - oRow.WriteNodeBegin(L"w:trPr"); - - if (m_oStyles.m_bIsHeader) - oRow += L""; - - if (0 < m_oStyles.m_unMaxHeight) - oRow += L""; - - if (0 < oTableStyles.m_nCellSpacing) - oRow += L""; - - oRow.WriteNodeEnd(L"w:trPr"); - } - - for (CTableCell* pCell : m_arCells) - oRow += pCell->ConvertToOOXML(oTableStyles); - - oRow.WriteNodeEnd(L"w:tr"); - - return oRow.GetData(); - } + std::wstring ConvertToOOXML(const CTable& oTable); CTableCell* operator[](UINT unIndex) { @@ -681,6 +589,10 @@ private: class CTableCol { public: + CTableCol(UINT unSpan) + : m_unSpan(unSpan) + {} + CTableCol(XmlUtils::CXmlLiteReader& oLiteReader) : m_unSpan(1) { @@ -688,15 +600,28 @@ public: { if (L"span" == oLiteReader.GetName()) m_unSpan = NSStringFinder::ToInt(oLiteReader.GetText(), 1); - else if (L"style" == oLiteReader.GetName()) - m_wsStyle = oLiteReader.GetText(); } oLiteReader.MoveToElement(); } + + UINT GetSpan() const + { + return m_unSpan; + } + + TTableCellStyle* GetStyle() + { + return &m_oStyle; + } + + const TTableCellStyle* GetStyle() const + { + return &m_oStyle; + } private: UINT m_unSpan; - std::wstring m_wsStyle; + TTableCellStyle m_oStyle; }; class CTableColgroup @@ -719,20 +644,31 @@ public: RELEASE_VECTOR(m_arCols, CTableCol) } + bool Empty() const + { + return m_arCols.empty(); + } + void AddCol(CTableCol* pCol) { if (NULL != pCol) m_arCols.push_back(pCol); } + + const std::vector& GetCols() const + { + return m_arCols; + } private: std::vector m_arCols; - UINT m_unWidth; + UINT m_unWidth; }; class CTable { public: CTable() + : m_unMaxColumns(0) {} ~CTable() @@ -759,11 +695,68 @@ public: return 0 != m_oCaption.GetCurSize(); } + bool HaveColgroups() const + { + return !m_arColgroups.empty(); + } + UINT GetRowCount() const { return m_arRows.size(); } + TTableStyles GetTableStyles() const + { + return m_oStyles; + } + + const TTableCellStyle* GetColStyle(UINT unColumnNumber) const + { + if (m_arColgroups.empty()) + return NULL; + + UINT unCurrentNumber = 0; + + for (const CTableColgroup* pColgroup : m_arColgroups) + { + for (const CTableCol* pCol : pColgroup->GetCols()) + { + unCurrentNumber += pCol->GetSpan(); + + if (unCurrentNumber >= unColumnNumber) + return pCol->GetStyle(); + } + } + + return NULL; + } + + std::wstring CalculateSidesToClean(UINT unColumnNumber) const + { + if (m_arColgroups.empty()) + return std::wstring(); + + UINT unCurrentNumber = 0; + + for (const CTableColgroup* pColgroup : m_arColgroups) + { + for (const CTableCol* pCol : pColgroup->GetCols()) + { + if (unCurrentNumber + 1 == unCurrentNumber) + return (1 != pCol->GetSpan()) ? L"" : std::wstring(); + + unCurrentNumber += pCol->GetSpan(); + + if (unColumnNumber == unCurrentNumber) + return (1 != pCol->GetSpan()) ? L"" : std::wstring(); + else if (unColumnNumber < unCurrentNumber) + return std::wstring((1 != unColumnNumber) ? L"" : L"") + std::wstring((m_unMaxColumns != unColumnNumber) ? L"" : L""); + } + } + + return std::wstring(); + } + void AddRow(CTableRow* pRow) { if (NULL == pRow) @@ -827,8 +820,8 @@ public: if (NSStringFinder::Equals(wsValue, L"all")) m_oStyles.m_enRules = TTableStyles::ETableRules::All; - else if (NSStringFinder::Equals(wsValue, L"group")) - m_oStyles.m_enRules = TTableStyles::ETableRules::Group; + else if (NSStringFinder::Equals(wsValue, L"groups")) + m_oStyles.m_enRules = TTableStyles::ETableRules::Groups; else if (NSStringFinder::Equals(wsValue, L"cols")) m_oStyles.m_enRules = TTableStyles::ETableRules::Cols; else if (NSStringFinder::Equals(wsValue, L"none")) @@ -853,19 +846,15 @@ public: return m_oStyles.m_bHaveBorderAttribute; } - UINT GetMaxColumns() + void RecalculateMaxColumns() { - UINT unMaxColumns = 0; - for (const CTableRow* pRow : m_arRows) - unMaxColumns = std::max(unMaxColumns, pRow->GetIndex()); - - return unMaxColumns; + m_unMaxColumns = std::max(m_unMaxColumns, pRow->GetIndex()); } void Shorten() { - UINT unIndex = 0; + UINT unIndex = 0; CTableCell* pCell = NULL; UINT unMaxIndex = 0; //Максимальный индекс без учета строк, где имеется только 1 ячейка @@ -930,6 +919,8 @@ public: for (UINT unIndex = pRow->GetIndex(); unIndex < unMaxIndex; ++unIndex) pRow->InsertCell(CTableCell::CreateEmpty(), unIndex); } + + RecalculateMaxColumns(); } std::wstring ConvertToOOXML() @@ -967,7 +958,7 @@ public: oTable += L""; if (!m_oStyles.m_oBorder.Empty() && !m_oStyles.m_oBorder.Zero()) - oTable += L"" + CreateBorders(m_oStyles.m_oBorder, NULL, true, m_oStyles.m_enRules) + L""; + oTable += L"" + CreateBorders(m_oStyles.m_oBorder, NULL, true, (TTableStyles::ETableRules::Groups == m_oStyles.m_enRules && !m_arColgroups.empty()) ? TTableStyles::ETableRules::Cols : m_oStyles.m_enRules) + L""; if (!m_oStyles.m_oPadding.Empty() && !m_oStyles.m_oPadding.Zero()) { @@ -1004,7 +995,7 @@ public: oTable.WriteNodeBegin(L"w:tc"); oTable.WriteNodeBegin(L"w:tcPr"); oTable += L""; - oTable += L""; + oTable += L""; oTable += L""; oTable += L""; oTable += L""; @@ -1015,7 +1006,7 @@ public: } for (CTableRow* pRow : m_arRows) - oTable += pRow->ConvertToOOXML(m_oStyles); + oTable += pRow->ConvertToOOXML(*this); oTable.WriteNodeEnd(L"w:tbl"); @@ -1030,6 +1021,8 @@ private: std::vector m_arColgroups; TTableStyles m_oStyles; + + UINT m_unMaxColumns; }; enum class EAbstructNumType @@ -2475,26 +2468,24 @@ private: return bResult; } - void CalculateCellStyles(CTableCell* pCell, const std::vector& arSelectors) + void CalculateCellStyles(TTableCellStyle* pCellStyle, const std::vector& arSelectors) { - if (NULL == pCell) + if (NULL == pCellStyle) return; - std::vector arNewSelectors{(std::vector::const_iterator)std::find_if(arSelectors.begin(), arSelectors.end(), [](const NSCSS::CNode& oNode){ return L"table" == oNode.m_wsName; }), arSelectors.cend()}; - NSCSS::CCompiledStyle oStyle; - m_oStylesCalculator.GetCompiledStyle(oStyle, arNewSelectors); + m_oStylesCalculator.GetCompiledStyle(oStyle, arSelectors); - pCell->SetVAlign(oStyle.m_oDisplay.GetVAlign().ToWString()); - pCell->SetHAlign(oStyle.m_oDisplay.GetHAlign().ToWString()); - pCell->SetBackground(oStyle.m_oBackground.GetColor()); - pCell->SetHeight(oStyle.m_oDisplay.GetHeight()); - pCell->SetWidth(oStyle.m_oDisplay.GetWidth()); - pCell->SetPadding(oStyle.m_oPadding); - pCell->SetBorder(oStyle.m_oBorder); + pCellStyle->m_wsVAlign = oStyle.m_oDisplay.GetVAlign().ToWString(); + pCellStyle->m_wsHAlign = oStyle.m_oDisplay.GetHAlign().ToWString(); + pCellStyle->m_oBackground = oStyle.m_oBackground.GetColor(); + pCellStyle->m_oHeight = oStyle.m_oDisplay.GetHeight(); + pCellStyle->m_oWidth = oStyle.m_oDisplay.GetWidth(); + pCellStyle->m_oPadding = oStyle.m_oPadding; + pCellStyle->m_oBorder = oStyle.m_oBorder; - if (pCell->GetStyles()->m_wsHAlign.empty()) - pCell->SetHAlign(oStyle.m_oText.GetAlign().ToWString()); + if (pCellStyle->m_wsHAlign.empty()) + pCellStyle->m_wsHAlign = oStyle.m_oText.GetAlign().ToWString(); } void ParseTableCaption(CTable& oTable, std::vector& sSelectors, const CTextSettings& oTS) @@ -2523,24 +2514,49 @@ private: if (NULL == pColgroup) return; + std::vector arNodes; + + GetSubClass(NULL, arNodes); + oTable.AddColgroup(pColgroup); const int nDeath = m_oLightReader.GetDepth(); - if (m_oLightReader.IsEmptyNode() || !m_oLightReader.ReadNextSiblingNode2(nDeath)) - return; - - do + if (!m_oLightReader.IsEmptyNode() && m_oLightReader.ReadNextSiblingNode2(nDeath)) { - if (L"col" != m_oLightReader.GetName()) - continue; + do + { + if (L"col" != m_oLightReader.GetName()) + continue; - CTableCol *pCol = new CTableCol(m_oLightReader); + CTableCol *pCol = new CTableCol(m_oLightReader); + + if (NULL == pCol) + continue; + + GetSubClass(NULL, arNodes); + CalculateCellStyles(pCol->GetStyle(), arNodes); + arNodes.pop_back(); + + if (NULL == pCol) + continue; + + pColgroup->AddCol(pCol); + } while(m_oLightReader.ReadNextSiblingNode2(nDeath)); + } + + if(pColgroup->Empty()) + { + std::map::const_iterator itFound = arNodes.begin()->m_mAttributes.find(L"span"); + + CTableCol *pCol = new CTableCol((arNodes.begin()->m_mAttributes.cend() != itFound) ? NSStringFinder::ToInt(itFound->second, 1) : 1); if (NULL == pCol) - continue; + return; + + CalculateCellStyles(pCol->GetStyle(), arNodes); pColgroup->AddCol(pCol); - } while(m_oLightReader.ReadNextSiblingNode2(nDeath)); + } } struct TRowspanElement @@ -2591,7 +2607,10 @@ private: pCell->SetMode(eMode); GetSubClass(pCell->GetData(), sSelectors); - CalculateCellStyles(pCell, sSelectors); + + const std::vector arNewSelectors{(std::vector::const_iterator)std::find_if(sSelectors.begin(), sSelectors.end(), [](const NSCSS::CNode& oNode){ return L"table" == oNode.m_wsName; }), sSelectors.cend()}; + + CalculateCellStyles(pCell->GetStyles(), arNewSelectors); while(m_oLightReader.MoveToNextAttribute()) { @@ -3624,3 +3643,139 @@ HRESULT CHtmlFile2::OpenBatchHtml(const std::vector& sSrc, const s m_internal->write(); return S_OK; } + +std::wstring CTableRow::ConvertToOOXML(const CTable& oTable) +{ + if (m_arCells.empty()) + return std::wstring(); + + NSStringUtils::CStringBuilder oRow; + oRow.WriteNodeBegin(L"w:tr"); + + const TTableStyles oTableStyles{oTable.GetTableStyles()}; + + if (!m_oStyles.Empty() || 0 < oTableStyles.m_nCellSpacing) + { + oRow.WriteNodeBegin(L"w:trPr"); + + if (m_oStyles.m_bIsHeader) + oRow += L""; + + if (0 < m_oStyles.m_unMaxHeight) + oRow += L""; + + if (0 < oTableStyles.m_nCellSpacing) + oRow += L""; + + oRow.WriteNodeEnd(L"w:trPr"); + } + + for (UINT unIndex = 0; unIndex < m_arCells.size();) + oRow += m_arCells[unIndex]->ConvertToOOXML(oTable, ++unIndex); + + oRow.WriteNodeEnd(L"w:tr"); + + return oRow.GetData(); +} + +std::wstring CTableCell::ConvertToOOXML(const CTable& oTable, UINT unColumnNumber) +{ + NSStringUtils::CStringBuilder oCell; + + oCell.WriteNodeBegin(L"w:tc"); + oCell.WriteNodeBegin(L"w:tcPr"); + + if (ParseModeHeader == m_enMode) + oCell += L""; + + TTableCellStyle oCellStyle(m_oStyles); + + const TTableCellStyle* pColStyle = oTable.GetColStyle(unColumnNumber); + + if (NULL != pColStyle) + oCellStyle += pColStyle; + + if (!oCellStyle.m_oWidth.Empty()) + { + if (NSCSS::UnitMeasure::Percent == oCellStyle.m_oWidth.GetUnitMeasure()) + oCell += L""; + else + { + if (!oCellStyle.m_oWidth.Zero()) + { + int nWidth; + if (NSCSS::UnitMeasure::None != oCellStyle.m_oWidth.GetUnitMeasure()) + nWidth = oCellStyle.m_oWidth.ToInt(NSCSS::UnitMeasure::Twips); + else + nWidth = static_cast(NSCSS::CUnitMeasureConverter::ConvertPx(oCellStyle.m_oWidth.ToDouble(), NSCSS::UnitMeasure::Twips, 96) + 0.5); + + oCell += L""; + } + else + oCell += L""; + } + } + else + oCell += L""; + + if (1 != m_unColspan) + oCell += L""; + + if (m_bIsMerged) + oCell += L""; + else if (1 < m_unRowSpan) + oCell += L""; + + const TTableStyles oTableStyles{oTable.GetTableStyles()}; + + if (!oCellStyle.m_oBorder.Zero() && !oCellStyle.m_oBorder.Empty() && oCellStyle.m_oBorder != oTableStyles.m_oBorder) + oCell += L"" + CreateBorders(oCellStyle.m_oBorder, &oCellStyle.m_oPadding) + L""; + else if (TTableStyles::ETableRules::Groups == oTable.GetTableStyles().m_enRules && oTable.HaveColgroups()) + { + oCell += L"" + oTable.CalculateSidesToClean(unColumnNumber) + L""; + } + // else if (oTableStyles.m_bHaveBorderAttribute) + // oCell += L"" + CreateOutsetBorders(oTableStyles.m_enRules) + L""; + + if (!oCellStyle.m_oBackground.Empty()) + { + const std::wstring wsShdFill{(NSCSS::NSProperties::ColorNone == oCellStyle.m_oBackground.GetType()) ? L"auto" : oCellStyle.m_oBackground.ToWString()}; + oCell += L""; + } + + if (!oCellStyle.m_wsVAlign.empty()) + oCell += L""; + else + oCell += L""; + + if (!oCellStyle.m_oPadding.Empty() && oTableStyles.m_oPadding != oCellStyle.m_oPadding) + { + const int nTopPadding = std::max(oTableStyles.m_oPadding.GetTop() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_HEIGHT), + oCellStyle .m_oPadding.GetTop() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_HEIGHT)); + const int nLeftPadding = std::max(oTableStyles.m_oPadding.GetLeft() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_WIDTH), + oCellStyle .m_oPadding.GetLeft() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_WIDTH)); + const int nBottomPadding = std::max(oTableStyles.m_oPadding.GetBottom().ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_HEIGHT), + oCellStyle .m_oPadding.GetBottom().ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_HEIGHT)); + const int nRightPadding = std::max(oTableStyles.m_oPadding.GetRight() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_WIDTH), + oCellStyle .m_oPadding.GetRight() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_WIDTH)); + + oCell += L"" + "" + "" + "" + "" + ""; + } + + oCell += L""; + oCell.WriteNodeEnd(L"w:tcPr"); + + if (0 != m_oData.GetCurSize()) + oCell += m_oData.GetData(); + else + WriteEmptyParagraph(&oCell); + + oCell.WriteNodeEnd(L"w:tc"); + + return oCell.GetData(); +} From ac4f15b9990c9743698ed9df21687708dee7fbc0 Mon Sep 17 00:00:00 2001 From: Kirill Polyakov Date: Fri, 19 Jul 2024 16:59:58 +0300 Subject: [PATCH 10/18] Improved html to ooxml conversion --- .../3dParty/html/css/src/StyleProperties.cpp | 55 +++++- Common/3dParty/html/css/src/StyleProperties.h | 7 + HtmlFile2/htmlfile2.cpp | 170 +++++++++++++----- 3 files changed, 185 insertions(+), 47 deletions(-) diff --git a/Common/3dParty/html/css/src/StyleProperties.cpp b/Common/3dParty/html/css/src/StyleProperties.cpp index 9987204ed5..ab0cba24d4 100644 --- a/Common/3dParty/html/css/src/StyleProperties.cpp +++ b/Common/3dParty/html/css/src/StyleProperties.cpp @@ -420,6 +420,17 @@ namespace NSCSS return true; } + bool CDigit::SetValue(double dValue, unsigned int unLevel, bool bHardMode) + { + if (CHECK_CONDITIONS && !bHardMode) + return false; + + m_oValue = dValue; + m_unLevel = unLevel; + + return true; + } + bool TRGB::Empty() const { return 0 == uchRed && 0 == uchGreen && 0 == uchBlue; @@ -1583,9 +1594,7 @@ namespace NSCSS if (L"none" == wsValue) { - SetColor(L"#ffffff", unLevel, bHardMode); - SetStyle(L"solid", unLevel, bHardMode); - SetWidth(L"0",unLevel,bHardMode); + SetNone(unLevel, bHardMode); return true; } @@ -1625,6 +1634,11 @@ namespace NSCSS return m_oWidth.SetValue(wsNewValue, unLevel, bHardMode); } + bool CBorderSide::SetWidth(double dValue, unsigned int unLevel, bool bHardMode) + { + return m_oWidth.SetValue(dValue, unLevel, bHardMode); + } + bool CBorderSide::SetStyle(const std::wstring &wsValue, unsigned int unLevel, bool bHardMode) { return m_oStyle.SetValue(wsValue, {std::make_pair(L"dotted", L"dotted"), std::make_pair(L"dashed", L"dashed"), std::make_pair(L"solid", L"single"), @@ -1637,6 +1651,13 @@ namespace NSCSS return m_oColor.SetValue(wsValue, unLevel, bHardMode); } + void CBorderSide::SetNone(unsigned int unLevel, bool bHardMode) + { + SetColor(L"#ffffff", unLevel, bHardMode); + SetStyle(L"solid", unLevel, bHardMode); + SetWidth(L"0",unLevel,bHardMode); + } + void CBorderSide::Block() { m_bBlock = true; @@ -1771,6 +1792,18 @@ namespace NSCSS return bResult; } + bool CBorder::SetWidth(double dValue, unsigned int unLevel, bool bHardMode) + { + bool bResult = false; + + if (m_oLeft .SetWidth(dValue, unLevel, bHardMode)) bResult = true; + if (m_oTop .SetWidth(dValue, unLevel, bHardMode)) bResult = true; + if (m_oRight .SetWidth(dValue, unLevel, bHardMode)) bResult = true; + if (m_oBottom.SetWidth(dValue, unLevel, bHardMode)) bResult = true; + + return bResult; + } + bool CBorder::SetStyle(const std::wstring &wsValue, unsigned int unLevel, bool bHardMode) { bool bResult = false; @@ -1880,6 +1913,14 @@ namespace NSCSS return m_oBottom.SetColor(wsValue, unLevel, bHardMode); } + void CBorder::SetNone(unsigned int unLevel, bool bHardMode) + { + m_oLeft .SetNone(unLevel, bHardMode); + m_oTop .SetNone(unLevel, bHardMode); + m_oRight .SetNone(unLevel, bHardMode); + m_oBottom.SetNone(unLevel, bHardMode); + } + void CBorder::Block() { m_oLeft .Block(); @@ -2429,10 +2470,10 @@ namespace NSCSS bool CFont::SetSize(const std::wstring &wsValue, unsigned int unLevel, bool bHardMode) { const std::vector> arAbsoluteFontValues = - {{L"xx-small", L"7.5pt"}, {L"xx-large", L"36pt" }, - {L"x-small", L"10pt" }, {L"x-large", L"24pt" }, - {L"small", L"12pt" }, {L"medium", L"13.5pt"}, - {L"large", L"18pt" }}; + {{L"xx-small", L"7.5pt"}, {L"xx-large", L"36pt" }, + {L"x-small", L"10pt" }, {L"x-large", L"24pt" }, + {L"small", L"12pt" }, {L"medium", L"13.5pt"}, + {L"large", L"18pt" }}; size_t unFoundPos = std::wstring::npos; std::wstring wsNewValue(wsValue); diff --git a/Common/3dParty/html/css/src/StyleProperties.h b/Common/3dParty/html/css/src/StyleProperties.h index dbede17a60..392f11aedc 100644 --- a/Common/3dParty/html/css/src/StyleProperties.h +++ b/Common/3dParty/html/css/src/StyleProperties.h @@ -136,6 +136,7 @@ namespace NSCSS bool SetValue(const std::wstring& wsValue, unsigned int unLevel = 0, bool bHardMode = true) override; bool SetValue(const CDigit& oValue); + bool SetValue(double dValue, unsigned int unLevel, bool bHardMode); bool Empty() const override; bool Zero() const; @@ -449,9 +450,12 @@ namespace NSCSS bool SetValue(const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); bool SetWidth(const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); + bool SetWidth(double dValue, unsigned int unLevel, bool bHardMode = false); bool SetStyle(const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); bool SetColor(const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); + void SetNone(unsigned int unLevel, bool bHardMode); + void Block(); void Unblock(); @@ -494,6 +498,7 @@ namespace NSCSS bool SetSides(const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); bool SetWidth(const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); + bool SetWidth(double dValue, unsigned int unLevel, bool bHardMode = false); bool SetStyle(const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); bool SetColor(const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); bool SetCollapse(const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); @@ -522,6 +527,8 @@ namespace NSCSS bool SetStyleBottomSide(const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); bool SetColorBottomSide(const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); + void SetNone(unsigned int unLevel, bool bHardMode = false); + void Block(); void Unblock(); diff --git a/HtmlFile2/htmlfile2.cpp b/HtmlFile2/htmlfile2.cpp index bb4739b636..3949a43205 100644 --- a/HtmlFile2/htmlfile2.cpp +++ b/HtmlFile2/htmlfile2.cpp @@ -153,7 +153,6 @@ struct TTableStyles NSCSS::NSProperties::CDigit m_oWidth; int m_nCellSpacing; - bool m_bHaveBorderAttribute; std::wstring m_wsAlign; @@ -167,12 +166,12 @@ struct TTableStyles } m_enRules; TTableStyles() - : m_nCellSpacing(-1), m_bHaveBorderAttribute(false), m_enRules(None) + : m_nCellSpacing(-1), m_enRules(None) {} bool Empty() const { - return m_oPadding.Empty() && m_oMargin.Empty() && m_oBorder.Empty() && m_oWidth.Empty() && -1 == m_nCellSpacing && false == m_bHaveBorderAttribute && m_wsAlign.empty(); + return m_oPadding.Empty() && m_oMargin.Empty() && m_oBorder.Empty() && m_oWidth.Empty() && -1 == m_nCellSpacing && m_wsAlign.empty(); } }; @@ -836,16 +835,6 @@ public: m_arColgroups.push_back(pElement); } - void HaveBorderAttribute() - { - m_oStyles.m_bHaveBorderAttribute = true; - } - - bool IsHaveBorderAttribute() const - { - return m_oStyles.m_bHaveBorderAttribute; - } - void RecalculateMaxColumns() { for (const CTableRow* pRow : m_arRows) @@ -2113,7 +2102,7 @@ private: else if(sName == L"big") { CTextSettings oTSR(oTS); - oTSR.AddRStyle(L""); + oTSR.AddRStyle(L""); bResult = readStream(oXml, sSelectors, oTSR); } // Перенос строки @@ -2150,12 +2139,24 @@ private: bResult = readStream(oXml, sSelectors, oTSR); } // Код - // Моноширинный шрифт, например, Consolas + // Моноширинный шрифт, например, Courier New // Результат скрипта - else if(sName == L"code" || sName == L"kbd" || sName == L"samp" || sName == L"tt") + else if(sName == L"code" || sName == L"tt") { CTextSettings oTSR(oTS); - oTSR.AddRStyle(L""); + oTSR.AddRStyle(L""); + bResult = readStream(oXml, sSelectors, oTSR); + } + else if (sName == L"kbd") + { + CTextSettings oTSR(oTS); + oTSR.AddRStyle(L""); + bResult = readStream(oXml, sSelectors, oTSR); + } + else if (sName == L"samp") + { + CTextSettings oTSR(oTS); + oTSR.AddRStyle(L""); bResult = readStream(oXml, sSelectors, oTSR); } // Зачеркнутый текст @@ -2358,7 +2359,7 @@ private: // С нового абзаца else if(sName == L"article" || sName == L"header" || 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"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") bResult = readStream(&oXmlData, sSelectors, oTS); @@ -2410,6 +2411,70 @@ private: oTSP.AddPStyle(L""); bResult = readStream(&oXmlData, sSelectors, oTSP); } + else if (sName == L"details") + { + bool bOpened = false; + if (m_oLightReader.MoveToFirstAttribute()) + { + do + { + bOpened = (L"open" == m_oLightReader.GetName()); + } while (m_oLightReader.MoveToNextAttribute() && !bOpened); + } + m_oLightReader.MoveToElement(); + + int nDeath = m_oLightReader.GetDepth(); + if(m_oLightReader.IsEmptyNode() || !m_oLightReader.ReadNextSiblingNode2(nDeath)) + { + sSelectors.pop_back(); + return false; + } + + NSStringUtils::CStringBuilder oSummary; + NSStringUtils::CStringBuilder oBody; + + const TState oCurrentState{m_oState}; + TState oSummaryState{m_oState}; + TState oBodyState{m_oState}; + + do + { + if (L"summary" == m_oLightReader.GetName()) + { + m_oState = oSummaryState; + if (0 == oSummary.GetSize()) + { + OpenP(&oSummary); + OpenR(&oSummary); + OpenT(&oSummary); + oSummary.WriteString((bOpened) ? L"\u25BD" : L"\u25B7"); + } + + readStream(&oSummary, sSelectors, oTS); + CloseP(&oSummary, sSelectors); + oSummaryState = m_oState; + m_oState = oCurrentState; + } + else if (bOpened) + { + m_oState = oBodyState; + readStream(&oBody, sSelectors, oTS); + CloseP(&oBody, sSelectors); + oBodyState = m_oState; + m_oState = oCurrentState; + } + } while (m_oLightReader.ReadNextSiblingNode2(nDeath)); + + oXmlData.WriteString(oSummary.GetData()); + + if (bOpened) + { + m_oState = oBodyState; + oXmlData.WriteString(oBody.GetData()); + } + + bResult = true; + } else if (sName == L"xml") { sSelectors.pop_back(); @@ -2682,6 +2747,9 @@ private: CTable oTable; + NSCSS::CCompiledStyle oStyle; + m_oStylesCalculator.GetCompiledStyle(oStyle, sSelectors); + //Table styles if (sSelectors.back().m_mAttributes.end() != sSelectors.back().m_mAttributes.find(L"border")) { @@ -2689,16 +2757,18 @@ private: if (0 < nWidth) { - sSelectors.back().m_mAttributes[L"border"] = L"outset " + std::to_wstring(nWidth) + L"px auto"; - oTable.HaveBorderAttribute(); + oStyle.m_oBorder.SetStyle(L"outset", 0, true); + oStyle.m_oBorder.SetWidth(nWidth, 0, true); + oStyle.m_oBorder.SetColor(L"auto", 0, true); + oTable.SetRules(L"all"); } else - sSelectors.back().m_mAttributes[L"border"] = L"none"; + { + oStyle.m_oBorder.SetNone(0, true); + oTable.SetRules(L"none"); + } } - NSCSS::CCompiledStyle oStyle; - m_oStylesCalculator.GetCompiledStyle(oStyle, sSelectors); - if (sSelectors.back().m_mAttributes.end() != sSelectors.back().m_mAttributes.find(L"cellpadding")) oStyle.m_oPadding.SetValues(sSelectors.back().m_mAttributes[L"cellpadding"] + L"px", 0, true); @@ -2783,10 +2853,10 @@ private: if(m_oLightReader.IsEmptyNode()) return; - std::wstring sStart = L"1"; + int nStart = 1; while(m_oLightReader.MoveToNextAttribute()) if(m_oLightReader.GetName() == L"start") - sStart = m_oLightReader.GetText(); + nStart = NSStringFinder::ToInt(m_oLightReader.GetText(), 1); m_oLightReader.MoveToElement(); int nDeath = m_oLightReader.GetDepth(); @@ -2819,10 +2889,19 @@ private: continue; } + std::wstring wsValue; + GetSubClass(oXml, sSelectors); while(m_oLightReader.MoveToNextAttribute()) + { if(m_oLightReader.GetName() == L"value") - sStart = m_oLightReader.GetText(); + { + if (sName == L"option") + wsValue = m_oLightReader.GetText(); + else + nStart = NSStringFinder::ToInt(m_oLightReader.GetText(), 1); + } + } m_oLightReader.MoveToElement(); CloseP(oXml, sSelectors); @@ -2846,6 +2925,15 @@ private: wrP(oXml, sSelectors, oTSLiP); oTSLiP.sPStyle = wsOldPStyle; + if (!wsValue.empty()) + { + OpenR(oXml); + OpenT(oXml); + oXml->WriteEncodeXmlString(wsValue); + CloseT(oXml); + CloseR(oXml); + } + readStream(oXml, sSelectors, oTSLiP); CloseP(oXml, sSelectors); @@ -2855,26 +2943,27 @@ private: // Нумерованный список if(!bType) { + const std::wstring wsStart(std::to_wstring(nStart)); m_oNumberXml.WriteString(L""); } } @@ -3728,14 +3817,15 @@ std::wstring CTableCell::ConvertToOOXML(const CTable& oTable, UINT unColumnNumbe const TTableStyles oTableStyles{oTable.GetTableStyles()}; - if (!oCellStyle.m_oBorder.Zero() && !oCellStyle.m_oBorder.Empty() && oCellStyle.m_oBorder != oTableStyles.m_oBorder) + if (!oCellStyle.m_oBorder.Empty() && !oCellStyle.m_oBorder.Zero() /*&& oCellStyle.m_oBorder != oTableStyles.m_oBorder*/) oCell += L"" + CreateBorders(oCellStyle.m_oBorder, &oCellStyle.m_oPadding) + L""; else if (TTableStyles::ETableRules::Groups == oTable.GetTableStyles().m_enRules && oTable.HaveColgroups()) { - oCell += L"" + oTable.CalculateSidesToClean(unColumnNumber) + L""; + const std::wstring wsBorders{oTable.CalculateSidesToClean(unColumnNumber)}; + + if (!wsBorders.empty()) + oCell += L"" + wsBorders + L""; } - // else if (oTableStyles.m_bHaveBorderAttribute) - // oCell += L"" + CreateOutsetBorders(oTableStyles.m_enRules) + L""; if (!oCellStyle.m_oBackground.Empty()) { From b8a8d36b8b571b8fd16d4dba33e02d69ce2e175f Mon Sep 17 00:00:00 2001 From: Kirill Polyakov Date: Tue, 23 Jul 2024 14:55:38 +0300 Subject: [PATCH 11/18] Improved html to ooxml conversion --- HtmlFile2/htmlfile2.cpp | 57 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/HtmlFile2/htmlfile2.cpp b/HtmlFile2/htmlfile2.cpp index 3949a43205..53b91e6ad4 100644 --- a/HtmlFile2/htmlfile2.cpp +++ b/HtmlFile2/htmlfile2.cpp @@ -125,10 +125,19 @@ void WriteEmptyParagraph(NSStringUtils::CStringBuilder* pXml, bool bVahish = fal pXml->WriteString(L""); } -void WriteLine(NSStringUtils::CStringBuilder* pXml, double dHeight, const std::wstring& wsColor) +void WriteLine(NSStringUtils::CStringBuilder* pXml, const std::wstring& wsAlign, const std::wstring& wsColor, bool bShade, double dSize, double dWidth) { + if (dWidth < 0) + dWidth = -dWidth; + + if (dSize < 0) + dSize = -dSize; + + if (dWidth > 100) + dWidth = 0; + pXml->WriteNodeBegin(L"w:pict"); - pXml->WriteString(L""); + pXml->WriteString(L""); pXml->WriteNodeEnd(L"w:pict"); } @@ -2310,7 +2319,7 @@ private: else if(sName == L"dd") { CTextSettings oTSP(oTS); - oTSP.sPStyle += L""; + oTSP.AddPStyle(L""); bResult = readStream(&oXmlData, sSelectors, oTSP); } // aside возможно использовать для сносок в epub @@ -2357,7 +2366,7 @@ private: bResult = readStream(&oXmlData, sSelectors, oNewTS); } // С нового абзаца - else if(sName == L"article" || sName == L"header" || sName == L"main" || sName == L"dir" || + else if(sName == L"article" || sName == L"header" || sName == L"main" || sName == L"summary" || sName == L"footer" || sName == L"nav" || sName == L"figcaption" || sName == L"form" || sName == L"option" || sName == L"dt" || sName == L"p" || sName == L"section" || sName == L"figure" || sName == L"dl" || sName == L"legend" || sName == L"map" || @@ -2377,9 +2386,45 @@ private: } if (bPrint) { + NSCSS::NSProperties::CDigit oSize, oWidth; + NSCSS::NSProperties::CColor oColor; + bool bShade = false; + std::wstring wsAlign{L"center"}; + + if (m_oLightReader.MoveToFirstAttribute()) + { + std::wstring wsAttributeName; + do + { + wsAttributeName = m_oLightReader.GetName(); + + if (L"align" == wsAttributeName) + { + const std::wstring wsValue{m_oLightReader.GetText()}; + + if (NSStringFinder::Equals(L"left", wsValue)) + wsAlign = L"left"; + else if (NSStringFinder::Equals(L"right", wsValue)) + wsAlign = L"right"; + else if (NSStringFinder::Equals(L"center", wsValue)) + wsAlign = L"center"; + } + if (L"color" == wsAttributeName) + oColor.SetValue(m_oLightReader.GetText()); + else if (L"noshade" == wsAttributeName) + bShade = true; + else if (L"size" == wsAttributeName) + oSize.SetValue(m_oLightReader.GetText()); + else if (L"width" == wsAttributeName) + oWidth.SetValue(m_oLightReader.GetText()); + } while (m_oLightReader.MoveToNextAttribute()); + + m_oLightReader.MoveToElement(); + } + const bool bOpenedP = OpenP(&oXmlData); OpenR(&oXmlData); - WriteLine(&oXmlData, 1.5, L"a0a0a0"); + WriteLine(&oXmlData, wsAlign, (!oColor.Empty()) ? oColor.ToWString() : L"a0a0a0", bShade, (!oSize.Empty()) ? oSize.ToDouble(NSCSS::Point) : 1.5, (NSCSS::UnitMeasure::Percent == oWidth.GetUnitMeasure()) ? oWidth.ToDouble() : 0); CloseR(&oXmlData); if (bOpenedP) CloseP(&oXmlData, sSelectors); @@ -2388,7 +2433,7 @@ private: } // Меню // Маркированный список - else if(sName == L"menu" || sName == L"ul" || sName == L"select" || sName == L"datalist") + else if(sName == L"menu" || sName == L"ul" || sName == L"select" || sName == L"datalist" || sName == L"dir") readLi(&oXmlData, sSelectors, oTS, true); // Нумерованный список else if(sName == L"ol") From a8fa59d64d3a82b7c605d0e9abc55d16941b835c Mon Sep 17 00:00:00 2001 From: Kirill Polyakov Date: Thu, 25 Jul 2024 14:09:31 +0300 Subject: [PATCH 12/18] Added ruby support and bug fixes --- .../html/css/src/CCssCalculator_Private.cpp | 16 +- HtmlFile2/HtmlFile2.pro | 1 + HtmlFile2/htmlfile2.cpp | 175 ++++++++++++++++-- HtmlFile2/src/Languages.h | 7 + 4 files changed, 176 insertions(+), 23 deletions(-) diff --git a/Common/3dParty/html/css/src/CCssCalculator_Private.cpp b/Common/3dParty/html/css/src/CCssCalculator_Private.cpp index e6a9160dd0..60bdc3560d 100644 --- a/Common/3dParty/html/css/src/CCssCalculator_Private.cpp +++ b/Common/3dParty/html/css/src/CCssCalculator_Private.cpp @@ -157,28 +157,34 @@ namespace NSCSS std::vector CCssCalculator_Private::FindElements(std::vector &arNodes, std::vector &arNextNodes, bool bIsSettings) { + if (arNodes.empty()) + return {}; + std::vector arFindedElements; std::wstring wsName, wsId; std::vector arClasses; - if (arNodes.back()[0] == L'#') + if (!arNodes.empty() && arNodes.back()[0] == L'#') { wsId = arNodes.back(); arNodes.pop_back(); arNextNodes.push_back(wsId); } - if (arNodes.back()[0] == L'.') + if (!arNodes.empty() && arNodes.back()[0] == L'.') { arClasses = NS_STATIC_FUNCTIONS::GetWordsW(arNodes.back(), false, L" "); arNextNodes.push_back(arNodes.back()); arNodes.pop_back(); } - wsName = arNodes.back(); - arNodes.pop_back(); - arNextNodes.push_back(wsName); + if (!arNodes.empty()) + { + wsName = arNodes.back(); + arNodes.pop_back(); + arNextNodes.push_back(wsName); + } const std::map::const_iterator oFindName = m_mData.find(wsName); std::map::const_iterator oFindId; diff --git a/HtmlFile2/HtmlFile2.pro b/HtmlFile2/HtmlFile2.pro index 97f6dd3cde..c898f45034 100644 --- a/HtmlFile2/HtmlFile2.pro +++ b/HtmlFile2/HtmlFile2.pro @@ -11,6 +11,7 @@ CONFIG += plugin DEFINES += HTMLFILE2_USE_DYNAMIC_LIBRARY DEFINES += CSSCALCULATOR_LIBRARY_STATIC DEFINES += CSS_CALCULATOR_WITH_XHTML +DEFINES += DESABLE_RUBY_SUPPORT CORE_ROOT_DIR = $$PWD/.. PWD_ROOT_DIR = $$PWD diff --git a/HtmlFile2/htmlfile2.cpp b/HtmlFile2/htmlfile2.cpp index 53b91e6ad4..31164f2191 100644 --- a/HtmlFile2/htmlfile2.cpp +++ b/HtmlFile2/htmlfile2.cpp @@ -1817,6 +1817,15 @@ private: m_oState.m_bInP = false; } + void WriteSpace(NSStringUtils::CStringBuilder* pXml) + { + if (NULL == pXml) + return; + + pXml->WriteString(L" "); + m_oState.m_bWasSpace = true; + } + void WriteBookmark(NSStringUtils::CStringBuilder* pXml, const std::wstring& wsId) { if (NULL == pXml) @@ -1990,10 +1999,7 @@ private: } if (oTS.bAddSpaces && m_oState.m_bInP && !m_oState.m_bInR && !iswspace(sText.front()) && !m_oState.m_bWasSpace) - { - oXml->WriteString(L" "); - m_oState.m_bWasSpace = true; - } + WriteSpace(oXml); std::wstring sPStyle = wrP(oXml, sSelectors, oTS); std::wstring sRStyle; @@ -2241,7 +2247,7 @@ private: bResult = readStream(oXml, sSelectors, oTSQ); } // Текст верхнего регистра - else if(sName == L"rt" || sName == L"sup") + else if(sName == L"sup") { CTextSettings oTSR(oTS); oTSR.AddRStyle(L""); @@ -2271,7 +2277,7 @@ private: 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" || + else if(sName == L"template" || sName == L"canvas" || sName == L"video" || sName == L"math" || 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") @@ -2297,7 +2303,7 @@ private: } // Без нового абзаца 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"noscript" || sName == L"output" || sName == L"abbr" || sName == L"time" || sName == L"progress" || sName == L"hgroup" || sName == L"meter" || sName == L"acronym") bResult = readStream(oXml, sSelectors, oTS); // С нового абзаца @@ -2429,11 +2435,10 @@ private: if (bOpenedP) CloseP(&oXmlData, sSelectors); } -// oXml->WriteString(L""); } // Меню // Маркированный список - else if(sName == L"menu" || sName == L"ul" || sName == L"select" || sName == L"datalist" || sName == L"dir") + else if(sName == L"ul" || sName == L"menu" || sName == L"select" || sName == L"datalist" || sName == L"dir") readLi(&oXmlData, sSelectors, oTS, true); // Нумерованный список else if(sName == L"ol") @@ -2442,13 +2447,15 @@ private: else if(sName == L"pre" || sName == L"xmp") { CTextSettings oTSPre(oTS); - sSelectors.back().m_wsStyle += L"; font-family:Consolas"; + sSelectors.back().m_wsStyle += L"; font-family:Courier New"; oTSPre.bPre = true; bResult = readStream(&oXmlData, sSelectors, oTSPre); } // Таблицы else if(sName == L"table") - ParseTable(&oXmlData, sSelectors, oTS); + bResult = ParseTable(&oXmlData, sSelectors, oTS); + else if(sName == L"ruby") + bResult = ParseRuby(&oXmlData, sSelectors, oTS); // Текст с границами else if(sName == L"textarea" || sName == L"fieldset") { @@ -2785,10 +2792,130 @@ private: } } - void ParseTable(NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, const CTextSettings& oTS) + bool ParseRuby(NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, const CTextSettings& oTS) { if(m_oLightReader.IsEmptyNode()) - return; + return false; + + const int nDepth = m_oLightReader.GetDepth(); + + #ifdef DESABLE_RUBY_SUPPORT + + wrP(oXml, sSelectors, oTS); + + while (m_oLightReader.ReadNextSiblingNode2(nDepth)) + { + GetSubClass(NULL, sSelectors); + + if (L"rp" == sSelectors.back().m_wsName) + { + sSelectors.pop_back(); + continue; + } + else if (L"rt" == sSelectors.back().m_wsName) + readStream(oXml, sSelectors, oTS); + else + readInside(oXml, sSelectors, oTS, sSelectors.back().m_wsName); + + sSelectors.pop_back(); + } + + CloseP(oXml, sSelectors); + + return true; + #endif + + NSStringUtils::CStringBuilder oBase; + NSStringUtils::CStringBuilder oRT; + + TState oRtState{m_oState}; + oRtState.m_bInP = true; + + TState oBaseState{m_oState}; + oBaseState.m_bInP = true; + + while (m_oLightReader.ReadNextSiblingNode2(nDepth)) + { + GetSubClass(NULL, sSelectors); + + CTextSettings oNewSettings{oTS}; + + NSCSS::CCompiledStyle oStyle{m_oStylesCalculator.GetCompiledStyle(sSelectors)}; + + const std::wstring wsHighlight{oStyle.m_oBackground.GetColor().EquateToColor({{{0, 0, 0}, L"black"}, {{0, 0, 255}, L"blue"}, {{0, 255, 255}, L"cyan"}, + {{0, 255, 0}, L"green"}, {{255, 0, 255}, L"magenta"}, {{255, 0, 0}, L"red"}, + {{255, 255, 0}, L"yellow"}, {{255, 255, 255}, L"white"}, {{0, 0, 139}, L"darkBlue"}, + {{0, 139, 139}, L"darkCyan"}, {{0, 100, 0}, L"darkGreen"}, {{139, 0, 139}, L"darkMagenta"}, + {{139, 0, 0}, L"darkRed"}, {{128, 128, 0}, L"darkYellow"},{{169, 169, 169}, L"darkGray"}, + {{211, 211, 211}, L"lightGray"}})}; + + if (L"none" != wsHighlight) + oNewSettings.AddRStyle(L""); + + if (L"rt" == sSelectors.back().m_wsName) + { + std::swap(m_oState, oRtState); + readStream(&oRT, sSelectors, oNewSettings); + std::swap(m_oState, oRtState); + } + else if (L"rp" == sSelectors.back().m_wsName) + { + sSelectors.pop_back(); + continue; + } + else if (L"#text" == sSelectors.back().m_wsName) + { + std::swap(m_oState, oBaseState); + readInside(&oBase, sSelectors, oNewSettings, sSelectors.back().m_wsName); + std::swap(m_oState, oBaseState); + } + sSelectors.pop_back(); + } + + WriteSpace(&oBase); + + wrP(oXml, sSelectors, oTS); + + if (0 != oRT.GetSize()) + { + NSCSS::CCompiledStyle oStyle{m_oStylesCalculator.GetCompiledStyle(sSelectors)}; + + int nFontSize = 24; + + if (!oStyle.m_oFont.GetSize().Empty() && !oStyle.m_oFont.GetSize().Zero()) + nFontSize = oStyle.m_oFont.GetSize().ToInt(NSCSS::Point) * 2; + + bool bConsistsChineseCharacters = false; + + const std::vector::const_reverse_iterator oFound{std::find_if(sSelectors.crbegin(), sSelectors.crend(), [](const NSCSS::CNode& oNode){ return oNode.m_mAttributes.cend() != oNode.m_mAttributes.find(L"lang");})}; + + if (sSelectors.crend() != oFound) + { + const size_t unFound{oFound->m_mAttributes.at(L"lang").find(L"-")}; + + if (std::wstring::npos != unFound) + bConsistsChineseCharacters = ConsistsChineseCharacters(oFound->m_mAttributes.at(L"lang").substr(0, unFound)); + } + + OpenR(oXml); + oXml->WriteString(L""); + oXml->WriteString(L"" + oRT.GetData() + L""); + oXml->WriteString(L"" + oBase.GetData() + L""); + oXml->WriteString(L""); + CloseR(oXml); + } + else + oXml->WriteString(oBase.GetData()); + + CloseP(oXml, sSelectors); + + return true; + } + + bool ParseTable(NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, const CTextSettings& oTS) + { + if(m_oLightReader.IsEmptyNode()) + return false; CTable oTable; @@ -2858,6 +2985,8 @@ private: oTable.CompleteTable(); oXml->WriteString(oTable.ConvertToOOXML()); WriteEmptyParagraph(oXml, true); + + return true; } void readInput (NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, const CTextSettings& oTS) @@ -2934,25 +3063,35 @@ private: continue; } + CloseP(oXml, sSelectors); + + CTextSettings oTSLiP(oTS); + std::wstring wsValue; + const std::wstring wsParentName{(!sSelectors.empty()) ? sSelectors.back().m_wsName : L""}; GetSubClass(oXml, sSelectors); + + std::wstring wsArgumentName; + while(m_oLightReader.MoveToNextAttribute()) { - if(m_oLightReader.GetName() == L"value") + wsArgumentName = m_oLightReader.GetName(); + + if(L"value" == wsArgumentName && L"datalist" == wsParentName) { if (sName == L"option") wsValue = m_oLightReader.GetText(); else nStart = NSStringFinder::ToInt(m_oLightReader.GetText(), 1); } + else if (L"disabled" == wsArgumentName) + oTSLiP.AddRStyle(L""); + else if (L"selected" == wsArgumentName) + oTSLiP.AddRStyle(L""); } m_oLightReader.MoveToElement(); - CloseP(oXml, sSelectors); - - CTextSettings oTSLiP(oTS); - if (std::wstring::npos != oTS.sPStyle.find(L"")) { wrP(oXml, sSelectors, oTS); diff --git a/HtmlFile2/src/Languages.h b/HtmlFile2/src/Languages.h index e267415ebe..a0449f94ef 100644 --- a/HtmlFile2/src/Languages.h +++ b/HtmlFile2/src/Languages.h @@ -81,4 +81,11 @@ static std::wstring IndentifyLanguage(std::wstring wsLanguage) return std::wstring(); } +static bool ConsistsChineseCharacters(std::wstring wsLanguage) +{ + std::transform(wsLanguage.begin(), wsLanguage.end(), wsLanguage.begin(), towlower); + + return L"zh" == wsLanguage || L"ja" == wsLanguage || L"ko" == wsLanguage; +} + #endif // LANGUAGES_LIST_H From 4b99c4179bd9cc0efcf07ce5796c9e3d5a06ebe0 Mon Sep 17 00:00:00 2001 From: Kirill Polyakov Date: Mon, 29 Jul 2024 12:39:51 +0300 Subject: [PATCH 13/18] Fixed inaccuracies when converting html to ooxml elements big and small --- HtmlFile2/htmlfile2.cpp | 123 ++++++++++++++++++++++++---------------- 1 file changed, 73 insertions(+), 50 deletions(-) diff --git a/HtmlFile2/htmlfile2.cpp b/HtmlFile2/htmlfile2.cpp index 31164f2191..53e5477460 100644 --- a/HtmlFile2/htmlfile2.cpp +++ b/HtmlFile2/htmlfile2.cpp @@ -146,6 +146,59 @@ 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; }); } +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; +} + +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; +} + +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; +} + typedef enum { ParseModeHeader, @@ -2113,13 +2166,6 @@ private: oTSBdo.bBdo = false; bResult = readStream(oXml, sSelectors, oTSBdo); } - // Увеличивает размер шрифта - else if(sName == L"big") - { - CTextSettings oTSR(oTS); - oTSR.AddRStyle(L""); - bResult = readStream(oXml, sSelectors, oTSR); - } // Перенос строки else if(sName == L"br") { @@ -2175,7 +2221,7 @@ private: bResult = readStream(oXml, sSelectors, oTSR); } // Зачеркнутый текст - else if(sName == L"del" || sName == L"s") + else if(sName == L"del" || sName == L"s" || sName == L"strike") { CTextSettings oTSR(oTS); oTSR.AddRStyle(L""); @@ -2253,13 +2299,6 @@ private: oTSR.AddRStyle(L""); bResult = readStream(oXml, sSelectors, oTSR); } - // Уменьшает размер шрифта - else if(sName == L"small") - { - CTextSettings oTSR(oTS); - oTSR.AddRStyle(L""); - bResult = readStream(oXml, sSelectors, oTSR); - } // Текст нижнего регистра else if(sName == L"sub") { @@ -2303,8 +2342,8 @@ private: } // Без нового абзаца 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"progress" || sName == L"hgroup" || sName == L"meter" || sName == L"acronym") + sName == L"noscript" || sName == L"output" || sName == L"abbr" || sName == L"time" || sName == L"small" || + sName == L"progress" || sName == L"hgroup" || sName == L"meter" || sName == L"acronym" || sName == L"big") bResult = readStream(oXml, sSelectors, oTS); // С нового абзаца else @@ -3491,18 +3530,6 @@ private: if (m_oState.m_bWasPStyle) return L""; - std::vector> 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); @@ -3523,25 +3550,6 @@ private: 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->WriteNodeBegin(L"w:pPr"); if (!sPStyle.empty()) @@ -3574,7 +3582,22 @@ private: const std::wstring sRSettings = m_oXmlStyle.GetStyle(); m_oXmlStyle.Clear(); - if (!sRStyle.empty() || !oTS.sRStyle.empty()) + std::wstring wsFontSize; + + const int nCalculatedFontChange{CalculateFontChange(sSelectors)}; + + if (0 != nCalculatedFontChange) + { + int nFontSizeLevel{static_cast((oStyle.m_oFont.Empty()) ? 3 : GetFontSizeLevel(oStyle.m_oFont.GetSize().ToInt(NSCSS::Point) * 2))}; + + nFontSizeLevel += nCalculatedFontChange; + + const UINT unFontSize{GetFontSizeByLevel(nFontSizeLevel)}; + + wsFontSize += L""; + } + + if (!sRStyle.empty() || !oTS.sRStyle.empty() || !wsFontSize.empty()) { oXml->WriteString(L""); if (!sRStyle.empty()) @@ -3584,7 +3607,7 @@ private: oXml->WriteString(L"\"/>"); } - oXml->WriteString(oTS.sRStyle + L' ' + sRSettings); + oXml->WriteString(oTS.sRStyle + L' ' + wsFontSize + L' ' + sRSettings); oXml->WriteString(L""); } return sRStyle; From c27aa4d80746ffe9f0daee89946c74cb7aeeb325 Mon Sep 17 00:00:00 2001 From: Kirill Polyakov Date: Thu, 1 Aug 2024 13:44:33 +0300 Subject: [PATCH 14/18] Improved html to ooxml conversion --- .../3dParty/html/css/src/StyleProperties.cpp | 52 +++- Common/3dParty/html/css/src/StyleProperties.h | 22 +- HtmlFile2/htmlfile2.cpp | 275 ++++++++++++++---- 3 files changed, 280 insertions(+), 69 deletions(-) diff --git a/Common/3dParty/html/css/src/StyleProperties.cpp b/Common/3dParty/html/css/src/StyleProperties.cpp index ab0cba24d4..9b14ac2540 100644 --- a/Common/3dParty/html/css/src/StyleProperties.cpp +++ b/Common/3dParty/html/css/src/StyleProperties.cpp @@ -420,7 +420,7 @@ namespace NSCSS return true; } - bool CDigit::SetValue(double dValue, unsigned int unLevel, bool bHardMode) + bool CDigit::SetValue(const double& dValue, unsigned int unLevel, bool bHardMode) { if (CHECK_CONDITIONS && !bHardMode) return false; @@ -1634,7 +1634,7 @@ namespace NSCSS return m_oWidth.SetValue(wsNewValue, unLevel, bHardMode); } - bool CBorderSide::SetWidth(double dValue, unsigned int unLevel, bool bHardMode) + bool CBorderSide::SetWidth(const double& dValue, unsigned int unLevel, bool bHardMode) { return m_oWidth.SetValue(dValue, unLevel, bHardMode); } @@ -1754,9 +1754,29 @@ namespace NSCSS void CBorder::Clear() { - m_oLeft .Clear(); - m_oTop .Clear(); - m_oRight .Clear(); + ClearLeftSide(); + ClearTopSide(); + ClearRightSide(); + ClearBottomSide(); + } + + void CBorder::ClearLeftSide() + { + m_oLeft.Clear(); + } + + void CBorder::ClearTopSide() + { + m_oTop.Clear(); + } + + void CBorder::ClearRightSide() + { + m_oRight.Clear(); + } + + void CBorder::ClearBottomSide() + { m_oBottom.Clear(); } @@ -1792,7 +1812,7 @@ namespace NSCSS return bResult; } - bool CBorder::SetWidth(double dValue, unsigned int unLevel, bool bHardMode) + bool CBorder::SetWidth(const double& dValue, unsigned int unLevel, bool bHardMode) { bool bResult = false; @@ -1843,6 +1863,11 @@ namespace NSCSS return m_oLeft.SetWidth(wsValue, unLevel, bHardMode); } + bool CBorder::SetWidthLeftSide(const double& dValue, unsigned int unLevel, bool bHardMode) + { + return m_oLeft.SetWidth(dValue, unLevel, bHardMode); + } + bool CBorder::SetStyleLeftSide(const std::wstring &wsValue, unsigned int unLevel, bool bHardMode) { return m_oLeft.SetStyle(wsValue, unLevel, bHardMode); @@ -1863,6 +1888,11 @@ namespace NSCSS return m_oTop.SetWidth(wsValue, unLevel, bHardMode); } + bool CBorder::SetWidthTopSide(const double& dValue, unsigned int unLevel, bool bHardMode) + { + return m_oTop.SetWidth(dValue, unLevel, bHardMode); + } + bool CBorder::SetStyleTopSide(const std::wstring &wsValue, unsigned int unLevel, bool bHardMode) { return m_oTop.SetStyle(wsValue, unLevel, bHardMode); @@ -1883,6 +1913,11 @@ namespace NSCSS return m_oRight.SetWidth(wsValue, unLevel, bHardMode); } + bool CBorder::SetWidthRightSide(const double& dValue, unsigned int unLevel, bool bHardMode) + { + return m_oRight.SetWidth(dValue, unLevel, bHardMode); + } + bool CBorder::SetStyleRightSide(const std::wstring &wsValue, unsigned int unLevel, bool bHardMode) { return m_oRight.SetStyle(wsValue, unLevel, bHardMode); @@ -1903,6 +1938,11 @@ namespace NSCSS return m_oBottom.SetWidth(wsValue, unLevel, bHardMode); } + bool CBorder::SetWidthBottomSide(const double& dValue, unsigned int unLevel, bool bHardMode) + { + return m_oBottom.SetWidth(dValue, unLevel, bHardMode); + } + bool CBorder::SetStyleBottomSide(const std::wstring &wsValue, unsigned int unLevel, bool bHardMode) { return m_oBottom.SetStyle(wsValue, unLevel, bHardMode); diff --git a/Common/3dParty/html/css/src/StyleProperties.h b/Common/3dParty/html/css/src/StyleProperties.h index 392f11aedc..fe2f1c90d0 100644 --- a/Common/3dParty/html/css/src/StyleProperties.h +++ b/Common/3dParty/html/css/src/StyleProperties.h @@ -136,7 +136,7 @@ namespace NSCSS bool SetValue(const std::wstring& wsValue, unsigned int unLevel = 0, bool bHardMode = true) override; bool SetValue(const CDigit& oValue); - bool SetValue(double dValue, unsigned int unLevel, bool bHardMode); + bool SetValue(const double& dValue, unsigned int unLevel, bool bHardMode); bool Empty() const override; bool Zero() const; @@ -450,7 +450,7 @@ namespace NSCSS bool SetValue(const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); bool SetWidth(const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); - bool SetWidth(double dValue, unsigned int unLevel, bool bHardMode = false); + bool SetWidth(const double& dValue, unsigned int unLevel, bool bHardMode = false); bool SetStyle(const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); bool SetColor(const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); @@ -493,37 +493,45 @@ namespace NSCSS CBorder(); void Clear(); + void ClearLeftSide(); + void ClearTopSide(); + void ClearRightSide(); + void ClearBottomSide(); static void Equation(CBorder &oFirstBorder, CBorder &oSecondBorder); - bool SetSides(const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); - bool SetWidth(const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); - bool SetWidth(double dValue, unsigned int unLevel, bool bHardMode = false); - bool SetStyle(const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); - bool SetColor(const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); + bool SetSides(const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); + bool SetWidth(const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); + bool SetWidth(const double& dValue, unsigned int unLevel, bool bHardMode = false); + bool SetStyle(const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); + bool SetColor(const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); bool SetCollapse(const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); //Left Side bool SetLeftSide (const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); bool SetWidthLeftSide (const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); + bool SetWidthLeftSide (const double& dValue, unsigned int unLevel, bool bHardMode = false); bool SetStyleLeftSide (const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); bool SetColorLeftSide (const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); //Top Side bool SetTopSide (const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); bool SetWidthTopSide (const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); + bool SetWidthTopSide (const double& dValue, unsigned int unLevel, bool bHardMode = false); bool SetStyleTopSide (const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); bool SetColorTopSide (const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); //Right Side bool SetRightSide (const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); bool SetWidthRightSide (const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); + bool SetWidthRightSide (const double& dValue, unsigned int unLevel, bool bHardMode = false); bool SetStyleRightSide (const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); bool SetColorRightSide (const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); //Bottom Side bool SetBottomSide (const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); bool SetWidthBottomSide(const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); + bool SetWidthBottomSide(const double& dValue, unsigned int unLevel, bool bHardMode = false); bool SetStyleBottomSide(const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); bool SetColorBottomSide(const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); diff --git a/HtmlFile2/htmlfile2.cpp b/HtmlFile2/htmlfile2.cpp index 53e5477460..e9b84c4c15 100644 --- a/HtmlFile2/htmlfile2.cpp +++ b/HtmlFile2/htmlfile2.cpp @@ -52,8 +52,6 @@ for (object_type* pElement : vector_object) \ RELEASEOBJECT(pElement) \ -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 CTc { @@ -83,16 +81,28 @@ struct CTextSettings std::wstring sRStyle; // w:rStyle std::wstring sPStyle; // w:pStyle + enum ETextMode + { + Normal, + Superscript, + Subscript + } eTextMode; + NSCSS::CCompiledStyle oPriorityStyle; CTextSettings() - : bBdo(false), bPre(false), bQ(false), bAddSpaces(true), bMergeText(false), nLi(-1) + : bBdo(false), bPre(false), bQ(false), bAddSpaces(true), bMergeText(false), nLi(-1), eTextMode(Normal) {} CTextSettings(const CTextSettings& oTS) : bBdo(oTS.bBdo), bPre(oTS.bPre), bQ(oTS.bQ), bAddSpaces(oTS.bAddSpaces), bMergeText(oTS.bMergeText), nLi(oTS.nLi), sRStyle(oTS.sRStyle), sPStyle(oTS.sPStyle) {} + bool HaveRStyles() const + { + return !sRStyle.empty() || Normal != eTextMode; + } + void AddRStyle(const std::wstring& wsStyle) { if (std::wstring::npos == sRStyle.find(wsStyle)) @@ -199,6 +209,18 @@ int CalculateFontChange(const std::vector& arSelectors) return nFontChange; } +#define FIRST_ELEMENT 0x00000001 +#define LAST_ELEMENT 0x00000002 +#define MID_ELEMENT 0x00000004 + +#define PARSE_MODE_HEADER 0x00000100 +#define PARSE_MODE_BODY 0x00000200 +#define PARSE_MODE_FOOTHER 0x00000400 + +#define COL_POSITION_MASK 0x0000000F +#define ROW_POSITION_MASK 0x000000F0 +#define PARSE_MODE_MASK 0x00000F00 + typedef enum { ParseModeHeader, @@ -297,6 +319,12 @@ std::wstring CreateBorders(const std::wstring& wsStyle, UINT unSize, UINT unSpac #define CreateOutsetBorders(enType) CreateBorders(L"outset", 6, 0, L"auto", enType) +std::wstring CreateDefaultBorder(std::wstring wsSideName) +{ + std::transform(wsSideName.begin(), wsSideName.end(), wsSideName.begin(), std::towlower); + return L""; +} + struct TTableRowStyle { UINT m_unMaxIndex; @@ -373,16 +401,16 @@ class CTableCell { public: CTableCell() - : m_unColspan(1), m_unRowSpan(1), m_bIsMerged(false), m_bIsEmpty(false), m_enMode(ParseModeBody) + : m_unColspan(1), m_unRowSpan(1), m_bIsMerged(false), m_bIsEmpty(false) {} CTableCell(UINT unColspan, UINT unRowspan, bool bIsMerged, bool bIsEmpty) - : m_unColspan(unColspan), m_unRowSpan(unRowspan), m_bIsMerged(bIsMerged), m_bIsEmpty(bIsEmpty), m_enMode(ParseModeBody) + : m_unColspan(unColspan), m_unRowSpan(unRowspan), m_bIsMerged(bIsMerged), m_bIsEmpty(bIsEmpty) {} CTableCell(CTableCell& oCell) : m_unColspan(oCell.m_unColspan), m_unRowSpan(oCell.m_unRowSpan), m_bIsMerged(oCell.m_bIsMerged), - m_bIsEmpty(oCell.m_bIsEmpty), m_enMode(oCell.m_enMode), m_oStyles(oCell.m_oStyles) + m_bIsEmpty(oCell.m_bIsEmpty), m_oStyles(oCell.m_oStyles) { m_oData.SetText(oCell.m_oData.GetData()); } @@ -415,11 +443,6 @@ public: return pCell; } - void SetMode(ERowParseMode eMode) - { - m_enMode = eMode; - } - void SetColspan(UINT unColspan, UINT unCurrentIndex) { if (MAXCOLUMNSINTABLE - 1 != unCurrentIndex) @@ -518,14 +541,13 @@ public: m_oStyles.m_oBackground = oColor; } - std::wstring ConvertToOOXML(const CTable& oTable, UINT unColumnNumber); + std::wstring ConvertToOOXML(const CTable& oTable, UINT unColumnNumber, int nInstruction); private: UINT m_unColspan; UINT m_unRowSpan; bool m_bIsMerged; bool m_bIsEmpty; - ERowParseMode m_enMode; TTableCellStyle m_oStyles; NSStringUtils::CStringBuilder m_oData; @@ -633,7 +655,7 @@ public: return m_arCells.size(); } - std::wstring ConvertToOOXML(const CTable& oTable); + std::wstring ConvertToOOXML(const CTable& oTable, int nInstruction); CTableCell* operator[](UINT unIndex) { @@ -734,6 +756,10 @@ public: ~CTable() { + for (std::vector& arHeaders : m_arHeaders) + RELEASE_VECTOR(arHeaders, CTableRow) + + RELEASE_VECTOR(m_arFoother, CTableRow) RELEASE_VECTOR(m_arRows, CTableRow) RELEASE_VECTOR(m_arColgroups, CTableColgroup) } @@ -818,7 +844,22 @@ public: return std::wstring(); } - void AddRow(CTableRow* pRow) + void AddRows(std::vector& m_arRows, ERowParseMode eParseMode = ERowParseMode::ParseModeBody) + { + if (m_arRows.empty()) + return; + + if (ERowParseMode::ParseModeFoother == eParseMode && !m_arFoother.empty()) + eParseMode = ERowParseMode::ParseModeHeader; + + if (ERowParseMode::ParseModeHeader == eParseMode) + m_arHeaders.push_back({}); + + for (CTableRow* pRow : m_arRows) + AddRow(pRow, eParseMode); + } + + void AddRow(CTableRow* pRow, ERowParseMode eParseMode = ERowParseMode::ParseModeBody) { if (NULL == pRow) return; @@ -831,7 +872,28 @@ public: m_arMinColspan[unIndex] = (*pRow)[unIndex]->GetColspan(); } - m_arRows.push_back(pRow); + switch (eParseMode) + { + default: + case ERowParseMode::ParseModeBody: + { + m_arRows.push_back(pRow); + break; + } + case ERowParseMode::ParseModeHeader: + { + if (m_arHeaders.empty()) + m_arHeaders.push_back({}); + + m_arHeaders.back().push_back(pRow); + break; + } + case ERowParseMode::ParseModeFoother: + { + m_arFoother.push_back(pRow); + break; + } + } } void AddCaption(NSStringUtils::CStringBuilder& oCaption) @@ -1056,15 +1118,37 @@ public: oTable.WriteNodeEnd(L"w:tr"); } - for (CTableRow* pRow : m_arRows) - oTable += pRow->ConvertToOOXML(*this); + #define CONVERT_ROWS(rows, mode) \ + { \ + for (UINT unRowIndex = 0; unRowIndex < rows.size(); ++unRowIndex) \ + { \ + int nInstruction = 0; \ + if (0 == unRowIndex) \ + nInstruction |= FIRST_ELEMENT << 4; \ + if (rows.size() - 1 == unRowIndex) \ + nInstruction |= LAST_ELEMENT << 4; \ + else if (0 != unRowIndex) \ + nInstruction |= MID_ELEMENT << 4; \ + nInstruction |= mode; \ + oTable += rows[unRowIndex]->ConvertToOOXML(*this, nInstruction); \ + } \ + } + + for (std::vector& arRows : m_arHeaders) + CONVERT_ROWS(arRows, PARSE_MODE_HEADER) + + CONVERT_ROWS(m_arRows, PARSE_MODE_BODY) + CONVERT_ROWS(m_arFoother, PARSE_MODE_FOOTHER) oTable.WriteNodeEnd(L"w:tbl"); return oTable.GetData(); } private: - std::vector m_arRows; + std::vector> m_arHeaders; + std::vector m_arFoother; + std::vector m_arRows; + std::vector m_arMinColspan; NSStringUtils::CStringBuilder m_oCaption; @@ -1395,7 +1479,7 @@ public: m_oStylesXml += L""; m_oStylesXml += L""; m_oStylesXml += L""; - m_oStylesXml += L""; + m_oStylesXml += L""; // m_oStylesXml += L""; } @@ -2043,7 +2127,7 @@ private: if (sText.end() == std::find_if_not(sText.begin(), sText.end(), [](wchar_t wchChar){ return iswspace(wchChar);})) return false; - bool bInT = m_oState.m_bInT; + const bool bInT = m_oState.m_bInT; if (!oTS.sRStyle.empty() || oTS.bPre) { @@ -2051,7 +2135,7 @@ private: CloseR(oXml); } - if (oTS.bAddSpaces && m_oState.m_bInP && !m_oState.m_bInR && !iswspace(sText.front()) && !m_oState.m_bWasSpace) + if (oTS.bAddSpaces && m_oState.m_bInP && !m_oState.m_bInR && !iswspace(sText.front()) && !m_oState.m_bWasSpace && CTextSettings::Normal == oTS.eTextMode) WriteSpace(oXml); std::wstring sPStyle = wrP(oXml, sSelectors, oTS); @@ -2296,14 +2380,14 @@ private: else if(sName == L"sup") { CTextSettings oTSR(oTS); - oTSR.AddRStyle(L""); + oTSR.eTextMode = CTextSettings::Superscript; bResult = readStream(oXml, sSelectors, oTSR); } // Текст нижнего регистра else if(sName == L"sub") { CTextSettings oTSR(oTS); - oTSR.AddRStyle(L""); + oTSR.eTextMode = CTextSettings::Subscript; bResult = readStream(oXml, sSelectors, oTSR); } // Векторная картинка @@ -2486,7 +2570,7 @@ private: else if(sName == L"pre" || sName == L"xmp") { CTextSettings oTSPre(oTS); - sSelectors.back().m_wsStyle += L"; font-family:Courier New"; + oTSPre.AddRStyle(L""); oTSPre.bPre = true; bResult = readStream(&oXmlData, sSelectors, oTSPre); } @@ -2729,6 +2813,7 @@ private: void ParseTableRows(CTable& oTable, std::vector& sSelectors, const CTextSettings& oTS, ERowParseMode eMode) { std::vector arRowspanElements; + std::vector arRows; int nDeath = m_oLightReader.GetDepth(); while (m_oLightReader.ReadNextSiblingNode(nDeath)) @@ -2760,8 +2845,6 @@ private: if (NULL == pCell) continue; - pCell->SetMode(eMode); - GetSubClass(pCell->GetData(), sSelectors); const std::vector arNewSelectors{(std::vector::const_iterator)std::find_if(sSelectors.begin(), sSelectors.end(), [](const NSCSS::CNode& oNode){ return L"table" == oNode.m_wsName; }), sSelectors.cend()}; @@ -2826,9 +2909,10 @@ private: } sSelectors.pop_back(); - - oTable.AddRow(pRow); + arRows.push_back(pRow); } + + oTable.AddRows(arRows, eMode); } bool ParseRuby(NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, const CTextSettings& oTS) @@ -2962,29 +3046,73 @@ private: m_oStylesCalculator.GetCompiledStyle(oStyle, sSelectors); //Table styles - if (sSelectors.back().m_mAttributes.end() != sSelectors.back().m_mAttributes.find(L"border")) - { - const int nWidth = NSStringFinder::ToInt(sSelectors.back().m_mAttributes[L"border"]); + std::wstring wsFrame; - if (0 < nWidth) + for (const std::pair oArgument : sSelectors.back().m_mAttributes) + { + if (L"border" == oArgument.first) { - oStyle.m_oBorder.SetStyle(L"outset", 0, true); - oStyle.m_oBorder.SetWidth(nWidth, 0, true); - oStyle.m_oBorder.SetColor(L"auto", 0, true); - oTable.SetRules(L"all"); - } - else - { - oStyle.m_oBorder.SetNone(0, true); - oTable.SetRules(L"none"); + const int nWidth = NSStringFinder::ToInt(oArgument.second); + + if (0 < nWidth) + { + oStyle.m_oBorder.SetStyle(L"outset", 0, true); + oStyle.m_oBorder.SetWidth(nWidth, 0, true); + oStyle.m_oBorder.SetColor(L"auto", 0, true); + oTable.SetRules(L"all"); + } + else + { + oStyle.m_oBorder.SetNone(0, true); + oTable.SetRules(L"none"); + } } + else if (L"cellpadding" == oArgument.first) + oStyle.m_oPadding.SetValues(oArgument.second + L"px", 0, true); + else if (L"rules" == oArgument.first) + oTable.SetRules(oArgument.second); + else if (L"frame" == oArgument.first) + wsFrame = oArgument.second; } - if (sSelectors.back().m_mAttributes.end() != sSelectors.back().m_mAttributes.find(L"cellpadding")) - oStyle.m_oPadding.SetValues(sSelectors.back().m_mAttributes[L"cellpadding"] + L"px", 0, true); + if (!wsFrame.empty() && oStyle.m_oBorder.Empty()) + { + #define SetDefaultBorderSide(side) \ + oStyle.m_oBorder.SetStyle##side(L"solid", 0, true); \ + oStyle.m_oBorder.SetWidth##side(1, 0, true); \ + oStyle.m_oBorder.SetColor##side(L"black", 0, true); - if (sSelectors.back().m_mAttributes.end() != sSelectors.back().m_mAttributes.find(L"rules")) - oTable.SetRules(sSelectors.back().m_mAttributes[L"rules"]); + if (NSStringFinder::Equals(L"border", wsFrame)) + { + SetDefaultBorderSide() + } + else if (NSStringFinder::Equals(L"above", wsFrame)) + { + SetDefaultBorderSide(TopSide) + } + else if (NSStringFinder::Equals(L"below", wsFrame)) + { + SetDefaultBorderSide(BottomSide) + } + else if (NSStringFinder::Equals(L"hsides", wsFrame)) + { + SetDefaultBorderSide(TopSide) + SetDefaultBorderSide(BottomSide) + } + else if (NSStringFinder::Equals(L"vsides", wsFrame)) + { + SetDefaultBorderSide(LeftSide) + SetDefaultBorderSide(RightSide) + } + else if (NSStringFinder::Equals(L"rhs", wsFrame)) + { + SetDefaultBorderSide(RightSide) + } + else if (NSStringFinder::Equals(L"lhs", wsFrame)) + { + SetDefaultBorderSide(LeftSide) + } + } if (oStyle.m_oBorder.GetCollapse() == NSCSS::NSProperties::BorderCollapse::Collapse) oTable.SetCellSpacing(0); @@ -3009,11 +3137,11 @@ private: if(sName == L"caption") ParseTableCaption(oTable, sSelectors, oTS); if(sName == L"thead") - ParseTableRows(oTable, sSelectors, oTS, ERowParseMode::ParseModeHeader); + ParseTableRows(oTable, sSelectors, oTS, ERowParseMode::ParseModeHeader); if(sName == L"tbody") ParseTableRows(oTable, sSelectors, oTS, ERowParseMode::ParseModeBody); else if(sName == L"tfoot") - ParseTableRows(oTable, sSelectors, oTS, ERowParseMode::ParseModeFoother); + ParseTableRows(oTable, sSelectors, oTS, ERowParseMode::ParseModeFoother); else if (sName == L"colgroup") ParseTableColspan(oTable); @@ -3597,7 +3725,7 @@ private: wsFontSize += L""; } - if (!sRStyle.empty() || !oTS.sRStyle.empty() || !wsFontSize.empty()) + if (!sRStyle.empty() || oTS.HaveRStyles() || !wsFontSize.empty()) { oXml->WriteString(L""); if (!sRStyle.empty()) @@ -3607,7 +3735,23 @@ private: oXml->WriteString(L"\"/>"); } - oXml->WriteString(oTS.sRStyle + L' ' + wsFontSize + L' ' + sRSettings); + switch (oTS.eTextMode) + { + case CTextSettings::Subscript: + { + oXml->WriteString(L""); + break; + } + case CTextSettings::Superscript: + { + oXml->WriteString(L""); + break; + } + default: + break; + } + + oXml->WriteString(oTS.sRStyle + wsFontSize + sRSettings); oXml->WriteString(L""); } return sRStyle; @@ -3940,7 +4084,7 @@ HRESULT CHtmlFile2::OpenBatchHtml(const std::vector& sSrc, const s return S_OK; } -std::wstring CTableRow::ConvertToOOXML(const CTable& oTable) +std::wstring CTableRow::ConvertToOOXML(const CTable& oTable, int nInstruction) { if (m_arCells.empty()) return std::wstring(); @@ -3967,21 +4111,32 @@ std::wstring CTableRow::ConvertToOOXML(const CTable& oTable) } for (UINT unIndex = 0; unIndex < m_arCells.size();) - oRow += m_arCells[unIndex]->ConvertToOOXML(oTable, ++unIndex); + { + int nNewInstruction{nInstruction}; + + if (0 == unIndex) + nNewInstruction |= FIRST_ELEMENT; + if (m_arCells.size() - 1 == unIndex) + nNewInstruction |= LAST_ELEMENT; + else if (0 != unIndex) + nNewInstruction |= MID_ELEMENT; + + oRow += m_arCells[unIndex]->ConvertToOOXML(oTable, ++unIndex, nNewInstruction); + } oRow.WriteNodeEnd(L"w:tr"); return oRow.GetData(); } -std::wstring CTableCell::ConvertToOOXML(const CTable& oTable, UINT unColumnNumber) +std::wstring CTableCell::ConvertToOOXML(const CTable& oTable, UINT unColumnNumber, int nInstruction) { NSStringUtils::CStringBuilder oCell; oCell.WriteNodeBegin(L"w:tc"); oCell.WriteNodeBegin(L"w:tcPr"); - if (ParseModeHeader == m_enMode) + if (PARSE_MODE_HEADER == (nInstruction & PARSE_MODE_MASK)) oCell += L""; TTableCellStyle oCellStyle(m_oStyles); @@ -4026,9 +4181,17 @@ std::wstring CTableCell::ConvertToOOXML(const CTable& oTable, UINT unColumnNumbe if (!oCellStyle.m_oBorder.Empty() && !oCellStyle.m_oBorder.Zero() /*&& oCellStyle.m_oBorder != oTableStyles.m_oBorder*/) oCell += L"" + CreateBorders(oCellStyle.m_oBorder, &oCellStyle.m_oPadding) + L""; - else if (TTableStyles::ETableRules::Groups == oTable.GetTableStyles().m_enRules && oTable.HaveColgroups()) + else if (TTableStyles::ETableRules::Groups == oTable.GetTableStyles().m_enRules) { - const std::wstring wsBorders{oTable.CalculateSidesToClean(unColumnNumber)}; + std::wstring wsBorders; + + if (oTable.HaveColgroups()) + wsBorders += oTable.CalculateSidesToClean(unColumnNumber); + + if (PARSE_MODE_HEADER == (nInstruction & PARSE_MODE_MASK) && (((nInstruction & ROW_POSITION_MASK) >> 4) & LAST_ELEMENT)) + wsBorders += CreateDefaultBorder(L"bottom"); + else if (PARSE_MODE_FOOTHER == (nInstruction & PARSE_MODE_MASK) && (((nInstruction & ROW_POSITION_MASK) >> 4) & FIRST_ELEMENT)) + wsBorders += CreateDefaultBorder(L"top"); if (!wsBorders.empty()) oCell += L"" + wsBorders + L""; From c026f33aa79603248d62686ca3c885af5d4258dd Mon Sep 17 00:00:00 2001 From: Kirill Polyakov Date: Tue, 6 Aug 2024 15:47:58 +0300 Subject: [PATCH 15/18] Improved html to ooxml conversion --- .../3dParty/html/css/src/StyleProperties.cpp | 1 + HtmlFile2/htmlfile2.cpp | 273 ++++++++++++------ 2 files changed, 190 insertions(+), 84 deletions(-) diff --git a/Common/3dParty/html/css/src/StyleProperties.cpp b/Common/3dParty/html/css/src/StyleProperties.cpp index 9b14ac2540..6467cae879 100644 --- a/Common/3dParty/html/css/src/StyleProperties.cpp +++ b/Common/3dParty/html/css/src/StyleProperties.cpp @@ -519,6 +519,7 @@ namespace NSCSS { m_enType = oColor.m_enType; m_oOpacity = oColor.m_oOpacity; + m_unLevel = oColor.m_unLevel; switch(m_enType) { diff --git a/HtmlFile2/htmlfile2.cpp b/HtmlFile2/htmlfile2.cpp index e9b84c4c15..7f99d9e171 100644 --- a/HtmlFile2/htmlfile2.cpp +++ b/HtmlFile2/htmlfile2.cpp @@ -46,9 +46,12 @@ #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 + #define SAVE_NORMALIZED_HTML 0 -#define RELEASE_VECTOR(vector_object, object_type) \ +#define RELEASE_VECTOR_PTR(vector_object, object_type) \ for (object_type* pElement : vector_object) \ RELEASEOBJECT(pElement) \ @@ -116,6 +119,31 @@ struct CTextSettings } }; +struct TImageData +{ + UINT m_unWidth; + UINT m_unHeight; + + int m_nHSpace; + int m_nVSpace; + + std::wstring m_wsAlign; + + TImageData() + : m_unWidth(0), m_unHeight(0), m_nHSpace(0), m_nVSpace(0), m_wsAlign(L"left") + {} + + bool ZeroSize() const + { + return 0 == m_unWidth || 0 == m_unHeight; + } + + bool ZeroSpaces() const + { + return 0 == m_nHSpace && 0 == m_nVSpace; + } +}; + void WriteEmptyParagraph(NSStringUtils::CStringBuilder* pXml, bool bVahish = false, bool bInP = false) { if (NULL == pXml) @@ -724,7 +752,7 @@ public: ~CTableColgroup() { - RELEASE_VECTOR(m_arCols, CTableCol) + RELEASE_VECTOR_PTR(m_arCols, CTableCol) } bool Empty() const @@ -757,11 +785,11 @@ public: ~CTable() { for (std::vector& arHeaders : m_arHeaders) - RELEASE_VECTOR(arHeaders, CTableRow) + RELEASE_VECTOR_PTR(arHeaders, CTableRow) - RELEASE_VECTOR(m_arFoother, CTableRow) - RELEASE_VECTOR(m_arRows, CTableRow) - RELEASE_VECTOR(m_arColgroups, CTableColgroup) + RELEASE_VECTOR_PTR(m_arFoother, CTableRow) + RELEASE_VECTOR_PTR(m_arRows, CTableRow) + RELEASE_VECTOR_PTR(m_arColgroups, CTableColgroup) } CTableRow* operator[](UINT unIndex) @@ -1502,6 +1530,8 @@ public: // m_oStylesXml += L""; // Сноски m_oStylesXml += L""; + // Web стиль по-умолчанию + m_oStylesXml += L""; } // Читает файл @@ -1959,7 +1989,9 @@ private: if (NULL == pXml) return; - pXml->WriteString(L" "); + OpenR(pXml); + pXml->WriteString(L" "); + CloseR(pXml); m_oState.m_bWasSpace = true; } @@ -2226,6 +2258,22 @@ private: // Область ссылки if(sName == L"a" || sName == L"area") readA(oXml, sSelectors, oTS, sNote); + else if (sName == L"abbr") + { + if (!sNote.empty()) + { + wrP(oXml, sSelectors, oTS); + const std::wstring wsName{L"Bookmark" + std::to_wstring(m_mBookmarks.size() + 1)}; + m_mBookmarks.insert(std::make_pair(wsName, m_mBookmarks.size() + 1)); + oXml->WriteString(L"HYPERLINK \\l \"" + wsName + L"\" \\o \""); + oXml->WriteEncodeXmlString(sNote); + oXml->WriteString(L"\""); + oXml->WriteString(L""); + bResult = readStream(oXml, sSelectors, oTS); + oXml->WriteString(L""); + sNote.clear(); + } + } // Полужирный текст // Акцентированный текст else if(sName == L"b" || sName == L"strong") @@ -2255,11 +2303,12 @@ private: { if (m_oState.m_bInP) { - oXml->WriteString(L""); + OpenR(oXml); NSCSS::CCompiledStyle oStyle = m_oStylesCalculator.GetCompiledStyle(sSelectors); if(oStyle.m_oText.GetAlign() == L"both") oXml->WriteString(L""); - oXml->WriteString(L""); + oXml->WriteString(L""); + CloseR(oXml); } else WriteEmptyParagraph(oXml, false, m_oState.m_bInP); @@ -2426,7 +2475,7 @@ private: } // Без нового абзаца 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"small" || + sName == L"noscript" || sName == L"output" || sName == L"time" || sName == L"small" || sName == L"progress" || sName == L"hgroup" || sName == L"meter" || sName == L"acronym" || sName == L"big") bResult = readStream(oXml, sSelectors, oTS); // С нового абзаца @@ -2660,7 +2709,6 @@ private: bResult = readStream(&oXmlData, sSelectors, oTS); readNote(&oXmlData, sSelectors, sNote); - sNote = L""; CloseP(&oXmlData, sSelectors); @@ -3179,11 +3227,12 @@ private: if(!sValue.empty()) { wrP(oXml, sSelectors, oTS); - oXml->WriteString(L""); + OpenR(oXml); wrRPr(oXml, sSelectors, oTS); - oXml->WriteString(L""); + OpenT(oXml); oXml->WriteEncodeXmlString(sValue + L' '); - oXml->WriteString(L""); + CloseT(oXml); + CloseR(oXml); } readStream(oXml, sSelectors, oTS, ElementInTable(sSelectors)); @@ -3211,13 +3260,15 @@ private: { if(m_oLightReader.GetName() != L"label") continue; + CloseP(oXml, sSelectors); wrP(oXml, sSelectors, oTS); - oXml->WriteString(L""); + OpenR(oXml); wrRPr(oXml, sSelectors, oTS); - oXml->WriteString(L""); + OpenT(oXml); oXml->WriteEncodeXmlString(m_oLightReader.GetText()); - oXml->WriteString(L""); + CloseT(oXml); + CloseR(oXml); } m_oLightReader.MoveToElement(); readLi(oXml, sSelectors, oTS, true); @@ -3345,6 +3396,7 @@ private: sFootnote = L"href"; } m_oLightReader.MoveToElement(); + if(sNote.empty()) sNote = sRef; @@ -3388,11 +3440,12 @@ private: if(!readStream(oXml, sSelectors, oTS)) { - oXml->WriteString(L""); + OpenR(oXml); wrRPr(oXml, sSelectors, oTS); - oXml->WriteString(L""); + OpenT(oXml); oXml->WriteEncodeXmlString(!sAlt.empty() ? sAlt : L" "); - oXml->WriteString(L""); + CloseT(oXml); + CloseR(oXml); } if (m_oState.m_bInP) @@ -3416,13 +3469,19 @@ private: if (!bFootnote) { std::wstring sFootnoteID = std::to_wstring(m_nFootnoteId++); - oXml->WriteString(L"WriteString(L"WriteString(sFootnoteID); - oXml->WriteString(L"\"/>"); + oXml->WriteString(L"\"/>"); + CloseR(oXml); m_mFootnotes.insert(std::make_pair(sFootnote, sFootnoteID)); } else - oXml->WriteString(L""); + { + OpenR(oXml); + oXml->WriteString(L""); + CloseR(oXml); + } } CloseP(oXml, sSelectors); @@ -3510,7 +3569,7 @@ private: 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, const std::wstring& wsSrc, unsigned int unWidth, unsigned int unHeight) + void ImageAlternative(NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, const CTextSettings& oTS, const std::wstring& wsAlt, const std::wstring& wsSrc, const TImageData& oImageData) { m_oDocXmlRels.WriteString(L"(NSCSS::CUnitMeasureConverter::ConvertPx(oDigit.ToDouble(), NSCSS::Inch, 96) * 914400); \ + else \ + data = static_cast(oDigit.ToDouble(NSCSS::Inch) * 914400); \ + } \ + }\ while (m_oLightReader.MoveToNextAttribute()) { @@ -3540,33 +3611,19 @@ private: else if (wsName == L"src") sSrcM = m_oLightReader.GetText(); else if (wsName == L"width") - { - NSCSS::NSProperties::CDigit oDigit; - if (oDigit.SetValue(m_oLightReader.GetText())) - { - if (NSCSS::UnitMeasure::None == oDigit.GetUnitMeasure()) - unWidth = static_cast(NSCSS::CUnitMeasureConverter::ConvertPx(oDigit.ToDouble(), NSCSS::Inch, 96) * 914400); - else - unWidth = static_cast(oDigit.ToDouble(NSCSS::Inch) * 914400); - } - } + READ_IMAGE_DATA(oImageData.m_unWidth) else if (wsName == L"height") - { - NSCSS::NSProperties::CDigit oDigit; - if (oDigit.SetValue(m_oLightReader.GetText())) - { - if (NSCSS::UnitMeasure::None == oDigit.GetUnitMeasure()) - unHeight = static_cast(NSCSS::CUnitMeasureConverter::ConvertPx(oDigit.ToDouble(), NSCSS::Inch, 96) * 914400 + 0.5); - else - unHeight = static_cast(oDigit.ToDouble(NSCSS::Inch) * 914400 + 0.5); - } - } + READ_IMAGE_DATA(oImageData.m_unHeight) + else if (wsName == L"hspace") + READ_IMAGE_DATA(oImageData.m_nHSpace) + else if (wsName == L"vspace") + READ_IMAGE_DATA(oImageData.m_nVSpace) } m_oLightReader.MoveToElement(); if (sSrcM.empty()) { - ImageAlternative(oXml, sSelectors, oTS, wsAlt, sSrcM, unWidth, unHeight); + ImageAlternative(oXml, sSelectors, oTS, wsAlt, sSrcM, oImageData); return; } @@ -3597,7 +3654,7 @@ private: std::transform(sExtention.begin(), sExtention.end(), sExtention.begin(), tolower); if (NotValidExtension(sExtention)) { - ImageAlternative(oXml, sSelectors, oTS, wsAlt, sSrcM, unWidth, unHeight); + ImageAlternative(oXml, sSelectors, oTS, wsAlt, sSrcM, oImageData); return; } @@ -3643,11 +3700,11 @@ private: } if (!bRes) - ImageAlternative(oXml, sSelectors, oTS, wsAlt, sSrcM, unWidth, unHeight); + ImageAlternative(oXml, sSelectors, oTS, wsAlt, sSrcM, oImageData); else { wrP(oXml, sSelectors, oTS); - ImageRels(oXml, nImageId, sImageSrc, sExtention, unWidth, unHeight); + ImageRels(oXml, nImageId, sImageSrc, sExtention, oImageData); } } @@ -3671,6 +3728,9 @@ private: std::wstring sPStyle = GetStyle(oStyle, true); + if (sPStyle.empty() && !ElementInTable(sSelectors)) + sPStyle = L"noraml-web"; + if (sPStyle.empty() && oTS.sPStyle.empty()) return L""; @@ -3757,39 +3817,79 @@ private: return sRStyle; } - void WriteImage(NSStringUtils::CStringBuilder* pXml, int nWidth, int nHeight, const std::wstring& wsId) + void WriteImage(NSStringUtils::CStringBuilder* pXml, const TImageData& oImageData, const std::wstring& wsId) { + if (NULL == pXml) + return; + + OpenR(pXml); + // Пишем в document.xml - pXml->WriteString(L"WriteString(std::to_wstring(nWidth)); - pXml->WriteString(L"\" cy=\""); - pXml->WriteString(std::to_wstring(nHeight)); - pXml->WriteString(L"\"/>WriteString(wsId); - pXml->WriteString(L"\" name=\"\"/>WriteString(wsId); - pXml->WriteString(L"\" name=\"\"/>WriteString(wsId); - pXml->WriteString(L"\"/>WriteString(std::to_wstring(nWidth)); - pXml->WriteString(L"\" cy=\""); - pXml->WriteString(std::to_wstring(nHeight)); - pXml->WriteString(L"\"/>"); + if (oImageData.ZeroSpaces()) + { + pXml->WriteString(L"WriteString(std::to_wstring(oImageData.m_unWidth)); + pXml->WriteString(L"\" cy=\""); + pXml->WriteString(std::to_wstring(oImageData.m_unHeight)); + pXml->WriteString(L"\"/>WriteString(wsId); + pXml->WriteString(L"\" name=\"Picture " + wsId + L"\"/>WriteString(wsId); + pXml->WriteString(L"\" name=\"Picture " + wsId + L"\"/>WriteString(wsId); + pXml->WriteString(L"\"/>WriteString(std::to_wstring(oImageData.m_unWidth)); + pXml->WriteString(L"\" cy=\""); + pXml->WriteString(std::to_wstring(oImageData.m_unHeight)); + pXml->WriteString(L"\"/>"); + } + else + { + pXml->WriteString(L""); + pXml->WriteString(L""); + pXml->WriteString(L""); + pXml->WriteString(L"" + oImageData.m_wsAlign + L""); + pXml->WriteString(L"0"); + pXml->WriteString(L""); + pXml->WriteString(L""); + pXml->WriteString(L""); + pXml->WriteString(L""); + pXml->WriteString(L""); + pXml->WriteString(L""); + pXml->WriteString(L""); + pXml->WriteString(L""); + pXml->WriteString(L""); + pXml->WriteString(L""); + pXml->WriteString(L""); + pXml->WriteString(L""); + pXml->WriteString(L""); + pXml->WriteString(L"00"); + pXml->WriteString(L""); + } + + CloseR(pXml); } void WriteEmptyImage(NSStringUtils::CStringBuilder* pXml, int nWidth, int nHeight, const std::wstring& wsName = L"", const std::wstring& wsDescr = L"") { - pXml->WriteString(L""); + if (NULL == pXml) + return; + + OpenR(pXml); + + pXml->WriteString(L""); pXml->WriteString(L""); pXml->WriteString(L""); pXml->WriteString(L""); pXml->WriteString(L""); pXml->WriteString(L""); pXml->WriteString(L""); - pXml->WriteString(L""); + pXml->WriteString(L""); + + CloseR(pXml); } - void ImageRels (NSStringUtils::CStringBuilder* oXml, int nImageId, const std::wstring& sImageSrc, const std::wstring& sExtention, unsigned int unWidth = 0, unsigned int unHeight = 0) + void ImageRels (NSStringUtils::CStringBuilder* oXml, int nImageId, const std::wstring& sImageSrc, const std::wstring& sExtention, const TImageData& oImageData = TImageData()) { bool bNew = nImageId < 0; if (bNew) @@ -3815,38 +3915,41 @@ private: m_oDocXmlRels.WriteString(L"\"/>"); } - if (0 != unWidth && 0 != unHeight) - return WriteImage(oXml, unWidth, unHeight, sImageId); + if (!oImageData.ZeroSize()) + return WriteImage(oXml, oImageData, sImageId); + + TImageData oNewImageData{oImageData}; // Получаем размеры картинки - int nHy = oBgraFrame.get_Height(); - int nWx = oBgraFrame.get_Width(); - if (nWx > nHy) + oNewImageData.m_unWidth = oBgraFrame.get_Width(); + oNewImageData.m_unHeight = oBgraFrame.get_Height(); + + if (oNewImageData.m_unWidth > oNewImageData.m_unHeight) { - int nW = nWx * 9525; + int nW = oNewImageData.m_unWidth * 9525; nW = (nW > 7000000 ? 7000000 : nW); - nHy = (int)((double)nHy * (double)nW / (double)nWx); - nWx = nW; + oNewImageData.m_unHeight = (int)((double)oNewImageData.m_unHeight * (double)nW / (double)oNewImageData.m_unWidth); + oNewImageData.m_unWidth = nW; } else { - int nH = nHy * 9525; + int nH = oNewImageData.m_unHeight * 9525; nH = (nH > 8000000 ? 8000000 : nH); - int nW = (int)((double)nWx * (double)nH / (double)nHy); + int nW = (int)((double)oNewImageData.m_unWidth * (double)nH / (double)oNewImageData.m_unHeight); if (nW > 7000000) { nW = 7000000; - nHy = (int)((double)nHy * (double)nW / (double)nWx); + oNewImageData.m_unHeight = (int)((double)oNewImageData.m_unHeight * (double)nW / (double)oNewImageData.m_unWidth); } else - nHy = nH; - nWx = nW; + oNewImageData.m_unHeight = nH; + oNewImageData.m_unWidth = nW; } - WriteImage(oXml, nWx, nHy, sImageId); + WriteImage(oXml, oNewImageData, sImageId); } - void readNote (NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, const std::wstring& sNote) + void readNote (NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, std::wstring& sNote) { if(sNote.empty()) return; @@ -3861,6 +3964,8 @@ private: m_oNoteXml.WriteString(L"\">"); m_oNoteXml.WriteEncodeXmlString(sNote); m_oNoteXml.WriteString(L""); + + sNote.clear(); } bool readSVG (const std::wstring& wsSvg) From a68bec86dfbb972bf935abbbb9bd4531f5d30a50 Mon Sep 17 00:00:00 2001 From: Kirill Polyakov Date: Thu, 8 Aug 2024 16:05:19 +0300 Subject: [PATCH 16/18] Improved html to ooxml conversion --- .../3dParty/html/css/src/CCompiledStyle.cpp | 6 + .../3dParty/html/css/src/CCssCalculator.cpp | 5 + Common/3dParty/html/css/src/CCssCalculator.h | 1 + .../html/css/src/CCssCalculator_Private.cpp | 7 +- .../html/css/src/CCssCalculator_Private.h | 1 + .../3dParty/html/css/src/StyleProperties.cpp | 145 +++++++---- Common/3dParty/html/css/src/StyleProperties.h | 10 + .../html/css/src/xhtml/CDocumentStyle.cpp | 14 +- .../html/css/src/xhtml/CXmlElement.cpp | 4 +- HtmlFile2/htmlfile2.cpp | 245 ++++++++++-------- 10 files changed, 273 insertions(+), 165 deletions(-) diff --git a/Common/3dParty/html/css/src/CCompiledStyle.cpp b/Common/3dParty/html/css/src/CCompiledStyle.cpp index 76617ed6a1..ee46db33ee 100644 --- a/Common/3dParty/html/css/src/CCompiledStyle.cpp +++ b/Common/3dParty/html/css/src/CCompiledStyle.cpp @@ -34,6 +34,9 @@ namespace NSCSS CCompiledStyle& CCompiledStyle::operator+= (const CCompiledStyle &oElement) { + if (oElement.Empty()) + return *this; + m_oBackground += oElement.m_oBackground; m_oBorder += oElement.m_oBorder; m_oFont += oElement.m_oFont; @@ -42,6 +45,9 @@ namespace NSCSS m_oText += oElement.m_oText; m_oDisplay += oElement.m_oDisplay; + if (!oElement.m_sId.empty()) + m_sId += L'+' + oElement.m_sId; + return *this; } diff --git a/Common/3dParty/html/css/src/CCssCalculator.cpp b/Common/3dParty/html/css/src/CCssCalculator.cpp index 3ece3b199f..bedad66c89 100644 --- a/Common/3dParty/html/css/src/CCssCalculator.cpp +++ b/Common/3dParty/html/css/src/CCssCalculator.cpp @@ -23,6 +23,11 @@ namespace NSCSS return m_pInternal->GetCompiledStyle(oStyle, arSelectors, bIsSettings, unitMeasure); } + std::wstring CCssCalculator::CalculateStyleId(const CNode& oNode) + { + return m_pInternal->CalculateStyleId(oNode); + } + bool CCssCalculator::CalculatePageStyle(NSProperties::CPage& oPageData, const std::vector &arSelectors) { return m_pInternal->CalculatePageStyle(oPageData, arSelectors); diff --git a/Common/3dParty/html/css/src/CCssCalculator.h b/Common/3dParty/html/css/src/CCssCalculator.h index 02bd6ed957..564adaa686 100644 --- a/Common/3dParty/html/css/src/CCssCalculator.h +++ b/Common/3dParty/html/css/src/CCssCalculator.h @@ -22,6 +22,7 @@ namespace NSCSS CCompiledStyle GetCompiledStyle(const std::vector &arSelectors, const bool& bIsSettings = false, const UnitMeasure& unitMeasure = Point) const; bool GetCompiledStyle(CCompiledStyle& oStyle, const std::vector &arSelectors, const bool& bIsSettings = false, const UnitMeasure& unitMeasure = Point) const; + std::wstring CalculateStyleId(const CNode& oNode); bool CalculatePageStyle(NSProperties::CPage& oPageData, const std::vector &arSelectors); // void AddStyle(const std::vector& sSelectors, const std::string& sStyle); diff --git a/Common/3dParty/html/css/src/CCssCalculator_Private.cpp b/Common/3dParty/html/css/src/CCssCalculator_Private.cpp index 60bdc3560d..25d13673ee 100644 --- a/Common/3dParty/html/css/src/CCssCalculator_Private.cpp +++ b/Common/3dParty/html/css/src/CCssCalculator_Private.cpp @@ -567,7 +567,7 @@ namespace NSCSS } } - oStyle.SetID(arSelectors.back().m_wsName + ((!arSelectors.back().m_wsClass.empty()) ? L'.' + arSelectors.back().m_wsClass : L"") + ((arSelectors.back().m_wsId.empty()) ? L"" : L'#' + arSelectors.back().m_wsId) + L'-' + std::to_wstring(++m_nCountNodes)); + oStyle.SetID(CalculateStyleId(arSelectors.back())); if (!bIsSettings && !oStyle.Empty()) m_mUsedStyles[arSelectors] = oStyle; @@ -575,6 +575,11 @@ namespace NSCSS return true; } + std::wstring CCssCalculator_Private::CalculateStyleId(const CNode& oNode) + { + return oNode.m_wsName + ((!oNode.m_wsClass.empty()) ? L'.' + oNode.m_wsClass : L"") + ((oNode.m_wsId.empty()) ? L"" : L'#' + oNode.m_wsId) + L'-' + std::to_wstring(++m_nCountNodes); + } + bool CCssCalculator_Private::CalculatePageStyle(NSProperties::CPage &oPageData, const std::vector &arSelectors) { if (arSelectors.empty()) diff --git a/Common/3dParty/html/css/src/CCssCalculator_Private.h b/Common/3dParty/html/css/src/CCssCalculator_Private.h index 7bda5a7937..510bf0030d 100644 --- a/Common/3dParty/html/css/src/CCssCalculator_Private.h +++ b/Common/3dParty/html/css/src/CCssCalculator_Private.h @@ -75,6 +75,7 @@ namespace NSCSS CCompiledStyle GetCompiledStyle(const std::vector &arSelectors, const bool& bIsSettings = false, const UnitMeasure& unitMeasure = Point); bool GetCompiledStyle(CCompiledStyle& oStyle, const std::vector &arSelectors, const bool& bIsSettings = false, const UnitMeasure& unitMeasure = Point); + std::wstring CalculateStyleId(const CNode& oNode); bool CalculatePageStyle(NSProperties::CPage& oPageData, const std::vector &arSelectors); #endif diff --git a/Common/3dParty/html/css/src/StyleProperties.cpp b/Common/3dParty/html/css/src/StyleProperties.cpp index 6467cae879..4874c22a7a 100644 --- a/Common/3dParty/html/css/src/StyleProperties.cpp +++ b/Common/3dParty/html/css/src/StyleProperties.cpp @@ -39,13 +39,17 @@ namespace NSCSS std::wstring wsNewValue = wsValue; - bool bImportant = CutImportant(wsNewValue); + const bool bImportant{CutImportant(wsNewValue)}; if (m_bImportant && !bImportant) return false; + if (UINT_MAX == unLevel) + m_unLevel++; + else + m_unLevel = unLevel; + m_oValue = wsNewValue; - m_unLevel = unLevel; m_bImportant = bImportant; return true; @@ -58,15 +62,19 @@ namespace NSCSS std::wstring wsNewValue = wsValue; - bool bImportant = CutImportant(wsNewValue); + const bool bImportant{CutImportant(wsNewValue)}; if (m_bImportant && !bImportant) return false; if (arValiableValues.end() != std::find(arValiableValues.begin(), arValiableValues.end(), wsNewValue)) { + if (UINT_MAX == unLevel) + m_unLevel++; + else + m_unLevel = unLevel; + m_oValue = wsNewValue; - m_unLevel = unLevel; m_bImportant = bImportant; return true; @@ -82,7 +90,7 @@ namespace NSCSS std::wstring wsNewValue = wsValue; - bool bImportant = CutImportant(wsNewValue); + const bool bImportant{CutImportant(wsNewValue)}; if (m_bImportant && !bImportant) return false; @@ -91,8 +99,12 @@ namespace NSCSS if (arValiableValues.end() != oFoundValue) { + if (UINT_MAX == unLevel) + m_unLevel++; + else + m_unLevel = unLevel; + m_oValue = oFoundValue->second; - m_unLevel = unLevel; m_bImportant = bImportant; return true; @@ -109,7 +121,7 @@ namespace NSCSS void CString::Clear() { m_oValue.clear(); - m_unLevel = NULL; + m_unLevel = 0; m_bImportant = false; } @@ -183,7 +195,7 @@ namespace NSCSS void CDigit::Clear() { m_oValue = DBL_MAX; - m_unLevel = NULL; + m_unLevel = 0; m_enUnitMeasure = None; m_bImportant = false; } @@ -380,15 +392,19 @@ namespace NSCSS std::wstring wsNewValue = wsValue; - bool bImportant = CutImportant(wsNewValue); //TODO:: иногда мы знаем, что "!important" точно не встретится - // возможно стоит добавить ещё метод + const bool bImportant{CutImportant(wsNewValue)}; //TODO:: иногда мы знаем, что "!important" точно не встретится + // возможно стоит добавить ещё метод if (m_bImportant && !bImportant) return false; if (!CUnitMeasureConverter::GetValue(wsValue, m_oValue, m_enUnitMeasure)) return false; - m_unLevel = unLevel; + if (UINT_MAX == unLevel) + m_unLevel++; + else + m_unLevel = unLevel; + m_bImportant = bImportant; return true; @@ -426,7 +442,11 @@ namespace NSCSS return false; m_oValue = dValue; - m_unLevel = unLevel; + + if (UINT_MAX == unLevel) + m_unLevel++; + else + m_unLevel = unLevel; return true; } @@ -652,33 +672,25 @@ namespace NSCSS std::wstring wsNewValue(wsValue); - bool bImportant = CutImportant(wsNewValue); + const bool bImportant = CutImportant(wsNewValue); std::transform(wsNewValue.begin(), wsNewValue.end(), wsNewValue.begin(), std::towlower); + NS_STATIC_FUNCTIONS::RemoveSpaces(wsNewValue); if (m_bImportant && !bImportant) return false; + bool bResult{false}; + if (wsNewValue[0] == L'#') { SetHEX(wsNewValue.substr(1, wsNewValue.length() - 1)); - m_unLevel = unLevel; - m_bImportant = bImportant; - return true; + bResult = true; } - else if (L"none" == wsNewValue) + else if (L"none" == wsNewValue || wsNewValue == L"transparent") { SetNone(); - m_unLevel = unLevel; - m_bImportant = bImportant; - return true; - } - else if (wsNewValue == L"transparent") - { - SetNone(); - m_unLevel = unLevel; - m_bImportant = bImportant; - return true; + bResult = true; } else if (10 <= wsNewValue.length() && wsNewValue.substr(0, 3) == L"rgb") { @@ -711,9 +723,7 @@ namespace NSCSS if (wsNewValue.substr(0, 4) == L"rgba" && 4 == arValues.size()) m_oOpacity.SetValue(arValues[3], unLevel, bHardMode); - m_unLevel = unLevel; - m_bImportant = bImportant; - return true; + bResult = true; } if (5 <= wsNewValue.length()) @@ -721,23 +731,30 @@ namespace NSCSS SetUrl(wsValue); if (m_enType == ColorUrl) + bResult = true; + } + + if (!bResult) + { + const std::map::const_iterator oHEX = NSConstValues::COLORS.find(wsNewValue); + if (oHEX != NSConstValues::COLORS.end()) { - m_unLevel = unLevel; - m_bImportant = bImportant;; - return true; + SetHEX(oHEX->second); + bResult = true; } } - const std::map::const_iterator oHEX = NSConstValues::COLORS.find(wsNewValue); - if (oHEX != NSConstValues::COLORS.end()) - { - SetHEX(oHEX->second); - m_unLevel = unLevel; - m_bImportant = bImportant; - return true; - } + if (!bResult) + return false; - return false; + m_bImportant = bImportant; + + if (UINT_MAX == unLevel) + m_unLevel++; + else + m_unLevel = unLevel; + + return true; } bool CColor::SetOpacity(const std::wstring &wsValue, unsigned int unLevel, bool bHardMode) @@ -2073,6 +2090,7 @@ namespace NSCSS CString::Equation(oFirstText.m_oAlign, oSecondText.m_oAlign); // CString::Equation(oFirstText.m_oDecoration, oSecondText.m_oDecoration); CColor ::Equation(oFirstText.m_oColor, oSecondText.m_oColor); + CColor ::Equation(oFirstText.m_oHighlight, oSecondText.m_oHighlight); } bool CText::SetIndent(const std::wstring &wsValue, unsigned int unLevel, bool bHardMode) @@ -2108,6 +2126,11 @@ namespace NSCSS return m_oColor.SetValue(wsValue, unLevel, bHardMode); } + bool CText::SetHighlight(const std::wstring& wsValue, unsigned int unLevel, bool bHardMode) + { + return m_oHighlight.SetValue(wsValue, unLevel, bHardMode); + } + const CDigit& CText::GetIndent() const { return m_oIndent; @@ -2128,6 +2151,11 @@ namespace NSCSS return m_oColor; } + const CColor& CText::GetHighlight() const + { + return m_oHighlight; + } + bool CText::Empty() const { return m_oIndent.Empty() && m_oAlign.Empty() && @@ -2155,6 +2183,7 @@ namespace NSCSS m_oAlign += oText.m_oAlign; m_oDecoration += oText.m_oDecoration; m_oColor += oText.m_oColor; + m_oHighlight += oText.m_oHighlight; return *this; } @@ -2164,7 +2193,8 @@ namespace NSCSS return m_oIndent == oText.m_oIndent && m_oAlign == oText.m_oAlign && m_oDecoration == oText.m_oDecoration && - m_oColor == oText.m_oColor; + m_oColor == oText.m_oColor && + m_oHighlight == oText.m_oHighlight; } // MARGIN @@ -2227,21 +2257,41 @@ namespace NSCSS return m_oTop.SetValue(wsValue, unLevel, bHardMode); } + bool CIndent::SetTop(const double& dValue, unsigned int unLevel, bool bHardMode) + { + return m_oTop.SetValue(dValue, unLevel, bHardMode); + } + bool CIndent::SetRight(const std::wstring &wsValue, unsigned int unLevel, bool bHardMode) { return m_oRight.SetValue(wsValue, unLevel, bHardMode); } + bool CIndent::SetRight(const double& dValue, unsigned int unLevel, bool bHardMode) + { + return m_oRight.SetValue(dValue, unLevel, bHardMode); + } + bool CIndent::SetBottom(const std::wstring &wsValue, unsigned int unLevel, bool bHardMode) { return m_oBottom.SetValue(wsValue, unLevel, bHardMode); } + bool CIndent::SetBottom(const double& dValue, unsigned int unLevel, bool bHardMode) + { + return m_oBottom.SetValue(dValue, unLevel, bHardMode); + } + bool CIndent::SetLeft(const std::wstring &wsValue, unsigned int unLevel, bool bHardMode) { return m_oLeft.SetValue(wsValue, unLevel, bHardMode); } + bool CIndent::SetLeft(const double& dValue, unsigned int unLevel, bool bHardMode) + { + return m_oLeft.SetValue(dValue, unLevel, bHardMode); + } + void CIndent::UpdateAll(double dFontSize) { UpdateTop (dFontSize); @@ -2529,6 +2579,11 @@ namespace NSCSS return m_oSize.SetValue(wsNewValue, unLevel, bHardMode); } + bool CFont:: SetSize(const double& dValue, unsigned int unLevel, bool bHardMode) + { + return m_oSize.SetValue(dValue, unLevel, bHardMode); + } + bool CFont::SetLineHeight(const std::wstring &wsValue, unsigned int unLevel, bool bHardMode) { return m_oLineHeight.SetValue(wsValue, unLevel, bHardMode); @@ -2536,13 +2591,13 @@ namespace NSCSS bool CFont::SetFamily(const std::wstring &wsValue, unsigned int unLevel, bool bHardMode) { - std::wstring wsNewFamily(wsValue); + // std::wstring wsNewFamily(wsValue); // if (wsNewFamily.end() == wsNewFamily.erase(std::remove(wsNewFamily.begin(), wsNewFamily.end(), L'\''), wsNewFamily.end()) && // wsNewFamily.end() == wsNewFamily.erase(std::remove(wsNewFamily.begin(), wsNewFamily.end(), L'"'), wsNewFamily.end())) // return false; - std::vector arWords = NS_STATIC_FUNCTIONS::GetWordsW(wsNewFamily, false, L"\"\',"); + std::vector arWords = NS_STATIC_FUNCTIONS::GetWordsW(wsValue, false, L"\"\',"); for (std::vector::iterator iWord = arWords.begin(); iWord != arWords.end(); ++iWord) { diff --git a/Common/3dParty/html/css/src/StyleProperties.h b/Common/3dParty/html/css/src/StyleProperties.h index fe2f1c90d0..b8a9e20d0a 100644 --- a/Common/3dParty/html/css/src/StyleProperties.h +++ b/Common/3dParty/html/css/src/StyleProperties.h @@ -13,6 +13,8 @@ namespace NSCSS { namespace NSProperties { + #define NEXT_LEVEL UINT_MAX, true + template class CValue { @@ -605,11 +607,13 @@ namespace NSCSS bool SetAlign (const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); bool SetDecoration(const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); bool SetColor (const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); + bool SetHighlight (const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); const CDigit& GetIndent() const; const CString& GetAlign() const; const TTextDecoration& GetDecoration() const; const CColor& GetColor() const; + const CColor& GetHighlight() const; bool Empty() const; @@ -624,6 +628,7 @@ namespace NSCSS CDigit m_oIndent; CString m_oAlign; CColor m_oColor; + CColor m_oHighlight; }; class CIndent @@ -641,9 +646,13 @@ namespace NSCSS bool SetValues (const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); bool SetTop (const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); + bool SetTop (const double& dValue, unsigned int unLevel, bool bHardMode = false); bool SetRight (const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); + bool SetRight (const double& dValue, unsigned int unLevel, bool bHardMode = false); bool SetBottom (const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); + bool SetBottom (const double& dValue, unsigned int unLevel, bool bHardMode = false); bool SetLeft (const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); + bool SetLeft (const double& dValue, unsigned int unLevel, bool bHardMode = false); void UpdateAll (double dFontSize); void UpdateTop (double dFontSize); @@ -683,6 +692,7 @@ namespace NSCSS bool SetValue (const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); bool SetSize (const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); + bool SetSize (const double& dValue, unsigned int unLevel, bool bHardMode = false); bool SetLineHeight (const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); bool SetFamily (const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); bool SetStretch (const std::wstring& wsValue, unsigned int unLevel, bool bHardMode = false); diff --git a/Common/3dParty/html/css/src/xhtml/CDocumentStyle.cpp b/Common/3dParty/html/css/src/xhtml/CDocumentStyle.cpp index 19bd8087b7..f47aea8d1a 100644 --- a/Common/3dParty/html/css/src/xhtml/CDocumentStyle.cpp +++ b/Common/3dParty/html/css/src/xhtml/CDocumentStyle.cpp @@ -491,17 +491,15 @@ namespace NSCSS if (!oStyle.m_oBackground.GetColor().Empty() && !oStyle.m_oBackground.GetColor().None() && !oStyle.m_oBackground.GetColor().Url()) oXmlElement.AddPropertiesInR(RProperties::R_Shd, oStyle.m_oBackground.GetColor().ToWString()); - /* - const std::wstring wsHighlight{oStyle.m_oBackground.GetColor().EquateToColor({{{0, 0, 0}, L"black"}, {{0, 0, 255}, L"blue"}, {{0, 255, 255}, L"cyan"}, - {{0, 255, 0}, L"green"}, {{255, 0, 255}, L"magenta"}, {{255, 0, 0}, L"red"}, - {{255, 255, 0}, L"yellow"}, {{255, 255, 255}, L"white"}, {{0, 0, 139}, L"darkBlue"}, - {{0, 139, 139}, L"darkCyan"}, {{0, 100, 0}, L"darkGreen"}, {{139, 0, 139}, L"darkMagenta"}, - {{139, 0, 0}, L"darkRed"}, {{128, 128, 0}, L"darkYellow"},{{169, 169, 169}, L"darkGray"}, - {{211, 211, 211}, L"lightGray"}})}; + const std::wstring wsHighlight{oStyle.m_oText.GetHighlight().EquateToColor({{{0, 0, 0}, L"black"}, {{0, 0, 255}, L"blue"}, {{0, 255, 255}, L"cyan"}, + {{0, 255, 0}, L"green"}, {{255, 0, 255}, L"magenta"}, {{255, 0, 0}, L"red"}, + {{255, 255, 0}, L"yellow"}, {{255, 255, 255}, L"white"}, {{0, 0, 139}, L"darkBlue"}, + {{0, 139, 139}, L"darkCyan"}, {{0, 100, 0}, L"darkGreen"}, {{139, 0, 139}, L"darkMagenta"}, + {{139, 0, 0}, L"darkRed"}, {{128, 128, 0}, L"darkYellow"},{{169, 169, 169}, L"darkGray"}, + {{211, 211, 211}, L"lightGray"}})}; if (L"none" != wsHighlight) oXmlElement.AddPropertiesInR(RProperties::R_Highlight, wsHighlight); - */ oXmlElement.AddPropertiesInR(RProperties::R_Color, oStyle.m_oText.GetColor().ToWString()); diff --git a/Common/3dParty/html/css/src/xhtml/CXmlElement.cpp b/Common/3dParty/html/css/src/xhtml/CXmlElement.cpp index 5f9db69058..c617695a44 100644 --- a/Common/3dParty/html/css/src/xhtml/CXmlElement.cpp +++ b/Common/3dParty/html/css/src/xhtml/CXmlElement.cpp @@ -35,7 +35,7 @@ void CXmlElement::CreateDefaultElement(const std::wstring& sNameDefaultElement) if (!Empty()) Clear(); - if (sNameDefaultElement == L"p") +/* if (sNameDefaultElement == L"p") { AddBasicProperties(CSSProperties::BasicProperties::B_Type, L"paragraph"); AddBasicProperties(CSSProperties::BasicProperties::B_StyleId, L"p"); @@ -47,7 +47,7 @@ void CXmlElement::CreateDefaultElement(const std::wstring& sNameDefaultElement) // AddPropertiesInP(CSSProperties::ParagraphProperties::P_Spacing, L"w:before=\"100\" w:beforeAutospacing=\"1\" w:after=\"100\" w:afterAutospacing=\"1\""); } - else if (sNameDefaultElement == L"li") + else */if (sNameDefaultElement == L"li") { AddBasicProperties(CSSProperties::BasicProperties::B_Type, L"paragraph"); AddBasicProperties(CSSProperties::BasicProperties::B_StyleId, L"li"); diff --git a/HtmlFile2/htmlfile2.cpp b/HtmlFile2/htmlfile2.cpp index 7f99d9e171..3d93214e25 100644 --- a/HtmlFile2/htmlfile2.cpp +++ b/HtmlFile2/htmlfile2.cpp @@ -55,6 +55,8 @@ for (object_type* pElement : vector_object) \ RELEASEOBJECT(pElement) \ +const static double HTML_FONTS[7] = {7.5, 10, 12, 13.5, 18, 24, 36}; + // Ячейка таблицы struct CTc { @@ -81,8 +83,8 @@ struct CTextSettings bool bAddSpaces; // Добавлять пробелы перед текстом? bool bMergeText; // Объединять подяр идущий текст в 1? int nLi; // Уровень списка - std::wstring sRStyle; // w:rStyle - std::wstring sPStyle; // w:pStyle + + std::wstring sPStyle; enum ETextMode { @@ -91,27 +93,16 @@ struct CTextSettings Subscript } eTextMode; - NSCSS::CCompiledStyle oPriorityStyle; + NSCSS::CCompiledStyle oAdditionalStyle; CTextSettings() : bBdo(false), bPre(false), bQ(false), bAddSpaces(true), bMergeText(false), nLi(-1), eTextMode(Normal) {} CTextSettings(const CTextSettings& oTS) : - bBdo(oTS.bBdo), bPre(oTS.bPre), bQ(oTS.bQ), bAddSpaces(oTS.bAddSpaces), bMergeText(oTS.bMergeText), nLi(oTS.nLi), sRStyle(oTS.sRStyle), sPStyle(oTS.sPStyle) + bBdo(oTS.bBdo), bPre(oTS.bPre), bQ(oTS.bQ), bAddSpaces(oTS.bAddSpaces), bMergeText(oTS.bMergeText), nLi(oTS.nLi), sPStyle(oTS.sPStyle) {} - bool HaveRStyles() const - { - return !sRStyle.empty() || Normal != eTextMode; - } - - void AddRStyle(const std::wstring& wsStyle) - { - if (std::wstring::npos == sRStyle.find(wsStyle)) - sRStyle += wsStyle; - } - void AddPStyle(const std::wstring& wsStyle) { if (std::wstring::npos == sPStyle.find(wsStyle)) @@ -1531,7 +1522,7 @@ public: // Сноски m_oStylesXml += L""; // Web стиль по-умолчанию - m_oStylesXml += L""; + m_oStylesXml += L""; } // Читает файл @@ -2037,25 +2028,36 @@ private: if (m_mDivs.empty()) pXml->WriteString(L""); - m_oStylesCalculator.GetCompiledStyle(oTS.oPriorityStyle, sSelectors); + NSCSS::CCompiledStyle oStyle; + m_oStylesCalculator.GetCompiledStyle(oStyle, sSelectors); - INT nMarLeft = 720; - INT nMarRight = 720; + INT nMarLeft = 720; + INT nMarRight = 720; + INT nMarTop = 100; + INT nMarBottom = 100; - if (!oTS.oPriorityStyle.m_oMargin.GetLeft().Empty() && !oTS.oPriorityStyle.m_oMargin.GetLeft().Zero()) - nMarLeft = oTS.oPriorityStyle.m_oMargin.GetLeft().ToInt(NSCSS::Twips, m_oPageData.GetWidth().ToInt(NSCSS::Twips)); + if (!oStyle.m_oMargin.GetLeft().Empty() && !oStyle.m_oMargin.GetLeft().Zero()) + nMarLeft = oStyle.m_oMargin.GetLeft().ToInt(NSCSS::Twips, m_oPageData.GetWidth().ToInt(NSCSS::Twips)); - if (!oTS.oPriorityStyle.m_oMargin.GetRight().Empty() && !oTS.oPriorityStyle.m_oMargin.GetRight().Zero()) - nMarRight = oTS.oPriorityStyle.m_oMargin.GetRight().ToInt(NSCSS::Twips, m_oPageData.GetWidth().ToInt(NSCSS::Twips)); + if (!oStyle.m_oMargin.GetRight().Empty() && !oStyle.m_oMargin.GetRight().Zero()) + nMarRight = oStyle.m_oMargin.GetRight().ToInt(NSCSS::Twips, m_oPageData.GetWidth().ToInt(NSCSS::Twips)); + + if (!oStyle.m_oMargin.GetTop().Empty() && !oStyle.m_oMargin.GetTop().Zero()) + nMarTop = oStyle.m_oMargin.GetTop().ToInt(NSCSS::Twips, m_oPageData.GetHeight().ToInt(NSCSS::Twips)); + + if (!oStyle.m_oMargin.GetBottom().Empty() && !oStyle.m_oMargin.GetBottom().Zero()) + nMarBottom = oStyle.m_oMargin.GetBottom().ToInt(NSCSS::Twips, m_oPageData.GetHeight().ToInt(NSCSS::Twips)); if (L"blockquote" == wsKeyWord) { + sSelectors.back().m_mAttributes.insert({L"margin", L"0px"}); + pXml->WriteString(L""); pXml->WriteString(L""); pXml->WriteString(L""); pXml->WriteString(L""); - pXml->WriteString(L""); - pXml->WriteString(L""); + pXml->WriteString(L""); + pXml->WriteString(L""); pXml->WriteString(L""); pXml->WriteString(L""); pXml->WriteString(L""); @@ -2145,10 +2147,11 @@ private: m_oDocXml.WriteString(L"\"/>"); */ - readStream(&m_oDocXml, sSelectors, {}); + CTextSettings oTS; + readStream(&m_oDocXml, sSelectors, oTS); } - bool readInside (NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, const CTextSettings& oTS, const std::wstring& sName) + bool readInside (NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, CTextSettings& oTS, const std::wstring& sName) { //TODO:: обработать все варианты return'а @@ -2159,9 +2162,12 @@ private: if (sText.end() == std::find_if_not(sText.begin(), sText.end(), [](wchar_t wchChar){ return iswspace(wchChar);})) return false; + if(oTS.bBdo) + std::reverse(sText.begin(), sText.end()); + const bool bInT = m_oState.m_bInT; - if (!oTS.sRStyle.empty() || oTS.bPre) + if (oTS.bPre) { CloseT(oXml); CloseR(oXml); @@ -2170,12 +2176,22 @@ private: if (oTS.bAddSpaces && m_oState.m_bInP && !m_oState.m_bInR && !iswspace(sText.front()) && !m_oState.m_bWasSpace && CTextSettings::Normal == oTS.eTextMode) WriteSpace(oXml); - std::wstring sPStyle = wrP(oXml, sSelectors, oTS); + OpenP(oXml); + + NSStringUtils::CStringBuilder oPPr; + + std::wstring sPStyle = wrP(&oPPr, sSelectors, oTS); + + oXml->WriteString(oPPr.GetData()); + + NSStringUtils::CStringBuilder oRPr; std::wstring sRStyle; if (OpenR(oXml)) { - sRStyle = wrRPr(oXml, sSelectors, oTS); + sRStyle = wrRPr(&oRPr, sSelectors, oTS); + + oXml->WriteString(oRPr.GetData()); if (oTS.bQ) oXml->WriteString(L"""); @@ -2185,9 +2201,6 @@ private: else if (oTS.bQ) oXml->WriteString(L"""); - if(oTS.bBdo) - std::reverse(sText.begin(), sText.end()); - if(oTS.bPre) { size_t nAfter = sText.find_first_of(L"\n\r"); @@ -2195,29 +2208,9 @@ private: { oXml->WriteEncodeXmlString(sText.c_str(), nAfter); oXml->WriteString(L""); - if(!sPStyle.empty() || !oTS.sPStyle.empty()) - { - oXml->WriteNodeBegin(L"w:pPr"); - - if (!sPStyle.empty()) - oXml->WriteString(L""); - - oXml->WriteString(oTS.sPStyle); - - oXml->WriteNodeEnd(L"w:pPr"); - } + oXml->WriteString(oPPr.GetData()); oXml->WriteNodeBegin(L"w:r"); - if (!sRStyle.empty() || !oTS.sRStyle.empty()) - { - oXml->WriteNodeBegin(L"w:rPr"); - - if (!sRStyle.empty()) - oXml->WriteString(L""); - - oXml->WriteString(oTS.sRStyle); - - oXml->WriteNodeEnd(L"w:rPr"); - } + oXml->WriteString(oRPr.GetData()); oXml->WriteString(L""); sText.erase(0, nAfter + 1); nAfter = sText.find_first_of(L"\n\r"); @@ -2279,7 +2272,7 @@ private: else if(sName == L"b" || sName == L"strong") { CTextSettings oTSR(oTS); - oTSR.AddRStyle(L""); + oTSR.oAdditionalStyle.m_oFont.SetWeight(L"bold", UINT_MAX, true); bResult = readStream(oXml, sSelectors, oTSR); } // Направление текста @@ -2318,7 +2311,7 @@ private: else if(sName == L"center") { CTextSettings oTSP(oTS); - oTSP.AddPStyle(L""); + oTSP.oAdditionalStyle.m_oText.SetAlign(L"center", UINT_MAX, true); bResult = readStream(oXml, sSelectors, oTSP); } // Цитата, обычно выделяется курсивом @@ -2329,7 +2322,7 @@ private: else if(sName == L"cite" || sName == L"dfn" || sName == L"em" || sName == L"i" || sName == L"var") { CTextSettings oTSR(oTS); - oTSR.AddRStyle(L""); + oTSR.oAdditionalStyle.m_oFont.SetStyle(L"italic", UINT_MAX, true); bResult = readStream(oXml, sSelectors, oTSR); } // Код @@ -2338,37 +2331,42 @@ private: else if(sName == L"code" || sName == L"tt") { CTextSettings oTSR(oTS); - oTSR.AddRStyle(L""); + oTSR.oAdditionalStyle.m_oFont.SetFamily(L"Courier New", UINT_MAX, true); + oTSR.oAdditionalStyle.m_oFont.SetSize(20, UINT_MAX, true); bResult = readStream(oXml, sSelectors, oTSR); } else if (sName == L"kbd") { CTextSettings oTSR(oTS); - oTSR.AddRStyle(L""); + oTSR.oAdditionalStyle.m_oFont.SetFamily(L"Courier New", UINT_MAX, true); + oTSR.oAdditionalStyle.m_oFont.SetSize(20, UINT_MAX, true); + oTSR.oAdditionalStyle.m_oFont.SetWeight(L"bold", UINT_MAX, true); bResult = readStream(oXml, sSelectors, oTSR); } else if (sName == L"samp") { CTextSettings oTSR(oTS); - oTSR.AddRStyle(L""); + oTSR.oAdditionalStyle.m_oFont.SetFamily(L"Courier New", UINT_MAX, true); bResult = readStream(oXml, sSelectors, oTSR); } // Зачеркнутый текст else if(sName == L"del" || sName == L"s" || sName == L"strike") { CTextSettings oTSR(oTS); - oTSR.AddRStyle(L""); + oTSR.oAdditionalStyle.m_oText.SetDecoration(L"line-through", UINT_MAX, true); bResult = readStream(oXml, sSelectors, oTSR); } else if(sName == L"font") { + CTextSettings oTSR(oTS); + while(m_oLightReader.MoveToNextAttribute()) { std::wstring sAName = m_oLightReader.GetName(); if(sAName == L"color") - sSelectors.back().m_wsStyle += L"; color: " + m_oLightReader.GetText(); + oTSR.oAdditionalStyle.m_oText.SetColor(m_oLightReader.GetText(), UINT_MAX, true); else if(sAName == L"face") - sSelectors.back().m_wsStyle += L"; font-family: " + m_oLightReader.GetText(); + oTSR.oAdditionalStyle.m_oFont.SetFamily(m_oLightReader.GetText(), UINT_MAX, true); else if(sAName == L"size") { int nSize = 3; @@ -2383,23 +2381,15 @@ private: nSize = NSStringFinder::ToInt(sSize); } - switch (nSize) - { - case 1: nSize = 10; break; - case 2: nSize = 12; break; - case 3: - default: nSize = 14; break; - case 4: nSize = 18; break; - case 5: nSize = 24; break; - case 6: nSize = 32; break; - case 7: nSize = 48; break; - } - - sSelectors.back().m_wsStyle += L"; font-size: " + std::to_wstring(nSize) + L"px"; + if (nSize < 1 || nSize > 7) + nSize = 3; + + oTSR.oAdditionalStyle.m_oFont.SetSize(HTML_FONTS[nSize - 1], UINT_MAX, true); } } m_oLightReader.MoveToElement(); - bResult = readStream(oXml, sSelectors, oTS); + bResult = readStream(oXml, sSelectors, oTSR); + m_oState.m_bWasSpace = true; } // Картинки else if(sName == L"img") @@ -2408,14 +2398,14 @@ private: else if(sName == L"ins" || sName == L"u") { CTextSettings oTSR(oTS); - oTSR.AddRStyle(L""); + oTSR.oAdditionalStyle.m_oText.SetDecoration(L"underline", UINT_MAX, true); bResult = readStream(oXml, sSelectors, oTSR); } // Выделенный текст, обычно выделяется желтым else if(sName == L"mark") { CTextSettings oTSR(oTS); - oTSR.AddRStyle(L""); + oTSR.oAdditionalStyle.m_oText.SetHighlight(L"yellow", UINT_MAX, true); bResult = readStream(oXml, sSelectors, oTSR); } // Цитата, выделенная кавычками, обычно выделяется курсивом @@ -2473,9 +2463,43 @@ private: oTSPre.bPre = true; bResult = readStream(oXml, sSelectors, oTSPre); } + else if (sName == L"basefont") + { + if (!m_oLightReader.MoveToFirstAttribute()) + return false; + + do + { + if (L"face" == m_oLightReader.GetName()) + oTS.oAdditionalStyle.m_oFont.SetFamily(m_oLightReader.GetText(), UINT_MAX, true); + else if (L"size" == m_oLightReader.GetName()) + { + switch(NSStringFinder::ToInt(m_oLightReader.GetText(), 3)) + { + case 1: oTS.oAdditionalStyle.m_oFont.SetSize(7.5, UINT_MAX, true); break; + case 2: oTS.oAdditionalStyle.m_oFont.SetSize(10, UINT_MAX, true); break; + default: + case 3: oTS.oAdditionalStyle.m_oFont.SetSize(12, UINT_MAX, true); break; + case 4: oTS.oAdditionalStyle.m_oFont.SetSize(13.5, UINT_MAX, true); break; + case 5: oTS.oAdditionalStyle.m_oFont.SetSize(18, UINT_MAX, true); break; + case 6: oTS.oAdditionalStyle.m_oFont.SetSize(24, UINT_MAX, true); break; + case 7: oTS.oAdditionalStyle.m_oFont.SetSize(36, UINT_MAX, true); break; + } + } + else if (L"color" == m_oLightReader.GetName()) + oTS.oAdditionalStyle.m_oText.SetColor(m_oLightReader.GetText(), UINT_MAX, true); + } while (m_oLightReader.MoveToNextAttribute()); + + m_oLightReader.MoveToElement(); + + oTS.oAdditionalStyle.SetID(m_oStylesCalculator.CalculateStyleId(sSelectors.back())); + + sSelectors.pop_back(); + return true; + } // Без нового абзаца - 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"time" || sName == L"small" || + else if(sName == L"button" || sName == L"label" || sName == L"data" || sName == L"object" || + sName == L"noscript" || sName == L"output" || sName == L"time" || sName == L"small" || sName == L"progress" || sName == L"hgroup" || sName == L"meter" || sName == L"acronym" || sName == L"big") bResult = readStream(oXml, sSelectors, oTS); // С нового абзаца @@ -2490,14 +2514,14 @@ private: if(sName == L"address") { CTextSettings oTSR(oTS); - oTSR.AddRStyle(L""); + oTSR.oAdditionalStyle.m_oFont.SetStyle(L"italic", UINT_MAX, true); bResult = readStream(&oXmlData, sSelectors, oTSR); } // Определение термина, отступ от левого края else if(sName == L"dd") { CTextSettings oTSP(oTS); - oTSP.AddPStyle(L""); + oTSP.oAdditionalStyle.m_oMargin.SetLeft(720, UINT_MAX, true); bResult = readStream(&oXmlData, sSelectors, oTSP); } // aside возможно использовать для сносок в epub @@ -2619,7 +2643,8 @@ private: else if(sName == L"pre" || sName == L"xmp") { CTextSettings oTSPre(oTS); - oTSPre.AddRStyle(L""); + oTSPre.oAdditionalStyle.m_oFont.SetFamily(L"Courier New", NEXT_LEVEL); + oTSPre.oAdditionalStyle.m_oFont.SetSize(20, NEXT_LEVEL); oTSPre.bPre = true; bResult = readStream(&oXmlData, sSelectors, oTSPre); } @@ -2722,7 +2747,7 @@ private: return bResult; } - bool readStream (NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, const CTextSettings& oTS, bool bInsertEmptyP = false) + bool readStream (NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, CTextSettings& oTS, bool bInsertEmptyP = false) { int nDeath = m_oLightReader.GetDepth(); if(m_oLightReader.IsEmptyNode() || !m_oLightReader.ReadNextSiblingNode2(nDeath)) @@ -2858,7 +2883,7 @@ private: {} }; - void ParseTableRows(CTable& oTable, std::vector& sSelectors, const CTextSettings& oTS, ERowParseMode eMode) + void ParseTableRows(CTable& oTable, std::vector& sSelectors, CTextSettings& oTS, ERowParseMode eMode) { std::vector arRowspanElements; std::vector arRows; @@ -2921,9 +2946,10 @@ private: CTextSettings oTSR(oTS); if (pCell->GetStyles()->m_wsHAlign.empty()) - oTSR.sPStyle += L""; + oTSR.oAdditionalStyle.m_oText.SetAlign(L"center", NEXT_LEVEL); + + oTSR.oAdditionalStyle.m_oFont.SetWeight(L"bold", NEXT_LEVEL); - oTSR.AddRStyle(L""); readStream(pCell->GetData(), sSelectors, oTSR, true); } // Читаем td. Ячейка таблицы @@ -2963,7 +2989,7 @@ private: oTable.AddRows(arRows, eMode); } - bool ParseRuby(NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, const CTextSettings& oTS) + bool ParseRuby(NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, CTextSettings& oTS) { if(m_oLightReader.IsEmptyNode()) return false; @@ -3021,7 +3047,8 @@ private: {{211, 211, 211}, L"lightGray"}})}; if (L"none" != wsHighlight) - oNewSettings.AddRStyle(L""); + oNewSettings.oAdditionalStyle.m_oText.SetHighlight(oStyle.m_oBackground.GetColor().ToWString(), NEXT_LEVEL); + // oNewSettings.AddRStyle(L""); if (L"rt" == sSelectors.back().m_wsName) { @@ -3083,7 +3110,7 @@ private: return true; } - bool ParseTable(NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, const CTextSettings& oTS) + bool ParseTable(NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, CTextSettings& oTS) { if(m_oLightReader.IsEmptyNode()) return false; @@ -3204,7 +3231,7 @@ private: return true; } - void readInput (NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, const CTextSettings& oTS) + void readInput (NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, CTextSettings& oTS) { std::wstring sValue; std::wstring sAlt; @@ -3238,7 +3265,7 @@ private: readStream(oXml, sSelectors, oTS, ElementInTable(sSelectors)); } - void readLi (NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, const CTextSettings& oTS, bool bType) + void readLi (NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, CTextSettings& oTS, bool bType) { if(m_oLightReader.IsEmptyNode()) return; @@ -3304,9 +3331,10 @@ private: nStart = NSStringFinder::ToInt(m_oLightReader.GetText(), 1); } else if (L"disabled" == wsArgumentName) - oTSLiP.AddRStyle(L""); + oTSLiP.oAdditionalStyle.m_oText.SetColor(L"#808080", NEXT_LEVEL); else if (L"selected" == wsArgumentName) - oTSLiP.AddRStyle(L""); + oTSLiP.oAdditionalStyle.m_oText.SetDecoration(L"underline", NEXT_LEVEL); + // oTSLiP.AddRStyle(L""); } m_oLightReader.MoveToElement(); @@ -3370,7 +3398,7 @@ private: } } - void readA (NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, const CTextSettings& oTS, std::wstring& sNote) + void readA (NSStringUtils::CStringBuilder* oXml, std::vector& sSelectors, CTextSettings& oTS, std::wstring& sNote) { std::wstring sRef; std::wstring sAlt; @@ -3715,27 +3743,23 @@ private: if (m_oState.m_bWasPStyle) return L""; - NSCSS::CCompiledStyle oStyleSetting = m_oStylesCalculator.GetCompiledStyle(sSelectors, true); - NSCSS::CCompiledStyle oStyle = m_oStylesCalculator.GetCompiledStyle(sSelectors); + NSCSS::CCompiledStyle oStyleSetting{m_oStylesCalculator.GetCompiledStyle(sSelectors, true)}; + NSCSS::CCompiledStyle oStyle{m_oStylesCalculator.GetCompiledStyle(sSelectors)}; NSCSS::CCompiledStyle::StyleEquation(oStyle, oStyleSetting); - if (!oTS.oPriorityStyle.Empty()) - { - NSCSS::CCompiledStyle oPriorityStyle{oTS.oPriorityStyle}; - NSCSS::CCompiledStyle::StyleEquation(oPriorityStyle, oStyle); - } + oStyleSetting += oTS.oAdditionalStyle; std::wstring sPStyle = GetStyle(oStyle, true); if (sPStyle.empty() && !ElementInTable(sSelectors)) - sPStyle = L"noraml-web"; + sPStyle = L"normal-web"; if (sPStyle.empty() && oTS.sPStyle.empty()) return L""; m_oXmlStyle.WriteLitePStyle(oStyleSetting); - std::wstring sPSettings = m_oXmlStyle.GetStyle(); + const std::wstring sPSettings = m_oXmlStyle.GetStyle(); m_oXmlStyle.Clear(); oXml->WriteNodeBegin(L"w:pPr"); @@ -3747,7 +3771,7 @@ private: oXml->WriteString(L"\"/>"); } - oXml->WriteString(oTS.sPStyle + L' ' + sPSettings); + oXml->WriteString(oTS.sPStyle + sPSettings); oXml->WriteNodeEnd(L"w:pPr"); m_oState.m_bWasPStyle = true; @@ -3760,6 +3784,9 @@ private: return L""; NSCSS::CCompiledStyle oStyleSetting = m_oStylesCalculator.GetCompiledStyle(sSelectors, true); + + oStyleSetting += oTS.oAdditionalStyle; + NSCSS::CCompiledStyle oStyle = m_oStylesCalculator.GetCompiledStyle(sSelectors); NSCSS::CCompiledStyle::StyleEquation(oStyle, oStyleSetting); @@ -3785,7 +3812,7 @@ private: wsFontSize += L""; } - if (!sRStyle.empty() || oTS.HaveRStyles() || !wsFontSize.empty()) + if (!sRStyle.empty() || CTextSettings::Normal != oTS.eTextMode || !wsFontSize.empty() || !sRSettings.empty()) { oXml->WriteString(L""); if (!sRStyle.empty()) @@ -3811,7 +3838,7 @@ private: break; } - oXml->WriteString(oTS.sRStyle + wsFontSize + sRSettings); + oXml->WriteString(sRSettings + wsFontSize); oXml->WriteString(L""); } return sRStyle; From fcf8e28ae8fd35e7a300f25ffe87f9573a26629a Mon Sep 17 00:00:00 2001 From: Kirill Polyakov Date: Fri, 9 Aug 2024 15:27:29 +0300 Subject: [PATCH 17/18] Added tabs to the text in html to ooxml conversion --- .../html/css/src/xhtml/CDocumentStyle.cpp | 10 +-- HtmlFile2/htmlfile2.cpp | 71 +++++++++++++++---- 2 files changed, 62 insertions(+), 19 deletions(-) diff --git a/Common/3dParty/html/css/src/xhtml/CDocumentStyle.cpp b/Common/3dParty/html/css/src/xhtml/CDocumentStyle.cpp index f47aea8d1a..9af7f99b4f 100644 --- a/Common/3dParty/html/css/src/xhtml/CDocumentStyle.cpp +++ b/Common/3dParty/html/css/src/xhtml/CDocumentStyle.cpp @@ -341,12 +341,14 @@ namespace NSCSS sSpacingValue.reserve(128); if (!oStyle.m_oMargin.GetTop().Empty() && !oStyle.m_oMargin.GetTop().Zero()) - sSpacingValue += L"w:before=\"" + std::to_wstring(VALUE_TO_INT(oStyle.m_oMargin.GetTop(), NSCSS::Twips)) + L"\" w:beforeAutospacing=\"0\" "; + sSpacingValue += L"w:before=\"" + std::to_wstring(VALUE_TO_INT(oStyle.m_oMargin.GetTop(), NSCSS::Twips)) + L"\" w:beforeAutospacing=\"0\""; + else if (oStyle.m_oMargin.GetBottom().Zero() || bInTable) + sSpacingValue += L"w:before=\"0\" w:beforeAutospacing=\"0\""; if (!oStyle.m_oMargin.GetBottom().Empty() && !oStyle.m_oMargin.GetBottom().Zero()) - sSpacingValue += L"w:after=\"" + std::to_wstring(VALUE_TO_INT(oStyle.m_oMargin.GetBottom(), NSCSS::Twips)) + L"\" w:afterAutospacing=\"0\" "; + sSpacingValue += L" w:after=\"" + std::to_wstring(VALUE_TO_INT(oStyle.m_oMargin.GetBottom(), NSCSS::Twips)) + L"\" w:afterAutospacing=\"0\""; else if (oStyle.m_oMargin.GetBottom().Zero() || bInTable) - sSpacingValue += L"w:after=\"0\" "; + sSpacingValue += L" w:after=\"0\" w:afterAutospacing=\"0\""; if (!oStyle.m_oFont.GetLineHeight().Empty() && !oStyle.m_oFont.GetLineHeight().Zero()) { @@ -356,7 +358,7 @@ namespace NSCSS sSpacingValue += L" w:line=\"" + wsLine + L"\" w:lineRule=\"" + wsLineRule + L"\""; } else if (oStyle.m_oFont.GetLineHeight().Zero() || bInTable) - sSpacingValue += L"w:lineRule=\"auto\" w:line=\"240\""; + sSpacingValue += L" w:lineRule=\"auto\" w:line=\"240\""; if (!sSpacingValue.empty()) oXmlElement.AddPropertiesInP(PProperties::P_Spacing, sSpacingValue); diff --git a/HtmlFile2/htmlfile2.cpp b/HtmlFile2/htmlfile2.cpp index 3d93214e25..f0433d7f60 100644 --- a/HtmlFile2/htmlfile2.cpp +++ b/HtmlFile2/htmlfile2.cpp @@ -2195,25 +2195,54 @@ private: if (oTS.bQ) oXml->WriteString(L"""); - - OpenT(oXml); } - else if (oTS.bQ) + + if (oTS.bQ) oXml->WriteString(L"""); if(oTS.bPre) { - size_t nAfter = sText.find_first_of(L"\n\r"); + if (L'\n' == sText.front() || L'\r' == sText.front()) + sText.erase(0, 1); + + size_t nAfter = sText.find_first_of(L"\n\r\t"); while(nAfter != std::wstring::npos) { + if (L'\t' == sText[0]) + { + oXml->WriteString(L""); + sText.erase(0, 1); + + if (0 == nAfter) + { + nAfter = sText.find_first_of(L"\n\r\t"); + continue; + } + + nAfter--; + } + + OpenT(oXml); oXml->WriteEncodeXmlString(sText.c_str(), nAfter); - oXml->WriteString(L""); - oXml->WriteString(oPPr.GetData()); - oXml->WriteNodeBegin(L"w:r"); + CloseT(oXml); + CloseR(oXml); + + if (L'\t' == sText[nAfter]) + { + sText.erase(0, nAfter); + nAfter = 1; + } + else + { + CloseP(oXml, sSelectors); + OpenP(oXml); + oXml->WriteString(oPPr.GetData()); + sText.erase(0, nAfter + 1); + nAfter = 0; + } + OpenR(oXml); oXml->WriteString(oRPr.GetData()); - oXml->WriteString(L""); - sText.erase(0, nAfter + 1); - nAfter = sText.find_first_of(L"\n\r"); + nAfter = sText.find_first_of(L"\n\r\t", nAfter); } if (sText.empty()) @@ -2222,16 +2251,26 @@ private: else ReplaceSpaces(sText); - if (std::iswspace(sText.front()) && m_oState.m_bWasSpace) + if (!sText.empty() && L'\t' == sText[0]) + { + oXml->WriteString(L""); + sText.erase(0, 1); + } + + if (!oTS.bPre && !sText.empty() && std::iswspace(sText.front()) && m_oState.m_bWasSpace) sText.erase(0, 1); - if (oTS.bMergeText && !m_oState.m_bWasSpace && bInT) - oXml->WriteEncodeXmlString(L" "); - if (!sText.empty()) + { + OpenT(oXml); + + if (oTS.bMergeText && !m_oState.m_bWasSpace && bInT && !oTS.bPre) + oXml->WriteEncodeXmlString(L" "); + m_oState.m_bWasSpace = std::iswspace(sText.back()); - oXml->WriteEncodeXmlString(sText); + oXml->WriteEncodeXmlString(sText); + } if (oTS.bQ) oXml->WriteString(L"""); @@ -2645,6 +2684,8 @@ private: CTextSettings oTSPre(oTS); oTSPre.oAdditionalStyle.m_oFont.SetFamily(L"Courier New", NEXT_LEVEL); oTSPre.oAdditionalStyle.m_oFont.SetSize(20, NEXT_LEVEL); + oTSPre.oAdditionalStyle.m_oMargin.SetTop(0, NEXT_LEVEL); + oTSPre.oAdditionalStyle.m_oMargin.SetBottom(0, NEXT_LEVEL); oTSPre.bPre = true; bResult = readStream(&oXmlData, sSelectors, oTSPre); } From 796537565b9f8e54a9b4b2a4973fd0416e529637 Mon Sep 17 00:00:00 2001 From: Kirill Polyakov Date: Fri, 9 Aug 2024 15:43:55 +0300 Subject: [PATCH 18/18] Fixed unnecessary use of standard styles in html to ooxml conversion --- Common/3dParty/html/css/src/xhtml/CDocumentStyle.cpp | 8 +------- Common/3dParty/html/css/src/xhtml/CXmlElement.cpp | 6 +++--- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/Common/3dParty/html/css/src/xhtml/CDocumentStyle.cpp b/Common/3dParty/html/css/src/xhtml/CDocumentStyle.cpp index 9af7f99b4f..c69d7ffa9c 100644 --- a/Common/3dParty/html/css/src/xhtml/CDocumentStyle.cpp +++ b/Common/3dParty/html/css/src/xhtml/CDocumentStyle.cpp @@ -523,10 +523,7 @@ namespace NSCSS Clear(); if(oStyle.GetId().empty()) - { - m_sId = L"normal"; return false; - } CStyleUsed structStyle(oStyle, false); @@ -589,10 +586,7 @@ namespace NSCSS Clear(); if(oStyle.GetId().empty()) - { - m_sId = L"normal"; - return true; - } + return false; CStyleUsed structStyle(oStyle, true); std::vector::iterator oItem = std::find(m_arStyleUsed.begin(), m_arStyleUsed.end(), structStyle); diff --git a/Common/3dParty/html/css/src/xhtml/CXmlElement.cpp b/Common/3dParty/html/css/src/xhtml/CXmlElement.cpp index c617695a44..6eb112e02a 100644 --- a/Common/3dParty/html/css/src/xhtml/CXmlElement.cpp +++ b/Common/3dParty/html/css/src/xhtml/CXmlElement.cpp @@ -27,7 +27,7 @@ CXmlElement::CXmlElement(const std::wstring& sNameDefaultElement) bool CXmlElement::Empty() const { - return m_mPStyleValues.empty() && m_mRStyleValues.empty() && m_mBasicValues.find(CSSProperties::BasicProperties::B_BasedOn) == m_mBasicValues.end(); + return m_mPStyleValues.empty() && m_mRStyleValues.empty() && GetBasedOn().empty(); } void CXmlElement::CreateDefaultElement(const std::wstring& sNameDefaultElement) @@ -203,7 +203,7 @@ void CXmlElement::CreateDefaultElement(const std::wstring& sNameDefaultElement) AddPropertiesInR(CSSProperties::RunnerProperties::R_Sz, L"15"); AddPropertiesInR(CSSProperties::RunnerProperties::R_B, L"bold"); } - else if (sNameDefaultElement == L"div-c") + /*else if (sNameDefaultElement == L"div-c") { AddBasicProperties(CSSProperties::BasicProperties::B_Type, L"character"); AddBasicProperties(CSSProperties::BasicProperties::B_StyleId, L"div-c"); @@ -219,7 +219,7 @@ void CXmlElement::CreateDefaultElement(const std::wstring& sNameDefaultElement) AddBasicProperties(CSSProperties::BasicProperties::B_Name, L"Div paragraph"); AddBasicProperties(CSSProperties::BasicProperties::B_BasedOn, L"normal"); AddBasicProperties(CSSProperties::BasicProperties::B_Link, L"div-c"); - } + }*/ else if (sNameDefaultElement == L"a-c") { AddBasicProperties(CSSProperties::BasicProperties::B_Type, L"character");