cui/source/inc/paragrph.hxx                                 |    8 
 cui/source/tabpages/paragrph.cxx                            |  140 +++++++
 cui/uiconfig/ui/textflowpage.ui                             |  205 ++++++++--
 editeng/source/items/paraitem.cxx                           |   56 ++-
 include/editeng/editrids.hrc                                |    4 
 include/editeng/hyphenzoneitem.hxx                          |   16 
 include/editeng/memberids.h                                 |   12 
 include/unotools/linguprops.hxx                             |   16 
 include/xmloff/xmltoken.hxx                                 |    4 
 offapi/com/sun/star/style/ParagraphProperties.idl           |   28 +
 schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng |   48 ++
 svx/sdi/svxitems.sdi                                        |    4 
 sw/inc/inspectorproperties.hrc                              |    4 
 sw/inc/unoprnms.hxx                                         |    4 
 sw/qa/extras/layout/data/tdf165984.fodt                     |  224 ++++++++++++
 sw/qa/extras/layout/layout3.cxx                             |   32 +
 sw/qa/uitest/styleInspector/styleInspector.py               |   20 -
 sw/qa/uitest/styleInspector/tdf137513.py                    |    2 
 sw/source/core/inc/txtfrm.hxx                               |    5 
 sw/source/core/text/guess.cxx                               |   86 +++-
 sw/source/core/text/inftxt.cxx                              |   16 
 sw/source/core/text/txtfrm.cxx                              |    1 
 sw/source/core/text/widorp.cxx                              |   54 ++
 sw/source/core/unocore/unomapproperties.hxx                 |    8 
 sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx       |    4 
 xmloff/inc/xmlprop.hxx                                      |    4 
 xmloff/source/core/xmltoken.cxx                             |    4 
 xmloff/source/text/txtprmap.cxx                             |    4 
 xmloff/source/token/tokens.txt                              |    4 
 29 files changed, 917 insertions(+), 100 deletions(-)

New commits:
commit 7d384fb1c232f7aa720436bc68dc1de334bf7297
Author:     László Németh <[email protected]>
AuthorDate: Tue Mar 25 12:21:48 2025 +0100
Commit:     László Németh <[email protected]>
CommitDate: Tue Apr 1 17:52:46 2025 +0200

    tdf#165984 sw cui xmloff: add Paragraph/Column/Page/Spread end zone
    
    Add new hyphenation options to adjust hyphenation, like DTP software do
    in accordance with typographic requirements:
    
    – Paragraph/Column/Page/Spread end zone spin boxes to Hyphenate Across
      section of Text Flow pane of paragraph settings;
    
    – loext:hyphenation-zone-always, loext:hyphenation-zone-column,
      loext:hyphenation-zone-page and loext:hyphenation-zone-spread
      ODF extensions;
    
    – widorp.cxx, guess.cxx: handle Column/Page/Spread end zones;
    
    – guess.cxx: handle Paragraph end zone (without portions, yet).
    
    According to their typography, languages with long words need better
    adjustment of line break of the last full paragraph/column/page/spread
    lines, than simply disabling hyphenation by the recent ODF hyphenation-
    keep or its equivalent MS Word OOXML extension. DTP software use
    paragraph and column/page/spread end zones to adjust the hyphenation
    zones in the last lines, for example, setting Column end zone to the
    150% percent of the Hyphenation zone, resulting less hyphenation, also
    less short words parts at the end of the columns, pages and spreads,
    increasing readability.
    
    Note: the default OOXML hyphenation zone 18 pt (or its MS Word variants,
    for example 21.25 pt) disables single letter or narrow 2-letter (li-,
    fi-, i.e. fi ligature) hyphenations at 12 pt font height. Setting 27 pt
    for Column end zone, all 2-letter hyphenation is disabled in last lines
    of columns and pages.
    
    Note: textflow.ui needs multiple adjustment definitions for correct work
    of the multiple spin boxes.
    
    Change-Id: I37bbb4da30de0ec75eb8636f70d8e16588c56ed1
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/183565
    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 e8806dcdac7c..5dab4d9e12cd 100644
--- a/cui/source/inc/paragrph.hxx
+++ b/cui/source/inc/paragrph.hxx
@@ -246,6 +246,14 @@ private:
     std::unique_ptr<weld::SpinButton> m_xMinWordLength;
     std::unique_ptr<weld::Label> m_xHyphenZoneLabel;
     SvxRelativeField m_aHyphenZone;
+    std::unique_ptr<weld::Label> m_xParagraphEndZoneLabel;
+    SvxRelativeField m_aParagraphEndZone;
+    std::unique_ptr<weld::Label> m_xColumnEndZoneLabel;
+    SvxRelativeField m_aColumnEndZone;
+    std::unique_ptr<weld::Label> m_xPageEndZoneLabel;
+    SvxRelativeField m_aPageEndZone;
+    std::unique_ptr<weld::Label> m_xSpreadEndZoneLabel;
+    SvxRelativeField m_aSpreadEndZone;
 
     // pagebreak
     std::unique_ptr<weld::CheckButton> m_xPageBreakBox;
diff --git a/cui/source/tabpages/paragrph.cxx b/cui/source/tabpages/paragrph.cxx
index b5c7bcd147fc..5db7ce43b474 100644
--- a/cui/source/tabpages/paragrph.cxx
+++ b/cui/source/tabpages/paragrph.cxx
@@ -1666,6 +1666,10 @@ bool SvxExtParagraphTabPage::FillItemSet( SfxItemSet* 
rOutSet )
          m_xMaxHyphenEdit->get_value_changed_from_saved() ||
          m_xMinWordLength->get_value_changed_from_saved() ||
          m_aHyphenZone.get_value_changed_from_saved() ||
+         m_aParagraphEndZone.get_value_changed_from_saved() ||
+         m_aColumnEndZone.get_value_changed_from_saved() ||
+         m_aPageEndZone.get_value_changed_from_saved() ||
+         m_aSpreadEndZone.get_value_changed_from_saved() ||
          m_xAcrossParagraphBox->get_state_changed_from_saved() ||
          m_xAcrossColumnBox->get_state_changed_from_saved() ||
          m_xAcrossPageBox->get_state_changed_from_saved() ||
@@ -1691,6 +1695,10 @@ bool SvxExtParagraphTabPage::FillItemSet( SfxItemSet* 
rOutSet )
         DBG_ASSERT( pPool, "Where is the pool?" );
         MapUnit eUnit = pPool->GetMetric( _nWhich );
         aHyphen.GetTextHyphenZone() = 
static_cast<sal_uInt16>(m_aHyphenZone.GetCoreValue(eUnit));
+        aHyphen.GetTextHyphenZoneAlways() = 
static_cast<sal_uInt16>(m_aParagraphEndZone.GetCoreValue(eUnit));
+        aHyphen.GetTextHyphenZoneColumn() = 
static_cast<sal_uInt16>(m_aColumnEndZone.GetCoreValue(eUnit));
+        aHyphen.GetTextHyphenZonePage() = 
static_cast<sal_uInt16>(m_aPageEndZone.GetCoreValue(eUnit));
+        aHyphen.GetTextHyphenZoneSpread() = 
static_cast<sal_uInt16>(m_aSpreadEndZone.GetCoreValue(eUnit));
         aHyphen.SetHyphen( eHyphenState == TRISTATE_TRUE );
         aHyphen.SetNoLastWordHyphenation(m_xHyphenNoLastWordBox->get_state() 
!= TRISTATE_TRUE);
         const TriState eAcrossParagraphState = 
m_xAcrossParagraphBox->get_state();
@@ -1943,6 +1951,26 @@ void SvxExtParagraphTabPage::Reset( const SfxItemSet* 
rSet )
         m_xMinWordLength->set_value(rHyphen.GetMinWordLength());
         m_aHyphenZone.SetFieldUnit(eFUnit);
         m_aHyphenZone.SetMetricValue(rHyphen.GetTextHyphenZone(), 
MapUnit::MapTwip);
+        m_aParagraphEndZone.SetFieldUnit(eFUnit);
+        m_aParagraphEndZone.SetMetricValue(rHyphen.GetTextHyphenZoneAlways(), 
MapUnit::MapTwip);
+        // 0 means inheritance, mark this with empty box
+        if ( !rHyphen.GetTextHyphenZoneAlways() )
+            m_aParagraphEndZone.set_text(OUString());
+        m_aColumnEndZone.SetFieldUnit(eFUnit);
+        m_aColumnEndZone.SetMetricValue(rHyphen.GetTextHyphenZoneColumn(), 
MapUnit::MapTwip);
+        // 0 means inheritance, mark this with empty box
+        if ( !rHyphen.GetTextHyphenZoneColumn() )
+            m_aColumnEndZone.set_text(OUString());
+        m_aPageEndZone.SetFieldUnit(eFUnit);
+        m_aPageEndZone.SetMetricValue(rHyphen.GetTextHyphenZonePage(), 
MapUnit::MapTwip);
+        // 0 means inheritance, mark this with empty box
+        if ( !rHyphen.GetTextHyphenZonePage() )
+            m_aPageEndZone.set_text(OUString());
+        m_aSpreadEndZone.SetFieldUnit(eFUnit);
+        m_aSpreadEndZone.SetMetricValue(rHyphen.GetTextHyphenZoneSpread(), 
MapUnit::MapTwip);
+        // 0 means inheritance, mark this with empty box
+        if ( !rHyphen.GetTextHyphenZoneSpread() )
+            m_aSpreadEndZone.set_text(OUString());
         m_xAcrossParagraphBox->set_state(!rHyphen.IsKeep() || 
rHyphen.GetKeepType() < 4 ? TRISTATE_TRUE : TRISTATE_FALSE);
         m_xAcrossColumnBox->set_state(!rHyphen.IsKeep() || 
rHyphen.GetKeepType() < 3 ? TRISTATE_TRUE : TRISTATE_FALSE);
         m_xAcrossPageBox->set_state(!rHyphen.IsKeep() || rHyphen.GetKeepType() 
< 2 ? TRISTATE_TRUE : TRISTATE_FALSE);
@@ -1976,11 +2004,32 @@ void SvxExtParagraphTabPage::Reset( const SfxItemSet* 
rSet )
     m_xMinWordLength->set_sensitive(bEnable);
     m_xHyphenZoneLabel->set_sensitive(bEnable);
     m_aHyphenZone.set_sensitive(bEnable);
+    m_xParagraphEndZoneLabel->set_sensitive(bEnable);
+    m_aParagraphEndZone.set_sensitive(bEnable);
+    m_xColumnEndZoneLabel->set_sensitive(bEnable);
+    m_aColumnEndZone.set_sensitive(bEnable);
+    m_xPageEndZoneLabel->set_sensitive(bEnable);
+    m_aPageEndZone.set_sensitive(bEnable);
+    m_xSpreadEndZoneLabel->set_sensitive(bEnable);
+    m_aSpreadEndZone.set_sensitive(bEnable);
     m_xAcrossText->set_sensitive(bEnable);
     m_xAcrossParagraphBox->set_sensitive(bEnable);
     m_xAcrossColumnBox->set_sensitive(bEnable);
     m_xAcrossPageBox->set_sensitive(bEnable);
     m_xAcrossSpreadBox->set_sensitive(bEnable);
