cui/source/inc/paragrph.hxx                                 |    5 
 cui/source/tabpages/paragrph.cxx                            |   28 ++++
 cui/uiconfig/ui/paragalignpage.ui                           |   64 +++++++++++
 editeng/source/items/paraitem.cxx                           |   26 ++++
 include/editeng/adjustitem.hxx                              |   25 ++++
 include/editeng/memberids.h                                 |    2 
 include/editeng/unotext.hxx                                 |    2 
 include/xmloff/xmltoken.hxx                                 |    2 
 offapi/com/sun/star/style/ParagraphProperties.idl           |   28 ++++
 schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng |   24 ++++
 svx/sdi/svxitems.sdi                                        |    2 
 sw/inc/inspectorproperties.hrc                              |    2 
 sw/inc/unoprnms.hxx                                         |    2 
 sw/qa/extras/layout/data/tdf168251.fodt                     |   56 +++++++++
 sw/qa/extras/layout/layout3.cxx                             |   68 ++++++++++++
 sw/qa/uitest/styleInspector/styleInspector.py               |   30 ++---
 sw/qa/uitest/styleInspector/tdf137513.py                    |    2 
 sw/source/core/inc/drawfont.hxx                             |   11 +
 sw/source/core/text/guess.cxx                               |   11 +
 sw/source/core/text/inftxt.cxx                              |    4 
 sw/source/core/text/itradj.cxx                              |   11 +
 sw/source/core/text/porlin.hxx                              |   10 +
 sw/source/core/text/portxt.cxx                              |   29 +++++
 sw/source/core/txtnode/swfont.cxx                           |    7 +
 sw/source/core/unocore/unomapproperties.hxx                 |    4 
 sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx       |    2 
 xmloff/inc/xmlprop.hxx                                      |    2 
 xmloff/source/core/xmltoken.cxx                             |    2 
 xmloff/source/style/xmlexppr.cxx                            |    6 -
 xmloff/source/text/txtprmap.cxx                             |    2 
 xmloff/source/token/tokens.txt                              |    2 
 31 files changed, 448 insertions(+), 23 deletions(-)

New commits:
commit 45ec7bd76196dcc60b4c2db2f6f00623ecbaf5a4
Author:     László Németh <[email protected]>
AuthorDate: Tue Sep 2 16:56:13 2025 +0200
Commit:     László Németh <[email protected]>
CommitDate: Wed Sep 3 15:22:37 2025 +0200

    tdf#168251 cui offapi xmloff sw glyph scaling: extend UNO/UX/ODF
    
    Add new paragraph justification options "Minimum glyph scaling"
    and "Maximum glyph scaling" to improve typography, like DTP software
    do, allowing to typeset visually better paragraphs, especially narrow
    columns without unacceptably big word spacing.
    
    * Add spin boxes to Alignment in paragraph formatting dialog window;
    
    * Store properties in paragraph model:
    
      css::style::ParagraphProperties::ParaPropScaleWidthMinimum
    
      css::style::ParagraphProperties::ParaPropScaleWidthMaximum
    
      Note: desired glyph scaling is already supported by ODF/UNO,
      see css::style::CharacterProperties::CharScaleWidth.
    
    * Implement visual layout of minimum and maximum glyph scaling,
      limited by the available extra word spacing and letter spacing,
      too.
    
      Note: last line can exceed the paragraph width, because minimum
      glyph scaling, also minimum letter scaling of the last line
      haven't been applied there, yet.
    
    * Add ODF import/export (loext:text-scale-maximum and
      loext:text-scale-minimum, where style:text-scale is for the
      desired text scaling).
    
    * Add ODF unit test.
    
    Note: hyphenated lines, lines with multiple portions haven't
    been using custom glyph scaling, yet.
    
    Note: resolution of glyph scaling is 1 percent, which will
    avoid of overgeneration of font types using variable fonts.
    
    Change-Id: I9b4576dde4303437b64e37202c47f888f94c3f67
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/190534
    Tested-by: Jenkins
    Reviewed-by: László Németh <[email protected]>

diff --git a/cui/source/inc/paragrph.hxx b/cui/source/inc/paragrph.hxx
index 4218f3091e3b..856daffcf776 100644
--- a/cui/source/inc/paragrph.hxx
+++ b/cui/source/inc/paragrph.hxx
@@ -164,6 +164,11 @@ class SvxParaAlignTabPage : public SfxTabPage
     std::unique_ptr<weld::MetricSpinButton> m_xLetterSpacingMinimum;
     std::unique_ptr<weld::MetricSpinButton> m_xLetterSpacingMaximum;
 
+    /// glyph scaling
+    std::unique_ptr<weld::Label> m_xLabelGlyphScaling;
+    std::unique_ptr<weld::MetricSpinButton> m_xGlyphScalingMinimum;
+    std::unique_ptr<weld::MetricSpinButton> m_xGlyphScalingMaximum;
+
     DECL_LINK(AlignHdl_Impl, weld::Toggleable&, void);
     DECL_LINK(LastLineHdl_Impl, weld::ComboBox&, void);
     DECL_LINK(TextDirectionHdl_Impl, weld::ComboBox&, void);
diff --git a/cui/source/tabpages/paragrph.cxx b/cui/source/tabpages/paragrph.cxx
index 5feaf5420286..652231e16347 100644
--- a/cui/source/tabpages/paragrph.cxx
+++ b/cui/source/tabpages/paragrph.cxx
@@ -1290,6 +1290,9 @@ SvxParaAlignTabPage::SvxParaAlignTabPage(weld::Container* 
pPage, weld::DialogCon
     , m_xLabelLetterSpacing(m_xBuilder->weld_label(u"labelLetterSpacing"_ustr))
     , 
m_xLetterSpacingMinimum(m_xBuilder->weld_metric_spin_button(u"spin_LETTER_SPACING_MIN"_ustr,
 FieldUnit::PERCENT))
     , 
m_xLetterSpacingMaximum(m_xBuilder->weld_metric_spin_button(u"spin_LETTER_SPACING_MAX"_ustr,
 FieldUnit::PERCENT))
+    , m_xLabelGlyphScaling(m_xBuilder->weld_label(u"labelGlyphScaling"_ustr))
+    , 
m_xGlyphScalingMinimum(m_xBuilder->weld_metric_spin_button(u"spin_GLYPH_SCALING_MIN"_ustr,
 FieldUnit::PERCENT))
+    , 
m_xGlyphScalingMaximum(m_xBuilder->weld_metric_spin_button(u"spin_GLYPH_SCALING_MAX"_ustr,
 FieldUnit::PERCENT))
 {
     SetExchangeSupport();
 
@@ -1383,7 +1386,9 @@ bool SvxParaAlignTabPage::FillItemSet( SfxItemSet* 
rOutSet )
             m_xWordSpacingMinimum->get_value_changed_from_saved() ||
             m_xWordSpacingMaximum->get_value_changed_from_saved() ||
             m_xLetterSpacingMinimum->get_value_changed_from_saved() ||
-            m_xLetterSpacingMaximum->get_value_changed_from_saved();
+            m_xLetterSpacingMaximum->get_value_changed_from_saved() ||
+            m_xGlyphScalingMinimum->get_value_changed_from_saved() ||
+            m_xGlyphScalingMaximum->get_value_changed_from_saved();
     }
 
     sal_uInt16 _nWhich = GetWhich( SID_ATTR_PARA_ADJUST );
@@ -1408,6 +1413,8 @@ bool SvxParaAlignTabPage::FillItemSet( SfxItemSet* 
rOutSet )
         aAdj.SetPropWordSpacingMaximum( 
m_xWordSpacingMaximum->get_value(FieldUnit::PERCENT) );
         aAdj.SetPropLetterSpacingMinimum( 
m_xLetterSpacingMinimum->get_value(FieldUnit::PERCENT) );
         aAdj.SetPropLetterSpacingMaximum( 
m_xLetterSpacingMaximum->get_value(FieldUnit::PERCENT) );
+        aAdj.SetPropScaleWidthMinimum( 
m_xGlyphScalingMinimum->get_value(FieldUnit::PERCENT) );
+        aAdj.SetPropScaleWidthMaximum( 
m_xGlyphScalingMaximum->get_value(FieldUnit::PERCENT) );
         rOutSet->Put( aAdj );
         bModified = true;
     }
@@ -1500,6 +1507,11 @@ void SvxParaAlignTabPage::Reset( const SfxItemSet* rSet )
             m_xLetterSpacingMinimum->set_sensitive(true);
             
m_xLetterSpacingMinimum->set_value(rAdj.GetPropLetterSpacingMinimum(), 
FieldUnit::PERCENT);
             
m_xLetterSpacingMaximum->set_value(rAdj.GetPropLetterSpacingMaximum(), 
FieldUnit::PERCENT);
+            // TODO add GlyphScaling (CharScaleWidth)
+            m_xGlyphScalingMaximum->set_sensitive(true);
+            m_xGlyphScalingMinimum->set_sensitive(true);
+            m_xGlyphScalingMinimum->set_value(rAdj.GetPropScaleWidthMinimum(), 
FieldUnit::PERCENT);
+            m_xGlyphScalingMaximum->set_value(rAdj.GetPropScaleWidthMaximum(), 
FieldUnit::PERCENT);
         }
         else
         {
@@ -1513,6 +1525,9 @@ void SvxParaAlignTabPage::Reset( const SfxItemSet* rSet )
             m_xLabelLetterSpacing->set_sensitive(false);
             m_xLetterSpacingMinimum->set_sensitive(false);
             m_xLetterSpacingMaximum->set_sensitive(false);
+            m_xLabelGlyphScaling->set_sensitive(false);
+            m_xGlyphScalingMinimum->set_sensitive(false);
+            m_xGlyphScalingMaximum->set_sensitive(false);
         }
     }
     else
