include/svx/compatflags.hxx                             |    2 
 include/tools/poly.hxx                                  |    1 
 oox/qa/unit/shape.cxx                                   |    4 
 sc/source/ui/docshell/docsh.cxx                         |    2 
 sd/qa/unit/export-tests-ooxml3.cxx                      |    2 
 sd/source/ui/docshell/docshel4.cxx                      |    2 
 svx/qa/unit/data/tdf148000_CurvedTextWidth.pptx         |binary
 svx/qa/unit/data/tdf148000_CurvedTextWidth_Legacy.odp   |binary
 svx/qa/unit/data/tdf148000_CurvedTextWidth_New.odp      |binary
 svx/qa/unit/svdraw.cxx                                  |   39 ++
 svx/source/customshapes/EnhancedCustomShapeFontWork.cxx |  275 +++++++++++++---
 svx/source/svdraw/svdmodel.cxx                          |   18 -
 sw/source/uibase/app/docshini.cxx                       |    2 
 13 files changed, 296 insertions(+), 51 deletions(-)

New commits:
commit 3d7dad88c409fecd3ba4a3f27f8e2e6e2e5f14d7
Author:     Attila Szűcs <attila.sz...@collabora.com>
AuthorDate: Wed Nov 15 07:04:32 2023 +0100
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Sun Nov 26 15:07:35 2023 +0100

    tdf#148000 impress: improve fontwork text placement.
    
    Improved the calculation of positions of text characters for multi-line 
texts.
    
    The previous version only fitted the text to the basic outline (curve), and 
then scale them to the appropriate text line.
    This means that the text will be wider or shorter, depending on the shape 
of the curve, and which line it is on
    
    Now it calculates a curve for each paragraph and fits text on it.
    Text will be approximately the same width on each line.
    Except if the text is wider as the curve. Because then it shrinks the text 
to fit on the curve. (this can only happens on inner curves)
    
    Reused the same compat flag that was used in bug148000, now it serves
    as a Powerpoint compatible mode for FontWork, so no need to create new
    compat flag every time FontWork has improve.
    
    That means that the Fontwork in old documents has remains the same
    
    Refactored horizontal/vertical alignment, but had to keep the old hacks
    as well.
    
    Note: if there are too many lines of text, and the vertical alignment 
causes internal curves, then curves can shrink to 0 length (center point of a 
circle) or even to negative length,
    These cases are impossible to display normally, so it will be glitchy
    similar to how it was before this patch.
    MS PowerPoint avoid these cases by not allowing vertical alignments that
    would result internal (smaller) curves.
    
    Added unittest to check legacy-odb / new-odp / pptx file.
    
    It change the display of fontwork, so in some cases it may feel like
    a regression.
    
    Squashed a lot of typo fix commits by Andrea Gelmini.
    
    Change-Id: Iac2d9bc751bbc2b6f747c33958f969cb3543fae5
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/159776
    Tested-by: Caolán McNamara <caolan.mcnam...@collabora.com>
    Reviewed-by: Caolán McNamara <caolan.mcnam...@collabora.com>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/159975
    Tested-by: Jenkins
    Reviewed-by: Andras Timar <andras.ti...@collabora.com>

diff --git a/include/svx/compatflags.hxx b/include/svx/compatflags.hxx
index f7d021f17bf7..9a64bd2bab80 100644
--- a/include/svx/compatflags.hxx
+++ b/include/svx/compatflags.hxx
@@ -11,7 +11,7 @@
 enum class SdrCompatibilityFlag
 {
     AnchoredTextOverflowLegacy, ///< for tdf#99729
-    LegacySingleLineFontwork, ///< for tdf#148000
+    LegacyFontwork, ///< for tdf#148000 false == Fontwork works in PowerPoint 
compat mode
     ConnectorUseSnapRect, ///< for tdf#149756
     IgnoreBreakAfterMultilineField, ///< for tdf#148966
 };
diff --git a/include/tools/poly.hxx b/include/tools/poly.hxx
index d9f2a4544901..fd653ec724b7 100644
--- a/include/tools/poly.hxx
+++ b/include/tools/poly.hxx
@@ -114,6 +114,7 @@ public:
 
     void                SetSize( sal_uInt16 nNewSize );
     sal_uInt16          GetSize() const;
+    sal_uInt16          size() const { return GetSize(); } //for vector 
compatibility
 
     void                Clear();
 
