From a8fa59d64d3a82b7c605d0e9abc55d16941b835c Mon Sep 17 00:00:00 2001 From: Kirill Polyakov Date: Thu, 25 Jul 2024 14:09:31 +0300 Subject: [PATCH] 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