include/vcl/embeddedfontsmanager.hxx  |   13 +
 svx/source/svdraw/svdpdf.cxx          |    1 
 vcl/Library_vcl.mk                    |    2 
 vcl/source/gdi/afdko.hxx              |   32 ++++
 vcl/source/gdi/embeddedfontsafdko.cxx |  240 ++++++++++++++++++++++++++++++++++
 5 files changed, 288 insertions(+)

New commits:
commit e20bf4d9dfeaf7370f9adce4c2428e4e08100fe3
Author:     Caolán McNamara <[email protected]>
AuthorDate: Fri Sep 19 15:57:29 2025 +0100
Commit:     Caolán McNamara <[email protected]>
CommitDate: Tue Oct 14 17:13:37 2025 +0200

    add apis for font format conversion and merging
    
    Change-Id: Ica67fba1ea32a0d98326e6c2979556bee1759a3b
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/191211
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Miklos Vajna <[email protected]>
    (cherry picked from commit fe9e639b38e6b6a07e0a37e215d1fc75e62e537b)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192333
    Reviewed-by: Caolán McNamara <[email protected]>
    Tested-by: Jenkins

diff --git a/include/vcl/embeddedfontsmanager.hxx 
b/include/vcl/embeddedfontsmanager.hxx
index ecf152827d91..74495b770e96 100644
--- a/include/vcl/embeddedfontsmanager.hxx
+++ b/include/vcl/embeddedfontsmanager.hxx
@@ -109,6 +109,19 @@ public:
 
     EmbeddedFontsManager(const css::uno::Reference<css::frame::XModel>& 
xModel);
     ~EmbeddedFontsManager();
+
+    // write text dump
+    static bool tx_dump(const OUString& srcFontUrl, const OUString& 
destFileUrl);
+    // write Type 1 font
+    static bool tx_t1(const OUString& srcFontUrl, const OUString& destFontUrl);
+    // merge fonts together (can also be used to convert a name keyed font to 
a cid keyed font)
+    // each font in fonts is glyphaliasfile, mergefontfile
+    static bool mergefonts(const OUString& cidFontInfoUrl, const OUString& 
destFileUrl,
+                           const std::vector<std::pair<OUString, OUString>>& 
fonts);
+    // write OTF font, features is optional
+    static bool makeotf(const OUString& srcFontUrl, const OUString& 
destFileUrl,
+                        const OUString& fontMenuNameDBUrl, const OUString& 
charMapUrl,
+                        const OUString& featuresUrl);
 };
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdpdf.cxx b/svx/source/svdraw/svdpdf.cxx
index b7d608a968cf..adf30335f13e 100644
--- a/svx/source/svdraw/svdpdf.cxx
+++ b/svx/source/svdraw/svdpdf.cxx
@@ -20,6 +20,7 @@
 #include <svdpdf.hxx>
 
 #include <tools/UnitConversion.hxx>
+#include <vcl/embeddedfontsmanager.hxx>
 #include <vcl/graph.hxx>
 #include <vcl/vectorgraphicdata.hxx>
 
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk
index cef0fe92c2f4..7dd374be5266 100644
--- a/vcl/Library_vcl.mk
+++ b/vcl/Library_vcl.mk
@@ -323,6 +323,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\
     vcl/source/gdi/formpdfexport \
     vcl/source/gdi/cvtgrf \
     vcl/source/gdi/embeddedfontsmanager \
+    vcl/source/gdi/embeddedfontsafdko \
     vcl/source/gdi/FileDefinitionWidgetDraw \
     vcl/source/gdi/WidgetDefinitionReader \
     vcl/source/gdi/WidgetDefinition \
