external/pdfium/UnpackedTarball_pdfium.mk |    1 
 external/pdfium/extractpatterns.patch     |  183 ++++++++++++++++++++++++++++++
 include/vcl/filter/PDFiumLibrary.hxx      |    6 
 sd/qa/unit/data/pdf/pattern-fill.pdf      |binary
 sd/qa/unit/data/pdf/pattern-stroke.pdf    |binary
 sd/qa/unit/export-tests.cxx               |   42 ++++++
 svx/source/inc/svdpdf.hxx                 |   12 +
 svx/source/svdraw/svdpdf.cxx              |  153 ++++++++++++++++++++-----
 vcl/source/pdf/PDFiumLibrary.cxx          |   34 +++++
 9 files changed, 405 insertions(+), 26 deletions(-)

New commits:
commit 4c1f88e1eaf030ff30184bc6191824c38eb8659d
Author:     Caolán McNamara <[email protected]>
AuthorDate: Tue Oct 21 21:07:28 2025 +0100
Commit:     Caolán McNamara <[email protected]>
CommitDate: Sun Oct 26 12:07:44 2025 +0100

    stash and elide global font matrix and overwrite local one with it
    
    rather than let both of them pass through, where conversion to
    otf of input with:
    
    /FontMatrix [0.0005 0 0 0.0005 0 0] def
    ...
    ...
    %ADOBeginFontDict
    ...
    /FontMatrix [1 0 0 1 0 0] def
    
    otherwise then results in output of otf with UnitsPerEm of 1 which then
    freetype rejects as invalid and so nothing is rendered.
    
    Change-Id: I037a2bea1b1caf5259af40c59cc529afe63db998
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192809
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Miklos Vajna <[email protected]>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192988
    Tested-by: Jenkins
    Reviewed-by: Caolán McNamara <[email protected]>

diff --git a/svx/source/svdraw/svdpdf.cxx b/svx/source/svdraw/svdpdf.cxx
index 0b203f8d6a00..ff9346c005a3 100644
--- a/svx/source/svdraw/svdpdf.cxx
+++ b/svx/source/svdraw/svdpdf.cxx
@@ -969,8 +969,12 @@ static bool isSimpleFamilyName(std::string_view Weight)
            || Weight == "BoldItalic";
 }
 