+    // gray out end zones, if hyphenation across is disabled
+    bool bAcross = bEnable && m_xAcrossParagraphBox->get_state() == 
TRISTATE_TRUE;
+    m_xParagraphEndZoneLabel->set_sensitive(bAcross);
+    m_aParagraphEndZone.set_sensitive(bAcross);
+    bAcross = bEnable && m_xAcrossColumnBox->get_state() == TRISTATE_TRUE;
+    m_xColumnEndZoneLabel->set_sensitive(bAcross);
+    m_aColumnEndZone.set_sensitive(bAcross);
+    bAcross = bEnable && m_xAcrossPageBox->get_state() == TRISTATE_TRUE;
+    m_xPageEndZoneLabel->set_sensitive(bAcross);
+    m_aPageEndZone.set_sensitive(bAcross);
+    bAcross = bEnable && m_xAcrossSpreadBox->get_state() == TRISTATE_TRUE;
+    m_xSpreadEndZoneLabel->set_sensitive(bAcross);
+    m_aSpreadEndZone.set_sensitive(bAcross);
     // always gray out MoveLine, if hyphenation is forbidden across spreads
     bool bAcrossSpread = m_xAcrossSpreadBox->get_state() == TRISTATE_TRUE;
     m_xAcrossMoveLineBox->set_sensitive( bEnable && !bAcrossSpread );
@@ -2247,6 +2296,10 @@ void SvxExtParagraphTabPage::ChangesApplied()
     m_xMaxHyphenEdit->save_value();
     m_xMinWordLength->save_value();
     m_aHyphenZone.save_value();
+    m_aParagraphEndZone.save_value();
+    m_aColumnEndZone.save_value();
+    m_aPageEndZone.save_value();
+    m_aSpreadEndZone.save_value();
     m_xAcrossParagraphBox->save_state();
     m_xAcrossColumnBox->save_state();
     m_xAcrossPageBox->save_state();
@@ -2307,6 +2360,14 @@ 
SvxExtParagraphTabPage::SvxExtParagraphTabPage(weld::Container* pPage, weld::Dia
     , m_xMinWordLength(m_xBuilder->weld_spin_button(u"spinMinLen"_ustr))
     , m_xHyphenZoneLabel(m_xBuilder->weld_label(u"labelHyphenZone"_ustr))
     , 
m_aHyphenZone(m_xBuilder->weld_metric_spin_button(u"spinHyphenZone"_ustr, 
FieldUnit::CM))
+    , 
m_xParagraphEndZoneLabel(m_xBuilder->weld_label(u"labelParagraphEndZone"_ustr))
+    , 
m_aParagraphEndZone(m_xBuilder->weld_metric_spin_button(u"spinParagraphEndZone"_ustr,
 FieldUnit::CM))
+    , m_xColumnEndZoneLabel(m_xBuilder->weld_label(u"labelColumnEndZone"_ustr))
+    , 
m_aColumnEndZone(m_xBuilder->weld_metric_spin_button(u"spinColumnEndZone"_ustr, 
FieldUnit::CM))
+    , m_xPageEndZoneLabel(m_xBuilder->weld_label(u"labelPageEndZone"_ustr))
+    , 
m_aPageEndZone(m_xBuilder->weld_metric_spin_button(u"spinPageEndZone"_ustr, 
FieldUnit::CM))
+    , m_xSpreadEndZoneLabel(m_xBuilder->weld_label(u"labelSpreadEndZone"_ustr))
+    , 
m_aSpreadEndZone(m_xBuilder->weld_metric_spin_button(u"spinSpreadEndZone"_ustr, 
FieldUnit::CM))
     //Page break
     , m_xPageBreakBox(m_xBuilder->weld_check_button(u"checkInsert"_ustr))
     , m_xBreakTypeFT(m_xBuilder->weld_label(u"labelType"_ustr))
@@ -2392,6 +2453,14 @@ 
SvxExtParagraphTabPage::SvxExtParagraphTabPage(weld::Container* pPage, weld::Dia
     m_xMinWordLength->set_sensitive(false);
     m_xHyphenZoneLabel->set_sensitive(false);
     m_aHyphenZone.set_sensitive(false);
+    m_xParagraphEndZoneLabel->set_sensitive(false);
+    m_aParagraphEndZone.set_sensitive(false);
+    m_xColumnEndZoneLabel->set_sensitive(false);
+    m_aColumnEndZone.set_sensitive(false);
+    m_xPageEndZoneLabel->set_sensitive(false);
+    m_aPageEndZone.set_sensitive(false);
+    m_xSpreadEndZoneLabel->set_sensitive(false);
+    m_aSpreadEndZone.set_sensitive(false);
     m_xPageNumBox->set_sensitive(false);
     m_xPagenumEdit->set_sensitive(false);
     m_xAcrossText->set_sensitive(false);
@@ -2538,9 +2607,21 @@ void SvxExtParagraphTabPage::HyphenClickHdl()
     m_aHyphenZone.set_sensitive(bEnable);
     m_xAcrossText->set_sensitive(bEnable);
     m_xAcrossParagraphBox->set_sensitive(bEnable);
+    bool bAcross = bEnable && m_xAcrossParagraphBox->get_state() == 
TRISTATE_TRUE;
+    m_xParagraphEndZoneLabel->set_sensitive(bAcross);
+    m_aParagraphEndZone.set_sensitive(bAcross);
     m_xAcrossColumnBox->set_sensitive(bEnable);
+    bAcross = bEnable && m_xAcrossColumnBox->get_state() == TRISTATE_TRUE;
+    m_xColumnEndZoneLabel->set_sensitive(bAcross);
+    m_aColumnEndZone.set_sensitive(bAcross);
     m_xAcrossPageBox->set_sensitive(bEnable);
+    bAcross = bEnable && m_xAcrossPageBox->get_state() == TRISTATE_TRUE;
+    m_xPageEndZoneLabel->set_sensitive(bAcross);
+    m_aPageEndZone.set_sensitive(bAcross);
     m_xAcrossSpreadBox->set_sensitive(bEnable);
+    bAcross = bEnable && m_xAcrossSpreadBox->get_state() == TRISTATE_TRUE;
+    m_xSpreadEndZoneLabel->set_sensitive(bAcross);
+    m_aSpreadEndZone.set_sensitive(bAcross);
     // only sensitive, if the hyphenation is disabled across spreads
     m_xAcrossMoveLineBox->set_sensitive( m_xAcrossSpreadBox->get_state() != 
TRISTATE_TRUE );
     m_xHyphenBox->set_state(bEnable ? TRISTATE_TRUE : TRISTATE_FALSE);
@@ -2653,6 +2734,19 @@ IMPL_LINK(SvxExtParagraphTabPage, 
AcrossParagraphHdl_Impl, weld::Toggleable&, rT
         m_xAcrossPageBox->set_state( TRISTATE_FALSE );
         m_xAcrossSpreadBox->set_state( TRISTATE_FALSE );
         m_xAcrossMoveLineBox->set_sensitive( true );
+        m_xParagraphEndZoneLabel->set_sensitive(false);
+        m_aParagraphEndZone.set_sensitive(false);
+        m_xColumnEndZoneLabel->set_sensitive(false);
+        m_aColumnEndZone.set_sensitive(false);
+        m_xPageEndZoneLabel->set_sensitive(false);
+        m_aPageEndZone.set_sensitive(false);
+        m_xSpreadEndZoneLabel->set_sensitive(false);
+        m_aSpreadEndZone.set_sensitive(false);
+    }
+    else
+    {
+        m_xParagraphEndZoneLabel->set_sensitive(true);
+        m_aParagraphEndZone.set_sensitive(true);
     }
 }
 
@@ -2664,9 +2758,21 @@ IMPL_LINK(SvxExtParagraphTabPage, AcrossColumnHdl_Impl, 
weld::Toggleable&, rTogg
         m_xAcrossPageBox->set_state( TRISTATE_FALSE );
         m_xAcrossSpreadBox->set_state( TRISTATE_FALSE );
         m_xAcrossMoveLineBox->set_sensitive( true );
+        m_xColumnEndZoneLabel->set_sensitive(false);
+        m_aColumnEndZone.set_sensitive(false);
+        m_xPageEndZoneLabel->set_sensitive(false);
+        m_aPageEndZone.set_sensitive(false);
+        m_xSpreadEndZoneLabel->set_sensitive(false);
+        m_aSpreadEndZone.set_sensitive(false);
     }
     else
+    {
         m_xAcrossParagraphBox->set_state( TRISTATE_TRUE );
+        m_xParagraphEndZoneLabel->set_sensitive(true);
+        m_aParagraphEndZone.set_sensitive(true);
+        m_xColumnEndZoneLabel->set_sensitive(true);
+        m_aColumnEndZone.set_sensitive(true);
+    }
 }
 
 IMPL_LINK(SvxExtParagraphTabPage, AcrossPageHdl_Impl, weld::Toggleable&, 
rToggle, void)
@@ -2676,11 +2782,23 @@ IMPL_LINK(SvxExtParagraphTabPage, AcrossPageHdl_Impl, 
weld::Toggleable&, rToggle
     {
         m_xAcrossParagraphBox->set_state( TRISTATE_TRUE );
         m_xAcrossColumnBox->set_state( TRISTATE_TRUE );
+        m_xParagraphEndZoneLabel->set_sensitive(true);
+        m_aParagraphEndZone.set_sensitive(true);
+        m_xColumnEndZoneLabel->set_sensitive(true);
+        m_aColumnEndZone.set_sensitive(true);
+        m_xPageEndZoneLabel->set_sensitive(true);
+        m_aPageEndZone.set_sensitive(true);
+        m_xSpreadEndZoneLabel->set_sensitive(false);
+        m_aSpreadEndZone.set_sensitive(false);
     }
     else
     {
         m_xAcrossSpreadBox->set_state( TRISTATE_FALSE );
         m_xAcrossMoveLineBox->set_sensitive( true );
+        m_xPageEndZoneLabel->set_sensitive(false);
+        m_aPageEndZone.set_sensitive(false);
+        m_xSpreadEndZoneLabel->set_sensitive(false);
+        m_aSpreadEndZone.set_sensitive(false);
     }
 }
 
@@ -2692,12 +2810,24 @@ IMPL_LINK(SvxExtParagraphTabPage, AcrossSpreadHdl_Impl, 
weld::Toggleable&, rTogg
         m_xAcrossParagraphBox->set_state( TRISTATE_TRUE );
         m_xAcrossColumnBox->set_state( TRISTATE_TRUE );
         m_xAcrossPageBox->set_state( TRISTATE_TRUE );
-    }
-    // only sensitive, if the hyphenation is disabled across spreads
-    bool bAcrossSpread = m_xAcrossSpreadBox->get_state() == TRISTATE_TRUE;
-    m_xAcrossMoveLineBox->set_sensitive( !bAcrossSpread );
-    if ( bAcrossSpread )
+        m_xParagraphEndZoneLabel->set_sensitive(true);
+        m_aParagraphEndZone.set_sensitive(true);
+        m_xColumnEndZoneLabel->set_sensitive(true);
+        m_aColumnEndZone.set_sensitive(true);
+        m_xPageEndZoneLabel->set_sensitive(true);
+        m_aPageEndZone.set_sensitive(true);
+        m_xSpreadEndZoneLabel->set_sensitive(true);
+        m_aSpreadEndZone.set_sensitive(true);
+        m_xAcrossMoveLineBox->set_sensitive( false );
         m_xAcrossMoveLineBox->set_state( TRISTATE_FALSE );
+    }
+    else
+    {
+        m_xSpreadEndZoneLabel->set_sensitive(false);
+        m_aSpreadEndZone.set_sensitive(false);
+        // only sensitive, if the hyphenation is disabled across spreads
+        m_xAcrossMoveLineBox->set_sensitive( true );
+    }
 }
 
 SvxAsianTabPage::SvxAsianTabPage(weld::Container* pPage, 
weld::DialogController* pController, const SfxItemSet& rSet)
diff --git a/cui/uiconfig/ui/textflowpage.ui b/cui/uiconfig/ui/textflowpage.ui
index 6434b4096035..b279a312b12c 100644
--- a/cui/uiconfig/ui/textflowpage.ui
+++ b/cui/uiconfig/ui/textflowpage.ui
@@ -60,6 +60,26 @@
     <property name="step-increment">1</property>
     <property name="page-increment">10</property>
   </object>
