external/afdko/extern_makeotf.patch   |   29 +++++++++++++++++
 include/vcl/filter/PDFiumLibrary.hxx  |   14 ++++++++
 svx/source/inc/svdpdf.hxx             |    5 +++
 svx/source/svdraw/svdpdf.cxx          |   42 +++++++++++++++++--------
 vcl/source/gdi/embeddedfontsafdko.cxx |   41 ++++++++++++++++--------
 vcl/source/pdf/PDFiumLibrary.cxx      |   56 ++++++++++++++++++++++++++++++++++
 6 files changed, 159 insertions(+), 28 deletions(-)

New commits:
commit 9dc6687e8c2ae00f324b5ed11a557491844bb863
Author:     Caolán McNamara <[email protected]>
AuthorDate: Tue Oct 28 15:17:58 2025 +0000
Commit:     Caolán McNamara <[email protected]>
CommitDate: Tue Oct 28 20:32:48 2025 +0100

    add PDFiumClipPath
    
    Change-Id: I32a535d94b392871eb3ebe047586ecb030ee1314
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193115
    Tested-by: Jenkins
    Reviewed-by: Caolán McNamara <[email protected]>

diff --git a/include/vcl/filter/PDFiumLibrary.hxx 
b/include/vcl/filter/PDFiumLibrary.hxx
index ef305387389c..336edf18d8ba 100644
--- a/include/vcl/filter/PDFiumLibrary.hxx
+++ b/include/vcl/filter/PDFiumLibrary.hxx
@@ -158,6 +158,17 @@ public:
     virtual PDFSegmentType getType() const = 0;
 };
 
+class VCL_DLLPUBLIC PDFiumClipPath
+{
+public:
+    virtual ~PDFiumClipPath() = default;
+
+    virtual int getPathCount() = 0;
+    virtual int getPathSegmentCount(int nPathIndex) = 0;
+    virtual std::unique_ptr<PDFiumPathSegment> getPathSegment(int nPathIndex, 
int nSegmentIndex)
+        = 0;
+};
+
 class VCL_DLLPUBLIC PDFiumPageObject
 {
 public:
@@ -193,6 +204,9 @@ public:
     virtual Size getImageSize(PDFiumPage& rPage) = 0;
     virtual std::unique_ptr<PDFiumBitmap> getImageBitmap() = 0;
     virtual bool getDrawMode(PDFFillMode& eFillMode, bool& bStroke) = 0;
+
+    // ClipPath
+    virtual std::unique_ptr<PDFiumClipPath> getClipPath() = 0;
 };
 
 class VCL_DLLPUBLIC PDFiumSearchHandle
diff --git a/vcl/source/pdf/PDFiumLibrary.cxx b/vcl/source/pdf/PDFiumLibrary.cxx
index 9f23053b967d..35d5de43c4ae 100644
--- a/vcl/source/pdf/PDFiumLibrary.cxx
+++ b/vcl/source/pdf/PDFiumLibrary.cxx
@@ -17,6 +17,7 @@
 #include <fpdf_annot.h>
 #include <fpdf_edit.h>
 #include <fpdf_text.h>
+#include <fpdf_transformpage.h>
 #include <fpdf_save.h>
 #include <fpdf_signature.h>
 #include <fpdf_formfill.h>
@@ -299,6 +300,22 @@ public:
     PDFSegmentType getType() const override;
 };
 
+class PDFiumClipPathImpl final : public PDFiumClipPath
+{
+private:
+    FPDF_CLIPPATH mpClipPath;
+
+    PDFiumClipPathImpl(const PDFiumClipPathImpl&) = delete;
+    PDFiumClipPathImpl& operator=(const PDFiumClipPathImpl&) = delete;
+
+public:
+    PDFiumClipPathImpl(FPDF_CLIPPATH pClipPath);
+
+    int getPathCount() override;
+    int getPathSegmentCount(int nPathIndex) override;
+    std::unique_ptr<PDFiumPathSegment> getPathSegment(int nPathIndex, int 
nSegmentIndex) override;
+};
+
 class PDFiumAnnotationImpl final : public PDFiumAnnotation
 {
 private:
@@ -434,6 +451,9 @@ public:
     Size getImageSize(PDFiumPage& rPage) override;
     std::unique_ptr<PDFiumBitmap> getImageBitmap() override;
     bool getDrawMode(PDFFillMode& eFillMode, bool& bStroke) override;
+
+    // ClipPath
+    std::unique_ptr<PDFiumClipPath> getClipPath() override;
 };
 
 class PDFiumFontImpl final : public PDFiumFont