diff --git a/oox/qa/unit/shape.cxx b/oox/qa/unit/shape.cxx
index 4db45f7451be..d2475095e716 100644
--- a/oox/qa/unit/shape.cxx
+++ b/oox/qa/unit/shape.cxx
@@ -178,9 +178,9 @@ CPPUNIT_TEST_FIXTURE(OoxShapeTest, 
testTdf125582_TextOnCircle)
     {
         SdrObjCustomShape& rSdrCustomShape(
             
static_cast<SdrObjCustomShape&>(*SdrObject::getSdrObjectFromXShape(xShape)));
-        // Without the fix in place width was 3639, but should be 4824 for 
96dpi.
+        // Without the fix in place width was 3639, but should be 4784 for 
96dpi.
         tools::Rectangle aBoundRect(rSdrCustomShape.GetCurrentBoundRect());
-        CPPUNIT_ASSERT_DOUBLES_EQUAL(tools::Long(4824), aBoundRect.GetWidth(), 
5);
+        CPPUNIT_ASSERT_DOUBLES_EQUAL(tools::Long(4784), aBoundRect.GetWidth(), 
5);
     }
 
     drawing::TextVerticalAdjust eAdjust;
diff --git a/sc/source/ui/docshell/docsh.cxx b/sc/source/ui/docshell/docsh.cxx
index 16cb0655c94f..7c9d67636c67 100644
--- a/sc/source/ui/docshell/docsh.cxx
+++ b/sc/source/ui/docshell/docsh.cxx
@@ -619,7 +619,7 @@ bool ScDocShell::Load( SfxMedium& rMedium )
         {
             
pDrawLayer->SetCompatibilityFlag(SdrCompatibilityFlag::AnchoredTextOverflowLegacy,
                                              true); // for tdf#99729
-            
pDrawLayer->SetCompatibilityFlag(SdrCompatibilityFlag::LegacySingleLineFontwork,
+            
pDrawLayer->SetCompatibilityFlag(SdrCompatibilityFlag::LegacyFontwork,
                                              true); // for tdf#148000
         }
     }
diff --git a/sd/qa/unit/export-tests-ooxml3.cxx 
b/sd/qa/unit/export-tests-ooxml3.cxx
index 3d630db22aa3..6a66e59af884 100644
--- a/sd/qa/unit/export-tests-ooxml3.cxx
+++ b/sd/qa/unit/export-tests-ooxml3.cxx
@@ -573,7 +573,7 @@ CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest3, 
testTdf125573_FontWorkScaleX)
     // BoundRect is DPI dependent, thus allow some range.
     // Expected width is 13139 in 96dpi and is 13106 in 120 dpi, for example
     // (Without fix Expected less than: 85 Actual  : 10432)
-    CPPUNIT_ASSERT_LESS(sal_Int32(85), std::abs(aBoundRectArch.Width - 13139));
+    CPPUNIT_ASSERT_LESS(sal_Int32(85), std::abs(aBoundRectArch.Width - 13145));
 
     // Error was, that text in shapes of category "Warp" was not scaled to the 
path.
     uno::Reference<beans::XPropertySet> xShapeWaveProps(getShapeFromPage(0, 
1));
diff --git a/sd/source/ui/docshell/docshel4.cxx 
b/sd/source/ui/docshell/docshel4.cxx
index aeee4709208a..74c70579bde7 100644
--- a/sd/source/ui/docshell/docshel4.cxx
+++ b/sd/source/ui/docshell/docshel4.cxx
@@ -270,7 +270,7 @@ bool DrawDocShell::Load( SfxMedium& rMedium )
     if (IsOwnStorageFormat(rMedium))
     {
         
mpDoc->SetCompatibilityFlag(SdrCompatibilityFlag::AnchoredTextOverflowLegacy, 
true); // for tdf#99729
-        
mpDoc->SetCompatibilityFlag(SdrCompatibilityFlag::LegacySingleLineFontwork, 
true); // for tdf#148000
+        mpDoc->SetCompatibilityFlag(SdrCompatibilityFlag::LegacyFontwork, 
true); // for tdf#148000
     }
 
     bool       bRet = false;
