From cd102c738fd1b3e0acbfe955977db3d3b6497c04 Mon Sep 17 00:00:00 2001 From: Svetlana Kulikova Date: Thu, 13 Feb 2025 11:37:28 +0300 Subject: [PATCH 01/28] Choice widget Opt only Parent --- PdfFile/SrcReader/PdfAnnot.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PdfFile/SrcReader/PdfAnnot.cpp b/PdfFile/SrcReader/PdfAnnot.cpp index 6db2cbd03b..34ed6ed596 100644 --- a/PdfFile/SrcReader/PdfAnnot.cpp +++ b/PdfFile/SrcReader/PdfAnnot.cpp @@ -1274,7 +1274,7 @@ CAnnotWidgetCh::CAnnotWidgetCh(PDFDoc* pdfDoc, AcroFormField* pField) : CAnnotWi Object oOpt; // 10 - Список значений - if (pField->fieldLookup("Opt", &oOpt)->isArray()) + if (oField.dictLookup("Opt", &oOpt)->isArray()) { m_unFlags |= (1 << 10); int nOptLength = oOpt.arrayGetLength(); From 600d2a6ac5e26231145fb129d47ea0a458d9a5f8 Mon Sep 17 00:00:00 2001 From: Svetlana Kulikova Date: Thu, 13 Feb 2025 11:44:21 +0300 Subject: [PATCH 02/28] Fix build --- PdfFile/PdfFile.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/PdfFile/PdfFile.cpp b/PdfFile/PdfFile.cpp index 7f76a7b9c2..ec96d91007 100644 --- a/PdfFile/PdfFile.cpp +++ b/PdfFile/PdfFile.cpp @@ -36,14 +36,12 @@ #include "../DesktopEditor/common/File.h" #include "../DesktopEditor/graphics/commands/DocInfo.h" #include "lib/xpdf/PDFDoc.h" +#include "Resources/BaseFonts.h" #ifndef BUILDING_WASM_MODULE #include "PdfEditor.h" #include "OnlineOfficeBinToPdf.h" - #include "SrcWriter/Document.h" -#include "Resources/BaseFonts.h" - #else class CPdfEditor { From 8bc887142c2a47d867190cbe1543cb35e2704c1e Mon Sep 17 00:00:00 2001 From: Svetlana Kulikova Date: Fri, 14 Feb 2025 11:51:33 +0300 Subject: [PATCH 03/28] Return fontInfo for readAnnotAP --- .../graphics/pro/js/qt/nativegraphics.pro | 2 ++ .../graphics/pro/js/wasm/js/drawingfile.js | 11 ++++++ .../pro/js/wasm/src/drawingfile_test.cpp | 23 +++++++++++-- PdfFile/SrcReader/Adaptors.cpp | 34 +++++++++++++++++++ PdfFile/SrcReader/Adaptors.h | 9 +++++ PdfFile/SrcReader/PdfAnnot.cpp | 15 ++++++++ PdfFile/SrcReader/PdfAnnot.h | 1 + PdfFile/SrcReader/RendererOutputDev.cpp | 6 ++++ 8 files changed, 99 insertions(+), 2 deletions(-) diff --git a/DesktopEditor/graphics/pro/js/qt/nativegraphics.pro b/DesktopEditor/graphics/pro/js/qt/nativegraphics.pro index 3b3710f95f..8f532e4fa5 100644 --- a/DesktopEditor/graphics/pro/js/qt/nativegraphics.pro +++ b/DesktopEditor/graphics/pro/js/qt/nativegraphics.pro @@ -682,6 +682,7 @@ HEADERS += \ $$DOCX_RENDERER_ROOT_DIR/src/logic/elements/DropCap.h \ $$DOCX_RENDERER_ROOT_DIR/src/logic/elements/Paragraph.h \ $$DOCX_RENDERER_ROOT_DIR/src/logic/elements/Shape.h \ + $$DOCX_RENDERER_ROOT_DIR/src/logic/elements/Table.h \ $$DOCX_RENDERER_ROOT_DIR/src/logic/elements/TextLine.h \ $$DOCX_RENDERER_ROOT_DIR/src/logic/managers/ExternalImageStorage.h \ $$DOCX_RENDERER_ROOT_DIR/src/logic/managers/FontStyleManager.h \ @@ -706,6 +707,7 @@ SOURCES += \ $$DOCX_RENDERER_ROOT_DIR/src/logic/elements/ContText.cpp \ $$DOCX_RENDERER_ROOT_DIR/src/logic/elements/Paragraph.cpp \ $$DOCX_RENDERER_ROOT_DIR/src/logic/elements/Shape.cpp \ + $$DOCX_RENDERER_ROOT_DIR/src/logic/elements/Table.cpp \ $$DOCX_RENDERER_ROOT_DIR/src/logic/elements/TextLine.cpp \ $$DOCX_RENDERER_ROOT_DIR/src/logic/managers/FontManager.cpp \ $$DOCX_RENDERER_ROOT_DIR/src/logic/managers/FontStyleManager.cpp \ diff --git a/DesktopEditor/graphics/pro/js/wasm/js/drawingfile.js b/DesktopEditor/graphics/pro/js/wasm/js/drawingfile.js index c75590e0e7..56474ba827 100644 --- a/DesktopEditor/graphics/pro/js/wasm/js/drawingfile.js +++ b/DesktopEditor/graphics/pro/js/wasm/js/drawingfile.js @@ -519,6 +519,17 @@ function readAnnotAP(reader, AP) // 0 - Normal, 1 - Multiply, 2 - Screen, 3 - Overlay, 4 - Darken, 5 - Lighten, 6 - ColorDodge, 7 - ColorBurn, 8 - HardLight, // 9 - SoftLight, 10 - Difference, 11 - Exclusion, 12 - Hue, 13 - Saturation, 14 - Color, 15 - Luminosity APi["BlendMode"] = reader.readByte(); + let k = reader.readInt(); + if (k != 0) + APi["fontInfo"] = []; + for (let j = 0; j < k; ++j) + { + let fontInfo = {}; + fontInfo["text"] = reader.readString(); + fontInfo["fontName"] = reader.readString(); + fontInfo["fontSize"] = reader.readDouble(); + APi["fontInfo"].push(fontInfo); + } } } diff --git a/DesktopEditor/graphics/pro/js/wasm/src/drawingfile_test.cpp b/DesktopEditor/graphics/pro/js/wasm/src/drawingfile_test.cpp index e36f48cd53..bc86752720 100644 --- a/DesktopEditor/graphics/pro/js/wasm/src/drawingfile_test.cpp +++ b/DesktopEditor/graphics/pro/js/wasm/src/drawingfile_test.cpp @@ -807,6 +807,25 @@ void ReadAnnotAP(BYTE* pWidgetsAP, int& i) std::string arrBlendMode[] = { "Normal", "Multiply", "Screen", "Overlay", "Darken", "Lighten", "ColorDodge", "ColorBurn", "HardLight", "SoftLight", "Difference", "Exclusion", "Hue", "Saturation", "Color", "Luminosity" }; std::cout << "Type " << arrBlendMode[nPathLength] << ", "; + + int nTextSize = READ_INT(pWidgetsAP + i); + i += 4; + for (int k = 0; k < nTextSize; ++k) + { + nPathLength = READ_INT(pWidgetsAP + i); + i += 4; + std::cout << k << " Text " << std::string((char*)(pWidgetsAP + i), nPathLength) << ", "; + i += nPathLength; + + nPathLength = READ_INT(pWidgetsAP + i); + i += 4; + std::cout << "Font " << std::string((char*)(pWidgetsAP + i), nPathLength) << ", "; + i += nPathLength; + + nPathLength = READ_INT(pWidgetsAP + i); + i += 4; + std::cout << "Size " << (double)nPathLength / 100.0 << ", "; + } } std::cout << std::endl; } @@ -1090,7 +1109,7 @@ int main(int argc, char* argv[]) } // GLYPHS - if (true && nPagesCount > 0) + if (false && nPagesCount > 0) { BYTE* pGlyphs = GetGlyphs(pGrFile, nTestPage); nLength = READ_INT(pGlyphs); @@ -1165,7 +1184,7 @@ int main(int argc, char* argv[]) } // INTERACTIVE FORMS - if (false) + if (true) { ReadInteractiveFormsFonts(pGrFile, 1); ReadInteractiveFormsFonts(pGrFile, 2); diff --git a/PdfFile/SrcReader/Adaptors.cpp b/PdfFile/SrcReader/Adaptors.cpp index ad8da8382c..a17269b51f 100644 --- a/PdfFile/SrcReader/Adaptors.cpp +++ b/PdfFile/SrcReader/Adaptors.cpp @@ -185,6 +185,40 @@ bool GlobalParamsAdaptor::GetCMap(const char* sName, char*& pData, unsigned int& return false; } +void GlobalParamsAdaptor::AddTextFormField(const std::wstring& sText, const std::wstring& sFontName, double dFontSize) +{ + if (!m_arrTextFormField.empty()) + { + TextFormField& tFF = m_arrTextFormField.back(); + if (tFF.sFontName == sFontName && tFF.dFontSize == dFontSize) + { + tFF.sText += sText; + return; + } + } + m_arrTextFormField.push_back({sText, sFontName, dFontSize}); +} +BYTE* GlobalParamsAdaptor::GetTextFormField() +{ + NSWasm::CData oRes; + oRes.SkipLen(); + + oRes.AddInt(m_arrTextFormField.size()); + for (TextFormField& tFF : m_arrTextFormField) + { + std::string sText = U_TO_UTF8(tFF.sText); + std::string sFontName = U_TO_UTF8(tFF.sFontName); + oRes.WriteString((BYTE*)sText.c_str(), (unsigned int)sText.length()); + oRes.WriteString((BYTE*)sFontName.c_str(), (unsigned int)sFontName.length()); + oRes.AddDouble(tFF.dFontSize); + } + m_arrTextFormField.clear(); + + oRes.WriteLen(); + BYTE* bRes = oRes.GetBuffer(); + oRes.ClearWithoutAttack(); + return bRes; +} bool operator==(const Ref &a, const Ref &b) { diff --git a/PdfFile/SrcReader/Adaptors.h b/PdfFile/SrcReader/Adaptors.h index 825c4801f1..5f7acf087b 100644 --- a/PdfFile/SrcReader/Adaptors.h +++ b/PdfFile/SrcReader/Adaptors.h @@ -59,6 +59,13 @@ class GlobalParamsAdaptor : public GlobalParams BYTE* m_bCMapData; DWORD m_nCMapDataLength; + struct TextFormField + { + std::wstring sText; + std::wstring sFontName; + double dFontSize; + }; + std::vector m_arrTextFormField; bool m_bDrawFormField; public: @@ -90,6 +97,8 @@ public: void SetCMapMemory(BYTE* pData, DWORD nSizeData); bool GetCMap(const char* sName, char*& pData, unsigned int& nSize); + void AddTextFormField(const std::wstring& sText, const std::wstring& sFontName, double dFontSize); + BYTE* GetTextFormField(); void setDrawFormField(bool bDrawFormField) { m_bDrawFormField = bDrawFormField; } bool getDrawFormField() { return m_bDrawFormField; } private: diff --git a/PdfFile/SrcReader/PdfAnnot.cpp b/PdfFile/SrcReader/PdfAnnot.cpp index 34ed6ed596..60bcc822f0 100644 --- a/PdfFile/SrcReader/PdfAnnot.cpp +++ b/PdfFile/SrcReader/PdfAnnot.cpp @@ -3241,6 +3241,7 @@ CAnnotAP::~CAnnotAP() for (int i = 0; i < m_arrAP.size(); ++i) { + RELEASEOBJECT(m_arrAP[i]->pText); RELEASEOBJECT(m_arrAP[i]); } } @@ -3545,6 +3546,7 @@ void CAnnotAP::WriteAppearance(unsigned int nColor, CAnnotAPView* pView) } pView->pAP = pSubMatrix; + pView->pText = ((GlobalParamsAdaptor*)globalParams)->GetTextFormField(); } BYTE CAnnotAP::GetBlendMode() { @@ -3577,6 +3579,19 @@ void CAnnotAP::ToWASM(NSWasm::CData& oRes) oRes.AddInt(npSubMatrix >> 32); oRes.WriteBYTE(m_arrAP[i]->nBlendMode); + + BYTE* pTextFormField = m_arrAP[i]->pText; + if (pTextFormField) + { + BYTE* x = pTextFormField; + unsigned int nLength = x[0] | x[1] << 8 | x[2] << 16 | x[3] << 24; + nLength -= 4; + oRes.Write(pTextFormField + 4, nLength); + } + else + oRes.AddInt(0); + RELEASEARRAYOBJECTS(pTextFormField); + m_arrAP[i]->pText = NULL; } } void CAnnots::ToWASM(NSWasm::CData& oRes) diff --git a/PdfFile/SrcReader/PdfAnnot.h b/PdfFile/SrcReader/PdfAnnot.h index 4eeec542c6..3146fd9489 100644 --- a/PdfFile/SrcReader/PdfAnnot.h +++ b/PdfFile/SrcReader/PdfAnnot.h @@ -130,6 +130,7 @@ private: std::string sAPName; std::string sASName; BYTE* pAP; + BYTE* pText; }; void WriteAppearance(unsigned int nColor, CAnnotAPView* pView); diff --git a/PdfFile/SrcReader/RendererOutputDev.cpp b/PdfFile/SrcReader/RendererOutputDev.cpp index 44fed8e90f..ae3e1a00ea 100644 --- a/PdfFile/SrcReader/RendererOutputDev.cpp +++ b/PdfFile/SrcReader/RendererOutputDev.cpp @@ -2518,6 +2518,12 @@ namespace PdfReader } } } + if (((GlobalParamsAdaptor*)globalParams)->getDrawFormField()) + { + double dFontSize; + m_pRenderer->get_FontSize(&dFontSize); + ((GlobalParamsAdaptor*)globalParams)->AddTextFormField(wsUnicodeText, sFontPath, dFontSize); + } #endif m_pRenderer->CommandDrawTextEx(wsUnicodeText, &unGid, unGidsCount, PDFCoordsToMM(dShiftX), PDFCoordsToMM(dShiftY), PDFCoordsToMM(dDx), PDFCoordsToMM(dDy)); if (bReplace) From d09ab5ecfee7846d4021cb74be4f990674e546dc Mon Sep 17 00:00:00 2001 From: Svetlana Kulikova Date: Fri, 14 Feb 2025 12:16:11 +0300 Subject: [PATCH 04/28] Only text fo apValue --- .../graphics/pro/js/wasm/js/drawingfile.js | 12 ++----- .../pro/js/wasm/src/drawingfile_test.cpp | 17 +++------- PdfFile/SrcReader/Adaptors.cpp | 34 +++---------------- PdfFile/SrcReader/Adaptors.h | 12 ++----- PdfFile/SrcReader/PdfAnnot.cpp | 20 ++++------- PdfFile/SrcReader/PdfAnnot.h | 2 +- PdfFile/SrcReader/RendererOutputDev.cpp | 6 +--- 7 files changed, 22 insertions(+), 81 deletions(-) diff --git a/DesktopEditor/graphics/pro/js/wasm/js/drawingfile.js b/DesktopEditor/graphics/pro/js/wasm/js/drawingfile.js index 56474ba827..cf835935fb 100644 --- a/DesktopEditor/graphics/pro/js/wasm/js/drawingfile.js +++ b/DesktopEditor/graphics/pro/js/wasm/js/drawingfile.js @@ -519,17 +519,9 @@ function readAnnotAP(reader, AP) // 0 - Normal, 1 - Multiply, 2 - Screen, 3 - Overlay, 4 - Darken, 5 - Lighten, 6 - ColorDodge, 7 - ColorBurn, 8 - HardLight, // 9 - SoftLight, 10 - Difference, 11 - Exclusion, 12 - Hue, 13 - Saturation, 14 - Color, 15 - Luminosity APi["BlendMode"] = reader.readByte(); - let k = reader.readInt(); + let k = reader.readByte(); if (k != 0) - APi["fontInfo"] = []; - for (let j = 0; j < k; ++j) - { - let fontInfo = {}; - fontInfo["text"] = reader.readString(); - fontInfo["fontName"] = reader.readString(); - fontInfo["fontSize"] = reader.readDouble(); - APi["fontInfo"].push(fontInfo); - } + APi["apValue"] = reader.readString(); } } diff --git a/DesktopEditor/graphics/pro/js/wasm/src/drawingfile_test.cpp b/DesktopEditor/graphics/pro/js/wasm/src/drawingfile_test.cpp index bc86752720..05cf4a2ab8 100644 --- a/DesktopEditor/graphics/pro/js/wasm/src/drawingfile_test.cpp +++ b/DesktopEditor/graphics/pro/js/wasm/src/drawingfile_test.cpp @@ -808,23 +808,14 @@ void ReadAnnotAP(BYTE* pWidgetsAP, int& i) "SoftLight", "Difference", "Exclusion", "Hue", "Saturation", "Color", "Luminosity" }; std::cout << "Type " << arrBlendMode[nPathLength] << ", "; - int nTextSize = READ_INT(pWidgetsAP + i); - i += 4; - for (int k = 0; k < nTextSize; ++k) + int bText = READ_BYTE(pWidgetsAP + i); + i += 1; + if (bText != 0) { nPathLength = READ_INT(pWidgetsAP + i); i += 4; - std::cout << k << " Text " << std::string((char*)(pWidgetsAP + i), nPathLength) << ", "; + std::cout << "Text " << std::string((char*)(pWidgetsAP + i), nPathLength) << ", "; i += nPathLength; - - nPathLength = READ_INT(pWidgetsAP + i); - i += 4; - std::cout << "Font " << std::string((char*)(pWidgetsAP + i), nPathLength) << ", "; - i += nPathLength; - - nPathLength = READ_INT(pWidgetsAP + i); - i += 4; - std::cout << "Size " << (double)nPathLength / 100.0 << ", "; } } std::cout << std::endl; diff --git a/PdfFile/SrcReader/Adaptors.cpp b/PdfFile/SrcReader/Adaptors.cpp index a17269b51f..62ac8e8001 100644 --- a/PdfFile/SrcReader/Adaptors.cpp +++ b/PdfFile/SrcReader/Adaptors.cpp @@ -185,39 +185,13 @@ bool GlobalParamsAdaptor::GetCMap(const char* sName, char*& pData, unsigned int& return false; } -void GlobalParamsAdaptor::AddTextFormField(const std::wstring& sText, const std::wstring& sFontName, double dFontSize) +void GlobalParamsAdaptor::AddTextFormField(const std::wstring& sText) { - if (!m_arrTextFormField.empty()) - { - TextFormField& tFF = m_arrTextFormField.back(); - if (tFF.sFontName == sFontName && tFF.dFontSize == dFontSize) - { - tFF.sText += sText; - return; - } - } - m_arrTextFormField.push_back({sText, sFontName, dFontSize}); + m_sTextFormField += sText; } -BYTE* GlobalParamsAdaptor::GetTextFormField() +std::string GlobalParamsAdaptor::GetTextFormField() { - NSWasm::CData oRes; - oRes.SkipLen(); - - oRes.AddInt(m_arrTextFormField.size()); - for (TextFormField& tFF : m_arrTextFormField) - { - std::string sText = U_TO_UTF8(tFF.sText); - std::string sFontName = U_TO_UTF8(tFF.sFontName); - oRes.WriteString((BYTE*)sText.c_str(), (unsigned int)sText.length()); - oRes.WriteString((BYTE*)sFontName.c_str(), (unsigned int)sFontName.length()); - oRes.AddDouble(tFF.dFontSize); - } - m_arrTextFormField.clear(); - - oRes.WriteLen(); - BYTE* bRes = oRes.GetBuffer(); - oRes.ClearWithoutAttack(); - return bRes; + return U_TO_UTF8(m_sTextFormField); } bool operator==(const Ref &a, const Ref &b) diff --git a/PdfFile/SrcReader/Adaptors.h b/PdfFile/SrcReader/Adaptors.h index 5f7acf087b..ca27ed5f15 100644 --- a/PdfFile/SrcReader/Adaptors.h +++ b/PdfFile/SrcReader/Adaptors.h @@ -59,13 +59,7 @@ class GlobalParamsAdaptor : public GlobalParams BYTE* m_bCMapData; DWORD m_nCMapDataLength; - struct TextFormField - { - std::wstring sText; - std::wstring sFontName; - double dFontSize; - }; - std::vector m_arrTextFormField; + std::wstring m_sTextFormField; bool m_bDrawFormField; public: @@ -97,8 +91,8 @@ public: void SetCMapMemory(BYTE* pData, DWORD nSizeData); bool GetCMap(const char* sName, char*& pData, unsigned int& nSize); - void AddTextFormField(const std::wstring& sText, const std::wstring& sFontName, double dFontSize); - BYTE* GetTextFormField(); + void AddTextFormField(const std::wstring& sText); + std::string GetTextFormField(); void setDrawFormField(bool bDrawFormField) { m_bDrawFormField = bDrawFormField; } bool getDrawFormField() { return m_bDrawFormField; } private: diff --git a/PdfFile/SrcReader/PdfAnnot.cpp b/PdfFile/SrcReader/PdfAnnot.cpp index 60bcc822f0..7809e17d4b 100644 --- a/PdfFile/SrcReader/PdfAnnot.cpp +++ b/PdfFile/SrcReader/PdfAnnot.cpp @@ -3241,7 +3241,6 @@ CAnnotAP::~CAnnotAP() for (int i = 0; i < m_arrAP.size(); ++i) { - RELEASEOBJECT(m_arrAP[i]->pText); RELEASEOBJECT(m_arrAP[i]); } } @@ -3546,7 +3545,7 @@ void CAnnotAP::WriteAppearance(unsigned int nColor, CAnnotAPView* pView) } pView->pAP = pSubMatrix; - pView->pText = ((GlobalParamsAdaptor*)globalParams)->GetTextFormField(); + pView->sText = ((GlobalParamsAdaptor*)globalParams)->GetTextFormField(); } BYTE CAnnotAP::GetBlendMode() { @@ -3580,18 +3579,13 @@ void CAnnotAP::ToWASM(NSWasm::CData& oRes) oRes.WriteBYTE(m_arrAP[i]->nBlendMode); - BYTE* pTextFormField = m_arrAP[i]->pText; - if (pTextFormField) - { - BYTE* x = pTextFormField; - unsigned int nLength = x[0] | x[1] << 8 | x[2] << 16 | x[3] << 24; - nLength -= 4; - oRes.Write(pTextFormField + 4, nLength); - } + if (m_arrAP[i]->sText.empty()) + oRes.WriteBYTE(0); else - oRes.AddInt(0); - RELEASEARRAYOBJECTS(pTextFormField); - m_arrAP[i]->pText = NULL; + { + oRes.WriteBYTE(1); + oRes.WriteString(m_arrAP[i]->sText); + } } } void CAnnots::ToWASM(NSWasm::CData& oRes) diff --git a/PdfFile/SrcReader/PdfAnnot.h b/PdfFile/SrcReader/PdfAnnot.h index 3146fd9489..9ad8d1597d 100644 --- a/PdfFile/SrcReader/PdfAnnot.h +++ b/PdfFile/SrcReader/PdfAnnot.h @@ -129,8 +129,8 @@ private: BYTE nBlendMode; std::string sAPName; std::string sASName; + std::string sText; BYTE* pAP; - BYTE* pText; }; void WriteAppearance(unsigned int nColor, CAnnotAPView* pView); diff --git a/PdfFile/SrcReader/RendererOutputDev.cpp b/PdfFile/SrcReader/RendererOutputDev.cpp index ae3e1a00ea..0f7e5e3e12 100644 --- a/PdfFile/SrcReader/RendererOutputDev.cpp +++ b/PdfFile/SrcReader/RendererOutputDev.cpp @@ -2519,11 +2519,7 @@ namespace PdfReader } } if (((GlobalParamsAdaptor*)globalParams)->getDrawFormField()) - { - double dFontSize; - m_pRenderer->get_FontSize(&dFontSize); - ((GlobalParamsAdaptor*)globalParams)->AddTextFormField(wsUnicodeText, sFontPath, dFontSize); - } + ((GlobalParamsAdaptor*)globalParams)->AddTextFormField(wsUnicodeText); #endif m_pRenderer->CommandDrawTextEx(wsUnicodeText, &unGid, unGidsCount, PDFCoordsToMM(dShiftX), PDFCoordsToMM(dShiftY), PDFCoordsToMM(dDx), PDFCoordsToMM(dDy)); if (bReplace) From b177d010715f3324e07fb50c1dd0f5d011845187 Mon Sep 17 00:00:00 2001 From: Oleg Korshul Date: Mon, 3 Mar 2025 18:38:24 +0300 Subject: [PATCH 05/28] Add text example --- Common/3dParty/harfbuzz/test/main.cpp | 807 ++++++++++++++++++++++++++ Common/3dParty/harfbuzz/test/test.pro | 19 + 2 files changed, 826 insertions(+) create mode 100644 Common/3dParty/harfbuzz/test/main.cpp create mode 100644 Common/3dParty/harfbuzz/test/test.pro diff --git a/Common/3dParty/harfbuzz/test/main.cpp b/Common/3dParty/harfbuzz/test/main.cpp new file mode 100644 index 0000000000..4af637053d --- /dev/null +++ b/Common/3dParty/harfbuzz/test/main.cpp @@ -0,0 +1,807 @@ +// https://android.googlesource.com/platform/external/harfbuzz/+/ics-mr0/contrib/tables/script-properties.h + +/* + * https://unicode.org/reports/tr29/ + +As far as a user is concerned, the underlying representation of text is not important, +but it is important that an editing interface present a uniform implementation of what +the user thinks of as characters. Grapheme clusters can be treated as units, by default, +for processes such as the formatting of drop caps, as well as the implementation of text +selection, arrow key movement or backspacing through text, and so forth. For example, +when a grapheme cluster is represented internally by a character sequence consisting of +base character + accents, then using the right arrow key would skip from the start of the +base character to the end of the last accent. + +This document defines a default specification for grapheme clusters. It may be customized + for particular languages, operations, or other situations. For example, arrow key movement +could be tailored by language, or could use knowledge specific to particular fonts to move +in a more granular manner, in circumstances where it would be useful to edit individual +components. This could apply, for example, to the complex editorial requirements for the +Northern Thai script Tai Tham (Lanna). Similarly, editing a grapheme cluster element by +element may be preferable in some circumstances. For example, on a given system the backspace +key might delete by code point, while the delete key may delete an entire cluster. + * */ + +#include "../../../core/DesktopEditor/common/File.h" +#include "../../../core/DesktopEditor/raster/BgraFrame.h" + +#include +#include FT_FREETYPE_H +#include FT_GLYPH_H +#include FT_OUTLINE_H + +#include +#include +#include + +class CDrawer +{ +public: + CBgraFrame m_oFrame; + BYTE *pixels; + int width; + int height; + int pitch; + BYTE Rshift; + BYTE Gshift; + BYTE Bshift; + BYTE Ashift; + +public: + CDrawer(int w, int h) + { + width = w; + height = h; + pitch = 4 * width; + m_oFrame.put_Width(width); + m_oFrame.put_Height(height); + m_oFrame.put_Stride(pitch); + + int size = 4 * width * height; + BYTE *pPixels = new BYTE[size]; + for (int i = 0; i < size; i += 4) + { + pPixels[i] = 0xFF; + pPixels[i + 1] = 0xFF; + pPixels[i + 2] = 0xFF; + pPixels[i + 3] = 0xFF; + } + pixels = pPixels; + m_oFrame.put_Data(pPixels); + + Bshift = 24; + Gshift = 16; + Rshift = 8; + Ashift = 0; + } + void Save() + { + m_oFrame.SaveFile(NSFile::GetProcessDirectory() + L"/output.png", 4); + } +}; + +#define NUM_EXAMPLES 3 + +/* fonts */ +const char *fonts_paths[NUM_EXAMPLES] = { + "C:/Windows/Fonts/arial.ttf", + //"C:/Windows/Fonts/arial.ttf", + "C:/Users/korol/AppData/Local/Microsoft/Windows/Fonts/ArabicTest.ttf", + "C:/Windows/Fonts/simsun.ttc" +}; + +#define NUM_GLYPH_TYPES 5 +const char *num_glyph_types[NUM_GLYPH_TYPES] = {"UNCLASSIFIED", "BASE_GLYPH", "LIGATURE", "MARK", "COMPONENT"}; + +/* tranlations courtesy of google */ +const char *texts[NUM_EXAMPLES] = { + "hello", + "لا لآ لأ لا", + "懶惰的姜貓" +}; + +const hb_direction_t text_directions[NUM_EXAMPLES] = { + HB_DIRECTION_LTR, + HB_DIRECTION_RTL, + HB_DIRECTION_TTB, +}; + +const int text_skip[NUM_EXAMPLES] = { + 1, + 0, + 1, +}; + +/* XXX: These are not correct, though it doesn't seem to break anything + * regardless of their value. */ +const char *languages[NUM_EXAMPLES] = { + "en", + "ar", + "ch", +}; + +const hb_script_t scripts[NUM_EXAMPLES] = { + HB_SCRIPT_LATIN, + HB_SCRIPT_ARABIC, + HB_SCRIPT_HAN, +}; + +enum +{ + ENGLISH = 0, + ARABIC, + CHINESE +}; + +typedef struct _spanner_baton_t +{ + /* rendering part - assumes 32bpp surface */ + uint32_t *pixels; // set to the glyph's origin. + uint32_t *first_pixel, *last_pixel; // bounds check + uint32_t pitch; + uint32_t rshift; + uint32_t gshift; + uint32_t bshift; + uint32_t ashift; + + /* sizing part */ + int min_span_x; + int max_span_x; + int min_y; + int max_y; +} spanner_baton_t; + +/* This spanner is write only, suitable for write-only mapped buffers, + but can cause dark streaks where glyphs overlap, like in arabic scripts. + + Note how spanners don't clip against surface width - resize the window + and see what it leads to. */ +void spanner_wo(int y, int count, const FT_Span *spans, void *user) +{ + spanner_baton_t *baton = (spanner_baton_t *)user; + uint32_t *scanline = baton->pixels - y * ((int)baton->pitch / 4); + if (scanline < baton->first_pixel) + return; + for (int i = 0; i < count; i++) + { + uint32_t color = ((spans[i].coverage / 2) << baton->rshift) | ((spans[i].coverage / 2) << baton->gshift) | ((spans[i].coverage / 2) << baton->bshift); + + uint32_t *start = scanline + spans[i].x; + if (start + spans[i].len > baton->last_pixel) + return; + + for (int x = 0; x < spans[i].len; x++) + *start++ = color; + } +} + +/* This spanner does read/modify/write, trading performance for accuracy. + The color here is simply half coverage value in all channels, + effectively mid-gray. + Suitable for when artifacts mostly do come up and annoy. + This might be optimized if one does rmw only for some values of x. + But since the whole buffer has to be rw anyway, and the previous value + is probably still in the cache, there's little point to. */ +void spanner_rw(int y, int count, const FT_Span *spans, void *user) +{ + spanner_baton_t *baton = (spanner_baton_t *)user; + uint32_t *scanline = baton->pixels - y * ((int)baton->pitch / 4); + if (scanline < baton->first_pixel) + return; + + for (int i = 0; i < count; i++) + { + uint32_t color = ((spans[i].coverage / 2) << baton->rshift) | ((spans[i].coverage / 2) << baton->gshift) | ((spans[i].coverage / 2) << baton->bshift); + uint32_t *start = scanline + spans[i].x; + if (start + spans[i].len > baton->last_pixel) + return; + + for (int x = 0; x < spans[i].len; x++) + *start++ |= color; + } +} + +/* This spanner is for obtaining exact bounding box for the string. + Unfortunately this can't be done without rendering it (or pretending to). + After this runs, we get min and max values of coordinates used. +*/ +void spanner_sizer(int y, int count, const FT_Span *spans, void *user) +{ + spanner_baton_t *baton = (spanner_baton_t *)user; + + if (y < baton->min_y) + baton->min_y = y; + if (y > baton->max_y) + baton->max_y = y; + for (int i = 0; i < count; i++) + { + if (spans[i].x + spans[i].len > baton->max_span_x) + baton->max_span_x = spans[i].x + spans[i].len; + if (spans[i].x < baton->min_span_x) + baton->min_span_x = spans[i].x; + } +} + +FT_SpanFunc spanner = spanner_wo; + +void ftfdump(FT_Face ftf) +{ + for (int i = 0; i < ftf->num_charmaps; i++) + { + printf( + "%d: %s %s %c%c%c%c plat=%hu id=%hu\n", i, ftf->family_name, ftf->style_name, ftf->charmaps[i]->encoding >> 24, (ftf->charmaps[i]->encoding >> 16) & 0xff, + (ftf->charmaps[i]->encoding >> 8) & 0xff, (ftf->charmaps[i]->encoding) & 0xff, ftf->charmaps[i]->platform_id, ftf->charmaps[i]->encoding_id); + } +} + +/* See http://www.microsoft.com/typography/otspec/name.htm + for a list of some possible platform-encoding pairs. + We're interested in 0-3 aka 3-1 - UCS-2. + Otherwise, fail. If a font has some unicode map, but lacks + UCS-2 - it is a broken or irrelevant font. What exactly + Freetype will select on face load (it promises most wide + unicode, and if that will be slower that UCS-2 - left as + an excercise to check. */ +int force_ucs2_charmap(FT_Face ftf) +{ + for (int i = 0; i < ftf->num_charmaps; i++) + if (((ftf->charmaps[i]->platform_id == 0) && (ftf->charmaps[i]->encoding_id == 3)) || ((ftf->charmaps[i]->platform_id == 3) && (ftf->charmaps[i]->encoding_id == 1))) + return FT_Set_Charmap(ftf, ftf->charmaps[i]); + return -1; +} + +void hline(CDrawer *s, int min_x, int max_x, int y, uint32_t color) +{ + if (y < 0) + y = 0; + uint32_t *pix = (uint32_t *)s->pixels + (y * s->pitch) / 4 + min_x; + uint32_t *end = (uint32_t *)s->pixels + (y * s->pitch) / 4 + max_x; + + while (pix - 1 != end) + *pix++ = color; +} + +void vline(CDrawer *s, int min_y, int max_y, int x, uint32_t color) +{ + if (min_y < 0) + min_y = 0; + + uint32_t *pix = (uint32_t *)s->pixels + (min_y * s->pitch) / 4 + x; + uint32_t *end = (uint32_t *)s->pixels + (max_y * s->pitch) / 4 + x; + + while (pix - s->pitch / 4 != end) + { + *pix = color; + pix += s->pitch / 4; + } +} + +void assert(const bool &valid) +{ + // TODO: +} + +#define MAIN_CC_NO_PRIVATE_API +#ifndef MAIN_CC_NO_PRIVATE_API +/* Only this part of this mini app uses private API */ +#include "hb-open-file.hh" +#include "hb-ot-layout-gdef-table.hh" +#include "hb-ot-layout-gsubgpos.hh" +#include "hb-static.cc" + +using namespace OT; + +static void print_layout_info_using_private_api(hb_blob_t *blob) +{ + const char *font_data = hb_blob_get_data(blob, nullptr); + hb_blob_t *font_blob = hb_sanitize_context_t().sanitize_blob(blob); + const OpenTypeFontFile *sanitized = font_blob->as(); + if (!font_blob->data) + { + printf("Sanitization of the file wasn't successful. Exit"); + exit(1); + } + const OpenTypeFontFile &ot = *sanitized; + + switch (ot.get_tag()) + { + case OpenTypeFontFile::TrueTypeTag: + printf("OpenType font with TrueType outlines\n"); + break; + case OpenTypeFontFile::CFFTag: + printf("OpenType font with CFF (Type1) outlines\n"); + break; + case OpenTypeFontFile::TTCTag: + printf("TrueType Collection of OpenType fonts\n"); + break; + case OpenTypeFontFile::TrueTag: + printf("Obsolete Apple TrueType font\n"); + break; + case OpenTypeFontFile::Typ1Tag: + printf("Obsolete Apple Type1 font in SFNT container\n"); + break; + case OpenTypeFontFile::DFontTag: + printf("DFont Mac Resource Fork\n"); + break; + default: + printf("Unknown font format\n"); + break; + } + + unsigned num_faces = hb_face_count(blob); + printf("%d font(s) found in file\n", num_faces); + for (unsigned n_font = 0; n_font < num_faces; ++n_font) + { + const OpenTypeFontFace &font = ot.get_face(n_font); + printf("Font %d of %d:\n", n_font, num_faces); + + unsigned num_tables = font.get_table_count(); + printf(" %d table(s) found in font\n", num_tables); + for (unsigned n_table = 0; n_table < num_tables; ++n_table) + { + const OpenTypeTable &table = font.get_table(n_table); + printf(" Table %2d of %2d: %.4s (0x%08x+0x%08x)\n", n_table, num_tables, (const char *)table.tag, (unsigned)table.offset, (unsigned)table.length); + + switch (table.tag) + { + + case HB_OT_TAG_GSUB: + case HB_OT_TAG_GPOS: + { + + const GSUBGPOS &g = *reinterpret_cast(font_data + table.offset); + + unsigned num_scripts = g.get_script_count(); + printf(" %d script(s) found in table\n", num_scripts); + for (unsigned n_script = 0; n_script < num_scripts; ++n_script) + { + const Script &script = g.get_script(n_script); + printf(" Script %2d of %2d: %.4s\n", n_script, num_scripts, (const char *)g.get_script_tag(n_script)); + + if (!script.has_default_lang_sys()) + printf(" No default language system\n"); + int num_langsys = script.get_lang_sys_count(); + printf(" %d language system(s) found in script\n", num_langsys); + for (int n_langsys = script.has_default_lang_sys() ? -1 : 0; n_langsys < num_langsys; ++n_langsys) + { + const LangSys &langsys = n_langsys == -1 ? script.get_default_lang_sys() : script.get_lang_sys(n_langsys); + if (n_langsys == -1) + printf(" Default Language System\n"); + else + printf(" Language System %2d of %2d: %.4s\n", n_langsys, num_langsys, (const char *)script.get_lang_sys_tag(n_langsys)); + if (!langsys.has_required_feature()) + printf(" No required feature\n"); + else + printf(" Required feature index: %d\n", langsys.get_required_feature_index()); + + unsigned num_features = langsys.get_feature_count(); + printf(" %d feature(s) found in language system\n", num_features); + for (unsigned n_feature = 0; n_feature < num_features; ++n_feature) + { + printf(" Feature index %2d of %2d: %d\n", n_feature, num_features, langsys.get_feature_index(n_feature)); + } + } + } + + unsigned num_features = g.get_feature_count(); + printf(" %d feature(s) found in table\n", num_features); + for (unsigned n_feature = 0; n_feature < num_features; ++n_feature) + { + const Feature &feature = g.get_feature(n_feature); + unsigned num_lookups = feature.get_lookup_count(); + printf(" Feature %2d of %2d: %c%c%c%c\n", n_feature, num_features, HB_UNTAG(g.get_feature_tag(n_feature))); + + printf(" %d lookup(s) found in feature\n", num_lookups); + for (unsigned n_lookup = 0; n_lookup < num_lookups; ++n_lookup) + { + printf(" Lookup index %2d of %2d: %d\n", n_lookup, num_lookups, feature.get_lookup_index(n_lookup)); + } + } + + unsigned num_lookups = g.get_lookup_count(); + printf(" %d lookup(s) found in table\n", num_lookups); + for (unsigned n_lookup = 0; n_lookup < num_lookups; ++n_lookup) + { + const Lookup &lookup = g.get_lookup(n_lookup); + printf(" Lookup %2d of %2d: type %d, props 0x%04X\n", n_lookup, num_lookups, lookup.get_type(), lookup.get_props()); + } + } + break; + + case GDEF::tableTag: + { + + const GDEF &gdef = *reinterpret_cast(font_data + table.offset); + + printf(" Has %sglyph classes\n", gdef.has_glyph_classes() ? "" : "no "); + printf(" Has %smark attachment types\n", gdef.has_mark_attachment_types() ? "" : "no "); + printf(" Has %sattach points\n", gdef.has_attach_points() ? "" : "no "); + printf(" Has %slig carets\n", gdef.has_lig_carets() ? "" : "no "); + printf(" Has %smark sets\n", gdef.has_mark_sets() ? "" : "no "); + + hb_position_t caret_array[16]; + unsigned int caret_count = 16; + + unsigned int num_carets = gdef.get_lig_carets(nullptr, HB_DIRECTION_LTR, 302, 0, &caret_count, caret_array); + int y = 0; + ++y; + + break; + } + } + } + } +} +/* end of private API use */ +#endif + +int main(int argc, char *argv[]) +{ + // hb_blob_t* blobFileTest = hb_blob_create_from_file("C:/Windows/Fonts/calibri.ttf"); + // print_layout_info_using_private_api(blobFileTest); + + int ptSize = 40 * 64; + int device_hdpi = 72; + int device_vdpi = 72; + + /* Init freetype */ + FT_Library ft_library; + assert(!FT_Init_FreeType(&ft_library)); + + /* Load our fonts */ + FT_Face ft_face[NUM_EXAMPLES]; + assert(!FT_New_Face(ft_library, fonts_paths[0], 0, &ft_face[ENGLISH])); + assert(!FT_Set_Char_Size(ft_face[ENGLISH], 0, ptSize, device_hdpi, device_vdpi)); + ftfdump(ft_face[ENGLISH]); // wonderful world of encodings ... + // force_ucs2_charmap(ft_face[ENGLISH]); // which we ignore. + + assert(!FT_New_Face(ft_library, fonts_paths[1], 0, &ft_face[ARABIC])); + assert(!FT_Set_Char_Size(ft_face[ARABIC], 0, ptSize, device_hdpi, device_vdpi)); + ftfdump(ft_face[ARABIC]); + // force_ucs2_charmap(ft_face[ARABIC]); + + assert(!FT_New_Face(ft_library, fonts_paths[2], 0, &ft_face[CHINESE])); + assert(!FT_Set_Char_Size(ft_face[CHINESE], 0, ptSize, device_hdpi, device_vdpi)); + ftfdump(ft_face[CHINESE]); + // force_ucs2_charmap(ft_face[CHINESE]); + + /* Get our harfbuzz font structs */ + hb_font_t *hb_ft_font[NUM_EXAMPLES]; + hb_ft_font[ENGLISH] = hb_ft_font_create(ft_face[ENGLISH], NULL); + + // hb_blob_t* blobFile = hb_blob_create_from_file(sFont1.c_str()); + // hb_face_t* faceFile = hb_face_create(blobFile, 0); + // hb_ft_font[ENGLISH] = hb_font_create(faceFile); + + hb_ft_font[ARABIC] = hb_ft_font_create(ft_face[ARABIC], NULL); + hb_ft_font[CHINESE] = hb_ft_font_create(ft_face[CHINESE], NULL); + + hb_ft_font_set_funcs(hb_ft_font[ENGLISH]); + hb_ft_font_set_funcs(hb_ft_font[ARABIC]); + hb_ft_font_set_funcs(hb_ft_font[CHINESE]); + + /** Setup our SDL window **/ + int width = 800; + int height = 600; + int bpp = 32; + + CDrawer oDrawer(width, height); + + /* Create a buffer for harfbuzz to use */ + hb_buffer_t *buf = hb_buffer_create(); + for (int i = 0; i < NUM_EXAMPLES; ++i) + { + if (text_skip[i]) + continue; + + hb_buffer_set_direction(buf, text_directions[i]); /* or LTR */ + hb_buffer_set_script(buf, scripts[i]); /* see hb-unicode.h */ + hb_buffer_set_language(buf, hb_language_from_string(languages[i], strlen(languages[i]))); + // hb_buffer_set_cluster_level (buf, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES); + // hb_buffer_set_cluster_level (buf, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS); + hb_buffer_set_cluster_level(buf, HB_BUFFER_CLUSTER_LEVEL_CHARACTERS); + +#if 0 +#define userfeatures_count 4 + hb_feature_t userfeatures[userfeatures_count]; + hb_tag_t tags[] = { + HB_TAG('l','i','g','a'), + HB_TAG('c','l','i','g'), + HB_TAG('d','l','i','g'), + HB_TAG('h','l','i','g'), + }; + for (int f = 0; f < userfeatures_count; f++) + { + userfeatures[f].tag = tags[f]; + userfeatures[f].value = 1; + userfeatures[f].start = HB_FEATURE_GLOBAL_START; + userfeatures[f].end = HB_FEATURE_GLOBAL_END; + } +#else +#define userfeatures_count 0 + hb_feature_t *userfeatures = NULL; +#endif + + /* Layout the text */ + hb_buffer_add_utf8(buf, texts[i], strlen(texts[i]), 0, strlen(texts[i])); + + // detect script by codes + hb_buffer_guess_segment_properties(buf); + + // const char*const pHbShapers[] = { "graphite2", "coretext_aat", "ot", "fallback", nullptr }; + // bool ok = hb_shape_full(hb_ft_font[i], buf, userfeatures, userfeatures_count, pHbShapers); + + hb_shape(hb_ft_font[i], buf, userfeatures, userfeatures_count); + + unsigned int glyph_count; + hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(buf, &glyph_count); + hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(buf, &glyph_count); + +#if 1 + hb_position_t caret_array[16]; + unsigned int caret_count = 16; + + unsigned int num_carets = hb_ot_layout_get_ligature_carets(hb_ft_font[i], text_directions[i], glyph_info[0].codepoint, -1, &caret_count, caret_array); +#endif + + /* set up rendering via spanners */ + spanner_baton_t stuffbaton; + + FT_Raster_Params ftr_params; + ftr_params.target = 0; + ftr_params.flags = FT_RASTER_FLAG_DIRECT | FT_RASTER_FLAG_AA; + ftr_params.user = &stuffbaton; + ftr_params.black_spans = 0; + ftr_params.bit_set = 0; + ftr_params.bit_test = 0; + + /* Calculate string bounding box in pixels */ + ftr_params.gray_spans = spanner_sizer; + + /* See http://www.freetype.org/freetype2/docs/glyphs/glyphs-3.html */ + + int max_x = INT_MIN; // largest coordinate a pixel has been set at, or the pen was advanced to. + int min_x = INT_MAX; // smallest coordinate a pixel has been set at, or the pen was advanced to. + int max_y = INT_MIN; // this is max topside bearing along the string. + int min_y = INT_MAX; // this is max value of (height - topbearing) along the string. + /* Naturally, the above comments swap their meaning between horizontal and vertical scripts, + since the pen changes the axis it is advanced along. + However, their differences still make up the bounding box for the string. + Also note that all this is in FT coordinate system where y axis points upwards. + */ + + int sizer_x = 0; + int sizer_y = 0; /* in FT coordinate system. */ + + printf("----------------------------------------------------\n"); + for (unsigned j = 0; j < glyph_count; ++j) + { + hb_ot_layout_glyph_class_t glyph_type = hb_ot_layout_get_glyph_class(hb_font_get_face(hb_ft_font[i]), glyph_info[j].codepoint); + hb_glyph_flags_t glyph_type_flags = hb_glyph_info_get_glyph_flags(&glyph_info[j]); + printf( + "glyph(%s, flags: %d): gid:%d, cluster:%d, [%d, %d, %d, %d, %d]\n", num_glyph_types[glyph_type], glyph_type_flags, (int)glyph_info[j].codepoint, (int)glyph_info[j].cluster, + glyph_pos[j].x_advance, glyph_pos[j].y_advance, glyph_pos[j].x_offset, glyph_pos[j].y_offset, glyph_pos[j].var); + } + + FT_Error fterr; + for (unsigned j = 0; j < glyph_count; ++j) + { + if ((fterr = FT_Load_Glyph(ft_face[i], glyph_info[j].codepoint, 0))) + { + printf("load %08x failed fterr=%d.\n", glyph_info[j].codepoint, fterr); + } + else + { + if (ft_face[i]->glyph->format != FT_GLYPH_FORMAT_OUTLINE) + { + printf("glyph->format = %4s\n", (char *)&ft_face[i]->glyph->format); + } + else + { + int gx = sizer_x + (glyph_pos[j].x_offset / 64); + int gy = sizer_y + (glyph_pos[j].y_offset / 64); // note how the sign differs from the rendering pass + + stuffbaton.min_span_x = INT_MAX; + stuffbaton.max_span_x = INT_MIN; + stuffbaton.min_y = INT_MAX; + stuffbaton.max_y = INT_MIN; + + if ((fterr = FT_Outline_Render(ft_library, &ft_face[i]->glyph->outline, &ftr_params))) + printf("FT_Outline_Render() failed err=%d\n", fterr); + + if (stuffbaton.min_span_x != INT_MAX) + { + /* Update values if the spanner was actually called. */ + if (min_x > stuffbaton.min_span_x + gx) + min_x = stuffbaton.min_span_x + gx; + + if (max_x < stuffbaton.max_span_x + gx) + max_x = stuffbaton.max_span_x + gx; + + if (min_y > stuffbaton.min_y + gy) + min_y = stuffbaton.min_y + gy; + + if (max_y < stuffbaton.max_y + gy) + max_y = stuffbaton.max_y + gy; + } + else + { + /* The spanner wasn't called at all - an empty glyph, like space. */ + if (min_x > gx) + min_x = gx; + if (max_x < gx) + max_x = gx; + if (min_y > gy) + min_y = gy; + if (max_y < gy) + max_y = gy; + } + } + } + + sizer_x += glyph_pos[j].x_advance / 64; + sizer_y += glyph_pos[j].y_advance / 64; // note how the sign differs from the rendering pass + } + /* Still have to take into account last glyph's advance. Or not? */ + if (min_x > sizer_x) + min_x = sizer_x; + if (max_x < sizer_x) + max_x = sizer_x; + if (min_y > sizer_y) + min_y = sizer_y; + if (max_y < sizer_y) + max_y = sizer_y; + + /* The bounding box */ + int bbox_w = max_x - min_x; + int bbox_h = max_y - min_y; + + /* Two offsets below position the bounding box with respect to the 'origin', + which is sort of origin of string's first glyph. + + baseline_offset - offset perpendecular to the baseline to the topmost (horizontal), + or leftmost (vertical) pixel drawn. + + baseline_shift - offset along the baseline, from the first drawn glyph's origin + to the leftmost (horizontal), or topmost (vertical) pixel drawn. + + Thus those offsets allow positioning the bounding box to fit the rendered string, + as they are in fact offsets from the point given to the renderer, to the top left + corner of the bounding box. + + NB: baseline is defined as y==0 for horizontal and x==0 for vertical scripts. + (0,0) here is where the first glyph's origin ended up after shaping, not taking + into account glyph_pos[0].xy_offset (yeah, my head hurts too). + */ + + int baseline_offset; + int baseline_shift; + + if (HB_DIRECTION_IS_HORIZONTAL(hb_buffer_get_direction(buf))) + { + baseline_offset = max_y; + baseline_shift = min_x; + } + if (HB_DIRECTION_IS_VERTICAL(hb_buffer_get_direction(buf))) + { + baseline_offset = min_x; + baseline_shift = max_y; + } + + /* The pen/baseline start coordinates in window coordinate system + - with those text placement in the window is controlled. + - note that for RTL scripts pen still goes LTR */ + int x = 0, y = 50 + i * 75; + if (i == ENGLISH) + { + x = 20; + } /* left justify */ + if (i == ARABIC) + { + x = width - bbox_w - 20; + } /* right justify */ + if (i == CHINESE) + { + x = width / 2 - bbox_w / 2; + } /* center, and for TTB script h_advance is half-width. */ + + /* Draw baseline and the bounding box */ + /* The below is complicated since we simultaneously + convert to the window coordinate system. */ + int left, right, top, bottom; + + if (HB_DIRECTION_IS_HORIZONTAL(hb_buffer_get_direction(buf))) + { + /* bounding box in window coordinates without offsets */ + left = x; + right = x + bbox_w; + top = y - bbox_h; + bottom = y; + + /* apply offsets */ + left += baseline_shift; + right += baseline_shift; + top -= baseline_offset - bbox_h; + bottom -= baseline_offset - bbox_h; + + /* draw the baseline */ + hline(&oDrawer, x, x + bbox_w, y, 0x0000ff00); + } + + if (HB_DIRECTION_IS_VERTICAL(hb_buffer_get_direction(buf))) + { + left = x; + right = x + bbox_w; + top = y; + bottom = y + bbox_h; + + left += baseline_offset; + right += baseline_offset; + top -= baseline_shift; + bottom -= baseline_shift; + + vline(&oDrawer, y, y + bbox_h, x, 0x0000ff00); + } + + /* +1/-1 are for the bbox borders be the next pixel outside the bbox itself */ + hline(&oDrawer, left - 1, right + 1, top - 1, 0xffff0000); + hline(&oDrawer, left - 1, right + 1, bottom + 1, 0xffff0000); + vline(&oDrawer, top - 1, bottom + 1, left - 1, 0xffff0000); + vline(&oDrawer, top - 1, bottom + 1, right + 1, 0xffff0000); + + /* set rendering spanner */ + ftr_params.gray_spans = spanner; + + /* initialize rendering part of the baton */ + stuffbaton.pixels = NULL; + stuffbaton.first_pixel = (uint32_t *)oDrawer.pixels; + stuffbaton.last_pixel = (uint32_t *)(((uint8_t *)oDrawer.pixels) + oDrawer.pitch * oDrawer.height); + stuffbaton.pitch = oDrawer.pitch; + stuffbaton.rshift = oDrawer.Rshift; + stuffbaton.gshift = oDrawer.Gshift; + stuffbaton.bshift = oDrawer.Bshift; + + /* render */ + for (unsigned j = 0; j < glyph_count; ++j) + { + if ((fterr = FT_Load_Glyph(ft_face[i], glyph_info[j].codepoint, 0))) + { + printf("load %08x failed fterr=%d.\n", glyph_info[j].codepoint, fterr); + } + else + { + if (ft_face[i]->glyph->format != FT_GLYPH_FORMAT_OUTLINE) + { + printf("glyph->format = %4s\n", (char *)&ft_face[i]->glyph->format); + } + else + { + int gx = x + (glyph_pos[j].x_offset / 64); + int gy = y - (glyph_pos[j].y_offset / 64); + + stuffbaton.pixels = (uint32_t *)(((uint8_t *)oDrawer.pixels) + gy * oDrawer.pitch) + gx; + + if ((fterr = FT_Outline_Render(ft_library, &ft_face[i]->glyph->outline, &ftr_params))) + printf("FT_Outline_Render() failed err=%d\n", fterr); + } + } + + x += glyph_pos[j].x_advance / 64; + y -= glyph_pos[j].y_advance / 64; + } + + /* clean up the buffer, but don't kill it just yet */ + hb_buffer_clear_contents(buf); + } + + /* Cleanup */ + hb_buffer_destroy(buf); + for (int i = 0; i < NUM_EXAMPLES; ++i) + hb_font_destroy(hb_ft_font[i]); + + FT_Done_FreeType(ft_library); + + oDrawer.Save(); + + return 0; +} diff --git a/Common/3dParty/harfbuzz/test/test.pro b/Common/3dParty/harfbuzz/test/test.pro new file mode 100644 index 0000000000..0c12104f72 --- /dev/null +++ b/Common/3dParty/harfbuzz/test/test.pro @@ -0,0 +1,19 @@ +CONFIG -= qt +TARGET = test +TEMPLATE = app +CONFIG += console +CONFIG -= app_bundle + +CORE_ROOT_DIR = $$PWD/../../../../../core +PWD_ROOT_DIR = $$PWD +include($$CORE_ROOT_DIR/Common/base.pri) + +include($$CORE_ROOT_DIR/DesktopEditor/graphics/pro/freetype.pri) +include($$CORE_ROOT_DIR/Common/3dParty/harfbuzz/harfbuzz.pri) + +SOURCES += main.cpp + +ADD_DEPENDENCY(UnicodeConverter, kernel, graphics) +DESTDIR = $$PWD/build + + From a3878b3770f6477bdd49409657843047d6ac9456 Mon Sep 17 00:00:00 2001 From: Prokhorov Kirill Date: Tue, 4 Mar 2025 16:34:29 +0300 Subject: [PATCH 06/28] Fix bug 72950 --- DesktopEditor/agg-2.4/include/agg_pixfmt_rgba.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DesktopEditor/agg-2.4/include/agg_pixfmt_rgba.h b/DesktopEditor/agg-2.4/include/agg_pixfmt_rgba.h index 50b4e28494..1365926a27 100644 --- a/DesktopEditor/agg-2.4/include/agg_pixfmt_rgba.h +++ b/DesktopEditor/agg-2.4/include/agg_pixfmt_rgba.h @@ -207,9 +207,9 @@ namespace agg } p[Order::A] = (value_type)((alpha + a) - ((alpha * a + base_mask) >> base_shift)); - p[Order::R] = (value_type)((alpha * cr + a * r - ((a * r * alpha + base_mask) >> base_shift)) / p[Order::A]); - p[Order::G] = (value_type)((alpha * cg + a * g - ((a * g * alpha + base_mask) >> base_shift)) / p[Order::A]); - p[Order::B] = (value_type)((alpha * cb + a * b - ((a * b * alpha + base_mask) >> base_shift)) / p[Order::A]); + if (r != cr) p[Order::R] = (value_type)((alpha * cr + a * r - ((a * r * alpha + base_mask) >> base_shift)) / p[Order::A]); + if (g != cg) p[Order::G] = (value_type)((alpha * cg + a * g - ((a * g * alpha + base_mask) >> base_shift)) / p[Order::A]); + if (b != cb) p[Order::B] = (value_type)((alpha * cb + a * b - ((a * b * alpha + base_mask) >> base_shift)) / p[Order::A]); } static AGG_INLINE void blend_pix_subpix(value_type* p, From ba9d3bcda5786f7c6b20c581900059d3ee20fc55 Mon Sep 17 00:00:00 2001 From: Svetlana Kulikova Date: Tue, 4 Mar 2025 19:46:43 +0300 Subject: [PATCH 07/28] Fix pdf properties exists --- PdfFile/PdfEditor.cpp | 5 +---- PdfFile/test/test.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/PdfFile/PdfEditor.cpp b/PdfFile/PdfEditor.cpp index 055d309137..ed34386b31 100644 --- a/PdfFile/PdfEditor.cpp +++ b/PdfFile/PdfEditor.cpp @@ -963,10 +963,7 @@ bool CPdfEditor::EditPage(int nPageIndex, bool bSet, bool bActualPos) { Object oRes; char* chKey2 = oTemp.dictGetKey(nIndex); - if (strcmp("Font", chKey2) == 0 || strcmp("ExtGState", chKey2) == 0 || strcmp("XObject", chKey2) == 0 || strcmp("Shading", chKey2) == 0 || strcmp("Pattern", chKey2) == 0) - oTemp.dictGetVal(nIndex, &oRes); - else - oTemp.dictGetValNF(nIndex, &oRes); + oTemp.dictGetVal(nIndex, &oRes); DictToCDictObject(&oRes, pDict, false, chKey2); oRes.free(); } diff --git a/PdfFile/test/test.cpp b/PdfFile/test/test.cpp index 49e36f3bc8..8dbdcec31a 100644 --- a/PdfFile/test/test.cpp +++ b/PdfFile/test/test.cpp @@ -372,7 +372,7 @@ TEST_F(CPdfFileTest, EditPdf) TEST_F(CPdfFileTest, EditPdfFromBase64) { - //GTEST_SKIP(); + GTEST_SKIP(); NSFonts::NSApplicationFontStream::SetGlobalMemoryStorage(NSFonts::NSApplicationFontStream::CreateDefaultGlobalMemoryStorage()); @@ -417,14 +417,14 @@ TEST_F(CPdfFileTest, EditPdfFromBase64) TEST_F(CPdfFileTest, EditPdfFromBin) { - GTEST_SKIP(); + // GTEST_SKIP(); LoadFromFile(); ASSERT_TRUE(pdfFile->EditPdf(wsDstFile)); // чтение бинарника NSFile::CFileBinary oFile; - ASSERT_TRUE(oFile.OpenFile(NSFile::GetProcessDirectory() + L"/changes0.json")); + ASSERT_TRUE(oFile.OpenFile(NSFile::GetProcessDirectory() + L"/changes.bin")); DWORD dwFileSize = oFile.GetFileSize(); BYTE* pFileContent = new BYTE[dwFileSize]; @@ -440,7 +440,7 @@ TEST_F(CPdfFileTest, EditPdfFromBin) CConvertFromBinParams* pParams = new CConvertFromBinParams(); pParams->m_sMediaDirectory = NSFile::GetProcessDirectory(); - pdfFile->AddToPdfFromBinary(pFileContent, dwReaded, pParams); + pdfFile->AddToPdfFromBinary(pFileContent + 4, dwReaded - 4, pParams); RELEASEOBJECT(pParams); RELEASEARRAYOBJECTS(pFileContent); From 631f4985329150a6b2fb3025ca9abda7c1e63b80 Mon Sep 17 00:00:00 2001 From: Svetlana Kulikova Date: Wed, 5 Mar 2025 14:12:58 +0300 Subject: [PATCH 08/28] Create OMetadata --- DesktopEditor/graphics/commands/AnnotField.cpp | 3 +++ DesktopEditor/graphics/commands/AnnotField.h | 2 ++ DesktopEditor/graphics/pro/js/wasm/js/drawingfile.js | 2 ++ .../graphics/pro/js/wasm/src/drawingfile_test.cpp | 7 +++++++ PdfFile/PdfWriter.cpp | 2 ++ PdfFile/SrcReader/PdfAnnot.cpp | 5 +++++ PdfFile/SrcReader/PdfAnnot.h | 1 + PdfFile/SrcWriter/Annotation.cpp | 10 ++++++++++ PdfFile/SrcWriter/Annotation.h | 1 + 9 files changed, 33 insertions(+) diff --git a/DesktopEditor/graphics/commands/AnnotField.cpp b/DesktopEditor/graphics/commands/AnnotField.cpp index 7569ec5dce..205928219a 100644 --- a/DesktopEditor/graphics/commands/AnnotField.cpp +++ b/DesktopEditor/graphics/commands/AnnotField.cpp @@ -685,6 +685,7 @@ const std::wstring& CAnnotFieldInfo::CWidgetAnnotPr::GetDV() { return m_wsDV; } const std::wstring& CAnnotFieldInfo::CWidgetAnnotPr::GetT() { return m_wsT; } const std::wstring& CAnnotFieldInfo::CWidgetAnnotPr::GetFontName() { return m_wsFN; } const std::wstring& CAnnotFieldInfo::CWidgetAnnotPr::GetFontKey() { return m_wsFK; } +const std::wstring& CAnnotFieldInfo::CWidgetAnnotPr::GetOMetadata() { return m_wsOMetadata; } const std::vector& CAnnotFieldInfo::CWidgetAnnotPr::GetTC() { return m_arrTC; } const std::vector& CAnnotFieldInfo::CWidgetAnnotPr::GetBC() { return m_arrBC; } const std::vector& CAnnotFieldInfo::CWidgetAnnotPr::GetBG() { return m_arrBG; } @@ -880,6 +881,8 @@ void CAnnotFieldInfo::CWidgetAnnotPr::Read(NSOnlineOfficeBinToPdf::CBufferReader m_nParentID = pReader->ReadInt(); if (nFlags & (1 << 18)) m_wsT = pReader->ReadString(); + if (nFlags & (1 << 20)) + m_wsOMetadata = pReader->ReadString(); // Action int nAction = pReader->ReadInt(); diff --git a/DesktopEditor/graphics/commands/AnnotField.h b/DesktopEditor/graphics/commands/AnnotField.h index 49f6bc3075..8a935d5d9a 100644 --- a/DesktopEditor/graphics/commands/AnnotField.h +++ b/DesktopEditor/graphics/commands/AnnotField.h @@ -164,6 +164,7 @@ public: const std::wstring& GetT(); const std::wstring& GetFontName(); const std::wstring& GetFontKey(); + const std::wstring& GetOMetadata(); const std::vector& GetTC(); const std::vector& GetBC(); const std::vector& GetBG(); @@ -193,6 +194,7 @@ public: std::wstring m_wsT; std::wstring m_wsFN; std::wstring m_wsFK; + std::wstring m_wsOMetadata; std::vector m_arrTC; std::vector m_arrBC; std::vector m_arrBG; diff --git a/DesktopEditor/graphics/pro/js/wasm/js/drawingfile.js b/DesktopEditor/graphics/pro/js/wasm/js/drawingfile.js index cf835935fb..608782cf79 100644 --- a/DesktopEditor/graphics/pro/js/wasm/js/drawingfile.js +++ b/DesktopEditor/graphics/pro/js/wasm/js/drawingfile.js @@ -654,6 +654,8 @@ CFile.prototype["getInteractiveFormsInfo"] = function() rec["name"] = reader.readString(); if (flags & (1 << 19)) rec["font"]["AP"] = reader.readString(); + if (flags & (1 << 20)) + rec["meta"] = reader.readString(); // Action let nAction = reader.readInt(); if (nAction > 0) diff --git a/DesktopEditor/graphics/pro/js/wasm/src/drawingfile_test.cpp b/DesktopEditor/graphics/pro/js/wasm/src/drawingfile_test.cpp index 05cf4a2ab8..d50befd840 100644 --- a/DesktopEditor/graphics/pro/js/wasm/src/drawingfile_test.cpp +++ b/DesktopEditor/graphics/pro/js/wasm/src/drawingfile_test.cpp @@ -542,6 +542,13 @@ void ReadInteractiveForms(BYTE* pWidgets, int& i) std::cout << "Font button " << std::string((char*)(pWidgets + i), nPathLength) << ", "; i += nPathLength; } + if (nFlags & (1 << 20)) + { + nPathLength = READ_INT(pWidgets + i); + i += 4; + std::cout << "OMetadata " << std::string((char*)(pWidgets + i), nPathLength) << ", "; + i += nPathLength; + } //Action diff --git a/PdfFile/PdfWriter.cpp b/PdfFile/PdfWriter.cpp index c16c76921e..56e4d1f8a9 100644 --- a/PdfFile/PdfWriter.cpp +++ b/PdfFile/PdfWriter.cpp @@ -2176,6 +2176,8 @@ HRESULT CPdfWriter::AddAnnotField(NSFonts::IApplicationFonts* pAppFonts, CAnnotF pWidgetAnnot->SetDV(pPr->GetDV()); if (nFlags & (1 << 18)) pWidgetAnnot->SetT(pPr->GetT()); + if (nFlags & (1 << 20)) + pWidgetAnnot->SetOMetadata(pPr->GetOMetadata()); const std::vector arrActions = pPr->GetActions(); for (CAnnotFieldInfo::CWidgetAnnotPr::CActionWidget* pAction : arrActions) diff --git a/PdfFile/SrcReader/PdfAnnot.cpp b/PdfFile/SrcReader/PdfAnnot.cpp index 7809e17d4b..5a5e5ecf8f 100644 --- a/PdfFile/SrcReader/PdfAnnot.cpp +++ b/PdfFile/SrcReader/PdfAnnot.cpp @@ -1502,6 +1502,9 @@ CAnnotWidget::CAnnotWidget(PDFDoc* pdfDoc, AcroFormField* pField) : CAnnot(pdfDo // 18 - Частичное имя поля - T m_sT = DictLookupString(&oField, "T", 18); + // 20 - OO метаданные форм - OMetadata + m_sOMetadata = DictLookupString(&oField, "OMetadata", 20); + // Action - A Object oAction; if (pField->fieldLookup("A", &oAction)->isDict()) @@ -3729,6 +3732,8 @@ void CAnnotWidget::ToWASM(NSWasm::CData& oRes) oRes.WriteString(m_sT); if (m_unFlags & (1 << 19)) oRes.WriteString(m_sButtonFontName); + if (m_unFlags & (1 << 20)) + oRes.WriteString(m_sOMetadata); oRes.AddInt(m_arrAction.size()); for (int i = 0; i < m_arrAction.size(); ++i) { diff --git a/PdfFile/SrcReader/PdfAnnot.h b/PdfFile/SrcReader/PdfAnnot.h index 9ad8d1597d..7b10bf5360 100644 --- a/PdfFile/SrcReader/PdfAnnot.h +++ b/PdfFile/SrcReader/PdfAnnot.h @@ -247,6 +247,7 @@ private: std::string m_sT; // Частичное имя поля - T std::string m_sFontKey; // Уникальный идентификатор шрифта std::string m_sFontName; // Имя шрифта - из DA + std::string m_sOMetadata; // OO метаданные формы std::string m_sActualFontName; // Имя замененного шрифта std::string m_sButtonFontName; // Имя шрифта кнопки }; diff --git a/PdfFile/SrcWriter/Annotation.cpp b/PdfFile/SrcWriter/Annotation.cpp index 2cf08c0312..eb123ae3a8 100644 --- a/PdfFile/SrcWriter/Annotation.cpp +++ b/PdfFile/SrcWriter/Annotation.cpp @@ -1628,6 +1628,16 @@ namespace PdfWriter pOwner->Add("T", new CStringObject(sValue.c_str(), true)); } } + void CWidgetAnnotation::SetOMetadata(const std::wstring& wsOMetadata) + { + std::string sValue = U_TO_UTF8(wsOMetadata); + CDictObject* pOwner = GetObjOwnValue("OMetadata"); + if (!pOwner) + { + pOwner = this; + pOwner->Add("OMetadata", new CStringObject(sValue.c_str(), true)); + } + } void CWidgetAnnotation::SetBC(const std::vector& arrBC) { CheckMK(); diff --git a/PdfFile/SrcWriter/Annotation.h b/PdfFile/SrcWriter/Annotation.h index 890249a9de..e1bb6a46ec 100644 --- a/PdfFile/SrcWriter/Annotation.h +++ b/PdfFile/SrcWriter/Annotation.h @@ -447,6 +447,7 @@ namespace PdfWriter void SetDS(const std::wstring& wsDS); void SetDV(const std::wstring& wsDV); void SetT (const std::wstring& wsT); + void SetOMetadata(const std::wstring& wsOMetadata); void SetBC(const std::vector& arrBC); void SetBG(const std::vector& arrBG); void AddAction(CAction* pAction); From d76dddeda6d05d5d1bfa2a026834b62b0b3bdcbc Mon Sep 17 00:00:00 2001 From: Oleg Korshul Date: Wed, 5 Mar 2025 17:54:47 +0300 Subject: [PATCH 09/28] Fix test --- Common/3dParty/harfbuzz/test/main.cpp | 61 ++++++++++++++++----------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/Common/3dParty/harfbuzz/test/main.cpp b/Common/3dParty/harfbuzz/test/main.cpp index 4af637053d..4d01396986 100644 --- a/Common/3dParty/harfbuzz/test/main.cpp +++ b/Common/3dParty/harfbuzz/test/main.cpp @@ -84,7 +84,7 @@ public: /* fonts */ const char *fonts_paths[NUM_EXAMPLES] = { - "C:/Windows/Fonts/arial.ttf", + "C:/Windows/Fonts/calibri.ttf", //"C:/Windows/Fonts/arial.ttf", "C:/Users/korol/AppData/Local/Microsoft/Windows/Fonts/ArabicTest.ttf", "C:/Windows/Fonts/simsun.ttc" @@ -95,7 +95,7 @@ const char *num_glyph_types[NUM_GLYPH_TYPES] = {"UNCLASSIFIED", "BASE_GLYPH", "L /* tranlations courtesy of google */ const char *texts[NUM_EXAMPLES] = { - "hello", + "fi", "لا لآ لأ لا", "懶惰的姜貓" }; @@ -107,7 +107,7 @@ const hb_direction_t text_directions[NUM_EXAMPLES] = { }; const int text_skip[NUM_EXAMPLES] = { - 1, + 0, 0, 1, }; @@ -435,6 +435,11 @@ static void print_layout_info_using_private_api(hb_blob_t *blob) /* end of private API use */ #endif +struct hb_feature_test { + hb_tag_t tag; + uint32_t value; +}; + int main(int argc, char *argv[]) { // hb_blob_t* blobFileTest = hb_blob_create_from_file("C:/Windows/Fonts/calibri.ttf"); @@ -499,28 +504,34 @@ int main(int argc, char *argv[]) hb_buffer_set_language(buf, hb_language_from_string(languages[i], strlen(languages[i]))); // hb_buffer_set_cluster_level (buf, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES); // hb_buffer_set_cluster_level (buf, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS); - hb_buffer_set_cluster_level(buf, HB_BUFFER_CLUSTER_LEVEL_CHARACTERS); + hb_buffer_set_cluster_level(buf, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES); -#if 0 -#define userfeatures_count 4 - hb_feature_t userfeatures[userfeatures_count]; - hb_tag_t tags[] = { - HB_TAG('l','i','g','a'), - HB_TAG('c','l','i','g'), - HB_TAG('d','l','i','g'), - HB_TAG('h','l','i','g'), - }; - for (int f = 0; f < userfeatures_count; f++) - { - userfeatures[f].tag = tags[f]; - userfeatures[f].value = 1; - userfeatures[f].start = HB_FEATURE_GLOBAL_START; - userfeatures[f].end = HB_FEATURE_GLOBAL_END; - } -#else -#define userfeatures_count 0 - hb_feature_t *userfeatures = NULL; -#endif + hb_feature_test features[] { + {HB_TAG('r','l','i','g'), 1}, + {HB_TAG('l','i','g','a'), 0}, + {HB_TAG('c','l','i','g'), 1}, + {HB_TAG('h','l','i','g'), 1}, + {HB_TAG('d','l','i','g'), 1}, + {HB_TAG('k','e','r','n'), 2}, + {0, 0} + }; + + int userfeatures_count = 0; + hb_feature_t userfeatures[100]; + + hb_feature_test* current_feature = features; + while (current_feature->tag != 0) + { + if (current_feature->value != 2) + { + userfeatures[userfeatures_count].tag = current_feature->tag; + userfeatures[userfeatures_count].value = current_feature->value; + userfeatures[userfeatures_count].start = HB_FEATURE_GLOBAL_START; + userfeatures[userfeatures_count].end = HB_FEATURE_GLOBAL_END; + userfeatures_count++; + } + current_feature++; + } /* Layout the text */ hb_buffer_add_utf8(buf, texts[i], strlen(texts[i]), 0, strlen(texts[i])); @@ -531,7 +542,7 @@ int main(int argc, char *argv[]) // const char*const pHbShapers[] = { "graphite2", "coretext_aat", "ot", "fallback", nullptr }; // bool ok = hb_shape_full(hb_ft_font[i], buf, userfeatures, userfeatures_count, pHbShapers); - hb_shape(hb_ft_font[i], buf, userfeatures, userfeatures_count); + hb_shape(hb_ft_font[i], buf, (userfeatures_count != 0) ? userfeatures : NULL, userfeatures_count); unsigned int glyph_count; hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(buf, &glyph_count); From d2ea9521b5d5becba5ddfecb4f28834f7053082f Mon Sep 17 00:00:00 2001 From: Oleg Korshul Date: Wed, 5 Mar 2025 18:59:59 +0300 Subject: [PATCH 10/28] Add support license --- .../doctrenderer/addon/docbuilder_addon.h | 5 +-- .../doctrenderer/app_builder/main.cpp | 36 +++++++++++++++++++ .../docbuilder.python/src/docbuilder.py | 6 ++++ DesktopEditor/doctrenderer/docbuilder_p.h | 4 +-- 4 files changed, 47 insertions(+), 4 deletions(-) diff --git a/DesktopEditor/doctrenderer/addon/docbuilder_addon.h b/DesktopEditor/doctrenderer/addon/docbuilder_addon.h index 7e5bf747ad..ae9b5f8445 100644 --- a/DesktopEditor/doctrenderer/addon/docbuilder_addon.h +++ b/DesktopEditor/doctrenderer/addon/docbuilder_addon.h @@ -33,6 +33,7 @@ #define DOC_BUILDER_ADDON_PRIVATE #include +#include "../docbuilder.h" namespace NSDoctRenderer { @@ -46,11 +47,11 @@ namespace NSDoctRenderer m_sWorkDirectory = sWorkDir; } public: - std::wstring GetX2tSaveAddon() + std::wstring GetX2tSaveAddon(NSDoctRenderer::CDocBuilder* builder, const int& filetype) { return L""; } - int GetX2tPreSaveError() + int GetX2tPreSaveError(NSDoctRenderer::CDocBuilder* builder, const int& filetype) { return 0; } diff --git a/DesktopEditor/doctrenderer/app_builder/main.cpp b/DesktopEditor/doctrenderer/app_builder/main.cpp index 0507c86a16..2b3a2ffe12 100644 --- a/DesktopEditor/doctrenderer/app_builder/main.cpp +++ b/DesktopEditor/doctrenderer/app_builder/main.cpp @@ -33,6 +33,7 @@ #include "./../common_deploy.h" #include "../docbuilder.h" #include "../../common/File.h" +#include "../../common/SystemUtils.h" #ifdef LINUX #include "../../../DesktopEditor/common/File.h" @@ -78,6 +79,13 @@ void parse_args(NSDoctRenderer::CDocBuilder* builder, int argc, char *argv[]) } } +bool CheckLicense(const std::wstring& sSrc, const std::wstring& sDst) +{ + NSFile::CFileBinary::Remove(sDst); + NSFile::CFileBinary::Copy(sSrc, sDst); + return NSFile::CFileBinary::Exists(sDst); +} + #ifdef WIN32 int wmain(int argc, wchar_t *argv[]) #else @@ -89,6 +97,7 @@ int main(int argc, char *argv[]) bool bIsHelp = false; bool bIsFonts = false; + bool bIsLicense = false; for (int i = 0; i < argc; ++i) { #ifdef WIN32 @@ -121,6 +130,33 @@ int main(int argc, char *argv[]) { bIsFonts = true; } + else if (sParam == "-register") + { + bIsLicense = true; + } + else + { + if (bIsLicense) + { + std::wstring sLicensePathSrc = UTF8_TO_U(sParam); + if (!NSFile::CFileBinary::Exists(sLicensePathSrc)) + return 1; + + std::wstring sLicensePath = NSSystemUtils::GetEnvVariable(L"ONLYOFFICE_BUILDER_LICENSE"); + if (CheckLicense(sLicensePathSrc, sLicensePath)) + return 0; + + sLicensePath = NSFile::GetProcessDirectory() + L"/license.xml"; + if (CheckLicense(sLicensePathSrc, sLicensePath)) + return 0; + + sLicensePath = NSSystemUtils::GetAppDataDir() + L"/docbuilder/license.xml"; + if (CheckLicense(sLicensePathSrc, sLicensePath)) + return 0; + + return 1; + } + } } if (bIsFonts) diff --git a/DesktopEditor/doctrenderer/docbuilder.python/src/docbuilder.py b/DesktopEditor/doctrenderer/docbuilder.python/src/docbuilder.py index ac06615386..e7973c4cab 100644 --- a/DesktopEditor/doctrenderer/docbuilder.python/src/docbuilder.py +++ b/DesktopEditor/doctrenderer/docbuilder.python/src/docbuilder.py @@ -612,4 +612,10 @@ builder_path = os.path.dirname(os.path.realpath(__file__)) _loadLibrary(builder_path) CDocBuilder.Initialize(builder_path) +def registerLibrary(license_path): + docbuilder_bin = os.path.dirname(os.path.realpath(__file__)) + os.pathsep + "docbuilder" + if ("windows" == platform.system().lower()): + docbuilder_bin += ".exe" + return subprocess.call([docbuilder_bin, license_path], stderr=subprocess.STDOUT, shell=True) + atexit.register(CDocBuilder.Dispose) diff --git a/DesktopEditor/doctrenderer/docbuilder_p.h b/DesktopEditor/doctrenderer/docbuilder_p.h index 6603297f22..d0d18f612b 100644 --- a/DesktopEditor/doctrenderer/docbuilder_p.h +++ b/DesktopEditor/doctrenderer/docbuilder_p.h @@ -1003,7 +1003,7 @@ namespace NSDoctRenderer CDocBuilderAddon oSaveAddon(m_sX2tPath); - int nPreSaveError = oSaveAddon.GetX2tPreSaveError(); + int nPreSaveError = oSaveAddon.GetX2tPreSaveError(m_pParent, m_nFileType); if (0 != nPreSaveError) return nPreSaveError; @@ -1090,7 +1090,7 @@ namespace NSDoctRenderer if (!sOptions.empty()) oBuilder.WriteString(UTF8_TO_U(sOptions)); - oBuilder.WriteString(oSaveAddon.GetX2tSaveAddon()); + oBuilder.WriteString(oSaveAddon.GetX2tSaveAddon(m_pParent, m_nFileType)); oBuilder.WriteString(L""); From e9852e812f7a1f30502c52e99eee6034ff813623 Mon Sep 17 00:00:00 2001 From: Oleg Korshul Date: Wed, 5 Mar 2025 19:13:38 +0300 Subject: [PATCH 11/28] Turn on ligatures for arabic & syriac scripts --- DesktopEditor/fontengine/TextShaper.cpp | 7 +++++++ DesktopEditor/fontengine/js/cpp/text.cpp | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/DesktopEditor/fontengine/TextShaper.cpp b/DesktopEditor/fontengine/TextShaper.cpp index 4e509d5cc8..6efcb547ed 100644 --- a/DesktopEditor/fontengine/TextShaper.cpp +++ b/DesktopEditor/fontengine/TextShaper.cpp @@ -693,6 +693,13 @@ namespace NSShaper g_userfeatures_init = true; } + // Turn on ligatures on arabic script + if (nScript == HB_SCRIPT_ARABIC || + nScript == HB_SCRIPT_SYRIAC) + { + nFeatures |= 1; + } + // font hb_font_t* pFont; if (NULL == font) diff --git a/DesktopEditor/fontengine/js/cpp/text.cpp b/DesktopEditor/fontengine/js/cpp/text.cpp index 9857242738..63503a3dea 100644 --- a/DesktopEditor/fontengine/js/cpp/text.cpp +++ b/DesktopEditor/fontengine/js/cpp/text.cpp @@ -594,6 +594,13 @@ WASM_EXPORT unsigned char* ASC_HB_ShapeText(FT_Face pFace, hb_font_t* pFont, cha g_userfeatures_init = true; } + // Turn on ligatures on arabic script + if (nScript == HB_SCRIPT_ARABIC || + nScript == HB_SCRIPT_SYRIAC) + { + nFeatures |= 1; + } + // font if (NULL == pFont) { From 6aa68f1c8141517c73495ad0134347cd7c92a4bb Mon Sep 17 00:00:00 2001 From: Oleg Korshul Date: Thu, 6 Mar 2025 15:56:30 +0300 Subject: [PATCH 12/28] Add GetPropertyInt method --- DesktopEditor/doctrenderer/docbuilder.h | 7 +++++++ DesktopEditor/doctrenderer/docbuilder_p.cpp | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/DesktopEditor/doctrenderer/docbuilder.h b/DesktopEditor/doctrenderer/docbuilder.h index 5580500cbf..bb0ac82202 100644 --- a/DesktopEditor/doctrenderer/docbuilder.h +++ b/DesktopEditor/doctrenderer/docbuilder.h @@ -475,6 +475,13 @@ namespace NSDoctRenderer */ void SetPropertyW(const wchar_t* param, const wchar_t* value); + /** + * GetProperty method. + * @param param The parameter name in the Unicode format, the value is always --argument. + * @return int value for property + */ + int GetPropertyInt(const wchar_t* param); + /** * Writes data to the log file. It is used for logs in JS code. * @param path The path to the file where all the logs will be written. diff --git a/DesktopEditor/doctrenderer/docbuilder_p.cpp b/DesktopEditor/doctrenderer/docbuilder_p.cpp index adb3c60f56..d1c7d73e63 100644 --- a/DesktopEditor/doctrenderer/docbuilder_p.cpp +++ b/DesktopEditor/doctrenderer/docbuilder_p.cpp @@ -1596,6 +1596,15 @@ namespace NSDoctRenderer return this->SetProperty(sA.c_str(), value); } + int CDocBuilder::GetPropertyInt(const wchar_t* param) + { + std::wstring sParam = std::wstring(param); + std::string sParamA = U_TO_UTF8(sParam); + if ("--save-use-only-names" == sParamA) + return m_pInternal->m_bIsServerSafeVersion ? 1 : 0; + return -1; + } + void CDocBuilder::Initialize(const wchar_t* directory) { std::wstring sDirectory = L""; From 43becd1cdb290621599de2b8c26335d95d40e1c3 Mon Sep 17 00:00:00 2001 From: Oleg Korshul Date: Fri, 7 Mar 2025 11:03:20 +0300 Subject: [PATCH 13/28] Fix registerLibrary method --- .../doctrenderer/docbuilder.python/src/docbuilder.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/DesktopEditor/doctrenderer/docbuilder.python/src/docbuilder.py b/DesktopEditor/doctrenderer/docbuilder.python/src/docbuilder.py index e7973c4cab..a09d1f4afd 100644 --- a/DesktopEditor/doctrenderer/docbuilder.python/src/docbuilder.py +++ b/DesktopEditor/doctrenderer/docbuilder.python/src/docbuilder.py @@ -2,6 +2,7 @@ import ctypes import os import platform import atexit +import subprocess OBJECT_HANDLE = ctypes.c_void_p STRING_HANDLE = ctypes.c_void_p @@ -608,14 +609,14 @@ class FileTypes: PNG = _IMAGE_MASK + 0x0005 BMP = _IMAGE_MASK + 0x0008 -builder_path = os.path.dirname(os.path.realpath(__file__)) +builder_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'lib') _loadLibrary(builder_path) CDocBuilder.Initialize(builder_path) def registerLibrary(license_path): - docbuilder_bin = os.path.dirname(os.path.realpath(__file__)) + os.pathsep + "docbuilder" + docbuilder_bin = os.path.dirname(os.path.realpath(__file__)) + "/lib/docbuilder" if ("windows" == platform.system().lower()): docbuilder_bin += ".exe" - return subprocess.call([docbuilder_bin, license_path], stderr=subprocess.STDOUT, shell=True) + return subprocess.call([docbuilder_bin, "-register", license_path], stderr=subprocess.STDOUT, shell=True) atexit.register(CDocBuilder.Dispose) From 9cde3dc699d0215e4d0bca9a04d9739a0104a396 Mon Sep 17 00:00:00 2001 From: Viktor Andreev Date: Fri, 7 Mar 2025 17:28:54 +0600 Subject: [PATCH 14/28] fix bug #73437 --- .../Logic/Biff_structures/StringPtgParser.cpp | 3 -- OOXML/Binary/Sheets/Reader/BinaryWriter.cpp | 3 ++ OOXML/Binary/Sheets/Writer/BinaryReader.cpp | 2 +- OOXML/XlsbFormat/Biff12_records/BeginSXPI.h | 10 ++-- OOXML/XlsxFormat/Pivot/Pivots.cpp | 53 +++++++++++++------ 5 files changed, 45 insertions(+), 26 deletions(-) diff --git a/MsBinaryFile/XlsFile/Format/Logic/Biff_structures/StringPtgParser.cpp b/MsBinaryFile/XlsFile/Format/Logic/Biff_structures/StringPtgParser.cpp index b6c345b5cc..fa24bcb4b8 100644 --- a/MsBinaryFile/XlsFile/Format/Logic/Biff_structures/StringPtgParser.cpp +++ b/MsBinaryFile/XlsFile/Format/Logic/Biff_structures/StringPtgParser.cpp @@ -317,9 +317,6 @@ const bool StringPtgParser::parseToPtgs(const std::wstring& assembled_formula, R [&](XLS::GlobalWorkbookInfo::_xti i) { return i.iSup == ixti; }); - if(pos->itabFirst == pos->itabLast) - rgce.addPtg(found_operand = OperandPtgPtr(new PtgRef3d(ixti, operand_str, OperandPtg::ptg_VALUE, rgce.getLocation()))); - else rgce.addPtg(found_operand = OperandPtgPtr(new PtgRef3d(ixti, operand_str, OperandPtg::ptg_REFERENCE, rgce.getLocation()))); } else if(SyntaxPtg::extract_PtgRefErr(it, itEnd)) diff --git a/OOXML/Binary/Sheets/Reader/BinaryWriter.cpp b/OOXML/Binary/Sheets/Reader/BinaryWriter.cpp index 3767dbebdd..6b6534fb16 100644 --- a/OOXML/Binary/Sheets/Reader/BinaryWriter.cpp +++ b/OOXML/Binary/Sheets/Reader/BinaryWriter.cpp @@ -2663,6 +2663,9 @@ void BinaryWorkbookTableWriter::WritePivotCache(OOX::Spreadsheet::CWorkbook& wor { nCurPos = m_oBcw.WriteItemStart(c_oSer_PivotTypes::cache); NSStringUtils::CStringBuilder writer; + writer.WriteString(L""); + if(pivotCacheFile->m_oPivotCashDefinition->m_oRid.IsInit()) + pivotCacheFile->m_oPivotCashDefinition->m_oRid.reset(); pivotCacheFile->m_oPivotCashDefinition->toXML(writer); auto wstringData = writer.GetData(); m_oBcw.m_oStream.WriteStringUtf8(wstringData); diff --git a/OOXML/Binary/Sheets/Writer/BinaryReader.cpp b/OOXML/Binary/Sheets/Writer/BinaryReader.cpp index 05a02799c8..f2b4fa8c9c 100644 --- a/OOXML/Binary/Sheets/Writer/BinaryReader.cpp +++ b/OOXML/Binary/Sheets/Writer/BinaryReader.cpp @@ -3493,7 +3493,7 @@ int BinaryWorkbookTableReader::ReadPivotCaches(BYTE type, long length, void* poR NSCommon::smart_ptr pFileRecords(oPivotCachesTemp.pRecords); srIdRecords = pDefinitionFile->Add(pFileRecords).ToString(); } - pDefinitionFile->setData(oPivotCachesTemp.pDefinitionData, oPivotCachesTemp.nDefinitionLength, L""); + pDefinitionFile->setData(oPivotCachesTemp.pDefinitionData, oPivotCachesTemp.nDefinitionLength, srIdRecords); NSCommon::smart_ptr pFile(pDefinitionFile); OOX::RId rIdDefinition = m_oWorkbook.Add(pFile); diff --git a/OOXML/XlsbFormat/Biff12_records/BeginSXPI.h b/OOXML/XlsbFormat/Biff12_records/BeginSXPI.h index f2c6ab120d..cb384c31e9 100644 --- a/OOXML/XlsbFormat/Biff12_records/BeginSXPI.h +++ b/OOXML/XlsbFormat/Biff12_records/BeginSXPI.h @@ -51,11 +51,11 @@ namespace XLSB void readFields(XLS::CFRecord& record) override; void writeFields(XLS::CFRecord& record) override; - _INT32 isxvd; - _UINT32 isxvi; - _INT32 isxth; - bool fUnique; - bool fDisplay; + _INT32 isxvd = 0; + _UINT32 isxvi = 0x001000FE; + _INT32 isxth = -1; + bool fUnique = false; + bool fDisplay = false; XLWideString irstUnique; XLWideString irstDisplay; }; diff --git a/OOXML/XlsxFormat/Pivot/Pivots.cpp b/OOXML/XlsxFormat/Pivot/Pivots.cpp index 2f5530cb29..b484f94540 100644 --- a/OOXML/XlsxFormat/Pivot/Pivots.cpp +++ b/OOXML/XlsxFormat/Pivot/Pivots.cpp @@ -1885,13 +1885,15 @@ xmlns:xr16=\"http://schemas.microsoft.com/office/spreadsheetml/2017/revision16\" if(m_oHier.IsInit()) ptr->isxth = m_oHier.get(); if(m_oName.IsInit()) + { ptr->irstUnique = m_oName.get(); - else - ptr->irstUnique = 0xFFFFFFFF; + ptr->fUnique = true; + } if(m_oCap.IsInit()) + { ptr->irstDisplay = m_oCap.get(); - else - ptr->irstDisplay = 0xFFFFFFFF; + ptr->fDisplay = true; + } return objectPtr; } void CPageField::fromBin(XLS::BaseObjectPtr& obj) @@ -4172,7 +4174,6 @@ xmlns:xr16=\"http://schemas.microsoft.com/office/spreadsheetml/2017/revision16\" writer.WriteString(L"GetValue()); WritingStringNullableAttrInt(L"mappingCount", m_oMappingCount, m_oMappingCount->GetValue()); WritingStringNullableAttrInt(L"numFmtId", m_oNumFmtId, m_oNumFmtId->GetValue()); + WritingStringNullableAttrBool2(L"databaseField", m_oDatabaseField); if(!m_oSharedItems.IsInit() && !m_oFieldGroup.IsInit()) { writer.WriteString(L"/>"); @@ -4406,22 +4408,29 @@ xmlns:xr16=\"http://schemas.microsoft.com/office/spreadsheetml/2017/revision16\" void CSharedItems::toXML(NSStringUtils::CStringBuilder& writer) const { writer.WriteString(L"ToString()); WritingStringNullableAttrString(L"maxDate", m_oMaxDate, m_oMaxDate->ToString()); - writer.WriteString(L">"); - + if(!m_arrItems.empty()) + { + WritingStringAttrInt(L"count", (int)m_arrItems.size()); + } + WritingStringNullableAttrBool2(L"longText", m_oLongText); + if(m_arrItems.empty()) + { + writer.WriteString(L"/>"); + return; + } + writer.WriteString(L">"); for ( size_t i = 0; i < m_arrItems.size(); ++i) { if ( m_arrItems[i] ) @@ -5127,7 +5136,7 @@ xmlns:xr16=\"http://schemas.microsoft.com/office/spreadsheetml/2017/revision16\" //------------------------------------ void CRangeGroupingProperties::toXML(NSStringUtils::CStringBuilder& writer) const { - writer.WriteString(L"ToString()); WritingStringNullableAttrBool2(L"autoStart", m_oAutoStart); WritingStringNullableAttrBool2(L"autoEnd", m_oAutoEnd); @@ -5835,6 +5844,11 @@ xmlns:xr16=\"http://schemas.microsoft.com/office/spreadsheetml/2017/revision16\" } WritingStringNullableAttrBool2(L"f", m_oCalculated); WritingStringNullableAttrBool2(L"u", m_oUnused); + if(m_arrItems.empty()) + { + writer.WriteString(L"/>"); + return; + } writer.WriteString(L">"); for ( size_t i = 0; i < m_arrItems.size(); ++i) @@ -5967,6 +5981,11 @@ xmlns:xr16=\"http://schemas.microsoft.com/office/spreadsheetml/2017/revision16\" } WritingStringNullableAttrBool2(L"f", m_oCalculated); WritingStringNullableAttrBool2(L"u", m_oUnused); + if(m_arrItems.empty()) + { + writer.WriteString(L"/>"); + return; + } writer.WriteString(L">"); for ( size_t i = 0; i < m_arrItems.size(); ++i) @@ -6864,8 +6883,8 @@ xmlns:xr16=\"http://schemas.microsoft.com/office/spreadsheetml/2017/revision16\" void CFieldGroupProperties::toXML(NSStringUtils::CStringBuilder& writer) const { writer.WriteString(L"GetValue()); WritingStringNullableAttrInt(L"par", m_oPar, m_oPar->GetValue()); + WritingStringNullableAttrInt(L"base", m_oBase, m_oBase->GetValue()); writer.WriteString(L">"); if(m_oDiscretePr.IsInit()) From e1274dadeaf1590d8398e2c73ef0691e8f3dfd47 Mon Sep 17 00:00:00 2001 From: ElenaSubbotina Date: Sat, 8 Mar 2025 08:36:37 +0300 Subject: [PATCH 15/28] . --- OOXML/PPTXFormat/DrawingConverter/ASCOfficeDrawingConverter.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/OOXML/PPTXFormat/DrawingConverter/ASCOfficeDrawingConverter.cpp b/OOXML/PPTXFormat/DrawingConverter/ASCOfficeDrawingConverter.cpp index a65dc81845..3d46d556a3 100644 --- a/OOXML/PPTXFormat/DrawingConverter/ASCOfficeDrawingConverter.cpp +++ b/OOXML/PPTXFormat/DrawingConverter/ASCOfficeDrawingConverter.cpp @@ -2592,6 +2592,8 @@ void CDrawingConverter::ConvertShape(PPTX::Logic::SpTreeElem *elem, XmlUtils::CX if (bPicture && false == pPPTShape->m_oSignatureLine.IsInit()) { + bStroked = false; + pPicture = new PPTX::Logic::Pic(); elem->InitElem(pPicture); From ac2562f83e718a00743873c21fe116253ad32caf Mon Sep 17 00:00:00 2001 From: Viktor Andreev Date: Mon, 10 Mar 2025 14:32:00 +0600 Subject: [PATCH 16/28] fix bug #73466 --- OOXML/XlsxFormat/Worksheets/SheetData.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/OOXML/XlsxFormat/Worksheets/SheetData.cpp b/OOXML/XlsxFormat/Worksheets/SheetData.cpp index a0f84fefce..6bb8be4261 100644 --- a/OOXML/XlsxFormat/Worksheets/SheetData.cpp +++ b/OOXML/XlsxFormat/Worksheets/SheetData.cpp @@ -2912,6 +2912,8 @@ namespace OOX XLSB::XLWideString str; if(m_oValue.IsInit()) str = m_oValue->m_sText; + else + str = L""; *CellRecord << str; } else From 80ed0318b60d9039896295f1350e180a928819f5 Mon Sep 17 00:00:00 2001 From: Viktor Andreev Date: Tue, 11 Mar 2025 18:34:32 +0600 Subject: [PATCH 17/28] fix bug #73433 --- .../Format/Logic/Biff_records/FileSharing.h | 2 +- OOXML/XlsxFormat/Workbook/Workbook.cpp | 14 +++++++-- OOXML/XlsxFormat/Workbook/WorkbookPr.cpp | 19 ++++++++---- OOXML/XlsxFormat/Workbook/WorkbookPr.h | 2 +- OOXML/XlsxFormat/Worksheets/SheetData.cpp | 27 ++++++++++------ .../Worksheets/WorksheetChildOther.cpp | 31 ++++++++++++++----- 6 files changed, 69 insertions(+), 26 deletions(-) diff --git a/MsBinaryFile/XlsFile/Format/Logic/Biff_records/FileSharing.h b/MsBinaryFile/XlsFile/Format/Logic/Biff_records/FileSharing.h index 080f5a6d5d..0da5c89195 100644 --- a/MsBinaryFile/XlsFile/Format/Logic/Biff_records/FileSharing.h +++ b/MsBinaryFile/XlsFile/Format/Logic/Biff_records/FileSharing.h @@ -56,7 +56,7 @@ public: static const ElementType type = typeFileSharing; Boolean fReadOnlyRec; - unsigned short wResPassNum; + unsigned short wResPassNum = 0; std::wstring wResPass; _UINT16 iNoResPass; XLUnicodeString stUNUsername; diff --git a/OOXML/XlsxFormat/Workbook/Workbook.cpp b/OOXML/XlsxFormat/Workbook/Workbook.cpp index 38179fbd5e..e02ba8919d 100644 --- a/OOXML/XlsxFormat/Workbook/Workbook.cpp +++ b/OOXML/XlsxFormat/Workbook/Workbook.cpp @@ -427,8 +427,18 @@ namespace OOX workBookStream->m_FRTWORKBOOK = m_oExtLst->toBinWorkBook(); if (m_oFileSharing.IsInit()) - workBookStream->m_BrtFileSharing = m_oFileSharing->toBin(); - + { + auto sharingVector = m_oFileSharing->toBin(); + if(sharingVector.size() == 2) + { + workBookStream->m_BrtFileSharingIso = sharingVector.at(0); + workBookStream->m_BrtFileSharing = sharingVector.at(1); + } + else if(sharingVector.size() == 1) + { + workBookStream->m_BrtFileSharing = sharingVector.at(0); + } + } return workBookStream; } void CWorkbook::read(const CPath& oPath) diff --git a/OOXML/XlsxFormat/Workbook/WorkbookPr.cpp b/OOXML/XlsxFormat/Workbook/WorkbookPr.cpp index 0b71a56d26..729b85fdad 100644 --- a/OOXML/XlsxFormat/Workbook/WorkbookPr.cpp +++ b/OOXML/XlsxFormat/Workbook/WorkbookPr.cpp @@ -1,4 +1,4 @@ -/* +/* * (c) Copyright Ascensio System SIA 2010-2023 * * This program is a free software product. You can redistribute it and/or @@ -428,13 +428,13 @@ namespace OOX if (!oReader.IsEmptyNode()) oReader.ReadTillEnd(); } - XLS::BaseObjectPtr CFileSharing::toBin() + std::vector CFileSharing::toBin() { - XLS::BaseObjectPtr objectPtr; + std::vector objectVector; if(m_oSpinCount.IsInit() || m_oAlgorithmName.IsInit() || m_oHashValue.IsInit()) { auto ptr(new XLSB::FileSharingIso); - objectPtr = XLS::BaseObjectPtr{ptr}; + XLS::BaseObjectPtr objectPtr = XLS::BaseObjectPtr{ptr}; if (m_oReadOnlyRecommended.IsInit()) ptr->fReadOnlyRec = m_oReadOnlyRecommended.get(); @@ -472,12 +472,18 @@ namespace OOX bytes1, len1); } ptr->ipdPasswordData.rgbSalt.cbLength = len1; + auto ptr1(new XLSB::FileSharing); + ptr1->wResPass = L""; + ptr1->fReadOnlyRec = ptr->fReadOnlyRec; + ptr1->stUserName = ptr->stUserName; + objectVector.push_back(objectPtr); + objectVector.push_back(XLS::BaseObjectPtr{ptr1}); } else { auto ptr(new XLSB::FileSharing); - objectPtr = XLS::BaseObjectPtr{ptr}; + XLS::BaseObjectPtr objectPtr = XLS::BaseObjectPtr{ptr}; if (m_oReadOnlyRecommended.IsInit()) ptr->fReadOnlyRec = m_oReadOnlyRecommended.get(); else @@ -490,8 +496,9 @@ namespace OOX ptr->wResPass = m_oPassword.get(); else ptr->wResPass = L""; + objectVector.push_back(objectPtr); } - return objectPtr; + return objectVector; } void CFileSharing::fromBin(XLS::BaseObjectPtr& obj) { diff --git a/OOXML/XlsxFormat/Workbook/WorkbookPr.h b/OOXML/XlsxFormat/Workbook/WorkbookPr.h index 3f32c987bf..77de4016ec 100644 --- a/OOXML/XlsxFormat/Workbook/WorkbookPr.h +++ b/OOXML/XlsxFormat/Workbook/WorkbookPr.h @@ -148,7 +148,7 @@ namespace OOX virtual void fromXML(XmlUtils::CXmlLiteReader& oReader); void fromBin(XLS::BaseObjectPtr& obj); - XLS::BaseObjectPtr toBin(); + std::vector toBin(); virtual EElementType getType() const; void ReadAttributes(XmlUtils::CXmlLiteReader& oReader); diff --git a/OOXML/XlsxFormat/Worksheets/SheetData.cpp b/OOXML/XlsxFormat/Worksheets/SheetData.cpp index 6bb8be4261..073a508084 100644 --- a/OOXML/XlsxFormat/Worksheets/SheetData.cpp +++ b/OOXML/XlsxFormat/Worksheets/SheetData.cpp @@ -1130,7 +1130,7 @@ namespace OOX } namespace SharedFormulasRef { - std::unique_ptr> sharedRefsLocations; + std::unique_ptr> sharedRefsLocations; std::unique_ptr>> ArrayRefsLocations; } void CFormula::fromBin(XLS::StreamCacheReaderPtr& reader, XLS::CFRecordPtr& record) @@ -1157,9 +1157,10 @@ namespace OOX rowRef = static_cast(BinFmla.rgce.sequence.begin()->get())->rowXlsb; ColumnRef = static_cast(BinFmla.rgcb.getPtgs().back().get())->col; if(!SharedFormulasRef::sharedRefsLocations) - SharedFormulasRef::sharedRefsLocations = std::unique_ptr>(new std::vector); - SharedFormulasRef::sharedRefsLocations->push_back(XLS::CellRef(rowRef, ColumnRef, true, true)); + SharedFormulasRef::sharedRefsLocations = std::unique_ptr>(new std::map<_UINT32, XLS::CellRef>); m_oSi = (unsigned int)SharedFormulasRef::sharedRefsLocations->size() - 1; + SharedFormulasRef::sharedRefsLocations->emplace(m_oSi->GetValue(), XLS::CellRef(rowRef, ColumnRef, true, true)); + } } auto fmlaRecord = reader->getNextRecord(XLSB::rt_ShrFmla); @@ -2926,7 +2927,7 @@ namespace OOX case SimpleTypes::Spreadsheet::celltypeInlineStr: case SimpleTypes::Spreadsheet::celltypeStr: { - if(m_oValue.IsInit() || m_oRichText.IsInit()) + if(m_oValue.IsInit() || m_oRichText.IsInit() || m_oFormula.IsInit()) { if(!m_oFormula.IsInit()) CellRecord = writer->getNextRecord(XLSB::rt_CellSt); @@ -2936,8 +2937,10 @@ namespace OOX XLSB::XLWideString str; if(m_oValue.IsInit()) str = m_oValue->m_sText; - else + else if(m_oRichText.IsInit()) str = m_oRichText->ToString(); + else + str = L""; *CellRecord << str; } else @@ -2958,15 +2961,19 @@ namespace OOX else if(m_oFormula->m_oT.get() == SimpleTypes::Spreadsheet::ECellFormulaType::cellformulatypeShared) { if(!SharedFormulasRef::sharedRefsLocations) - SharedFormulasRef::sharedRefsLocations = std::unique_ptr>(new std::vector); + SharedFormulasRef::sharedRefsLocations = std::unique_ptr>(new std::map<_UINT32, XLS::CellRef>); if(!m_oFormula->m_sText.empty()) { ExtraRecord = writer->getNextRecord(XLSB::rt_ShrFmla); - SharedFormulasRef::sharedRefsLocations->push_back(CellReference); + if(!m_oFormula->m_oSi.IsInit()) + m_oFormula->m_oSi = (_UINT32)SharedFormulasRef::sharedRefsLocations->size(); + SharedFormulasRef::sharedRefsLocations->emplace(m_oFormula->m_oSi->GetValue(), CellReference); + SharedFmlaRef = &CellReference; } - if(m_oFormula->m_oSi.IsInit() && m_oFormula->m_oSi->GetValue() < SharedFormulasRef::sharedRefsLocations->size()) + if(m_oFormula->m_oSi.IsInit() && SharedFormulasRef::sharedRefsLocations->find(m_oFormula->m_oSi->GetValue()) != SharedFormulasRef::sharedRefsLocations->end()) { - SharedFmlaRef = &SharedFormulasRef::sharedRefsLocations->at(m_oFormula->m_oSi->GetValue()); + if(m_oFormula->m_sText.empty()) + SharedFmlaRef = &SharedFormulasRef::sharedRefsLocations->at(m_oFormula->m_oSi->GetValue()); XLS::CellParsedFormula BinFmla(false); //пишем флаги для формулы @@ -4727,6 +4734,8 @@ namespace OOX } if(SharedFormulasRef::sharedRefsLocations) SharedFormulasRef::sharedRefsLocations.reset(); + if(SharedFormulasRef::ArrayRefsLocations) + SharedFormulasRef::ArrayRefsLocations.reset(); } XLS::BaseObjectPtr CSheetData::toBin() { diff --git a/OOXML/XlsxFormat/Worksheets/WorksheetChildOther.cpp b/OOXML/XlsxFormat/Worksheets/WorksheetChildOther.cpp index 2a01c9e9d5..1de30226d1 100644 --- a/OOXML/XlsxFormat/Worksheets/WorksheetChildOther.cpp +++ b/OOXML/XlsxFormat/Worksheets/WorksheetChildOther.cpp @@ -2812,32 +2812,49 @@ namespace OOX if(m_oOddHeader.IsInit()) dataString = m_oOddHeader->m_sText; else - dataString = L""; + dataString.setSize(0xFFFFFFFF); *begin << dataString; + } + { + XLSB::XLNullableWideString dataString; if(m_oOddFooter.IsInit()) dataString = m_oOddFooter->m_sText; else - dataString = L""; + dataString.setSize(0xFFFFFFFF); *begin << dataString; + } + { + XLSB::XLNullableWideString dataString; + if(m_oEvenHeader.IsInit()) dataString = m_oEvenHeader->m_sText; else - dataString = L""; + dataString.setSize(0xFFFFFFFF); *begin << dataString; + } + { + XLSB::XLNullableWideString dataString; if(m_oEvenFooter.IsInit()) dataString = m_oEvenFooter->m_sText; else - dataString = L""; - *begin << dataString; + dataString.setSize(0xFFFFFFFF); + *begin << dataString; + } + { + XLSB::XLNullableWideString dataString; + if(m_oFirstHeader.IsInit()) dataString = m_oFirstHeader->m_sText; else - dataString = L""; + dataString.setSize(0xFFFFFFFF); *begin << dataString; + } + { + XLSB::XLNullableWideString dataString; if(m_oFirstFooter.IsInit()) dataString = m_oFirstFooter->m_sText; else - dataString = L""; + dataString.setSize(0xFFFFFFFF); *begin << dataString; } writer->storeNextRecord(begin); From c0222c03db36897453b7290ae6a446b5bfdecd0c Mon Sep 17 00:00:00 2001 From: Green Date: Tue, 11 Mar 2025 17:32:14 +0300 Subject: [PATCH 18/28] Fix bug #73485 --- Common/3dParty/html/fetch.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Common/3dParty/html/fetch.py b/Common/3dParty/html/fetch.py index 4a0d3f5068..4107bfc2d1 100644 --- a/Common/3dParty/html/fetch.py +++ b/Common/3dParty/html/fetch.py @@ -25,3 +25,5 @@ if not base.is_dir("katana-parser"): base.replaceInFileUtf8(base_directory + "/katana-parser/src/tokenizer.c", "static inline bool2 katana_is_html_space(char c);", "static inline bool katana_is_html_space(char c);") base.replaceInFileUtf8(base_directory + "/katana-parser/src/parser.c", "katanaget_text(parser->scanner)", "/*katanaget_text(parser->scanner)*/\"error\"") base.replaceInFileUtf8(base_directory + "/katana-parser/src/parser.c", "#define KATANA_PARSER_STRING(literal) (KatanaParserString){", "#define KATANA_PARSER_STRING(literal) {") + # katana may not be able to handle an empty string correctly in some cases (bug#73485) + base.replaceInFileUtf8(base_directory + "/katana-parser/src/foundation.c", "size_t len = strlen(str);", "if (NULL == str)\n return;\n size_t len = strlen(str);") From 20a4bcf7083633073ec22635425b181dcafa8d64 Mon Sep 17 00:00:00 2001 From: Green Date: Tue, 11 Mar 2025 17:32:45 +0300 Subject: [PATCH 19/28] Fix bug #73486 --- Common/3dParty/html/htmltoxhtml.h | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/Common/3dParty/html/htmltoxhtml.h b/Common/3dParty/html/htmltoxhtml.h index 3fdf63a5d1..68a505e59b 100644 --- a/Common/3dParty/html/htmltoxhtml.h +++ b/Common/3dParty/html/htmltoxhtml.h @@ -19,7 +19,6 @@ static std::string nonbreaking_inline = "|a|abbr|acronym|b|bdo|big|cite|code|df static std::string empty_tags = "|area|base|basefont|bgsound|br|command|col|embed|event-source|frame|hr|image|img|input|keygen|link|menuitem|meta|param|source|spacer|track|wbr|"; static std::string preserve_whitespace = "|pre|textarea|script|style|"; static std::string special_handling = "|html|body|"; -static std::string no_entity_sub = ""; //"|style|"; static std::string treat_like_inline = "|p|"; static std::vector html_tags = {"div","span","a","img","p","h1","h2","h3","h4","h5","h6", @@ -436,9 +435,25 @@ static void substitute_xml_entities_into_text(std::string& text) replace_all(text, ">", ">"); } +// After running through Gumbo, the values of type "" are replaced with the corresponding code '0x01' +// Since the attribute value does not use control characters (value <= 0x1F), +// then just delete them, otherwise XmlUtils::CXmlLiteReader crashes on them. +// bug#73486 +static void remove_control_symbols(std::string& text) +{ + std::string::iterator itFound = std::find_if(text.begin(), text.end(), [](char chValue){ return chValue <= 0x1F; }); + + while (itFound != text.end()) + { + itFound = text.erase(itFound); + itFound = std::find_if(itFound, text.end(), [](char chValue){ return chValue <= 0x1F; }); + } +} + // Заменяет сущности " в text static void substitute_xml_entities_into_attributes(std::string& text) { + remove_control_symbols(text); substitute_xml_entities_into_text(text); replace_all(text, "\"", """); } @@ -486,7 +501,7 @@ static void build_doctype(GumboNode* node, NSStringUtils::CStringBuilderA& oBuil } } -static void build_attributes(const GumboVector* attribs, bool no_entities, NSStringUtils::CStringBuilderA& atts) +static void build_attributes(const GumboVector* attribs, NSStringUtils::CStringBuilderA& atts) { std::vector arrRepeat; for (size_t i = 0; i < attribs->length; ++i) @@ -532,8 +547,7 @@ static void build_attributes(const GumboVector* attribs, bool no_entities, NSStr std::string qs ="\""; atts.WriteString("="); atts.WriteString(qs); - if(!no_entities) - substitute_xml_entities_into_attributes(sVal); + substitute_xml_entities_into_attributes(sVal); atts.WriteString(sVal); atts.WriteString(qs); } @@ -542,7 +556,6 @@ static void build_attributes(const GumboVector* attribs, bool no_entities, NSStr static void prettyprint_contents(GumboNode* node, NSStringUtils::CStringBuilderA& contents, bool bCheckValidNode) { std::string key = "|" + get_tag_name(node) + "|"; - bool no_entity_substitution = no_entity_sub.find(key) != std::string::npos; bool keep_whitespace = preserve_whitespace.find(key) != std::string::npos; bool is_inline = nonbreaking_inline.find(key) != std::string::npos; bool is_like_inline = treat_like_inline.find(key) != std::string::npos; @@ -556,8 +569,7 @@ static void prettyprint_contents(GumboNode* node, NSStringUtils::CStringBuilderA if (child->type == GUMBO_NODE_TEXT) { std::string val(child->v.text.text); - if(!no_entity_substitution) - substitute_xml_entities_into_text(val); + substitute_xml_entities_into_text(val); // Избавление от FF size_t found = val.find_first_of("\014"); @@ -613,7 +625,6 @@ static void prettyprint(GumboNode* node, NSStringUtils::CStringBuilderA& oBuilde std::string closeTag = ""; std::string key = "|" + tagname + "|"; bool is_empty_tag = empty_tags.find(key) != std::string::npos; - bool no_entity_substitution = no_entity_sub.find(key) != std::string::npos; // determine closing tag type if (is_empty_tag) @@ -626,7 +637,7 @@ static void prettyprint(GumboNode* node, NSStringUtils::CStringBuilderA& oBuilde // build attr string const GumboVector* attribs = &node->v.element.attributes; - build_attributes(attribs, no_entity_substitution, oBuilder); + build_attributes(attribs, oBuilder); oBuilder.WriteString(close + ">"); // prettyprint your contents From 18b8e3ed7c9d4ba02297463af899daf30ed7f3c3 Mon Sep 17 00:00:00 2001 From: Alexey Nagaev Date: Tue, 11 Mar 2025 20:12:42 +0300 Subject: [PATCH 20/28] Fix bug 73484 --- Test/Applications/x2tTester/x2tTester.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Test/Applications/x2tTester/x2tTester.cpp b/Test/Applications/x2tTester/x2tTester.cpp index 50fd098819..0b07b91a72 100644 --- a/Test/Applications/x2tTester/x2tTester.cpp +++ b/Test/Applications/x2tTester/x2tTester.cpp @@ -194,6 +194,7 @@ CFormatsList CFormatsList::GetDefaultExts() list.m_documents.push_back(L"odt"); list.m_documents.push_back(L"ott"); list.m_documents.push_back(L"oxps"); + list.m_documents.push_back(L"pages"); list.m_documents.push_back(L"rtf"); list.m_documents.push_back(L"stw"); list.m_documents.push_back(L"sxw"); @@ -205,6 +206,7 @@ CFormatsList CFormatsList::GetDefaultExts() list.m_presentations.push_back(L"dps"); list.m_presentations.push_back(L"dpt"); list.m_presentations.push_back(L"fodp"); + list.m_presentations.push_back(L"key"); list.m_presentations.push_back(L"odp"); list.m_presentations.push_back(L"otp"); list.m_presentations.push_back(L"pot"); @@ -223,6 +225,7 @@ CFormatsList CFormatsList::GetDefaultExts() list.m_spreadsheets.push_back(L"et"); list.m_spreadsheets.push_back(L"ett"); list.m_spreadsheets.push_back(L"fods"); + list.m_spreadsheets.push_back(L"numbers"); list.m_spreadsheets.push_back(L"ods"); list.m_spreadsheets.push_back(L"ots"); list.m_spreadsheets.push_back(L"sxc"); From 6e9500a6e71dbb692b94e040e83c2e9758a24592 Mon Sep 17 00:00:00 2001 From: Svetlana Kulikova Date: Wed, 12 Mar 2025 16:18:35 +0300 Subject: [PATCH 21/28] Fix bug 73493 --- PdfFile/SrcReader/RendererOutputDev.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/PdfFile/SrcReader/RendererOutputDev.cpp b/PdfFile/SrcReader/RendererOutputDev.cpp index 4109ae0b24..d6e9ec2e9d 100644 --- a/PdfFile/SrcReader/RendererOutputDev.cpp +++ b/PdfFile/SrcReader/RendererOutputDev.cpp @@ -474,6 +474,13 @@ namespace PdfReader void RendererOutputDev::restoreState(GfxState* pGState) { RELEASEINTERFACE(m_pSoftMask); + if (m_sStates.empty()) + { // Несбалансированный q/Q - сломанный файл + updateAll(pGState); + UpdateAllClip(pGState); + return; + } + m_pSoftMask = m_sStates.back().pSoftMask; if (c_nGrRenderer == m_lRendererType) { From 89d965283b1fb58380b7602a70b51ed109a35ebe Mon Sep 17 00:00:00 2001 From: Svetlana Kulikova Date: Wed, 12 Mar 2025 16:23:09 +0300 Subject: [PATCH 22/28] Fix command with empty states --- PdfFile/SrcReader/RendererOutputDev.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/PdfFile/SrcReader/RendererOutputDev.cpp b/PdfFile/SrcReader/RendererOutputDev.cpp index d6e9ec2e9d..a6fced6987 100644 --- a/PdfFile/SrcReader/RendererOutputDev.cpp +++ b/PdfFile/SrcReader/RendererOutputDev.cpp @@ -2175,7 +2175,7 @@ namespace PdfReader } void RendererOutputDev::clip(GfxState* pGState) { - if (m_bDrawOnlyText) + if (m_bDrawOnlyText || m_sStates.empty()) return; if (!m_sStates.back().pClip) @@ -2186,7 +2186,7 @@ namespace PdfReader } void RendererOutputDev::eoClip(GfxState* pGState) { - if (m_bDrawOnlyText) + if (m_bDrawOnlyText || m_sStates.empty()) return; if (!m_sStates.back().pClip) @@ -2197,7 +2197,7 @@ namespace PdfReader } void RendererOutputDev::clipToStrokePath(GfxState* pGState) { - if (m_bDrawOnlyText) + if (m_bDrawOnlyText || m_sStates.empty()) return; if (!m_sStates.back().pClip) @@ -2251,7 +2251,7 @@ namespace PdfReader } void RendererOutputDev::endTextObject(GfxState* pGState) { - if (m_sStates.back().pTextClip && 4 <= pGState->getRender()) + if (!m_sStates.empty() && m_sStates.back().pTextClip && 4 <= pGState->getRender()) { AddTextClip(pGState, &m_sStates.back()); updateFont(pGState); @@ -2591,9 +2591,13 @@ namespace PdfReader m_pRenderer->get_FontSize(&dTempFontSize); m_pRenderer->get_FontStyle(&lTempFontStyle); // tmpchange - if (!m_sStates.back().pTextClip) - m_sStates.back().pTextClip = new GfxTextClip(); - m_sStates.back().pTextClip->ClipToText(wsTempFontName, wsTempFontPath, dTempFontSize, (int)lTempFontStyle, arrMatrix, wsClipText, dShiftX, /*-fabs(pFont->getFontBBox()[3]) * dTfs + */ dShiftY, 0, 0, 0); + if (!m_sStates.empty()) + { + if (!m_sStates.back().pTextClip) + m_sStates.back().pTextClip = new GfxTextClip(); + m_sStates.back().pTextClip->ClipToText(wsTempFontName, wsTempFontPath, dTempFontSize, (int)lTempFontStyle, arrMatrix, wsClipText, dShiftX, /*-fabs(pFont->getFontBBox()[3]) * dTfs + */ dShiftY, 0, 0, 0); + + } } m_pRenderer->put_FontSize(dOldSize); From 561221102e9e5a08e6ca68c9e834fad25f75652c Mon Sep 17 00:00:00 2001 From: ElenaSubbotina Date: Wed, 12 Mar 2025 16:59:27 +0300 Subject: [PATCH 23/28] fix bug #73509 --- .../Format/Logic/Biff_structures/ODRAW/OfficeArtContainer.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MsBinaryFile/XlsFile/Format/Logic/Biff_structures/ODRAW/OfficeArtContainer.cpp b/MsBinaryFile/XlsFile/Format/Logic/Biff_structures/ODRAW/OfficeArtContainer.cpp index 89b9640792..dd116de631 100644 --- a/MsBinaryFile/XlsFile/Format/Logic/Biff_structures/ODRAW/OfficeArtContainer.cpp +++ b/MsBinaryFile/XlsFile/Format/Logic/Biff_structures/ODRAW/OfficeArtContainer.cpp @@ -82,6 +82,7 @@ OfficeArtRecordPtr OfficeArtContainer::CreateOfficeArt(unsigned short type) case FSP: art_record = OfficeArtRecordPtr(new OfficeArtFSP); break; case FOPT: + case SecondaryFOPT: art_record = OfficeArtRecordPtr(new OfficeArtFOPT); break; case ChildAnchor: art_record = OfficeArtRecordPtr(new OfficeArtChildAnchor); break; @@ -116,8 +117,6 @@ OfficeArtRecordPtr OfficeArtContainer::CreateOfficeArt(unsigned short type) art_record = OfficeArtRecordPtr(new OfficeArtBStoreContainer); break; case TertiaryFOPT: art_record = OfficeArtRecordPtr(new OfficeArtTertiaryFOPT); break; - case SecondaryFOPT: - case FPSPL: case FDGSL: case FBSE: From c9e3c3452f4f420739d2365fac711a7205bb0f8e Mon Sep 17 00:00:00 2001 From: ElenaSubbotina Date: Wed, 12 Mar 2025 17:24:35 +0300 Subject: [PATCH 24/28] fix bug #73500 --- OOXML/Binary/Document/BinWriter/BinWriters.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/OOXML/Binary/Document/BinWriter/BinWriters.cpp b/OOXML/Binary/Document/BinWriter/BinWriters.cpp index 85aeee134f..8c6dc07ebe 100644 --- a/OOXML/Binary/Document/BinWriter/BinWriters.cpp +++ b/OOXML/Binary/Document/BinWriter/BinWriters.cpp @@ -5110,16 +5110,16 @@ void BinaryDocumentTableWriter::WriteMathRunContent(OOX::Logic::CMRun* pMRun) int nBreakType = -1; switch (pBr->m_oType.GetValue()) { - case SimpleTypes::brtypeColumn: nBreakType = c_oSerRunType::columnbreak; break; - case SimpleTypes::brtypePage: nBreakType = c_oSerRunType::pagebreak; break; + case SimpleTypes::brtypeColumn: nBreakType = c_oSer_OMathContentType::columnbreak; break; + case SimpleTypes::brtypePage: nBreakType = c_oSer_OMathContentType::pagebreak; break; case SimpleTypes::brtypeTextWrapping: { switch (pBr->m_oClear.GetValue()) { - case SimpleTypes::brclearAll: nBreakType = c_oSerRunType::linebreakClearAll; break; - case SimpleTypes::brclearLeft: nBreakType = c_oSerRunType::linebreakClearLeft; break; - case SimpleTypes::brclearRight: nBreakType = c_oSerRunType::linebreakClearRight; break; - default: nBreakType = c_oSerRunType::linebreak; break; + //case SimpleTypes::brclearAll: nBreakType = c_oSer_OMathContentType::linebreakClearAll; break; + //case SimpleTypes::brclearLeft: nBreakType = c_oSer_OMathContentType::linebreakClearLeft; break; + //case SimpleTypes::brclearRight: nBreakType = c_oSer_OMathContentType::linebreakClearRight; break; + default: nBreakType = c_oSer_OMathContentType::linebreak; break; } }break; From 6e6a71e46dcf2ce29eb1c13daf27783c9dbde0ec Mon Sep 17 00:00:00 2001 From: Green Date: Wed, 12 Mar 2025 18:01:36 +0300 Subject: [PATCH 25/28] Fix bug #73496 --- DesktopEditor/raster/Metafile/Emf/EmfParser/CEmfPlusParser.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/DesktopEditor/raster/Metafile/Emf/EmfParser/CEmfPlusParser.cpp b/DesktopEditor/raster/Metafile/Emf/EmfParser/CEmfPlusParser.cpp index 5ae466d3c1..8fa6c9560b 100644 --- a/DesktopEditor/raster/Metafile/Emf/EmfParser/CEmfPlusParser.cpp +++ b/DesktopEditor/raster/Metafile/Emf/EmfParser/CEmfPlusParser.cpp @@ -645,6 +645,9 @@ namespace MetaFile //TODO::реализовать при встрече } + if (BrushDataTransform & unBrushDataFlags) + m_oStream.Skip(24); + if (BrushDataPresetColors & unBrushDataFlags) { unsigned int unPositionCount; From 3b67e4ee0ceb598d606b77b9af3879f2368dee37 Mon Sep 17 00:00:00 2001 From: ElenaSubbotina Date: Thu, 13 Mar 2025 10:55:16 +0300 Subject: [PATCH 26/28] fix bug #73494 --- MsBinaryFile/PptFile/PPTXWriter/Converter.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/MsBinaryFile/PptFile/PPTXWriter/Converter.cpp b/MsBinaryFile/PptFile/PPTXWriter/Converter.cpp index 943564f3c6..baa6bb3eb4 100644 --- a/MsBinaryFile/PptFile/PPTXWriter/Converter.cpp +++ b/MsBinaryFile/PptFile/PPTXWriter/Converter.cpp @@ -619,6 +619,7 @@ namespace PPT WriteSlides(); WriteNotes(); + m_pShapeWriter->SetRelsGenerator(NULL); } // todo reforming and refactoring! @@ -1544,6 +1545,8 @@ namespace PPT oRels.StartSlide(nLayout, pSlide->m_lNotesID); } + + m_pShapeWriter->SetRelsGenerator(&oRels); oWriter.WriteString(std::wstring(L"")); oWriter.WriteString(std::wstring(L"m_arNotes[nIndexNotes]; oRels.StartNotes(pNotes->m_lSlideID, m_pDocument->m_pNotesMaster != NULL); + + m_pShapeWriter->SetRelsGenerator(&oRels); oWriter.WriteString(std::wstring(L"")); oWriter.WriteString(std::wstring(L" Date: Thu, 13 Mar 2025 13:57:18 +0600 Subject: [PATCH 27/28] fux bug #73517 --- OOXML/XlsxFormat/Worksheets/SheetData.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/OOXML/XlsxFormat/Worksheets/SheetData.cpp b/OOXML/XlsxFormat/Worksheets/SheetData.cpp index 073a508084..9a710afbef 100644 --- a/OOXML/XlsxFormat/Worksheets/SheetData.cpp +++ b/OOXML/XlsxFormat/Worksheets/SheetData.cpp @@ -1205,12 +1205,12 @@ namespace OOX auto ColumnPart = static_cast(BinFmla.rgcb.getPtgs().back().get()); if(!SharedFormulasRef::sharedRefsLocations) return; - for(auto i = 0; i size(); i++) + for(const auto& location : *SharedFormulasRef::sharedRefsLocations) { - if(SharedFormulasRef::sharedRefsLocations->at(i).row == rowPart->rowXlsb && - SharedFormulasRef::sharedRefsLocations->at(i).column == ColumnPart->col) + if(location.second.row == rowPart->rowXlsb && + location.second.column == ColumnPart->col) { - m_oSi = i; + m_oSi = location.first; m_oT = SimpleTypes::Spreadsheet::ECellFormulaType::cellformulatypeShared; } } From a8d8f44cefd735d16e8ebf706c1ae33f0a6e6e6b Mon Sep 17 00:00:00 2001 From: Viktor Andreev Date: Fri, 7 Mar 2025 17:28:54 +0600 Subject: [PATCH 28/28] fix bug #73437 (cherry picked from commit 9cde3dc699d0215e4d0bca9a04d9739a0104a396) --- .../Logic/Biff_structures/StringPtgParser.cpp | 3 -- OOXML/Binary/Sheets/Reader/BinaryWriter.cpp | 3 ++ OOXML/Binary/Sheets/Writer/BinaryReader.cpp | 2 +- OOXML/XlsbFormat/Biff12_records/BeginSXPI.h | 10 ++-- OOXML/XlsxFormat/Pivot/Pivots.cpp | 53 +++++++++++++------ 5 files changed, 45 insertions(+), 26 deletions(-) diff --git a/MsBinaryFile/XlsFile/Format/Logic/Biff_structures/StringPtgParser.cpp b/MsBinaryFile/XlsFile/Format/Logic/Biff_structures/StringPtgParser.cpp index b6c345b5cc..fa24bcb4b8 100644 --- a/MsBinaryFile/XlsFile/Format/Logic/Biff_structures/StringPtgParser.cpp +++ b/MsBinaryFile/XlsFile/Format/Logic/Biff_structures/StringPtgParser.cpp @@ -317,9 +317,6 @@ const bool StringPtgParser::parseToPtgs(const std::wstring& assembled_formula, R [&](XLS::GlobalWorkbookInfo::_xti i) { return i.iSup == ixti; }); - if(pos->itabFirst == pos->itabLast) - rgce.addPtg(found_operand = OperandPtgPtr(new PtgRef3d(ixti, operand_str, OperandPtg::ptg_VALUE, rgce.getLocation()))); - else rgce.addPtg(found_operand = OperandPtgPtr(new PtgRef3d(ixti, operand_str, OperandPtg::ptg_REFERENCE, rgce.getLocation()))); } else if(SyntaxPtg::extract_PtgRefErr(it, itEnd)) diff --git a/OOXML/Binary/Sheets/Reader/BinaryWriter.cpp b/OOXML/Binary/Sheets/Reader/BinaryWriter.cpp index 3767dbebdd..6b6534fb16 100644 --- a/OOXML/Binary/Sheets/Reader/BinaryWriter.cpp +++ b/OOXML/Binary/Sheets/Reader/BinaryWriter.cpp @@ -2663,6 +2663,9 @@ void BinaryWorkbookTableWriter::WritePivotCache(OOX::Spreadsheet::CWorkbook& wor { nCurPos = m_oBcw.WriteItemStart(c_oSer_PivotTypes::cache); NSStringUtils::CStringBuilder writer; + writer.WriteString(L""); + if(pivotCacheFile->m_oPivotCashDefinition->m_oRid.IsInit()) + pivotCacheFile->m_oPivotCashDefinition->m_oRid.reset(); pivotCacheFile->m_oPivotCashDefinition->toXML(writer); auto wstringData = writer.GetData(); m_oBcw.m_oStream.WriteStringUtf8(wstringData); diff --git a/OOXML/Binary/Sheets/Writer/BinaryReader.cpp b/OOXML/Binary/Sheets/Writer/BinaryReader.cpp index 05a02799c8..f2b4fa8c9c 100644 --- a/OOXML/Binary/Sheets/Writer/BinaryReader.cpp +++ b/OOXML/Binary/Sheets/Writer/BinaryReader.cpp @@ -3493,7 +3493,7 @@ int BinaryWorkbookTableReader::ReadPivotCaches(BYTE type, long length, void* poR NSCommon::smart_ptr pFileRecords(oPivotCachesTemp.pRecords); srIdRecords = pDefinitionFile->Add(pFileRecords).ToString(); } - pDefinitionFile->setData(oPivotCachesTemp.pDefinitionData, oPivotCachesTemp.nDefinitionLength, L""); + pDefinitionFile->setData(oPivotCachesTemp.pDefinitionData, oPivotCachesTemp.nDefinitionLength, srIdRecords); NSCommon::smart_ptr pFile(pDefinitionFile); OOX::RId rIdDefinition = m_oWorkbook.Add(pFile); diff --git a/OOXML/XlsbFormat/Biff12_records/BeginSXPI.h b/OOXML/XlsbFormat/Biff12_records/BeginSXPI.h index f2c6ab120d..cb384c31e9 100644 --- a/OOXML/XlsbFormat/Biff12_records/BeginSXPI.h +++ b/OOXML/XlsbFormat/Biff12_records/BeginSXPI.h @@ -51,11 +51,11 @@ namespace XLSB void readFields(XLS::CFRecord& record) override; void writeFields(XLS::CFRecord& record) override; - _INT32 isxvd; - _UINT32 isxvi; - _INT32 isxth; - bool fUnique; - bool fDisplay; + _INT32 isxvd = 0; + _UINT32 isxvi = 0x001000FE; + _INT32 isxth = -1; + bool fUnique = false; + bool fDisplay = false; XLWideString irstUnique; XLWideString irstDisplay; }; diff --git a/OOXML/XlsxFormat/Pivot/Pivots.cpp b/OOXML/XlsxFormat/Pivot/Pivots.cpp index 2f5530cb29..b484f94540 100644 --- a/OOXML/XlsxFormat/Pivot/Pivots.cpp +++ b/OOXML/XlsxFormat/Pivot/Pivots.cpp @@ -1885,13 +1885,15 @@ xmlns:xr16=\"http://schemas.microsoft.com/office/spreadsheetml/2017/revision16\" if(m_oHier.IsInit()) ptr->isxth = m_oHier.get(); if(m_oName.IsInit()) + { ptr->irstUnique = m_oName.get(); - else - ptr->irstUnique = 0xFFFFFFFF; + ptr->fUnique = true; + } if(m_oCap.IsInit()) + { ptr->irstDisplay = m_oCap.get(); - else - ptr->irstDisplay = 0xFFFFFFFF; + ptr->fDisplay = true; + } return objectPtr; } void CPageField::fromBin(XLS::BaseObjectPtr& obj) @@ -4172,7 +4174,6 @@ xmlns:xr16=\"http://schemas.microsoft.com/office/spreadsheetml/2017/revision16\" writer.WriteString(L"GetValue()); WritingStringNullableAttrInt(L"mappingCount", m_oMappingCount, m_oMappingCount->GetValue()); WritingStringNullableAttrInt(L"numFmtId", m_oNumFmtId, m_oNumFmtId->GetValue()); + WritingStringNullableAttrBool2(L"databaseField", m_oDatabaseField); if(!m_oSharedItems.IsInit() && !m_oFieldGroup.IsInit()) { writer.WriteString(L"/>"); @@ -4406,22 +4408,29 @@ xmlns:xr16=\"http://schemas.microsoft.com/office/spreadsheetml/2017/revision16\" void CSharedItems::toXML(NSStringUtils::CStringBuilder& writer) const { writer.WriteString(L"ToString()); WritingStringNullableAttrString(L"maxDate", m_oMaxDate, m_oMaxDate->ToString()); - writer.WriteString(L">"); - + if(!m_arrItems.empty()) + { + WritingStringAttrInt(L"count", (int)m_arrItems.size()); + } + WritingStringNullableAttrBool2(L"longText", m_oLongText); + if(m_arrItems.empty()) + { + writer.WriteString(L"/>"); + return; + } + writer.WriteString(L">"); for ( size_t i = 0; i < m_arrItems.size(); ++i) { if ( m_arrItems[i] ) @@ -5127,7 +5136,7 @@ xmlns:xr16=\"http://schemas.microsoft.com/office/spreadsheetml/2017/revision16\" //------------------------------------ void CRangeGroupingProperties::toXML(NSStringUtils::CStringBuilder& writer) const { - writer.WriteString(L"ToString()); WritingStringNullableAttrBool2(L"autoStart", m_oAutoStart); WritingStringNullableAttrBool2(L"autoEnd", m_oAutoEnd); @@ -5835,6 +5844,11 @@ xmlns:xr16=\"http://schemas.microsoft.com/office/spreadsheetml/2017/revision16\" } WritingStringNullableAttrBool2(L"f", m_oCalculated); WritingStringNullableAttrBool2(L"u", m_oUnused); + if(m_arrItems.empty()) + { + writer.WriteString(L"/>"); + return; + } writer.WriteString(L">"); for ( size_t i = 0; i < m_arrItems.size(); ++i) @@ -5967,6 +5981,11 @@ xmlns:xr16=\"http://schemas.microsoft.com/office/spreadsheetml/2017/revision16\" } WritingStringNullableAttrBool2(L"f", m_oCalculated); WritingStringNullableAttrBool2(L"u", m_oUnused); + if(m_arrItems.empty()) + { + writer.WriteString(L"/>"); + return; + } writer.WriteString(L">"); for ( size_t i = 0; i < m_arrItems.size(); ++i) @@ -6864,8 +6883,8 @@ xmlns:xr16=\"http://schemas.microsoft.com/office/spreadsheetml/2017/revision16\" void CFieldGroupProperties::toXML(NSStringUtils::CStringBuilder& writer) const { writer.WriteString(L"GetValue()); WritingStringNullableAttrInt(L"par", m_oPar, m_oPar->GetValue()); + WritingStringNullableAttrInt(L"base", m_oBase, m_oBase->GetValue()); writer.WriteString(L">"); if(m_oDiscretePr.IsInit())