@@ -1531,6 +1546,9 @@ void SvxParaAlignTabPage::Reset( const SfxItemSet* rSet )
         m_xLabelLetterSpacing->set_sensitive(false);
         m_xLetterSpacingMinimum->set_sensitive(false);
         m_xLetterSpacingMaximum->set_sensitive(false);
+        m_xLabelGlyphScaling->set_sensitive(false);
+        m_xGlyphScalingMinimum->set_sensitive(false);
+        m_xGlyphScalingMaximum->set_sensitive(false);
     }
     m_xLastLineLB->set_active(nLBSelect);
 
@@ -1593,6 +1611,8 @@ void SvxParaAlignTabPage::Reset( const SfxItemSet* rSet )
     m_xWordSpacingMaximum->save_value();
     m_xLetterSpacingMinimum->save_value();
     m_xLetterSpacingMaximum->save_value();
+    m_xGlyphScalingMinimum->save_value();
+    m_xGlyphScalingMaximum->save_value();
 
     UpdateExample_Impl();
 }
@@ -1613,6 +1633,8 @@ void SvxParaAlignTabPage::ChangesApplied()
     m_xWordSpacingMaximum->save_value();
     m_xLetterSpacingMinimum->save_value();
     m_xLetterSpacingMaximum->save_value();
+    m_xGlyphScalingMinimum->save_value();
+    m_xGlyphScalingMaximum->save_value();
 }
 
 IMPL_LINK_NOARG(SvxParaAlignTabPage, AlignHdl_Impl, weld::Toggleable&, void)
@@ -1631,6 +1653,10 @@ IMPL_LINK_NOARG(SvxParaAlignTabPage, AlignHdl_Impl, 
weld::Toggleable&, void)
     // TODO visualize CharKerning with percentage
     m_xLetterSpacingMinimum->set_sensitive(bJustify);
     m_xLetterSpacingMaximum->set_sensitive(bJustify);
+    m_xLabelGlyphScaling->set_sensitive(bJustify);
+    // TODO visualize CharScaleWidth with percentage
+    m_xGlyphScalingMinimum->set_sensitive(bJustify);
+    m_xGlyphScalingMaximum->set_sensitive(bJustify);
 
     bool bLastLineIsBlock = m_xLastLineLB->get_active() == 2;
     m_xExpandCB->set_sensitive(bJustify && bLastLineIsBlock);
diff --git a/cui/uiconfig/ui/paragalignpage.ui 
b/cui/uiconfig/ui/paragalignpage.ui
index 451f6b6765cc..92824780cf41 100644
--- a/cui/uiconfig/ui/paragalignpage.ui
+++ b/cui/uiconfig/ui/paragalignpage.ui
@@ -34,6 +34,20 @@
     <property name="step-increment">1</property>
     <property name="page-increment">10</property>
   </object>
+  <object class="GtkAdjustment" id="adjustmentPercent6">
+    <property name="upper">100</property>
+    <property name="lower">50</property>
+    <property name="value">100</property>
+    <property name="step-increment">1</property>
+    <property name="page-increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="adjustmentPercent7">
+    <property name="upper">200</property>
+    <property name="lower">100</property>
+    <property name="value">100</property>
+    <property name="step-increment">1</property>
+    <property name="page-increment">10</property>
+  </object>
   <!-- n-columns=2 n-rows=1 -->
   <object class="GtkGrid" id="ParaAlignPage">
     <property name="visible">True</property>
@@ -594,6 +608,56 @@
                     <property name="top-attach">2</property>
                   </packing>
                 </child>
+                <child>
+                  <object class="GtkLabel" id="labelGlyphScaling">
+                    <property name="visible">True</property>
+                    <property name="can-focus">False</property>
+                    <property name="label" translatable="yes" 
context="paragalignpage|labelGlyphScaling">_Glyph scaling:</property>
+                    <property name="use-underline">True</property>
+                    <property name="xalign">0</property>
+                  </object>
+                  <packing>
+                    <property name="left-attach">0</property>
+                    <property name="top-attach">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkSpinButton" id="spin_GLYPH_SCALING_MIN">
+                    <property name="visible">True</property>
+                    <property name="can-focus">True</property>
+                    <property name="activates-default">True</property>
+                    <property name="truncate-multiline">True</property>
+                    <property name="adjustment">adjustmentPercent6</property>
+                    <child internal-child="accessible">
+                      <object class="AtkObject" 
id="spin_GLYPH_SCALING_MIN-atkobject">
+                        <property name="AtkObject::accessible-description" 
translatable="yes" 
context="paralignpage|extended_tip|GLYPH-SCALING-MIN">Adjusts the minimum glyph 
scaling. Enter a number between 50% (ultra-condensed) and 100% 
(regular).</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left-attach">1</property>
+                    <property name="top-attach">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkSpinButton" id="spin_GLYPH_SCALING_MAX">
+                    <property name="visible">True</property>
+                    <property name="can-focus">True</property>
+                    <property name="activates-default">True</property>
+                    <property name="truncate-multiline">True</property>
+                    <property name="adjustment">adjustmentPercent7</property>
+                    <child internal-child="accessible">
+                      <object class="AtkObject" 
id="spin_GLYPH_SCALING_MAX-atkobject">
+                        <property name="AtkObject::accessible-description" 
translatable="yes" 
context="paralignpage|extended_tip|GLYPH-SCALING-MMAX">Adjusts the maximum 
glyph scaling. Enter a number between 100% (regular) and 200% 
(ultra-expanded).</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left-attach">3</property>
+                    <property name="top-attach">3</property>
+                  </packing>
+                </child>
+
                 <child>
                   <placeholder/>
                 </child>
diff --git a/editeng/source/items/paraitem.cxx 
b/editeng/source/items/paraitem.cxx
index b3f24417c82e..30adcd39a35e 100644
--- a/editeng/source/items/paraitem.cxx
+++ b/editeng/source/items/paraitem.cxx
@@ -334,7 +334,9 @@ SvxAdjustItem::SvxAdjustItem(const SvxAdjust eAdjst, const 
sal_uInt16 nId )
     nPropWordSpacingMinimum(100),
     nPropWordSpacingMaximum(100),
     nPropLetterSpacingMinimum(0),
-    nPropLetterSpacingMaximum(0)
+    nPropLetterSpacingMaximum(0),
+    nPropScaleWidthMinimum(100),
+    nPropScaleWidthMaximum(100)
 {
     SetAdjust( eAdjst );
 }
@@ -352,7 +354,9 @@ bool SvxAdjustItem::operator==( const SfxPoolItem& rAttr ) 
const
            nPropWordSpacingMaximum == rItem.nPropWordSpacingMaximum &&
            nPropWordSpacing == rItem.nPropWordSpacing &&
            nPropLetterSpacingMinimum == rItem.nPropLetterSpacingMinimum &&
-           nPropLetterSpacingMaximum == rItem.nPropLetterSpacingMaximum;
+           nPropLetterSpacingMaximum == rItem.nPropLetterSpacingMaximum &&
+           nPropScaleWidthMinimum == rItem.nPropScaleWidthMinimum &&
+           nPropScaleWidthMaximum == rItem.nPropScaleWidthMaximum;
 }
 
 size_t SvxAdjustItem::hashCode() const
@@ -367,6 +371,8 @@ size_t SvxAdjustItem::hashCode() const
     o3tl::hash_combine(seed, nPropWordSpacingMaximum);
     o3tl::hash_combine(seed, nPropLetterSpacingMinimum);
     o3tl::hash_combine(seed, nPropLetterSpacingMaximum);
+    o3tl::hash_combine(seed, nPropScaleWidthMinimum);
+    o3tl::hash_combine(seed, nPropScaleWidthMaximum);
     return seed;
 }
 
@@ -382,6 +388,8 @@ bool SvxAdjustItem::QueryValue( uno::Any& rVal, sal_uInt8 
nMemberId ) const
         case MID_WORD_SPACING_MAX : rVal <<= 
static_cast<sal_Int16>(GetPropWordSpacingMaximum()); break;
         case MID_LETTER_SPACING_MIN : rVal <<= GetPropLetterSpacingMinimum(); 
break;
         case MID_LETTER_SPACING_MAX : rVal <<= GetPropLetterSpacingMaximum(); 
break;
+        case MID_SCALE_WIDTH_MIN : rVal <<= GetPropScaleWidthMinimum(); break;
+        case MID_SCALE_WIDTH_MAX : rVal <<= GetPropScaleWidthMaximum(); break;
         case MID_EXPAND_SINGLE    :
         {
             rVal <<= bOneBlock;
@@ -450,6 +458,20 @@ bool SvxAdjustItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
             SetPropLetterSpacingMaximum(nVal);
         }
         break;
+        case MID_SCALE_WIDTH_MIN :
+        {
+            sal_Int16 nVal = -1;
+            rVal >>= nVal;
+            SetPropScaleWidthMinimum(nVal);
+        }
+        break;
+        case MID_SCALE_WIDTH_MAX :
+        {
+            sal_Int16 nVal = -1;
+            rVal >>= nVal;
+            SetPropScaleWidthMaximum(nVal);
+        }
+        break;
         case MID_EXPAND_SINGLE :
             ASSERT_CHANGE_REFCOUNTED_ITEM;
             bOneBlock = Any2Bool(rVal);
diff --git a/include/editeng/adjustitem.hxx b/include/editeng/adjustitem.hxx
index 534bacd78cec..4f9697f5b20e 100644
--- a/include/editeng/adjustitem.hxx
+++ b/include/editeng/adjustitem.hxx
@@ -55,6 +55,10 @@ class EDITENG_DLLPUBLIC SvxAdjustItem final : public 
SfxPoolItem
     sal_Int16 nPropLetterSpacingMinimum;
     sal_Int16 nPropLetterSpacingMaximum;
 