+  <object class="GtkAdjustment" id="adjustment10">
+    <property name="upper">55.88</property>
+    <property name="step-increment">1</property>
+    <property name="page-increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="adjustment11">
+    <property name="upper">55.88</property>
+    <property name="step-increment">1</property>
+    <property name="page-increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="adjustment12">
+    <property name="upper">55.88</property>
+    <property name="step-increment">1</property>
+    <property name="page-increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="adjustment13">
+    <property name="upper">55.88</property>
+    <property name="step-increment">1</property>
+    <property name="page-increment">10</property>
+  </object>
   <!-- n-columns=2 n-rows=2 -->
   <object class="GtkGrid" id="TextFlowPage">
     <property name="visible">True</property>
@@ -347,39 +367,33 @@
                 <property name="width">2</property>
               </packing>
             </child>
-            <child>
-              <!-- n-columns=2 n-rows=1 -->
-              <object class="GtkGrid" id="gridHyphenAcrossLabel">
-                <property name="visible">True</property>
-                <property name="can-focus">False</property>
-                <property name="margin-start">12</property>
-                <property name="margin-top">6</property>
-                <property name="row-spacing">6</property>
-                <property name="column-spacing">12</property>
-                <child>
-                  <object class="GtkLabel" id="labelHyphenAcross">
-                    <property name="visible">True</property>
-                    <property name="can-focus">False</property>
-                    <property name="label" translatable="yes" 
context="textflowpage|labelHyphenAcross">Hyphenation across</property>
-                    <property name="use-underline">True</property>
-                    <property 
name="mnemonic-widget">labelHyphenAcross</property>
-                    <property name="xalign">0</property>
-                    <attributes>
-                      <attribute name="weight" value="bold"/>
-                    </attributes>
-                  </object>
-                  <packing>
-                    <property name="left-attach">0</property>
-                    <property name="top-attach">0</property>
-                  </packing>
-                </child>
-              </object>
-              <packing>
-                <property name="left-attach">0</property>
-                <property name="top-attach">10</property>
-                <property name="width">2</property>
-              </packing>
-            </child>
+          </object>
+        </child>
+        <child type="label">
+          <object class="GtkLabel" id="LabelHyphenation">
+            <property name="visible">True</property>
+            <property name="can-focus">False</property>
+            <property name="label" translatable="yes" 
context="textflowpage|LabelHyphenation">Hyphenation</property>
+            <attributes>
+              <attribute name="weight" value="bold"/>
+            </attributes>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="left-attach">0</property>
+        <property name="top-attach">0</property>
+        <property name="height">2</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkFrame" id="FrameHyphenationAcross">
+        <property name="visible">True</property>
+        <property name="can-focus">False</property>
+        <property name="events">GDK_EXPOSURE_MASK</property>
+        <property name="label-xalign">0</property>
+        <property name="shadow-type">none</property>
+          <!-- n-columns=2 n-rows=8 -->
             <child>
               <!-- n-columns=2 n-rows=1 -->
               <object class="GtkGrid" id="gridHyphenAcross">
@@ -469,6 +483,114 @@
                     <property name="top-attach">3</property>
                   </packing>
                 </child>
+                <child>
+                  <object class="GtkSpinButton" id="spinParagraphEndZone">
+                    <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">adjustment10</property>
+                  </object>
+                  <packing>
+                    <property name="left-attach">2</property>
+                    <property name="top-attach">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="labelParagraphEndZone">
+                    <property name="visible">True</property>
+                    <property name="can-focus">False</property>
+                    <property name="label" translatable="yes" 
context="textflowpage|labelParagraphEndZone">Paragraph end _zone:</property>
+                    <property name="use-underline">True</property>
+                    <property 
name="mnemonic-widget">spinParagraphEndZone</property>
+                    <property name="xalign">0</property>
+                  </object>
+                  <packing>
+                    <property name="left-attach">1</property>
+                    <property name="top-attach">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkSpinButton" id="spinColumnEndZone">
+                    <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">adjustment11</property>
+                  </object>
+                  <packing>
+                    <property name="left-attach">2</property>
+                    <property name="top-attach">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="labelColumnEndZone">
+                    <property name="visible">True</property>
+                    <property name="can-focus">False</property>
+                    <property name="label" translatable="yes" 
context="textflowpage|labelColumnEndZone">Column end _zone:</property>
+                    <property name="use-underline">True</property>
+                    <property 
name="mnemonic-widget">spinColumnEndZone</property>
+                    <property name="xalign">0</property>
+                  </object>
+                  <packing>
+                    <property name="left-attach">1</property>
+                    <property name="top-attach">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkSpinButton" id="spinPageEndZone">
+                    <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">adjustment12</property>
+                  </object>
+                  <packing>
+                    <property name="left-attach">2</property>
+                    <property name="top-attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="labelPageEndZone">
+                    <property name="visible">True</property>
+                    <property name="can-focus">False</property>
+                    <property name="label" translatable="yes" 
context="textflowpage|labelPageEndZone">Page end _zone:</property>
+                    <property name="use-underline">True</property>
+                    <property name="mnemonic-widget">spinPageEndZone</property>
+                    <property name="xalign">0</property>
+                  </object>
+                  <packing>
+                    <property name="left-attach">1</property>
+                    <property name="top-attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkSpinButton" id="spinSpreadEndZone">
+                    <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">adjustment13</property>
+                  </object>
+                  <packing>
+                    <property name="left-attach">2</property>
+                    <property name="top-attach">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="labelSpreadEndZone">
+                    <property name="visible">True</property>
+                    <property name="can-focus">False</property>
+                    <property name="label" translatable="yes" 
context="textflowpage|labelSpreadEndZone">Spread end _zone:</property>
+                    <property name="use-underline">True</property>
+                    <property 
name="mnemonic-widget">spinSpreadEndZone</property>
+                    <property name="xalign">0</property>
+                  </object>
+                  <packing>
+                    <property name="left-attach">1</property>
+                    <property name="top-attach">3</property>
+                  </packing>
+                </child>
                 <child>
                   <object class="GtkCheckButton" id="checkMoveLine">
                     <property name="label" translatable="yes" 
context="textflowpage|checkMoveLine">Move Line</property>
@@ -485,24 +607,17 @@
                     </child>
                   </object>
                   <packing>
-                    <property name="left-attach">1</property>
+                    <property name="left-attach">3</property>
                     <property name="top-attach">3</property>
                   </packing>
                 </child>
               </object>
-              <packing>
-                <property name="left-attach">0</property>
-                <property name="top-attach">11</property>
-                <property name="width">2</property>
-              </packing>
             </child>
-          </object>
-        </child>
         <child type="label">
-          <object class="GtkLabel" id="LabelHyphenation">
+          <object class="GtkLabel" id="labelHyphenAcross">
             <property name="visible">True</property>
             <property name="can-focus">False</property>
-            <property name="label" translatable="yes" 
context="textflowpage|LabelHyphenation">Hyphenation</property>
+            <property name="label" translatable="yes" 
context="textflowpage|labelHyphenAcross">Hyphenation across</property>
             <attributes>
               <attribute name="weight" value="bold"/>
             </attributes>
@@ -511,8 +626,8 @@
       </object>
       <packing>
         <property name="left-attach">0</property>
-        <property name="top-attach">0</property>
-        <property name="height">2</property>
+        <property name="top-attach">2</property>
+        <property name="width">2</property>
       </packing>
     </child>
     <child>
diff --git a/editeng/source/items/paraitem.cxx 
b/editeng/source/items/paraitem.cxx
index 91c30e5b55e1..dc209a85e3b2 100644
--- a/editeng/source/items/paraitem.cxx
+++ b/editeng/source/items/paraitem.cxx
@@ -593,6 +593,10 @@ SvxHyphenZoneItem::SvxHyphenZoneItem( const bool bHyph, 
const sal_uInt16 nId ) :
     nMaxHyphens(255),
     nMinWordLength(0),
     nTextHyphenZone(0),
+    nTextHyphenZoneAlways(0),
+    nTextHyphenZoneColumn(0),
+    nTextHyphenZonePage(0),
+    nTextHyphenZoneSpread(0),
     nKeepType(css::text::ParagraphHyphenationKeepType::COLUMN),
     bKeepLine(false),
     nCompoundMinLead(0)
@@ -632,6 +636,18 @@ bool    SvxHyphenZoneItem::QueryValue( uno::Any& rVal, 
sal_uInt8 nMemberId ) con
         case MID_HYPHEN_ZONE:
             rVal <<= static_cast<sal_Int16>(nTextHyphenZone);
         break;
+        case MID_HYPHEN_ZONE_ALWAYS:
+            rVal <<= static_cast<sal_Int16>(nTextHyphenZoneAlways);
+        break;
+        case MID_HYPHEN_ZONE_COLUMN:
+            rVal <<= static_cast<sal_Int16>(nTextHyphenZoneColumn);
+        break;
+        case MID_HYPHEN_ZONE_PAGE:
+            rVal <<= static_cast<sal_Int16>(nTextHyphenZonePage);
+        break;
+        case MID_HYPHEN_ZONE_SPREAD:
+            rVal <<= static_cast<sal_Int16>(nTextHyphenZoneSpread);
+        break;
         case MID_HYPHEN_KEEP_TYPE:
             rVal <<= static_cast<sal_Int16>(nKeepType);
         break;
@@ -687,6 +703,18 @@ bool SvxHyphenZoneItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
         case MID_HYPHEN_ZONE:
             nTextHyphenZone = nNewVal;
         break;
+        case MID_HYPHEN_ZONE_ALWAYS:
+            nTextHyphenZoneAlways = nNewVal;
+        break;
+        case MID_HYPHEN_ZONE_COLUMN:
+            nTextHyphenZoneColumn = nNewVal;
+        break;
+        case MID_HYPHEN_ZONE_PAGE:
+            nTextHyphenZonePage = nNewVal;
+        break;
+        case MID_HYPHEN_ZONE_SPREAD:
+            nTextHyphenZoneSpread = nNewVal;
+        break;
         case MID_HYPHEN_KEEP_TYPE:
             nKeepType = static_cast<sal_uInt8>(nNewVal);
         break;
@@ -716,6 +744,10 @@ bool SvxHyphenZoneItem::operator==( const SfxPoolItem& 
rAttr ) const
             && rItem.nMaxHyphens == nMaxHyphens
             && rItem.nMinWordLength == nMinWordLength
             && rItem.nTextHyphenZone == nTextHyphenZone
+            && rItem.nTextHyphenZoneAlways == nTextHyphenZoneAlways
+            && rItem.nTextHyphenZoneColumn == nTextHyphenZoneColumn
+            && rItem.nTextHyphenZonePage == nTextHyphenZonePage
+            && rItem.nTextHyphenZoneSpread == nTextHyphenZoneSpread
             && rItem.bKeepLine == bKeepLine
             && rItem.nKeepType == nKeepType );
 }
@@ -748,6 +780,14 @@ bool SvxHyphenZoneItem::GetPresentation
                     OUString::number( nMaxHyphens ) + cpDelimTmp +
                     OUString::number( nMinWordLength ) + cpDelimTmp +
                     GetMetricText( nTextHyphenZone, eCoreUnit, ePresUnit, 
&rIntl ) +
+                        " " + EditResId(GetMetricId(ePresUnit)) +
+                    GetMetricText( nTextHyphenZoneAlways, eCoreUnit, 
ePresUnit, &rIntl ) +
+                        " " + EditResId(GetMetricId(ePresUnit)) +
+                    GetMetricText( nTextHyphenZoneColumn, eCoreUnit, 
ePresUnit, &rIntl ) +
+                        " " + EditResId(GetMetricId(ePresUnit)) +
+                    GetMetricText( nTextHyphenZonePage, eCoreUnit, ePresUnit, 
&rIntl ) +
+                        " " + EditResId(GetMetricId(ePresUnit)) +
+                    GetMetricText( nTextHyphenZoneSpread, eCoreUnit, 
ePresUnit, &rIntl ) +
                         " " + EditResId(GetMetricId(ePresUnit));
 
             if ( bNoCapsHyphenation )