diff --git a/svx/qa/unit/data/tdf148000_CurvedTextWidth.pptx 
b/svx/qa/unit/data/tdf148000_CurvedTextWidth.pptx
new file mode 100644
index 000000000000..be286cb1cea6
Binary files /dev/null and b/svx/qa/unit/data/tdf148000_CurvedTextWidth.pptx 
differ
diff --git a/svx/qa/unit/data/tdf148000_CurvedTextWidth_Legacy.odp 
b/svx/qa/unit/data/tdf148000_CurvedTextWidth_Legacy.odp
new file mode 100644
index 000000000000..24cf1593f133
Binary files /dev/null and 
b/svx/qa/unit/data/tdf148000_CurvedTextWidth_Legacy.odp differ
diff --git a/svx/qa/unit/data/tdf148000_CurvedTextWidth_New.odp 
b/svx/qa/unit/data/tdf148000_CurvedTextWidth_New.odp
new file mode 100644
index 000000000000..45b6ed0e1f16
Binary files /dev/null and b/svx/qa/unit/data/tdf148000_CurvedTextWidth_New.odp 
differ
diff --git a/svx/qa/unit/svdraw.cxx b/svx/qa/unit/svdraw.cxx
index d61dde7734cd..15d526fdeb37 100644
--- a/svx/qa/unit/svdraw.cxx
+++ b/svx/qa/unit/svdraw.cxx
@@ -426,6 +426,45 @@ CPPUNIT_TEST_FIXTURE(SvdrawTest, 
testTdf148000_EOLinCurvedText)
     }
 }
 