+    // minimum and maximum glyph scaling values in percent
+    sal_Int16 nPropScaleWidthMinimum;
+    sal_Int16 nPropScaleWidthMaximum;
+
 protected:
     virtual ItemInstanceManager* getItemInstanceManager() const override;
 
@@ -207,6 +211,27 @@ public:
     {
         nPropLetterSpacingMaximum = nVal;
     }
+
+
+    sal_Int16 GetPropScaleWidthMaximum() const
+    {
+        return nPropScaleWidthMaximum;
+    }
+
+    void SetPropScaleWidthMaximum( sal_Int16 nVal )
+    {
+        nPropScaleWidthMaximum = nVal;
+    }
+
+    sal_Int16 GetPropScaleWidthMinimum() const
+    {
+        return nPropScaleWidthMinimum;
+    }
+
+    void SetPropScaleWidthMinimum( sal_Int16 nVal )
+    {
+        nPropScaleWidthMinimum = nVal;
+    }
 };
 
 #endif
diff --git a/include/editeng/memberids.h b/include/editeng/memberids.h
index 26b2e3c7693a..944664bc0ecf 100644
--- a/include/editeng/memberids.h
+++ b/include/editeng/memberids.h
@@ -74,6 +74,8 @@
 #define MID_WORD_SPACING_MAX    5
 #define MID_LETTER_SPACING_MIN  6
 #define MID_LETTER_SPACING_MAX  7
+#define MID_SCALE_WIDTH_MIN  8
+#define MID_SCALE_WIDTH_MAX  9
 
 //SvxFontItem
 // Don't use 0 as it used for the whole struct
diff --git a/include/editeng/unotext.hxx b/include/editeng/unotext.hxx
index 035f8287b9dc..3a144ef5ed9c 100644
--- a/include/editeng/unotext.hxx
+++ b/include/editeng/unotext.hxx
@@ -169,6 +169,8 @@ struct SfxItemPropertyMapEntry;
     {u"ParaWordSpacingMaximum"_ustr,   EE_PARA_JUST,               
::cppu::UnoType<sal_Int16>::get(),            0, MID_WORD_SPACING_MAX }, \
     {u"ParaLetterSpacingMinimum"_ustr, EE_PARA_JUST,               
::cppu::UnoType<sal_Int16>::get(),            0, MID_LETTER_SPACING_MIN }, \
     {u"ParaLetterSpacingMaximum"_ustr, EE_PARA_JUST,               
::cppu::UnoType<sal_Int16>::get(),            0, MID_LETTER_SPACING_MAX }, \
+    {u"ParaScaleWidthMinimum"_ustr,    EE_PARA_JUST,               
::cppu::UnoType<sal_Int16>::get(),            0, MID_SCALE_WIDTH_MIN }, \
+    {u"ParaScaleWidthMaximum"_ustr,    EE_PARA_JUST,               
::cppu::UnoType<sal_Int16>::get(),            0, MID_SCALE_WIDTH_MAX }, \
     {u"WritingMode"_ustr,              EE_PARA_WRITINGDIR, 
::cppu::UnoType<sal_Int16>::get(),            0, 0 }
 
 class SvxFieldData;
diff --git a/include/xmloff/xmltoken.hxx b/include/xmloff/xmltoken.hxx
index 760f44427496..828de31eb2aa 100644
--- a/include/xmloff/xmltoken.hxx
+++ b/include/xmloff/xmltoken.hxx
@@ -2021,6 +2021,8 @@ namespace xmloff::token {
         XML_TEXT_ROTATION_ANGLE,
         XML_TEXT_ROTATION_SCALE,
         XML_TEXT_SCALE,
+        XML_TEXT_SCALE_MAXIMUM,
+        XML_TEXT_SCALE_MINIMUM,
         XML_TEXT_SHADOW,
         XML_TEXT_STYLE,
         XML_TEXT_TRANSFORM,
diff --git a/offapi/com/sun/star/style/ParagraphProperties.idl 
b/offapi/com/sun/star/style/ParagraphProperties.idl
index 45bc14c6508e..c18217a1bf79 100644
--- a/offapi/com/sun/star/style/ParagraphProperties.idl
+++ b/offapi/com/sun/star/style/ParagraphProperties.idl
@@ -580,6 +580,34 @@ published service ParagraphProperties
             @since LibreOffice 26.3
          */
         [optional, property] long ParaLetterSpacingMaximum;
+
+        /** specifies the minimum glyph scale width as percentage value 
relative
+            to the width of the glyph.
+
+            <p>It takes a percent value between [50, 100], where the original
+            width is denoted by 100.</p>
+
+            @see CharScaleWidth
+
+            @see ParaScaleWidthMaximum
+
+            @since LibreOffice 26.3
+         */
+        [optional, property] long ParaScaleWidthMinimum;
+
+        /** specifies the maximum glyph scale width as percentage value 
relative
+            to the width of the glyph.
+
+            <p>It takes a percent value between [100, 200], where the original
+            width is denoted by 100.</p>
+
+            @see CharScaleWidth
+
+            @see ParaScaleWidthMininum
+
+            @since LibreOffice 26.3
+         */
+        [optional, property] long ParaScaleWidthMaximum;
 };
 
 
diff --git a/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng 
b/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng
index 7b0ce565ab5e..05bc58c52dc7 100644
--- a/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng
+++ b/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng
@@ -3220,6 +3220,30 @@ 
xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.
     </rng:optional>
   </rng:define>
 
+  <!-- TODO no proposal -->
+  <rng:define name="style-paragraph-properties-attlist" combine="interleave">
+    <rng:optional>
+      <rng:attribute name="loext:text-scale-minimum">
+        <rng:choice>
+          <rng:ref name="length"/>
+          <rng:ref name="percent"/>
+        </rng:choice>
+      </rng:attribute>
+    </rng:optional>
+  </rng:define>
+
+  <!-- TODO no proposal -->
+  <rng:define name="style-paragraph-properties-attlist" combine="interleave">
+    <rng:optional>
+      <rng:attribute name="loext:text-scale-maximum">
+        <rng:choice>
+          <rng:ref name="length"/>
+          <rng:ref name="percent"/>
+        </rng:choice>
+      </rng:attribute>
+    </rng:optional>
+  </rng:define>
+
   <!-- TODO no proposal -->
   <rng:define name="style-text-properties-attlist" combine="interleave">
     <rng:optional>
diff --git a/svx/sdi/svxitems.sdi b/svx/sdi/svxitems.sdi
index 1af2e4a1e2ca..fca1b739b832 100644
--- a/svx/sdi/svxitems.sdi
+++ b/svx/sdi/svxitems.sdi
@@ -62,6 +62,8 @@ struct SvxAdjustStruct
     INT16       WordSpacingMaximum  MID_WORD_SPACING_MAX;
     INT16       LetterSpacingMinimum  MID_LETTER_SPACING_MIN;
     INT16       LetterSpacingMaximum  MID_LETTER_SPACING_MAX;
+    INT16       ScaleWidthMinimum  MID_SCALE_WIDTH_MIN;
+    INT16       ScaleWidthMaximum  MID_SCALE_WIDTH_MAX;
 };
 item SvxAdjustStruct SvxAdjustItem;
 
diff --git a/sw/inc/inspectorproperties.hrc b/sw/inc/inspectorproperties.hrc
index bd0d3b166f79..5c7d5f2c62cd 100644
--- a/sw/inc/inspectorproperties.hrc
+++ b/sw/inc/inspectorproperties.hrc
@@ -243,6 +243,8 @@
 #define RID_PARA_SPLIT                                      
NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Split")
 #define RID_PARA_STYLE_NAME                                 
NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Style Name")
 #define RID_PARA_TAB_STOPS                                  
NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Tab Stops")
+#define RID_PARA_SCALE_WIDTH_MIN                            
NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Scale Width Minimum")
+#define RID_PARA_SCALE_WIDTH_MAX                            
NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Scale Width Maximum")
 #define RID_PARA_TOP_MARGIN                                 
NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Top Margin")
 #define RID_PARA_TOP_MARGIN_RELATIVE                        
NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Top Margin Relative")
 #define RID_PARA_USER_DEFINED_ATTRIBUTES                    
NC_("RID_ATTRIBUTE_NAMES_MAP", "Para User Defined Attributes")
diff --git a/sw/inc/unoprnms.hxx b/sw/inc/unoprnms.hxx
index 8ffe778f437a..341777e80b23 100644
--- a/sw/inc/unoprnms.hxx
+++ b/sw/inc/unoprnms.hxx
@@ -208,6 +208,8 @@ inline constexpr OUString UNO_NAME_PARA_WORD_SPACING_MIN = 
u"ParaWordSpacingMini
 inline constexpr OUString UNO_NAME_PARA_WORD_SPACING_MAX = 
u"ParaWordSpacingMaximum"_ustr;
 inline constexpr OUString UNO_NAME_PARA_LETTER_SPACING_MIN = 
u"ParaLetterSpacingMinimum"_ustr;
 inline constexpr OUString UNO_NAME_PARA_LETTER_SPACING_MAX = 
u"ParaLetterSpacingMaximum"_ustr;
+inline constexpr OUString UNO_NAME_PARA_SCALE_WIDTH_MIN = 
u"ParaScaleWidthMinimum"_ustr;
+inline constexpr OUString UNO_NAME_PARA_SCALE_WIDTH_MAX = 
u"ParaScaleWidthMaximum"_ustr;
 inline constexpr OUString UNO_NAME_PARA_LINE_NUMBER_COUNT = 
u"ParaLineNumberCount"_ustr;
 inline constexpr OUString UNO_NAME_PARA_LINE_NUMBER_START_VALUE = 
