[Libreoffice-commits] core.git: Branch 'private/tvajngerl/staging' - 8 commits - basegfx/CppunitTest_basegfx.mk basegfx/test drawinglayer/source filter/CppunitTest_filter_pdfimporter_test.mk filter/Library_pdfimporter.mk filter/Module_filter.mk filter/qa filter/source include/basegfx include/filter include/o3tl include/vcl Repository.mk sw/inc sw/qa sw/source vcl/backendtest vcl/Executable_benchmark.mk vcl/inc vcl/Library_vcl.mk vcl/Module_vcl.mk vcl/qa vcl/source

Tomaž Vajngerl (via logerrit) logerrit at kemper.freedesktop.org
Mon Aug 2 14:18:35 UTC 2021


Rebased ref, commits from common ancestor:
commit aa6b920faf0aa740558473059b7fe2413ab9a105
Author:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Sun Jul 25 21:17:24 2021 +0900
Commit:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
CommitDate: Mon Aug 2 23:17:46 2021 +0900

    sw: preserve the value in original units to minimize conv. error
    
    Not really an big issue but let's preserve the values in original
    units as long as we can without conversion so that the error is
    minimal.
    
    Change-Id: I96981eaecac74b591803dbc93e487c1d106ab23c

diff --git a/sw/inc/swtypes.hxx b/sw/inc/swtypes.hxx
index b2c41bd31c94..3cd7740fd849 100644
--- a/sw/inc/swtypes.hxx
+++ b/sw/inc/swtypes.hxx
@@ -69,7 +69,8 @@ constexpr SwTwips DEF_GUTTER_WIDTH = o3tl::convertTwip(tools::Long(3), o3tl::Len
 constexpr SwTwips MIN_BORDER_DIST = 28; // ~0.5mm
 
 // Minimal document border: 20mm.
-constexpr SwTwips lMinBorder = o3tl::convertTwip(tools::Long(20), o3tl::Length::mm); // ~20mm
+constexpr tools::Long lMinBorderInMm = tools::Long(20);
+constexpr SwTwips lMinBorder = o3tl::convertTwip(lMinBorderInMm, o3tl::Length::mm);
 static_assert(lMinBorder == 1134); // this was the old constant for 20mm, making sure o3tl::convert returns the same value
 
 // Margin left and above document.
diff --git a/sw/source/uibase/dochdl/swdtflvr.cxx b/sw/source/uibase/dochdl/swdtflvr.cxx
index 5d8581f423a9..647983a4beca 100644
--- a/sw/source/uibase/dochdl/swdtflvr.cxx
+++ b/sw/source/uibase/dochdl/swdtflvr.cxx
@@ -134,10 +134,12 @@
 
 #include <memory>
 
-constexpr tools::Long constOleWidth = 11905 - 2 * lMinBorder;
-constexpr tools::Long constOleHeight = o3tl::convertTwip(tools::Long(3), o3tl::Length::cm);
+/* default (A4 format) width of 210mm - 2 * border size (border on both sides) */
+constexpr tools::Long constOleWidthInMm = 210 - 2 * lMinBorderInMm;
 
-#define OLESIZE constOleWidth, constOleHeight
+constexpr Size constOleSizeTwip(
+    o3tl::convertTwip(constOleWidthInMm, o3tl::Length::cm),
+    o3tl::convertTwip(tools::Long(3), o3tl::Length::cm));
 
 constexpr sal_uInt32 SWTRANSFER_OBJECTTYPE_DRAWMODEL = 0x00000001;
 constexpr sal_uInt32 SWTRANSFER_OBJECTTYPE_HTML      = 0x00000002;
@@ -343,7 +345,7 @@ void SwTransferable::InitOle( SfxObjectShell* pDoc )
 {
     //set OleVisArea. Upper left corner of the page and size of
     //RealSize in Twips.
-    const Size aSz( OLESIZE );
+    const Size aSz(constOleSizeTwip);
     SwRect aVis( Point( DOCUMENTBORDER, DOCUMENTBORDER ), aSz );
     pDoc->SetVisArea( aVis.SVRect() );
 }
@@ -1157,7 +1159,7 @@ int SwTransferable::PrepareForCopy( bool bIsCut )
         //ObjectDescriptor was already filly from the old DocShell.
         //Now adjust it. Thus in GetData the first query can still
         //be answered with delayed rendering.
-        Size aSz( OLESIZE );
+        Size aSz(constOleSizeTwip);
         m_aObjDesc.maSize = OutputDevice::LogicToLogic(aSz, MapMode(MapUnit::MapTwip), MapMode(MapUnit::Map100thMM));
 
         PrepareOLE( m_aObjDesc );
@@ -1260,7 +1262,7 @@ bool SwTransferable::CopyGlossary( SwTextBlocks& rGlossary, const OUString& rStr
     //ObjectDescriptor was already filled from the old DocShell.
     //Now adjust it. Thus in GetData the first query can still
     //be answered with delayed rendering.
-    Size aSz( OLESIZE );
+    Size aSz(constOleSizeTwip);
     m_aObjDesc.maSize = OutputDevice::LogicToLogic(aSz, MapMode(MapUnit::MapTwip), MapMode(MapUnit::Map100thMM));
 
     PrepareOLE( m_aObjDesc );
@@ -3627,7 +3629,7 @@ void SwTransferable::SetDataForDragAndDrop( const Point& rSttPos )
         //Now adjust it. Thus in GetData the first query can still
         //be answered with delayed rendering.
         m_aObjDesc.maDragStartPos = rSttPos;
-        m_aObjDesc.maSize = OutputDevice::LogicToLogic( Size( OLESIZE ),
+        m_aObjDesc.maSize = OutputDevice::LogicToLogic(constOleSizeTwip,
                     MapMode(MapUnit::MapTwip), MapMode(MapUnit::Map100thMM));
         PrepareOLE( m_aObjDesc );
         AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR );
commit e8f5cfc18e1f69a96112bd8d9db242cdb5fa8814
Author:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Fri Jul 23 08:44:14 2021 +0900
Commit:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
CommitDate: Mon Aug 2 23:17:46 2021 +0900

    sw: remove all uses of MM50 with (added) o3tl::convertTwip
    
    MM50 constant is the number of twips for 5mm. This is (ab)used as
    a "shortcut" to set or compare various variables through the code
    and also to set a multiplied value (like 1cm, 2cm, 4cm) or divided
    value (2.5mm, 1.25mm). The problem with this is that converting the
    5mm to twip doesn't round up exactly and multiplied and divided
    values increase the error even more.
    
    Instead of basing it from MM50 constant, it is just better to use
    our o3tl::convert (or the added variant o3tl::convertTwip), which
    actually can calculate the conversion at compile time, so there is
    no added complexity at runtime and we get a more readable code with
    more exact result.
    
    i.e.
    o3tl::convertTwip(4, o3tl::Length::cm)
    instead of the more cryptic
    MM50 * 8
    
    In addition also sanitize and comment the values of the constants
    in sw/inc/swtypes.hxx.
    
    Change-Id: I85c306c36207d47ac3dc207094b68d0fb1ca5b70

diff --git a/include/o3tl/unit_conversion.hxx b/include/o3tl/unit_conversion.hxx
index 4c76375737a2..927e8c565a0e 100644
--- a/include/o3tl/unit_conversion.hxx
+++ b/include/o3tl/unit_conversion.hxx
@@ -199,6 +199,12 @@ template <typename N, typename U> constexpr auto convert(N n, U from, U to)
     return convert(n, detail::md(from, to), detail::md(to, from));
 }
 
+// Just for convenience as we do this a lot
+template <typename N, typename U> constexpr auto convertTwip(N number, U from)
+{
+    return convert(number, detail::md(from, Length::twip), detail::md(Length::twip, from));
+}
+
 // Returns nDefault if intermediate multiplication overflows sal_Int64 (only for integral types).
 // On return, bOverflow indicates if overflow happened. nDefault is returned when overflow occurs.
 template <typename N, typename U>
diff --git a/sw/inc/swtypes.hxx b/sw/inc/swtypes.hxx
index dd15ce03bb88..b2c41bd31c94 100644
--- a/sw/inc/swtypes.hxx
+++ b/sw/inc/swtypes.hxx
@@ -25,9 +25,9 @@
 #include <com/sun/star/i18n/CollatorOptions.hpp>
 #include "swdllapi.h"
 #include <o3tl/typed_flags_set.hxx>
+#include <o3tl/unit_conversion.hxx>
 #include <i18nlangtag/lang.h>
 #include <vcl/outdev.hxx>
-#include <svx/flagsdef.hxx>
 
 namespace com::sun::star {
     namespace linguistic2{
@@ -55,26 +55,27 @@ typedef tools::Long SwTwips;
 
 constexpr sal_Int32 COMPLETE_STRING = SAL_MAX_INT32;
 
-constexpr SwTwips cMinHdFtHeight = 56;
+constexpr SwTwips cMinHdFtHeight = 56; // ~1mm
 
 #define MINFLY 23   // Minimal size for FlyFrames.
 #define MINLAY 23   // Minimal size for other Frames.
 
 // Default column distance of two text columns corresponds to 0.3 cm.
-constexpr SwTwips DEF_GUTTER_WIDTH = MM50 / 5 * 3;
+constexpr SwTwips DEF_GUTTER_WIDTH = o3tl::convertTwip(tools::Long(3), o3tl::Length::mm);
 
 // Minimal distance (distance to text) for border attribute
 // in order not to crock up aligned lines.
-// 28 Twips == 0,4mm
-constexpr SwTwips MIN_BORDER_DIST = 28;
+// 28 Twips == 0,5mm
+constexpr SwTwips MIN_BORDER_DIST = 28; // ~0.5mm
 
 // Minimal document border: 20mm.
-constexpr SwTwips lMinBorder = 1134;
+constexpr SwTwips lMinBorder = o3tl::convertTwip(tools::Long(20), o3tl::Length::mm); // ~20mm
+static_assert(lMinBorder == 1134); // this was the old constant for 20mm, making sure o3tl::convert returns the same value
 
 // Margin left and above document.
 // Half of it is gap between the pages.
 //TODO: Replace with SwViewOption::defDocumentBorder
-constexpr SwTwips DOCUMENTBORDER = 284;
+constexpr SwTwips DOCUMENTBORDER = 284; // ~5mm
 
 // For inserting of captions (what and where to insert).
 // It's here because it is not big enough to justify its own hxx
@@ -95,11 +96,14 @@ constexpr sal_uInt8 NO_NUMLEVEL = 0x20;    // "or" with the levels.
 //  (For more levels the values have to be multiplied with the levels+1;
 //  levels 0 ..4!)
 
-constexpr short lBulletIndent = 1440 / 4;
+constexpr short lBulletIndent = o3tl::convertTwip(25, o3tl::Length::in100); // 0.25 inch
 constexpr short lBulletFirstLineOffset = -lBulletIndent;
-constexpr sal_uInt16 lNumberIndent = 1440 / 4;
+constexpr sal_uInt16 lNumberIndent = o3tl::convertTwip(25, o3tl::Length::in100); // 0.25 inch
 constexpr short lNumberFirstLineOffset = -lNumberIndent;
-constexpr short lOutlineMinTextDistance = 216; // 0.15 inch = 0.38 cm
+constexpr short lOutlineMinTextDistance = o3tl::convertTwip(15, o3tl::Length::in100); // 0.15 inch = 0.38 cm
+
+static_assert(1440 / 4 == lBulletIndent);
+static_assert(216 == lOutlineMinTextDistance);
 
 // Count of SystemField-types of SwDoc.
 #define INIT_FLDTYPES   33
diff --git a/sw/qa/extras/tiledrendering/tiledrendering.cxx b/sw/qa/extras/tiledrendering/tiledrendering.cxx
index 91f6689f1d73..ad09174796af 100644
--- a/sw/qa/extras/tiledrendering/tiledrendering.cxx
+++ b/sw/qa/extras/tiledrendering/tiledrendering.cxx
@@ -597,7 +597,8 @@ void SwTiledRenderingTest::testInsertShape()
     IDocumentDrawModelAccess &rDrawModelAccess = pWrtShell->GetDoc()->getIDocumentDrawModelAccess();
     SdrPage* pPage = rDrawModelAccess.GetDrawModel()->GetPage(0);
     SdrObject* pObject = pPage->GetObj(0);
-    CPPUNIT_ASSERT_EQUAL(tools::Rectangle(3302, 302, 6698, 3698), pObject->GetSnapRect());
+
+    CPPUNIT_ASSERT_EQUAL(tools::Rectangle(Point(3299, 299), Size(3403, 3403)), pObject->GetSnapRect());
 
     // check that it is in the foreground layer
     CPPUNIT_ASSERT_EQUAL(rDrawModelAccess.GetHeavenId().get(), pObject->GetLayer().get());
diff --git a/sw/source/core/doc/DocumentStylePoolManager.cxx b/sw/source/core/doc/DocumentStylePoolManager.cxx
index 9238622db126..9e454d169b81 100644
--- a/sw/source/core/doc/DocumentStylePoolManager.cxx
+++ b/sw/source/core/doc/DocumentStylePoolManager.cxx
@@ -1669,9 +1669,9 @@ SwFormat* DocumentStylePoolManager::GetFormatFromPool( sal_uInt16 nId )
             aSet.Put( SwFormatVertOrient( 0, text::VertOrientation::TOP, text::RelOrientation::FRAME ));
             aSet.Put( SwFormatSurround( css::text::WrapTextMode_PARALLEL ));
             // Set the default width to 3.5 cm, use the minimum value for the height
-            aSet.Put( SwFormatFrameSize( SwFrameSize::Minimum,
-                    o3tl::convert(35, o3tl::Length::mm, o3tl::Length::twip),
-                    MM50 ));
+            aSet.Put(SwFormatFrameSize(SwFrameSize::Minimum,
+                                       o3tl::convertTwip(35, o3tl::Length::mm),
+                                       o3tl::convertTwip(5, o3tl::Length::mm)));
         }
         break;
     case RES_POOLFRM_WATERSIGN:
diff --git a/sw/source/core/doc/lineinfo.cxx b/sw/source/core/doc/lineinfo.cxx
index e77e14a174d8..be89d9439ac3 100644
--- a/sw/source/core/doc/lineinfo.cxx
+++ b/sw/source/core/doc/lineinfo.cxx
@@ -52,7 +52,7 @@ const SwLineNumberInfo& SwDoc::GetLineNumberInfo() const
 }
 
 SwLineNumberInfo::SwLineNumberInfo() :
-    m_nPosFromLeft( MM50 ),
+    m_nPosFromLeft(o3tl::convertTwip(5, o3tl::Length::mm)),
     m_nCountBy( 5 ),
     m_nDividerCountBy( 3 ),
     m_ePos( LINENUMBER_POS_LEFT ),
diff --git a/sw/source/core/edit/edattr.cxx b/sw/source/core/edit/edattr.cxx
index 6ab5a3ae9ad8..f1201628ae7f 100644
--- a/sw/source/core/edit/edattr.cxx
+++ b/sw/source/core/edit/edattr.cxx
@@ -501,8 +501,13 @@ bool SwEditShell::IsMoveLeftMargin( bool bRight, bool bModulus ) const
 {
     bool bRet = true;
 
+    constexpr sal_uInt16 constTwips_2cm = o3tl::convertTwip(tools::Long(20), o3tl::Length::mm);
+    constexpr tools::Long constTwips_5mm = o3tl::convertTwip(tools::Long(5), o3tl::Length::mm);
+
     const SvxTabStopItem& rTabItem = GetDoc()->GetDefault( RES_PARATR_TABSTOP );
-    sal_uInt16 nDefDist = o3tl::narrowing<sal_uInt16>(rTabItem.Count() ? rTabItem[0].GetTabPos() : 1134);
+    sal_uInt16 nDefDist = o3tl::narrowing<sal_uInt16>(
+        rTabItem.Count() ? rTabItem[0].GetTabPos() : constTwips_2cm);
+
     if( !nDefDist )
         return false;
 
@@ -534,7 +539,7 @@ bool SwEditShell::IsMoveLeftMargin( bool bRight, bool bModulus ) const
                         const sal_uInt32 nFrameWidth = o3tl::narrowing<sal_uInt32>( pFrame->IsVertical() ?
                                                  pFrame->getFrameArea().Height() :
                                                  pFrame->getFrameArea().Width() );
-                        bRet = o3tl::narrowing<SwTwips>(nFrameWidth) > ( nNext + MM50 );
+                        bRet = o3tl::narrowing<SwTwips>(nFrameWidth) > (nNext + constTwips_5mm);
                     }
                     else
                         bRet = false;
diff --git a/sw/source/core/frmedt/feshview.cxx b/sw/source/core/frmedt/feshview.cxx
index ab185b70d893..6d21ceac1ae2 100644
--- a/sw/source/core/frmedt/feshview.cxx
+++ b/sw/source/core/frmedt/feshview.cxx
@@ -1908,11 +1908,13 @@ bool SwFEShell::ImpEndCreate()
 
             // The crsr should not be too far away
             bCharBound = true;
+            constexpr tools::Long constTwips_1cm = o3tl::convertTwip(tools::Long(1), o3tl::Length::cm);
             tools::Rectangle aRect( aTmp.SVRect() );
-            aRect.AdjustLeft( -(MM50*2) );
-            aRect.AdjustTop( -(MM50*2) );
-            aRect.AdjustRight(MM50*2 );
-            aRect.AdjustBottom(MM50*2 );
+            // Extend by 1 cm in each direction
+            aRect.AdjustLeft(-constTwips_1cm);
+            aRect.AdjustTop(-constTwips_1cm);
+            aRect.AdjustRight(constTwips_1cm);
+            aRect.AdjustBottom(constTwips_1cm);
 
             if( !aRect.IsOver( rBound ) && !::GetHtmlMode( GetDoc()->GetDocShell() ))
                 bCharBound = false;
diff --git a/sw/source/core/layout/calcmove.cxx b/sw/source/core/layout/calcmove.cxx
index 71a598c229bc..798d1231d1bc 100644
--- a/sw/source/core/layout/calcmove.cxx
+++ b/sw/source/core/layout/calcmove.cxx
@@ -860,7 +860,9 @@ void SwPageFrame::MakeAll(vcl::RenderContext* pRenderContext)
                     if (nWidth < nDefWidth)
                         nWidth = nDefWidth;
                     nWidth += + 2 * aBorder.Width();
-                    nWidth = std::max( nWidth, 2L * aBorder.Width() + 4*MM50 );
+
+                    constexpr tools::Long constTwips_2cm = o3tl::convertTwip(tools::Long(2), o3tl::Length::cm);
+                    nWidth = std::max(nWidth, 2L * aBorder.Width() + constTwips_2cm);
 
                     {
                         SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
diff --git a/sw/source/core/unocore/unoframe.cxx b/sw/source/core/unocore/unoframe.cxx
index 69c726721745..4b7887d4e953 100644
--- a/sw/source/core/unocore/unoframe.cxx
+++ b/sw/source/core/unocore/unoframe.cxx
@@ -879,9 +879,10 @@ bool BaseFrameProperties_Impl::FillBaseProperties(SfxItemSet& rToSet, const SfxI
         {
             rSizeFound = false;
             SwFormatFrameSize aFrameSz;
+            constexpr sal_Int32 constTwips_1cm = o3tl::convertTwip(sal_Int32(1), o3tl::Length::cm);
             awt::Size aSize;
-            aSize.Width = 2 * MM50;
-            aSize.Height = 2 * MM50;
+            aSize.Width = constTwips_1cm;
+            aSize.Height = constTwips_1cm;
             ::uno::Any aSizeVal;
             aSizeVal <<= aSize;
             static_cast<SfxPoolItem&>(aFrameSz).PutValue(aSizeVal, MID_FRMSIZE_SIZE|CONVERT_TWIPS);
diff --git a/sw/source/core/unocore/unostyle.cxx b/sw/source/core/unocore/unostyle.cxx
index 3012637bbe4c..3ba7388593b6 100644
--- a/sw/source/core/unocore/unostyle.cxx
+++ b/sw/source/core/unocore/unostyle.cxx
@@ -2923,7 +2923,8 @@ void SwXPageStyle::SetPropertyValues_Impl(const uno::Sequence<OUString>& rProper
                         aTempSet.SetParent(&GetDoc()->GetDfltFrameFormat()->GetAttrSet());
 
                         aTempSet.Put(SfxBoolItem(SID_ATTR_PAGE_ON, true));
-                        aTempSet.Put(SvxSizeItem(SID_ATTR_PAGE_SIZE, Size(MM50, MM50)));
+                        constexpr tools::Long constTwips_5mm = o3tl::convertTwip(tools::Long(5), o3tl::Length::mm);
+                        aTempSet.Put(SvxSizeItem(SID_ATTR_PAGE_SIZE, Size(constTwips_5mm, constTwips_5mm)));
                         aTempSet.Put(SvxLRSpaceItem(RES_LR_SPACE));
                         aTempSet.Put(SvxULSpaceItem(RES_UL_SPACE));
                         aTempSet.Put(SfxBoolItem(SID_ATTR_PAGE_SHARED, true));
diff --git a/sw/source/filter/html/htmlnum.hxx b/sw/source/filter/html/htmlnum.hxx
index 2e7957996509..e155dfda000d 100644
--- a/sw/source/filter/html/htmlnum.hxx
+++ b/sw/source/filter/html/htmlnum.hxx
@@ -22,8 +22,8 @@
 #include <swtypes.hxx>
 #include <string.h>
 
-#define HTML_NUMBER_BULLET_MARGINLEFT  (MM50*2 + MM50/2)
-#define HTML_NUMBER_BULLET_INDENT      (-MM50)
+#define HTML_NUMBER_BULLET_MARGINLEFT  (o3tl::convertTwip(tools::Long(125), o3tl::Length::mm10))
+#define HTML_NUMBER_BULLET_INDENT      (-o3tl::convertTwip(tools::Long(5), o3tl::Length::mm))
 
 class SwTextNode;
 class SwNumRule;
diff --git a/sw/source/filter/html/htmlplug.cxx b/sw/source/filter/html/htmlplug.cxx
index 9ae5abc57095..e505624bbe78 100644
--- a/sw/source/filter/html/htmlplug.cxx
+++ b/sw/source/filter/html/htmlplug.cxx
@@ -83,11 +83,12 @@
 
 using namespace com::sun::star;
 
-#define HTML_DFLT_EMBED_WIDTH ((MM50*5)/2)
-#define HTML_DFLT_EMBED_HEIGHT ((MM50*5)/2)
 
-#define HTML_DFLT_APPLET_WIDTH ((MM50*5)/2)
-#define HTML_DFLT_APPLET_HEIGHT ((MM50*5)/2)
+#define HTML_DFLT_EMBED_WIDTH (o3tl::convertTwip(tools::Long(125), o3tl::Length::mm10))
+#define HTML_DFLT_EMBED_HEIGHT (o3tl::convertTwip(tools::Long(125), o3tl::Length::mm10))
+
+#define HTML_DFLT_APPLET_WIDTH (o3tl::convertTwip(tools::Long(125), o3tl::Length::mm10))
+#define HTML_DFLT_APPLET_HEIGHT (o3tl::convertTwip(tools::Long(125), o3tl::Length::mm10))
 
 
 const HtmlFrmOpts HTML_FRMOPTS_EMBED_ALL      =
diff --git a/sw/source/filter/html/htmltab.cxx b/sw/source/filter/html/htmltab.cxx
index e1cc324d73c2..ec1c6a03702f 100644
--- a/sw/source/filter/html/htmltab.cxx
+++ b/sw/source/filter/html/htmltab.cxx
@@ -3421,7 +3421,9 @@ void SwHTMLParser::BuildTableCell( HTMLTable *pCurTable, bool bReadOptions,
                                             true );
                     aFrameSet.Put( SwFormatSurround(eSurround) );
 
-                    SwFormatFrameSize aFrameSize( SwFrameSize::Variable, 20*MM50, MINLAY );
+                    constexpr tools::Long constTwips_100mm = o3tl::convert(tools::Long(100), o3tl::Length::mm, o3tl::Length::twip);
+
+                    SwFormatFrameSize aFrameSize( SwFrameSize::Variable, constTwips_100mm, MINLAY );
                     aFrameSize.SetWidthPercent( 100 );
                     aFrameSet.Put( aFrameSize );
 
diff --git a/sw/source/filter/html/svxcss1.hxx b/sw/source/filter/html/svxcss1.hxx
index fd547a0e9d76..a69850979533 100644
--- a/sw/source/filter/html/svxcss1.hxx
+++ b/sw/source/filter/html/svxcss1.hxx
@@ -202,7 +202,8 @@ class SvxCSS1Parser : public CSS1Parser
     std::unique_ptr<SvxCSS1PropertyInfo> m_pSheetPropInfo;
     SvxCSS1PropertyInfo *m_pPropInfo;
 
-    static constexpr sal_uInt16 gnMinFixLineSpace = MM50/2;    // minimum spacing for fixed line spacing
+    // minimum spacing for fixed line spacing
+    static constexpr sal_uInt16 gnMinFixLineSpace = o3tl::convertTwip(tools::Long(25), o3tl::Length::mm10);
 
     rtl_TextEncoding    m_eDefaultEnc;
     bool m_bIgnoreFontFamily;
diff --git a/sw/source/filter/html/swhtml.hxx b/sw/source/filter/html/swhtml.hxx
index 3e3b693eb5a8..7aaf6f0aa1c1 100644
--- a/sw/source/filter/html/swhtml.hxx
+++ b/sw/source/filter/html/swhtml.hxx
@@ -58,11 +58,11 @@ struct SwPending;
 class SvxCSS1PropertyInfo;
 struct ImplSVEvent;
 
-#define HTML_CJK_PARSPACE (MM50/2)
-#define HTML_CTL_PARSPACE (MM50/2)
+constexpr tools::Long HTML_CJK_PARSPACE = o3tl::convertTwip(tools::Long(25), o3tl::Length::mm10); // 2.5mm
+constexpr tools::Long HTML_CTL_PARSPACE = o3tl::convertTwip(tools::Long(25), o3tl::Length::mm10); // 2.5mm
 
-#define HTML_DFLT_IMG_WIDTH (MM50*4)
-#define HTML_DFLT_IMG_HEIGHT (MM50*2)
+constexpr tools::Long HTML_DFLT_IMG_WIDTH = o3tl::convertTwip(tools::Long(2), o3tl::Length::cm); // 2cm
+constexpr tools::Long HTML_DFLT_IMG_HEIGHT = o3tl::convertTwip(tools::Long(1), o3tl::Length::cm); // 1cm
 
 // some things you often need
 extern HTMLOptionEnum<SvxAdjust> const aHTMLPAlignTable[];
diff --git a/sw/source/filter/html/wrthtml.hxx b/sw/source/filter/html/wrthtml.hxx
index 12f693a72872..fa2b058e03d7 100644
--- a/sw/source/filter/html/wrthtml.hxx
+++ b/sw/source/filter/html/wrthtml.hxx
@@ -66,7 +66,7 @@ namespace utl { class TempFile; }
 
 extern SwAttrFnTab aHTMLAttrFnTab;
 
-#define HTML_PARSPACE (MM50)
+#define HTML_PARSPACE (o3tl::convertTwip(tools::Long(5), o3tl::Length::mm))
 
 // flags for the output of any kind of frames
 // BORDER only possible if OutHTML_Image
diff --git a/sw/source/ui/chrdlg/pardlg.cxx b/sw/source/ui/chrdlg/pardlg.cxx
index aef94f46111a..c351f5b39807 100644
--- a/sw/source/ui/chrdlg/pardlg.cxx
+++ b/sw/source/ui/chrdlg/pardlg.cxx
@@ -167,8 +167,9 @@ void SwParaDlg::PageCreated(const OString& rId, SfxTabPage& rPage)
         if (!bDrawParaDlg)
         {
             // See SvxStdParagraphTabPage::PageCreated: enable RegisterMode, AutoFirstLine, NegativeMode, ContextualMode
+            constexpr tools::Long constTwips_0_5mm = o3tl::convertTwip(tools::Long(5), o3tl::Length::mm10);
             aSet.Put(SfxUInt32Item(SID_SVXSTDPARAGRAPHTABPAGE_FLAGSET,0x0002|0x0004|0x0008|0x0010));
-            aSet.Put(SfxUInt32Item(SID_SVXSTDPARAGRAPHTABPAGE_ABSLINEDIST, MM50/10));
+            aSet.Put(SfxUInt32Item(SID_SVXSTDPARAGRAPHTABPAGE_ABSLINEDIST, constTwips_0_5mm));
 
         }
         rPage.PageCreated(aSet);
diff --git a/sw/source/ui/dbui/mmlayoutpage.cxx b/sw/source/ui/dbui/mmlayoutpage.cxx
index a6a32ddd76cb..9bd5f381ccb5 100644
--- a/sw/source/ui/dbui/mmlayoutpage.cxx
+++ b/sw/source/ui/dbui/mmlayoutpage.cxx
@@ -61,11 +61,11 @@ using namespace ::com::sun::star::frame;
 using namespace ::com::sun::star::lang;
 using namespace ::com::sun::star::view;
 
-#define DEFAULT_LEFT_DISTANCE (MM50*5)  // 2,5 cm
-#define DEFAULT_TOP_DISTANCE  (MM50*11) // 5,5 cm
-#define GREETING_TOP_DISTANCE (MM50*25) //12,5 cm
-#define DEFAULT_ADDRESS_WIDTH  (MM50*15)// 7,5 cm
-#define DEFAULT_ADDRESS_HEIGHT (MM50*7) // 3,5cm
+constexpr tools::Long DEFAULT_LEFT_DISTANCE = o3tl::convertTwip(tools::Long(25), o3tl::Length::mm); // 2,5 cm
+constexpr tools::Long DEFAULT_TOP_DISTANCE  = o3tl::convertTwip(tools::Long(55), o3tl::Length::mm); // 5,5 cm
+constexpr tools::Long GREETING_TOP_DISTANCE = o3tl::convertTwip(tools::Long(125), o3tl::Length::mm); //12,5 cm
+constexpr tools::Long DEFAULT_ADDRESS_WIDTH = o3tl::convertTwip(tools::Long(75), o3tl::Length::mm); // 7,5 cm
+constexpr tools::Long DEFAULT_ADDRESS_HEIGHT = o3tl::convertTwip(tools::Long(35), o3tl::Length::mm); // 3,5cm
 
 SwMailMergeLayoutPage::SwMailMergeLayoutPage(weld::Container* pPage, SwMailMergeWizard* pWizard)
     : vcl::OWizardPage(pPage, pWizard, "modules/swriter/ui/mmlayoutpage.ui", "MMLayoutPage")
diff --git a/sw/source/ui/fmtui/tmpdlg.cxx b/sw/source/ui/fmtui/tmpdlg.cxx
index 6e797e45a5a4..77551a02319e 100644
--- a/sw/source/ui/fmtui/tmpdlg.cxx
+++ b/sw/source/ui/fmtui/tmpdlg.cxx
@@ -489,7 +489,8 @@ void SwTemplateDlgController::PageCreated(const OString& rId, SfxTabPage &rPage
     {
         if( rPage.GetItemSet().GetParent() )
         {
-            aSet.Put(SfxUInt32Item(SID_SVXSTDPARAGRAPHTABPAGE_ABSLINEDIST,MM50/10));
+            constexpr tools::Long constTwips_0_5mm = o3tl::convertTwip(tools::Long(5), o3tl::Length::mm10);
+            aSet.Put(SfxUInt32Item(SID_SVXSTDPARAGRAPHTABPAGE_ABSLINEDIST, constTwips_0_5mm));
             aSet.Put(SfxUInt32Item(SID_SVXSTDPARAGRAPHTABPAGE_FLAGSET,0x000F));
             rPage.PageCreated(aSet);
         }
diff --git a/sw/source/ui/misc/pggrid.cxx b/sw/source/ui/misc/pggrid.cxx
index 32186b8a8895..d9dedae3a6ee 100644
--- a/sw/source/ui/misc/pggrid.cxx
+++ b/sw/source/ui/misc/pggrid.cxx
@@ -35,11 +35,13 @@
 #include <swmodule.hxx>
 #include <view.hxx>
 
+constexpr tools::Long constTwips_5mm = o3tl::convertTwip(tools::Long(5), o3tl::Length::mm);
+
 SwTextGridPage::SwTextGridPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rSet)
     : SfxTabPage(pPage, pController, "modules/swriter/ui/textgridpage.ui", "TextGridPage", &rSet)
     , m_nRubyUserValue(0)
     , m_bRubyUserValue(false)
-    , m_aPageSize(MM50, MM50)
+    , m_aPageSize(constTwips_5mm, constTwips_5mm)
     , m_bVertical(false)
     , m_bSquaredMode(false)
     , m_bHRulerChanged(false)
diff --git a/sw/source/uibase/dochdl/swdtflvr.cxx b/sw/source/uibase/dochdl/swdtflvr.cxx
index c36b6d87837b..5d8581f423a9 100644
--- a/sw/source/uibase/dochdl/swdtflvr.cxx
+++ b/sw/source/uibase/dochdl/swdtflvr.cxx
@@ -134,7 +134,10 @@
 
 #include <memory>
 
-#define OLESIZE 11905 - 2 * lMinBorder, 6 * MM50
+constexpr tools::Long constOleWidth = 11905 - 2 * lMinBorder;
+constexpr tools::Long constOleHeight = o3tl::convertTwip(tools::Long(3), o3tl::Length::cm);
+
+#define OLESIZE constOleWidth, constOleHeight
 
 constexpr sal_uInt32 SWTRANSFER_OBJECTTYPE_DRAWMODEL = 0x00000001;
 constexpr sal_uInt32 SWTRANSFER_OBJECTTYPE_HTML      = 0x00000002;
diff --git a/sw/source/uibase/inc/frmmgr.hxx b/sw/source/uibase/inc/frmmgr.hxx
index 27ebc70a66d3..b6d0adf9196e 100644
--- a/sw/source/uibase/inc/frmmgr.hxx
+++ b/sw/source/uibase/inc/frmmgr.hxx
@@ -35,8 +35,8 @@ struct  SwPosition;
 class   SwFormatCol;
 class SvGlobalName;
 
-const SwTwips   DFLT_WIDTH      = MM50 * 4;
-const SwTwips   DFLT_HEIGHT     = MM50;
+constexpr SwTwips DFLT_WIDTH = o3tl::convertTwip(tools::Long(20), o3tl::Length::mm);
+constexpr SwTwips DFLT_HEIGHT = o3tl::convertTwip(tools::Long(5), o3tl::Length::mm);
 
 enum class Frmmgr_Type
 {
diff --git a/sw/source/uibase/ribbar/conform.cxx b/sw/source/uibase/ribbar/conform.cxx
index f51e9123ca40..993f307b2fac 100644
--- a/sw/source/uibase/ribbar/conform.cxx
+++ b/sw/source/uibase/ribbar/conform.cxx
@@ -89,12 +89,15 @@ void ConstFormControl::Activate(const sal_uInt16 nSlotId)
 
 void ConstFormControl::CreateDefaultObject()
 {
+    constexpr tools::Long constTwips_5mm = o3tl::convertTwip(tools::Long(5), o3tl::Length::mm);
+    constexpr tools::Long constTwips_10mm = o3tl::convertTwip(tools::Long(10), o3tl::Length::mm);
+
     Point aStartPos(GetDefaultCenterPos());
     Point aEndPos(aStartPos);
-    aStartPos.AdjustX(-(2 * MM50));
-    aStartPos.AdjustY(-MM50);
-    aEndPos.AdjustX(2 * MM50);
-    aEndPos.AdjustY(MM50);
+    aStartPos.AdjustX(-constTwips_10mm);
+    aStartPos.AdjustY(-constTwips_5mm);
+    aEndPos.AdjustX(constTwips_10mm);
+    aEndPos.AdjustY(constTwips_5mm);
 
     if(!m_pSh->HasDrawView())
         m_pSh->MakeDrawView();
diff --git a/sw/source/uibase/ribbar/drawbase.cxx b/sw/source/uibase/ribbar/drawbase.cxx
index b1ee706dfa5d..e3295bf47fc4 100644
--- a/sw/source/uibase/ribbar/drawbase.cxx
+++ b/sw/source/uibase/ribbar/drawbase.cxx
@@ -511,12 +511,14 @@ void SwDrawBase::EnterSelectMode(const MouseEvent& rMEvt)
 
 void SwDrawBase::CreateDefaultObject()
 {
+    constexpr tools::Long constTwips_3cm = o3tl::convertTwip(tools::Long(3), o3tl::Length::cm);
+
     Point aStartPos = GetDefaultCenterPos();
     Point aEndPos(aStartPos);
-    aStartPos.AdjustX( -(6 * MM50) );
-    aStartPos.AdjustY( -(6 * MM50) );
-    aEndPos.AdjustX(6 * MM50 );
-    aEndPos.AdjustY(6 * MM50 );
+    aStartPos.AdjustX(-constTwips_3cm);
+    aStartPos.AdjustY(-constTwips_3cm);
+    aEndPos.AdjustX(constTwips_3cm);
+    aEndPos.AdjustY(constTwips_3cm);
     tools::Rectangle aRect(aStartPos, aEndPos);
     m_pSh->CreateDefaultShape(m_pWin->GetSdrDrawMode(), aRect, m_nSlotId);
 }
diff --git a/sw/source/uibase/shells/textsh.cxx b/sw/source/uibase/shells/textsh.cxx
index af3fc2e21494..3157a17461f0 100644
--- a/sw/source/uibase/shells/textsh.cxx
+++ b/sw/source/uibase/shells/textsh.cxx
@@ -399,9 +399,11 @@ void SwTextShell::ExecInsert(SfxRequest &rReq)
             Size aWinSize = rEdtWin.GetSizePixel();
             Point aStartPos(aWinSize.Width()/2, aWinSize.Height() / 2);
             aStartPos = rEdtWin.PixelToLogic(aStartPos);
-            aStartPos.AdjustX( -(8 * MM50) );
-            aStartPos.AdjustY( -(4 * MM50) );
-            Size aSize(16 * MM50, 8 * MM50);
+            constexpr tools::Long constTwips_2cm = o3tl::convertTwip(tools::Long(2), o3tl::Length::cm);
+            constexpr tools::Long constTwips_4cm = o3tl::convertTwip(tools::Long(4), o3tl::Length::cm);
+            aStartPos.AdjustX(-constTwips_4cm);
+            aStartPos.AdjustY(-constTwips_2cm);
+            Size aSize(2 * constTwips_4cm, 2 * constTwips_2cm);
             GetShell().LockPaint();
             GetShell().StartAllAction();
             SwFlyFrameAttrMgr aMgr( true, GetShellPtr(), Frmmgr_Type::TEXT, nullptr );
diff --git a/sw/source/uibase/wrtsh/wrtsh1.cxx b/sw/source/uibase/wrtsh/wrtsh1.cxx
index 41f71cd26462..6b68da98f171 100644
--- a/sw/source/uibase/wrtsh/wrtsh1.cxx
+++ b/sw/source/uibase/wrtsh/wrtsh1.cxx
@@ -1896,7 +1896,8 @@ void SwWrtShell::ChangeHeaderOrFooter(
                     rMaster.SetFormatAttr( SwFormatFooter( bOn ));
                 if( bOn )
                 {
-                    SvxULSpaceItem aUL(bHeader ? 0 : MM50, bHeader ? MM50 : 0, RES_UL_SPACE );
+                    constexpr tools::Long constTwips_5mm = o3tl::convertTwip(tools::Long(5), o3tl::Length::mm);
+                    SvxULSpaceItem aUL(bHeader ? 0 : constTwips_5mm, bHeader ? constTwips_5mm : 0, RES_UL_SPACE );
                     SwFrameFormat* pFormat = bHeader ?
                         const_cast<SwFrameFormat*>(rMaster.GetHeader().GetHeaderFormat()) :
                         const_cast<SwFrameFormat*>(rMaster.GetFooter().GetFooterFormat());
commit de4d9dce26b9c9cb70d09cedee09f43f43839205
Author:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Mon May 10 15:45:13 2021 +0900
Commit:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
CommitDate: Mon Aug 2 23:17:46 2021 +0900

    vcl: add more methods to the PDFium
    
    Change-Id: I74ef0f713125c7069620d1abc9534a636c1707d9

diff --git a/include/vcl/filter/PDFiumLibrary.hxx b/include/vcl/filter/PDFiumLibrary.hxx
index c5f1766f60c0..46988cf3c946 100644
--- a/include/vcl/filter/PDFiumLibrary.hxx
+++ b/include/vcl/filter/PDFiumLibrary.hxx
@@ -111,6 +111,13 @@ public:
     virtual PDFSegmentType getType() const = 0;
 };
 
+struct PDFImageMetadata
+{
+    sal_uInt32 mnWidth;
+    sal_uInt32 mnHeight;
+    sal_uInt32 mnBitsPerPixel;
+};
+
 class VCL_DLLPUBLIC PDFiumPageObject
 {
 public:
@@ -127,15 +134,20 @@ public:
     virtual double getFontSize() = 0;
     virtual OUString getFontName() = 0;
     virtual PDFTextRenderMode getTextRenderMode() = 0;
+    virtual bool hasTransparency() = 0;
     virtual Color getFillColor() = 0;
     virtual Color getStrokeColor() = 0;
     virtual double getStrokeWidth() = 0;
     // Path
     virtual int getPathSegmentCount() = 0;
     virtual std::unique_ptr<PDFiumPathSegment> getPathSegment(int index) = 0;
+    virtual bool getDrawMode(PDFFillMode& eFillMode, bool& bStroke) = 0;
+    // Image
     virtual Size getImageSize(PDFiumPage& rPage) = 0;
+    virtual PDFImageMetadata getImageMetadata(PDFiumPage& rPage) = 0;
+
     virtual std::unique_ptr<PDFiumBitmap> getImageBitmap() = 0;
-    virtual bool getDrawMode(PDFFillMode& eFillMode, bool& bStroke) = 0;
+    virtual bool getDecodedImageData(std::vector<sal_uInt8>& rData) = 0;
 };
 
 class VCL_DLLPUBLIC PDFiumSearchHandle
diff --git a/vcl/source/pdf/PDFiumLibrary.cxx b/vcl/source/pdf/PDFiumLibrary.cxx
index 28b8e8f3e309..760a28bce173 100644
--- a/vcl/source/pdf/PDFiumLibrary.cxx
+++ b/vcl/source/pdf/PDFiumLibrary.cxx
@@ -254,15 +254,19 @@ public:
     double getFontSize() override;
     OUString getFontName() override;
     PDFTextRenderMode getTextRenderMode() override;
+    bool hasTransparency() override;
     Color getFillColor() override;
     Color getStrokeColor() override;
     double getStrokeWidth() override;
     // Path
     int getPathSegmentCount() override;
     std::unique_ptr<PDFiumPathSegment> getPathSegment(int index) override;
+    bool getDrawMode(PDFFillMode& eFillMode, bool& bStroke) override;
+    // Image
     Size getImageSize(PDFiumPage& rPage) override;
+    PDFImageMetadata getImageMetadata(PDFiumPage& rPage) override;
     std::unique_ptr<PDFiumBitmap> getImageBitmap() override;
-    bool getDrawMode(PDFFillMode& eFillMode, bool& bStroke) override;
+    bool getDecodedImageData(std::vector<sal_uInt8>& rData) override;
 };
 
 class PDFiumSearchHandleImpl final : public PDFiumSearchHandle
@@ -840,6 +844,8 @@ Color PDFiumPageObjectImpl::getFillColor()
     return aColor;
 }
 
+bool PDFiumPageObjectImpl::hasTransparency() { return FPDFPageObj_HasTransparency(mpPageObject); }
+
 Color PDFiumPageObjectImpl::getStrokeColor()
 {
     Color aColor = COL_TRANSPARENT;
@@ -879,6 +885,28 @@ Size PDFiumPageObjectImpl::getImageSize(PDFiumPage& rPage)
     return Size(aMeta.width, aMeta.height);
 }
 
+PDFImageMetadata PDFiumPageObjectImpl::getImageMetadata(PDFiumPage& rPage)
+{
+    FPDF_IMAGEOBJ_METADATA aMeta;
+    auto& rPageImpl = static_cast<PDFiumPageImpl&>(rPage);
+    FPDFImageObj_GetImageMetadata(mpPageObject, rPageImpl.getPointer(), &aMeta);
+    return { aMeta.width, aMeta.height, aMeta.bits_per_pixel };
+}
+
+bool PDFiumPageObjectImpl::getDecodedImageData(std::vector<sal_uInt8>& rData)
+{
+    unsigned long nLength = FPDFImageObj_GetImageDataDecoded(mpPageObject, nullptr, 0);
+    if (nLength > 0)
+    {
+        rData.resize(nLength);
+        unsigned long nReadLength
+            = FPDFImageObj_GetImageDataDecoded(mpPageObject, rData.data(), nLength);
+        if (nReadLength == nLength)
+            return true;
+    }
+    return false;
+}
+
 std::unique_ptr<PDFiumBitmap> PDFiumPageObjectImpl::getImageBitmap()
 {
     std::unique_ptr<PDFiumBitmap> pPDFiumBitmap;
commit 313a81d9b21e4d214b8c0fc242d242c1e25855e1
Author:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Mon May 3 16:42:22 2021 +0900
Commit:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
CommitDate: Mon Aug 2 23:17:46 2021 +0900

    Add PDF importer to read a PDF into drawinglayer primitives
    
    Change-Id: I8d2e28a39515dfef8a1b4c6b06df095dd3a4eaec

diff --git a/Repository.mk b/Repository.mk
index 74ec315aae5c..3a58a23c0d75 100644
--- a/Repository.mk
+++ b/Repository.mk
@@ -402,6 +402,7 @@ $(eval $(call gb_Helper_register_libraries_for_install,OOOLIBS,ooo, \
 	passwordcontainer \
 	pcr \
 	pdffilter \
+	pdfimporter \
 	$(call gb_Helper_optional,SCRIPTING,protocolhandler) \
 	sax \
 	sb \
diff --git a/drawinglayer/source/tools/primitive2dxmldump.cxx b/drawinglayer/source/tools/primitive2dxmldump.cxx
index d48115baafa6..40288fea32ac 100644
--- a/drawinglayer/source/tools/primitive2dxmldump.cxx
+++ b/drawinglayer/source/tools/primitive2dxmldump.cxx
@@ -171,6 +171,13 @@ void Primitive2dXmlDump::dump(
     aWriter.endDocument();
 
     pStream->Seek(STREAM_SEEK_TO_BEGIN);
+
+    std::size_t nSize = pStream->remainingSize();
+    std::unique_ptr<sal_uInt8[]> pBuffer(new sal_uInt8[nSize + 1]);
+    pStream->ReadBytes(pBuffer.get(), nSize);
+    pBuffer[nSize] = 0;
+
+    printf ("%s\n", pBuffer.get());
 }
 
 xmlDocUniquePtr Primitive2dXmlDump::dumpAndParse(
diff --git a/filter/CppunitTest_filter_pdfimporter_test.mk b/filter/CppunitTest_filter_pdfimporter_test.mk
new file mode 100644
index 000000000000..3b17dcdcb43f
--- /dev/null
+++ b/filter/CppunitTest_filter_pdfimporter_test.mk
@@ -0,0 +1,48 @@
+# -*- 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,filter_pdfimporter_test))
+
+$(eval $(call gb_CppunitTest_use_externals,filter_pdfimporter_test,\
+	boost_headers \
+	libxml2 \
+))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,filter_pdfimporter_test, \
+    filter/qa/cppunit/PdfImporterTest \
+))
+
+$(eval $(call gb_CppunitTest_use_library_objects,filter_pdfimporter_test,\
+    pdfimporter \
+))
+
+$(eval $(call gb_CppunitTest_use_libraries,filter_pdfimporter_test, \
+    basegfx \
+    drawinglayer \
+    comphelper \
+    cppu \
+    cppuhelper \
+    sal \
+    test \
+    unotest \
+    utl \
+    tl \
+    vcl \
+    tk \
+))
+
+$(eval $(call gb_CppunitTest_use_configuration,filter_pdfimporter_test))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,filter_pdfimporter_test))
+$(eval $(call gb_CppunitTest_use_ure,filter_pdfimporter_test))
+$(eval $(call gb_CppunitTest_use_vcl,filter_pdfimporter_test))
+$(eval $(call gb_CppunitTest_use_rdb,filter_pdfimporter_test,services))
+
+
+# vim: set noet sw=4 ts=4:
diff --git a/filter/Library_pdfimporter.mk b/filter/Library_pdfimporter.mk
new file mode 100644
index 000000000000..93a567cbc247
--- /dev/null
+++ b/filter/Library_pdfimporter.mk
@@ -0,0 +1,35 @@
+# -*- 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_Library_Library,pdfimporter))
+
+$(eval $(call gb_Library_set_include,pdfimporter,\
+	-I$(SRCDIR)/filter/inc \
+	$$(INCLUDE) \
+))
+
+$(eval $(call gb_Library_use_external,pdfimporter,boost_headers))
+
+$(eval $(call gb_Library_add_exception_objects,pdfimporter,\
+	filter/source/pdfimporter/PDFImporter \
+))
+
+$(eval $(call gb_Library_use_libraries,pdfimporter,\
+	drawinglayer \
+	basegfx \
+	vcl \
+	tl \
+	sal \
+	cppu \
+	tk \
+))
+
+$(eval $(call gb_Library_use_sdk_api,pdfimporter))
+
+# vim: set noet sw=4 ts=4:
diff --git a/filter/Module_filter.mk b/filter/Module_filter.mk
index 42a82bacf5c0..e079e523aa99 100644
--- a/filter/Module_filter.mk
+++ b/filter/Module_filter.mk
@@ -26,6 +26,7 @@ $(eval $(call gb_Module_add_targets,filter,\
 	Library_msfilter \
 	Library_odfflatxml \
 	Library_pdffilter \
+	Library_pdfimporter \
 	Library_storagefd \
 	Library_svgfilter \
 	Library_graphicfilter \
@@ -49,6 +50,7 @@ $(eval $(call gb_Module_add_check_targets,filter,\
     CppunitTest_filter_xslt \
     CppunitTest_filter_priority \
     CppunitTest_filter_msfilter \
+    CppunitTest_filter_pdfimporter_test \
     CppunitTest_filter_textfilterdetect \
 ))
 
diff --git a/filter/qa/cppunit/PdfImporterTest.cxx b/filter/qa/cppunit/PdfImporterTest.cxx
new file mode 100644
index 000000000000..ee57b9bdbc67
--- /dev/null
+++ b/filter/qa/cppunit/PdfImporterTest.cxx
@@ -0,0 +1,173 @@
+/* -*- 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 <sal/config.h>
+#include <sal/types.h>
+#include <test/bootstrapfixture.hxx>
+#include <test/xmltesttools.hxx>
+
+#include <filter/pdfimporter/PDFImporter.hxx>
+#include <vcl/BinaryDataContainer.hxx>
+#include <tools/stream.hxx>
+#include <drawinglayer/converters.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/tools/primitive2dxmldump.hxx>
+
+#include <tools/stream.hxx>
+#include <vcl/pngwrite.hxx>
+
+class PDFImporterTest : public test::BootstrapFixture, public XmlTestTools
+{
+    OUString getFullUrl(std::u16string_view sFileName)
+    {
+        return m_directories.getURLFromSrc(u"/filter/qa/cppunit/data/") + sFileName;
+    }
+
+    void testPath();
+    void testImage();
+    void testText();
+
+    CPPUNIT_TEST_SUITE(PDFImporterTest);
+    //CPPUNIT_TEST(testPath);
+    //CPPUNIT_TEST(testImage);
+    CPPUNIT_TEST(testText);
+    CPPUNIT_TEST_SUITE_END();
+};
+
+void PDFImporterTest::testPath()
+{
+    SvFileStream aFileStream(getFullUrl(u"/PdfTest-Rect.pdf"), StreamMode::READ);
+    const sal_uInt64 nStreamLength = aFileStream.TellEnd();
+    auto rData = std::make_unique<std::vector<sal_uInt8>>(nStreamLength);
+    aFileStream.ReadBytes(rData->data(), rData->size());
+    BinaryDataContainer aDataContainer(std::move(rData));
+    PDFImporter aImporter(aDataContainer);
+
+    drawinglayer::primitive2d::Primitive2DContainer aContainer;
+    aImporter.importPage(0, aContainer);
+
+    drawinglayer::geometry::ViewInformation2D rViewInformation2D;
+
+    auto aRange = aContainer.getB2DRange(rViewInformation2D);
+
+    BitmapEx aBitmapEx
+        = drawinglayer::convertToBitmapEx(aContainer, rViewInformation2D, 2000, 2000, 10000 * 1000);
+
+    SvFileStream aNew("~/xxxxxxx.png", StreamMode::WRITE | StreamMode::TRUNC);
+    vcl::PNGWriter aPNGWriter(aBitmapEx);
+    aPNGWriter.Write(aNew);
+
+    drawinglayer::Primitive2dXmlDump aDumper;
+    aDumper.dump(aContainer, OUString());
+    xmlDocUniquePtr pDocument = aDumper.dumpAndParse(aContainer);
+    CPPUNIT_ASSERT(pDocument);
+
+    assertXPath(pDocument, "/primitive2D/metafile/transform/mask/transform/unifiedtransparence",
+                "transparence", "0.498039215686275");
+
+    CPPUNIT_ASSERT_EQUAL(-0.5, aRange.getMinX());
+    CPPUNIT_ASSERT_EQUAL(-0.5, aRange.getMinY());
+    CPPUNIT_ASSERT_EQUAL(612.5, aRange.getMaxX());
+    CPPUNIT_ASSERT_EQUAL(792.5, aRange.getMaxY());
+
+    CPPUNIT_ASSERT_EQUAL(tools::Long(1000), aBitmapEx.GetSizePixel().Width());
+    CPPUNIT_ASSERT_EQUAL(tools::Long(1000), aBitmapEx.GetSizePixel().Height());
+
+    CPPUNIT_ASSERT(false);
+}
+
+void PDFImporterTest::testImage()
+{
+    SvFileStream aFileStream(getFullUrl(u"/PdfTest-Image.pdf"), StreamMode::READ);
+    const sal_uInt64 nStreamLength = aFileStream.TellEnd();
+    auto rData = std::make_unique<std::vector<sal_uInt8>>(nStreamLength);
+    aFileStream.ReadBytes(rData->data(), rData->size());
+    BinaryDataContainer aDataContainer(std::move(rData));
+    PDFImporter aImporter(aDataContainer);
+
+    drawinglayer::primitive2d::Primitive2DContainer aContainer;
+    aImporter.importPage(0, aContainer);
+
+    drawinglayer::geometry::ViewInformation2D rViewInformation2D;
+
+    auto aRange = aContainer.getB2DRange(rViewInformation2D);
+
+    BitmapEx aBitmapEx
+        = drawinglayer::convertToBitmapEx(aContainer, rViewInformation2D, 2000, 2000, 10000 * 1000);
+
+    SvFileStream aNew("~/xxxxxxx.png", StreamMode::WRITE | StreamMode::TRUNC);
+    vcl::PNGWriter aPNGWriter(aBitmapEx);
+    aPNGWriter.Write(aNew);
+
+    drawinglayer::Primitive2dXmlDump aDumper;
+    aDumper.dump(aContainer, OUString());
+    xmlDocUniquePtr pDocument = aDumper.dumpAndParse(aContainer);
+    CPPUNIT_ASSERT(pDocument);
+
+    assertXPath(pDocument, "/primitive2D/metafile/transform/mask/transform/unifiedtransparence",
+                "transparence", "0.498039215686275");
+
+    CPPUNIT_ASSERT_EQUAL(-0.5, aRange.getMinX());
+    CPPUNIT_ASSERT_EQUAL(-0.5, aRange.getMinY());
+    CPPUNIT_ASSERT_EQUAL(612.5, aRange.getMaxX());
+    CPPUNIT_ASSERT_EQUAL(792.5, aRange.getMaxY());
+
+    CPPUNIT_ASSERT_EQUAL(tools::Long(1000), aBitmapEx.GetSizePixel().Width());
+    CPPUNIT_ASSERT_EQUAL(tools::Long(1000), aBitmapEx.GetSizePixel().Height());
+
+    CPPUNIT_ASSERT(false);
+}
+
+void PDFImporterTest::testText()
+{
+    SvFileStream aFileStream(getFullUrl(u"/PdfTest-Text.pdf"), StreamMode::READ);
+    const sal_uInt64 nStreamLength = aFileStream.TellEnd();
+    auto rData = std::make_unique<std::vector<sal_uInt8>>(nStreamLength);
+    aFileStream.ReadBytes(rData->data(), rData->size());
+    BinaryDataContainer aDataContainer(std::move(rData));
+    PDFImporter aImporter(aDataContainer);
+
+    drawinglayer::primitive2d::Primitive2DContainer aContainer;
+    aImporter.importPage(0, aContainer);
+
+    drawinglayer::geometry::ViewInformation2D rViewInformation2D;
+
+    auto aRange = aContainer.getB2DRange(rViewInformation2D);
+
+    BitmapEx aBitmapEx
+        = drawinglayer::convertToBitmapEx(aContainer, rViewInformation2D, 2000, 2000, 10000 * 1000);
+
+    SvFileStream aNew("~/xxxxxxx.png", StreamMode::WRITE | StreamMode::TRUNC);
+    vcl::PNGWriter aPNGWriter(aBitmapEx);
+    aPNGWriter.Write(aNew);
+
+    drawinglayer::Primitive2dXmlDump aDumper;
+    aDumper.dump(aContainer, OUString());
+    xmlDocUniquePtr pDocument = aDumper.dumpAndParse(aContainer);
+    CPPUNIT_ASSERT(pDocument);
+
+    assertXPath(pDocument, "/primitive2D/metafile/transform/mask/transform/unifiedtransparence",
+                "transparence", "0.498039215686275");
+
+    CPPUNIT_ASSERT_EQUAL(-0.5, aRange.getMinX());
+    CPPUNIT_ASSERT_EQUAL(-0.5, aRange.getMinY());
+    CPPUNIT_ASSERT_EQUAL(612.5, aRange.getMaxX());
+    CPPUNIT_ASSERT_EQUAL(792.5, aRange.getMaxY());
+
+    CPPUNIT_ASSERT_EQUAL(tools::Long(1000), aBitmapEx.GetSizePixel().Width());
+    CPPUNIT_ASSERT_EQUAL(tools::Long(1000), aBitmapEx.GetSizePixel().Height());
+
+    CPPUNIT_ASSERT(false);
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(PDFImporterTest);
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/qa/cppunit/data/PdfTest-Image.pdf b/filter/qa/cppunit/data/PdfTest-Image.pdf
new file mode 100644
index 000000000000..394ee272f3d4
Binary files /dev/null and b/filter/qa/cppunit/data/PdfTest-Image.pdf differ
diff --git a/filter/qa/cppunit/data/PdfTest-Rect.pdf b/filter/qa/cppunit/data/PdfTest-Rect.pdf
new file mode 100644
index 000000000000..7115df65176f
--- /dev/null
+++ b/filter/qa/cppunit/data/PdfTest-Rect.pdf
@@ -0,0 +1,134 @@
+%PDF-1.6
+%äüöß
+2 0 obj
+<</Length 3 0 R/Filter/FlateDecode>>
+stream
+xœ¥ÁJA†ïyŠœ{˜&Ù™ÙY(=¸UQè¡zð	Úªl.H_ßÄìÈ"¢™Ãü!Éÿ%¡Àx7$¤@R03‡®el;ÿÇ=<-ðŒöÆ#¦Oà…ò
X#7¾ìLyÅ3°¼y9¾ûÕj¹íï6HëõÕ¦
+1¶”´%K“̤0·ÙhL:R,ÊSsSHÇ8Sž¬õBJ¹duL’bg„¢|¸Õ•s ê4¸èŒ÷ºÜë¿h°ƒëmÿ}Eö¢	ç‰Æ…ª£*§H’™òlíø…'ž´‘8a¥ÔñM9¬É4Sž­ÓQºÃX8wY
+endstream
+endobj
+
+3 0 obj
+241
+endobj
+
+8 0 obj
+<<
+>>
+endobj
+
+9 0 obj
+<</Font 8 0 R
+/ProcSet[/PDF/Text]
+>>
+endobj
+
+1 0 obj
+<</Type/Page/Parent 7 0 R/Resources 9 0 R/MediaBox[0 0 612 792]/StructParents 0
+/Group<</S/Transparency/CS/DeviceRGB/I true>>/Contents 2 0 R>>
+endobj
+
+10 0 obj
+<</Count 1/First 11 0 R/Last 11 0 R
+>>
+endobj
+
+11 0 obj
+<</Count 0/Title<FEFF005000610067006500200031>
+/Dest[1 0 R/XYZ 0 792 0]/Parent 10 0 R>>
+endobj
+
+4 0 obj
+<</Type/StructElem
+/S/Figure
+/P 12 0 R
+/Pg 1 0 R
+/K[0 ]
+>>
+endobj
+
+5 0 obj
+<</Type/StructElem
+/S/Figure
+/P 12 0 R
+/Pg 1 0 R
+/K[1 ]
+>>
+endobj
+
+6 0 obj
+<</Type/StructElem
+/S/Figure
+/P 12 0 R
+/Pg 1 0 R
+/K[2 ]
+>>
+endobj
+
+12 0 obj
+<</Type/StructTreeRoot
+/ParentTree 13 0 R
+/K[4 0 R 5 0 R 6 0 R ]
+>>
+endobj
+
+13 0 obj
+<</Nums[
+0 [ 4 0 R 5 0 R 6 0 R ]
+]>>
+endobj
+
+7 0 obj
+<</Type/Pages
+/Resources 9 0 R
+/MediaBox[ 0 0 612 792 ]
+/Kids[ 1 0 R ]
+/Count 1>>
+endobj
+
+14 0 obj
+<</Type/Catalog/Pages 7 0 R
+/OpenAction[1 0 R /XYZ null null 0]
+/ViewerPreferences<</DisplayDocTitle true
+>>
+/Outlines 10 0 R
+/StructTreeRoot 12 0 R
+/MarkInfo<</Marked true>>
+>>
+endobj
+
+15 0 obj
+<</Title<FEFF004D007900540065006D0070006C006100740065>
+/Creator<FEFF0044007200610077>
+/Producer<FEFF004C0069006200720065004F0066006600690063006500200037002E0030>
+/CreationDate(D:20210505091047+09'00')>>
+endobj
+
+xref
+0 16
+0000000000 65535 f 
+0000000426 00000 n 
+0000000019 00000 n 
+0000000331 00000 n 
+0000000746 00000 n 
+0000000821 00000 n 
+0000000896 00000 n 
+0000001110 00000 n 
+0000000351 00000 n 
+0000000373 00000 n 
+0000000585 00000 n 
+0000000641 00000 n 
+0000000971 00000 n 
+0000001056 00000 n 
+0000001208 00000 n 
+0000001403 00000 n 
+trailer
+<</Size 16/Root 14 0 R
+/Info 15 0 R
+/ID [ <3F7788F2D0928B3B95B0CDBBD9EAC1B8>
+<3F7788F2D0928B3B95B0CDBBD9EAC1B8> ]
+/DocChecksum /F2715E25D79DC609834CA579FA23A5BA
+>>
+startxref
+1623
+%%EOF
diff --git a/filter/qa/cppunit/data/PdfTest-Text.pdf b/filter/qa/cppunit/data/PdfTest-Text.pdf
new file mode 100644
index 000000000000..c449bf559228
Binary files /dev/null and b/filter/qa/cppunit/data/PdfTest-Text.pdf differ
diff --git a/filter/source/pdfimporter/PDFImporter.cxx b/filter/source/pdfimporter/PDFImporter.cxx
new file mode 100644
index 000000000000..b4b37aea7128
--- /dev/null
+++ b/filter/source/pdfimporter/PDFImporter.cxx
@@ -0,0 +1,384 @@
+/* -*- 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 <filter/pdfimporter/PDFImporter.hxx>
+
+#include <sal/log.hxx>
+#include <tools/UnitConversion.hxx>
+#include <tools/color.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/BitmapTools.hxx>
+
+#include <cmath>
+
+#include <toolkit/helper/vclunohelper.hxx>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+
+#include <drawinglayer/attribute/lineattribute.hxx>
+#include <drawinglayer/attribute/strokeattribute.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
+#include <drawinglayer/primitive2d/hiddengeometryprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+
+PDFImporter::PDFImporter(BinaryDataContainer& rDataContainer)
+    : mpPDFium(vcl::pdf::PDFiumLibrary::get())
+{
+    auto* pData = rDataContainer.getData();
+    sal_Int32 nSize = rDataContainer.getSize();
+
+    mpPdfDocument = mpPDFium->openDocument(pData, nSize);
+}
+
+namespace
+{
+void setupPage(drawinglayer::primitive2d::Primitive2DContainer& rContainer,
+               basegfx::B2DSize const& rPageSize)
+{
+    basegfx::B2DRange aPageRange(0.0, 0.0, rPageSize.getX(), rPageSize.getY());
+
+    printf("Page Size %.2fpt %.2fpt\n", rPageSize.getX(), rPageSize.getY());
+
+    const auto aPolygon = basegfx::utils::createPolygonFromRect(aPageRange);
+
+    const drawinglayer::primitive2d::Primitive2DReference xPage(
+        new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(aPolygon,
+                                                                  basegfx::BColor(0.0, 0.0, 0.0)));
+    const drawinglayer::primitive2d::Primitive2DReference xHidden(
+        new drawinglayer::primitive2d::HiddenGeometryPrimitive2D(
+            drawinglayer::primitive2d::Primitive2DContainer{ xPage }));
+
+    rContainer.push_back(xHidden);
+}
+
+double sqrt2(double a, double b) { return sqrt(a * a + b * b); }
+
+} // end anonymous namespace
+
+bool PDFImporter::importPage(int nPageIndex,
+                             drawinglayer::primitive2d::Primitive2DContainer& rContainer)
+{
+    if (!mpPdfDocument)
+        return false;
+
+    drawinglayer::primitive2d::Primitive2DContainer aContent;
+
+    const int nPageCount = mpPdfDocument->getPageCount();
+    if (!(nPageCount > 0 && nPageIndex >= 0 && nPageIndex < nPageCount))
+        return false;
+
+    mpPdfPage = mpPdfDocument->openPage(nPageIndex);
+    if (!mpPdfPage)
+        return false;
+
+    basegfx::B2DSize aPageSize = mpPdfDocument->getPageSize(nPageIndex);
+
+    setupPage(aContent, aPageSize);
+
+    // Load the page text to extract it when we get text elements.
+    auto pTextPage = mpPdfPage->getTextPage();
+
+    const int nPageObjectCount = mpPdfPage->getObjectCount();
+
+    for (int nPageObjectIndex = 0; nPageObjectIndex < nPageObjectCount; ++nPageObjectIndex)
+    {
+        auto pPageObject = mpPdfPage->getObject(nPageObjectIndex);
+        importPdfObject(pPageObject, pTextPage, nPageObjectIndex, aContent);
+    }
+
+    // point to pixel conversion
+    double dConversionFactor = double(conversionFract(o3tl::Length::pt, o3tl::Length::px));
+    const auto aTransform = basegfx::utils::createScaleTranslateB2DHomMatrix(
+        dConversionFactor, -dConversionFactor, 0.0, aPageSize.getY() * dConversionFactor);
+
+    const drawinglayer::primitive2d::Primitive2DReference xTransform(
+        new drawinglayer::primitive2d::TransformPrimitive2D(aTransform, aContent));
+
+    rContainer.push_back(xTransform);
+
+    return true;
+}
+
+void PDFImporter::importPdfObject(std::unique_ptr<vcl::pdf::PDFiumPageObject> const& pPageObject,
+                                  std::unique_ptr<vcl::pdf::PDFiumTextPage> const& pTextPage,
+                                  int nPageObjectIndex,
+                                  drawinglayer::primitive2d::Primitive2DContainer& rContent)
+{
+    if (!pPageObject)
+        return;
+
+    const vcl::pdf::PDFPageObjectType ePageObjectType = pPageObject->getType();
+    switch (ePageObjectType)
+    {
+        case vcl::pdf::PDFPageObjectType::Text:
+            printf("pdf::PDFPageObjectType::Text\n");
+            importText(pPageObject, pTextPage, rContent);
+            break;
+        case vcl::pdf::PDFPageObjectType::Path:
+            printf("pdf::PDFPageObjectType::Path\n");
+            importPath(pPageObject, rContent);
+            break;
+        case vcl::pdf::PDFPageObjectType::Image:
+            printf("pdf::PDFPageObjectType::Image\n");
+            importImage(pPageObject, rContent);
+            break;
+        case vcl::pdf::PDFPageObjectType::Shading:
+            printf("pdf::PDFPageObjectType::Shading\n");
+            break;
+        case vcl::pdf::PDFPageObjectType::Form:
+            printf("pdf::PDFPageObjectType::Form\n");
+            break;
+        case vcl::pdf::PDFPageObjectType::Unknown:
+            SAL_WARN("filter", "Unknown PDF page object #" << nPageObjectIndex
+                                                           << " of type: " << int(ePageObjectType));
+            break;
+    }
+}
+
+void PDFImporter::importText(std::unique_ptr<vcl::pdf::PDFiumPageObject> const& pPageObject,
+                             std::unique_ptr<vcl::pdf::PDFiumTextPage> const& pTextPage,
+                             drawinglayer::primitive2d::Primitive2DContainer& rRootContainer)
+{
+    basegfx::B2DRectangle aTextRect = pPageObject->getBounds();
+    basegfx::B2DHomMatrix aMatrix = pPageObject->getMatrix();
+
+    OUString sText = pPageObject->getText(pTextPage);
+
+    const double dFontSize = pPageObject->getFontSize();
+    double dFontSizeH = std::fabs(sqrt2(aMatrix.a(), aMatrix.c()) * dFontSize);
+    double dFontSizeV = std::fabs(sqrt2(aMatrix.b(), aMatrix.d()) * dFontSize);
+
+    OUString sFontName = pPageObject->getFontName();
+
+    printf("TEXT: %s\n", sText.toUtf8().getStr());
+
+    Color aTextColor(COL_TRANSPARENT);
+    bool bFill = false;
+    bool bUse = true;
+
+    switch (pPageObject->getTextRenderMode())
+    {
+        case vcl::pdf::PDFTextRenderMode::Fill:
+        case vcl::pdf::PDFTextRenderMode::FillClip:
+        case vcl::pdf::PDFTextRenderMode::FillStroke:
+        case vcl::pdf::PDFTextRenderMode::FillStrokeClip:
+            bFill = true;
+            break;
+        case vcl::pdf::PDFTextRenderMode::Stroke:
+        case vcl::pdf::PDFTextRenderMode::StrokeClip:
+        case vcl::pdf::PDFTextRenderMode::Unknown:
+            break;
+        case vcl::pdf::PDFTextRenderMode::Invisible:
+        case vcl::pdf::PDFTextRenderMode::Clip:
+            bUse = false;
+            break;
+    }
+
+    if (bUse)
+    {
+        Color aColor = bFill ? pPageObject->getFillColor() : pPageObject->getStrokeColor();
+        if (aColor != COL_TRANSPARENT)
+        {
+            aTextColor = aColor.GetRGBColor();
+        }
+    }
+}
+
+void PDFImporter::importImage(std::unique_ptr<vcl::pdf::PDFiumPageObject> const& pPageObject,
+                              drawinglayer::primitive2d::Primitive2DContainer& rRootContainer)
+{
+    std::unique_ptr<vcl::pdf::PDFiumBitmap> pPdfBitmap = pPageObject->getImageBitmap();
+    if (!pPdfBitmap)
+    {
+        SAL_WARN("filter", "Failed to get IMAGE");
+        return;
+    }
+
+    const vcl::pdf::PDFBitmapType eFormat = pPdfBitmap->getFormat();
+    if (eFormat == vcl::pdf::PDFBitmapType::Unknown)
+    {
+        SAL_WARN("filter", "Failed to get IMAGE format");
+        return;
+    }
+
+    vcl::pdf::PDFImageMetadata aMetadata = pPageObject->getImageMetadata(*mpPdfPage);
+    printf("METADATA %lu %lu %lu\n", aMetadata.mnWidth, aMetadata.mnHeight,
+           aMetadata.mnBitsPerPixel);
+
+    const sal_uInt8* pBuffer = pPdfBitmap->getBuffer();
+    const int nWidth = pPdfBitmap->getWidth();
+    const int nHeight = pPdfBitmap->getHeight();
+    const int nStride = pPdfBitmap->getStride();
+
+    BitmapEx aBitmap;
+
+    printf("hasTransparency %d\n", pPageObject->hasTransparency());
+
+    switch (eFormat)
+    {
+        case vcl::pdf::PDFBitmapType::BGR:
+            printf("vcl::pdf::PDFBitmapType::BGR\n");
+            aBitmap = vcl::bitmap::CreateFromData(pBuffer, nWidth, nHeight, nStride,
+                                                  vcl::PixelFormat::N24_BPP);
+            break;
+        case vcl::pdf::PDFBitmapType::BGRx:
+            printf("vcl::pdf::PDFBitmapType::BGRx\n");
+            aBitmap = vcl::bitmap::CreateFromData(pBuffer, nWidth, nHeight, nStride,
+                                                  vcl::PixelFormat::N32_BPP);
+            break;
+        case vcl::pdf::PDFBitmapType::BGRA:
+            printf("vcl::pdf::PDFBitmapType::BGRA\n");
+            aBitmap = vcl::bitmap::CreateFromData(pBuffer, nWidth, nHeight, nStride,
+                                                  vcl::PixelFormat::N32_BPP);
+            break;
+        case vcl::pdf::PDFBitmapType::Gray:
+            // TODO
+        default:
+            SAL_WARN("filter", "Got IMAGE width: " << nWidth << ", height: " << nHeight
+                                                   << ", stride: " << nStride
+                                                   << ", format: " << int(eFormat));
+            break;
+    }
+
+    basegfx::B2DRectangle aBounds = pPageObject->getBounds();
+
+    rRootContainer.push_back(new drawinglayer::primitive2d::BitmapPrimitive2D(
+        VCLUnoHelper::CreateVCLXBitmap(aBitmap), basegfx::utils::createScaleTranslateB2DHomMatrix(
+                                                     aBounds.getRange(), aBounds.getMinimum())));
+}
+
+void PDFImporter::importPath(std::unique_ptr<vcl::pdf::PDFiumPageObject> const& pPageObject,
+                             drawinglayer::primitive2d::Primitive2DContainer& rRootContainer)
+{
+    drawinglayer::primitive2d::Primitive2DContainer aContent;
+
+    basegfx::B2DHomMatrix aPathMatrix = pPageObject->getMatrix();
+
+    basegfx::B2DPolyPolygon aPolyPolygon;
+    basegfx::B2DPolygon aPolygon;
+    std::vector<basegfx::B2DPoint> aBezier;
+
+    const int nSegments = pPageObject->getPathSegmentCount();
+    for (int nSegmentIndex = 0; nSegmentIndex < nSegments; ++nSegmentIndex)
+    {
+        auto pPathSegment = pPageObject->getPathSegment(nSegmentIndex);
+        if (!pPathSegment)
+            continue;
+
+        basegfx::B2DPoint aB2DPoint = pPathSegment->getPoint();
+
+        aPolygon.setClosed(pPathSegment->isClosed());
+
+        const vcl::pdf::PDFSegmentType eSegmentType = pPathSegment->getType();
+        switch (eSegmentType)
+        {
+            case vcl::pdf::PDFSegmentType::Lineto:
+            {
+                aPolygon.append(aB2DPoint);
+            }
+            break;
+
+            case vcl::pdf::PDFSegmentType::Bezierto:
+            {
+                aBezier.emplace_back(aB2DPoint.getX(), aB2DPoint.getY());
+                if (aBezier.size() == 3)
+                {
+                    aPolygon.appendBezierSegment(aBezier[0], aBezier[1], aBezier[2]);
+                    aBezier.clear();
+                }
+            }
+            break;
+
+            case vcl::pdf::PDFSegmentType::Moveto:
+            {
+                if (aPolygon.count() > 0)
+                {
+                    aPolyPolygon.append(aPolygon);
+                    aPolygon.clear();
+                }
+
+                aPolygon.append(aB2DPoint);
+            }
+            break;
+
+            case vcl::pdf::PDFSegmentType::Unknown:
+            default:
+            {
+                SAL_WARN("filter", "Unknown path segment type in PDF: " << int(eSegmentType));
+            }
+            break;
+        }
+    }
+
+    if (aBezier.size() == 3)
+    {
+        aPolygon.appendBezierSegment(aBezier[0], aBezier[1], aBezier[2]);
+        aBezier.clear();
+    }
+
+    if (aPolygon.count() > 0)
+    {
+        aPolyPolygon.append(aPolygon, 1);
+        aPolygon.clear();
+    }
+
+    printf("PolyPoly size %d\n", aPolyPolygon.count());
+    for (auto const& rPoly : aPolyPolygon)
+        printf("Poly size %d\n", rPoly.count());
+
+    double fStrokeWidth = pPageObject->getStrokeWidth();
+    printf("Stroke: %f\n", fStrokeWidth);
+
+    vcl::pdf::PDFFillMode nFillMode = vcl::pdf::PDFFillMode::Alternate;
+    bool bStroke = true;
+
+    if (!pPageObject->getDrawMode(nFillMode, bStroke))
+    {
+        SAL_WARN("filter", "Huh...");
+    }
+
+    Color aFillColor = pPageObject->getFillColor();
+    Color aStokeColor = COL_TRANSPARENT;
+
+    if (bStroke)
+    {
+        aStokeColor = pPageObject->getStrokeColor();
+    }
+
+    if (aStokeColor == COL_TRANSPARENT)
+        aStokeColor = aFillColor;
+
+    if (!bStroke)
+    {
+        const drawinglayer::primitive2d::Primitive2DReference xPolyPolygonColorPrimitive(
+            new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(aPolyPolygon,
+                                                                       aFillColor.getBColor()));
+        aContent.push_back(xPolyPolygonColorPrimitive);
+    }
+
+    drawinglayer::attribute::LineAttribute aLineAttribute(aStokeColor.getBColor(), fStrokeWidth);
+    const drawinglayer::primitive2d::Primitive2DReference xPolyPolygonStrokePrimitive(
+        new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(aPolyPolygon, aLineAttribute));
+    aContent.push_back(xPolyPolygonStrokePrimitive);
+
+    const drawinglayer::primitive2d::Primitive2DReference xTransform(
+        new drawinglayer::primitive2d::TransformPrimitive2D(aPathMatrix, aContent));
+    rRootContainer.push_back(xTransform);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/filter/pdfimporter/PDFImporter.hxx b/include/filter/pdfimporter/PDFImporter.hxx
new file mode 100644
index 000000000000..01cd94d264c9
--- /dev/null
+++ b/include/filter/pdfimporter/PDFImporter.hxx
@@ -0,0 +1,45 @@
+/* -*- 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/filter/PDFiumLibrary.hxx>
+#include <drawinglayer/primitive2d/Primitive2DContainer.hxx>
+#include <vcl/BinaryDataContainer.hxx>
+
+class PDFImporter
+{
+private:
+    std::shared_ptr<vcl::pdf::PDFium> mpPDFium;
+    std::unique_ptr<vcl::pdf::PDFiumDocument> mpPdfDocument;
+    std::unique_ptr<vcl::pdf::PDFiumPage> mpPdfPage;
+
+    void importPdfObject(std::unique_ptr<vcl::pdf::PDFiumPageObject> const& pPageObject,
+                         std::unique_ptr<vcl::pdf::PDFiumTextPage> const& pTextPage,
+                         int nPageObjectIndex,
+                         drawinglayer::primitive2d::Primitive2DContainer& rRootContainer);
+
+    void importText(std::unique_ptr<vcl::pdf::PDFiumPageObject> const& pPageObject,
+                    std::unique_ptr<vcl::pdf::PDFiumTextPage> const& pTextPage,
+                    drawinglayer::primitive2d::Primitive2DContainer& rRootContainer);
+
+    void importPath(std::unique_ptr<vcl::pdf::PDFiumPageObject> const& pPageObject,
+                    drawinglayer::primitive2d::Primitive2DContainer& rRootContainer);
+
+    void importImage(std::unique_ptr<vcl::pdf::PDFiumPageObject> const& pPageObject,
+                     drawinglayer::primitive2d::Primitive2DContainer& rRootContainer);
+
+public:
+    PDFImporter(BinaryDataContainer& rDataContainer);
+
+    bool importPage(int nPageIndex, drawinglayer::primitive2d::Primitive2DContainer& rContainer);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
commit 58789ee9b9830f5d9f4993acccdebe081f5f43b8
Author:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Thu Mar 18 15:59:20 2021 +0900
Commit:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
CommitDate: Mon Aug 2 23:17:46 2021 +0900

    vcl: bring back RGB565 scanline transformer
    
    While we don't support this as a Bitmap format anymore, we still
    need to transform a buffer that is in RGB565 format in some cases.
    For example backwards compatibility or if a certain bitmap format
    supports such pixel format.
    This change also simplifies some scanline transformers by removing
    code duplication.
    
    Change-Id: I64aa258b8b1fbebf0ed174c0d5fdd2f75f382b28

diff --git a/vcl/inc/bitmap/ScanlineTools.hxx b/vcl/inc/bitmap/ScanlineTools.hxx
index c343cf34f61e..dc305937b5fd 100644
--- a/vcl/inc/bitmap/ScanlineTools.hxx
+++ b/vcl/inc/bitmap/ScanlineTools.hxx
@@ -16,7 +16,7 @@
 
 namespace vcl::bitmap
 {
-class ScanlineTransformer
+class IScanlineTransformer
 {
 public:
     virtual void startLine(sal_uInt8* pLine) = 0;
@@ -24,127 +24,162 @@ public:
     virtual Color readPixel() = 0;
     virtual void writePixel(Color nColor) = 0;
 
-    virtual ~ScanlineTransformer() = default;
+    virtual ~IScanlineTransformer() = default;
 };
 
-class ScanlineTransformer_ARGB final : public ScanlineTransformer
+class ScanlineTransformer_RGB565 final : public IScanlineTransformer
 {
-private:
-    sal_uInt8* pData;
+protected:
+    sal_uInt16* mpData;
 
 public:
-    virtual void startLine(sal_uInt8* pLine) override { pData = pLine; }
+    void startLine(sal_uInt8* pLine) override { mpData = reinterpret_cast<sal_uInt16*>(pLine); }
 
-    virtual void skipPixel(sal_uInt32 nPixel) override { pData += nPixel << 2; }
+    void skipPixel(sal_uInt32 nPixel) override { mpData += nPixel; }
 
-    virtual Color readPixel() override
+    Color readPixel() override
     {
-        const Color aColor(ColorTransparency, pData[4], pData[1], pData[2], pData[3]);
-        pData += 4;
-        return aColor;
+        sal_uInt8 R = sal_uInt8((*mpData & 0xf800) >> 8);
+        sal_uInt8 G = sal_uInt8((*mpData & 0x07e0) >> 3);
+        sal_uInt8 B = sal_uInt8((*mpData & 0x001f) << 3);
+        mpData++;
+        return Color(R, G, B);
     }
 
-    virtual void writePixel(Color nColor) override
+    void writePixel(Color nColor) override
     {
-        *pData++ = 255 - nColor.GetAlpha();
-        *pData++ = nColor.GetRed();
-        *pData++ = nColor.GetGreen();
-        *pData++ = nColor.GetBlue();
+        sal_uInt16 R = (nColor.GetRed() & 0xf8) << 8;
+        sal_uInt16 G = (nColor.GetGreen() & 0xfc) << 3;
+        sal_uInt16 B = (nColor.GetBlue() & 0xf8) >> 3;
+        *mpData++ = R | G | B;
     }
 };
 
-class ScanlineTransformer_BGR final : public ScanlineTransformer
+class ScanlineTransformerBase : public IScanlineTransformer
 {
-private:
-    sal_uInt8* pData;
+protected:
+    sal_uInt8* mpData;
 
 public:
-    virtual void startLine(sal_uInt8* pLine) override { pData = pLine; }
+    ScanlineTransformerBase()
+        : mpData(nullptr)
+    {
+    }
+
+    void startLine(sal_uInt8* pLine) override { mpData = pLine; }
+};
 
-    virtual void skipPixel(sal_uInt32 nPixel) override { pData += (nPixel << 1) + nPixel; }
+class ScanlineTransformer_ARGB final : public ScanlineTransformerBase
+{
+public:
+    void skipPixel(sal_uInt32 nPixel) override { mpData += nPixel << 2; }
 
-    virtual Color readPixel() override
+    Color readPixel() override
     {
-        const Color aColor(pData[2], pData[1], pData[0]);
-        pData += 3;
+        const Color aColor(ColorTransparency, mpData[4], mpData[1], mpData[2], mpData[3]);
+        mpData += 4;
         return aColor;
     }
 
-    virtual void writePixel(Color nColor) override
+    void writePixel(Color nColor) override
     {
-        *pData++ = nColor.GetBlue();
-        *pData++ = nColor.GetGreen();
-        *pData++ = nColor.GetRed();
+        *mpData++ = 255 - nColor.GetAlpha();
+        *mpData++ = nColor.GetRed();
+        *mpData++ = nColor.GetGreen();
+        *mpData++ = nColor.GetBlue();
     }
 };
 
-class ScanlineTransformer_8BitPalette final : public ScanlineTransformer
+class ScanlineTransformer_BGR final : public ScanlineTransformerBase
 {
-private:
-    sal_uInt8* pData;
+public:
+    void skipPixel(sal_uInt32 nPixel) override { mpData += (nPixel << 1) + nPixel; }
+
+    Color readPixel() override
+    {
+        const Color aColor(mpData[2], mpData[1], mpData[0]);
+        mpData += 3;
+        return aColor;
+    }
+
+    void writePixel(Color nColor) override
+    {
+        *mpData++ = nColor.GetBlue();
+        *mpData++ = nColor.GetGreen();
+        *mpData++ = nColor.GetRed();
+    }
+};
+
+class ScanlineTransformerPaletteBase : public ScanlineTransformerBase
+{
+protected:
     const BitmapPalette& mrPalette;
 
 public:
-    explicit ScanlineTransformer_8BitPalette(const BitmapPalette& rPalette)
-        : pData(nullptr)
+    ScanlineTransformerPaletteBase(const BitmapPalette& rPalette)
+        : ScanlineTransformerBase()
         , mrPalette(rPalette)
     {
     }
+};
 
-    virtual void startLine(sal_uInt8* pLine) override { pData = pLine; }
+class ScanlineTransformer_8BitPalette final : public ScanlineTransformerPaletteBase
+{
+public:
+    explicit ScanlineTransformer_8BitPalette(const BitmapPalette& rPalette)
+        : ScanlineTransformerPaletteBase(rPalette)
+    {
+    }
 
-    virtual void skipPixel(sal_uInt32 nPixel) override { pData += nPixel; }
+    void skipPixel(sal_uInt32 nPixel) override { mpData += nPixel; }
 
-    virtual Color readPixel() override
+    Color readPixel() override
     {
-        const sal_uInt8 nIndex(*pData++);
+        const sal_uInt8 nIndex(*mpData++);
         if (nIndex < mrPalette.GetEntryCount())
             return mrPalette[nIndex];
         else
             return COL_BLACK;
     }
 
-    virtual void writePixel(Color nColor) override
+    void writePixel(Color nColor) override
     {
-        *pData++ = static_cast<sal_uInt8>(mrPalette.GetBestIndex(nColor));
+        *mpData++ = static_cast<sal_uInt8>(mrPalette.GetBestIndex(nColor));
     }
 };
 
-class ScanlineTransformer_4BitPalette final : public ScanlineTransformer
+class ScanlineTransformer_4BitPalette final : public ScanlineTransformerPaletteBase
 {
 private:
-    sal_uInt8* pData;
-    const BitmapPalette& mrPalette;
     sal_uInt32 mnX;
     sal_uInt32 mnShift;
 
 public:
     explicit ScanlineTransformer_4BitPalette(const BitmapPalette& rPalette)
-        : pData(nullptr)
-        , mrPalette(rPalette)
+        : ScanlineTransformerPaletteBase(rPalette)
         , mnX(0)
         , mnShift(0)
     {
     }
 
-    virtual void skipPixel(sal_uInt32 nPixel) override
+    void skipPixel(sal_uInt32 nPixel) override
     {
         mnX += nPixel;
         if (nPixel & 1) // is nPixel an odd number
             mnShift ^= 4;
     }
 
-    virtual void startLine(sal_uInt8* pLine) override
+    void startLine(sal_uInt8* pLine) override
     {
-        pData = pLine;
+        ScanlineTransformerBase::startLine(pLine);
         mnX = 0;
         mnShift = 4;
     }
 
-    virtual Color readPixel() override
+    Color readPixel() override
     {
         const sal_uInt32 nDataIndex = mnX / 2;
-        const sal_uInt8 nIndex((pData[nDataIndex] >> mnShift) & 0x0f);
+        const sal_uInt8 nIndex((mpData[nDataIndex] >> mnShift) & 0x0f);
         mnX++;
         mnShift ^= 4;
 
@@ -154,42 +189,39 @@ public:
             return COL_BLACK;
     }
 
-    virtual void writePixel(Color nColor) override
+    void writePixel(Color nColor) override
     {
         const sal_uInt32 nDataIndex = mnX / 2;
         const sal_uInt8 nColorIndex = mrPalette.GetBestIndex(nColor);
-        pData[nDataIndex] |= (nColorIndex & 0x0f) << mnShift;
+        mpData[nDataIndex] |= (nColorIndex & 0x0f) << mnShift;
         mnX++;
         mnShift ^= 4;
     }
 };
 
-class ScanlineTransformer_1BitPalette final : public ScanlineTransformer
+class ScanlineTransformer_1BitPalette final : public ScanlineTransformerPaletteBase
 {
 private:
-    sal_uInt8* pData;
-    const BitmapPalette& mrPalette;
     sal_uInt32 mnX;
 
 public:
     explicit ScanlineTransformer_1BitPalette(const BitmapPalette& rPalette)
-        : pData(nullptr)
-        , mrPalette(rPalette)
+        : ScanlineTransformerPaletteBase(rPalette)
         , mnX(0)
     {
     }
 
-    virtual void skipPixel(sal_uInt32 nPixel) override { mnX += nPixel; }
+    void skipPixel(sal_uInt32 nPixel) override { mnX += nPixel; }
 
-    virtual void startLine(sal_uInt8* pLine) override
+    void startLine(sal_uInt8* pLine) override
     {
-        pData = pLine;
+        ScanlineTransformerBase::startLine(pLine);
         mnX = 0;
     }
 
-    virtual Color readPixel() override
+    Color readPixel() override
     {
-        const sal_uInt8 nIndex((pData[mnX >> 3] >> (7 - (mnX & 7))) & 1);
+        const sal_uInt8 nIndex((mpData[mnX >> 3] >> (7 - (mnX & 7))) & 1);
         mnX++;
 
         if (nIndex < mrPalette.GetEntryCount())
@@ -198,18 +230,18 @@ public:
             return COL_BLACK;
     }
 
-    virtual void writePixel(Color nColor) override
+    void writePixel(Color nColor) override
     {
         if (mrPalette.GetBestIndex(nColor) & 1)
-            pData[mnX >> 3] |= 1 << (7 - (mnX & 7));
+            mpData[mnX >> 3] |= 1 << (7 - (mnX & 7));
         else
-            pData[mnX >> 3] &= ~(1 << (7 - (mnX & 7)));
+            mpData[mnX >> 3] &= ~(1 << (7 - (mnX & 7)));
         mnX++;
     }
 };
 
-std::unique_ptr<ScanlineTransformer> getScanlineTransformer(sal_uInt16 nBits,
-                                                            const BitmapPalette& rPalette)
+std::unique_ptr<IScanlineTransformer> getScanlineTransformer(sal_uInt16 nBits,
+                                                             const BitmapPalette& rPalette)
 {
     switch (nBits)
     {
@@ -219,6 +251,8 @@ std::unique_ptr<ScanlineTransformer> getScanlineTransformer(sal_uInt16 nBits,
             return std::make_unique<ScanlineTransformer_4BitPalette>(rPalette);
         case 8:
             return std::make_unique<ScanlineTransformer_8BitPalette>(rPalette);
+        case 16:
+            return std::make_unique<ScanlineTransformer_RGB565>();
         case 24:
             return std::make_unique<ScanlineTransformer_BGR>();
         case 32:
diff --git a/vcl/qa/cppunit/ScanlineToolsTest.cxx b/vcl/qa/cppunit/ScanlineToolsTest.cxx
index c6751b827ca1..6f497b2b8295 100644
--- a/vcl/qa/cppunit/ScanlineToolsTest.cxx
+++ b/vcl/qa/cppunit/ScanlineToolsTest.cxx
@@ -19,6 +19,7 @@ class ScanlineToolsTest : public CppUnit::TestFixture
 {
     void ScanlineTransformer_32_ARGB();
     void ScanlineTransformer_24_BGR();
+    void ScanlineTransformer_16_RGB565();
     void ScanlineTransformer_8bit_Palette();
     void ScanlineTransformer_4bit_Palette();
     void ScanlineTransformer_1bit_Palette();
@@ -26,6 +27,7 @@ class ScanlineToolsTest : public CppUnit::TestFixture
     CPPUNIT_TEST_SUITE(ScanlineToolsTest);
     CPPUNIT_TEST(ScanlineTransformer_32_ARGB);
     CPPUNIT_TEST(ScanlineTransformer_24_BGR);
+    CPPUNIT_TEST(ScanlineTransformer_16_RGB565);
     CPPUNIT_TEST(ScanlineTransformer_8bit_Palette);
     CPPUNIT_TEST(ScanlineTransformer_4bit_Palette);
     CPPUNIT_TEST(ScanlineTransformer_1bit_Palette);
@@ -35,8 +37,7 @@ class ScanlineToolsTest : public CppUnit::TestFixture
 void ScanlineToolsTest::ScanlineTransformer_32_ARGB()
 {
     BitmapPalette aPalette;
-    std::unique_ptr<vcl::bitmap::ScanlineTransformer> pScanlineTransformer
-        = vcl::bitmap::getScanlineTransformer(32, aPalette);
+    auto pScanlineTransformer = vcl::bitmap::getScanlineTransformer(32, aPalette);
 
     std::vector<sal_uInt8> aScanLine(5 * 4, 0); // 5 * 4 BytesPerPixel
     pScanlineTransformer->startLine(aScanLine.data());
@@ -64,8 +65,7 @@ void ScanlineToolsTest::ScanlineTransformer_32_ARGB()
 void ScanlineToolsTest::ScanlineTransformer_24_BGR()
 {
     BitmapPalette aPalette;
-    std::unique_ptr<vcl::bitmap::ScanlineTransformer> pScanlineTransformer
-        = vcl::bitmap::getScanlineTransformer(24, aPalette);
+    auto pScanlineTransformer = vcl::bitmap::getScanlineTransformer(24, aPalette);
 
     std::vector<sal_uInt8> aScanLine(5 * 3, 0); // 5 * 3 BytesPerPixel
     pScanlineTransformer->startLine(aScanLine.data());
@@ -90,6 +90,51 @@ void ScanlineToolsTest::ScanlineTransformer_24_BGR()
     }
 }
 
+void ScanlineToolsTest::ScanlineTransformer_16_RGB565()
+{
+    BitmapPalette aPalette;
+    auto pScanlineTransformer = vcl::bitmap::getScanlineTransformer(16, aPalette);
+
+    // Test writing - we apply colors which will be written into the scanline
+    // in the R5G6B5 format
+    std::vector<sal_uInt8> aScanLine(5 * 2, 0); // 5 * 2 BytesPerPixel
+    pScanlineTransformer->startLine(aScanLine.data());
+
+    std::vector<Color> aColors{
+        Color(ColorTransparency, 0, 10, 250, 120),   Color(ColorTransparency, 50, 30, 230, 110),
+        Color(ColorTransparency, 100, 50, 210, 100), Color(ColorTransparency, 150, 70, 190, 90),
+        Color(ColorTransparency, 200, 90, 170, 80),
+    };
+
+    for (Color const& aColor : aColors)
+    {
+        pScanlineTransformer->writePixel(aColor);
+    }
+
+    std::vector<sal_uInt8> aExpectedBytes{ 207, 15, 45, 31, 140, 54, 235, 69, 74, 93 };
+
+    for (size_t i = 0; i < aScanLine.size(); ++i)
+    {
+        CPPUNIT_ASSERT_EQUAL(int(aExpectedBytes[i]), int(aScanLine[i]));
+    }
+
+    // Test reading - we insert a scanline in R5G6B5 format and read
+    // the colors from it
+
+    pScanlineTransformer->startLine(aScanLine.data());
+
+    std::vector<Color> aExpectedColors{
+        Color(8, 248, 120), Color(24, 228, 104), Color(48, 208, 96),
+        Color(64, 188, 88), Color(88, 168, 80),
+    };
+
+    for (size_t i = 0; i < aExpectedColors.size(); ++i)
+    {
+        Color aColor = pScanlineTransformer->readPixel();
+        CPPUNIT_ASSERT_EQUAL(aExpectedColors[i], aColor);
+    }
+}
+
 void ScanlineToolsTest::ScanlineTransformer_8bit_Palette()
 {
     std::vector<Color> aColors{
@@ -102,8 +147,7 @@ void ScanlineToolsTest::ScanlineTransformer_8bit_Palette()
     for (size_t i = 0; i < aColors.size(); ++i)
         aPalette[i] = aColors[i];
 
-    std::unique_ptr<vcl::bitmap::ScanlineTransformer> pScanlineTransformer
-        = vcl::bitmap::getScanlineTransformer(8, aPalette);
+    auto pScanlineTransformer = vcl::bitmap::getScanlineTransformer(8, aPalette);
 
     std::vector<sal_uInt8> aScanLine(5, 0); // 5 * 1 BytesPerPixel
     pScanlineTransformer->startLine(aScanLine.data());
@@ -142,8 +186,7 @@ void ScanlineToolsTest::ScanlineTransformer_4bit_Palette()
         aPalette[i] = aColors[i];
     }
 
-    std::unique_ptr<vcl::bitmap::ScanlineTransformer> pScanlineTransformer
-        = vcl::bitmap::getScanlineTransformer(4, aPalette);
+    auto pScanlineTransformer = vcl::bitmap::getScanlineTransformer(4, aPalette);
 
     std::vector<sal_uInt8> aScanLine(3, 0); // 6 * 0.5 BytesPerPixel
     pScanlineTransformer->startLine(aScanLine.data());
@@ -182,8 +225,7 @@ void ScanlineToolsTest::ScanlineTransformer_1bit_Palette()
     aPalette[0] = Color(10, 250, 120);
     aPalette[1] = Color(110, 150, 70);
 
-    std::unique_ptr<vcl::bitmap::ScanlineTransformer> pScanlineTransformer
-        = vcl::bitmap::getScanlineTransformer(1, aPalette);
+    auto pScanlineTransformer = vcl::bitmap::getScanlineTransformer(1, aPalette);
 
     std::vector<sal_uInt8> aScanLine(2, 0); // 13 * 1/8 BytesPerPixel
     pScanlineTransformer->startLine(aScanLine.data());
commit e635295182e85d562eeb79609bede6ab2a9f9668
Author:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Sun Mar 7 13:48:39 2021 +0900
Commit:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
CommitDate: Mon Aug 2 23:17:46 2021 +0900

    vcl: add PNG writer based on libpng
    
    Change-Id: I52ffd1b286162ee0dd9f694c4f3210385f71daf8

diff --git a/include/vcl/filter/PngImageWriter.hxx b/include/vcl/filter/PngImageWriter.hxx
new file mode 100644
index 000000000000..4f64a028af53
--- /dev/null
+++ b/include/vcl/filter/PngImageWriter.hxx
@@ -0,0 +1,50 @@
+/* -*- 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 <vcl/dllapi.h>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <tools/stream.hxx>
+#include <vcl/bitmapex.hxx>
+
+#pragma once
+
+namespace vcl
+{
+class VCL_DLLPUBLIC PngImageWriter
+{
+    SvStream& mrStream;
+    css::uno::Reference<css::task::XStatusIndicator> mxStatusIndicator;
+
+    int mnCompressionLevel;
+    bool mbInterlaced;
+
+public:
+    PngImageWriter(SvStream& rStream);
+
+    virtual ~PngImageWriter() {}
+
+    void setParameters(css::uno::Sequence<css::beans::PropertyValue> const& rParameters)
+    {
+        for (auto const& rValue : rParameters)
+        {
+            if (rValue.Name == "Compression")
+                rValue.Value >>= mnCompressionLevel;
+            else if (rValue.Name == "Interlaced")
+                rValue.Value >>= mbInterlaced;
+        }
+    }
+    bool write(BitmapEx& rBitmap);
+};
+
+} // namespace vcl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk
index 9bdfb3d1ee43..c00f531ef329 100644
--- a/vcl/Library_vcl.mk
+++ b/vcl/Library_vcl.mk
@@ -462,6 +462,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\
     vcl/source/filter/wmf/wmfexternal \
     vcl/source/filter/wmf/wmfwr \
     vcl/source/filter/png/PngImageReader \
+    vcl/source/filter/png/PngImageWriter \
     vcl/source/filter/png/pngwrite \
     vcl/source/font/Feature \
     vcl/source/font/FeatureCollector \
diff --git a/vcl/qa/cppunit/png/PngFilterTest.cxx b/vcl/qa/cppunit/png/PngFilterTest.cxx
index c167c4c9c636..1c5f29b40abe 100644
--- a/vcl/qa/cppunit/png/PngFilterTest.cxx
+++ b/vcl/qa/cppunit/png/PngFilterTest.cxx
@@ -24,14 +24,20 @@
 #include <test/bootstrapfixture.hxx>
 #include <tools/stream.hxx>
 #include <vcl/filter/PngImageReader.hxx>
+#include <vcl/filter/PngImageWriter.hxx>
 #include <vcl/BitmapReadAccess.hxx>
+#include <bitmap/BitmapWriteAccess.hxx>
 #include <vcl/alpha.hxx>
 #include <vcl/graphicfilter.hxx>
+#include <unotools/tempfile.hxx>
 
 using namespace css;
 
 class PngFilterTest : public test::BootstrapFixture
 {
+    // Should keep the temp files (should be false)
+    static constexpr bool bKeepTemp = true;
+
     OUString maDataUrl;
 
     OUString getFullUrl(std::u16string_view sFileName)
@@ -48,10 +54,16 @@ public:
 
     void testPng();
     void testMsGifInPng();
+    void testPngRoundtrip8BitGrey();
+    void testPngRoundtrip24();
+    void testPngRoundtrip32();
 
     CPPUNIT_TEST_SUITE(PngFilterTest);
     CPPUNIT_TEST(testPng);
     CPPUNIT_TEST(testMsGifInPng);
+    CPPUNIT_TEST(testPngRoundtrip8BitGrey);
+    CPPUNIT_TEST(testPngRoundtrip24);
+    CPPUNIT_TEST(testPngRoundtrip32);
     CPPUNIT_TEST_SUITE_END();
 };
 
@@ -245,6 +257,106 @@ void PngFilterTest::testMsGifInPng()
     CPPUNIT_ASSERT(aGraphic.IsAnimated());
 }
 
+void PngFilterTest::testPngRoundtrip8BitGrey()
+{
+    utl::TempFile aTempFile("testPngRoundtrip8BitGrey");
+    if (!bKeepTemp)
+        aTempFile.EnableKillingFile();
+    {
+        SvStream& rStream = *aTempFile.GetStream(StreamMode::WRITE);
+        Bitmap aBitmap(Size(16, 16), vcl::PixelFormat::N8_BPP, &Bitmap::GetGreyPalette(256));
+        {
+            BitmapScopedWriteAccess pWriteAccess(aBitmap);
+            pWriteAccess->Erase(COL_BLACK);
+            for (int i = 0; i < 8; ++i)
+            {
+                for (int j = 0; j < 8; ++j)
+                {
+                    pWriteAccess->SetPixel(i, j, COL_GRAY);
+                }
+            }
+            for (int i = 8; i < 16; ++i)
+            {
+                for (int j = 8; j < 16; ++j)
+                {
+                    pWriteAccess->SetPixel(i, j, COL_LIGHTGRAY);
+                }
+            }
+        }
+        BitmapEx aBitmapEx(aBitmap);
+
+        vcl::PngImageWriter aPngWriter(rStream);
+        CPPUNIT_ASSERT_EQUAL(true, aPngWriter.write(aBitmapEx));
+        aTempFile.CloseStream();
+    }
+    {
+        SvStream& rStream = *aTempFile.GetStream(StreamMode::READ);
+
+        vcl::PngImageReader aPngReader(rStream);
+        BitmapEx aBitmapEx;
+        CPPUNIT_ASSERT_EQUAL(true, aPngReader.read(aBitmapEx));
+
+        CPPUNIT_ASSERT_EQUAL(16L, aBitmapEx.GetSizePixel().Width());
+        CPPUNIT_ASSERT_EQUAL(16L, aBitmapEx.GetSizePixel().Height());
+
+        CPPUNIT_ASSERT_EQUAL(COL_GRAY, aBitmapEx.GetPixelColor(0, 0));
+        CPPUNIT_ASSERT_EQUAL(COL_LIGHTGRAY, aBitmapEx.GetPixelColor(15, 15));
+        CPPUNIT_ASSERT_EQUAL(COL_BLACK, aBitmapEx.GetPixelColor(15, 0));
+        CPPUNIT_ASSERT_EQUAL(COL_BLACK, aBitmapEx.GetPixelColor(0, 15));
+    }
+}
+
+void PngFilterTest::testPngRoundtrip24()
+{
+    utl::TempFile aTempFile("testPngRoundtrip24");
+    if (!bKeepTemp)
+        aTempFile.EnableKillingFile();
+    {
+        SvStream& rStream = *aTempFile.GetStream(StreamMode::WRITE);
+        Bitmap aBitmap(Size(16, 16), vcl::PixelFormat::N24_BPP);
+        {
+            BitmapScopedWriteAccess pWriteAccess(aBitmap);
+            pWriteAccess->Erase(COL_BLACK);
+            for (int i = 0; i < 8; ++i)
+            {
+                for (int j = 0; j < 8; ++j)
+                {
+                    pWriteAccess->SetPixel(i, j, COL_LIGHTRED);
+                }
+            }
+            for (int i = 8; i < 16; ++i)
+            {
+                for (int j = 8; j < 16; ++j)
+                {
+                    pWriteAccess->SetPixel(i, j, COL_LIGHTBLUE);
+                }
+            }
+        }
+        BitmapEx aBitmapEx(aBitmap);
+
+        vcl::PngImageWriter aPngWriter(rStream);
+        CPPUNIT_ASSERT_EQUAL(true, aPngWriter.write(aBitmapEx));
+    }
+    {
+        SvStream& rStream = *aTempFile.GetStream(StreamMode::READ);
+        rStream.Seek(0);
+
+        vcl::PngImageReader aPngReader(rStream);
+        BitmapEx aBitmapEx;
+        CPPUNIT_ASSERT_EQUAL(true, aPngReader.read(aBitmapEx));
+
+        CPPUNIT_ASSERT_EQUAL(16L, aBitmapEx.GetSizePixel().Width());
+        CPPUNIT_ASSERT_EQUAL(16L, aBitmapEx.GetSizePixel().Height());
+
+        CPPUNIT_ASSERT_EQUAL(COL_LIGHTRED, aBitmapEx.GetPixelColor(0, 0));
+        CPPUNIT_ASSERT_EQUAL(COL_LIGHTBLUE, aBitmapEx.GetPixelColor(15, 15));
+        CPPUNIT_ASSERT_EQUAL(COL_BLACK, aBitmapEx.GetPixelColor(15, 0));
+        CPPUNIT_ASSERT_EQUAL(COL_BLACK, aBitmapEx.GetPixelColor(0, 15));
+    }
+}
+
+void PngFilterTest::testPngRoundtrip32() {}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(PngFilterTest);
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/vcl/source/filter/png/PngImageWriter.cxx b/vcl/source/filter/png/PngImageWriter.cxx
new file mode 100644
index 000000000000..c1e638e0aad0
--- /dev/null
+++ b/vcl/source/filter/png/PngImageWriter.cxx
@@ -0,0 +1,143 @@
+/* -*- 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 <vcl/filter/PngImageWriter.hxx>
+#include <png.h>
+#include <bitmap/BitmapWriteAccess.hxx>
+#include <vcl/bitmap.hxx>
+
+namespace vcl
+{
+static void lclWriteStream(png_structp pPng, png_bytep pData, png_size_t pDataSize)
+{
+    png_voidp pIO = png_get_io_ptr(pPng);
+
+    if (pIO == nullptr)
+        return;
+
+    SvStream* pStream = static_cast<SvStream*>(pIO);
+
+    sal_Size nBytesWritten = pStream->WriteBytes(pData, pDataSize);
+
+    if (nBytesWritten != pDataSize)
+        png_error(pPng, "Write Error");
+}
+
+bool pngWrite(SvStream& rStream, BitmapEx& rBitmapEx, int nCompressionLevel)
+{
+    png_structp pPng = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
+
+    if (!pPng)
+        return false;
+
+    png_infop pInfo = png_create_info_struct(pPng);
+    if (!pInfo)
+    {
+        png_destroy_write_struct(&pPng, nullptr);
+        return false;
+    }
+
+    Bitmap aBitmap;
+
+    if (setjmp(png_jmpbuf(pPng)))
+    {
+        png_destroy_read_struct(&pPng, &pInfo, nullptr);
+        return false;
+    }
+
+    // Set our custom stream writer
+    png_set_write_fn(pPng, &rStream, lclWriteStream, nullptr);
+
+    aBitmap = rBitmapEx.GetBitmap();
+
+    {
+        Bitmap::ScopedReadAccess pAccess(aBitmap);
+        Size aSize = rBitmapEx.GetSizePixel();
+
+        int bitDepth = -1;
+        int colorType = -1;
+
+        /* PNG_COLOR_TYPE_GRAY (1, 2, 4, 8, 16)
+           PNG_COLOR_TYPE_GRAY_ALPHA (8, 16)
+           PNG_COLOR_TYPE_PALETTE (bit depths 1, 2, 4, 8)
+           PNG_COLOR_TYPE_RGB (bit_depths 8, 16)
+           PNG_COLOR_TYPE_RGB_ALPHA (bit_depths 8, 16)
+           PNG_COLOR_MASK_PALETTE
+           PNG_COLOR_MASK_COLOR
+           PNG_COLOR_MASK_ALPHA
+        */
+        auto eScanlineFormat = pAccess->GetScanlineFormat();
+        if (eScanlineFormat == ScanlineFormat::N8BitPal && aBitmap.HasGreyPalette8Bit())
+        {
+            colorType = PNG_COLOR_TYPE_GRAY;
+            bitDepth = 8;
+        }
+        else if (eScanlineFormat == ScanlineFormat::N24BitTcBgr
+                 || eScanlineFormat == ScanlineFormat::N24BitTcRgb)
+        {
+            colorType = PNG_COLOR_TYPE_RGB;
+            bitDepth = 8;
+            if (eScanlineFormat == ScanlineFormat::N24BitTcBgr)
+                png_set_bgr(pPng);
+        }
+        else
+        {
+            return false;
+        }
+
+        png_set_compression_level(pPng, nCompressionLevel);
+
+        int interlaceType = PNG_INTERLACE_NONE;
+        int compressionType = PNG_COMPRESSION_TYPE_DEFAULT;
+        int filterMethod = PNG_FILTER_TYPE_DEFAULT;
+
+        png_set_IHDR(pPng, pInfo, aSize.Width(), aSize.Height(), bitDepth, colorType, interlaceType,
+                     compressionType, filterMethod);
+
+        png_write_info(pPng, pInfo);
+
+        int nNumberOfPasses = 1;
+
+        Scanline pSourcePointer;
+
+        tools::Long nHeight = pAccess->Height();
+
+        for (int nPass = 0; nPass < nNumberOfPasses; nPass++)
+        {
+            for (tools::Long y = 0; y <= nHeight; y++)
+            {
+                pSourcePointer = pAccess->GetScanline(y);
+                png_write_rows(pPng, &pSourcePointer, 1);
+            }
+        }
+    }
+
+    png_write_end(pPng, pInfo);
+
+    png_destroy_write_struct(&pPng, &pInfo);
+
+    return true;
+}
+
+PngImageWriter::PngImageWriter(SvStream& rStream)
+    : mrStream(rStream)
+    , mnCompressionLevel(6)
+    , mbInterlaced(false)
+{
+}
+
+bool PngImageWriter::write(BitmapEx& rBitmapEx)
+{
+    return pngWrite(mrStream, rBitmapEx, mnCompressionLevel);
+}
+
+} // namespace vcl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
commit a69f139dd2e390d9f68537a29a91dcf272276831
Author:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Thu Nov 12 10:01:20 2020 +0100
Commit:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
CommitDate: Mon Aug 2 23:17:45 2021 +0900

    basegfx: added LengthUnit class as the base unit for length
    
    Change-Id: I1d4790b60dd784e8b2e2e438274f3ebd6db4b60c

diff --git a/basegfx/CppunitTest_basegfx.mk b/basegfx/CppunitTest_basegfx.mk
index 310424bd6446..680bbcffe25b 100644
--- a/basegfx/CppunitTest_basegfx.mk
+++ b/basegfx/CppunitTest_basegfx.mk
@@ -43,6 +43,7 @@ $(eval $(call gb_CppunitTest_add_exception_objects,basegfx,\
     basegfx/test/basegfxtools \
     basegfx/test/clipstate \
     basegfx/test/genericclipper \
+    basegfx/test/LengthUnitTest \
 ))
 
 # vim: set noet sw=4 ts=4:
diff --git a/basegfx/test/LengthUnitTest.cxx b/basegfx/test/LengthUnitTest.cxx
new file mode 100644
index 000000000000..0b80c33da5b0
--- /dev/null
+++ b/basegfx/test/LengthUnitTest.cxx
@@ -0,0 +1,96 @@
+/* -*- 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 <basegfx/units/LengthUnit.hxx>
+
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+
+class LengthUnitTest : public CppUnit::TestFixture
+{
+public:
+    void test();
+
+    CPPUNIT_TEST_SUITE(LengthUnitTest);
+    CPPUNIT_TEST(test);
+    CPPUNIT_TEST_SUITE_END();
+};
+
+void LengthUnitTest::test()
+{
+    gfx::LengthUnit cm = 1_cm + 5_cm - 2_cm;
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(4.0, cm.as_cm(), 1e-4);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(0.04, cm.as_m(), 1e-4);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(40.0, cm.as_mm(), 1e-4);
+    CPPUNIT_ASSERT_EQUAL(sal_Int64(1440000), cm.raw());
+
+    gfx::LengthUnit cm2 = 5_cm * 2;
+    CPPUNIT_ASSERT_EQUAL(sal_Int64(3600000), cm2.raw());
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(10.0, cm2.as_cm(), 1e-4);
+
+    // 1 km - 50 m = 950 m = 95000 cm
+    gfx::LengthUnit cm3 = 100000_cm - 5000_cm;
+    CPPUNIT_ASSERT_EQUAL(sal_Int64(34200000000), cm3.raw());
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(95000.0, cm3.as_cm(), 1e-4);
+
+    gfx::LengthUnit cm4(1_cm);
+    cm4 /= 2;
+    CPPUNIT_ASSERT_EQUAL(sal_Int64(180000), cm4.raw());
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(0.5, cm4.as_cm(), 1e-4);
+
+    // (635 * 20) + 3 * (635 * 15) = 41275EMU
+    gfx::LengthUnit pt = 1_pt + 3_px;
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(3.25, pt.as_pt(), 1e-4);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(65.0, pt.as_twip(), 1e-4);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0451, pt.as_in(), 1e-4);
+    CPPUNIT_ASSERT_EQUAL(sal_Int64(41275), pt.raw());
+
+    gfx::LengthUnit inch = 1_in; // 1440 * 635
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(1440.0, inch.as_twip(), 1e-4);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(96.0, inch.as_px(), 1e-4);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, inch.as_in(), 1e-4);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(914400.0, inch.as_emu(), 1e-4);
+    CPPUNIT_ASSERT_EQUAL(sal_Int64(914400), inch.raw());
+
+    // Conversion
+    sal_Int64 asNumber(17_pt);
+    asNumber += sal_Int64(1_pt);
+    gfx::LengthUnit asLength = gfx::LengthUnit::emu(asNumber);
+    CPPUNIT_ASSERT_EQUAL(sal_Int64(18 * 635 * 20), asLength.raw());
+
+    gfx::LengthUnit maximum = gfx::LengthUnit::emu(SAL_MAX_INT64);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(256204778801.5, maximum.as_m(), 1e-1);
+    // 256204778 km
+    CPPUNIT_ASSERT_EQUAL(sal_Int64(SAL_MAX_INT64), maximum.raw());
+
+    gfx::LengthUnit minimum = gfx::LengthUnit::emu(SAL_MIN_INT64);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(-256204778801.5, minimum.as_m(), 1e-1);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(double(SAL_MIN_INT64), minimum.as_emu(), 1e-1);
+    CPPUNIT_ASSERT_EQUAL(sal_Int64(SAL_MIN_INT64), minimum.raw());
+
+    // 27 emu + 33 emu + 360 emu = 420
+    gfx::LengthUnit emus = 27_emu + 33_emu + 1_hmm;
+    CPPUNIT_ASSERT_EQUAL(sal_Int64(420), emus.raw());
+
+    //  Creation from number
+    int number = 10;
+    auto asCm = gfx::LengthUnit::cm(number);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(10.0, asCm.as_cm(), 1e-4);
+    CPPUNIT_ASSERT_EQUAL(sal_Int64(3600000), asCm.raw());
+
+    auto asMm = gfx::LengthUnit::mm(number);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(10.0, asMm.as_mm(), 1e-4);
+    CPPUNIT_ASSERT_EQUAL(sal_Int64(360000), asMm.raw());
+
+    auto asInch = gfx::LengthUnit::in(number);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(10.0, asInch.as_in(), 1e-4);
+    CPPUNIT_ASSERT_EQUAL(sal_Int64(9144000), asInch.raw());
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(LengthUnitTest);
diff --git a/include/basegfx/units/LengthUnit.hxx b/include/basegfx/units/LengthUnit.hxx
new file mode 100644
index 000000000000..4edd1cc4704f
--- /dev/null
+++ b/include/basegfx/units/LengthUnit.hxx
@@ -0,0 +1,194 @@
+/* -*- 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/types.h>
+
+namespace gfx
+{
+namespace
+{
+constexpr sal_Int64 constFactor_hmm_to_EMU = 360ll;
+constexpr sal_Int64 constFactor_mm_to_EMU = constFactor_hmm_to_EMU * 100ll;
+constexpr sal_Int64 constFactor_cm_to_EMU = constFactor_hmm_to_EMU * 1000ll;
+constexpr sal_Int64 constFactor_m_to_EMU = constFactor_hmm_to_EMU * 100000ll;
+
+constexpr sal_Int64 constFactor_twip_to_EMU = 635ll;
+constexpr sal_Int64 constFactor_in_to_EMU = constFactor_twip_to_EMU * 1440ll;
+constexpr sal_Int64 constFactor_pt_to_EMU = constFactor_twip_to_EMU * 20ll;
+constexpr sal_Int64 constFactor_px_to_EMU = constFactor_twip_to_EMU * 15ll;
+
+} // end anonymous namespace
+
+template <typename T> class LengthUnitBase
+{
+private:
+    // value in EMU units
+    T m_nValue;
+
+    constexpr explicit LengthUnitBase(T nValue)
+        : m_nValue(nValue)
+    {
+    }
+
+public:
+    static constexpr LengthUnitBase cm(T nValue)
+    {
+        return LengthUnitBase(gfx::constFactor_cm_to_EMU * nValue);
+    }
+
+    static constexpr LengthUnitBase mm(T nValue)
+    {
+        return LengthUnitBase(gfx::constFactor_mm_to_EMU * nValue);
+    }
+
+    static constexpr LengthUnitBase hmm(T nValue)
+    {
+        return LengthUnitBase(gfx::constFactor_hmm_to_EMU * nValue);
+    }
+
+    static constexpr LengthUnitBase in(T nValue)
+    {
+        return LengthUnitBase(gfx::constFactor_in_to_EMU * nValue);
+    }
+
+    static constexpr LengthUnitBase twip(T nValue)
+    {
+        return LengthUnitBase(gfx::constFactor_twip_to_EMU * nValue);
+    }
+
+    static constexpr LengthUnitBase pt(T nValue)
+    {
+        return LengthUnitBase(gfx::constFactor_pt_to_EMU * nValue);
+    }
+
+    static constexpr LengthUnitBase px(T nValue)
+    {
+        return LengthUnitBase(gfx::constFactor_px_to_EMU * nValue);
+    }
+

... etc. - the rest is truncated


More information about the Libreoffice-commits mailing list