@@ -789,11 +829,25 @@ bool SvxHyphenZoneItem::GetPresentation
                     cpDelimTmp +
                     EditResId(RID_SVXITEMS_HYPHEN_MINWORDLEN).replaceAll("%1", 
OUString::number(nMinWordLength));
 
-            if ( nTextHyphenZone > 0 )
+            if ( nTextHyphenZone > 0 || nTextHyphenZoneAlways > 0 ||
+                 nTextHyphenZoneColumn > 0 || nTextHyphenZonePage > 0 ||
+                 nTextHyphenZoneSpread > 0 )
             {
                 rText += cpDelimTmp + EditResId(RID_SVXITEMS_HYPHEN_ZONE) +
                         GetMetricText( nTextHyphenZone, eCoreUnit, ePresUnit, 
&rIntl ) +
                         " " + EditResId(GetMetricId(ePresUnit));
+                rText += cpDelimTmp + 
EditResId(RID_SVXITEMS_HYPHEN_ZONE_ALWAYS) +
+                        GetMetricText( nTextHyphenZoneAlways, eCoreUnit, 
ePresUnit, &rIntl ) +
+                        " " + EditResId(GetMetricId(ePresUnit));
+                rText += cpDelimTmp + 
EditResId(RID_SVXITEMS_HYPHEN_ZONE_COLUMN) +
+                        GetMetricText( nTextHyphenZoneColumn, eCoreUnit, 
ePresUnit, &rIntl ) +
+                        " " + EditResId(GetMetricId(ePresUnit));
+                rText += cpDelimTmp + EditResId(RID_SVXITEMS_HYPHEN_ZONE_PAGE) 
+
+                        GetMetricText( nTextHyphenZonePage, eCoreUnit, 
ePresUnit, &rIntl ) +
+                        " " + EditResId(GetMetricId(ePresUnit));
+                rText += cpDelimTmp + 
EditResId(RID_SVXITEMS_HYPHEN_ZONE_SPREAD) +
+                        GetMetricText( nTextHyphenZoneSpread, eCoreUnit, 
ePresUnit, &rIntl ) +
+                        " " + EditResId(GetMetricId(ePresUnit));
             }
 
             if ( bNoCapsHyphenation )
diff --git a/include/editeng/editrids.hrc b/include/editeng/editrids.hrc
index 4cdc87bcf856..d0872f0a88a7 100644
--- a/include/editeng/editrids.hrc
+++ b/include/editeng/editrids.hrc
@@ -238,6 +238,10 @@
 #define RID_SVXITEMS_HYPHEN_LAST_WORD_TRUE      
NC_("RID_SVXITEMS_HYPHEN_NO_CAPS_FALSE", "Not hyphenated last word")
 #define RID_SVXITEMS_HYPHEN_MINWORDLEN          
NC_("RID_SVXITEMS_HYPHEN_MINWORDLEN", "%1 characters in words")
 #define RID_SVXITEMS_HYPHEN_ZONE                
NC_("RID_SVXITEMS_HYPHEN_ZONE", "Hyphenation zone ")
+#define RID_SVXITEMS_HYPHEN_ZONE_ALWAYS         
NC_("RID_SVXITEMS_HYPHEN_ZONE_ALWAYS", "Paragraph end zone ")
+#define RID_SVXITEMS_HYPHEN_ZONE_COLUMN         
NC_("RID_SVXITEMS_HYPHEN_ZONE_COLUMN", "Column end zone ")
+#define RID_SVXITEMS_HYPHEN_ZONE_PAGE           
NC_("RID_SVXITEMS_HYPHEN_ZONE_PAGE", "Page end zone ")
+#define RID_SVXITEMS_HYPHEN_ZONE_SPREAD         
NC_("RID_SVXITEMS_HYPHEN_ZONE_SPREAD", "Spread end zone ")
 #define RID_SVXITEMS_HYPHEN_KEEP_AUTO           
NC_("RID_SVXITEMS_HYPHEN_KEEP_AUTO", "Hyphenation across page")
 #define RID_SVXITEMS_HYPHEN_KEEP_SPREAD         