u"ParaLineNumberStartValue"_ustr;
 inline constexpr OUString UNO_NAME_BACK_COLOR = u"BackColor"_ustr;
diff --git a/sw/qa/extras/layout/data/tdf168251.fodt 
b/sw/qa/extras/layout/data/tdf168251.fodt
new file mode 100644
index 000000000000..0f6c405114da
--- /dev/null
+++ b/sw/qa/extras/layout/data/tdf168251.fodt
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<office:document xmlns:css3t="http://www.w3.org/TR/css3-text/"; 
xmlns:grddl="http://www.w3.org/2003/g/data-view#"; 
xmlns:xhtml="http://www.w3.org/1999/xhtml"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xmlns:xsd="http://www.w3.org/2001/XMLSchema"; 
xmlns:xforms="http://www.w3.org/2002/xforms"; 
xmlns:dom="http://www.w3.org/2001/xml-events"; 
xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" 
xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" 
xmlns:math="http://www.w3.org/1998/Math/MathML"; 
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" 
xmlns:ooo="http://openoffice.org/2004/office"; 
xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" 
xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" 
xmlns:ooow="http://openoffice.org/2004/writer"; 
xmlns:xlink="http://www.w3.org/1999/xlink"; 
xmlns:drawooo="http://openoffice.org/2010/draw"; 
xmlns:oooc="http://openoffice.org/2004/calc"; 
xmlns:dc="http://purl.org/dc/elements/1.1/"; xmlns:c
 alcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" 
xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" 
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" 
xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" 
xmlns:tableooo="http://openoffice.org/2009/table"; 
xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" 
xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" 
xmlns:rpt="http://openoffice.org/2005/report"; 
xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0"
 xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" 
xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" 
xmlns:officeooo="http://openoffice.org/2009/office"; 
xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" 
xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" 
xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" 
xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:
 meta:1.0" 
xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0"
 office:version="1.4" office:mimetype="application/vnd.oasis.opendocument.text">