-static void rewriteBrokenFontName(std::string_view brokenName, 
std::string_view brokenCIDName,
-                                  std::string_view fixedName, const OUString& 
pfaCIDUrl)
+// a) change brokenName/brokenCIDName if present to fixedName
+// b) remove preamble FontMatrix and overwrite following ones in the 
%ADOBeginFontDict
+// section with that content instead to avoid generating fonts with unusual 
UnitsPerEm
+// of 1 that freetype will reject
+static void rewriteFont(std::string_view brokenName, std::string_view 
brokenCIDName,
+                        std::string_view fixedName, const OUString& pfaCIDUrl)
 {
     OUString oldCIDUrl = pfaCIDUrl + ".broken";
     if (osl::File::move(pfaCIDUrl, oldCIDUrl) != osl::File::E_None)
@@ -979,6 +983,12 @@ static void rewriteBrokenFontName(std::string_view 
brokenName, std::string_view
         return;
     }
 
+    const bool rewriteNames = !brokenName.empty();
+
+    bool fontDict = false;
+
+    OString sGlobalMatrix;
+
     const OString sBrokenFontLine = "/FontName /"_ostr + brokenName + " 
def"_ostr;
     const OString sFixedFontLine = "/FontName /"_ostr + fixedName + " 
def"_ostr;
 
@@ -990,16 +1000,40 @@ static void rewriteBrokenFontName(std::string_view 
brokenName, std::string_view
     OString sLine;
     while (input.ReadLine(sLine))
     {
-        if (sLine == sBrokenFontLine)
+        if (rewriteNames)
         {
-            output.WriteLine(sFixedFontLine);
-            continue;
+            if (sLine == sBrokenFontLine)
+            {
+                output.WriteLine(sFixedFontLine);
+                continue;
+            }
+            else if (sLine == sBrokenCIDFontLine)
+            {
+                output.WriteLine(sFixedCIDFontLine);
+                continue;
+            }
         }
-        else if (sLine == sBrokenCIDFontLine)
+
+        if (sLine.startsWith("%ADOBeginFontDict"))
+            fontDict = true;
+        else if (sLine.startsWith("/FontMatrix "))
         {
-            output.WriteLine(sFixedCIDFontLine);
-            continue;
+            if (!fontDict)
+            {
+                // Global case, stash and don't emit the global matrix
+                sGlobalMatrix = sLine;
+                continue;
+            }
+
+            if (!sGlobalMatrix.isEmpty())
+            {
+                // Local case, emit the global matrix instead if
+                // there was one, otherwise emit the local matrix
+                output.WriteLine(sGlobalMatrix);
+                continue;
+            }
         }
+
         output.WriteLine(sLine);
         if (sLine.startsWith("%%BeginData"))
         {
@@ -1027,7 +1061,7 @@ static bool toPfaCID(SubSetInfo& rSubSetInfo, const 
OUString& fileUrl,
     OUString toMergedMapUrl = fileUrl + u".tomergedmap";
 
     OString version, Notice, FullName, FamilyName, CIDFontName, 
CIDFontVersion, srcFontType,
-        glyphTag;
+        glyphTag, FontMatrix;
     OString brokenFontName;
     FontName = postScriptName.toUtf8();
     std::map<sal_Int32, OString> glyphIndexToName;
@@ -1067,6 +1101,8 @@ static bool toPfaCID(SubSetInfo& rSubSetInfo, const 
OUString& fileUrl,
             continue;
         if (extractEntry(sLine, "Weight", Weight))
             continue;
+        if (extractEntry(sLine, "FontMatrix", FontMatrix))
+            continue;
         if (extractEntry(sLine, "sup.srcFontType", srcFontType))
             continue;
         if (extractEntry(sLine, "## glyph[tag]", glyphTag))
@@ -1218,8 +1254,12 @@ static bool toPfaCID(SubSetInfo& rSubSetInfo, const 
OUString& fileUrl,
         }
     }
 
-    if (!brokenFontName.isEmpty())
-        rewriteBrokenFontName(brokenFontName, CIDFontName, FontName, 
pfaCIDUrl);
+    if (!brokenFontName.isEmpty() || !FontMatrix.isEmpty())
+    {
+        // If the fontname isn't as expected, or if there is a
+        // font matrix present then we rewrite the font.
+        rewriteFont(brokenFontName, CIDFontName, FontName, pfaCIDUrl);
+    }
 
     return true;
 }
commit cfb9b5eed006947caed0687798253c5b65c983ab
Author:     Caolán McNamara <[email protected]>
AuthorDate: Thu Oct 16 16:06:38 2025 +0100
Commit:     Caolán McNamara <[email protected]>
CommitDate: Sun Oct 26 12:07:35 2025 +0100

    extract fill/stroke patterns as bitmaps
    
    We can apply fill patterns as tiled bitmap fill.
    
    For strokes/text-color apply center color of pattern as the color to
    use.
    
    Change-Id: I123184d19363c0f0e0a2988108efdb71ff3d34f4
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192552
    Reviewed-by: Miklos Vajna <[email protected]>
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192987
    Tested-by: Caolán McNamara <[email protected]>
    Reviewed-by: Caolán McNamara <[email protected]>

diff --git a/external/pdfium/UnpackedTarball_pdfium.mk 
b/external/pdfium/UnpackedTarball_pdfium.mk
index a7c61a86a8a9..8df29bbf3e6f 100644
--- a/external/pdfium/UnpackedTarball_pdfium.mk
+++ b/external/pdfium/UnpackedTarball_pdfium.mk
@@ -28,6 +28,7 @@ endif
 
 # TODO, attempt upstream
 pdfium_patches += ofz451333752.patch
+pdfium_patches += extractpatterns.patch
 
 $(eval $(call gb_UnpackedTarball_UnpackedTarball,pdfium))
 
diff --git a/external/pdfium/extractpatterns.patch 
b/external/pdfium/extractpatterns.patch
new file mode 100644
index 000000000000..377ab0b0472a
--- /dev/null
+++ b/external/pdfium/extractpatterns.patch
@@ -0,0 +1,183 @@
+--- core/fpdfapi/page/cpdf_pattern.h   2025-10-17 15:44:08.393532043 +0100
++++ core/fpdfapi/page/cpdf_pattern.h   2025-10-17 15:44:12.020459390 +0100
+@@ -26,6 +26,7 @@
+   virtual CPDF_ShadingPattern* AsShadingPattern();
+ 
+   const CFX_Matrix& pattern_to_form() const { return pattern_to_form_; }
++  RetainPtr<CPDF_Object> pattern_obj() const { return pattern_obj_; }
+ 
+  protected:
+   CPDF_Pattern(CPDF_Document* pDoc,
+@@ -35,7 +36,6 @@
+ 
+   // All the getters that return pointers return non-NULL pointers.
+   CPDF_Document* document() const { return document_; }
+-  RetainPtr<CPDF_Object> pattern_obj() const { return pattern_obj_; }
+   const CFX_Matrix& parent_matrix() const { return parent_matrix_; }
+ 
+   void SetPatternToFormMatrix();
+--- fpdfsdk/fpdf_edittext.cpp  2025-10-17 15:44:08.410532151 +0100
++++ fpdfsdk/fpdf_edittext.cpp  2025-10-17 15:55:18.804847559 +0100
+@@ -13,6 +13,9 @@
+ #include "core/fpdfapi/font/cpdf_cidfont.h"
+ #include "core/fpdfapi/font/cpdf_font.h"
+ #include "core/fpdfapi/page/cpdf_docpagedata.h"
++#include "core/fpdfapi/page/cpdf_form.h"
++#include "core/fpdfapi/page/cpdf_pathobject.h"
++#include "core/fpdfapi/page/cpdf_tilingpattern.h"
+ #include "core/fpdfapi/page/cpdf_textobject.h"
+ #include "core/fpdfapi/page/cpdf_textstate.h"
+ #include "core/fpdfapi/parser/cpdf_array.h"
+@@ -834,6 +835,108 @@
+   return FPDFBitmapFromCFXDIBitmap(result_bitmap.Leak());
+ }
+ 
++FPDF_BITMAP
++FPDFPageObj_GetRenderedPattern(CPDF_Pattern& pattern,
++                               CPDF_Document& doc,
++                               CPDF_PageObject& pageObj,
++                               CPDF_Page* optional_page) {
++  CPDF_TilingPattern* pTilingPattern = pattern.AsTilingPattern();
++  if (!pTilingPattern)
++      return nullptr;
++  const std::unique_ptr<CPDF_Form> pPatternForm = 
pTilingPattern->Load(&pageObj);
++  if (!pPatternForm)
++    return nullptr;
++  RetainPtr<const CPDF_Dictionary> pDict = pattern.pattern_obj()->GetDict();
++  CFX_FloatRect pattern_bbox = pDict->GetRectFor("BBox");
++
++  // `rect` has to use integer values. Round up as needed.
++  const FX_RECT rect = pattern_bbox.GetOuterRect();
++  if (rect.IsEmpty())
++    return nullptr;
++
++  auto result_bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
++  if (!result_bitmap->Create(rect.Width(), rect.Height(),
++                             FXDIB_Format::kBgra)) {
++    return nullptr;
++  }
++  CFX_FloatRect cell_bbox = 
pattern.pattern_to_form().TransformRect(pattern_bbox);
++  CFX_FloatRect bitmap_rect(0.0f, 0.0f, rect.Width(), rect.Height());
++  CFX_Matrix mtPattern2Bitmap;
++  mtPattern2Bitmap.MatchRect(bitmap_rect, cell_bbox);
++
++  CFX_DefaultRenderDevice bitmap_device;
++  bitmap_device.AttachWithBackdropAndGroupKnockout(
++      result_bitmap, /*pBackdropBitmap=*/nullptr, /*bGroupKnockout=*/true);
++
++  CPDF_RenderOptions options;
++  if (!pTilingPattern->colored())
++    options.SetColorMode(CPDF_RenderOptions::kAlpha);
++  options.GetOptions().bForceHalftone = true;
++
++  CPDF_RenderContext context(&doc, nullptr, nullptr);
++  context.AppendLayer(pPatternForm.get(), mtPattern2Bitmap);
++  context.Render(&bitmap_device, nullptr, &options, nullptr);
++
++  CHECK(!result_bitmap->IsPremultiplied());
++
++  // Caller takes ownership.
++  return FPDFBitmapFromCFXDIBitmap(result_bitmap.Leak());
++}
++
++FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV
++FPDFPageObj_GetRenderedStrokePattern(FPDF_DOCUMENT document,
++                                     FPDF_PAGE page,
++                                     FPDF_PAGEOBJECT page_object) {
++  CPDF_Document* doc = CPDFDocumentFromFPDFDocument(document);
++  if (!doc)
++    return nullptr;
++
++  CPDF_Page* optional_page = CPDFPageFromFPDFPage(page);
++  if (optional_page && optional_page->GetDocument() != doc)
++    return nullptr;
++
++  CPDF_PageObject* object = CPDFPageObjectFromFPDFPageObject(page_object);
++  if (!object)
++    return nullptr;
++
++  const CPDF_Color* stroke = object->color_state().GetStrokeColor();
++  if (!stroke || !stroke->IsPattern())
++    return nullptr;
++
++  RetainPtr<CPDF_Pattern> pattern = stroke->GetPattern();
++  if (!pattern)
++    return nullptr;
++
++  return FPDFPageObj_GetRenderedPattern(*pattern, *doc, *object, 
optional_page);
++}
++
++FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV
++FPDFPageObj_GetRenderedFillPattern(FPDF_DOCUMENT document,
++                                   FPDF_PAGE page,
++                                   FPDF_PAGEOBJECT page_object) {
++  CPDF_Document* doc = CPDFDocumentFromFPDFDocument(document);
++  if (!doc)
++    return nullptr;
++
++  CPDF_Page* optional_page = CPDFPageFromFPDFPage(page);
++  if (optional_page && optional_page->GetDocument() != doc)
++    return nullptr;
++
++  CPDF_PageObject* object = CPDFPageObjectFromFPDFPageObject(page_object);
++  if (!object)
++    return nullptr;
++
++  const CPDF_Color* fill = object->color_state().GetFillColor();
++  if (!fill || !fill->IsPattern())
++    return nullptr;
++
++  RetainPtr<CPDF_Pattern> pattern = fill->GetPattern();
++  if (!pattern)
++    return nullptr;
++
++  return FPDFPageObj_GetRenderedPattern(*pattern, *doc, *object, 
optional_page);
++}
++
+ FPDF_EXPORT void FPDF_CALLCONV FPDFFont_Close(FPDF_FONT font) {
+   // Take back ownership from caller and release.
+   RetainPtr<CPDF_Font>().Unleak(CPDFFontFromFPDFFont(font));
+--- public/fpdf_edit.h 2025-10-17 15:44:08.415888612 +0100
++++ public/fpdf_edit.h 2025-10-17 15:44:12.022555158 +0100
+@@ -1417,6 +1417,41 @@
+                               float scale);
+ 
+ // Experimental API.
++// Get a bitmap rasterization of the stroke pattern of |page object|.
++// To render correctly, the caller must provide the |document| associated with
++// |page_object|. If there is a |page| associated with |page_object|, the
++// caller should provide that as well.  The returned bitmap will be owned by
++// the caller, and FPDFBitmap_Destroy() must be called on the returned bitmap
++// when it is no longer needed.
++//
++//   document     - handle to a document associated with |page_object|.
++//   page         - handle to an optional page associated with |page_object|.
++//   page_object - handle to a page object.
++//
++// Returns the bitmap or NULL if the stroke is not a pattern or on failure.
++FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV
++FPDFPageObj_GetRenderedStrokePattern(FPDF_DOCUMENT document,
++                                     FPDF_PAGE page,
++                                     FPDF_PAGEOBJECT page_object);
++
++// Get a bitmap rasterization of the fill pattern of |page object|.
++// To render correctly, the caller must provide the |document| associated with
++// |page_object|. If there is a |page| associated with |page_object|, the
++// caller should provide that as well.  The returned bitmap will be owned by
++// the caller, and FPDFBitmap_Destroy() must be called on the returned bitmap
++// when it is no longer needed.
++//
++//   document     - handle to a document associated with |page_object|.
++//   page         - handle to an optional page associated with |page_object|.
++//   page_object - handle to a page object.
++//
++// Returns the bitmap or NULL if the fill is not a pattern or on failure.
++FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV
++FPDFPageObj_GetRenderedFillPattern(FPDF_DOCUMENT document,
++                                   FPDF_PAGE page,
++                                   FPDF_PAGEOBJECT page_object);
++
++// Experimental API.
+ // Get the font of a text object.
+ //
+ // text - the handle to the text object.
diff --git a/include/vcl/filter/PDFiumLibrary.hxx 
b/include/vcl/filter/PDFiumLibrary.hxx
index 699ef04ba2e9..36bfa8743ac7 100644
--- a/include/vcl/filter/PDFiumLibrary.hxx
+++ b/include/vcl/filter/PDFiumLibrary.hxx
@@ -165,8 +165,14 @@ public:
     virtual bool getFontProperties(FontWeight& weight) = 0;
     virtual PDFTextRenderMode getTextRenderMode() = 0;
     virtual Color getFillColor() = 0;
+    virtual std::unique_ptr<PDFiumBitmap> 
getRenderedFillPattern(PDFiumDocument& rDoc,
+                                                                 PDFiumPage& 
rPage)
+        = 0;
     virtual Color getStrokeColor() = 0;
     virtual double getStrokeWidth() = 0;
+    virtual std::unique_ptr<PDFiumBitmap> 
getRenderedStrokePattern(PDFiumDocument& rDoc,
+                                                                   PDFiumPage& 
rPage)
+        = 0;
     // Path
     virtual int getPathSegmentCount() = 0;
     virtual std::unique_ptr<PDFiumPathSegment> getPathSegment(int index) = 0;
diff --git a/sd/qa/unit/data/pdf/pattern-fill.pdf 
b/sd/qa/unit/data/pdf/pattern-fill.pdf
new file mode 100644
index 000000000000..d5c4f0acebf9
Binary files /dev/null and b/sd/qa/unit/data/pdf/pattern-fill.pdf differ
diff --git a/sd/qa/unit/data/pdf/pattern-stroke.pdf 
b/sd/qa/unit/data/pdf/pattern-stroke.pdf
new file mode 100644
index 000000000000..ce26b39e295c
Binary files /dev/null and b/sd/qa/unit/data/pdf/pattern-stroke.pdf differ
diff --git a/sd/qa/unit/export-tests.cxx b/sd/qa/unit/export-tests.cxx
index 66d9e1cfbc57..1eb3b00f6adb 100644
--- a/sd/qa/unit/export-tests.cxx
+++ b/sd/qa/unit/export-tests.cxx
@@ -1235,6 +1235,48 @@ CPPUNIT_TEST_FIXTURE(SdExportTest, 
testExplodedPdfEmbeddedFonts)
 #endif
 }
 
+CPPUNIT_TEST_FIXTURE(SdExportTest, testExplodedPdfPatternStroke)
+{
+    auto pPdfium = vcl::pdf::PDFiumLibrary::get();
+    if (!pPdfium)
+        return;
+    UsePdfium aGuard;
+
+    loadFromFile(u"pdf/pattern-stroke.pdf");
+
+    
setFilterOptions("{\"DecomposePDF\":{\"type\":\"boolean\",\"value\":\"true\"}}");
+    save(u"OpenDocument Drawing Flat XML"_ustr);
+
+    xmlDocUniquePtr pXmlDoc = parseExportedFile();
+
+    // ensure the stroke color is this redish color, and not gray which is 
what it
+    // defaults to if the stroke pattern isn't taken into account.
+    assertXPath(pXmlDoc, 
"/office:document/office:automatic-styles/style:style[@style:name='gr1']/"
+                         
"style:graphic-properties[@svg:stroke-color='#ed1b2d']");
+}
+
+CPPUNIT_TEST_FIXTURE(SdExportTest, testExplodedPdfPatternFill)
+{
+    auto pPdfium = vcl::pdf::PDFiumLibrary::get();
+    if (!pPdfium)
+        return;
+    UsePdfium aGuard;
+
+    loadFromFile(u"pdf/pattern-fill.pdf");
+
+    
setFilterOptions("{\"DecomposePDF\":{\"type\":\"boolean\",\"value\":\"true\"}}");
+    save(u"OpenDocument Drawing Flat XML"_ustr);
+
+    xmlDocUniquePtr pXmlDoc = parseExportedFile();
+
+    // ensure the stroke color is this redish color, and not gray which is 
what it
+    // defaults to if the stroke pattern isn't taken into account.
+    assertXPath(pXmlDoc, 
"/office:document/office:automatic-styles/style:style[@style:name='gr1']/"
+                         "style:graphic-properties[@style:repeat='repeat' and "
+                         "@draw:fill-image-width='1.27cm' and 
@draw:fill-image-height='1.27cm' and "
+                         "@draw:fill-image-name='Bitmap_20_1']");
+}
+
 CPPUNIT_TEST_FIXTURE(SdExportTest, testEmbeddedText)
 {
     createSdDrawDoc("objectwithtext.fodg");
diff --git a/svx/source/inc/svdpdf.hxx b/svx/source/inc/svdpdf.hxx
index f215c1f05938..c6a331adf044 100644
--- a/svx/source/inc/svdpdf.hxx
+++ b/svx/source/inc/svdpdf.hxx
@@ -107,6 +107,8 @@ class ImpSdrPdfImport final
     sal_Int32 mnLineWidth;
     static constexpr css::drawing::LineCap gaLineCap = 
css::drawing::LineCap_BUTT;
     XDash maDash;
+    std::optional<Color> moFillColor;
+    std::optional<Bitmap> moFillPattern;
 
     bool mbMov;
     bool mbSize;
@@ -145,17 +147,25 @@ class ImpSdrPdfImport final
     void checkClip();
     bool isClip() const;
 
+    Color getStrokeColor(std::unique_ptr<vcl::pdf::PDFiumPageObject> const& 
pPageObject,
+                         std::unique_ptr<vcl::pdf::PDFiumPage> const& pPage);
+    Color getFillColor(std::unique_ptr<vcl::pdf::PDFiumPageObject> const& 
pPageObject,
+                       std::unique_ptr<vcl::pdf::PDFiumPage> const& pPage);
+
     void ImportPdfObject(std::unique_ptr<vcl::pdf::PDFiumPageObject> const& 
pPageObject,
+                         std::unique_ptr<vcl::pdf::PDFiumPage> const& pPage,
                          std::unique_ptr<vcl::pdf::PDFiumTextPage> const& 
pTextPage,
                          int nPageObjectIndex);
     void ImportForm(std::unique_ptr<vcl::pdf::PDFiumPageObject> const& 
pPageObject,
+                    std::unique_ptr<vcl::pdf::PDFiumPage> const& pPage,
                     std::unique_ptr<vcl::pdf::PDFiumTextPage> const& pTextPage,
                     int nPageObjectIndex);
     void ImportImage(std::unique_ptr<vcl::pdf::PDFiumPageObject> const& 
pPageObject,
                      int nPageObjectIndex);
     void ImportPath(std::unique_ptr<vcl::pdf::PDFiumPageObject> const& 
pPageObject,
-                    int nPageObjectIndex);
+                    std::unique_ptr<vcl::pdf::PDFiumPage> const& pPage, int 
nPageObjectIndex);
     void ImportText(std::unique_ptr<vcl::pdf::PDFiumPageObject> const& 
pPageObject,
+                    std::unique_ptr<vcl::pdf::PDFiumPage> const& pPage,
                     std::unique_ptr<vcl::pdf::PDFiumTextPage> const& pTextPage,
                     int nPageObjectIndex);
     void InsertTextObject(const Point& rPos, const Size& rSize, const 
OUString& rStr,
diff --git a/svx/source/svdraw/svdpdf.cxx b/svx/source/svdraw/svdpdf.cxx
index 65e108e9fd58..0b203f8d6a00 100644
--- a/svx/source/svdraw/svdpdf.cxx
+++ b/svx/source/svdraw/svdpdf.cxx
@@ -31,6 +31,7 @@
 #include <editeng/udlnitem.hxx>
 #include <editeng/crossedoutitem.hxx>
 #include <editeng/shdditem.hxx>
+#include <svx/xflbmsxy.hxx>
 #include <svx/xlnclit.hxx>
 #include <svx/xlncapit.hxx>
 #include <svx/xlnwtit.hxx>
@@ -125,7 +126,6 @@ ImpSdrPdfImport::ImpSdrPdfImport(SdrModel& rModel, 
SdrLayerID nLay, const tools:
     mpVD->SetMapMode(MapMode(MapUnit::Map100thMM));
     mpVD->EnableOutput(false);
     mpVD->SetLineColor();
-    mpVD->SetFillColor();
 
     // Get TextBounds relative to baseline
     vcl::Font aFnt = mpVD->GetFont();
@@ -379,7 +379,7 @@ void ImpSdrPdfImport::DoObjects(SvdProgressInfo* 
pProgrInfo, sal_uInt32* pAction
     for (int nPageObjectIndex = 0; nPageObjectIndex < nPageObjectCount; 
++nPageObjectIndex)
     {
         auto pPageObject = pPdfPage->getObject(nPageObjectIndex);
-        ImportPdfObject(pPageObject, pTextPage, nPageObjectIndex);
+        ImportPdfObject(pPageObject, pPdfPage, pTextPage, nPageObjectIndex);
         if (pProgrInfo && pActionsToReport)
         {
             (*pActionsToReport)++;
@@ -552,10 +552,29 @@ void ImpSdrPdfImport::SetAttributes(SdrObject* pObj, bool 
bForceTextAttr)
 
     if (bFill)
     {
-        if (mpVD->IsFillColor())
+        bool doFill
+            = mpVD->GetDrawMode() != DrawModeFlags::NoFill && (moFillColor || 
moFillPattern);
+        if (doFill && moFillColor)
+            doFill = !moFillColor->IsTransparent();
+        if (doFill)
         {
-            mpFillAttr->Put(XFillStyleItem(drawing::FillStyle_SOLID));
-            mpFillAttr->Put(XFillColorItem(OUString(), mpVD->GetFillColor()));
+            if (moFillColor)
+            {
+                mpFillAttr->Put(XFillStyleItem(drawing::FillStyle_SOLID));
+                mpFillAttr->Put(XFillColorItem(OUString(), *moFillColor));
+            }
+            else
+            {
+                assert(moFillPattern && "pattern should exist");
+                mpFillAttr->Put(XFillStyleItem(drawing::FillStyle_BITMAP));
+                mpFillAttr->Put(XFillBitmapItem(OUString(), 
Graphic(*moFillPattern)));
+                mpFillAttr->Put(XFillBmpStretchItem(false));
+                mpFillAttr->Put(XFillBmpTileItem(true));
+                mpFillAttr->Put(
+                    
XFillBmpSizeXItem(convertPointToMm100(moFillPattern->GetSizePixel().Width())));
+                mpFillAttr->Put(
+                    
XFillBmpSizeYItem(convertPointToMm100(moFillPattern->GetSizePixel().Height())));
+            }
         }
         else
         {
@@ -877,6 +896,7 @@ void ImpSdrPdfImport::checkClip()
 bool ImpSdrPdfImport::isClip() const { return !maClip.getB2DRange().isEmpty(); 
}
 void ImpSdrPdfImport::ImportPdfObject(
     std::unique_ptr<vcl::pdf::PDFiumPageObject> const& pPageObject,
+    std::unique_ptr<vcl::pdf::PDFiumPage> const& pPage,
     std::unique_ptr<vcl::pdf::PDFiumTextPage> const& pTextPage, int 
nPageObjectIndex)
 {
     if (!pPageObject)
@@ -886,10 +906,10 @@ void ImpSdrPdfImport::ImportPdfObject(
     switch (ePageObjectType)
     {
         case vcl::pdf::PDFPageObjectType::Text:
-            ImportText(pPageObject, pTextPage, nPageObjectIndex);
+            ImportText(pPageObject, pPage, pTextPage, nPageObjectIndex);
             break;
         case vcl::pdf::PDFPageObjectType::Path:
-            ImportPath(pPageObject, nPageObjectIndex);
+            ImportPath(pPageObject, pPage, nPageObjectIndex);
             break;
         case vcl::pdf::PDFPageObjectType::Image:
             ImportImage(pPageObject, nPageObjectIndex);
@@ -898,7 +918,7 @@ void ImpSdrPdfImport::ImportPdfObject(
             SAL_WARN("sd.filter", "Got page object SHADING: " << 
nPageObjectIndex);
             break;
         case vcl::pdf::PDFPageObjectType::Form:
-            ImportForm(pPageObject, pTextPage, nPageObjectIndex);
+            ImportForm(pPageObject, pPage, pTextPage, nPageObjectIndex);
             break;
         default:
             SAL_WARN("sd.filter", "Unknown PDF page object #" << 
nPageObjectIndex << " of type: "
@@ -908,6 +928,7 @@ void ImpSdrPdfImport::ImportPdfObject(
 }
 
 void ImpSdrPdfImport::ImportForm(std::unique_ptr<vcl::pdf::PDFiumPageObject> 
const& pPageObject,
+                                 std::unique_ptr<vcl::pdf::PDFiumPage> const& 
pPage,
                                  std::unique_ptr<vcl::pdf::PDFiumTextPage> 
const& pTextPage,
                                  int /*nPageObjectIndex*/)
 {
@@ -921,7 +942,7 @@ void 
ImpSdrPdfImport::ImportForm(std::unique_ptr<vcl::pdf::PDFiumPageObject> con
     {
         auto pFormObject = pPageObject->getFormObject(nIndex);
 
-        ImportPdfObject(pFormObject, pTextPage, -1);
+        ImportPdfObject(pFormObject, pPage, pTextPage, -1);
     }
 
     // Restore the old one.
@@ -1753,7 +1774,39 @@ EmbeddedFontInfo ImpSdrPdfImport::convertToOTF(sal_Int64 
prefix, SubSetInfo& rSu
     return EmbeddedFontInfo();
 }
 
+// There isn't, as far as I know, a way to stroke with a pattern at the moment,
+// so extract some sensible color if this is a stroke pattern
+Color ImpSdrPdfImport::getStrokeColor(
+    std::unique_ptr<vcl::pdf::PDFiumPageObject> const& pPageObject,
+    std::unique_ptr<vcl::pdf::PDFiumPage> const& pPage)
+{
+    if (std::unique_ptr<vcl::pdf::PDFiumBitmap> bitmap
+        = pPageObject->getRenderedStrokePattern(*mpPdfDocument, *pPage))
+    {
+        Bitmap aBitmap(bitmap->createBitmapFromBuffer());
+        return aBitmap.GetPixelColor(aBitmap.GetSizePixel().Width() / 2,
+                                     aBitmap.GetSizePixel().Height() / 2);
+    }
+    return pPageObject->getStrokeColor();
+}
+
+// Typically for a fill pattern you want to use some pattern fill equivalent
+// but if that's not possible then this fallback can be useful
+Color 
ImpSdrPdfImport::getFillColor(std::unique_ptr<vcl::pdf::PDFiumPageObject> 
const& pPageObject,
+                                    std::unique_ptr<vcl::pdf::PDFiumPage> 
const& pPage)
+{
+    if (std::unique_ptr<vcl::pdf::PDFiumBitmap> bitmap
+        = pPageObject->getRenderedFillPattern(*mpPdfDocument, *pPage))
+    {
+        Bitmap aBitmap(bitmap->createBitmapFromBuffer());
+        return aBitmap.GetPixelColor(aBitmap.GetSizePixel().Width() / 2,
+                                     aBitmap.GetSizePixel().Height() / 2);
+    }
+    return pPageObject->getFillColor();
+}
+
 void ImpSdrPdfImport::ImportText(std::unique_ptr<vcl::pdf::PDFiumPageObject> 
const& pPageObject,
+                                 std::unique_ptr<vcl::pdf::PDFiumPage> const& 
pPage,
                                  std::unique_ptr<vcl::pdf::PDFiumTextPage> 
const& pTextPage,
                                  int /*nPageObjectIndex*/)
 {
@@ -1843,7 +1896,8 @@ void 
ImpSdrPdfImport::ImportText(std::unique_ptr<vcl::pdf::PDFiumPageObject> con
     }
     if (bUse)
     {
-        Color aColor = bFill ? pPageObject->getFillColor() : 
pPageObject->getStrokeColor();
+        Color aColor
+            = bFill ? getFillColor(pPageObject, pPage) : 
getStrokeColor(pPageObject, pPage);
         if (aColor != COL_TRANSPARENT)
             aTextColor = aColor.GetRGBColor();
     }
@@ -1979,6 +2033,7 @@ void 
ImpSdrPdfImport::ImportImage(std::unique_ptr<vcl::pdf::PDFiumPageObject> co
 }
 
 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();
@@ -2074,12 +2129,20 @@ void 
ImpSdrPdfImport::ImportPath(std::unique_ptr<vcl::pdf::PDFiumPageObject> con
             mpVD->SetDrawMode(DrawModeFlags::NoFill);
     }
 
-    mpVD->SetFillColor(pPageObject->getFillColor());
-
-    if (bStroke)
+    if (std::unique_ptr<vcl::pdf::PDFiumBitmap> bitmap
+        = pPageObject->getRenderedFillPattern(*mpPdfDocument, *pPage))
     {
-        mpVD->SetLineColor(pPageObject->getStrokeColor());
+        moFillPattern = bitmap->createBitmapFromBuffer();
+        moFillColor.reset();
     }
+    else
+    {
+        moFillColor = pPageObject->getFillColor();
+        moFillPattern.reset();
+    }
+
+    if (bStroke)
+        mpVD->SetLineColor(getStrokeColor(pPageObject, pPage));
     else
         mpVD->SetLineColor(COL_TRANSPARENT);
 
diff --git a/vcl/source/pdf/PDFiumLibrary.cxx b/vcl/source/pdf/PDFiumLibrary.cxx
index f1677461f17b..1a60815ccf98 100644
--- a/vcl/source/pdf/PDFiumLibrary.cxx
+++ b/vcl/source/pdf/PDFiumLibrary.cxx
@@ -425,8 +425,12 @@ public:
     bool getFontProperties(FontWeight& weight) override;
     PDFTextRenderMode getTextRenderMode() override;
     Color getFillColor() override;
+    std::unique_ptr<PDFiumBitmap> getRenderedFillPattern(PDFiumDocument& rDoc,
+                                                         PDFiumPage& rPage) 
override;
     Color getStrokeColor() override;
     double getStrokeWidth() override;
+    std::unique_ptr<PDFiumBitmap> getRenderedStrokePattern(PDFiumDocument& 
rDoc,
+                                                           PDFiumPage& rPage) 
override;
     // Path
     int getPathSegmentCount() override;
     std::unique_ptr<PDFiumPathSegment> getPathSegment(int index) override;
@@ -1375,6 +1379,36 @@ std::unique_ptr<PDFiumBitmap> 
PDFiumPageObjectImpl::getImageBitmap()
     return pPDFiumBitmap;
 }
 
+std::unique_ptr<PDFiumBitmap> 
PDFiumPageObjectImpl::getRenderedStrokePattern(PDFiumDocument& rDoc,
+                                                                             
PDFiumPage& rPage)
+{
+    auto& rDocImpl = static_cast<PDFiumDocumentImpl&>(rDoc);
+    auto& rPageImpl = static_cast<PDFiumPageImpl&>(rPage);
+    std::unique_ptr<PDFiumBitmap> pPDFiumBitmap;
+    FPDF_BITMAP pBitmap = FPDFPageObj_GetRenderedStrokePattern(
+        rDocImpl.getPointer(), rPageImpl.getPointer(), mpPageObject);
+    if (pBitmap)
+    {
+        pPDFiumBitmap = std::make_unique<PDFiumBitmapImpl>(pBitmap);
+    }
+    return pPDFiumBitmap;
+}
+
+std::unique_ptr<PDFiumBitmap> 
PDFiumPageObjectImpl::getRenderedFillPattern(PDFiumDocument& rDoc,
+                                                                           
PDFiumPage& rPage)
+{
+    auto& rDocImpl = static_cast<PDFiumDocumentImpl&>(rDoc);
+    auto& rPageImpl = static_cast<PDFiumPageImpl&>(rPage);
+    std::unique_ptr<PDFiumBitmap> pPDFiumBitmap;
+    FPDF_BITMAP pBitmap = 
FPDFPageObj_GetRenderedFillPattern(rDocImpl.getPointer(),
+                                                             
rPageImpl.getPointer(), mpPageObject);
+    if (pBitmap)
+    {
+        pPDFiumBitmap = std::make_unique<PDFiumBitmapImpl>(pBitmap);
+    }
+    return pPDFiumBitmap;
+}
+
 bool PDFiumPageObjectImpl::getDrawMode(PDFFillMode& rFillMode, bool& rStroke)
 {
     auto nFillMode = static_cast<int>(rFillMode);

Reply via email to