include/o3tl/BigEndianTypes.hxx                |   62 ++++++
 include/vcl/font/EOTConverter.hxx              |   67 +++++++
 include/vcl/font/FontDataContainer.hxx         |   36 ++++
 o3tl/CppunitTest_o3tl_tests.mk                 |    1 
 o3tl/qa/BigEndianTypesTest.cxx                 |  107 +++++++++++
 package/source/zippackage/ZipPackage.cxx       |    3 
 sd/Library_sd.mk                               |    1 
 sd/inc/ModelTraverser.hxx                      |   53 +++++
 sd/source/core/ModelTraverser.cxx              |   47 +++++
 sd/source/filter/eppt/epptooxml.hxx            |    8 
 sd/source/filter/eppt/pptx-epptooxml.cxx       |  223 ++++++++++++++++++++++++-
 sd/source/ui/tools/GraphicSizeCheck.cxx        |   59 ------
 vcl/CppunitTest_vcl_font_ttf_structure_test.mk |   44 ++++
 vcl/Library_vcl.mk                             |    1 
 vcl/Module_vcl.mk                              |    1 
 vcl/inc/font/TTFReader.hxx                     |  201 ++++++++++++++++++++++
 vcl/inc/font/TTFStructure.hxx                  |  183 ++++++++++++++++++++
 vcl/inc/sft.hxx                                |   20 --
 vcl/qa/cppunit/font/TTFStructureTest.cxx       |   87 +++++++++
 vcl/qa/cppunit/font/data/Ahem.ttf              |binary
 vcl/source/font/EOTConverter.cxx               |  166 ++++++++++++++++++
 21 files changed, 1292 insertions(+), 78 deletions(-)

New commits:
commit f6ad4397dc7cee97020d8d2e89345157dbfc7a0f
Author:     Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk>
AuthorDate: Fri Apr 25 14:41:35 2025 +0900
Commit:     Tomaž Vajngerl <qui...@gmail.com>
CommitDate: Wed Apr 30 08:30:53 2025 +0200

    sd: move ModelTraverser to own file so it can be reused
    
    Change-Id: I8d487fd009ca3fa5fb9de8aabe65fd3e5e21b41d
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/184692
    Reviewed-by: Tomaž Vajngerl <qui...@gmail.com>
    Tested-by: Jenkins