+ <office:font-face-decls>
+  <style:font-face style:name="Liberation Serif" 
svg:font-family="&apos;Liberation Serif&apos;" 
style:font-family-generic="modern" style:font-pitch="fixed"/>
+ </office:font-face-decls>
+ <office:styles>
+  <style:default-style style:family="graphic">
+   <style:graphic-properties svg:stroke-color="#3465a4" 
draw:fill-color="#729fcf" fo:wrap-option="no-wrap" draw:shadow-offset-x="0.3cm" 
draw:shadow-offset-y="0.3cm" draw:start-line-spacing-horizontal="0.283cm" 
draw:start-line-spacing-vertical="0.283cm" 
draw:end-line-spacing-horizontal="0.283cm" 
draw:end-line-spacing-vertical="0.283cm" style:writing-mode="lr-tb" 
style:flow-with-text="false"/>
+   <style:paragraph-properties style:text-autospace="ideograph-alpha" 
style:line-break="strict" loext:tab-stop-distance="0cm" 
style:writing-mode="lr-tb" style:font-independent-line-spacing="false">
+    <style:tab-stops/>
+   </style:paragraph-properties>
+   <style:text-properties style:use-window-font-color="true" 
loext:opacity="0%" style:font-name="Liberation Serif" fo:font-size="12pt" 
fo:language="en" fo:country="US" style:letter-kerning="true" 
style:font-name-asian="Noto Serif CJK SC" style:font-size-asian="10.5pt" 
style:language-asian="zh" style:country-asian="CN" 
style:font-name-complex="Lohit Devanagari1" style:font-size-complex="12pt" 
style:language-complex="hi" style:country-complex="IN"/>
+  </style:default-style>
+  <style:default-style style:family="paragraph">
+   <style:paragraph-properties fo:hyphenation-ladder-count="no-limit" 
fo:hyphenation-keep="auto" loext:hyphenation-keep-type="column" 
loext:hyphenation-keep-line="false" style:text-autospace="ideograph-alpha" 
style:punctuation-wrap="hanging" style:line-break="strict" 
style:tab-stop-distance="1.251cm" style:writing-mode="page"/>
+   <style:text-properties style:use-window-font-color="true" 
loext:opacity="0%" style:font-name="Liberation Serif" fo:font-size="12pt" 
fo:language="en" fo:country="US" style:font-name-asian="Noto Serif CJK SC" 
style:font-size-asian="10.5pt" style:language-asian="zh" 
style:country-asian="CN" style:font-name-complex="Lohit Devanagari1" 
style:font-size-complex="12pt" style:language-complex="hi" 
style:country-complex="IN" fo:hyphenate="false" 
fo:hyphenation-remain-char-count="2" fo:hyphenation-push-char-count="2" 
loext:hyphenation-no-caps="false" loext:hyphenation-no-last-word="false" 
loext:hyphenation-word-char-count="no-limit" loext:hyphenation-zone="no-limit"/>
+  </style:default-style>
+  <style:default-style style:family="table">
+   <style:table-properties table:border-model="collapsing"/>
+  </style:default-style>
+  <style:default-style style:family="table-row">
+   <style:table-row-properties fo:keep-together="auto"/>
+  </style:default-style>
+  <style:style style:name="Standard" style:family="paragraph" 
style:class="text"/>
+  <style:style style:name="Text_20_body" style:display-name="Text body" 
style:family="paragraph" style:parent-style-name="Standard" style:class="text" 
style:master-page-name="">
+   <style:paragraph-properties fo:margin-left="0cm" fo:margin-right="0cm" 
fo:margin-top="0cm" fo:margin-bottom="0cm" style:contextual-spacing="false" 
fo:line-height="100%" fo:text-align="start" style:justify-single-word="false" 
fo:orphans="2" fo:widows="2" fo:hyphenation-ladder-count="no-limit" 
fo:hyphenation-keep="auto" loext:hyphenation-keep-type="column" 
loext:hyphenation-keep-line="false" fo:text-indent="0.423cm" 
style:auto-text-indent="false" style:page-number="auto"/>
+   <style:text-properties fo:hyphenate="true" 
fo:hyphenation-remain-char-count="2" fo:hyphenation-push-char-count="2" 
loext:hyphenation-no-caps="false" loext:hyphenation-no-last-word="false" 
loext:hyphenation-word-char-count="no-limit" loext:hyphenation-zone="no-limit"/>
+  </style:style>
+ </office:styles>
+ <office:automatic-styles>
+  <style:style style:name="P1" style:family="paragraph" 
style:parent-style-name="Text_20_body" style:master-page-name="">
+   <style:paragraph-properties fo:text-align="justify" 
style:justify-single-word="false" fo:hyphenation-ladder-count="no-limit" 
fo:hyphenation-keep="auto" loext:hyphenation-keep-type="column" 
loext:hyphenation-keep-line="false" style:page-number="auto" 
loext:letter-spacing-maximum="10%" loext:text-scale-minimum="99%" 
loext:text-scale-maximum="110%"/>
+   <style:text-properties officeooo:paragraph-rsid="001f2a0b" 
fo:hyphenate="false" fo:hyphenation-remain-char-count="2" 
fo:hyphenation-push-char-count="2" loext:hyphenation-no-caps="false" 
loext:hyphenation-no-last-word="false" 
loext:hyphenation-word-char-count="no-limit" loext:hyphenation-zone="no-limit"/>
+  </style:style>
+  <style:page-layout style:name="pm1">
+   <style:page-layout-properties fo:page-width="594.99pt" 
fo:page-height="842pt" style:num-format="1" style:print-orientation="portrait" 
fo:margin-top="72pt" fo:margin-bottom="72pt" fo:margin-left="195pt" 
fo:margin-right="195pt" style:writing-mode="lr-tb" 
style:layout-grid-color="#c0c0c0" style:layout-grid-lines="20" 
style:layout-grid-base-height="20.01pt" style:layout-grid-ruby-height="10.01pt" 
style:layout-grid-mode="none" style:layout-grid-ruby-below="false" 
style:layout-grid-print="false" style:layout-grid-display="false" 
style:footnote-max-height="0pt" loext:margin-gutter="0pt">
+    <style:footnote-sep style:width="0.018cm" 
style:distance-before-sep="0.101cm" style:distance-after-sep="0.101cm" 
style:line-style="solid" style:adjustment="left" style:rel-width="25%" 
style:color="#000000"/>
+   </style:page-layout-properties>
+  </style:page-layout>
+ </office:automatic-styles>
+ <office:master-styles>
+  <style:master-page style:name="Standard" style:page-layout-name="pm1" 
draw:style-name="dp1">
+   <style:header>
+    <text:p text:style-name="Header"/>
+   </style:header>
+  </style:master-page>
+  <style:master-page style:name="Footnote" style:page-layout-name="pm2" 
draw:style-name="dp1"/>
+  <style:master-page style:name="Endnote" style:page-layout-name="pm2" 
draw:style-name="dp1"/>
+ </office:master-styles>
+ <office:body>
+  <office:text>
+   <text:p text:style-name="P1">Lorem ipsum dolor sit amet, consectetur 
adipiscing elit. Vestibulum consequat mi quis pretium semper. Proin luctus orci 
ac neque venenatis, quis commodo dolor posuere. Curabitur dignissim sapien quis 
cursus egestas. Donec blandit auctor arcu, nec pellentesque eros molestie eget. 
In consectetur aliquam hendrerit.</text:p>
+  </office:text>
+ </office:body>
+</office:document>
diff --git a/sw/qa/extras/layout/layout3.cxx b/sw/qa/extras/layout/layout3.cxx
index 6b259aa69bc4..1cd8d590f546 100644
--- a/sw/qa/extras/layout/layout3.cxx
+++ b/sw/qa/extras/layout/layout3.cxx
@@ -629,6 +629,74 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, 
testTdf167648_minimum)
     }
 }
 
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf168251)
+{
+    createSwDoc("tdf168251.fodt");
+    // Ensure that all text portions are calculated before testing.
+    SwDocShell* pShell = getSwDocShell();
+
+    // Dump the rendering of the first page as an XML file.
+    std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
+    MetafileXmlDump dumper;
+
+    xmlDocUniquePtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
+    CPPUNIT_ASSERT(pXmlDoc);
+
+    // Find the first text array action
+    for (size_t nAction = 0; nAction < xMetaFile->GetActionSize(); nAction++)
+    {
+        auto pAction = xMetaFile->GetAction(nAction);
+        if (pAction->GetType() == MetaActionType::TEXTARRAY)
+        {
+            auto pTextArrayAction = static_cast<MetaTextArrayAction*>(pAction);
+            auto pDXArray = pTextArrayAction->GetDXArray();
+
+            // There should be 39 characters on the first line
+            // This was 27 characters, but setting minimum glyph scaling
+            // to 99% allows more words in the line
+            CPPUNIT_ASSERT_EQUAL(size_t(39), pDXArray.size());
+
+            // Assert we are using the expected position for the
+            // second character of the first word with enlarged letter-spacing
+            // This was 286, now 266, according to the -25% minimum letter 
spacing
+            CPPUNIT_ASSERT_LESS(sal_Int32(270), sal_Int32(pDXArray[1]));
+
+            break;
+        }
+    }
+
+    // Find the fourth text array action
+    int nLine = 0;
+    for (size_t nAction = 0; nAction < xMetaFile->GetActionSize(); nAction++)
+    {
+        auto pAction = xMetaFile->GetAction(nAction);
+        if (pAction->GetType() == MetaActionType::TEXTARRAY)
+        {
+            if (++nLine < 6)
+                continue;
+
+            auto pTextArrayAction = static_cast<MetaTextArrayAction*>(pAction);
+            auto pDXArray = pTextArrayAction->GetDXArray();
+
+            // There should be 35 characters on the first line
+            CPPUNIT_ASSERT_EQUAL(size_t(35), pDXArray.size());
+
+            // Assert we are using the expected position for the
+            // second character of the first word with enlarged glyph width
+            // This was 238, now 251, according to the 110% maximum glyph 
scaling
+            // (and no changes in letter spacing)
+            CPPUNIT_ASSERT_GREATER(sal_Int32(245), sal_Int32(pDXArray[1]));
+
+            // Assert we are using the expected position for the
+            // first character of the last word with enlarged glyph width
+            // This was 3689, now 3659, according to the 110% maximum glyph 
scaling
+            CPPUNIT_ASSERT_LESS(sal_Int32(3665), sal_Int32(pDXArray[30]));
+
+            break;
+        }
+    }
+}
+
 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf164499)
 {
     createSwDoc("tdf164499.docx");
diff --git a/sw/qa/uitest/styleInspector/styleInspector.py 
b/sw/qa/uitest/styleInspector/styleInspector.py
index d133461c7a82..a9ab1fc940ee 100644
--- a/sw/qa/uitest/styleInspector/styleInspector.py
+++ b/sw/qa/uitest/styleInspector/styleInspector.py
@@ -26,7 +26,7 @@ class styleNavigator(UITestCase):
             # The cursor is on text without formatting and default style
             self.assertEqual(1, len(xListBox.getChild('0').getChildren()))
             self.assertEqual("Default Paragraph Style  ", 
get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text'])
-            self.assertEqual(153, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(155, 
len(xListBox.getChild('0').getChild('0').getChildren()))
             self.assertEqual(0, len(xListBox.getChild('1').getChildren()))
             self.assertEqual(0, len(xListBox.getChild('2').getChildren()))
             self.assertEqual(0, len(xListBox.getChild('3').getChildren()))
@@ -36,7 +36,7 @@ class styleNavigator(UITestCase):
             # The cursor is on text with direct formatting
             self.assertEqual(1, len(xListBox.getChild('0').getChildren()))
             self.assertEqual("Default Paragraph Style  ", 
get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text'])
-            self.assertEqual(153, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(155, 
len(xListBox.getChild('0').getChild('0').getChildren()))
             self.assertEqual(0, len(xListBox.getChild('1').getChildren()))
             self.assertEqual(0, len(xListBox.getChild('2').getChildren()))
 
@@ -54,7 +54,7 @@ class styleNavigator(UITestCase):
             # The cursor is on text with paragraph direct formatting
             self.assertEqual(1, len(xListBox.getChild('0').getChildren()))
             self.assertEqual("Default Paragraph Style  ", 
get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text'])
-            self.assertEqual(153, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(155, 
len(xListBox.getChild('0').getChild('0').getChildren()))
 
             xParDirFormatting = xListBox.getChild('1')
             self.assertEqual(7, len(xParDirFormatting.getChildren()))
@@ -75,13 +75,13 @@ class styleNavigator(UITestCase):
             xParStyle = xListBox.getChild('0')
             self.assertEqual(3, len(xParStyle.getChildren()))
             self.assertEqual("Default Paragraph Style  ", 
get_state_as_dict(xParStyle.getChild('0'))['Text'])
-            self.assertEqual(153, len(xParStyle.getChild('0').getChildren()))
+            self.assertEqual(155, len(xParStyle.getChild('0').getChildren()))
             self.assertEqual("Heading  ", 
get_state_as_dict(xParStyle.getChild('1'))['Text'])
             self.assertEqual(28, len(xParStyle.getChild('1').getChildren()))
 
             xTitleStyle = xParStyle.getChild('2')
             self.assertEqual("Title    ", 
get_state_as_dict(xTitleStyle)['Text'])
-            self.assertEqual(21, len(xTitleStyle.getChildren()))
+            self.assertEqual(23, len(xTitleStyle.getChildren()))
             self.assertEqual("Char Difference Height   0", 
get_state_as_dict(xTitleStyle.getChild('0'))['Text'])
             self.assertEqual("Char Difference Height Asian     0", 
get_state_as_dict(xTitleStyle.getChild('1'))['Text'])
             self.assertEqual("Char Difference Height Complex   0", 
get_state_as_dict(xTitleStyle.getChild('2'))['Text'])
@@ -100,9 +100,11 @@ class styleNavigator(UITestCase):
             self.assertEqual("Para Last Line Adjust    0", 
get_state_as_dict(xTitleStyle.getChild('15'))['Text'])
             self.assertEqual("Para Letter Spacing Maximum      0", 
get_state_as_dict(xTitleStyle.getChild('16'))['Text'])
             self.assertEqual("Para Letter Spacing Minimum      0", 
get_state_as_dict(xTitleStyle.getChild('17'))['Text'])
-            self.assertEqual("Para Word Spacing        100", 
get_state_as_dict(xTitleStyle.getChild('18'))['Text'])
-            self.assertEqual("Para Word Spacing Maximum        100", 
get_state_as_dict(xTitleStyle.getChild('19'))['Text'])
-            self.assertEqual("Para Word Spacing Minimum        100", 
get_state_as_dict(xTitleStyle.getChild('20'))['Text'])
+            self.assertEqual("Para Scale Width Maximum 100", 
get_state_as_dict(xTitleStyle.getChild('18'))['Text'])
+            self.assertEqual("Para Scale Width Minimum 100", 
get_state_as_dict(xTitleStyle.getChild('19'))['Text'])
+            self.assertEqual("Para Word Spacing        100", 
get_state_as_dict(xTitleStyle.getChild('20'))['Text'])
+            self.assertEqual("Para Word Spacing Maximum        100", 
get_state_as_dict(xTitleStyle.getChild('21'))['Text'])
+            self.assertEqual("Para Word Spacing Minimum        100", 
get_state_as_dict(xTitleStyle.getChild('22'))['Text'])
 
             self.assertEqual(0, len(xListBox.getChild('1').getChildren()))
             self.assertEqual(0, len(xListBox.getChild('2').getChildren()))
@@ -114,7 +116,7 @@ class styleNavigator(UITestCase):
             xParStyle = xListBox.getChild('0')
             self.assertEqual(3, len(xParStyle.getChildren()))
             self.assertEqual("Default Paragraph Style  ", 
get_state_as_dict(xParStyle.getChild('0'))['Text'])
-            self.assertEqual(153, len(xParStyle.getChild('0').getChildren()))
+            self.assertEqual(155, len(xParStyle.getChild('0').getChildren()))
             self.assertEqual("Body Text        ", 
get_state_as_dict(xParStyle.getChild('1'))['Text'])
             self.assertEqual(6, len(xParStyle.getChild('1').getChildren()))
 
@@ -149,7 +151,7 @@ class styleNavigator(UITestCase):
             # The cursor is on text without metadata
             self.assertEqual(1, len(xListBox.getChild('0').getChildren()))
             self.assertEqual("Default Paragraph Style  ", 
get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text'])
-            self.assertEqual(153, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(155, 
len(xListBox.getChild('0').getChild('0').getChildren()))
             self.assertEqual(0, len(xListBox.getChild('1').getChildren()))
             self.assertEqual(0, len(xListBox.getChild('2').getChildren()))
             self.assertEqual(0, len(xListBox.getChild('3').getChildren()))
@@ -159,7 +161,7 @@ class styleNavigator(UITestCase):
             # The cursor is on text with paragraph metadata showed under 
direct paragraph formatting
             self.assertEqual(1, len(xListBox.getChild('0').getChildren()))
             self.assertEqual("Default Paragraph Style  ", 
get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text'])
-            self.assertEqual(153, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(155, 
len(xListBox.getChild('0').getChild('0').getChildren()))
 
             xParDirFormatting = xListBox.getChild('1')
             self.assertEqual(1, len(xParDirFormatting.getChildren()))
@@ -212,7 +214,7 @@ class styleNavigator(UITestCase):
             # The cursor is on text without metadata
             self.assertEqual(1, len(xListBox.getChild('0').getChildren()))
             self.assertEqual("Default Paragraph Style  ", 
get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text'])
-            self.assertEqual(153, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(155, 
len(xListBox.getChild('0').getChild('0').getChildren()))
             self.assertEqual(0, len(xListBox.getChild('1').getChildren()))
             self.assertEqual(0, len(xListBox.getChild('2').getChildren()))
             self.assertEqual(0, len(xListBox.getChild('3').getChildren()))
@@ -222,7 +224,7 @@ class styleNavigator(UITestCase):
             # The cursor is on text with paragraph metadata showed under 
direct paragraph formatting
             self.assertEqual(1, len(xListBox.getChild('1').getChildren()))
             self.assertEqual("Default Paragraph Style  ", 
get_state_as_dict(xListBox.getChild('1').getChild('0'))['Text'])
-            self.assertEqual(153, 
len(xListBox.getChild('1').getChild('0').getChildren()))
+            self.assertEqual(155, 
len(xListBox.getChild('1').getChild('0').getChildren()))
 
             # Outer bookmark
             xBookmarkFormatting = xListBox.getChild('0')
@@ -269,7 +271,7 @@ class styleNavigator(UITestCase):
             # The cursor is on text without metadata
             self.assertEqual(1, len(xListBox.getChild('0').getChildren()))
             self.assertEqual("Default Paragraph Style  ", 
get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text'])
-            self.assertEqual(153, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(155, 
len(xListBox.getChild('0').getChild('0').getChildren()))
             self.assertEqual(0, len(xListBox.getChild('1').getChildren()))
             self.assertEqual(0, len(xListBox.getChild('2').getChildren()))
 
diff --git a/sw/qa/uitest/styleInspector/tdf137513.py 
b/sw/qa/uitest/styleInspector/tdf137513.py
index af0e24d993d3..8352c2a352e9 100644
--- a/sw/qa/uitest/styleInspector/tdf137513.py
+++ b/sw/qa/uitest/styleInspector/tdf137513.py
@@ -35,7 +35,7 @@ class tdf137513(UITestCase):
             self.assertEqual(2, len(xListBox.getChild('0').getChildren()))
             self.assertEqual("Default Paragraph Style  ", 
get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text'])
             self.assertEqual("Table Contents   ", 
get_state_as_dict(xListBox.getChild('0').getChild('1'))['Text'])
-            self.assertEqual(153, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(155, 
len(xListBox.getChild('0').getChild('0').getChildren()))
 
             xTableContent = xListBox.getChild('0').getChild('1')
             self.assertEqual(5, len(xTableContent.getChildren()))
diff --git a/sw/source/core/inc/drawfont.hxx b/sw/source/core/inc/drawfont.hxx
index 3438c366c03a..443f47635353 100644
--- a/sw/source/core/inc/drawfont.hxx
+++ b/sw/source/core/inc/drawfont.hxx
@@ -75,6 +75,7 @@ class SW_DLLPUBLIC SwDrawTextInfo
     tools::Long m_nSpace = 0;
     tools::Long m_nKern = 0;
     tools::Long m_nLetterSpacing = 0;
+    sal_Int16 m_nScaleWidth = 100;
     TextFrameIndex m_nNumberOfBlanks = TextFrameIndex{ 0 };
     sal_uInt8 m_nCursorBidiLevel = 0;
     bool m_bBullet : 1;
@@ -550,6 +551,16 @@ public:
         return m_nLetterSpacing;
     }
 
+    void SetScaleWidth( tools::Long nNew )
+    {
+        m_nScaleWidth = nNew;
+    }
+
+    tools::Long GetScaleWidth() const
+    {
+        return m_nScaleWidth;
+    }
+
     void SetSpace( tools::Long nNew )
     {
         if( nNew < 0 )
diff --git a/sw/source/core/text/guess.cxx b/sw/source/core/text/guess.cxx
index 384c98baba8d..f75ffa5510cf 100644
--- a/sw/source/core/text/guess.cxx
+++ b/sw/source/core/text/guess.cxx
@@ -499,6 +499,17 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, 
SwTextFormatInfo &rInf,
     {
         m_nCutPos = rInf.GetTextBreak( nLineWidth, nMaxLen, nMaxComp, 
rInf.GetCachedVclData().get() );
 
+        // tdf#168251 minimum glyph scaling allows more text in the line
+        // TODO don't be greedy, allow only an extra word or word part
+        const sal_Int16 nScaleWidthMinimum = 
aAdjustItem.GetPropScaleWidthMinimum();
+        if ( nScaleWidthMinimum < 100 )
+        {
+            SwTwips nExtraSpace = nLineWidth / (nScaleWidthMinimum / 100.0) - 
nLineWidth;
+            nLineWidth += nExtraSpace;
+            rInf.SetExtraSpace( rInf.GetExtraSpace() + nExtraSpace );
+            m_nCutPos = rInf.GetTextBreak( nLineWidth, nMaxLen, nMaxComp, 
rInf.GetCachedVclData().get() );
+        }
+
         // tdf#167648 minimum letter spacing allows more text in the line
         // TODO don't be greedy, allow only an extra word or word part
         if ( const sal_Int16 nLetterSpacingMinimum = 
aAdjustItem.GetPropLetterSpacingMinimum() )
diff --git a/sw/source/core/text/inftxt.cxx b/sw/source/core/text/inftxt.cxx
index 8c297d4e300d..a9333e66b9b4 100644
--- a/sw/source/core/text/inftxt.cxx
+++ b/sw/source/core/text/inftxt.cxx
@@ -783,6 +783,10 @@ void SwTextPaintInfo::DrawText_( const OUString &rText, 
const SwLinePortion &rPo
             if ( rPor.GetLetterSpacing() != 0 )
                 aDrawInf.SetLetterSpacing( rPor.GetLetterSpacing() / 
sal_Int32(nLength) );
 
+            // set custom glyph scaling (hyphenation hasn't been supported yet)
+            // Note: set 100 percent, too (to reset the setting of the 
previous line)
+            aDrawInf.SetScaleWidth( rPor.GetScaleWidth() );
+
             m_pFnt->DrawText_( aDrawInf );
         }
     }
diff --git a/sw/source/core/text/itradj.cxx b/sw/source/core/text/itradj.cxx
index c6230b18f5db..64bd6e7dd582 100644
--- a/sw/source/core/text/itradj.cxx
+++ b/sw/source/core/text/itradj.cxx
@@ -370,11 +370,16 @@ void SwTextAdjuster::CalcNewBlock( SwLineLayout *pCurrent,
                             : 0;
 
                     SwLinePortion *pPortion = pCurrent->GetFirstPortion();
-                    tools::Long nSpaceKern = pPortion->GetSpaceCount()
-                        ? tools::Long(pPortion->GetLetterSpacing()) / 
sal_Int32(pPortion->GetSpaceCount()) * 100
+                    // word spacing filled by letter spacing and glyph scaling 
(at expansion) or
+                    // word spacing shrunk by them (at shrinking)
+                    tools::Long nSpaceKernAndScale = pPortion->GetSpaceCount()
+                        ? ( pPortion->GetLetterSpacing() + 
pPortion->GetScaleWidthSpacing() ) * 100.0 /
+                                                                            
sal_Int32(pPortion->GetSpaceCount())
                         : 0;
                     // set expansion in 1/100 twips/space
-                    pCurrent->SetLLSpaceAdd( nSpaceSub ? nSpaceSub + 
nSpaceKern : (nSpaceAdd > nSpaceKern ? nSpaceAdd - nSpaceKern : 0), nSpaceIdx );
+                    pCurrent->SetLLSpaceAdd( nSpaceSub
+                        ? ( nSpaceSub + nSpaceKernAndScale <= LONG_MAX/2 ? 0 : 
nSpaceSub + nSpaceKernAndScale )
+                        : ( nSpaceAdd > nSpaceKernAndScale ? nSpaceAdd - 
nSpaceKernAndScale : 0 ), nSpaceIdx );
                     pPos->Width( 
static_cast<SwGluePortion*>(pPos)->GetFixWidth() );
                 }
                 else if (IsOneBlock() && nCharCnt > TextFrameIndex(1))
diff --git a/sw/source/core/text/porlin.hxx b/sw/source/core/text/porlin.hxx
index 4405a1aff144..6a3a58f1aec6 100644
--- a/sw/source/core/text/porlin.hxx
+++ b/sw/source/core/text/porlin.hxx
@@ -54,6 +54,8 @@ private:
     SwTwips m_nExtraSpaceSize = 0;     // extra space over normal space width
     SwTwips m_nLetterSpacing = 0;      // letter spacing, TODO: add better 
resolution
     TextFrameIndex m_nSpaceCount;      // space count for letter spacing
+    SwTwips m_nScaleWidth = 100;       // glyph scaling (good resolution to 
limit font generation)
+    float m_fScaleWidthSpacing = 0.0;  // extra space filled by glyph scaling
 
     std::optional<SwLinePortionLayoutContext> m_nLayoutContext;
 
@@ -85,6 +87,10 @@ public:
     SwTwips GetLetterSpacing() const { return m_nLetterSpacing; }
     void SetLetterSpacing(const SwTwips nNew) { m_nLetterSpacing = nNew; }
     TextFrameIndex GetSpaceCount() const { return m_nSpaceCount; }
+    SwTwips GetScaleWidth() const { return m_nScaleWidth; }
+    void SetScaleWidth(const SwTwips nNew) { m_nScaleWidth = nNew; }
+    float GetScaleWidthSpacing() const { return m_fScaleWidthSpacing; }
+    void SetScaleWidthSpacing(const float fNew) { m_fScaleWidthSpacing = fNew; 
}
     void SetSpaceCount(TextFrameIndex const nSpaceCount) { m_nSpaceCount = 
nSpaceCount; }
     SwTwips GetHangingBaseline() const { return mnHangingBaseline; }
     void SetHangingBaseline( const SwTwips nNewBaseline ) { mnHangingBaseline 
= nNewBaseline; }
@@ -205,6 +211,8 @@ inline SwLinePortion &SwLinePortion::operator=(const 
SwLinePortion &rPortion)
     m_nExtraSpaceSize = rPortion.m_nExtraSpaceSize;
     m_nLetterSpacing = rPortion.m_nLetterSpacing;
     m_nSpaceCount = rPortion.m_nSpaceCount;
+    m_nScaleWidth = rPortion.m_nScaleWidth;
+    m_fScaleWidthSpacing = rPortion.m_fScaleWidthSpacing;
     m_nLayoutContext = rPortion.m_nLayoutContext;
     return *this;
 }
@@ -224,6 +232,8 @@ inline SwLinePortion::SwLinePortion(const SwLinePortion 
&rPortion) :
     m_nExtraSpaceSize(rPortion.m_nExtraSpaceSize),
     m_nLetterSpacing(rPortion.m_nLetterSpacing),
     m_nSpaceCount(rPortion.m_nSpaceCount),
+    m_nScaleWidth(rPortion.m_nScaleWidth),
+    m_fScaleWidthSpacing(rPortion.m_fScaleWidthSpacing),
     m_nLayoutContext(rPortion.m_nLayoutContext)
 {
 }
diff --git a/sw/source/core/text/portxt.cxx b/sw/source/core/text/portxt.cxx
index a8c4fac62a03..8e836ce1fc39 100644
--- a/sw/source/core/text/portxt.cxx
+++ b/sw/source/core/text/portxt.cxx
@@ -306,6 +306,8 @@ void SwTextPortion::BreakCut( SwTextFormatInfo &rInf, const 
SwTextGuess &rGuess
         ExtraShrunkWidth( 0 );
         ExtraSpaceSize( 0 );
         SetLetterSpacing( 0 );
+        SetScaleWidth( 100 );
+        SetScaleWidthSpacing( 0 );
     }
 }
 
@@ -317,6 +319,8 @@ void SwTextPortion::BreakUnderflow( SwTextFormatInfo &rInf )
     ExtraShrunkWidth( 0 );
     ExtraSpaceSize( 0 );
     SetLetterSpacing( 0 );
+    SetScaleWidth( 100 );
+    SetScaleWidthSpacing( 0 );
     SetLen( TextFrameIndex(0) );
     SetAscent( 0 );
     rInf.SetUnderflow( this );
@@ -359,9 +363,28 @@ void SwTextPortion::SetSpacing( SwTextFormatInfo &rInf, 
const TextFrameIndex nBr
     // final letter spacing/character based on the desired word spacing and 
maximum letter spacing
     // TODO fix resolution applying 1/100 twips instead of 1 twip
     SwTwips nLetterSpacing = std::min( fLetterSpacingForDesiredWordSpacing, 
fMaximumLetterSpacing );
+    // at shrinking by letter spacing, avoid overshrinking
+    if ( nLetterSpacing < 0 )
+    {
+        float fMinimumLetterSpacing =
+            nWidthOf10Spaces / 10.0 * 
aAdjustItem.GetPropLetterSpacingMinimum() / 100.0;
+        nLetterSpacing = std::max( fLetterSpacingForDesiredWordSpacing, 
fMinimumLetterSpacing );
+    }
     // full width of the extra (rounded) letter spacing within the line
+    // to adjust word spacing in SwTextAdjuster::CalcNewBlock()
     SetLetterSpacing( SwTwips(nLetterSpacing * nLetterCount) );
     SetSpaceCount( TextFrameIndex(nSpaces) );
+
+    // limit glyph scaling without optical sizing:
+    // apply it only after applying maximum letter spacing
+    // TODO: change this after variable font support
+    if ( aAdjustItem.GetPropScaleWidthMaximum() != 100 )
+        SetScaleWidth( 100.0 * rInf.GetLineWidth() / (rInf.GetBreakWidth() + 
GetLetterSpacing()) );
+    if ( GetScaleWidth() > aAdjustItem.GetPropScaleWidthMaximum() )
+        SetScaleWidth( aAdjustItem.GetPropScaleWidthMaximum() );
+    // space used by glyph scaling to adjust word spacing
+    if ( aAdjustItem.GetPropScaleWidthMaximum() != 100 )
+        SetScaleWidthSpacing( rInf.GetBreakWidth() * (GetScaleWidth() - 100.0) 
/ 100.0 );
 }
 
 bool SwTextPortion::Format_( SwTextFormatInfo &rInf )
@@ -391,6 +414,8 @@ bool SwTextPortion::Format_( SwTextFormatInfo &rInf )
     ExtraShrunkWidth( 0 );
     ExtraSpaceSize( 0 );
     SetLetterSpacing( 0 );
+    SetScaleWidth( 100 );
+    SetScaleWidthSpacing( 0 );
     std::optional<SwTextGuess> pGuess(std::in_place);
     bool bFull = !pGuess->Guess( *this, rInf, Height() );
 
@@ -406,6 +431,8 @@ bool SwTextPortion::Format_( SwTextFormatInfo &rInf )
     bool bNoWordSpacing = aAdjustItem.GetPropWordSpacing() == 100 &&
                     aAdjustItem.GetPropWordSpacingMinimum() == 100 &&
                     aAdjustItem.GetPropWordSpacingMaximum() == 100 &&
+                    aAdjustItem.GetPropScaleWidthMinimum() == 100 &&
+                    aAdjustItem.GetPropScaleWidthMaximum() == 100 &&
                     aAdjustItem.GetPropLetterSpacingMinimum() == 0 &&
                     aAdjustItem.GetPropLetterSpacingMaximum() == 0;
     // support old ODT documents, where only JustifyLinesWithShrinking was set
@@ -746,6 +773,8 @@ bool SwTextPortion::Format( SwTextFormatInfo &rInf )
         ExtraShrunkWidth( 0 );
         ExtraSpaceSize( 0 );
         SetLetterSpacing( 0 );
+        SetScaleWidth( 100 );
+        SetScaleWidthSpacing( 0 );
         SetLen( TextFrameIndex(0) );
         SetAscent( 0 );
         SetNextPortion( nullptr );  // ????
diff --git a/sw/source/core/txtnode/swfont.cxx 
b/sw/source/core/txtnode/swfont.cxx
index 0dd010370e2d..07dfef5d43c7 100644
--- a/sw/source/core/txtnode/swfont.cxx
+++ b/sw/source/core/txtnode/swfont.cxx
@@ -1140,6 +1140,13 @@ void SwSubFont::DrawText_( SwDrawTextInfo &rInf, const 
bool bGrey )
     if( !pLastFont || pLastFont->GetOwner() != m_nFontCacheId )
         ChgFnt( rInf.GetShell(), rInf.GetOut() );
 
+    // change glyph scaling, if needed
+    if ( GetPropWidth() != rInf.GetScaleWidth() )
+    {
+        SetPropWidth( rInf.GetScaleWidth() );
+        ChgFnt( rInf.GetShell(), rInf.GetOut() );
+    }
+
     SwDigitModeModifier aDigitModeModifier(rInf.GetOut(), 
rInf.GetFont()->GetLanguage(),
                                            
SwModule::get()->GetCTLTextNumerals());
 
diff --git a/sw/source/core/unocore/unomapproperties.hxx 
b/sw/source/core/unocore/unomapproperties.hxx
index 595c4aa40749..0e6ee62e52af 100644
--- a/sw/source/core/unocore/unomapproperties.hxx
+++ b/sw/source/core/unocore/unomapproperties.hxx
@@ -189,6 +189,8 @@
         { UNO_NAME_PARA_WORD_SPACING_MAX,               RES_PARATR_ADJUST,     
        cppu::UnoType<sal_Int16>::get(),         PropertyAttribute::MAYBEVOID, 
MID_WORD_SPACING_MAX                   }, \
         { UNO_NAME_PARA_LETTER_SPACING_MIN,             RES_PARATR_ADJUST,     
        cppu::UnoType<sal_Int16>::get(),         PropertyAttribute::MAYBEVOID, 
MID_LETTER_SPACING_MIN                 }, \
         { UNO_NAME_PARA_LETTER_SPACING_MAX,             RES_PARATR_ADJUST,     
        cppu::UnoType<sal_Int16>::get(),         PropertyAttribute::MAYBEVOID, 
MID_LETTER_SPACING_MAX                 }, \
+        { UNO_NAME_PARA_SCALE_WIDTH_MIN,                RES_PARATR_ADJUST,     
        cppu::UnoType<sal_Int16>::get(),         PropertyAttribute::MAYBEVOID, 
MID_SCALE_WIDTH_MIN                    }, \
+        { UNO_NAME_PARA_SCALE_WIDTH_MAX,                RES_PARATR_ADJUST,     
        cppu::UnoType<sal_Int16>::get(),         PropertyAttribute::MAYBEVOID, 
MID_SCALE_WIDTH_MAX                    }, \
         { UNO_NAME_PARA_LINE_NUMBER_COUNT,              RES_LINENUMBER,        
        cppu::UnoType<bool>::get(),       PropertyAttribute::MAYBEVOID, 
MID_LINENUMBER_COUNT                   }, \
         { UNO_NAME_PARA_LINE_NUMBER_START_VALUE,        RES_LINENUMBER,        
        cppu::UnoType<sal_Int32>::get(),         PropertyAttribute::MAYBEVOID, 
MID_LINENUMBER_STARTVALUE              }, \
         { UNO_NAME_PARA_LINE_SPACING,                   
RES_PARATR_LINESPACING,        cppu::UnoType<css::style::LineSpacing>::get(),   
  PropertyAttribute::MAYBEVOID, CONVERT_TWIPS                          }, \
@@ -458,6 +460,8 @@
                     { UNO_NAME_PARA_WORD_SPACING_MAX, RES_PARATR_ADJUST,      
cppu::UnoType<sal_Int16>::get(),       PROPERTY_NONE, MID_WORD_SPACING_MAX},\
                     { UNO_NAME_PARA_LETTER_SPACING_MIN, RES_PARATR_ADJUST,    
cppu::UnoType<sal_Int16>::get(),       PROPERTY_NONE, MID_LETTER_SPACING_MIN},\
                     { UNO_NAME_PARA_LETTER_SPACING_MAX, RES_PARATR_ADJUST,    
cppu::UnoType<sal_Int16>::get(),       PROPERTY_NONE, MID_LETTER_SPACING_MAX},\
+                    { UNO_NAME_PARA_SCALE_WIDTH_MIN, RES_PARATR_ADJUST,       
cppu::UnoType<sal_Int16>::get(),       PROPERTY_NONE, MID_SCALE_WIDTH_MIN},\
+                    { UNO_NAME_PARA_SCALE_WIDTH_MAX, RES_PARATR_ADJUST,       
cppu::UnoType<sal_Int16>::get(),       PROPERTY_NONE, MID_SCALE_WIDTH_MAX},\
                     { UNO_NAME_PARA_LINE_NUMBER_COUNT, RES_LINENUMBER,        
cppu::UnoType<bool>::get(),         PROPERTY_NONE ,MID_LINENUMBER_COUNT     },\
                     { UNO_NAME_PARA_LINE_NUMBER_START_VALUE, RES_LINENUMBER,   
   cppu::UnoType<sal_Int32>::get(),           PROPERTY_NONE 
,MID_LINENUMBER_STARTVALUE},\
                     { UNO_NAME_PARA_LINE_SPACING, RES_PARATR_LINESPACING, 
cppu::UnoType<css::style::LineSpacing>::get(),PROPERTY_NONE,     
CONVERT_TWIPS},\
diff --git a/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx 
b/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx
index 4e6b905ea2c1..33a27b191cac 100644
--- a/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx
+++ b/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx
@@ -303,6 +303,8 @@ static OUString PropertyNametoRID(const OUString& rName)
         { "ParaRegisterModeActive", RID_PARA_REGISTER_MODE_ACTIVE },
         { "ParaRightMargin", RID_PARA_RIGHT_MARGIN },
         { "ParaRightMarginRelative", RID_PARA_RIGHT_MARGIN_RELATIVE },
+        { "ParaScaleWidthMinimum", RID_PARA_SCALE_WIDTH_MIN },
+        { "ParaScaleWidthMaximum", RID_PARA_SCALE_WIDTH_MAX },
         { "ParaShadowFormat", RID_PARA_SHADOW_FORMAT },
         { "ParaSplit", RID_PARA_SPLIT },
         { "ParaStyleName", RID_PARA_STYLE_NAME },
diff --git a/xmloff/inc/xmlprop.hxx b/xmloff/inc/xmlprop.hxx
index b82ed64e0a6d..657ddf1eeae8 100644
--- a/xmloff/inc/xmlprop.hxx
+++ b/xmloff/inc/xmlprop.hxx
@@ -537,6 +537,8 @@ inline constexpr OUString PROP_ParaShadowFormat = 
u"ParaShadowFormat"_ustr;
 inline constexpr OUString PROP_ParaSplit = u"ParaSplit"_ustr;
 inline constexpr OUString PROP_ParaTabStops = u"ParaTabStops"_ustr;
 inline constexpr OUString PROP_ParaTabStopDefaultDistance = 
u"ParaTabStopDefaultDistance"_ustr;
+inline constexpr OUString PROP_ParaScaleWidthMinimum = 
u"ParaScaleWidthMinimum"_ustr;
+inline constexpr OUString PROP_ParaScaleWidthMaximum = 
u"ParaScaleWidthMaximum"_ustr;
 inline constexpr OUString PROP_ParaTopMargin = u"ParaTopMargin"_ustr;
 inline constexpr OUString PROP_ParaTopMarginRelative = 
u"ParaTopMarginRelative"_ustr;
 inline constexpr OUString PROP_ParaUserDefinedAttributes = 
u"ParaUserDefinedAttributes"_ustr;
diff --git a/xmloff/source/core/xmltoken.cxx b/xmloff/source/core/xmltoken.cxx
index 8f678d44fbfb..4b8c63d5936c 100644
--- a/xmloff/source/core/xmltoken.cxx
+++ b/xmloff/source/core/xmltoken.cxx
@@ -2034,6 +2034,8 @@ namespace xmloff::token {
         TOKEN( "text-rotation-angle",             XML_TEXT_ROTATION_ANGLE ),
         TOKEN( "text-rotation-scale",             XML_TEXT_ROTATION_SCALE ),
         TOKEN( "text-scale",                      XML_TEXT_SCALE ),
+        TOKEN( "text-scale-maximum",              XML_TEXT_SCALE_MAXIMUM ),
+        TOKEN( "text-scale-minimum",              XML_TEXT_SCALE_MINIMUM ),
         TOKEN( "text-shadow",                     XML_TEXT_SHADOW ),
         TOKEN( "text-style",                      XML_TEXT_STYLE ),
         TOKEN( "text-transform",                  XML_TEXT_TRANSFORM ),
diff --git a/xmloff/source/style/xmlexppr.cxx b/xmloff/source/style/xmlexppr.cxx
index 552f0dde8f51..803c441b7b3e 100644
--- a/xmloff/source/style/xmlexppr.cxx
+++ b/xmloff/source/style/xmlexppr.cxx
@@ -947,10 +947,12 @@ sal_Int8 CheckExtendedNamespace(std::u16string_view 
sXMLAttributeName, std::u16s
         || IsXMLToken(sXMLAttributeName, XML_HYPHENATION_ZONE_PAGE)
         || IsXMLToken(sXMLAttributeName, XML_HYPHENATION_ZONE_SPREAD))
         return IsXMLToken(sValue, XML_NO_LIMIT) ? -1 : 1;
-    // don't export word spacing when they have the default 100% value
+    // don't export word spacing and glyph scaling when they have the default 
100% value
     else if (IsXMLToken(sXMLAttributeName, XML_WORD_SPACING)
         || IsXMLToken(sXMLAttributeName, XML_WORD_SPACING_MINIMUM)
-        || IsXMLToken(sXMLAttributeName, XML_WORD_SPACING_MAXIMUM))
+        || IsXMLToken(sXMLAttributeName, XML_WORD_SPACING_MAXIMUM)
+        || IsXMLToken(sXMLAttributeName, XML_TEXT_SCALE_MINIMUM)
+        || IsXMLToken(sXMLAttributeName, XML_TEXT_SCALE_MAXIMUM))
     {
         static constexpr OUString s100PercentCompare( u"100%"_ustr );
         size_t nBegin = sValue.find( s100PercentCompare );
diff --git a/xmloff/source/text/txtprmap.cxx b/xmloff/source/text/txtprmap.cxx
index db2c0ad64fdf..593006cf0ec3 100644
--- a/xmloff/source/text/txtprmap.cxx
+++ b/xmloff/source/text/txtprmap.cxx
@@ -487,6 +487,8 @@ XMLPropertyMapEntry constexpr aXMLParaPropMap[] =
     MAP_EXT( PROP_ParaWordSpacingMaximum, XML_NAMESPACE_LO_EXT, 
XML_WORD_SPACING_MAXIMUM, XML_TYPE_PROP_PARAGRAPH|XML_TYPE_PERCENT16, 0 ),
     MAP_EXT( PROP_ParaLetterSpacingMinimum, XML_NAMESPACE_LO_EXT, 
XML_LETTER_SPACING_MINIMUM, XML_TYPE_PROP_PARAGRAPH|XML_TYPE_PERCENT16, 0 ),
     MAP_EXT( PROP_ParaLetterSpacingMaximum, XML_NAMESPACE_LO_EXT, 
XML_LETTER_SPACING_MAXIMUM, XML_TYPE_PROP_PARAGRAPH|XML_TYPE_PERCENT16, 0 ),
+    MAP_EXT( PROP_ParaScaleWidthMinimum, XML_NAMESPACE_LO_EXT, 
XML_TEXT_SCALE_MINIMUM, XML_TYPE_PROP_PARAGRAPH|XML_TYPE_PERCENT16, 0 ),
+    MAP_EXT( PROP_ParaScaleWidthMaximum, XML_NAMESPACE_LO_EXT, 
XML_TEXT_SCALE_MAXIMUM, XML_TYPE_PROP_PARAGRAPH|XML_TYPE_PERCENT16, 0 ),
 
     MT_ED( PROP_CharScriptHint, XML_NAMESPACE_STYLE, XML_SCRIPT_TYPE, 
XML_TYPE_TEXT_SCRIPT_TYPE|MID_FLAG_MERGE_PROPERTY, 0 ),
 
diff --git a/xmloff/source/token/tokens.txt b/xmloff/source/token/tokens.txt
index 979779e54eb1..9576187b7522 100644
--- a/xmloff/source/token/tokens.txt
+++ b/xmloff/source/token/tokens.txt
@@ -1934,6 +1934,8 @@ text-position
 text-rotation-angle
 text-rotation-scale
 text-scale
+text-scale-maximum
+text-scale-minimum
 text-shadow
 text-style
 text-transform

Reply via email to