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: */