@@ -1388,6 +1408,17 @@ std::unique_ptr<PDFiumPathSegment> 
PDFiumPageObjectImpl::getPathSegment(int inde
     return pPDFiumPathSegment;
 }
 
+std::unique_ptr<PDFiumClipPath> PDFiumPageObjectImpl::getClipPath()
+{
+    std::unique_ptr<PDFiumClipPath> pPDFiumClipPath;
+    FPDF_CLIPPATH pClipPath = FPDFPageObj_GetClipPath(mpPageObject);
+    if (pClipPath)
+    {
+        pPDFiumClipPath = std::make_unique<PDFiumClipPathImpl>(pClipPath);
+    }
+    return pPDFiumClipPath;
+}
+
 Size PDFiumPageObjectImpl::getImageSize(PDFiumPage& rPage)
 {
     FPDF_IMAGEOBJ_METADATA aMeta;
@@ -1509,6 +1540,31 @@ PDFSegmentType PDFiumPathSegmentImpl::getType() const
     return static_cast<PDFSegmentType>(FPDFPathSegment_GetType(mpPathSegment));
 }
 
+PDFiumClipPathImpl::PDFiumClipPathImpl(FPDF_CLIPPATH pClipPath)
+    : mpClipPath(pClipPath)
+{
+}
+
+int PDFiumClipPathImpl::getPathCount() { return 
FPDFClipPath_CountPaths(mpClipPath); }
+
+int PDFiumClipPathImpl::getPathSegmentCount(int nPathIndex)
+{
+    return FPDFClipPath_CountPathSegments(mpClipPath, nPathIndex);
+}
+
+std::unique_ptr<PDFiumPathSegment> PDFiumClipPathImpl::getPathSegment(int 
nPathIndex,
+                                                                      int 
nSegmentIndex)
+{
+    std::unique_ptr<PDFiumPathSegment> pPDFiumPathSegment;
+    FPDF_PATHSEGMENT pPathSegment
+        = FPDFClipPath_GetPathSegment(mpClipPath, nPathIndex, nSegmentIndex);
+    if (pPathSegment)
+    {
+        pPDFiumPathSegment = 
std::make_unique<PDFiumPathSegmentImpl>(pPathSegment);
+    }
+    return pPDFiumPathSegment;
+}
+
 PDFiumFormHandle::PDFiumFormHandle(FPDF_FORMHANDLE pHandle)
     : mpHandle(pHandle)
 {
commit 8e9817b8ee5426b9a61ee8875dbbbbc5324ebeb7
Author:     Caolán McNamara <[email protected]>
AuthorDate: Tue Oct 28 15:02:25 2025 +0000
Commit:     Caolán McNamara <[email protected]>
CommitDate: Tue Oct 28 20:32:40 2025 +0100

    split out pdfium segments to polypolygon conversion
    
    Change-Id: Ib3af196943324a8ae543a1306c008060e53aa5a6
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193114
    Tested-by: Jenkins
    Reviewed-by: Caolán McNamara <[email protected]>

diff --git a/svx/source/inc/svdpdf.hxx b/svx/source/inc/svdpdf.hxx
index 7f6c0a6103a8..f3eee410d920 100644
--- a/svx/source/inc/svdpdf.hxx
+++ b/svx/source/inc/svdpdf.hxx
@@ -157,6 +157,11 @@ class ImpSdrPdfImport final
     void InsertObj(SdrObject* pObj, bool bScale = true);
     void MapScaling();
 
+    void appendSegmentsToPolyPoly(
+        basegfx::B2DPolyPolygon& rPolyPoly,
+        const std::vector<std::unique_ptr<vcl::pdf::PDFiumPathSegment>>& 
rPathSegments,
+        const basegfx::B2DHomMatrix& rPathMatrix);
+
     // #i73407# reformulation to use new B2DPolygon classes
     bool CheckLastPolyLineAndFillMerge(const basegfx::B2DPolyPolygon& 
rPolyPolygon);
 
diff --git a/svx/source/svdraw/svdpdf.cxx b/svx/source/svdraw/svdpdf.cxx
index 2cfe4595edae..cb358c636e6c 100644
--- a/svx/source/svdraw/svdpdf.cxx
+++ b/svx/source/svdraw/svdpdf.cxx
@@ -1987,26 +1987,20 @@ void 
ImpSdrPdfImport::ImportImage(std::unique_ptr<vcl::pdf::PDFiumPageObject> co
     InsertObj(pGraf.get());
 }
 
-void ImpSdrPdfImport::ImportPath(std::unique_ptr<vcl::pdf::PDFiumPageObject> 
const& pPageObject,
-                                 std::unique_ptr<vcl::pdf::PDFiumPage> const& 
pPage,
-                                 int /*nPageObjectIndex*/)
+void ImpSdrPdfImport::appendSegmentsToPolyPoly(
+    basegfx::B2DPolyPolygon& rPolyPoly,
+    const std::vector<std::unique_ptr<vcl::pdf::PDFiumPathSegment>>& 
rPathSegments,
+    const basegfx::B2DHomMatrix& rPathMatrix)
 {
-    auto aPathMatrix = pPageObject->getMatrix();
-
-    aPathMatrix *= maCurrentMatrix;
-
-    basegfx::B2DPolyPolygon aPolyPoly;
     basegfx::B2DPolygon aPoly;
     std::vector<basegfx::B2DPoint> aBezier;
 
-    const int nSegments = pPageObject->getPathSegmentCount();
-    for (int nSegmentIndex = 0; nSegmentIndex < nSegments; ++nSegmentIndex)
+    for (const auto& pPathSegment : rPathSegments)
     {
-        auto pPathSegment = pPageObject->getPathSegment(nSegmentIndex);
         if (pPathSegment != nullptr)
         {
             basegfx::B2DPoint aB2DPoint = pPathSegment->getPoint();
-            aB2DPoint *= aPathMatrix;
+            aB2DPoint *= rPathMatrix;
 
             const bool bClose = pPathSegment->isClosed();
             if (bClose)
@@ -2036,7 +2030,7 @@ void 
ImpSdrPdfImport::ImportPath(std::unique_ptr<vcl::pdf::PDFiumPageObject> con
                     // New Poly.
                     if (aPoly.count() > 0)
                     {
-                        aPolyPoly.append(aPoly, 1);
+                        rPolyPoly.append(aPoly, 1);
                         aPoly.clear();
                     }
 
@@ -2060,9 +2054,29 @@ void 
ImpSdrPdfImport::ImportPath(std::unique_ptr<vcl::pdf::PDFiumPageObject> con
 
     if (aPoly.count() > 0)
     {
-        aPolyPoly.append(aPoly, 1);
+        rPolyPoly.append(aPoly, 1);
         aPoly.clear();
     }
+}
+
+void ImpSdrPdfImport::ImportPath(std::unique_ptr<vcl::pdf::PDFiumPageObject> 
const& pPageObject,
+                                 std::unique_ptr<vcl::pdf::PDFiumPage> const& 
pPage,
+                                 int /*nPageObjectIndex*/)
+{
+    auto aPathMatrix = pPageObject->getMatrix();
+
+    aPathMatrix *= maCurrentMatrix;
+
+    basegfx::B2DPolyPolygon aPolyPoly;
+
+    std::vector<std::unique_ptr<vcl::pdf::PDFiumPathSegment>> aPathSegments;
+    const int nSegments = pPageObject->getPathSegmentCount();
+    for (int nSegmentIndex = 0; nSegmentIndex < nSegments; ++nSegmentIndex)
+    {
+        aPathSegments.push_back(pPageObject->getPathSegment(nSegmentIndex));
+    }
+
+    appendSegmentsToPolyPoly(aPolyPoly, aPathSegments, aPathMatrix);
 
     const basegfx::B2DHomMatrix aTransform(
         basegfx::utils::createScaleTranslateB2DHomMatrix(mfScaleX, mfScaleY, 
maOfs.X(), maOfs.Y()));
commit a987919caeb24fd8b34fd8d9e93280cd6e0b44ea
Author:     Caolán McNamara <[email protected]>
AuthorDate: Tue Oct 28 16:04:28 2025 +0000
Commit:     Caolán McNamara <[email protected]>
CommitDate: Tue Oct 28 20:32:33 2025 +0100

    handle errors from makeotf
    
    Change-Id: I97b702f32ca125fd2eb5c652b079ed94e521d47a
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193112
    Tested-by: Jenkins
    Reviewed-by: Caolán McNamara <[email protected]>

diff --git a/external/afdko/extern_makeotf.patch 
b/external/afdko/extern_makeotf.patch
index fd827b0f0ceb..4021d52dec45 100644
--- a/external/afdko/extern_makeotf.patch
+++ b/external/afdko/extern_makeotf.patch
@@ -66,6 +66,25 @@
  }
  
  // [fcdb callback] Report database parsing warning.
+@@ -1185,7 +1185,8 @@
+ 
+ /* Create new callback context */
+ cbCtx cbNew(char *progname, char *pfbdir, char *otfdir,
+-            char *cmapdir, char *featdir, dnaCtx mainDnaCtx) {
++            char *cmapdir, char *featdir, dnaCtx mainDnaCtx,
++            void (*fatal)(void*)) {
+     static hotCallbacks template = {
+         NULL, /* Callback context; set after creation */
+         myfatal,
+@@ -1233,6 +1234,8 @@
+     h->dir.cmap = cmapdir;
+ 
+     h->hot.cb = template; /* Copy template */
++    if (fatal)
++      h->hot.cb.fatal = fatal;
+     h->hot.cb.ctx = h;
+ 
+     h->dnaCtx = mainDnaCtx;
 --- afdko/c/makeotf/source/file.c      2025-09-23 13:18:57.919265608 +0100
 +++ afdko/c/makeotf/source/file.c      2025-09-23 13:20:04.148358137 +0100
 @@ -23,7 +23,7 @@
@@ -314,6 +333,16 @@
  typedef struct cbCtx_ *cbCtx;
  
  /* Define to supply Microsoft-specific function calling info, e.g. __cdecl */
+@@ -22,7 +22,8 @@
+ 
+ /* --- Basic functions --- */
+ extern cbCtx cbNew(char *progname, char *pfbdir, char *otfdir,
+-                   char *cmapdir, char *featdir, dnaCtx mainDnaCtx);
++                   char *cmapdir, char *featdir, dnaCtx mainDnaCtx,
++                   void (*fatal)(void*));
+ extern void cbConvert(cbCtx h, int flags, char *clientVers,
+                       char *pfbfile, char *otffile,
+                       char *featurefile, char *hcmapfile, char *vcmapfile,
 @@ -80,4 +85,8 @@
  #define OTHERFLAGS_VERBOSE (1 << 15)
  #define OTHERFLAGS_FINAL_NAMES (1 << 16)
diff --git a/vcl/source/gdi/embeddedfontsafdko.cxx 
b/vcl/source/gdi/embeddedfontsafdko.cxx
index a2b83cdb4161..0d4a3cee75a4 100644
--- a/vcl/source/gdi/embeddedfontsafdko.cxx
+++ b/vcl/source/gdi/embeddedfontsafdko.cxx
@@ -295,11 +295,14 @@ static void* cb_memory(ctlMemoryCallbacks* /*cb*/, void* 
old, size_t size)
 }
 #endif
 
+static void makeOtfFatalCallback(void*) { throw std::runtime_error("fatal tx 
error"); }
+
 // 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)
 {
+    bool ret = false;
 #if HAVE_FEATURE_AFDKO
     OUString srcFontPath, destFilePath, charMapPath, fontMenuNameDBPath, 
featuresPath;
     if (osl::FileBase::E_None != 
osl::FileBase::getSystemPathFromFileURL(srcFontUrl, srcFontPath)
@@ -331,8 +334,9 @@ bool EmbeddedFontsManager::makeotf(const OUString& 
srcFontUrl, const OUString& d
     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);
+    cbCtx cbctx
+        = cbNew(nullptr, const_cast<char*>(""), const_cast<char*>(""), 
const_cast<char*>(""),
+                const_cast<char*>(""), mainDnaCtx, makeOtfFatalCallback);
 
     OString fontMenuNameDBPathA(fontMenuNameDBPath.toUtf8());
     OString srcFontPathA(srcFontPath.toUtf8());
@@ -346,31 +350,40 @@ bool EmbeddedFontsManager::makeotf(const OUString& 
srcFontUrl, const OUString& d
                          << (!charMapPathA.isEmpty() ? " -ch "_ostr + 
charMapPathA : OString())
                          << (!featuresPathA.isEmpty() ? " -ff "_ostr + 
featuresPathA : OString()));
 
-    cbFCDBRead(cbctx, const_cast<char*>(fontMenuNameDBPathA.getStr()));
+    ret = true;
+    try
+    {
+        cbFCDBRead(cbctx, const_cast<char*>(fontMenuNameDBPathA.getStr()));
 
-    int flags = HOT_NO_OLD_OPS;
-    int fontConvertFlags = 0;
+        int flags = HOT_NO_OLD_OPS;
+        int fontConvertFlags = 0;
 #if !SUPERVERBOSE
-    flags |= HOT_SUPRESS_WARNINGS | HOT_SUPRESS_HINT_WARNINGS;
+        flags |= HOT_SUPRESS_WARNINGS | HOT_SUPRESS_HINT_WARNINGS;
 #else
-    fontConvertFlags |= HOT_CONVERT_VERBOSE;
+        fontConvertFlags |= HOT_CONVERT_VERBOSE;
 #endif
 
-    cbConvert(cbctx, flags, nullptr, const_cast<char*>(srcFontPathA.getStr()),
-              const_cast<char*>(destFilePathA.getStr()),
-              !featuresPathA.isEmpty() ? 
const_cast<char*>(featuresPathA.getStr()) : nullptr,
-              !charMapPathA.isEmpty() ? 
const_cast<char*>(charMapPathA.getStr()) : nullptr, nullptr,
-              nullptr, nullptr, fontConvertFlags, 0, 0, 0, 0, -1, -1, 0, 
nullptr);
+        cbConvert(cbctx, flags, nullptr, 
const_cast<char*>(srcFontPathA.getStr()),
+                  const_cast<char*>(destFilePathA.getStr()),
+                  !featuresPathA.isEmpty() ? 
const_cast<char*>(featuresPathA.getStr()) : nullptr,
+                  !charMapPathA.isEmpty() ? 
const_cast<char*>(charMapPathA.getStr()) : nullptr,
+                  nullptr, nullptr, nullptr, fontConvertFlags, 0, 0, 0, 0, -1, 
-1, 0, nullptr);
+    }
+    catch (const std::exception& e)
+    {
+        SAL_WARN("vcl.fonts", "mergeFonts failure: " << e.what());
+        ret = false;
+    }
 
-    return true;
+    cbFree(cbctx);
 #else
     (void)srcFontUrl;
     (void)destFileUrl;
     (void)fontMenuNameDBUrl;
     (void)charMapUrl;
     (void)featuresUrl;
-    return false;
 #endif
+    return ret;
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */

Reply via email to