@@ -722,6 +723,7 @@ $(eval $(call gb_Library_use_externals,vcl,\
         freetype \
     ) \
     $(if $(filter PDFIUM,$(BUILD_TYPE)),pdfium) \
+    $(if $(filter AFDKO,$(BUILD_TYPE)),afdko libxml2) \
 ))
 
 $(eval $(call gb_Library_add_libs,vcl,\
diff --git a/vcl/source/gdi/afdko.hxx b/vcl/source/gdi/afdko.hxx
new file mode 100644
index 000000000000..6eb64f9e6672
--- /dev/null
+++ b/vcl/source/gdi/afdko.hxx
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Copyright 2014-2018 Adobe Systems Incorporated (http://www.adobe.com/). All 
Rights Reserved.
+ * This software is licensed as OpenSource, under the Apache License, Version 
2.0.
+ * This license is available at: http://opensource.org/licenses/Apache-2.0.
+ */
+
+#pragma once
+
+extern "C" {
+#include <tx_shared.h>
+#include <hotconv.h>
+#include <cb.h>
+extern txCtx txNew(char* progname);
+extern void txFree(txCtx h);
+extern void cfrReadFont(txCtx h, long origin, int ttcIndex);
+
+extern txCtx mergeFontsNew(char* progname);
+extern void mergeFontsFree(txCtx h);
+extern void readCIDFontInfo(txCtx h, char* filePath);
+extern int doMergeFileSet(txCtx h, int argc, char* args[], int i);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/vcl/source/gdi/embeddedfontsafdko.cxx 
b/vcl/source/gdi/embeddedfontsafdko.cxx
new file mode 100644
index 000000000000..cbac66a0aa38
--- /dev/null
+++ b/vcl/source/gdi/embeddedfontsafdko.cxx
@@ -0,0 +1,240 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Copyright 2014-2018 Adobe Systems Incorporated (http://www.adobe.com/). All 
Rights Reserved.
+ * This software is licensed as OpenSource, under the Apache License, Version 
2.0.
+ * This license is available at: http://opensource.org/licenses/Apache-2.0.
+ */
+
+#include <vcl/embeddedfontsmanager.hxx>
+#include <osl/file.hxx>
+#include "afdko.hxx"
+
+static bool convertTx(txCtx h)
+{
+    h->src.stm.fp = fopen(h->src.stm.filename, "rb");
+    if (!h->src.stm.fp)
+        return false;
+
+    h->src.stm.flags = STM_DONT_CLOSE;
+
+    buildFontList(h);
+
+    for (long i = 0; i < h->fonts.cnt; ++i)
+    {
+        const FontRec& rec = h->fonts.array[i];
+
+        h->src.type = rec.type;
+
+        switch (h->src.type)
+        {
+            case src_Type1:
+                t1rReadFont(h, rec.offset);
+                break;
+            case src_OTF:
+                h->cfr.flags |= CFR_NO_ENCODING;
+                [[fallthrough]];
+            case src_CFF:
+                cfrReadFont(h, rec.offset, rec.iTTC);
+                break;
+            default:
+                SAL_WARN("vcl.fonts", "unhandled font type: " << h->src.type);
+                break;
+        }
+    }
+
+    return true;
+}
+
+// System afdko could be used by calling: tx -dump src dest here
+bool EmbeddedFontsManager::tx_dump(const OUString& srcFontUrl, const OUString& 
destFileUrl)
+{
+    OUString srcFontPath, destFilePath;
+    if (osl::FileBase::E_None != 
osl::FileBase::getSystemPathFromFileURL(srcFontUrl, srcFontPath)
+        || osl::FileBase::E_None
+               != osl::FileBase::getSystemPathFromFileURL(destFileUrl, 
destFilePath))
+    {
+        SAL_WARN("vcl.fonts", "path failure");
+        return false;
+    }
+
+    txCtx h = txNew(nullptr);
+    if (!h)
+        return false;
+
+    OString srcFontPathA(srcFontPath.toUtf8());
+    h->src.stm.filename = const_cast<char*>(srcFontPathA.getStr());
+    OString destFilePathA(destFilePath.toUtf8());
+    h->dst.stm.filename = const_cast<char*>(destFilePathA.getStr());
+    bool result = convertTx(h);
+    txFree(h);
+    return result;
+}
+
+// System afdko could be used by calling: tx -t1 src dest here
+bool EmbeddedFontsManager::tx_t1(const OUString& srcFontUrl, const OUString& 
destFileUrl)
+{
+    OUString srcFontPath, destFilePath;
+    if (osl::FileBase::E_None != 
osl::FileBase::getSystemPathFromFileURL(srcFontUrl, srcFontPath)
+        || osl::FileBase::E_None
+               != osl::FileBase::getSystemPathFromFileURL(destFileUrl, 
destFilePath))
+    {
+        SAL_WARN("vcl.fonts", "path failure");
+        return false;
+    }
+
+    txCtx h = txNew(nullptr);
+    if (!h)
+        return false;
+
+    setMode(h, mode_t1);
+
+    OString srcFontPathA(srcFontPath.toUtf8());
+    h->src.stm.filename = const_cast<char*>(srcFontPathA.getStr());
+    OString destFilePathA(destFilePath.toUtf8());
+    h->dst.stm.filename = const_cast<char*>(destFilePathA.getStr());
+    bool result = convertTx(h);
+    txFree(h);
+    return result;
+}
+
+// System afdko could be used by calling: mergefonts -cid cidfontinfo destfile 
[glyphaliasfile mergefontfile]+ here
+bool EmbeddedFontsManager::mergefonts(const OUString& cidFontInfoUrl, const 
OUString& destFileUrl,
+                                      const std::vector<std::pair<OUString, 
OUString>>& fonts)
+{
+    OUString cidFontInfoPath, destFilePath;
+    if (osl::FileBase::E_None
+            != osl::FileBase::getSystemPathFromFileURL(cidFontInfoUrl, 
cidFontInfoPath)
+        || osl::FileBase::E_None
+               != osl::FileBase::getSystemPathFromFileURL(destFileUrl, 
destFilePath))
+    {
+        SAL_WARN("vcl.fonts", "path failure");
+        return false;
+    }
+
+    std::vector<OString> paths;
+    for (const auto& font : fonts)
+    {
+        OUString glyphAliasPath;
+        OUString mergeFontFilePath;
+        if (osl::FileBase::E_None
+                != osl::FileBase::getSystemPathFromFileURL(font.first, 
glyphAliasPath)
+            || osl::FileBase::E_None
+                   != osl::FileBase::getSystemPathFromFileURL(font.second, 
mergeFontFilePath))
+        {
+            SAL_WARN("vcl.fonts", "path failure");
+            return false;
+        }
+        paths.push_back(glyphAliasPath.toUtf8());
+        paths.push_back(mergeFontFilePath.toUtf8());
+    }
+
+    txCtx h = mergeFontsNew(nullptr);
+    if (!h)
+        return false;
+
+    OString cidFontInfoPathA(cidFontInfoPath.toUtf8());
+    readCIDFontInfo(h, const_cast<char*>(cidFontInfoPathA.getStr()));
+
+    setMode(h, mode_cff);
+
+    OString destFilePathA(destFilePath.toUtf8());
+    dstFileSetName(h, const_cast<char*>(destFilePathA.getStr()));
+    h->cfw.flags |= CFW_CHECK_IF_GLYPHS_DIFFER;
+    h->cfw.flags |= CFW_PRESERVE_GLYPH_ORDER;
+
+    std::vector<char*> args;
+    for (const auto& path : paths)
+    {
+        args.push_back(const_cast<char*>(path.getStr()));
+    }
+    // merge the input fonts into destfile
+    size_t resultarg = doMergeFileSet(h, args.size(), args.data(), 0);
+    SAL_WARN_IF(resultarg != args.size() - 1, "vcl.fonts",
+                "suspicious doMergeFileSet result of: " << resultarg);
+
+    // convert that merged cid result to Type 1
+    h->src.stm.filename = const_cast<char*>(destFilePathA.getStr());
+    OString tmpdestfile = destFilePathA + ".temp";
+    h->dst.stm.filename = const_cast<char*>(tmpdestfile.getStr());
+    setMode(h, mode_t1);
+    bool result = convertTx(h);
+
+    remove(destFilePathA.getStr());
+    rename(h->dst.stm.filename, destFilePathA.getStr());
+
+    mergeFontsFree(h);
+
+    return result;
+}
+
+static void* cb_memory(ctlMemoryCallbacks* /*cb*/, void* old, size_t size)
+{
+    if (size == 0)
+    {
+        free(old);
+        return nullptr;
+    }
+
+    if (old != nullptr)
+        return realloc(old, size);
+
+    return malloc(size);
+}
+
+// System afdko could be used by calling: makeotf[exe] -mf fontMenuNameDB -f 
srcFont -o destFile -ch charMap [-ff features]
+bool EmbeddedFontsManager::makeotf(const OUString& srcFontUrl, const OUString& 
destFileUrl,
+                                   const OUString& fontMenuNameDBUrl, const 
OUString& charMapUrl,
+                                   const OUString& featuresUrl)
+{
+    OUString srcFontPath, destFilePath, charMapPath, fontMenuNameDBPath, 
featuresPath;
+    if (osl::FileBase::E_None != 
osl::FileBase::getSystemPathFromFileURL(srcFontUrl, srcFontPath)
+        || osl::FileBase::E_None
+               != osl::FileBase::getSystemPathFromFileURL(destFileUrl, 
destFilePath)
+        || osl::FileBase::E_None
+               != osl::FileBase::getSystemPathFromFileURL(fontMenuNameDBUrl, 
fontMenuNameDBPath)
+        || osl::FileBase::E_None
+               != osl::FileBase::getSystemPathFromFileURL(charMapUrl, 
charMapPath))
+    {
+        SAL_WARN("vcl.fonts", "path failure");
+        return false;
+    }
+
+    if (!featuresUrl.isEmpty()
+        && osl::FileBase::E_None
+               != osl::FileBase::getSystemPathFromFileURL(featuresUrl, 
featuresPath))
+    {
+        SAL_WARN("vcl.fonts", "path failure");
+        return false;
+    }
+
+    ctlMemoryCallbacks cb_dna_memcb{ nullptr, cb_memory };
+    dnaCtx mainDnaCtx = dnaNew(&cb_dna_memcb, DNA_CHECK_ARGS);
+
+    cbCtx cbctx = cbNew(nullptr, const_cast<char*>(""), const_cast<char*>(""),
+                        const_cast<char*>(""), const_cast<char*>(""), 
mainDnaCtx);
+
+    OString fontMenuNameDBPathA(fontMenuNameDBPath.toUtf8());
+    cbFCDBRead(cbctx, const_cast<char*>(fontMenuNameDBPathA.getStr()));
+
+    OString srcFontPathA(srcFontPath.toUtf8());
+    OString destFilePathA(destFilePath.toUtf8());
+    OString charMapPathA(charMapPath.toUtf8());
+    OString featuresPathA(featuresPath.toUtf8());
+    cbConvert(cbctx, HOT_NO_OLD_OPS, nullptr, 
const_cast<char*>(srcFontPathA.getStr()),
+              const_cast<char*>(destFilePathA.getStr()),
+              !featuresPathA.isEmpty() ? 
const_cast<char*>(featuresPathA.getStr()) : nullptr,
+              const_cast<char*>(charMapPathA.getStr()), nullptr, nullptr, 
nullptr, 0, 0, 0, 0, 0,
+              -1, -1, 0, nullptr);
+
+    return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */

Reply via email to