+CPPUNIT_TEST_FIXTURE(SvdrawTest, testTdf148000_CurvedTextWidth)
+{
+    std::vector<OUString> aFilenames
+        = { u"tdf148000_CurvedTextWidth.pptx", 
u"tdf148000_CurvedTextWidth_New.odp",
+            u"tdf148000_CurvedTextWidth_Legacy.odp" };
+
+    for (int i = 0; i < 3; i++)
+    {
+        loadFromURL(aFilenames[i]);
+
+        SdrPage* pSdrPage = getFirstDrawPageWithAssert();
+
+        xmlDocUniquePtr pXmlDoc = 
lcl_dumpAndParseFirstObjectWithAssert(pSdrPage);
+
+        OString aBasePath = 
"/primitive2D/objectinfo[4]/objectinfo/unhandled/unhandled/"
+                            "polypolygoncolor/polypolygon/";
+
+        // The text is: 7 line od "OOOOOOO"
+        // Take the x coord of the 4 "O" on the corners
+        sal_Int32 nX1 = getXPath(pXmlDoc, aBasePath + "polygon[1]/point[1]", 
"x").toInt32();
+        sal_Int32 nX2 = getXPath(pXmlDoc, aBasePath + "polygon[13]/point[1]", 
"x").toInt32();
+        sal_Int32 nX3 = getXPath(pXmlDoc, aBasePath + "polygon[85]/point[1]", 
"x").toInt32();
+        sal_Int32 nX4 = getXPath(pXmlDoc, aBasePath + "polygon[97]/point[1]", 
"x").toInt32();
+
+        if (i < 2)
+        {
+            // All the lines should be positioned similar (start/end is 
similar)
+            CPPUNIT_ASSERT_LESS(sal_Int32(150), abs(nX3 - nX1));
+            CPPUNIT_ASSERT_LESS(sal_Int32(150), abs(nX4 - nX2));
+        }
+        else
+        {
+            // In legacy mode, the outer lines become much wider
+            CPPUNIT_ASSERT_GREATER(sal_Int32(1500), nX3 - nX1);
+            CPPUNIT_ASSERT_GREATER(sal_Int32(1500), nX2 - nX4);
+        }
+    }
+}
+
 CPPUNIT_TEST_FIXTURE(SvdrawTest, testSurfaceMetal)
 {
     loadFromURL(u"tdf140321_metal.odp");
diff --git a/svx/source/customshapes/EnhancedCustomShapeFontWork.cxx 
b/svx/source/customshapes/EnhancedCustomShapeFontWork.cxx
index 7d7ee4fdbc81..2e19aa344ec4 100644
--- a/svx/source/customshapes/EnhancedCustomShapeFontWork.cxx
+++ b/svx/source/customshapes/EnhancedCustomShapeFontWork.cxx
@@ -75,6 +75,7 @@ struct FWTextArea                       // representing 
multiple concluding para
 {
     std::vector< FWParagraphData >      vParagraphs;
     tools::Rectangle                           aBoundRect;
+    sal_Int32                           nHAlignMove = 0;
 };
 struct FWData                           // representing the whole text
 {
@@ -135,7 +136,7 @@ static bool InitializeFontWorkData(
                 {
                     // search line break.
                     if 
(!rSdrObjCustomShape.getSdrModelFromSdrObject().GetCompatibilityFlag(
-                            SdrCompatibilityFlag::LegacySingleLineFontwork))
+                            SdrCompatibilityFlag::LegacyFontwork))
                         nPos = aParaText[nPara].indexOf(sal_Unicode(u'\1'), 
nPrevPos);
                     else
                         nPos = -1; // tdf#148000: ignore line breaks in legacy 
fontworks
@@ -570,6 +571,7 @@ static bool GetFontWorkOutline(
             {
                 sal_Int32 nHorzDiff = 0;
                 sal_Int32 nVertDiff = static_cast<double>( 
rFWData.nSingleLineHeight ) * fFactor * ( rTextArea.vParagraphs.size() - 1 );
+                rTextArea.nHAlignMove = nVertDiff;
 
                 if ( eHorzAdjust == SDRTEXTHORZADJUST_CENTER )
                     nHorzDiff = ( rFWData.fHorizontalTextScaling * 
rTextArea.aBoundRect.GetWidth() - rParagraph.aBoundRect.GetWidth() ) / 2;
@@ -721,10 +723,12 @@ static void InsertMissingOutlinePoints( const 
std::vector< double >& rDistances,
     }
 }
 
-static void GetPoint( const tools::Polygon& rPoly, const std::vector< double 
>& rDistances, const double& fX, double& fx1, double& fy1 )
+//only 2 types used: 'const tools::Polygon&' and 'const std::vector<Point>&'
+template <class T>
+static void GetPoint( T rPoly, const std::vector< double >& rDistances, const 
double& fX, double& fx1, double& fy1 )
 {
     fy1 = fx1 = 0.0;
-    if ( rPoly.GetSize() <= 1 )
+    if (rPoly.size() <= 1)
         return;
 
     std::vector< double >::const_iterator aIter = std::lower_bound( 
rDistances.begin(), rDistances.end(), fX );
@@ -749,7 +753,8 @@ static void GetPoint( const tools::Polygon& rPoly, const 
std::vector< double >&
     fy1 = rPt2.Y() + fHeight;
 }
 
-static void FitTextOutlinesToShapeOutlines( const tools::PolyPolygon& 
aOutlines2d, FWData& rFWData )
+static void FitTextOutlinesToShapeOutlines(const tools::PolyPolygon& 
aOutlines2d, FWData& rFWData,
+                                           SdrTextHorzAdjust eHorzAdjust, bool 
bPPFontwork)
 {
     sal_uInt16 nOutline2dIdx = 0;
     for( auto& rTextArea : rFWData.vTextAreas )
@@ -776,46 +781,242 @@ static void FitTextOutlinesToShapeOutlines( const 
tools::PolyPolygon& aOutlines2
                 std::vector< double > vDistances;
                 vDistances.reserve( nPointCount );
                 CalcDistances( rOutlinePoly, vDistances );
+
                 if ( !vDistances.empty() )
                 {
-                    for( auto& rParagraph : rTextArea.vParagraphs )
+                    // horizontal alignment: how much we have to move text to 
the right.
+                    int nAdjust = -1;
+                    switch (eHorzAdjust)
+                    {
+                        case SDRTEXTHORZADJUST_RIGHT:
+                            nAdjust = 2; // 2 half of the possible
+                            break;
+                        case SDRTEXTHORZADJUST_CENTER:
+                            nAdjust = 1; // 1 half of the possible
+                            break;
+                        case SDRTEXTHORZADJUST_BLOCK:
+                            nAdjust = -1; // don't know what it is, so don't 
even align
+                            break;
+                        case SDRTEXTHORZADJUST_LEFT:
+                            nAdjust = 0; // no need to move
+                            break;
+                    }
+
+                    if (bPPFontwork && rTextArea.vParagraphs.size() > 1 && 
nAdjust >= 0)
+                    {
+                        // If we have multiple lines of text to fit to the 
outline (curve)
+                        // then we have to be able to calculate outer versions 
of the outline
+                        // where we can fit the next lines of texts
+                        // those outer lines will be wider (or shorter) as the 
original outline
+                        // and probably will looks different as the original 
outline.
+                        //
+                        // for example if we have an outline like this:
+                        // <____>
+                        // then the middle part will have the same normals, so 
distances there,
+                        //  will not change for an outer outline
+                        // while the points near the edge will have different 
normals,
+                        //  distances around there will increase for an outer 
(wider) outline
+
+                        //Normal vectors for every rOutlinePoly point. 1024 
long
+                        std::vector<Point> vNorm;
+                        //wider curve path points, for current paragraph 
(rOutlinePoly + vNorm*line)
+                        std::vector<Point> vCurOutline;
+                        //distances between points of this wider curve
+                        std::vector<double> vCurDistances;
+
+                        vCurDistances.reserve(nPointCount);
+                        vCurOutline.reserve(nPointCount);
+                        vNorm.reserve(nPointCount);
+
+                        // Calculate Normal vectors, and allocate curve data
+                        sal_uInt16 i;
+                        for (i = 0; i < nPointCount; i++)
+                        {
+                            //Normal vector for a point will be calculated 
from its neighbour points
+                            //except if it is in the start/end of the vector
+                            sal_uInt16 nPointIdx1 = i == 0 ? i : i - 1;
+                            sal_uInt16 nPointIdx2 = i == nPointCount - 1 ? i : 
i + 1;
+
+                            Point aPoint = rOutlinePoly.GetPoint(nPointIdx2)
+                                           - rOutlinePoly.GetPoint(nPointIdx1);
+
+                            double fLen = sqrt(aPoint.X() * aPoint.X() + 
aPoint.Y() * aPoint.Y());
+
+                            if (fLen > 0)
+                            {
+                                //Rotate by 90 degree, and divide by length, 
to get normal vector
+                                vNorm.emplace_back(aPoint.getY() * 1024 / fLen,
+                                                   -aPoint.getX() * 1024 / 
fLen);
+                            }
+                            else
+                            {
+                                vNorm.emplace_back(0, 0);
+                            }
+                            vCurOutline.emplace_back(Point());
+                            vCurDistances.push_back(0);
+
+                        }
+
+                        for( auto& rParagraph : rTextArea.vParagraphs )
+                        {
+                            //calculate the actual outline length, and its 
align adjustments
+                            double fAdjust;
+                            double fCurWidth;
+
+                            // distance between the original and the current 
curve
+                            double fCurvesDist = 
rTextArea.aBoundRect.GetHeight() / 2.0
+                                                 + rTextArea.aBoundRect.Top()
+                                                 - 
rParagraph.aBoundRect.Center().Y();
+                            // vertical alignment adjust
+                            fCurvesDist -= rTextArea.nHAlignMove;
+
+                            for (i = 0; i < nPointCount; i++)
+                            {
+                                vCurOutline[i]
+                                    = rOutlinePoly.GetPoint(i) + vNorm[i] * 
fCurvesDist / 1024.0;
+                                if (i > 0)
+                                {
+                                    //calculate distances between points on 
the outer outline
+                                    const double fDx = vCurOutline[i].X() - 
vCurOutline[i - 1].X();
+                                    const double fDy = vCurOutline[i].Y() - 
vCurOutline[i - 1].Y();
+                                    vCurDistances[i] = sqrt(fDx * fDx + fDy * 
fDy);
+                                }
+                                else
+                                    vCurDistances[i] = 0;
+                            }
+                            std::partial_sum(vCurDistances.begin(), 
vCurDistances.end(),
+                                             vCurDistances.begin());
+                            fCurWidth = vCurDistances[vCurDistances.size() - 
1];
+                            if (fCurWidth > 0.0)
+                            {
+                                for (auto& rDistance : vCurDistances)
+                                    rDistance /= fCurWidth;
+                            }
+
+                            // if the current outline is longer then the text 
to fit in,
+                            // then we have to divide the bonus space between 
the
+                            // before-/after- text area.
+                            // fAdjust means how much space we put before the 
text.
+                            if (fCurWidth > rParagraph.aBoundRect.GetWidth())
+                            {
+                                fAdjust
+                                    = nAdjust * (fCurWidth - 
rParagraph.aBoundRect.GetWidth()) / 2;
+                            }
+                            else
+                                fAdjust = -1;   // we need to shrink the text 
to fit the curve
+
+                            for ( auto& rCharacter : rParagraph.vCharacters )
+                            {
+                                for (tools::PolyPolygon& rPolyPoly : 
rCharacter.vOutlines)
+                                {
+                                    tools::Rectangle 
aBoundRect(rPolyPoly.GetBoundRect());
+                                    double fx1 = aBoundRect.Left() - nLeft;
+                                    double fx2 = aBoundRect.Right() - nLeft;
+
+                                    double fParaRectWidth = 
rParagraph.aBoundRect.GetWidth();
+                                    // Undo Horizontal alignment, hacked into 
poly coords,
+                                    // so we can calculate it the right way
+                                    double fHA = 
(rFWData.fHorizontalTextScaling
+                                                      * 
rTextArea.aBoundRect.GetWidth()
+                                                  - 
rParagraph.aBoundRect.GetWidth())
+                                                 * nAdjust / 2;
+
+                                    fx1 -= fHA;
+                                    fx2 -= fHA;
+
+                                    double fy1, fy2;
+                                    double fM1 = fx1 / fParaRectWidth;
+                                    double fM2 = fx2 / fParaRectWidth;
+
+                                    // if fAdjust<0, then it means, the text 
was longer, as
+                                    // the current outline, so we will skip 
the text scaling, and
+                                    // the text horizontal alignment adjustment
+                                    // so the text will be rendered just as 
long as the curve is.
+                                    if (fAdjust >= 0)
+                                    {
+                                        fM1 = (fM1 * fParaRectWidth + fAdjust) 
/ fCurWidth;
+                                        fM2 = (fM2 * fParaRectWidth + fAdjust) 
/ fCurWidth;
+                                    }
+                                    // 0 <= fM1,fM2 <= 1 should be true, but 
rounding errors can
+                                    // make a small mistake.
+                                    // make sure they are >0 because 
GetPoint() need that
+                                    if (fM1 < 0) fM1 = 0;
+                                    if (fM2 < 0) fM2 = 0;
+
+                                    GetPoint(vCurOutline, vCurDistances, fM1, 
fx1, fy1);
+                                    GetPoint(vCurOutline, vCurDistances, fM2, 
fx2, fy2);
+
+                                    double fvx = fy2 - fy1;
+                                    double fvy = - ( fx2 - fx1 );
+                                    fx1 = fx1 + ( ( fx2 - fx1 ) * 0.5 );
+                                    fy1 = fy1 + ( ( fy2 - fy1 ) * 0.5 );
+
+                                    double fAngle = atan2( -fvx, -fvy );
+                                    double fL = hypot( fvx, fvy );
+                                    if (fL == 0.0)
+                                    {
+                                        SAL_WARN("svx", 
"FitTextOutlinesToShapeOutlines div-by-zero, abandon fit");
+                                        break;
+                                    }
+                                    fvx = fvx / fL;
+                                    fvy = fvy / fL;
+                                    // Undo Vertical alignment hacked into 
poly coords
+                                    // We already calculated the right 
alignment into the curve
+                                    fL = rTextArea.nHAlignMove;
+                                    fvx *= fL;
+                                    fvy *= fL;
+                                    rPolyPoly.Rotate( Point( 
aBoundRect.Center().X(), rParagraph.aBoundRect.Center().Y() ), sin( fAngle ), 
cos( fAngle ) );
+                                    rPolyPoly.Move( static_cast<sal_Int32>( ( 
fx1 + fvx )- aBoundRect.Center().X() ), static_cast<sal_Int32>( ( fy1 + fvy ) - 
rParagraph.aBoundRect.Center().Y() ) );
+                                }
+                            }
+                        }
+                    }
+                    else
                     {
-                        for ( auto& rCharacter : rParagraph.vCharacters )
+                        // Fallback / old way to handle multiple lines:
+                        // Every text lines use the same original outline 
(curve),
+                        // it just scale character coordinates to fit to the 
right text line
+                        // (curve), resulting wider/thinner space between 
characters
+                        for (auto& rParagraph : rTextArea.vParagraphs)
                         {
-                            for( tools::PolyPolygon& rPolyPoly : 
rCharacter.vOutlines )
+                            for (auto& rCharacter : rParagraph.vCharacters)
                             {
-                                tools::Rectangle aBoundRect( 
rPolyPoly.GetBoundRect() );
-                                double fx1 = aBoundRect.Left() - nLeft;
-                                double fx2 = aBoundRect.Right() - nLeft;
-                                double fy1, fy2;
-                                double fM1 = fx1 / static_cast<double>(nWidth);
-                                double fM2 = fx2 / static_cast<double>(nWidth);
-
-                                GetPoint( rOutlinePoly, vDistances, fM1, fx1, 
fy1 );
-                                GetPoint( rOutlinePoly, vDistances, fM2, fx2, 
fy2 );
-
-                                double fvx = fy2 - fy1;
-                                double fvy = - ( fx2 - fx1 );
-                                fx1 = fx1 + ( ( fx2 - fx1 ) * 0.5 );
-                                fy1 = fy1 + ( ( fy2 - fy1 ) * 0.5 );
-
-                                double fAngle = atan2( -fvx, -fvy );
-                                double fL = hypot( fvx, fvy );
-                                if (fL == 0.0)
+                                for (tools::PolyPolygon& rPolyPoly : 
rCharacter.vOutlines)
                                 {
-                                    SAL_WARN("svx", 
"FitTextOutlinesToShapeOutlines div-by-zero, abandon fit");
-                                    break;
+                                    tools::Rectangle 
aBoundRect(rPolyPoly.GetBoundRect());
+                                    double fx1 = aBoundRect.Left() - nLeft;
+                                    double fx2 = aBoundRect.Right() - nLeft;
+                                    double fy1, fy2;
+                                    double fM1 = fx1 / 
static_cast<double>(nWidth);
+                                    double fM2 = fx2 / 
static_cast<double>(nWidth);
+
+                                    GetPoint(rOutlinePoly, vDistances, fM1, 
fx1, fy1);
+                                    GetPoint(rOutlinePoly, vDistances, fM2, 
fx2, fy2);
+
+                                    double fvx = fy2 - fy1;
+                                    double fvy = -(fx2 - fx1);
+                                    fx1 = fx1 + ((fx2 - fx1) * 0.5);
+                                    fy1 = fy1 + ((fy2 - fy1) * 0.5);
+
+                                    double fAngle = atan2(-fvx, -fvy);
+                                    double fL = hypot(fvx, fvy);
+                                    if (fL == 0.0)
+                                    {
+                                        SAL_WARN("svx", 
"FitTextOutlinesToShapeOutlines div-by-zero, abandon fit");
+                                        break;
+                                    }
+                                    fvx = fvx / fL;
+                                    fvy = fvy / fL;
+                                    fL = rTextArea.aBoundRect.GetHeight() / 
2.0 + rTextArea.aBoundRect.Top() - rParagraph.aBoundRect.Center().Y();
+                                    fvx *= fL;
+                                    fvy *= fL;
+                                    rPolyPoly.Rotate( Point( 
aBoundRect.Center().X(), rParagraph.aBoundRect.Center().Y() ), sin( fAngle ), 
cos( fAngle ) );
+                                    rPolyPoly.Move( static_cast<sal_Int32>( ( 
fx1 + fvx )- aBoundRect.Center().X() ), static_cast<sal_Int32>( ( fy1 + fvy ) - 
rParagraph.aBoundRect.Center().Y() ) );
                                 }
-                                fvx = fvx / fL;
-                                fvy = fvy / fL;
-                                fL = rTextArea.aBoundRect.GetHeight() / 2.0 + 
rTextArea.aBoundRect.Top() - rParagraph.aBoundRect.Center().Y();
-                                fvx *= fL;
-                                fvy *= fL;
-                                rPolyPoly.Rotate( Point( 
aBoundRect.Center().X(), rParagraph.aBoundRect.Center().Y() ), sin( fAngle ), 
cos( fAngle ) );
-                                rPolyPoly.Move( static_cast<sal_Int32>( ( fx1 
+ fvx )- aBoundRect.Center().X() ), static_cast<sal_Int32>( ( fy1 + fvy ) - 
rParagraph.aBoundRect.Center().Y() ) );
                             }
                         }
                     }
+
                 }
             }
         }
@@ -973,7 +1174,11 @@ rtl::Reference<SdrObject> 
EnhancedCustomShapeFontWork::CreateFontWork(
                 return nullptr;
             }
 
-            FitTextOutlinesToShapeOutlines( aOutlines2d, aFWData );
+            SdrTextHorzAdjust eHorzAdjust(
+                
rSdrObjCustomShape.GetMergedItem(SDRATTR_TEXT_HORZADJUST).GetValue());
+            bool bPPFontwork = 
!rSdrObjCustomShape.getSdrModelFromSdrObject().GetCompatibilityFlag(
+                              SdrCompatibilityFlag::LegacyFontwork);
+            FitTextOutlinesToShapeOutlines( aOutlines2d, aFWData, eHorzAdjust, 
bPPFontwork );
 
             pRet = CreateSdrObjectFromParagraphOutlines(
                 aFWData,
diff --git a/svx/source/svdraw/svdmodel.cxx b/svx/source/svdraw/svdmodel.cxx
index 4e1ea3c78ac5..d25d9b47d7f4 100644
--- a/svx/source/svdraw/svdmodel.cxx
+++ b/svx/source/svdraw/svdmodel.cxx
@@ -85,7 +85,7 @@ struct SdrModelImpl
     SfxUndoManager* mpUndoManager;
     SdrUndoFactory* mpUndoFactory;
     bool mbAnchoredTextOverflowLegacy; // tdf#99729 compatibility flag
-    bool mbLegacySingleLineFontwork;   // tdf#148000 compatibility flag
+    bool mbLegacyFontwork;             // tdf#148000 compatibility flag
     bool mbConnectorUseSnapRect;       // tdf#149756 compatibility flag
     bool mbIgnoreBreakAfterMultilineField; ///< tdf#148966 compatibility flag
     std::shared_ptr<model::Theme> mpTheme;
@@ -94,7 +94,7 @@ struct SdrModelImpl
         : mpUndoManager(nullptr)
         , mpUndoFactory(nullptr)
         , mbAnchoredTextOverflowLegacy(false)
-        , mbLegacySingleLineFontwork(false)
+        , mbLegacyFontwork(false)
         , mbConnectorUseSnapRect(false)
         , mbIgnoreBreakAfterMultilineField(false)
         , mpTheme(new model::Theme("Office"))
@@ -1718,8 +1718,8 @@ void SdrModel::SetCompatibilityFlag(SdrCompatibilityFlag 
eFlag, bool bEnabled)
         case SdrCompatibilityFlag::AnchoredTextOverflowLegacy:
             mpImpl->mbAnchoredTextOverflowLegacy = bEnabled;
             break;
-        case SdrCompatibilityFlag::LegacySingleLineFontwork:
-            mpImpl->mbLegacySingleLineFontwork = bEnabled;
+        case SdrCompatibilityFlag::LegacyFontwork:
+            mpImpl->mbLegacyFontwork = bEnabled;
             break;
         case SdrCompatibilityFlag::ConnectorUseSnapRect:
             mpImpl->mbConnectorUseSnapRect = bEnabled;
@@ -1736,8 +1736,8 @@ bool SdrModel::GetCompatibilityFlag(SdrCompatibilityFlag 
eFlag) const
     {
         case SdrCompatibilityFlag::AnchoredTextOverflowLegacy:
             return mpImpl->mbAnchoredTextOverflowLegacy;
-        case SdrCompatibilityFlag::LegacySingleLineFontwork:
-            return mpImpl->mbLegacySingleLineFontwork;
+        case SdrCompatibilityFlag::LegacyFontwork:
+            return mpImpl->mbLegacyFontwork;
         case SdrCompatibilityFlag::ConnectorUseSnapRect:
             return mpImpl->mbConnectorUseSnapRect;
         case SdrCompatibilityFlag::IgnoreBreakAfterMultilineField:
@@ -1801,9 +1801,9 @@ void SdrModel::ReadUserDataSequenceValue(const 
beans::PropertyValue* pValue)
     else if (pValue->Name == "LegacySingleLineFontwork")
     {
         bool bBool = false;
-        if (pValue->Value >>= bBool)
+        if ((pValue->Value >>= bBool) && mpImpl->mbLegacyFontwork != bBool)
         {
-            mpImpl->mbLegacySingleLineFontwork = bBool;
+            mpImpl->mbLegacyFontwork = bBool;
             // tdf#148000 hack: reset all CustomShape geometry as they may 
depend on this property
             // Ideally this ReadUserDataSequenceValue should be called before 
geometry creation
             // Once the calling order will be fixed, this hack will not be 
needed.
@@ -1839,7 +1839,7 @@ void SdrModel::WriteUserDataSequence(uno::Sequence 
<beans::PropertyValue>& rValu
     std::vector< std::pair< OUString, uno::Any > > aUserData
     {
         { "AnchoredTextOverflowLegacy", 
uno::Any(GetCompatibilityFlag(SdrCompatibilityFlag::AnchoredTextOverflowLegacy))
 },
-        { "LegacySingleLineFontwork", 
uno::Any(GetCompatibilityFlag(SdrCompatibilityFlag::LegacySingleLineFontwork)) 
},
+        { "LegacySingleLineFontwork", 
uno::Any(GetCompatibilityFlag(SdrCompatibilityFlag::LegacyFontwork)) },
         { "ConnectorUseSnapRect", 
uno::Any(GetCompatibilityFlag(SdrCompatibilityFlag::ConnectorUseSnapRect)) },
         { "IgnoreBreakAfterMultilineField", 
uno::Any(GetCompatibilityFlag(SdrCompatibilityFlag::IgnoreBreakAfterMultilineField))
 }
     };
diff --git a/sw/source/uibase/app/docshini.cxx 
b/sw/source/uibase/app/docshini.cxx
index 7407de9ef6f5..9643c5a3c878 100644
--- a/sw/source/uibase/app/docshini.cxx
+++ b/sw/source/uibase/app/docshini.cxx
@@ -482,7 +482,7 @@ bool  SwDocShell::Load( SfxMedium& rMedium )
             {
                 
pDrawModel->SetCompatibilityFlag(SdrCompatibilityFlag::AnchoredTextOverflowLegacy,
                                                  true); // legacy processing 
for tdf#99729
-                
pDrawModel->SetCompatibilityFlag(SdrCompatibilityFlag::LegacySingleLineFontwork,
+                
pDrawModel->SetCompatibilityFlag(SdrCompatibilityFlag::LegacyFontwork,
                                                  true); // legacy processing 
for tdf#148000
             }
         }

Reply via email to