NC_("RID_SVXITEMS_HYPHEN_KEEP_SPREAD", "Avoid hyphenation across page when the 
next page is not visible to the reader at the same time")
 #define RID_SVXITEMS_HYPHEN_KEEP_PAGE           
NC_("RID_SVXITEMS_HYPHEN_KEEP_PAGE", "Avoid hyphenation across page")
diff --git a/include/editeng/hyphenzoneitem.hxx 
b/include/editeng/hyphenzoneitem.hxx
index 05d3c9145ab8..84f4ac333ecc 100644
--- a/include/editeng/hyphenzoneitem.hxx
+++ b/include/editeng/hyphenzoneitem.hxx
@@ -41,6 +41,10 @@ class EDITENG_DLLPUBLIC SvxHyphenZoneItem final : public 
SfxPoolItem
     sal_uInt8 nMaxHyphens;      // max. consecutive lines with hyphenation
     sal_uInt8 nMinWordLength;   // hyphenate only words with at least 
nMinWordLength characters
     sal_uInt16 nTextHyphenZone; // don't force hyphenation at line end, allow 
this extra white space
+    sal_uInt16 nTextHyphenZoneAlways; // don't force hyphenation at paragraph 
end, allow this extra white space
+    sal_uInt16 nTextHyphenZoneColumn; // don't force hyphenation at column 
end, allow this extra white space
+    sal_uInt16 nTextHyphenZonePage;   // don't force hyphenation at page end, 
allow this extra white space
+    sal_uInt16 nTextHyphenZoneSpread; // don't force hyphenation at spread 
end, allow this extra white space
     sal_uInt8 nKeepType;        // avoid hyphenation across page etc., see 
ParagraphHyphenationKeep
     bool      bKeepLine : 1;    // if bKeep, shift the hyphenated word (true), 
or the full line
     sal_uInt8 nCompoundMinLead; // min. characters between compound word 
boundary and hyphenation
@@ -93,6 +97,18 @@ public:
     sal_uInt16 &GetTextHyphenZone() { return nTextHyphenZone; }
     sal_uInt16 GetTextHyphenZone() const { return nTextHyphenZone; }
 
+    sal_uInt16 &GetTextHyphenZoneAlways() { return nTextHyphenZoneAlways; }
+    sal_uInt16 GetTextHyphenZoneAlways() const { return nTextHyphenZoneAlways; 
}
+
+    sal_uInt16 &GetTextHyphenZoneColumn() { return nTextHyphenZoneColumn; }
+    sal_uInt16 GetTextHyphenZoneColumn() const { return nTextHyphenZoneColumn; 
}
+
+    sal_uInt16 &GetTextHyphenZonePage() { return nTextHyphenZonePage; }
+    sal_uInt16 GetTextHyphenZonePage() const { return nTextHyphenZonePage; }
+
+    sal_uInt16 &GetTextHyphenZoneSpread() { return nTextHyphenZoneSpread; }
+    sal_uInt16 GetTextHyphenZoneSpread() const { return nTextHyphenZoneSpread; 
}
+
     sal_uInt8 &GetKeepType() { return nKeepType; }
     sal_uInt8 GetKeepType() const { return nKeepType; }
 
diff --git a/include/editeng/memberids.h b/include/editeng/memberids.h
index f07d051c4825..1bb3d24912dc 100644
--- a/include/editeng/memberids.h
+++ b/include/editeng/memberids.h
@@ -51,10 +51,14 @@
 #define MID_HYPHEN_NO_LAST_WORD 5
 #define MID_HYPHEN_MIN_WORD_LENGTH 6
 #define MID_HYPHEN_ZONE         7
-#define MID_HYPHEN_KEEP         8
-#define MID_HYPHEN_KEEP_TYPE    9
-#define MID_HYPHEN_COMPOUND_MIN_LEAD 10
-#define MID_HYPHEN_KEEP_LINE    11
+#define MID_HYPHEN_ZONE_ALWAYS  8
+#define MID_HYPHEN_ZONE_COLUMN  9
+#define MID_HYPHEN_ZONE_PAGE    10
+#define MID_HYPHEN_ZONE_SPREAD  11
+#define MID_HYPHEN_KEEP         12
+#define MID_HYPHEN_KEEP_TYPE    13
+#define MID_HYPHEN_COMPOUND_MIN_LEAD 14
+#define MID_HYPHEN_KEEP_LINE    15
 
 // SvxBoxInfoItem
 #define MID_HORIZONTAL          1
diff --git a/include/unotools/linguprops.hxx b/include/unotools/linguprops.hxx
index 0451a0d94a27..dd57dc089d05 100644
--- a/include/unotools/linguprops.hxx
+++ b/include/unotools/linguprops.hxx
@@ -42,6 +42,10 @@ inline constexpr OUString UPN_HYPH_MIN_WORD_LENGTH           
 = u"HyphMinWordLen
 inline constexpr OUString UPN_HYPH_NO_CAPS                    = 
u"HyphNoCaps"_ustr;
 inline constexpr OUString UPN_HYPH_NO_LAST_WORD               = 
u"HyphNoLastWord"_ustr;
 inline constexpr OUString UPN_HYPH_ZONE                       = 
u"HyphZone"_ustr;
+inline constexpr OUString UPN_HYPH_ZONE_ALWAYS                = 
u"HyphZoneAlways"_ustr;
+inline constexpr OUString UPN_HYPH_ZONE_COLUMN                = 
u"HyphZoneColumn"_ustr;
+inline constexpr OUString UPN_HYPH_ZONE_PAGE                  = 
u"HyphZonePage"_ustr;
+inline constexpr OUString UPN_HYPH_ZONE_SPREAD                = 
u"HyphZoneSpread"_ustr;
 inline constexpr OUString UPN_HYPH_KEEP                       = 
u"HyphKeep"_ustr;
 inline constexpr OUString UPN_HYPH_KEEP_TYPE                  = 
u"HyphKeepType"_ustr;
 inline constexpr OUString UPN_HYPH_KEEP_LINE                  = 
u"HyphKeepLine"_ustr;
@@ -111,10 +115,14 @@ inline constexpr OUString UPN_IS_GRAMMAR_INTERACTIVE      
    = u"IsInteractiveG
 #define UPH_HYPH_NO_CAPS                    31
 #define UPH_HYPH_NO_LAST_WORD               32
 #define UPH_HYPH_ZONE                       33
-#define UPH_HYPH_KEEP                       34
-#define UPH_HYPH_KEEP_TYPE                  35
-#define UPH_HYPH_COMPOUND_MIN_LEADING       36
-#define UPH_HYPH_KEEP_LINE                  37
+#define UPH_HYPH_ZONE_ALWAYS                34
+#define UPH_HYPH_ZONE_COLUMN                35
+#define UPH_HYPH_ZONE_PAGE                  36
+#define UPH_HYPH_ZONE_SPREAD                37
+#define UPH_HYPH_KEEP                       38
+#define UPH_HYPH_KEEP_TYPE                  39
+#define UPH_HYPH_COMPOUND_MIN_LEADING       40
+#define UPH_HYPH_KEEP_LINE                  41
 
 #ifdef __GNUC__
 #pragma GCC diagnostic pop
diff --git a/include/xmloff/xmltoken.hxx b/include/xmloff/xmltoken.hxx
index 3dadf9fa10fe..1c36a3faf4a0 100644
--- a/include/xmloff/xmltoken.hxx
+++ b/include/xmloff/xmltoken.hxx
@@ -1069,6 +1069,10 @@ namespace xmloff::token {
         XML_HYPHENATION_NO_LAST_WORD,
         XML_HYPHENATION_WORD_CHAR_COUNT,
         XML_HYPHENATION_ZONE,
+        XML_HYPHENATION_ZONE_ALWAYS,
+        XML_HYPHENATION_ZONE_COLUMN,
+        XML_HYPHENATION_ZONE_PAGE,
+        XML_HYPHENATION_ZONE_SPREAD,
         XML_HYPHENATION_COMPOUND_REMAIN_CHAR_COUNT,
         XML_I,
         XML_ICON,
diff --git a/offapi/com/sun/star/style/ParagraphProperties.idl 
b/offapi/com/sun/star/style/ParagraphProperties.idl
index 5b1097feae04..645dfc091e3c 100644
--- a/offapi/com/sun/star/style/ParagraphProperties.idl
+++ b/offapi/com/sun/star/style/ParagraphProperties.idl
@@ -466,6 +466,34 @@ published service ParagraphProperties
             @since LibreOffice 25.8
          */
         [optional, property] boolean ParaHyphenationKeepLine;
+
+        /** specifies the paragraph end zone, i.e. allowed extra white space
+            in the last full line of paragraph before applying hyphenation.
+
+            @since LibreOffice 25.8
+         */
+        [optional, property] long ParaHyphenationZoneAlways;
+
+        /** specifies the column end zone, i.e. allowed extra white space
+            in the last line of columns before applying hyphenation.
+
+            @since LibreOffice 25.8
+         */
+        [optional, property] long ParaHyphenationZoneColumn;
+
+        /** specifies the page end zone, i.e. allowed extra white space
+            in the last line of page before applying hyphenation.
+
+            @since LibreOffice 25.8
+         */
+        [optional, property] long ParaHyphenationZonePage;
+
+        /** specifies the spread end zone, i.e. allowed extra white space
+            in the last line of spread before applying hyphenation.
+
+            @since LibreOffice 25.8
+         */
+        [optional, property] long ParaHyphenationZoneSpread;
 };
 
 
diff --git a/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng 
b/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng
index dfc2bba4af45..5c201b4c95b7 100644
--- a/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng
+++ b/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng
@@ -3202,6 +3202,54 @@ 
xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.
     </rng:optional>
   </rng:define>
 
+  <!-- TODO no proposal -->
+  <rng:define name="style-text-properties-attlist" combine="interleave">
+    <rng:optional>
+      <rng:attribute name="loext:hyphenation-zone-always">
+        <rng:choice>
+          <rng:value>no-limit</rng:value>
+          <rng:ref name="positiveInteger"/>
+        </rng:choice>
+      </rng:attribute>
+    </rng:optional>
+  </rng:define>
+
+  <!-- TODO no proposal -->
+  <rng:define name="style-text-properties-attlist" combine="interleave">
+    <rng:optional>
+      <rng:attribute name="loext:hyphenation-zone-column">
+        <rng:choice>
+          <rng:value>no-limit</rng:value>
+          <rng:ref name="positiveInteger"/>
+        </rng:choice>
+      </rng:attribute>
+    </rng:optional>
+  </rng:define>
+
+  <!-- TODO no proposal -->
+  <rng:define name="style-text-properties-attlist" combine="interleave">
+    <rng:optional>
+      <rng:attribute name="loext:hyphenation-zone-page">
+        <rng:choice>
+          <rng:value>no-limit</rng:value>
+          <rng:ref name="positiveInteger"/>
+        </rng:choice>
+      </rng:attribute>
+    </rng:optional>
+  </rng:define>
+
+  <!-- TODO no proposal -->
+  <rng:define name="style-text-properties-attlist" combine="interleave">
+    <rng:optional>
+      <rng:attribute name="loext:hyphenation-zone-spread">
+        <rng:choice>
+          <rng:value>no-limit</rng:value>
+          <rng:ref name="positiveInteger"/>
+        </rng:choice>
+      </rng:attribute>
+    </rng:optional>
+  </rng:define>
+
   <!-- TODO no proposal -->
   <rng:define name="chart-data-point-attlist" combine="interleave">
     <rng:optional>
diff --git a/svx/sdi/svxitems.sdi b/svx/sdi/svxitems.sdi
index 0415b6955dd9..0810b70e2fa2 100644
--- a/svx/sdi/svxitems.sdi
+++ b/svx/sdi/svxitems.sdi
@@ -275,6 +275,10 @@ struct SvxHyphenZone
     INT16       MaxHyphens    MID_HYPHEN_MAX_HYPHENS;
     INT16       MinWordLength MID_HYPHEN_MIN_WORD_LENGTH;
     INT16       HyphenZone    MID_HYPHEN_ZONE;
+    INT16       HyphenZoneAlways  MID_HYPHEN_ZONE_ALWAYS;
+    INT16       HyphenZoneColumn  MID_HYPHEN_ZONE_COLUMN;
+    INT16       HyphenZonePage    MID_HYPHEN_ZONE_PAGE;
+    INT16       HyphenZoneSpread  MID_HYPHEN_ZONE_SPREAD;
     BOOL        HyphenKeep    MID_HYPHEN_KEEP;
     INT16       HyphenKeepType MID_HYPHEN_KEEP_TYPE;
     BOOL        HyphenKeepLine MID_HYPHEN_KEEP_LINE;
diff --git a/sw/inc/inspectorproperties.hrc b/sw/inc/inspectorproperties.hrc
index 3404018a8c19..faecb2c06e64 100644
--- a/sw/inc/inspectorproperties.hrc
+++ b/sw/inc/inspectorproperties.hrc
@@ -210,6 +210,10 @@
 #define RID_PARA_HYPHENATION_NO_LAST_WORD                   
NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Hyphenation No Last Word")
 #define RID_PARA_HYPHENATION_MIN_WORD_LENGTH                
NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Hyphenation Min Word Length")
 #define RID_PARA_HYPHENATION_ZONE                           
NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Hyphenation Zone")
+#define RID_PARA_HYPHENATION_ZONE_ALWAYS                    
NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Hyphenation Paragraph End Zone")
+#define RID_PARA_HYPHENATION_ZONE_COLUMN                    
NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Hyphenation Column End Zone")
+#define RID_PARA_HYPHENATION_ZONE_PAGE                      
NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Hyphenation Page End Zone")
+#define RID_PARA_HYPHENATION_ZONE_SPREAD                    
NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Hyphenation Spread End Zone")
 #define RID_PARA_HYPHENATION_KEEP                           
NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Hyphenation Keep")
 #define RID_PARA_HYPHENATION_KEEP_TYPE                      
NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Hyphenation Keep Type")
 #define RID_PARA_HYPHENATION_KEEP_LINE                      
NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Hyphenation Keep Line")
diff --git a/sw/inc/unoprnms.hxx b/sw/inc/unoprnms.hxx
index 82bd0b806c93..1cbac3d0a733 100644
--- a/sw/inc/unoprnms.hxx
+++ b/sw/inc/unoprnms.hxx
@@ -78,6 +78,10 @@ inline constexpr OUString 
UNO_NAME_PARA_HYPHENATION_MAX_HYPHENS = u"ParaHyphenat
 inline constexpr OUString UNO_NAME_PARA_HYPHENATION_MIN_WORD_LENGTH
     = u"ParaHyphenationMinWordLength"_ustr;
 inline constexpr OUString UNO_NAME_PARA_HYPHENATION_ZONE = 
u"ParaHyphenationZone"_ustr;
+inline constexpr OUString UNO_NAME_PARA_HYPHENATION_ZONE_ALWAYS = 
u"ParaHyphenationZoneAlways"_ustr;
+inline constexpr OUString UNO_NAME_PARA_HYPHENATION_ZONE_COLUMN = 
u"ParaHyphenationZoneColumn"_ustr;
+inline constexpr OUString UNO_NAME_PARA_HYPHENATION_ZONE_PAGE = 
u"ParaHyphenationZonePage"_ustr;
+inline constexpr OUString UNO_NAME_PARA_HYPHENATION_ZONE_SPREAD = 
u"ParaHyphenationZoneSpread"_ustr;
 inline constexpr OUString UNO_NAME_PARA_HYPHENATION_NO_CAPS = 
u"ParaHyphenationNoCaps"_ustr;
 inline constexpr OUString UNO_NAME_PARA_HYPHENATION_NO_LAST_WORD
     = u"ParaHyphenationNoLastWord"_ustr;
diff --git a/sw/qa/extras/layout/data/tdf165984.fodt 
b/sw/qa/extras/layout/data/tdf165984.fodt
new file mode 100644
index 000000000000..8bda4cc1a2dc
--- /dev/null
+++ b/sw/qa/extras/layout/data/tdf165984.fodt
@@ -0,0 +1,224 @@
+<?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:settings>
+  <config:config-item-set config:name="ooo:view-settings">
+   <config:config-item config:name="ViewAreaTop" 
config:type="long">0</config:config-item>
+   <config:config-item config:name="ViewAreaLeft" 
config:type="long">0</config:config-item>
+   <config:config-item config:name="ViewAreaWidth" 
config:type="long">68698</config:config-item>
+   <config:config-item config:name="ViewAreaHeight" 
config:type="long">38887</config:config-item>
+   <config:config-item config:name="ShowRedlineChanges" 
config:type="boolean">true</config:config-item>
+   <config:config-item config:name="InBrowseMode" 
config:type="boolean">false</config:config-item>
+   <config:config-item-map-indexed config:name="Views">
+    <config:config-item-map-entry>
+     <config:config-item config:name="ViewId" 
config:type="string">view2</config:config-item>
+     <config:config-item config:name="ViewLeft" 
config:type="long">36348</config:config-item>
+     <config:config-item config:name="ViewTop" 
config:type="long">14429</config:config-item>
+     <config:config-item config:name="VisibleLeft" 
config:type="long">0</config:config-item>
+     <config:config-item config:name="VisibleTop" 
config:type="long">0</config:config-item>
+     <config:config-item config:name="VisibleRight" 
config:type="long">68696</config:config-item>
+     <config:config-item config:name="VisibleBottom" 
config:type="long">38885</config:config-item>
+     <config:config-item config:name="ZoomType" 
config:type="short">0</config:config-item>
+     <config:config-item config:name="ViewLayoutColumns" 
config:type="short">2</config:config-item>
+     <config:config-item config:name="ViewLayoutBookMode" 
config:type="boolean">true</config:config-item>
+     <config:config-item config:name="ZoomFactor" 
config:type="short">56</config:config-item>
+     <config:config-item config:name="IsSelectedFrame" 
config:type="boolean">false</config:config-item>
+     <config:config-item config:name="KeepRatio" 
config:type="boolean">false</config:config-item>
+     <config:config-item config:name="AnchoredTextOverflowLegacy" 
config:type="boolean">false</config:config-item>
+     <config:config-item config:name="LegacySingleLineFontwork" 
config:type="boolean">false</config:config-item>
+     <config:config-item config:name="ConnectorUseSnapRect" 
config:type="boolean">false</config:config-item>
+     <config:config-item config:name="IgnoreBreakAfterMultilineField" 
config:type="boolean">false</config:config-item>
+    </config:config-item-map-entry>
+   </config:config-item-map-indexed>
+  </config:config-item-set>
+  <config:config-item-set config:name="ooo:configuration-settings">
+   <config:config-item config:name="PrintProspectRTL" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="PrintPageBackground" 
config:type="boolean">true</config:config-item>
+   <config:config-item config:name="PrintDrawings" 
config:type="boolean">true</config:config-item>
+   <config:config-item config:name="PrintFaxName" config:type="string"/>
+   <config:config-item config:name="PrintReversed" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="PrintAnnotationMode" 
config:type="short">0</config:config-item>
+   <config:config-item config:name="PrintHiddenText" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="PrintEmptyPages" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="MsWordCompGridMetrics" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="PaintHellOverHeaderFooter" 
config:type="boolean">false</config:config-item>
+   <config:config-item 
config:name="ApplyParagraphMarkFormatToEmptyLineAtEndOfParagraph" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="PrintGraphics" 
config:type="boolean">true</config:config-item>
+   <config:config-item config:name="NoNumberingShowFollowBy" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="MinRowHeightInclBorder" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="HyphenateURLs" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="ImagePreferredDPI" 
config:type="int">0</config:config-item>
+   <config:config-item config:name="FootnoteInColumnToPageEnd" 
config:type="boolean">true</config:config-item>
+   <config:config-item config:name="GutterAtTop" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="ContinuousEndnotes" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="MsWordUlTrailSpace" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="FrameAutowidthWithMorePara" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="SubtractFlysAnchoredAtFlys" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="SurroundTextWrapSmall" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="PrintControls" 
config:type="boolean">true</config:config-item>
+   <config:config-item config:name="TreatSingleColumnBreakAsPageBreak" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="TabOverSpacing" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="AutoFirstLineIndentDisregardLineSpace" 
config:type="boolean">true</config:config-item>
+   <config:config-item config:name="TabOverMargin" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="EmbedComplexScriptFonts" 
config:type="boolean">true</config:config-item>
+   <config:config-item config:name="EmbedAsianScriptFonts" 
config:type="boolean">true</config:config-item>
+   <config:config-item config:name="PrintTextPlaceholder" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="ApplyTextAttrToEmptyLineAtEndOfParagraph" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="EmbedSystemFonts" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="EmbedFonts" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="NoClippingWithWrapPolygon" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="DisableOffPagePositioning" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="BackgroundParaOverDrawings" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="TabOverflow" 
config:type="boolean">true</config:config-item>
+   <config:config-item config:name="SmallCapsPercentage66" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="AllowPrintJobCancel" 
config:type="boolean">true</config:config-item>
+   <config:config-item config:name="AddVerticalFrameOffsets" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="ProtectBookmarks" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="AddFrameOffsets" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="PrintBlackFonts" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="TableRowKeep" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="ApplyParagraphMarkFormatToNumbering" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="PrinterIndependentLayout" 
config:type="string">high-resolution</config:config-item>
+   <config:config-item config:name="JustifyLinesWithShrinking" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="RsidRoot" 
config:type="int">681377</config:config-item>
+   <config:config-item config:name="PrintProspect" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="CollapseEmptyCellPara" 
config:type="boolean">true</config:config-item>
+   <config:config-item config:name="PrintPaperFromSetup" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="CurrentDatabaseCommand" 
config:type="string"/>
+   <config:config-item config:name="CurrentDatabaseDataSource" 
config:type="string"/>
+   <config:config-item config:name="SaveThumbnail" 
config:type="boolean">true</config:config-item>
+   <config:config-item config:name="EmbeddedDatabaseName" 
config:type="string"/>
+   <config:config-item config:name="UnbreakableNumberings" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="SaveGlobalDocumentLinks" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="MsWordCompTrailingBlanks" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="PrintTables" 
config:type="boolean">true</config:config-item>
+   <config:config-item config:name="PrintLeftPages" 
config:type="boolean">true</config:config-item>
+   <config:config-item config:name="AddParaTableSpacing" 
config:type="boolean">true</config:config-item>
+   <config:config-item config:name="PrinterPaperFromSetup" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="CurrentDatabaseCommandType" 
config:type="int">0</config:config-item>
+   <config:config-item config:name="ChartAutoUpdate" 
config:type="boolean">true</config:config-item>
+   <config:config-item config:name="OutlineLevelYieldsNumbering" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="UseOldNumbering" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="AddParaSpacingToTableCells" 
config:type="boolean">true</config:config-item>
+   <config:config-item config:name="FieldAutoUpdate" 
config:type="boolean">true</config:config-item>
+   <config:config-item config:name="PropLineSpacingShrinksFirstLine" 
config:type="boolean">true</config:config-item>
+   <config:config-item config:name="TabAtLeftIndentForParagraphsInList" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="PrintRightPages" 
config:type="boolean">true</config:config-item>
+   <config:config-item config:name="DoNotCaptureDrawObjsOnPage" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="LoadReadonly" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="HeaderSpacingBelowLastPara" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="SaveVersionOnClose" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="UseFormerLineSpacing" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="PrinterName" config:type="string"/>
+   <config:config-item config:name="AddParaLineSpacingToTableCells" 
config:type="boolean">true</config:config-item>
+   <config:config-item config:name="IsKernAsianPunctuation" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="DoNotJustifyLinesWithManualBreak" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="CharacterCompressionType" 
config:type="short">0</config:config-item>
+   <config:config-item config:name="IsLabelDocument" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="EmbedLatinScriptFonts" 
config:type="boolean">true</config:config-item>
+   <config:config-item config:name="PrinterSetup" config:type="base64Binary"/>
+   <config:config-item config:name="UseVariableWidthNBSP" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="EmbedOnlyUsedFonts" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="ApplyUserData" 
config:type="boolean">true</config:config-item>
+   <config:config-item config:name="AddParaTableSpacingAtStart" 
config:type="boolean">true</config:config-item>
+   <config:config-item config:name="InvertBorderSpacing" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="ProtectFields" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="AddExternalLeading" 
config:type="boolean">true</config:config-item>
+   <config:config-item config:name="LinkUpdateMode" 
config:type="short">1</config:config-item>
+   <config:config-item config:name="UseFormerObjectPositioning" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="UnxForceZeroExtLeading" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="UseFormerTextWrapping" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="ConsiderTextWrapOnObjPos" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="StylesNoDefault" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="IgnoreFirstLineIndentInNumbering" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="NoGapAfterNoteNumber" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="EmptyDbFieldHidesPara" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="DoNotResetParaAttrsForNumFont" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="IgnoreTabsAndBlanksForLineCalculation" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="ClippedPictures" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="MathBaselineAlignment" 
config:type="boolean">true</config:config-item>
+   <config:config-item config:name="AlignTabStopPosition" 
config:type="boolean">true</config:config-item>
+   <config:config-item config:name="IgnoreHiddenCharsForLineCalculation" 
config:type="boolean">true</config:config-item>
+   <config:config-item config:name="ClipAsCharacterAnchoredWriterFlyFrames" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="DropCapPunctuation" 
config:type="boolean">true</config:config-item>
+   <config:config-item config:name="UseOldPrinterMetrics" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="RedlineProtectionKey" 
config:type="base64Binary"/>
+   <config:config-item config:name="TabsRelativeToIndent" 
config:type="boolean">true</config:config-item>
+   <config:config-item config:name="Rsid" 
config:type="int">906067</config:config-item>
+   <config:config-item config:name="UpdateFromTemplate" 
config:type="boolean">true</config:config-item>
+   <config:config-item config:name="ProtectForm" 
config:type="boolean">false</config:config-item>
+   <config:config-item config:name="MsWordCompMinLineHeightByFly" 
config:type="boolean">false</config:config-item>
+  </config:config-item-set>
+ </office:settings>
+ <office:font-face-decls>
+  <style:font-face style:name="Liberation Sans" 
svg:font-family="&apos;Liberation Sans&apos;" style:font-family-generic="swiss" 
style:font-pitch="variable"/>
+  <style:font-face style:name="Times New Roman" svg:font-family="&apos;Times 
New Roman&apos;" style:font-family-generic="swiss"/>
+ </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="Times New Roman" fo:font-size="12pt" 
fo:language="en" fo:country="US" style:letter-kerning="true"        />
+  </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="Times New Roman" fo:font-size="12pt" 
fo:language="en" fo:country="US" style:letter-kerning="true"         
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="5" 
loext:hyphenation-zone="no-limit" loext:hyphenation-zone-always="no-limit" 
loext:hyphenation-zone-column="no-limit" loext:hyphenation-zone-page="no-limit" 
loext:hyphenation-zone-spread="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:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0cm" 
style:contextual-spacing="false" 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"/>
+   <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="5" loext:hyphenation-zone="no-limit" 
loext:hyphenation-zone-always="no-limit" 
loext:hyphenation-zone-column="no-limit" loext:hyphenation-zone-page="no-limit" 
loext:hyphenation-zone-spread="no-limit"/>
+  </style:style>
+  <style:style style:name="Heading" style:family="paragraph" 
style:parent-style-name="Standard" style:next-style-name="Text_20_body" 
style:class="chapter">
+   <style:paragraph-properties fo:margin-top="0.423cm" 
fo:margin-bottom="0.212cm" style:contextual-spacing="false" 
fo:keep-with-next="always"/>
+   <style:text-properties style:font-name="Liberation Sans" 
fo:font-family="&apos;Liberation Sans&apos;" style:font-family-generic="swiss" 
style:font-pitch="variable" fo:font-size="14pt"/>
+  </style:style>
+  <style:style style:name="Text_20_body" style:display-name="Text body" 
style:family="paragraph" style:parent-style-name="Standard" style:class="text">
+   <style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0.247cm" 
style:contextual-spacing="false" fo:line-height="115%"/>
+  </style:style>
+ </office:styles>
+ <office:automatic-styles>
+  <style:style style:name="P1" style:family="paragraph" 
style:parent-style-name="Standard" style:master-page-name="">
+   <style:paragraph-properties fo:hyphenation-ladder-count="no-limit" 
fo:hyphenation-keep="auto" loext:hyphenation-keep-type="column" 
loext:hyphenation-keep-line="true" style:page-number="auto"/>
+   <style:text-properties fo:font-size="49pt" 
officeooo:paragraph-rsid="000dd353" 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="5" loext:hyphenation-zone="no-limit" 
loext:hyphenation-zone-always="4320" loext:hyphenation-zone-column="no-limit" 
loext:hyphenation-zone-page="2880" loext:hyphenation-zone-spread="no-limit" 
loext:hyphenation-compound-remain-char-count="2"/>
+  </style:style>
+  <style:style style:name="P2" style:family="paragraph" 
style:parent-style-name="Standard">
+   <style:paragraph-properties fo:hyphenation-ladder-count="no-limit" 
fo:hyphenation-keep="auto" loext:hyphenation-keep-type="column" 
loext:hyphenation-keep-line="true"/>
+   <style:text-properties fo:font-size="49pt" 
officeooo:paragraph-rsid="000dd353" 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="5" loext:hyphenation-zone="no-limit" 
loext:hyphenation-zone-always="no-limit" 
loext:hyphenation-zone-column="no-limit" loext:hyphenation-zone-page="2880" 
loext:hyphenation-zone-spread="no-limit" 
loext:hyphenation-compound-remain-char-count="2"/>
+  </style:style>
+  <style:style style:name="P3" style:family="paragraph" 
style:parent-style-name="Standard" style:master-page-name="">
+   <style:paragraph-properties fo:hyphenation-ladder-count="no-limit" 
fo:hyphenation-keep="auto" loext:hyphenation-keep-type="column" 
loext:hyphenation-keep-line="true" style:page-number="auto"/>
+   <style:text-properties fo:font-size="49pt" 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="5" loext:hyphenation-zone="no-limit" 
loext:hyphenation-zone-always="no-limit" 
loext:hyphenation-zone-column="no-limit" loext:hyphenation-zone-page="2880" 
loext:hyphenation-zone-spread="no-limit" 
loext:hyphenation-compound-remain-char-count="2"/>
+  </style:style>
+  <style:style style:name="T1" style:family="text">
+   <style:text-properties officeooo:rsid="000dd353"/>
+  </style:style>
+  <style:page-layout style:name="pm1">
+   <style:page-layout-properties fo:page-width="20.814cm" 
fo:page-height="27.94cm" style:num-format="1" 
style:print-orientation="portrait" fo:margin-top="2cm" fo:margin-bottom="2cm" 
fo:margin-left="2cm" fo:margin-right="2cm" style:writing-mode="lr-tb" 
style:layout-grid-color="#c0c0c0" style:layout-grid-lines="20" 
style:layout-grid-base-height="0.706cm" style:layout-grid-ruby-height="0.353cm" 
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="0cm" loext:margin-gutter="0cm">
+    <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:header-style/>
+   <style:footer-style/>
+  </style:page-layout>
+  <style:style style:name="dp1" style:family="drawing-page">
+   <style:drawing-page-properties draw:background-size="full"/>
+  </style:style>
+ </office:automatic-styles>
+ <office:master-styles>
+  <style:master-page style:name="Standard" style:page-layout-name="pm1" 
draw:style-name="dp1"/>
+ </office:master-styles>
+ <office:body>
+  <office:text text:use-soft-page-breaks="true">
+   <text:p text:style-name="P1">The Earth is no different to any other 
celestial body out there in space. It merely moves along in space inertially. 
<text:span text:style-name="T1">Even</text:span></text:p>
+   <text:p text:style-name="P2"/>
+   <text:p text:style-name="P3">The Earth is no different to any other 
celestial body out there in space. It merely moves along in space 
iner<text:soft-page-break/>tially. Even just one inch above the surface of the 
Earth is space, except that it has an atmosphere. The Earthisno different to 
any other celestial body out there in space. It merely moves along in space 
inertially. Even just one inch above the surface of the Earth is space 
<text:soft-page-break/>except that it has an atmosphere. The Earth is no 
different to any other celestial body out…</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 269d01e8d82b..9189590f6804 100644
--- a/sw/qa/extras/layout/layout3.cxx
+++ b/sw/qa/extras/layout/layout3.cxx
@@ -980,6 +980,38 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, 
testTdf165354_long_paragraph_3)
                 u"of the Earth is space ex");
 }
 
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf165984)
+{
+    // enabled hyphenation on page 1, disabled on page 2 by 
hyphenation-zone-page
+    // (no hyphenation, if the word part is completely inside the Page end 
zone:
+    // "iner-tially", but "except" and not "ex-cept")
+    uno::Reference<linguistic2::XHyphenator> xHyphenator = 
LinguMgr::GetHyphenator();
+    if (!xHyphenator->hasLocale(lang::Locale(u"en"_ustr, u"US"_ustr, 
OUString())))
+        return;
+
+    createSwDoc("tdf165984.fodt");
+    // Ensure that all text portions are calculated before testing.
+    SwViewShell* pViewShell = 
getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+    CPPUNIT_ASSERT(pViewShell);
+    pViewShell->Reformat();
+
+    xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+    // paragraph with loext:hyphenation-zone-paragraph="7.62cm"
+    // This was "tially. Even" (now disabled hyphenation in the last full 
paragraph line)
+    assertXPath(pXmlDoc, 
"/root/page[1]/body/txt/SwParaPortion/SwLineLayout[6]", "portion",
+                u"inertially. Even");
+
+    // 3-page paragraph, loext:hyphenation-zone-page="5.08cm"
+    assertXPath(pXmlDoc, 
"/root/page[2]/body/txt/SwParaPortion/SwLineLayout[1]", "portion",
+                u"tially. Even just one ");
+
+    // disabled hyphenation by hyphenation-zone-page="5.08cm"
+    // This ended with "ex-"
+    assertXPath(pXmlDoc, 
"/root/page[2]/body/txt/SwParaPortion/SwLineLayout[12]", "portion",
+                u"of the Earth is space ");
+}
+
 CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf106234)
 {
     createSwDoc("tdf106234.fodt");
diff --git a/sw/qa/uitest/styleInspector/styleInspector.py 
b/sw/qa/uitest/styleInspector/styleInspector.py
index 2dc13d93cc48..b29f42407f75 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(144, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(148, 
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(144, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(148, 
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(144, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(148, 
len(xListBox.getChild('0').getChild('0').getChildren()))
 
             xParDirFormatting = xListBox.getChild('1')
             self.assertEqual(7, len(xParDirFormatting.getChildren()))
@@ -75,7 +75,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(144, len(xParStyle.getChild('0').getChildren()))
+            self.assertEqual(148, len(xParStyle.getChild('0').getChildren()))
             self.assertEqual("Heading  ", 
get_state_as_dict(xParStyle.getChild('1'))['Text'])
             self.assertEqual(28, len(xParStyle.getChild('1').getChildren()))
 
@@ -109,7 +109,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(144, len(xParStyle.getChild('0').getChildren()))
+            self.assertEqual(148, 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()))
 
@@ -144,7 +144,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(144, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(148, 
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()))
@@ -154,7 +154,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(144, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(148, 
len(xListBox.getChild('0').getChild('0').getChildren()))
 
             xParDirFormatting = xListBox.getChild('1')
             self.assertEqual(1, len(xParDirFormatting.getChildren()))
@@ -207,7 +207,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(144, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(148, 
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()))
@@ -217,7 +217,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(144, 
len(xListBox.getChild('1').getChild('0').getChildren()))
+            self.assertEqual(148, 
len(xListBox.getChild('1').getChild('0').getChildren()))
 
             # Outer bookmark
             xBookmarkFormatting = xListBox.getChild('0')
@@ -264,7 +264,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(144, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(148, 
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 70f74c76a5e9..e5da641bbc03 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(144, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(148, 
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/txtfrm.hxx b/sw/source/core/inc/txtfrm.hxx
index f2eb15cf187c..d89efec8f16d 100644
--- a/sw/source/core/inc/txtfrm.hxx
+++ b/sw/source/core/inc/txtfrm.hxx
@@ -208,7 +208,8 @@ class SW_DLLPUBLIC SwTextFrame final : public SwContentFrame
     std::unique_ptr<sw::MergedPara> m_pMergedPara;
 
     TextFrameIndex mnOffset; // Is the offset in the Content (character count)
-    TextFrameIndex mnNoHyphOffset; // Is the offset of the last line to 
disable its hyphenation
+    TextFrameIndex mnNoHyphOffset; // Is the offset of the last line with 
disabled or limited hyphenation
+    sal_Int16 mnNoHyphEndZone;     // size of end zone (-1 means disabled 
hyphenation)
 
     sal_uInt16 mnCacheIndex; // Index into the cache, USHRT_MAX if there's 
definitely no fitting object in the cache
 
@@ -457,6 +458,8 @@ public:
     inline void        SetOffset (TextFrameIndex nNewOfst);
     TextFrameIndex GetNoHyphOffset() const { return mnNoHyphOffset; }
     void SetNoHyphOffset(TextFrameIndex const nNewOfst) { mnNoHyphOffset = 
nNewOfst; }
+    sal_Int16 GetNoHyphEndZone() const { return mnNoHyphEndZone; }
+    void SetNoHyphEndZone(sal_Int16 nNewType) { mnNoHyphEndZone = nNewType; }
     void ManipOfst(TextFrameIndex const nNewOfst) { mnOffset = nNewOfst; }
            SwTextFrame   *GetFrameAtPos ( const SwPosition &rPos);
     inline const SwTextFrame *GetFrameAtPos ( const SwPosition &rPos) const;
diff --git a/sw/source/core/text/guess.cxx b/sw/source/core/text/guess.cxx
index 5754422b4cf7..55e109136e78 100644
--- a/sw/source/core/text/guess.cxx
+++ b/sw/source/core/text/guess.cxx
@@ -280,11 +280,22 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, 
SwTextFormatInfo &rInf,
         }
     }
 
-    bool bHyph = rInf.IsHyphenate() && !rInf.IsHyphForbud() &&
-        // disable hyphenation in the last line, when hyphenation-keep-line is 
enabled
-        ( rInf.GetTextFrame()->GetNoHyphOffset() == 
TextFrameIndex(COMPLETE_STRING) ||
+    bool bHyph = rInf.IsHyphenate() && !rInf.IsHyphForbud();
+    // disable hyphenation according to hyphenation-keep and 
hyphenation-keep-type,
+    // or modify hyphenation according to hyphenation-zone-column/page/spread 
(see widorp.cxx)
+    sal_Int16 nEndZone = 0;
+    if ( bHyph &&
+          rInf.GetTextFrame()->GetNoHyphOffset() != 
TextFrameIndex(COMPLETE_STRING) &&
           // when there is a portion in the last line, rInf.GetIdx() > 
GetNoHyphOffset()
-          rInf.GetTextFrame()->GetNoHyphOffset() > rInf.GetIdx() );
+          rInf.GetTextFrame()->GetNoHyphOffset() <= rInf.GetIdx() )
+    {
+          nEndZone = rInf.GetTextFrame()->GetNoHyphEndZone();
+          // disable hyphenation in the last line, when hyphenation-keep-line 
is enabled
+          // and hyphenation-keep, too (i.e. when end zone is not defined),
+          // also when the end zone is bigger, than the line width
+          if ( nEndZone < 0 || nEndZone >= nLineWidth )
+              bHyph = false;
+    }
     TextFrameIndex nHyphPos(0);
 
     // nCutPos is the first character not fitting to the current line
@@ -296,33 +307,76 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, 
SwTextFormatInfo &rInf,
         // or 0, if the whole line in the hyphenation zone,
         // or -1, if no hyphenation zone defined (i.e. it is 0)
         sal_Int32 nHyphZone = -1;
+        sal_Int32 nParaZone = -1;
         const css::beans::PropertyValues & rHyphValues = rInf.GetHyphValues();
-        assert( rHyphValues.getLength() > 5 && rHyphValues[5].Name == 
UPN_HYPH_ZONE );
+        assert( rHyphValues.getLength() > 10 && rHyphValues[5].Name == 
UPN_HYPH_ZONE && rHyphValues[10].Name == UPN_HYPH_ZONE_ALWAYS );
         // hyphenation zone (distance from the line end in twips)
-        sal_uInt16 nTextHyphenZone;
-        if ( rHyphValues[5].Value >>= nTextHyphenZone )
+        sal_Int16 nTextHyphenZone = 0;
+        sal_Int16 nTextHyphenZoneAlways = 0;
+        rHyphValues[5].Value >>= nTextHyphenZone;
+        rHyphValues[10].Value >>= nTextHyphenZoneAlways;
+        if ( nTextHyphenZone || nTextHyphenZoneAlways || nEndZone )
+        {
             nHyphZone = nTextHyphenZone >= nLineWidth
                 ? 0
-                : sal_Int32(rInf.GetTextBreak( nLineWidth - nTextHyphenZone,
+                : sal_Int32(rInf.GetTextBreak( nLineWidth - (nEndZone ? 
nEndZone : nTextHyphenZone),
                                         nMaxLen, nMaxComp, 
rInf.GetCachedVclData().get() ));
-
+        }
         m_nCutPos = rInf.GetTextBreak( nLineWidth, nMaxLen, nMaxComp, 
nHyphPos, rInf.GetCachedVclData().get() );
 
-        // don't try to hyphenate in the hyphenation zone
-        if ( nHyphZone != -1 && TextFrameIndex(COMPLETE_STRING) != m_nCutPos )
+        if ( !nEndZone && nTextHyphenZoneAlways &&
+               // if paragraph end zone is not different from the hyphenation 
zone, skip its handling
+               nTextHyphenZoneAlways != nTextHyphenZone &&
+               // end of the paragraph
+               // FIXME: handle text portions
+               rInf.GetText().getLength() - sal_Int32(nHyphZone) <
+               sal_Int32(m_nCutPos) - sal_Int32(rInf.GetIdx() ) )
+        {
+            nParaZone = nTextHyphenZoneAlways >= nLineWidth
+                ? 0
+                : sal_Int32(rInf.GetTextBreak( nLineWidth - 
nTextHyphenZoneAlways,
+                                        nMaxLen, nMaxComp, 
rInf.GetCachedVclData().get() ));
+        }
+
+        // don't try to hyphenate in the hyphenation zone or in the paragraph 
end zone
+        // maximum end zone is the lower non-negative text break position
+        // (-1 means that no hyphenation zone is defined)
+        sal_Int32 nMaxZone = nParaZone > -1 && nParaZone < nHyphZone
+                ? nParaZone
+                : nHyphZone;
+        if ( nMaxZone != -1 && TextFrameIndex(COMPLETE_STRING) != m_nCutPos )
         {
             sal_Int32 nZonePos = sal_Int32(m_nCutPos);
-            // disable hyphenation, if there is a space within the hyphenation 
zone
+            // disable hyphenation, if there is a space within the hyphenation 
or end zones
             // Note: for better interoperability, not fitting space character 
at
             // rInf.GetIdx()[nHyphZone] always disables the hyphenation, don't 
need to calculate
             // with its fitting part. Moreover, do not check double or more 
spaces there, they
             // are accepted outside of the hyphenation zone, too.
-            for (; sal_Int32(rInf.GetIdx()) <= nZonePos && nHyphZone <= 
nZonePos; --nZonePos )
+            for (; sal_Int32(rInf.GetIdx()) <= nZonePos && nMaxZone <= 
nZonePos; --nZonePos )
             {
                 sal_Unicode cChar = rInf.GetText()[nZonePos];
                 if ( cChar == CH_BLANK || cChar == CH_FULL_BLANK || cChar == 
CH_SIX_PER_EM )
                 {
-                    bHyph = false;
+                    // no column/page/spread/end zone (!nEndZone),
+                    // but is it good for a paragraph end zone?
+                    if ( nParaZone != -1 && nParaZone <= nZonePos &&
+                        // still end of the paragraph, i.e. still more 
characters in the original
+                        // last full line, then in the planned last paragraph 
line
+                        // FIXME: guarantee, that last not full line won't 
become a full line
+                        // FIXME: handle text portions
+                        rInf.GetText().getLength() - sal_Int32(nZonePos) <
+                        sal_Int32(m_nCutPos) - sal_Int32(rInf.GetIdx() ) )
+                    {
+                        bHyph = false;
+                    }
+                    // otherwise disable hyphenation, if there is a space in 
the hyphenation zone
+                    else if ( nHyphZone != 1 && nHyphZone <= nZonePos )
+                    {
+                        bHyph = false;
+                    }
+                    // set applied end zone
+                    if ( !bHyph && nEndZone )
+                        
rInf.GetTextFrame()->SetNoHyphOffset(TextFrameIndex(COMPLETE_STRING));
                 }
             }
         }
@@ -410,6 +464,10 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, 
SwTextFormatInfo &rInf,
                             // with hyphenation, i.e. don't skip hyphenation, 
if the last paragraph
                             // line is already near full.
                             ( !bDoNotHyphenateLastLine ||
+                                  // FIXME: character count is not fail-safe: 
remaining characters
+                                  // can exceed the line, resulting two last 
full paragraph lines
+                                  // with disabled hyphenation.
+                                  // FIXME: handle text portions
                                   rInf.GetText().getLength() - 
sal_Int32(nLastWord) <
                                   sal_Int32(m_nCutPos) - 
sal_Int32(rInf.GetIdx() ) ) )
             {
diff --git a/sw/source/core/text/inftxt.cxx b/sw/source/core/text/inftxt.cxx
index 1ad5fe78934b..b8eca0f24575 100644
--- a/sw/source/core/text/inftxt.cxx
+++ b/sw/source/core/text/inftxt.cxx
@@ -1563,13 +1563,13 @@ static void lcl_InitHyphValues( PropertyValues &rVals,
             sal_Int16 nMinLeading, sal_Int16 nMinTrailing,
             bool bNoCapsHyphenation, bool bNoLastWordHyphenation,
             sal_Int16 nMinWordLength, sal_Int16 nTextHyphZone, bool bKeep, 
sal_Int16 nKeepType,
-            bool bKeepLine, sal_Int16 nCompoundMinLeading )
+            bool bKeepLine, sal_Int16 nCompoundMinLeading, sal_Int16 
nTextHyphZoneAlways )
 {
     sal_Int32 nLen = rVals.getLength();
 
     if (0 == nLen)  // yet to be initialized?
     {
-        rVals.realloc( 10 );
+        rVals.realloc( 11 );
         PropertyValue *pVal = rVals.getArray();
 
         pVal[0].Name    = UPN_HYPH_MIN_LEADING;
@@ -1611,8 +1611,12 @@ static void lcl_InitHyphValues( PropertyValues &rVals,
         pVal[9].Name    = UPN_HYPH_KEEP_LINE;
         pVal[9].Handle  = UPH_HYPH_KEEP_LINE;
         pVal[9].Value   <<= bKeepLine;
+
+        pVal[10].Name    = UPN_HYPH_ZONE_ALWAYS;
+        pVal[10].Handle  = UPH_HYPH_ZONE_ALWAYS;
+        pVal[10].Value   <<= nTextHyphZoneAlways;
     }
-    else if (10 == nLen) // already initialized once?
+    else if (11 == nLen) // already initialized once?
     {
         PropertyValue *pVal = rVals.getArray();
         pVal[0].Value <<= nMinLeading;
@@ -1625,6 +1629,7 @@ static void lcl_InitHyphValues( PropertyValues &rVals,
         pVal[7].Value <<= nCompoundMinLeading;
         pVal[8].Value <<= bKeep;
         pVal[9].Value <<= bKeepLine;
+        pVal[10].Value <<= nTextHyphZoneAlways;
     }
     else {
         OSL_FAIL( "unexpected size of sequence" );
@@ -1633,7 +1638,7 @@ static void lcl_InitHyphValues( PropertyValues &rVals,
 
 const PropertyValues & SwTextFormatInfo::GetHyphValues() const
 {
-    OSL_ENSURE( 10 == m_aHyphVals.getLength(),
+    OSL_ENSURE( 11 == m_aHyphVals.getLength(),
             "hyphenation values not yet initialized" );
     return m_aHyphVals;
 }
@@ -1655,6 +1660,7 @@ bool SwTextFormatInfo::InitHyph( const bool bAutoHyphen )
         const bool bNoCapsHyphenation = rAttr.IsNoCapsHyphenation();
         const bool bNoLastWordHyphenation = rAttr.IsNoLastWordHyphenation();
         const sal_Int16 nTextHyphZone = rAttr.GetTextHyphenZone();
+        const sal_Int16 nTextHyphZoneAlways = rAttr.GetTextHyphenZoneAlways();
         const bool bKeep = rAttr.IsKeep();
         const sal_Int16 nKeepType = rAttr.GetKeepType();
         const bool bKeepLine = rAttr.IsKeepLine();
@@ -1662,7 +1668,7 @@ bool SwTextFormatInfo::InitHyph( const bool bAutoHyphen )
         lcl_InitHyphValues( m_aHyphVals, nMinimalLeading, nMinimalTrailing,
                  bNoCapsHyphenation, bNoLastWordHyphenation,
                  nMinimalWordLength, nTextHyphZone, bKeep, nKeepType,
-                 bKeepLine, nCompoundMinimalLeading );
+                 bKeepLine, nCompoundMinimalLeading, nTextHyphZoneAlways );
     }
     return bAuto;
 }
diff --git a/sw/source/core/text/txtfrm.cxx b/sw/source/core/text/txtfrm.cxx
index 7eb35cb29e81..9e0c662aa3c1 100644
--- a/sw/source/core/text/txtfrm.cxx
+++ b/sw/source/core/text/txtfrm.cxx
@@ -774,6 +774,7 @@ SwTextFrame::SwTextFrame(SwTextNode * const pNode, SwFrame* 
pSib,
     , mnAdditionalFirstLineOffset( 0 )
     , mnOffset( 0 )
     , mnNoHyphOffset( COMPLETE_STRING )
+    , mnNoHyphEndZone( 0 )
     , mnCacheIndex( USHRT_MAX )
     , mbLocked( false )
     , mbWidow( false )
diff --git a/sw/source/core/text/widorp.cxx b/sw/source/core/text/widorp.cxx
index 3f83fd115b7d..1c31d2ba2245 100644
--- a/sw/source/core/text/widorp.cxx
+++ b/sw/source/core/text/widorp.cxx
@@ -470,6 +470,7 @@ bool WidowsAndOrphans::FindWidows( SwTextFrame *pFrame, 
SwTextMargin &rLine )
     // hyphenation-keep: truncate a hyphenated line at the end of
     // the column, page or spread (but not more)
     // hyphenation-keep-line: disable hyphenation in the last line instead of 
truncating it
+    // hyphenation-zone-always/page/column/spread: modify hyphenation in the 
last line (end zone)
     int nExtraWidLines = 0;
     if( rLine.GetLineNr() >= m_nWidLines && pMaster->HasPara() )
     {
@@ -480,26 +481,58 @@ bool WidowsAndOrphans::FindWidows( SwTextFrame *pFrame, 
SwTextMargin &rLine )
         bool bKeep = rAttr.IsHyphen() && rAttr.IsKeep() && rAttr.GetKeepType();
         bool bKeepLine = bKeep && rAttr.IsKeepLine();
 
+        // last line of a column inside a page
+        auto pMasterPage = pMaster->FindPageFrame();
+        auto pPage = pFrame->FindPageFrame();
+        // across column, but not page or spread, when both parts are there on 
the same page
+        bool bAcrossColumnNotPage = pMasterPage == pPage;
+        // across page, but not spread, when the parts are there not on the 
same page
+        bool bAcrossPageNotSpread = !bAcrossColumnNotPage &&
+                   !pMasterPage->OnRightPage() && pPage->OnRightPage() &&
+                   // linked text frames can be on a different spread, so check
+                   // that the master is there on the previous page
+                   pMasterPage == pPage->GetPrev();
+
+        // calculate end zones, based on their inheritance (0 means 
inheritance)
+        sal_Int16 nEndZoneParagraph = rAttr.GetTextHyphenZoneAlways() > 0
+                ? rAttr.GetTextHyphenZoneAlways()
+                : rAttr.GetTextHyphenZone();
+        sal_Int16 nEndZoneColumn = rAttr.GetTextHyphenZoneColumn() > 0
+                ? rAttr.GetTextHyphenZoneColumn()
+                : nEndZoneParagraph;
+        sal_Int16 nEndZonePage = rAttr.GetTextHyphenZonePage() > 0
+                ? rAttr.GetTextHyphenZonePage()
+                : nEndZoneColumn;
+        sal_Int16 nEndZoneSpread = rAttr.GetTextHyphenZoneSpread() > 0
+                ? rAttr.GetTextHyphenZoneSpread()
+                : nEndZonePage;
+
+        // set end zone
+        sal_Int16 nNoHyphEndZone = bAcrossColumnNotPage
+            ? nEndZoneColumn
+            : bAcrossPageNotSpread
+                ? nEndZonePage
+                : nEndZoneSpread;
+
         // if PAGE or SPREAD, allow hyphenation in the not last column or in 
the
         // not last linked frame on the same page
-        if( bKeep && (
+        if( bKeep && bAcrossColumnNotPage && (
                 rAttr.GetKeepType() == 
css::text::ParagraphHyphenationKeepType::SPREAD ||
-e 
... etc. - the rest is truncated

Reply via email to