diff --git a/sd/Library_sd.mk b/sd/Library_sd.mk
index df3309c6d78d..a05aba888eb6 100644
--- a/sd/Library_sd.mk
+++ b/sd/Library_sd.mk
@@ -154,6 +154,7 @@ $(eval $(call gb_Library_add_exception_objects,sd,\
        sd/source/core/CustomAnimationEffect \
        sd/source/core/CustomAnimationPreset \
        sd/source/core/EffectMigration \
+       sd/source/core/ModelTraverser \
        sd/source/core/PageListWatcher \
        sd/source/core/TransitionPreset \
        sd/source/core/ThemeColorChanger \
diff --git a/sd/inc/ModelTraverser.hxx b/sd/inc/ModelTraverser.hxx
new file mode 100644
index 000000000000..209ace6047e4
--- /dev/null
+++ b/sd/inc/ModelTraverser.hxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <vector>
+#include <memory>
+#include "drawdoc.hxx"
+
+class SdrObject;
+
+namespace sd
+{
+/**
+ * Interface for the visitor class, which handles each visited SdrObject
+ * in the DOM.
+ */
+class ModelTraverseHandler
+{
+public:
+    virtual ~ModelTraverseHandler() {}
+    virtual void handleSdrObject(SdrObject* pObject) = 0;
+};
+
+/**
+ * Traverses the DOM and calls a handler for each object (SdrObject) it
+ * encounters.
+ */
+class ModelTraverser
+{
+private:
+    std::vector<std::shared_ptr<ModelTraverseHandler>> m_pNodeHandler;
+    SdDrawDocument* m_pDocument;
+
+public:
+    ModelTraverser(SdDrawDocument* pDocument)
+        : m_pDocument(pDocument)
+    {
+    }
+
+    void traverse();
+    void addNodeHandler(std::shared_ptr<ModelTraverseHandler> pHandler);
+};
+
+} // end sd namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/core/ModelTraverser.cxx 
b/sd/source/core/ModelTraverser.cxx
new file mode 100644
index 000000000000..6eb15de0a6ee
--- /dev/null
+++ b/sd/source/core/ModelTraverser.cxx
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <ModelTraverser.hxx>
+
+#include <svx/svdobj.hxx>
+#include <svx/svdpage.hxx>
+
+using namespace css;
+
+namespace sd
+{
+void ModelTraverser::traverse()
+{
+    if (!m_pDocument)
+        return;
+
+    for (sal_uInt16 nPage = 0; nPage < m_pDocument->GetPageCount(); ++nPage)
+    {
+        SdrPage* pPage = m_pDocument->GetPage(nPage);
+        if (pPage)
+        {
+            for (const rtl::Reference<SdrObject>& pObject : *pPage)
+            {
+                for (auto& pNodeHandler : m_pNodeHandler)
+                {
+                    pNodeHandler->handleSdrObject(pObject.get());
+                }
+            }
+        }
+    }
+}
+
+void ModelTraverser::addNodeHandler(std::shared_ptr<ModelTraverseHandler> 
pHandler)
+{
+    m_pNodeHandler.push_back(pHandler);
+}
+
+} // end sd namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/tools/GraphicSizeCheck.cxx 
b/sd/source/ui/tools/GraphicSizeCheck.cxx
index 68bd33a3e194..d145de1ad22d 100644
--- a/sd/source/ui/tools/GraphicSizeCheck.cxx
+++ b/sd/source/ui/tools/GraphicSizeCheck.cxx
@@ -10,6 +10,7 @@
 
 #include <memory>
 #include <tools/GraphicSizeCheck.hxx>
+#include <ModelTraverser.hxx>
 #include <svx/strings.hrc>
 #include <svx/svdobj.hxx>
 #include <svx/svdpage.hxx>
@@ -22,64 +23,6 @@
 
 namespace sd
 {
-namespace
-{
-/**
- * Interface for the visitor class, which handles each visited SdrObject
- * in the DOM.
- */
-class ModelTraverseHandler
-{
-public:
-    virtual ~ModelTraverseHandler() {}
-
-    virtual void handleSdrObject(SdrObject* pObject) = 0;
-};
-
-/**
- * Traverses the DOM and calls a handler for each object (SdrObject) it
- * encounters.
- */
-class ModelTraverser
-{
-private:
-    std::vector<std::shared_ptr<ModelTraverseHandler>> m_pNodeHandler;
-    SdDrawDocument* m_pDocument;
-
-public:
-    ModelTraverser(SdDrawDocument* pDocument)
-        : m_pDocument(pDocument)
-    {
-    }
-
-    void traverse()
-    {
-        if (!m_pDocument)
-            return;
-
-        for (sal_uInt16 nPage = 0; nPage < m_pDocument->GetPageCount(); 
++nPage)
-        {
-            SdrPage* pPage = m_pDocument->GetPage(nPage);
-            if (pPage)
-            {
-                for (const rtl::Reference<SdrObject>& pObject : *pPage)
-                {
-                    for (auto& pNodeHandler : m_pNodeHandler)
-                    {
-                        pNodeHandler->handleSdrObject(pObject.get());
-                    }
-                }
-            }
-        }
-    }
-
-    void addNodeHandler(std::shared_ptr<ModelTraverseHandler> pHandler)
-    {
-        m_pNodeHandler.push_back(pHandler);
-    }
-};
-}
-
 GraphicSizeViolation::GraphicSizeViolation(sal_Int32 nDPI, SdrGrafObj* 
pGraphicObject)
     : m_pGraphicObject(pGraphicObject)
 {
commit d0ee08cfbf12145027eee7ad46448a8734693c06
Author:     Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk>
AuthorDate: Thu Feb 27 14:30:51 2025 +0900
Commit:     Tomaž Vajngerl <qui...@gmail.com>
CommitDate: Wed Apr 30 08:30:48 2025 +0200

    oox: export embedded font used in a presentation document
    
    This adds support to export the fonts that are used in an
    presentation document. The fonts are converted to EOT fonts.
    
    Adds an EOT converter, which converts a TTF font to EOT font by
    writing a EOT header and adding the TTF font data.
    
    Adds BigEndianTypes so we can read a structure that is defined in
    big endian and the types get converted (if needed) to system
    endian when accessed. + test
    
    Adds TTF reader which reads the true type font structure in an
    easy way that is used by the EOTConverter. + test
    
    Change-Id: I8b0bcb69e47943c2a8679edf881d7c2bab45c450
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/184288
    Tested-by: Jenkins
    Reviewed-by: Tomaž Vajngerl <qui...@gmail.com>

diff --git a/include/o3tl/BigEndianTypes.hxx b/include/o3tl/BigEndianTypes.hxx
new file mode 100644
index 000000000000..5b637c4a5d62
--- /dev/null
+++ b/include/o3tl/BigEndianTypes.hxx
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <sal/config.h>
+#include <sal/types.h>
+#include <osl/endian.h>
+
+namespace o3tl
+{
+/** 16-bit unsigned integer type that can be used in a struct to read from 
data that is big endian.
+ *
+ * Type can't be instantiated but only used in a struct which is 
reinterpret_cast from bytes.
+ */
+class sal_uInt16_BE
+{
+private:
+    sal_uInt16 mnValue;
+    sal_uInt16_BE() = delete;
+
+public:
+    constexpr operator sal_uInt16() const
+    {
+#ifdef OSL_LITENDIAN
+        return OSL_SWAPWORD(mnValue);
+#else
+        return mnValue;
+#endif
+    }
+};
+
+/** 32-bit unsigned integer type that can be used in a struct to read from 
data that is big endian.
+ *
+ * Type can't be instantiated but only used in a struct which is 
reinterpret_cast from bytes.
+ */
+class sal_uInt32_BE
+{
+private:
+    sal_uInt32 mnValue;
+    sal_uInt32_BE() = delete;
+
+public:
+    constexpr operator sal_uInt32() const
+    {
+#ifdef OSL_LITENDIAN
+        return OSL_SWAPDWORD(mnValue);
+#else
+        return mnValue;
+#endif
+    }
+};
+
+} // namespace o3tl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/vcl/font/EOTConverter.hxx 
b/include/vcl/font/EOTConverter.hxx
new file mode 100644
index 000000000000..8695355b3628
--- /dev/null
+++ b/include/vcl/font/EOTConverter.hxx
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <vcl/dllapi.h>
+#include <vcl/font/FontDataContainer.hxx>
+
+namespace font
+{
+#pragma pack(push, 1)
+
+/** Main EOT Header
+ *
+ * See: https://www.w3.org/submissions/EOT/ */
+struct EOTHeader
+{
+    sal_uInt32 nEotSize;
+    sal_uInt32 nFontDataSize;
+    sal_uInt32 nVersion;
+    sal_uInt32 nFlags;
+    sal_uInt8 nFontPANOSE[10];
+    sal_uInt8 nCharset;
+    sal_uInt8 nItalic;
+    sal_uInt32 nWeight;
+    sal_uInt16 nFsType;
+    sal_uInt16 nMagicNumber;
+    sal_uInt32 nUnicodeRange1;
+    sal_uInt32 nUnicodeRange2;
+    sal_uInt32 nUnicodeRange3;
+    sal_uInt32 nUnicodeRange4;
+    sal_uInt32 nCodePageRange1;
+    sal_uInt32 nCodePageRange2;
+    sal_uInt32 nCheckSumAdjustment;
+    sal_uInt32 nReserved1;
+    sal_uInt32 nReserved2;
+    sal_uInt32 nReserved3;
+    sal_uInt32 nReserved4;
+    // variable length types come after this
+};
+
+#pragma pack(pop)
+
+/** Converts TTF Font wrapped in a FontDataContainer to EOT type */
+class VCL_DLLPUBLIC EOTConverter
+{
+private:
+    font::FontDataContainer const& mrFontDataContainer;
+
+public:
+    explicit EOTConverter(font::FontDataContainer const& rFontDataContainer)
+        : mrFontDataContainer(rFontDataContainer)
+    {
+    }
+
+    bool convert(std::vector<sal_uInt8>& rEmbeddedOutput);
+};
+
+} // end font namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/vcl/font/FontDataContainer.hxx 
b/include/vcl/font/FontDataContainer.hxx
new file mode 100644
index 000000000000..159eb4964420
--- /dev/null
+++ b/include/vcl/font/FontDataContainer.hxx
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <vector>
+
+namespace font
+{
+/** Font data container wraps binary content of a TTF font */
+class FontDataContainer
+{
+    std::vector<sal_uInt8> const& mrFontBytes;
+
+public:
+    FontDataContainer(std::vector<sal_uInt8> const& rFontBytes)
+        : mrFontBytes(rFontBytes)
+    {
+    }
+
+    const char* getPointer() const { return reinterpret_cast<const 
char*>(mrFontBytes.data()); }
+
+    size_t size() const { return mrFontBytes.size(); }
+
+    std::vector<sal_uInt8>::const_iterator begin() const { return 
mrFontBytes.begin(); }
+    std::vector<sal_uInt8>::const_iterator end() const { return 
mrFontBytes.end(); }
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/o3tl/CppunitTest_o3tl_tests.mk b/o3tl/CppunitTest_o3tl_tests.mk
index 01f6e410a1b2..60dad44ed54a 100644
--- a/o3tl/CppunitTest_o3tl_tests.mk
+++ b/o3tl/CppunitTest_o3tl_tests.mk
@@ -40,6 +40,7 @@ $(eval $(call 
gb_CppunitTest_add_exception_objects,o3tl_tests,\
        o3tl/qa/test-unit_conversion \
        o3tl/qa/test-vector_pool \
        o3tl/qa/test-hash_combine \
+       o3tl/qa/BigEndianTypesTest \
 ))
 
 # vim: set noet sw=4:
diff --git a/o3tl/qa/BigEndianTypesTest.cxx b/o3tl/qa/BigEndianTypesTest.cxx
new file mode 100644
index 000000000000..055be80705f5
--- /dev/null
+++ b/o3tl/qa/BigEndianTypesTest.cxx
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <cppunit/TestAssert.h>
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+
+#include <o3tl/BigEndianTypes.hxx>
+#include <array>
+
+namespace
+{
+// Struct using endian of the system with 16-bit variables
+struct SystemEndian16
+{
+    sal_uInt16 x;
+    sal_uInt16 y;
+    sal_uInt16 z;
+    sal_uInt16 w;
+};
+
+// Struct using big endian with 16-bit variables
+struct BigEndian16
+{
+    o3tl::sal_uInt16_BE x;
+    o3tl::sal_uInt16_BE y;
+    o3tl::sal_uInt16_BE z;
+    o3tl::sal_uInt16_BE w;
+};
+
+// Struct using endian of the system with 32-bit variables
+struct SystemEndian32
+{
+    sal_uInt32 x;
+    sal_uInt32 y;
+};
+
+// Struct using big endian with 32-bit variables
+struct BigEndian32
+{
+    o3tl::sal_uInt32_BE x;
+    o3tl::sal_uInt32_BE y;
+};
+
+// Test data
+constexpr std::array<sal_uInt8, 8> aDataArray = { 0x01, 0x02, 0x03, 0x04, 
0x05, 0x06, 0x07, 0x08 };
+
+class BigEndianTypesTest : public CppUnit::TestFixture
+{
+public:
+    void testCast()
+    {
+        auto* pSystem16 = reinterpret_cast<const 
SystemEndian16*>(aDataArray.data());
+
+        // We expect different results depending on the system endian
+#ifdef OSL_LITENDIAN
+        CPPUNIT_ASSERT_EQUAL(sal_uInt16(0x0201), pSystem16->x);
+        CPPUNIT_ASSERT_EQUAL(sal_uInt16(0x0403), pSystem16->y);
+        CPPUNIT_ASSERT_EQUAL(sal_uInt16(0x0605), pSystem16->z);
+        CPPUNIT_ASSERT_EQUAL(sal_uInt16(0x0807), pSystem16->w);
+#else
+        CPPUNIT_ASSERT_EQUAL(sal_uInt16(0x0102), pSystem16->x);
+        CPPUNIT_ASSERT_EQUAL(sal_uInt16(0x0304), pSystem16->y);
+        CPPUNIT_ASSERT_EQUAL(sal_uInt16(0x0506), pSystem16->z);
+        CPPUNIT_ASSERT_EQUAL(sal_uInt16(0x0708), pSystem16->w);
+#endif
+
+        // Reading in big endian should read the same independent of system 
endian
+        auto* pBig16 = reinterpret_cast<const BigEndian16*>(aDataArray.data());
+        CPPUNIT_ASSERT_EQUAL(sal_uInt16(0x0102), sal_uInt16(pBig16->x));
+        CPPUNIT_ASSERT_EQUAL(sal_uInt16(0x0304), sal_uInt16(pBig16->y));
+        CPPUNIT_ASSERT_EQUAL(sal_uInt16(0x0506), sal_uInt16(pBig16->z));
+        CPPUNIT_ASSERT_EQUAL(sal_uInt16(0x0708), sal_uInt16(pBig16->w));
+
+        // We expect different results depending on the system endian
+        auto* pSystem32 = reinterpret_cast<const 
SystemEndian32*>(aDataArray.data());
+#ifdef OSL_LITENDIAN
+        CPPUNIT_ASSERT_EQUAL(sal_uInt32(0x04030201), pSystem32->x);
+        CPPUNIT_ASSERT_EQUAL(sal_uInt32(0x08070605), pSystem32->y);
+#else
+        CPPUNIT_ASSERT_EQUAL(sal_uInt32(0x01020304), pSystem32->x);
+        CPPUNIT_ASSERT_EQUAL(sal_uInt32(0x05060708), pSystem32->y);
+#endif
+
+        // Reading in big endian should read the same independent of system 
endian
+        auto* pBig32 = reinterpret_cast<const BigEndian32*>(aDataArray.data());
+        CPPUNIT_ASSERT_EQUAL(sal_uInt32(0x01020304), sal_uInt32(pBig32->x));
+        CPPUNIT_ASSERT_EQUAL(sal_uInt32(0x05060708), sal_uInt32(pBig32->y));
+    }
+
+    CPPUNIT_TEST_SUITE(BigEndianTypesTest);
+    CPPUNIT_TEST(testCast);
+    CPPUNIT_TEST_SUITE_END();
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(BigEndianTypesTest);
+
+} // end anonymous namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zippackage/ZipPackage.cxx 
b/package/source/zippackage/ZipPackage.cxx
index 36a48ac7c04e..2364ce12d6ec 100644
--- a/package/source/zippackage/ZipPackage.cxx
+++ b/package/source/zippackage/ZipPackage.cxx
@@ -1216,7 +1216,8 @@ void ZipPackage::WriteContentTypes( ZipOutputStream& 
aZipOut, const std::vector<
         { u"xml"_ustr, u"application/xml"_ustr },
         { u"rels"_ustr, 
u"application/vnd.openxmlformats-package.relationships+xml"_ustr },
         { u"png"_ustr, u"image/png"_ustr },
-        { u"jpeg"_ustr, u"image/jpeg"_ustr }
+        { u"jpeg"_ustr, u"image/jpeg"_ustr },
+        { u"fntdata"_ustr, u"application/x-fontdata"_ustr }
     };
 
     uno::Sequence< beans::StringPair > aOverridesSequence(aManList.size());
diff --git a/sd/source/filter/eppt/epptooxml.hxx 
b/sd/source/filter/eppt/epptooxml.hxx
index 7ac7dc938067..b2b472412aea 100644
--- a/sd/source/filter/eppt/epptooxml.hxx
+++ b/sd/source/filter/eppt/epptooxml.hxx
@@ -201,6 +201,14 @@ private:
     void WriteVBA();
 
     void WriteModifyVerifier();
+
+    bool mbEmbedFonts = false;
+    bool mbEmbedUsedOnly = false;
+    bool mbEmbedLatinScript = true;
+    bool mbEmbedAsianScript = true;
+    bool mbEmbedComplexScript = true;
+
+    void WriteEmbeddedFontList();
 };
 
 }
diff --git a/sd/source/filter/eppt/pptx-epptooxml.cxx 
b/sd/source/filter/eppt/pptx-epptooxml.cxx
index eb88eefdf359..baf6db88883a 100644
--- a/sd/source/filter/eppt/pptx-epptooxml.cxx
+++ b/sd/source/filter/eppt/pptx-epptooxml.cxx
@@ -27,6 +27,7 @@
 #include <oox/export/shapes.hxx>
 #include <svx/svdlayer.hxx>
 #include <unokywds.hxx>
+#include <osl/file.hxx>
 
 #include <comphelper/sequenceashashmap.hxx>
 #include <comphelper/storagehelper.hxx>
@@ -60,6 +61,8 @@
 #include <com/sun/star/container/XNamed.hpp>
 #include <com/sun/star/presentation/XPresentationSupplier.hpp>
 #include <comphelper/diagnose_ex.hxx>
+#include <comphelper/hash.hxx>
+#include <vcl/embeddedfontshelper.hxx>
 
 #include <oox/export/utils.hxx>
 #include <oox/export/ThemeExport.hxx>
@@ -76,13 +79,18 @@
 #include <svx/ColorSets.hxx>
 #include <sdmod.hxx>
 #include <sdpage.hxx>
+#include <unomodel.hxx>
 
 #include <vcl/svapp.hxx>
 #include <vcl/settings.hxx>
+#include <vcl/font/EOTConverter.hxx>
 
 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
 #include <com/sun/star/document/XStorageBasedDocument.hpp>
 #include <utility>
+#include <unordered_map>
+#include <unordered_set>
+
 #if OSL_DEBUG_LEVEL > 1
 #include <com/sun/star/drawing/RectanglePoint.hpp>
 #endif
@@ -393,6 +401,12 @@ void PowerPointExport::writeDocumentProperties()
         try
         {
             xSettings->getPropertyValue(u"LoadReadonly"_ustr) >>= 
bSecurityOptOpenReadOnly;
+
+            xSettings->getPropertyValue(u"EmbedFonts"_ustr) >>= mbEmbedFonts;
+            xSettings->getPropertyValue(u"EmbedOnlyUsedFonts"_ustr) >>= 
mbEmbedUsedOnly;
+            xSettings->getPropertyValue(u"EmbedLatinScriptFonts"_ustr)  >>= 
mbEmbedLatinScript;
+            xSettings->getPropertyValue(u"EmbedAsianScriptFonts"_ustr)  >>= 
mbEmbedAsianScript;
+            xSettings->getPropertyValue(u"EmbedComplexScriptFonts"_ustr) >>= 
mbEmbedComplexScript;
         }
         catch( Exception& )
         {
@@ -454,7 +468,10 @@ bool PowerPointExport::exportDocument()
                 oox::getRelationship(Relationship::THEME),
                 u"theme/theme1.xml");
 
-    mPresentationFS->startElementNS(XML_p, XML_presentation, 
presentationNamespaces(*this));
+    auto pAttributes = presentationNamespaces(*this);
+    if (mbEmbedFonts)
+        pAttributes->add(XML_embedTrueTypeFonts, "1");
+    mPresentationFS->startElementNS(XML_p, XML_presentation, pAttributes);
 
     mXStatusIndicator = getStatusIndicator();
 
@@ -473,6 +490,7 @@ bool PowerPointExport::exportDocument()
     mPresentationFS->singleElementNS(XML_p, XML_notesSz,
                                      XML_cx, 
OString::number(PPTtoEMU(maNotesPageSize.Width)),
                                      XML_cy, 
OString::number(PPTtoEMU(maNotesPageSize.Height)));
+    WriteEmbeddedFontList();
 
     WriteCustomSlideShow();
 
@@ -512,6 +530,209 @@ bool PowerPointExport::exportDocument()
     return new ::oox::ole::VbaProject(getComponentContext(), getModel(), 
u"Impress");
 }
 
+namespace
+{
+struct EmbeddedFont
+{
+    OUString sFamilyName;
+
+    OString aRegularRelID;
+    OString aBoldRelID;
+    OString aItalicRelID;
+    OString aBoldItalicRelID;
+};
+} // end anonymous namespace
+
+// Writers the list of all embedded fonts and reference to the fonts
+void PowerPointExport::WriteEmbeddedFontList()
+{
+    if (!mbEmbedFonts)
+        return;
+
+    SdDrawDocument* pDocument = nullptr;
+    if (auto* pSdXImpressDocument = 
dynamic_cast<SdXImpressDocument*>(mXModel.get()))
+        pDocument = pSdXImpressDocument->GetDoc();
+
+    if (!pDocument)
+        return;
+
+    int nextFontId = 1;
+
+    std::unordered_set<OUString> aFontFamilyNameSet;
+    std::unordered_map<std::string, OString> aFontDeduplicationMap;
+
+    std::vector<EmbeddedFont> aEmbeddedFontInfo;
+
+    uno::Reference<beans::XPropertySet> xProperties(mXModel, UNO_QUERY);
+    if (!xProperties.is())
+        return;
+
+    uno::Sequence<uno::Any> aAnySeq;
+    if (!(xProperties->getPropertyValue("Fonts") >>= aAnySeq))
+        return;
+
+    if (aAnySeq.getLength() % 5 != 0)
+        return;
+
+    int nLen = aAnySeq.getLength() / 5;
+    int nSeqIndex = 0;
+
+    for (int i = 0; i < nLen; i++)
+    {
+        OUString sFamilyName;
+        OUString sStyleName;
+        sal_uInt16 eFamily = FAMILY_DONTKNOW;
+        sal_uInt16 ePitch = PITCH_DONTKNOW;
+        sal_uInt16 eCharSet = RTL_TEXTENCODING_DONTKNOW;
+
+        aAnySeq[nSeqIndex++] >>= sFamilyName;
+        aAnySeq[nSeqIndex++] >>= sStyleName;
+        aAnySeq[nSeqIndex++] >>= eFamily;
+        aAnySeq[nSeqIndex++] >>= ePitch;
+        aAnySeq[nSeqIndex++] >>= eCharSet;
+
+        if (aFontFamilyNameSet.contains(sFamilyName))
+            continue;
+
+        static std::vector<std::pair<FontItalic, FontWeight>> 
aFontVariantCombinations =
+        {
+            { ITALIC_NONE, WEIGHT_NORMAL },
+            { ITALIC_NONE, WEIGHT_BOLD },
+            { ITALIC_NORMAL, WEIGHT_NORMAL },
+            { ITALIC_NORMAL, WEIGHT_BOLD }
+        };
+
+        EmbeddedFont aInfo;
+        aInfo.sFamilyName = sFamilyName;
+
+        for (auto [eItalic, eWeight] : aFontVariantCombinations)
+        {
+            OUString sFontUrl = EmbeddedFontsHelper::fontFileUrl(
+                                    sFamilyName, FontFamily(eFamily), eItalic, 
eWeight, FontPitch(ePitch),
+                                    
EmbeddedFontsHelper::FontRights::ViewingAllowed);
+
+            if (sFontUrl.isEmpty())
+                continue;
+
+            osl::File aFile(sFontUrl);
+            if (aFile.open(osl_File_OpenFlag_Read ) != osl::File::E_None)
+                continue;
+
+            std::vector<sal_uInt8> rFontData;
+
+            std::array<sal_uInt8, 4096> buffer;
+            sal_uInt64 readSize;
+
+            comphelper::Hash aHashCalc(comphelper::HashType::SHA256);
+
+            OString uRelID;
+
+            // Read file
+            for(;;)
+            {
+                sal_Bool eof;
+
+                if (aFile.isEndOfFile(&eof) != osl::File::E_None)
+                {
+                    SAL_WARN("sw.ww8", "Error reading font file " << sFontUrl);
+                    break;
+                }
+                if (eof)
+                    break;
+
+                if (aFile.read(buffer.data(), 4096, readSize) != 
osl::File::E_None)
+                {
+                    SAL_WARN("sw.ww8", "Error reading font file " << sFontUrl);
+                    break;
+                }
+
+                if (readSize == 0)
+                    break;
+
+                rFontData.insert(rFontData.end(), buffer.data(), buffer.data() 
+ readSize);
+                aHashCalc.update(reinterpret_cast<const unsigned 
char*>(buffer.data()), readSize);
+            }
+
+            std::string aHash =  
comphelper::hashToString(aHashCalc.finalize());
+            auto iterator = aFontDeduplicationMap.find(aHash);
+            if (iterator == aFontDeduplicationMap.end())
+            {
+                std::vector<sal_uInt8> rEOT;
+
+                font::FontDataContainer aContainer(rFontData);
+                font::EOTConverter aConverter(aContainer);
+                if (!aConverter.convert(rEOT))
+                    continue;
+
+                OUString sFontFileName = "font" + OUString::number(nextFontId) 
+ ".fntdata";
+                OUString sArchivePath = "ppt/fonts/" + sFontFileName;
+                uno::Reference<css::io::XOutputStream> xOutStream = 
openFragmentStream(sArchivePath, u"application/x-fontdata"_ustr);
+                
xOutStream->writeBytes(uno::Sequence<sal_Int8>(reinterpret_cast<const 
sal_Int8*>(rEOT.data()), rEOT.size()));
+                xOutStream->closeOutput();
+
+                OUString sRelID = 
addRelation(mPresentationFS->getOutputStream(),
+                                              
oox::getRelationship(Relationship::FONT),
+                                              Concat2View("fonts/font" + 
OUString::number(nextFontId) + ".fntdata"));
+
+                ++nextFontId;
+
+                uRelID = OUStringToOString(sRelID, RTL_TEXTENCODING_UTF8);
+                aFontDeduplicationMap.emplace(aHash, uRelID);
+            }
+            else
+            {
+                uRelID = iterator->second;
+            }
+
+            if (eItalic == ITALIC_NONE && eWeight == WEIGHT_NORMAL)
+            {
+                aInfo.aRegularRelID = uRelID;
+            }
+            else if (eItalic == ITALIC_NONE && eWeight == WEIGHT_BOLD)
+            {
+                aInfo.aBoldRelID = uRelID;
+            }
+            else if (eItalic == ITALIC_NORMAL && eWeight == WEIGHT_NORMAL)
+            {
+                aInfo.aItalicRelID = uRelID;
+            }
+            else if (eItalic == ITALIC_NORMAL && eWeight == WEIGHT_BOLD)
+            {
+                aInfo.aBoldItalicRelID = uRelID;
+            }
+        }
+
+        aEmbeddedFontInfo.push_back(aInfo);
+        aFontFamilyNameSet.insert(sFamilyName);
+    }
+
+    // if there are fonts to embed and font embeding enabled
+    mPresentationFS->startElementNS(XML_p, XML_embeddedFontLst);
+    for (auto const& rInfo : aEmbeddedFontInfo)
+    {
+        mPresentationFS->startElementNS(XML_p, XML_embeddedFont);
+
+        mPresentationFS->singleElementNS(XML_p, XML_font,
+            //XML_charset, OUString::number(0),
+            XML_typeface, rInfo.sFamilyName);
+
+        if (!rInfo.aRegularRelID.isEmpty())
+            mPresentationFS->singleElementNS(XML_p, XML_regular, FSNS(XML_r, 
XML_id), rInfo.aRegularRelID);
+
+        if (!rInfo.aBoldRelID.isEmpty())
+            mPresentationFS->singleElementNS(XML_p, XML_bold, FSNS(XML_r, 
XML_id), rInfo.aBoldRelID);
+
+        if (!rInfo.aItalicRelID.isEmpty())
+            mPresentationFS->singleElementNS(XML_p, XML_italic, FSNS(XML_r, 
XML_id), rInfo.aItalicRelID);
+
+        if (!rInfo.aBoldItalicRelID.isEmpty())
+            mPresentationFS->singleElementNS(XML_p, XML_boldItalic, 
FSNS(XML_r, XML_id), rInfo.aBoldItalicRelID);
+
+        mPresentationFS->endElementNS(XML_p, XML_embeddedFont);
+    }
+    mPresentationFS->endElementNS(XML_p, XML_embeddedFontLst);
+}
+
 void PowerPointExport::WriteCustomSlideShow()
 {
     Reference<XCustomPresentationSupplier> aXCPSup(mXModel, 
css::uno::UNO_QUERY);
diff --git a/vcl/CppunitTest_vcl_font_ttf_structure_test.mk 
b/vcl/CppunitTest_vcl_font_ttf_structure_test.mk
new file mode 100644
index 000000000000..8759c800ecb4
--- /dev/null
+++ b/vcl/CppunitTest_vcl_font_ttf_structure_test.mk
@@ -0,0 +1,44 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_CppunitTest_CppunitTest,vcl_font_ttf_structure_test))
+
+$(eval $(call gb_CppunitTest_set_include,vcl_font_ttf_structure_test,\
+    $$(INCLUDE) \
+    -I$(SRCDIR)/vcl/inc \
+))
+
+$(eval $(call 
gb_CppunitTest_add_exception_objects,vcl_font_ttf_structure_test, \
+       vcl/qa/cppunit/font/TTFStructureTest \
+))
+
+$(eval $(call 
gb_CppunitTest_use_externals,vcl_font_ttf_structure_test,boost_headers))
+
+$(eval $(call gb_CppunitTest_use_libraries,vcl_font_ttf_structure_test, \
+       basegfx \
+       comphelper \
+       cppu \
+       cppuhelper \
+       sal \
+       subsequenttest \
+       test \
+       unotest \
+       utl \
+       tl \
+       vcl \
+       xmlsecurity \
+))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,vcl_font_ttf_structure_test))
+$(eval $(call gb_CppunitTest_use_ure,vcl_font_ttf_structure_test))
+$(eval $(call gb_CppunitTest_use_vcl,vcl_font_ttf_structure_test))
+$(eval $(call gb_CppunitTest_use_rdb,vcl_font_ttf_structure_test,services))
+$(eval $(call gb_CppunitTest_use_configuration,vcl_font_ttf_structure_test))
+
+# vim: set noet sw=4 ts=4:
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk
index 8edfc3e21f8e..a4402914132e 100644
--- a/vcl/Library_vcl.mk
+++ b/vcl/Library_vcl.mk
@@ -553,6 +553,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\
     vcl/source/font/fontcharmap \
     vcl/source/font/fontmetric \
     vcl/source/font/font \
+    vcl/source/font/EOTConverter \
     vcl/source/fontsubset/cff \
     vcl/source/fontsubset/fontsubset \
     vcl/source/fontsubset/sft \
diff --git a/vcl/Module_vcl.mk b/vcl/Module_vcl.mk
index cc22693cf512..b2eeb946bf02 100644
--- a/vcl/Module_vcl.mk
+++ b/vcl/Module_vcl.mk
@@ -229,6 +229,7 @@ $(eval $(call gb_Module_add_check_targets,vcl,\
     CppunitTest_vcl_fontmetric \
     CppunitTest_vcl_text \
     CppunitTest_vcl_textlayout \
+    CppunitTest_vcl_font_ttf_structure_test \
     CppunitTest_vcl_filters_test \
     CppunitTest_vcl_mnemonic \
     CppunitTest_vcl_outdev \
diff --git a/vcl/inc/font/TTFReader.hxx b/vcl/inc/font/TTFReader.hxx
new file mode 100644
index 000000000000..eb8d2be34de8
--- /dev/null
+++ b/vcl/inc/font/TTFReader.hxx
@@ -0,0 +1,201 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <font/TTFStructure.hxx>
+#include <vcl/font/FontDataContainer.hxx>
+#include <rtl/ustrbuf.hxx>
+
+namespace font
+{
+/** Handles reading of the name table */
+class NameTableHandler
+{
+private:
+    FontDataContainer const& mrFontDataContainer;
+
+    const TableDirectoryEntry* mpTableDirectoryEntry;
+    const char* mpNameTablePointer;
+    const NameTable* mpNameTable;
+
+    const char* getTablePointer(const TableDirectoryEntry* pEntry)
+    {
+        return mrFontDataContainer.getPointer() + pEntry->offset;
+    }
+
+public:
+    NameTableHandler(FontDataContainer const& rFontDataContainer,
+                     const TableDirectoryEntry* pTableDirectoryEntry)
+        : mrFontDataContainer(rFontDataContainer)
+        , mpTableDirectoryEntry(pTableDirectoryEntry)
+        , mpNameTablePointer(getTablePointer(mpTableDirectoryEntry))
+        , mpNameTable(reinterpret_cast<const NameTable*>(mpNameTablePointer))
+    {
+    }
+
+    sal_uInt32 getTableOffset() { return mpTableDirectoryEntry->offset; }
+
+    const NameTable* getNameTable() { return mpNameTable; }
+
+    /** Number of tables */
+    sal_uInt16 getNumberOfRecords() { return mpNameTable->nCount; }
+
+    /** Get a name table record for index */
+    const NameRecord* getNameRecord(sal_uInt32 index)
+    {
+        const char* pPointer = mpNameTablePointer + sizeof(NameTable);
+        pPointer += sizeof(NameRecord) * index;
+        return reinterpret_cast<const NameRecord*>(pPointer);
+    }
+
+    /** Get offset to english unicode string
+     *
+     * See: 
https://learn.microsoft.com/en-us/typography/opentype/spec/name#name-ids
+     */
+    bool findEnglishUnicodeNameOffset(font::NameID eNameID, sal_uInt64& 
rOffset,
+                                      sal_uInt16& rLength)
+    {
+        rOffset = 0;
+        rLength = 0;
+
+        for (int n = 0; n < getNumberOfRecords(); n++)
+        {
+            const font::NameRecord* pNameRecord = getNameRecord(n);
+
+            if (pNameRecord->nPlatformID == 3 // Windows
+                && pNameRecord->nEncodingID == 1 // Unicode BMP
+                && pNameRecord->nLanguageID == 0x0409 // en-us
+                && pNameRecord->nNameID == sal_uInt16(eNameID))
+            {
+                rLength = pNameRecord->nLength;
+                rOffset = getTableOffset() + getNameTable()->nStorageOffset
+                          + pNameRecord->nStringOffset;
+                return true;
+            }
+        }
+        return false;
+    }
+};
+
+/** Handles reading of table entries */
+class TableEntriesHandler
+{
+private:
+    FontDataContainer const& mrFontDataContainer;
+    const char* mpFirstPosition;
+    sal_uInt16 mnNumberOfTables;
+
+    const char* getTablePointer(const TableDirectoryEntry* pEntry)
+    {
+        return mrFontDataContainer.getPointer() + pEntry->offset;
+    }
+
+public:
+    TableEntriesHandler(FontDataContainer const& rFontDataContainer, const 
char* pPosition,
+                        sal_uInt16 nNumberOfTables)
+        : mrFontDataContainer(rFontDataContainer)
+        , mpFirstPosition(pPosition)
+        , mnNumberOfTables(nNumberOfTables)
+    {
+    }
+
+    const TableDirectoryEntry* getEntry(sal_uInt32 nTag)
+    {
+        for (sal_uInt32 i = 0; i < mnNumberOfTables; i++)
+        {
+            const char* pPosition = mpFirstPosition + 
sizeof(TableDirectoryEntry) * i;
+            const auto* pEntry = reinterpret_cast<const 
TableDirectoryEntry*>(pPosition);
+
+            if (nTag == pEntry->tag)
+                return pEntry;
+        }
+        return nullptr;
+    }
+
+    const OS2Table* getOS2Table()
+    {
+        const auto* pEntry = getEntry(T_OS2);
+        if (!pEntry)
+            return nullptr;
+        return reinterpret_cast<const OS2Table*>(getTablePointer(pEntry));
+    }
+
+    const HeadTable* getHeadTable()
+    {
+        const auto* pEntry = getEntry(T_head);
+        if (!pEntry)
+            return nullptr;
+        return reinterpret_cast<const HeadTable*>(getTablePointer(pEntry));
+    }
+
+    const NameTable* getNameTable()
+    {
+        const auto* pEntry = getEntry(T_name);
+        if (!pEntry)
+            return nullptr;
+        return reinterpret_cast<const NameTable*>(getTablePointer(pEntry));
+    }
+
+    std::unique_ptr<NameTableHandler> getNameTableHandler()
+    {
+        const auto* pEntry = getEntry(T_name);
+        if (!pEntry)
+            return nullptr;
+
+        return std::unique_ptr<NameTableHandler>(new 
NameTableHandler(mrFontDataContainer, pEntry));
+    }
+};
+
+/** Entry point handler for the TTF Font */
+class TTFFont
+{
+private:
+    FontDataContainer const& mrFontDataContainer;
+
+    const char* getTablePointer(const TableDirectoryEntry* pEntry)
+    {
+        return mrFontDataContainer.getPointer() + pEntry->offset;
+    }
+
+public:
+    TTFFont(FontDataContainer const& rFontDataContainer)
+        : mrFontDataContainer(rFontDataContainer)
+    {
+    }
+
+    const TableDirectory* getTableDirector()
+    {
+        return reinterpret_cast<const 
TableDirectory*>(mrFontDataContainer.getPointer());
+    }
+
+    std::unique_ptr<TableEntriesHandler> getTableEntriesHandler()
+    {
+        auto* pDirectory = getTableDirector();
+        const char* pPosition = mrFontDataContainer.getPointer() + 
sizeof(TableDirectory);
+
+        std::unique_ptr<TableEntriesHandler> pHandler(
+            new TableEntriesHandler(mrFontDataContainer, pPosition, 
pDirectory->nNumberOfTables));
+        return pHandler;
+    }
+
+    /** Gets the string from a name table */
+    OUString getNameTableString(sal_uInt64 nOffset, sal_uInt16 nLength)
+    {
+        const auto* pString = reinterpret_cast<const o3tl::sal_uInt16_BE*>(
+            mrFontDataContainer.getPointer() + nOffset);
+        OUStringBuffer aStringBuffer;
+        for (sal_uInt16 i = 0; i < (nLength / 2); i++)
+            aStringBuffer.append(sal_Unicode(pString[i]));
+        return aStringBuffer.makeStringAndClear();
+    }
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/inc/font/TTFStructure.hxx b/vcl/inc/font/TTFStructure.hxx
new file mode 100644
index 000000000000..3a751c39fca6
--- /dev/null
+++ b/vcl/inc/font/TTFStructure.hxx
@@ -0,0 +1,183 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <o3tl/BigEndianTypes.hxx>
+
+namespace font
+{
+// make sure no padding bytes are added for the struct
+#pragma pack(push, 1)
+
+/** Table directory of a TTF font, values are all in big endian */
+struct TableDirectory
+{
+    o3tl::sal_uInt32_BE nSfntVersion;
+    o3tl::sal_uInt16_BE nNumberOfTables;
+    o3tl::sal_uInt16_BE nSearchRange;
+    o3tl::sal_uInt16_BE nEntrySelector;
+    o3tl::sal_uInt16_BE nRangeShift;
+};
+
+/** Table directory entry
+ *
+ * Array of those follows TableDirectory structure.
+ */
+struct TableDirectoryEntry
+{
+    o3tl::sal_uInt32_BE tag;
+    o3tl::sal_uInt32_BE checkSum;
+    o3tl::sal_uInt32_BE offset;
+    o3tl::sal_uInt32_BE length;
+};
+
+/** Structure of the OS2 table (Version 4)
+ *
+ * See: https://learn.microsoft.com/en-us/typography/opentype/spec/os2
+ */
+struct OS2Table
+{
+    o3tl::sal_uInt16_BE nVersion;
+    o3tl::sal_uInt16_BE nXAvgCharWidth; // FWORD
+    o3tl::sal_uInt16_BE nWeightClass;
+    o3tl::sal_uInt16_BE nWidthClass;
+    o3tl::sal_uInt16_BE nFsType;
+    o3tl::sal_uInt16_BE nSubscriptXSize; // FWORD
+    o3tl::sal_uInt16_BE nSubscriptYSize; // FWORD
+    o3tl::sal_uInt16_BE nSubscriptXOffset; // FWORD
+    o3tl::sal_uInt16_BE nSubscriptYOffset; // FWORD
+    o3tl::sal_uInt16_BE nSuperscriptXSize; // FWORD
+    o3tl::sal_uInt16_BE nSuperscriptYSize; // FWORD
+    o3tl::sal_uInt16_BE nSuperscriptXOffset; // FWORD
+    o3tl::sal_uInt16_BE nSuperscriptYOffset; // FWORD
+    o3tl::sal_uInt16_BE nStrikeoutSize; // FWORD
+    o3tl::sal_uInt16_BE nStrikeoutPosition; // FWORD
+    o3tl::sal_uInt16_BE nFamilyClass;
+    sal_uInt8 nPanose[10];
+    o3tl::sal_uInt32_BE nUnicodeRange1;
+    o3tl::sal_uInt32_BE nUnicodeRange2;
+    o3tl::sal_uInt32_BE nUnicodeRange3;
+    o3tl::sal_uInt32_BE nUnicodeRange4;
+    sal_uInt8 nFontVendorID[4]; // Tag type
+    o3tl::sal_uInt16_BE nFsSelection;
+    o3tl::sal_uInt16_BE nFirstCharIndex;
+    o3tl::sal_uInt16_BE nLastCharIndex;
+    o3tl::sal_uInt16_BE nTypoAscender; // FWORD
+    o3tl::sal_uInt16_BE nTypoDescender; // FWORD
+    o3tl::sal_uInt16_BE nTypoLineGap; // FWORD
+    o3tl::sal_uInt16_BE nWinAscent; // UFWORD
+    o3tl::sal_uInt16_BE nWinDescent; // UFWORD
+    o3tl::sal_uInt32_BE nCodePageRange1;
+    o3tl::sal_uInt32_BE nCodePageRange2;
+    o3tl::sal_uInt16_BE nXHeight; // FWORD
+    o3tl::sal_uInt16_BE nCapHeight; // FWORD
+    o3tl::sal_uInt16_BE nDefaultChar;
+    o3tl::sal_uInt16_BE nBreakChar;
+    o3tl::sal_uInt16_BE nMaxContext;
+    o3tl::sal_uInt16_BE nLowerOpticalPointSize;
+    o3tl::sal_uInt16_BE nUpperOpticalPointSize;
+};
+
+// Check the size of OS2Table struct is as expected
+static_assert(sizeof(OS2Table) == 100);
+
+/** Structure of "head" table.
+ *
+ * See: https://learn.microsoft.com/en-us/typography/opentype/spec/head
+ */
+struct HeadTable
+{
+    sal_uInt16 nMajorVersion;
+    sal_uInt16 nMinorVersion;
+    sal_uInt32 nFontRevision;
+    o3tl::sal_uInt32_BE nCheckSumAdjustment;
+    o3tl::sal_uInt32_BE nMagicNumber;
+    o3tl::sal_uInt16_BE nFlags;
+    o3tl::sal_uInt16_BE nUnitsPerEm;
+    sal_Int64 nCreated; // LONGDATETIME - signed 64-bit (TODO: need a BE type)
+    sal_Int64 nModified; // LONGDATETIME - signed 64-bit (TODO: need a BE type)
+    o3tl::sal_uInt16_BE nXMin;
+    o3tl::sal_uInt16_BE nXMax;
+    o3tl::sal_uInt16_BE nYMin;
+    o3tl::sal_uInt16_BE nYMax;
+    o3tl::sal_uInt16_BE nMacStyle;
+    o3tl::sal_uInt16_BE nLowestRectPPEM;
+    o3tl::sal_uInt16_BE nFontDirectionHint;
+    o3tl::sal_uInt16_BE nIndexToLocFormat;
+    o3tl::sal_uInt16_BE nGlyphDataFormat;
+};
+
+// Check the size of HeadTable struct is as expected
+static_assert(sizeof(HeadTable) == 54);
+
+/** Structure of "name" table (Version 0)
+ *
+ * See: https://learn.microsoft.com/en-us/typography/opentype/spec/name
+ */
+struct NameTable
+{
+    o3tl::sal_uInt16_BE nVersion;
+    o3tl::sal_uInt16_BE nCount;
+    o3tl::sal_uInt16_BE nStorageOffset;
+    // Following this are NameRecords -> nCount times
+};
+
+/** Name record structure
+ *
+ * Array of those follows NameTable structure.
+ */
+struct NameRecord
+{
+    o3tl::sal_uInt16_BE nPlatformID;
+    o3tl::sal_uInt16_BE nEncodingID;
+    o3tl::sal_uInt16_BE nLanguageID;
+    o3tl::sal_uInt16_BE nNameID;
+    o3tl::sal_uInt16_BE nLength; // (in bytes)
+    o3tl::sal_uInt16_BE nStringOffset; // offset from start of storage area 
(in bytes)
+};
+
+/** Name IDs
+ *
+ * See https://learn.microsoft.com/en-us/typography/opentype/spec/name#name-ids
+ */
+enum class NameID : sal_uInt16
+{
+    Copyright = 0, // example: "© Copyright..."
+    FamilyName = 1, // example: "Times New Roman"
+    SubfamilyName = 2, // example: "Bold"
+    UniqueID = 3, // example: "Monotype: Times New Roman Bold: 1990"
+    FullFontName = 4, // example: "Times New Roman Bold"
+    Version = 5, // example: "Version 1.00 June 1, 1990, initial release"
+    PostScriptName = 6, // example: "TimesNewRoman-Bold"
+};
+
+#pragma pack(pop)
+
+} // end font namespace
+
+// Standard TrueType table tags
+constexpr sal_uInt32 T_maxp = 0x6D617870;
+constexpr sal_uInt32 T_glyf = 0x676C7966;
+constexpr sal_uInt32 T_head = 0x68656164;
+constexpr sal_uInt32 T_loca = 0x6C6F6361;
+constexpr sal_uInt32 T_name = 0x6E616D65;
+constexpr sal_uInt32 T_hhea = 0x68686561;
+constexpr sal_uInt32 T_hmtx = 0x686D7478;
+constexpr sal_uInt32 T_cmap = 0x636D6170;
+constexpr sal_uInt32 T_vhea = 0x76686561;
+constexpr sal_uInt32 T_vmtx = 0x766D7478;
+constexpr sal_uInt32 T_OS2 = 0x4F532F32;
+constexpr sal_uInt32 T_post = 0x706F7374;
+constexpr sal_uInt32 T_cvt = 0x63767420;
+constexpr sal_uInt32 T_prep = 0x70726570;
+constexpr sal_uInt32 T_fpgm = 0x6670676D;
+constexpr sal_uInt32 T_CFF = 0x43464620;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/inc/sft.hxx b/vcl/inc/sft.hxx
index 0423c64af2af..0a2697c3e062 100644
--- a/vcl/inc/sft.hxx
+++ b/vcl/inc/sft.hxx
@@ -53,6 +53,8 @@
 #include <memory>
 #include <vector>
 
+#include "font/TTFStructure.hxx"
+
 class SvStream;
 
 namespace vcl
@@ -433,24 +435,6 @@ constexpr sal_uInt32 T_true = 0x74727565;        /* 'true' 
*/
 constexpr sal_uInt32 T_ttcf = 0x74746366;        /* 'ttcf' */
 constexpr sal_uInt32 T_otto = 0x4f54544f;        /* 'OTTO' */
 
-// standard TrueType table tags
-constexpr sal_uInt32 T_maxp = 0x6D617870;
-constexpr sal_uInt32 T_glyf = 0x676C7966;
-constexpr sal_uInt32 T_head = 0x68656164;
-constexpr sal_uInt32 T_loca = 0x6C6F6361;
-constexpr sal_uInt32 T_name = 0x6E616D65;
-constexpr sal_uInt32 T_hhea = 0x68686561;
-constexpr sal_uInt32 T_hmtx = 0x686D7478;
-constexpr sal_uInt32 T_cmap = 0x636D6170;
-constexpr sal_uInt32 T_vhea = 0x76686561;
-constexpr sal_uInt32 T_vmtx = 0x766D7478;
-constexpr sal_uInt32 T_OS2  = 0x4F532F32;
-constexpr sal_uInt32 T_post = 0x706F7374;
-constexpr sal_uInt32 T_cvt  = 0x63767420;
-constexpr sal_uInt32 T_prep = 0x70726570;
-constexpr sal_uInt32 T_fpgm = 0x6670676D;
-constexpr sal_uInt32 T_CFF  = 0x43464620;
-
 class AbstractTrueTypeFont;
 class TrueTypeFont;
 
diff --git a/vcl/qa/cppunit/font/TTFStructureTest.cxx 
b/vcl/qa/cppunit/font/TTFStructureTest.cxx
new file mode 100644
index 000000000000..26c5f3aa0538
--- /dev/null
+++ b/vcl/qa/cppunit/font/TTFStructureTest.cxx
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <test/bootstrapfixture.hxx>
+#include <test/unoapi_test.hxx>
+
+#include <font/TTFStructure.hxx>
+#include <font/TTFReader.hxx>
+#include <font/TTFReader.hxx>
+
+namespace
+{
+class TTFTest : public UnoApiTest
+{
+public:
+    TTFTest()
+        : UnoApiTest("/vcl/qa/cppunit/font/data/")
+    {
+    }
+};
+
+CPPUNIT_TEST_FIXTURE(TTFTest, testReadTTFStructure)
+{
+    OUString aFileName = createFileURL(u"Ahem.ttf");
+    SvFileStream aStream(aFileName, StreamMode::READ);
+    std::vector<sal_uInt8> aFontBytes(aStream.remainingSize());
+    aStream.ReadBytes(aFontBytes.data(), aFontBytes.size());
+    CPPUNIT_ASSERT(aStream.good());
+    aStream.Close();
+
+    font::FontDataContainer aContainer(aFontBytes);
+    font::TTFFont aFont(aContainer);
+    auto pHanlder = aFont.getTableEntriesHandler();
+    const font::OS2Table* pOS2 = pHanlder->getOS2Table();
+    CPPUNIT_ASSERT(pOS2);
+    CPPUNIT_ASSERT_EQUAL(sal_uInt16(400), sal_uInt16(pOS2->nWeightClass));
+    CPPUNIT_ASSERT_EQUAL(sal_uInt16(5), sal_uInt16(pOS2->nWidthClass));
+    CPPUNIT_ASSERT_EQUAL(sal_uInt16(1000), sal_uInt16(pOS2->nXAvgCharWidth));
+    CPPUNIT_ASSERT_EQUAL(sal_uInt16(0), sal_uInt16(pOS2->nFamilyClass));
+
+    CPPUNIT_ASSERT_EQUAL(sal_uInt16(175), sal_uInt16(pOS2->nUnicodeRange1));
+    CPPUNIT_ASSERT_EQUAL(sal_uInt16(8264), sal_uInt16(pOS2->nUnicodeRange2));
+    CPPUNIT_ASSERT_EQUAL(sal_uInt16(0), sal_uInt16(pOS2->nUnicodeRange3));
+    CPPUNIT_ASSERT_EQUAL(sal_uInt16(0), sal_uInt16(pOS2->nUnicodeRange4));
+
+    const font::HeadTable* pHeadTable = pHanlder->getHeadTable();
+    CPPUNIT_ASSERT(pHeadTable);
+    CPPUNIT_ASSERT_EQUAL(sal_uInt16(1000), 
sal_uInt16(pHeadTable->nUnitsPerEm));
+
+    auto pNameTableHanlder = pHanlder->getNameTableHandler();
+    CPPUNIT_ASSERT(pNameTableHanlder);
+
+    sal_uInt64 nOffset = 0;
+    sal_uInt16 nLength = 0;
+
+    
CPPUNIT_ASSERT(pNameTableHanlder->findEnglishUnicodeNameOffset(font::NameID::FamilyName,
+                                                                   nOffset, 
nLength));
+    OUString aFamilyName = aFont.getNameTableString(nOffset, nLength);
+    CPPUNIT_ASSERT_EQUAL(u"Ahem"_ustr, aFamilyName);
+
+    
CPPUNIT_ASSERT(pNameTableHanlder->findEnglishUnicodeNameOffset(font::NameID::FullFontName,
+                                                                   nOffset, 
nLength));
+    OUString aFullFontName = aFont.getNameTableString(nOffset, nLength);
+    CPPUNIT_ASSERT_EQUAL(u"Ahem"_ustr, aFullFontName);
+
+    
CPPUNIT_ASSERT(pNameTableHanlder->findEnglishUnicodeNameOffset(font::NameID::SubfamilyName,
+                                                                   nOffset, 
nLength));
+    OUString aSubFamilyName = aFont.getNameTableString(nOffset, nLength);
+    CPPUNIT_ASSERT_EQUAL(u"Regular"_ustr, aSubFamilyName);
+
+    CPPUNIT_ASSERT(
+        pNameTableHanlder->findEnglishUnicodeNameOffset(font::NameID::Version, 
nOffset, nLength));
+    OUString aVersion = aFont.getNameTableString(nOffset, nLength);
+    CPPUNIT_ASSERT_EQUAL(u"Version 1.1"_ustr, aVersion);
+}
+
+} // end anonymous namespace
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/qa/cppunit/font/data/Ahem.ttf 
b/vcl/qa/cppunit/font/data/Ahem.ttf
new file mode 100644
index 000000000000..ac81cb03165a
Binary files /dev/null and b/vcl/qa/cppunit/font/data/Ahem.ttf differ
diff --git a/vcl/source/font/EOTConverter.cxx b/vcl/source/font/EOTConverter.cxx
new file mode 100644
index 000000000000..d8b044dfc413
--- /dev/null
+++ b/vcl/source/font/EOTConverter.cxx
@@ -0,0 +1,166 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <string.h>
+#include <vector>
+#include <tools/vcompat.hxx>
+#include <cstdio>
+#include <tools/stream.hxx>
+#include <vcl/font/EOTConverter.hxx>
+#include <osl/endian.h>
+#include <font/TTFStructure.hxx>
+#include <font/TTFReader.hxx>
+
+namespace font
+{
+namespace
+{
+// Writes padding, length and string data to font output
+void writeNameTableString(font::TTFFont& rFont,
+                          std::unique_ptr<NameTableHandler>& pNameTableHanlder,
+                          font::NameID eNameID, std::vector<sal_uInt8>& 
rEotOutput)
+{
+    sal_uInt64 nOffset = 0;
+    sal_uInt16 nLength = 0;
+
+    // Padding
+    rEotOutput.push_back(0);
+    rEotOutput.push_back(0);
+
+    if (pNameTableHanlder
+        && pNameTableHanlder->findEnglishUnicodeNameOffset(eNameID, nOffset, 
nLength))
+    {
+        // Length
+        rEotOutput.push_back(sal_uInt8((nLength + 2) & 0xff));
+        rEotOutput.push_back(sal_uInt8((nLength + 2) >> 8));
+
+        OUString aString = rFont.getNameTableString(nOffset, nLength);
+        for (sal_Int32 i = 0; i < aString.getLength(); i++)
+        {
+            sal_Unicode nUniChar = aString[i];
+            rEotOutput.push_back(sal_uInt8(nUniChar & 0xff));
+            rEotOutput.push_back(sal_uInt8(nUniChar >> 8));
+        }
+        // null terminated
+        rEotOutput.push_back(sal_uInt8(0));
+        rEotOutput.push_back(sal_uInt8(0));
+    }
+    else
+    {
+        // Length 0
+        rEotOutput.push_back(sal_uInt8(0));
+        rEotOutput.push_back(sal_uInt8(0));
+    }
+}
+}
+
+bool EOTConverter::convert(std::vector<sal_uInt8>& rEotOutput)
+{
+    font::TTFFont aFont(mrFontDataContainer);
+
+    rEotOutput.clear();
+    rEotOutput.resize(sizeof(EOTHeader));
+
+    EOTHeader* pEot = reinterpret_cast<EOTHeader*>(rEotOutput.data());
+    pEot->nFontDataSize = mrFontDataContainer.size();
+    pEot->nVersion = 0x00020002;
+    pEot->nFlags = 0;
+    pEot->nCharset = 0;
+    pEot->nMagicNumber = 0x504c;
+    pEot->nReserved1 = 0;
+    pEot->nReserved2 = 0;
+    pEot->nReserved3 = 0;
+    pEot->nReserved4 = 0;
+
+    auto pHanlder = aFont.getTableEntriesHandler();
+
+    const font::OS2Table* pOS2 = pHanlder->getOS2Table();
+    if (pOS2)
+    {
+        for (sal_uInt32 n = 0; n < 10; n++)
+            pEot->nFontPANOSE[n] = pOS2->nPanose[n];
+
+        pEot->nItalic = pOS2->nFsSelection & 0x01;
+        pEot->nWeight = pOS2->nWeightClass;
+        // FIXME: Should use OS2->fsType, but some TrueType fonts set it to an 
over-restrictive value.
+        // Since ATS does not enforce this on Mac OS X, we do not enforce it 
either.
+        pEot->nFsType = 0x0000;
+        pEot->nUnicodeRange1 = pOS2->nUnicodeRange1;
+        pEot->nUnicodeRange2 = pOS2->nUnicodeRange2;
+        pEot->nUnicodeRange3 = pOS2->nUnicodeRange3;
+        pEot->nUnicodeRange4 = pOS2->nUnicodeRange4;
+        pEot->nCodePageRange1 = pOS2->nCodePageRange1;
+        pEot->nCodePageRange2 = pOS2->nCodePageRange2;
+    }
+
+    const font::HeadTable* pHeadTable = pHanlder->getHeadTable();
+    if (pHeadTable)
+    {
+        pEot->nCheckSumAdjustment = pHeadTable->nCheckSumAdjustment;
+    }
+
+    auto pNameTableHanlder = pHanlder->getNameTableHandler();
+
+    writeNameTableString(aFont, pNameTableHanlder, font::NameID::FamilyName, 
rEotOutput);
+    writeNameTableString(aFont, pNameTableHanlder, 
font::NameID::SubfamilyName, rEotOutput);
+    writeNameTableString(aFont, pNameTableHanlder, font::NameID::Version, 
rEotOutput);
+    writeNameTableString(aFont, pNameTableHanlder, font::NameID::FullFontName, 
rEotOutput);
+
+    // Padding5
+    rEotOutput.push_back(0);
+    rEotOutput.push_back(0);
+
+    // Root String Size
+    rEotOutput.push_back(0);
+    rEotOutput.push_back(0);
+
+    // Root String CheckSum (for size 0)
+    rEotOutput.push_back(0x42);
+    rEotOutput.push_back(0x53);
+    rEotOutput.push_back(0x47);
+    rEotOutput.push_back(0x50);
+
+    // EUDC CodePage
+    rEotOutput.push_back(0xE4);
+    rEotOutput.push_back(0x04);
+    rEotOutput.push_back(0x00);
+    rEotOutput.push_back(0x00);
+
+    // Padding6
+    rEotOutput.push_back(0);
+    rEotOutput.push_back(0);
+
+    // Signature Size = should be 0x0000
+    rEotOutput.push_back(0);
+    rEotOutput.push_back(0);
+
+    // EUDC Flags
+    rEotOutput.push_back(0);
+    rEotOutput.push_back(0);
+    rEotOutput.push_back(0);
+    rEotOutput.push_back(0);
+
+    // EUDC Font Size = 0
+    rEotOutput.push_back(0);
+    rEotOutput.push_back(0);
+    rEotOutput.push_back(0);
+    rEotOutput.push_back(0);
+
+    // rEOTOutput could've been reallocated - need to reinterpret that.
+    pEot = reinterpret_cast<EOTHeader*>(rEotOutput.data());
+    pEot->nEotSize = rEotOutput.size() + mrFontDataContainer.size();
+
+    rEotOutput.insert(rEotOutput.end(), mrFontDataContainer.begin(), 
mrFontDataContainer.end());
+
+    return true;
+}
+
+} // end font namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Reply via email to