editeng/source/editeng/impedit.hxx | 9 - editeng/source/editeng/impedit2.cxx | 4 editeng/source/editeng/impedit3.cxx | 20 +-- include/svx/compatflags.hxx | 3 sd/qa/unit/data/odp/trailing-paragraphs-compat.odp |binary sd/qa/unit/data/odp/trailing-paragraphs.odp |binary sd/qa/unit/data/pptx/trailing-paragraphs.pptx |binary sd/qa/unit/layout-tests.cxx | 126 ++++++++++++++++++++- sd/source/ui/docshell/docshel4.cxx | 4 svx/source/svdraw/svdmodel.cxx | 9 + svx/source/svdraw/svdotextdecomposition.cxx | 12 +- 11 files changed, 167 insertions(+), 20 deletions(-)
New commits: commit 055008aca0cfd9de44b8dd96b32e55ddaf13bfe5 Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Tue Aug 19 23:33:02 2025 +0500 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Thu Aug 21 11:50:54 2025 +0200 tdf#168010: introduce UseTrailingEmptyLinesInLayout compat option In PowerPoint, autoshrink text size is calculated the same way as in Impress, ignoring the empty lines block in the end. But when the text is positioned, these lines are taken into account, which may change position of texts aligned to bottom. Since this method of positioning text may change text placement very seriously, it can't be used unconditionally, and requires a compat flag, which is introduced here, only enabled for PPTX import, and in ODP documents having the option explicitly set. It is not used for other MS Office document types. I saw that Excel behaves similarly to PowerPoint, so maybe it makes sense to enable it for XLSX, too; on the other hand, MS Word works differently. Also I couldn't prepare a test document in binary PPT to test hehavior. The decisions about these file types should go in separate changes. Commit f61ea135430d7b4a1fac7de1e57a1314fbb1b49e (editeng: use text scaling that better mimics MSO text scaling, 2024-04-03) introduced ImpEditEngine::FormatParagraphs, that calculated paragraphs' heights, and returned the height of the part before trailing empty paragraphs block. It needed that height to make decisions about text scaling; and the implementation simply assigned zero height to the trailing empty paragraphs, therefore adding their heights didn't count. Now we need the heights of the trailing paragraphs in the new compat mode. This change modifies implementation of FormatParagraphs and CalcHeight: the latter now calculates paragraph heights regardless of the paragraph content; and the former checks if current paragraph height should count in the returned result. This keeps the scaling algorithm, and at the same time, provides the required height data. We could possibly make the height calculation depend on the compat flag; but this would make the code more complex, without a benefit. The actual difference that the trailing empty paragraphs do is in SdrTextObj::impDecomposeAutoFitTextPrimitive. Change-Id: Ie37f1d2b3393f9c52be89586c73df70b108190a1 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/189935 Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> Tested-by: Jenkins (cherry picked from commit 425b55efaf1f04e8f91cb049a5820fcf61e678dd) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/189945 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Miklos Vajna <vmik...@collabora.com> diff --git a/editeng/source/editeng/impedit.hxx b/editeng/source/editeng/impedit.hxx index 12428d1e054f..4bdcdaa6eefd 100644 --- a/editeng/source/editeng/impedit.hxx +++ b/editeng/source/editeng/impedit.hxx @@ -651,7 +651,7 @@ private: void ParaAttribsChanged( ContentNode const * pNode, bool bIgnoreUndoCheck = false ); void TextModified(); - void CalcHeight(ParaPortion& rParaPortion, bool bIsScaling = false); + void CalcHeight(ParaPortion& rParaPortion); bool isInEmptyClusterAtTheEnd(ParaPortion& rParaPortion, bool bIsScaling); void InsertUndo( std::unique_ptr<EditUndo> pUndo, bool bTryMerge = false ); @@ -683,13 +683,13 @@ private: void Clear(); EditPaM RemoveText(); - bool createLinesForEmptyParagraph(ParaPortion& rParaPortion, bool bIsScaling = false); + bool createLinesForEmptyParagraph(ParaPortion& rParaPortion); tools::Long calculateMaxLineWidth(tools::Long nStartX, SvxLRSpaceItem const& rLRItem, const SvxFontUnitMetrics& rMetrics); - bool CreateLines(sal_Int32 nPara, sal_uInt32 nStartPosY, bool bIsScaling = false); + bool CreateLines(sal_Int32 nPara, sal_uInt32 nStartPosY); void CreateAndInsertEmptyLine(ParaPortion& rParaPortion); - bool FinishCreateLines(ParaPortion& rParaPortion, bool bIsScaling = false); + bool FinishCreateLines(ParaPortion& rParaPortion); void CreateTextPortions(ParaPortion& rParaPortion, sal_Int32& rStartPos); void RecalcTextPortion(ParaPortion& rParaPortion, sal_Int32 nStartPos, sal_Int32 nNewChars); sal_Int32 SplitTextPortion(ParaPortion& rParaPortion, sal_Int32 nPos, EditLine* pCurLine = nullptr); @@ -980,6 +980,7 @@ public: void SetMinColumnWrapHeight(tools::Long nVal) { mnMinColumnWrapHeight = nVal; } + // Returns the height of the text, excluding empty lines in the end tools::Long FormatParagraphs(o3tl::sorted_vector<sal_Int32>& rRepaintParagraphs, bool bIsScaling); void ScaleContentToFitWindow(o3tl::sorted_vector<sal_Int32>& rRepaintParagraphs); void FormatDoc(); diff --git a/editeng/source/editeng/impedit2.cxx b/editeng/source/editeng/impedit2.cxx index 5b8abb8f2461..642f84946250 100644 --- a/editeng/source/editeng/impedit2.cxx +++ b/editeng/source/editeng/impedit2.cxx @@ -4431,12 +4431,12 @@ bool ImpEditEngine::isInEmptyClusterAtTheEnd(ParaPortion& rPortion, bool bIsScal return false; } -void ImpEditEngine::CalcHeight(ParaPortion& rPortion, bool bIsScaling) +void ImpEditEngine::CalcHeight(ParaPortion& rPortion) { rPortion.mnHeight = 0; rPortion.mnFirstLineOffset = 0; - if (!rPortion.IsVisible() || isInEmptyClusterAtTheEnd(rPortion, bIsScaling)) + if (!rPortion.IsVisible()) return; OSL_ENSURE(rPortion.GetLines().Count(), "Paragraph with no lines in ParaPortion::CalcHeight"); diff --git a/editeng/source/editeng/impedit3.cxx b/editeng/source/editeng/impedit3.cxx index 7f5cf45bffb2..85a21a39c20b 100644 --- a/editeng/source/editeng/impedit3.cxx +++ b/editeng/source/editeng/impedit3.cxx @@ -321,7 +321,7 @@ void ImpEditEngine::FormatFullDoc() tools::Long ImpEditEngine::FormatParagraphs(o3tl::sorted_vector<sal_Int32>& aRepaintParagraphList, bool bIsScaling) { sal_Int32 nParaCount = GetParaPortions().Count(); - tools::Long nY = 0; + tools::Long nY = 0, nResult = 0; bool bGrow = false; for (sal_Int32 nParagraph = 0; nParagraph < nParaCount; nParagraph++) @@ -330,7 +330,7 @@ tools::Long ImpEditEngine::FormatParagraphs(o3tl::sorted_vector<sal_Int32>& aRep if (rParaPortion.MustRepaint() || (rParaPortion.IsInvalid() && rParaPortion.IsVisible())) { // No formatting should be necessary for MustRepaint()! - if (CreateLines(nParagraph, nY, bIsScaling)) + if (CreateLines(nParagraph, nY)) { if (!bGrow && GetTextRanger()) { @@ -359,8 +359,10 @@ tools::Long ImpEditEngine::FormatParagraphs(o3tl::sorted_vector<sal_Int32>& aRep aRepaintParagraphList.insert(nParagraph); } nY += rParaPortion.GetHeight(); + if (!isInEmptyClusterAtTheEnd(rParaPortion, bIsScaling)) + nResult = nY; // The total height excluding trailing blank paragraphs } - return nY; + return nResult; } namespace @@ -593,7 +595,7 @@ tools::Long ImpEditEngine::GetColumnWidth(const Size& rPaperSize) const return (nWidth - mnColumnSpacing * (mnColumns - 1)) / mnColumns; } -bool ImpEditEngine::createLinesForEmptyParagraph(ParaPortion& rParaPortion, bool bIsScaling) +bool ImpEditEngine::createLinesForEmptyParagraph(ParaPortion& rParaPortion) { // fast special treatment... if (rParaPortion.GetTextPortions().Count()) @@ -602,7 +604,7 @@ bool ImpEditEngine::createLinesForEmptyParagraph(ParaPortion& rParaPortion, bool rParaPortion.GetLines().Reset(); CreateAndInsertEmptyLine(rParaPortion); - return FinishCreateLines(rParaPortion, bIsScaling); + return FinishCreateLines(rParaPortion); } tools::Long ImpEditEngine::calculateMaxLineWidth(tools::Long nStartX, SvxLRSpaceItem const& rLRItem, @@ -627,7 +629,7 @@ tools::Long ImpEditEngine::calculateMaxLineWidth(tools::Long nStartX, SvxLRSpace return nMaxLineWidth; } -bool ImpEditEngine::CreateLines( sal_Int32 nPara, sal_uInt32 nStartPosY, bool bIsScaling ) +bool ImpEditEngine::CreateLines( sal_Int32 nPara, sal_uInt32 nStartPosY ) { assert(GetParaPortions().exists(nPara) && "Portion paragraph index is not valid"); ParaPortion& rParaPortion = GetParaPortions().getRef(nPara); @@ -644,7 +646,7 @@ bool ImpEditEngine::CreateLines( sal_Int32 nPara, sal_uInt32 nStartPosY, bool bI // Fast special treatment for empty paragraphs... bool bEmptyParagraph = rParaPortion.GetNode()->Len() == 0 && !GetTextRanger(); if (bEmptyParagraph) - return createLinesForEmptyParagraph(rParaPortion, bIsScaling); + return createLinesForEmptyParagraph(rParaPortion); sal_Int64 nCurrentPosY = nStartPosY; // If we're allowed to skip parts outside and this cannot possibly fit in the given height, @@ -1936,12 +1938,12 @@ void ImpEditEngine::CreateAndInsertEmptyLine(ParaPortion& rParaPortion) } } -bool ImpEditEngine::FinishCreateLines(ParaPortion& rParaPortion, bool bIsScaling) +bool ImpEditEngine::FinishCreateLines(ParaPortion& rParaPortion) { // CalcCharPositions( pParaPortion ); rParaPortion.SetValid(); tools::Long nOldHeight = rParaPortion.GetHeight(); - CalcHeight(rParaPortion, bIsScaling); + CalcHeight(rParaPortion); DBG_ASSERT(rParaPortion.GetTextPortions().Count(), "FinishCreateLines: No Text-Portion?"); bool bRet = rParaPortion.GetHeight() != nOldHeight; diff --git a/include/svx/compatflags.hxx b/include/svx/compatflags.hxx index 4caecf18be55..75b85f79607f 100644 --- a/include/svx/compatflags.hxx +++ b/include/svx/compatflags.hxx @@ -14,7 +14,8 @@ enum class SdrCompatibilityFlag LegacyFontwork, ///< for tdf#148000 false == Fontwork works in PowerPoint compat mode ConnectorUseSnapRect, ///< for tdf#149756 IgnoreBreakAfterMultilineField, ///< for tdf#148966 - LAST = IgnoreBreakAfterMultilineField /// add new items above + UseTrailingEmptyLinesInLayout, ///< for tdf#168010 + LAST = UseTrailingEmptyLinesInLayout /// add new items above }; /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/sd/qa/unit/data/odp/trailing-paragraphs-compat.odp b/sd/qa/unit/data/odp/trailing-paragraphs-compat.odp new file mode 100644 index 000000000000..b7f5798e00d4 Binary files /dev/null and b/sd/qa/unit/data/odp/trailing-paragraphs-compat.odp differ diff --git a/sd/qa/unit/data/odp/trailing-paragraphs.odp b/sd/qa/unit/data/odp/trailing-paragraphs.odp new file mode 100644 index 000000000000..ea87cc8f6001 Binary files /dev/null and b/sd/qa/unit/data/odp/trailing-paragraphs.odp differ diff --git a/sd/qa/unit/data/pptx/trailing-paragraphs.pptx b/sd/qa/unit/data/pptx/trailing-paragraphs.pptx new file mode 100644 index 000000000000..fa8907e543bf Binary files /dev/null and b/sd/qa/unit/data/pptx/trailing-paragraphs.pptx differ diff --git a/sd/qa/unit/layout-tests.cxx b/sd/qa/unit/layout-tests.cxx index 6fae40d135c7..9a593b611da0 100644 --- a/sd/qa/unit/layout-tests.cxx +++ b/sd/qa/unit/layout-tests.cxx @@ -10,6 +10,10 @@ #include <sfx2/objsh.hxx> #include <sfx2/sfxbasemodel.hxx> +#include <svx/compatflags.hxx> + +#include <drawdoc.hxx> +#include <unomodel.hxx> class SdLayoutTest : public UnoApiXmlTest { @@ -19,9 +23,8 @@ public: { } - xmlDocUniquePtr load(const char* pName) + xmlDocUniquePtr parseLayout() const { - loadFromFile(OUString::createFromAscii(pName)); SfxBaseModel* pModel = dynamic_cast<SfxBaseModel*>(mxComponent.get()); CPPUNIT_ASSERT(pModel); SfxObjectShell* pShell = pModel->GetObjectShell(); @@ -33,6 +36,21 @@ public: return pXmlDoc; } + + xmlDocUniquePtr load(const char* pName) + { + loadFromFile(OUString::createFromAscii(pName)); + return parseLayout(); + } + + SdDrawDocument* getDoc() + { + auto* pImpress = dynamic_cast<SdXImpressDocument*>(mxComponent.get()); + CPPUNIT_ASSERT(pImpress); + auto* pDoc = pImpress->GetDoc(); + CPPUNIT_ASSERT(pDoc); + return pDoc; + } }; CPPUNIT_TEST_FIXTURE(SdLayoutTest, testTdf104722) @@ -458,6 +476,110 @@ CPPUNIT_TEST_FIXTURE(SdLayoutTest, testTdf164622) "y", u"892"); } +CPPUNIT_TEST_FIXTURE(SdLayoutTest, testTdf168010) +{ + // Test UseTrailingEmptyLinesInLayout compatibility option. + // The test documents have an auto-shrink text "textbox "; the box itself is positioned + // identically in all cases; the text is aligned to bottom. + // When "UseTrailingEmptyLinesInLayout" is set, "textbox" string is placed higher, than when + // the setting is not set (y value ~6700 vs. ~8100). + + // The existing ODPs have a standard draw:text-box. It produces three textarray elements, + // in order from bottom to top. We need the topmost, third. + + // 1. UseTrailingEmptyLinesInLayout must be enabled in an existing ODP with respective option + // in settings.xml + loadFromFile(u"odp/trailing-paragraphs-compat.odp"); + { + CPPUNIT_ASSERT( + getDoc()->GetCompatibilityFlag(SdrCompatibilityFlag::UseTrailingEmptyLinesInLayout)); + + xmlDocUniquePtr pXml = parseLayout(); + sal_Int32 y = getXPath(pXml, "/metafile['1']/push/push/textarray[3]", "y").toInt32(); + CPPUNIT_ASSERT_DOUBLES_EQUAL(6700, y, 100); // could be 6641 or 6760 + assertXPathContent(pXml, "/metafile['1']/push/push/textarray[3]/text", u"textbox"); + } + + // 2. It must stay enabled after ODP round-trip + saveAndReload(u"impress8"_ustr); + { + CPPUNIT_ASSERT( + getDoc()->GetCompatibilityFlag(SdrCompatibilityFlag::UseTrailingEmptyLinesInLayout)); + + xmlDocUniquePtr pXml = parseLayout(); + sal_Int32 y = getXPath(pXml, "/metafile['2']/push/push/textarray[3]", "y").toInt32(); + CPPUNIT_ASSERT_DOUBLES_EQUAL(6700, y, 100); + assertXPathContent(pXml, "/metafile['2']/push/push/textarray[3]/text", u"textbox"); + } + + // 3. It must be disabled in an existing ODP without that option in settings.xml + loadFromFile(u"odp/trailing-paragraphs.odp"); + { + CPPUNIT_ASSERT( + !getDoc()->GetCompatibilityFlag(SdrCompatibilityFlag::UseTrailingEmptyLinesInLayout)); + + xmlDocUniquePtr pXml = parseLayout(); + sal_Int32 y = getXPath(pXml, "/metafile['3']/push/push/textarray[3]", "y").toInt32(); + CPPUNIT_ASSERT_DOUBLES_EQUAL(8100, y, 100); + assertXPathContent(pXml, "/metafile['3']/push/push/textarray[3]/text", u"textbox"); + } + + // 4. It must stay disabled after ODP round-trip + saveAndReload(u"impress8"_ustr); + { + CPPUNIT_ASSERT( + !getDoc()->GetCompatibilityFlag(SdrCompatibilityFlag::UseTrailingEmptyLinesInLayout)); + + xmlDocUniquePtr pXml = parseLayout(); + sal_Int32 y = getXPath(pXml, "/metafile['4']/push/push/textarray[3]", "y").toInt32(); + CPPUNIT_ASSERT_DOUBLES_EQUAL(8100, y, 100); + assertXPathContent(pXml, "/metafile['4']/push/push/textarray[3]/text", u"textbox"); + } + + // Now test PPTX and its round-trip. The text there imports as draw:custom-shape; it generates + // a single textarray element. + + // 5. It must be enabled for PPTX documents unconditionally + loadFromFile(u"pptx/trailing-paragraphs.pptx"); + { + CPPUNIT_ASSERT( + getDoc()->GetCompatibilityFlag(SdrCompatibilityFlag::UseTrailingEmptyLinesInLayout)); + + xmlDocUniquePtr pXml = parseLayout(); + sal_Int32 y = getXPath(pXml, "/metafile['5']/push/push/textarray", "y").toInt32(); + CPPUNIT_ASSERT_DOUBLES_EQUAL(6700, y, 100); + assertXPathContent(pXml, "/metafile['5']/push/push/textarray/text", u"textbox"); + } + + // 6. Check PPTX round-trip + saveAndReload(u"Impress Office Open XML"_ustr); + { + CPPUNIT_ASSERT( + getDoc()->GetCompatibilityFlag(SdrCompatibilityFlag::UseTrailingEmptyLinesInLayout)); + + xmlDocUniquePtr pXml = parseLayout(); + sal_Int32 y = getXPath(pXml, "/metafile['6']/push/push/textarray", "y").toInt32(); + CPPUNIT_ASSERT_DOUBLES_EQUAL(6700, y, 100); + assertXPathContent(pXml, "/metafile['6']/push/push/textarray/text", u"textbox"); + } + + // For some reason, saving PPTX to ODT in step 7 below produces negative fo:padding-top, which + // fails validation; that is unrelated, so disable validation for now. + skipValidation(); + + // 7. It must round-trip to ODP + saveAndReload(u"impress8"_ustr); + { + CPPUNIT_ASSERT( + getDoc()->GetCompatibilityFlag(SdrCompatibilityFlag::UseTrailingEmptyLinesInLayout)); + + xmlDocUniquePtr pXml = parseLayout(); + sal_Int32 y = getXPath(pXml, "/metafile['7']/push/push/textarray", "y").toInt32(); + CPPUNIT_ASSERT_DOUBLES_EQUAL(6700, y, 100); + assertXPathContent(pXml, "/metafile['7']/push/push/textarray/text", u"textbox"); + } +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/docshell/docshel4.cxx b/sd/source/ui/docshell/docshel4.cxx index dca885bcd2cd..223b5921cc4c 100644 --- a/sd/source/ui/docshell/docshel4.cxx +++ b/sd/source/ui/docshell/docshel4.cxx @@ -430,6 +430,10 @@ bool DrawDocShell::ImportFrom(SfxMedium &rMedium, // compatibility flag for tdf#148966 mpDoc->SetCompatibilityFlag(SdrCompatibilityFlag::IgnoreBreakAfterMultilineField, true); + + // tdf#168010: PowerPoint ignores empty trailing lines in autoshrink text when scaling font + // (same as Impress), but takes into account in layout: + mpDoc->SetCompatibilityFlag(SdrCompatibilityFlag::UseTrailingEmptyLinesInLayout, true); } if (aFilterName == "Impress MS PowerPoint 2007 XML" || diff --git a/svx/source/svdraw/svdmodel.cxx b/svx/source/svdraw/svdmodel.cxx index d75ea61595f7..e3da9decea16 100644 --- a/svx/source/svdraw/svdmodel.cxx +++ b/svx/source/svdraw/svdmodel.cxx @@ -98,6 +98,7 @@ struct SdrModelImpl false, // tdf#148000 LegacyFontwork false, // tdf#149756 ConnectorUseSnapRect false, // tdf#148966 IgnoreBreakAfterMultilineField + false, // tdf#168010 UseTrailingEmptyLinesInLayout } , mpTheme(new model::Theme(u"Office"_ustr)) {} @@ -1836,6 +1837,11 @@ void SdrModel::ReadUserDataSequenceValue(const beans::PropertyValue* pValue) SetCompatibilityFlag(SdrCompatibilityFlag::IgnoreBreakAfterMultilineField, bBool); } } + else if (pValue->Name == "UseTrailingEmptyLinesInLayout") + { + if (bool bBool; pValue->Value >>= bBool) + SetCompatibilityFlag(SdrCompatibilityFlag::UseTrailingEmptyLinesInLayout, bBool); + } } void SdrModel::WriteUserDataSequence(uno::Sequence <beans::PropertyValue>& rValues) @@ -1845,7 +1851,8 @@ void SdrModel::WriteUserDataSequence(uno::Sequence <beans::PropertyValue>& rValu { "AnchoredTextOverflowLegacy", uno::Any(GetCompatibilityFlag(SdrCompatibilityFlag::AnchoredTextOverflowLegacy)) }, { "LegacySingleLineFontwork", uno::Any(GetCompatibilityFlag(SdrCompatibilityFlag::LegacyFontwork)) }, { "ConnectorUseSnapRect", uno::Any(GetCompatibilityFlag(SdrCompatibilityFlag::ConnectorUseSnapRect)) }, - { "IgnoreBreakAfterMultilineField", uno::Any(GetCompatibilityFlag(SdrCompatibilityFlag::IgnoreBreakAfterMultilineField)) } + { "IgnoreBreakAfterMultilineField", uno::Any(GetCompatibilityFlag(SdrCompatibilityFlag::IgnoreBreakAfterMultilineField)) }, + { "UseTrailingEmptyLinesInLayout", uno::Any(GetCompatibilityFlag(SdrCompatibilityFlag::UseTrailingEmptyLinesInLayout)) }, }; const sal_Int32 nOldLength = rValues.getLength(); diff --git a/svx/source/svdraw/svdotextdecomposition.cxx b/svx/source/svdraw/svdotextdecomposition.cxx index af7b3e1e4831..d5870e4cf7f6 100644 --- a/svx/source/svdraw/svdotextdecomposition.cxx +++ b/svx/source/svdraw/svdotextdecomposition.cxx @@ -1001,7 +1001,17 @@ void SdrTextObj::impDecomposeAutoFitTextPrimitive( rOutliner.SetFixedCellHeight(rSdrAutofitTextPrimitive.isFixedCellHeight()); // now get back the layouted text size from outliner - const Size aOutlinerTextSize(rOutliner.GetPaperSize()); + Size aOutlinerTextSize(rOutliner.GetPaperSize()); + if (getSdrModelFromSdrObject().GetCompatibilityFlag( + SdrCompatibilityFlag::UseTrailingEmptyLinesInLayout)) + { + // The height of the text may be larger than the box height, because of the trailing + // empty paragraphs, ignored when scaling, and normally ignored for layout. PowerPoint + // has a different handling: it also ignores the lines when scaling, but uses them for + // positioning of the text. + if (tools::Long h = rOutliner.GetTextHeight(); h > aOutlinerTextSize.Height()) + aOutlinerTextSize.setHeight(h); + } const basegfx::B2DVector aOutlinerScale(aOutlinerTextSize.Width(), aOutlinerTextSize.Height()); basegfx::B2DVector aAdjustTranslate(0.0, 0.0);