cui/source/inc/paragrph.hxx                               |    5 
 cui/source/tabpages/paragrph.cxx                          |   37 ++++-
 cui/uiconfig/ui/textflowpage.ui                           |  100 ++++++++------
 editeng/source/items/paraitem.cxx                         |   32 ++++
 include/editeng/editrids.hrc                              |    5 
 include/editeng/hyphenzoneitem.hxx                        |    4 
 include/editeng/memberids.h                               |    1 
 include/unotools/linguprops.hxx                           |    2 
 include/xmloff/xmltypes.hxx                               |    1 
 offapi/UnoApi_offapi.mk                                   |    1 
 offapi/com/sun/star/style/ParagraphProperties.idl         |    8 +
 offapi/com/sun/star/text/ParagraphHyphenationKeepType.idl |   63 ++++++++
 svx/sdi/svxitems.sdi                                      |    1 
 sw/CppunitTest_sw_odfexport2.mk                           |    1 
 sw/inc/inspectorproperties.hrc                            |    1 
 sw/inc/unoprnms.hxx                                       |    1 
 sw/qa/extras/odfexport/data/tdf132599_auto.fodt           |   49 ++++++
 sw/qa/extras/odfexport/data/tdf132599_page.fodt           |   49 ++++++
 sw/qa/extras/odfexport/odfexport2.cxx                     |   27 +++
 sw/qa/uitest/styleInspector/styleInspector.py             |   20 +-
 sw/qa/uitest/styleInspector/tdf137513.py                  |    2 
 sw/source/core/text/inftxt.cxx                            |   16 +-
 sw/source/core/text/itrform2.cxx                          |    1 
 sw/source/core/text/porlay.hxx                            |    3 
 sw/source/core/text/widorp.cxx                            |   42 +++++
 sw/source/core/unocore/unomapproperties.hxx               |    2 
 sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx     |    1 
 xmloff/inc/xmlprop.hxx                                    |    1 
 xmloff/source/text/txtprhdl.cxx                           |   13 +
 xmloff/source/text/txtprmap.cxx                           |    1 
 30 files changed, 423 insertions(+), 67 deletions(-)

New commits:
commit 9574a62add8e4901405e12117e75c86c2d2c2f21
Author:     László Németh <nem...@numbertext.org>
AuthorDate: Thu Feb 29 14:07:34 2024 +0100
Commit:     László Németh <nem...@numbertext.org>
CommitDate: Mon Mar 4 23:39:51 2024 +0100

    tdf#132599 cui offapi sw xmloff: implement hyphenate-keep
    
    Both parts of a hyphenated word shall lie within a single
    page with ODF paragraph setting fo:hyphenation-keep="page".
    The implementation follows the default page layout of
    MSO 2016 and newer by shifting the bottom hyphenated line
    to the next page (and to the next column, see last note).
    
    Note: this is a MSO DOCX interoperability feature, used
    also in DTP software, XSL and CSS.
    
    * Add checkbox/combobox to Text Flow in paragraph dialog
    * Store property in paragraph model 
(com::sun::star::style::ParagraphProperties::ParaHyphenationKeep)
    * Add ODF import/export
    * Add ODF unit tests
    
    New constants of com::sun::star::text::ParagraphHyphenationKeepType,
    containing ODF AUTO and PAGE (borrowed from XSL), and for the
    planned extension ParaHyphenationKeepType of ParagraphProperties:
    
    – COLUMN (standard XSL value, defined in
      https://www.w3.org/TR/2001/REC-xsl-20011015/slice7.html#hyphenation-keep)
    
    – SPREAD and ALWAYS (CSS 4 values of hyphenate-limit-last,
      equivalent of hyphenation-keep, defined in
      https://www.w3.org/TR/css-text-4/#hyphenate-line-limits).
    
    Note: the implementation truncates only a single hyphenated
    line, like MSO does: the pages can end in hyphenated
    lines (i.e. in the case of consecutive hyphenated lines),
    but less often, than before.
    
    Clean-up hyphenation dialog by collecting "Don't hyphenate"
    options at the end of the hyphenation settings, and negating them
    (similar to MSO and DTP), adding also the new option
    "Hyphenate across column and page":
    
    [x] Hyphenate words in CAPS
    [x] Hyphenate last word
    [x] Hyphenate across column and page
    
    Note: ODF fo:hyphenation-keep has got only "auto" and
    "page" attributes, while XSL defines also "column".
    Because of the interoperability with MSO and DTP,
    fo:hyphenation-keep="page" is interpreted as
    XSL "column", avoiding hyphenation at the end
    of column, not only at the end of page.
    
    Change-Id: I5c6b7adc0671a5a790568e7bf1d33256e607f85f
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164158
    Tested-by: László Németh <nem...@numbertext.org>
    Reviewed-by: László Németh <nem...@numbertext.org>

diff --git a/cui/source/inc/paragrph.hxx b/cui/source/inc/paragrph.hxx
index 9e78c8311015..0972203be73f 100644
--- a/cui/source/inc/paragrph.hxx
+++ b/cui/source/inc/paragrph.hxx
@@ -223,6 +223,7 @@ private:
     weld::TriStateEnabled aKeepParaState;
     weld::TriStateEnabled aOrphanState;
     weld::TriStateEnabled aWidowState;
+    weld::TriStateEnabled aKeepState;
 
     bool                bPageBreak;
     bool                bHtmlMode;
@@ -267,6 +268,9 @@ private:
     std::unique_ptr<weld::SpinButton> m_xWidowRowNo;
     std::unique_ptr<weld::Label> m_xWidowRowLabel;
 
+    // avoid hyphenation across
+    std::unique_ptr<weld::CheckButton> m_xKeepBox;
+
     void HyphenClickHdl();
     void PageNumBoxClickHdl();
     void ApplyCollClickHdl();
@@ -285,6 +289,7 @@ private:
     DECL_LINK(PageBreakTypeHdl_Impl, weld::ComboBox&, void);
     DECL_LINK(PageNumBoxClickHdl_Impl, weld::Toggleable&, void);
     DECL_LINK(KeepParaBoxClickHdl_Impl, weld::Toggleable&, void);
+    DECL_LINK(KeepHdl_Impl, weld::Toggleable&, void);
 
     virtual void            PageCreated(const SfxAllItemSet& aSet) override;
 };
diff --git a/cui/source/tabpages/paragrph.cxx b/cui/source/tabpages/paragrph.cxx
index d644570784e6..a1403b30ea74 100644
--- a/cui/source/tabpages/paragrph.cxx
+++ b/cui/source/tabpages/paragrph.cxx
@@ -1627,13 +1627,14 @@ bool SvxExtParagraphTabPage::FillItemSet( SfxItemSet* 
rOutSet )
          m_xExtHyphenAfterBox->get_value_changed_from_saved() ||
          m_xMaxHyphenEdit->get_value_changed_from_saved() ||
          m_xMinWordLength->get_value_changed_from_saved() ||
-         m_aHyphenZone.get_value_changed_from_saved() )
+         m_aHyphenZone.get_value_changed_from_saved() ||
+         m_xKeepBox->get_state_changed_from_saved() )
     {
         SvxHyphenZoneItem aHyphen(
             static_cast<const SvxHyphenZoneItem&>(GetItemSet().Get( _nWhich )) 
);
         aHyphen.SetHyphen( eHyphenState == TRISTATE_TRUE );
-        aHyphen.SetNoCapsHyphenation(m_xHyphenNoCapsBox->get_state() == 
TRISTATE_TRUE);
-        aHyphen.SetNoLastWordHyphenation(m_xHyphenNoLastWordBox->get_state() 
== TRISTATE_TRUE);
+        aHyphen.SetNoCapsHyphenation(m_xHyphenNoCapsBox->get_state() != 
TRISTATE_TRUE);
+        aHyphen.SetNoLastWordHyphenation(m_xHyphenNoLastWordBox->get_state() 
!= TRISTATE_TRUE);
 
         if ( eHyphenState == TRISTATE_TRUE )
         {
@@ -1647,6 +1648,18 @@ 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.SetHyphen( eHyphenState == TRISTATE_TRUE );
+
+        const TriState eKeepState = m_xKeepBox->get_state();
+        aHyphen.SetNoLastWordHyphenation(m_xHyphenNoLastWordBox->get_state() 
!= TRISTATE_TRUE);
+        if ( eKeepState == TRISTATE_TRUE )
+        {
+             // hyphenate across column and page -> 0 (AUTO)
+            aHyphen.GetKeep() = static_cast<sal_uInt8>(0);
+        }
+        else
+            // don't hyphenate across column -> 3 (COLUMN)
+            aHyphen.GetKeep() = static_cast<sal_uInt8>(3);
 
         if ( !pOld ||
             *static_cast<const SvxHyphenZoneItem*>(pOld) != aHyphen ||
@@ -1857,8 +1870,8 @@ void SvxExtParagraphTabPage::Reset( const SfxItemSet* 
rSet )
 
         bIsHyphen = rHyphen.IsHyphen();
         m_xHyphenBox->set_state(bIsHyphen ? TRISTATE_TRUE : TRISTATE_FALSE);
-        m_xHyphenNoCapsBox->set_state(rHyphen.IsNoCapsHyphenation() ? 
TRISTATE_TRUE : TRISTATE_FALSE);
-        m_xHyphenNoLastWordBox->set_state(rHyphen.IsNoLastWordHyphenation() ? 
TRISTATE_TRUE : TRISTATE_FALSE);
+        m_xHyphenNoCapsBox->set_state(rHyphen.IsNoCapsHyphenation() ? 
TRISTATE_FALSE : TRISTATE_TRUE);
+        m_xHyphenNoLastWordBox->set_state(rHyphen.IsNoLastWordHyphenation() ? 
TRISTATE_FALSE : TRISTATE_TRUE);
 
         m_xExtHyphenBeforeBox->set_value(rHyphen.GetMinLead());
         m_xExtHyphenAfterBox->set_value(rHyphen.GetMinTrail());
@@ -1866,6 +1879,9 @@ void SvxExtParagraphTabPage::Reset( const SfxItemSet* 
rSet )
         m_xMinWordLength->set_value(rHyphen.GetMinWordLength());
         m_aHyphenZone.SetFieldUnit(eFUnit);
         m_aHyphenZone.SetMetricValue(rHyphen.GetTextHyphenZone(), 
MapUnit::MapTwip);
+        // don't hyphenate column or paragraph -> disable checkbox
+        m_xKeepBox->set_state(rHyphen.GetKeep() < 3 ? TRISTATE_TRUE : 
TRISTATE_FALSE);
+        aKeepState.bTriStateEnabled = false;
     }
     else
     {
@@ -1886,6 +1902,7 @@ void SvxExtParagraphTabPage::Reset( const SfxItemSet* 
rSet )
     m_xMinWordLength->set_sensitive(bEnable);
     m_xHyphenZoneLabel->set_sensitive(bEnable);
     m_aHyphenZone.set_sensitive(bEnable);
+    m_xKeepBox->set_sensitive(bEnable);
 
     switch (rSet->GetItemState(SID_ATTR_PARA_PAGENUM))
     {
@@ -2147,6 +2164,7 @@ void SvxExtParagraphTabPage::ChangesApplied()
     m_xMaxHyphenEdit->save_value();
     m_xMinWordLength->save_value();
     m_aHyphenZone.save_value();
+    m_xKeepBox->save_state();
     m_xPageBreakBox->save_state();
     m_xBreakPositionLB->save_value();
     m_xBreakTypeLB->save_value();
@@ -2220,6 +2238,8 @@ 
SvxExtParagraphTabPage::SvxExtParagraphTabPage(weld::Container* pPage, weld::Dia
     , m_xWidowBox(m_xBuilder->weld_check_button("checkWidow"))
     , m_xWidowRowNo(m_xBuilder->weld_spin_button("spinWidow"))
     , m_xWidowRowLabel(m_xBuilder->weld_label("labelWidow"))
+    // Avoid hyphenation across
+    , m_xKeepBox(m_xBuilder->weld_check_button("checkKeep"))
 {
     // this page needs ExchangeSupport
     SetExchangeSupport();
@@ -2234,6 +2254,7 @@ 
SvxExtParagraphTabPage::SvxExtParagraphTabPage(weld::Container* pPage, weld::Dia
     m_xBreakPositionLB->connect_changed(LINK(this, SvxExtParagraphTabPage, 
PageBreakPosHdl_Impl));
     m_xPageNumBox->connect_toggled(LINK(this, SvxExtParagraphTabPage, 
PageNumBoxClickHdl_Impl));
     m_xKeepParaBox->connect_toggled(LINK(this, SvxExtParagraphTabPage, 
KeepParaBoxClickHdl_Impl));
+    m_xKeepBox->connect_toggled(LINK(this, SvxExtParagraphTabPage, 
KeepHdl_Impl));
 
     if (SfxObjectShell* pSh = SfxObjectShell::Current())
     {
@@ -2409,6 +2430,7 @@ void SvxExtParagraphTabPage::HyphenClickHdl()
     m_xMinWordLength->set_sensitive(bEnable);
     m_xHyphenZoneLabel->set_sensitive(bEnable);
     m_aHyphenZone.set_sensitive(bEnable);
+    m_xKeepBox->set_sensitive(bEnable);
     m_xHyphenBox->set_state(bEnable ? TRISTATE_TRUE : TRISTATE_FALSE);
 }
 
@@ -2510,6 +2532,11 @@ void SvxExtParagraphTabPage::PageCreated(const 
SfxAllItemSet& aSet)
                     DisablePageBreak();
 }
 
+IMPL_LINK(SvxExtParagraphTabPage, KeepHdl_Impl, weld::Toggleable&, rToggle, 
void)
+{
+    aKeepState.ButtonToggled(rToggle);
+}
+
 SvxAsianTabPage::SvxAsianTabPage(weld::Container* pPage, 
weld::DialogController* pController, const SfxItemSet& rSet)
     : SfxTabPage(pPage, pController, "cui/ui/asiantypography.ui", 
"AsianTypography", &rSet)
     , m_xForbiddenRulesCB(m_xBuilder->weld_check_button("checkForbidList"))
diff --git a/cui/uiconfig/ui/textflowpage.ui b/cui/uiconfig/ui/textflowpage.ui
index 9937427cf8c2..47bfc39de261 100644
--- a/cui/uiconfig/ui/textflowpage.ui
+++ b/cui/uiconfig/ui/textflowpage.ui
@@ -114,7 +114,7 @@
               </object>
               <packing>
                 <property name="left-attach">0</property>
-                <property name="top-attach">6</property>
+                <property name="top-attach">4</property>
               </packing>
             </child>
             <child>
@@ -134,7 +134,7 @@
               </object>
               <packing>
                 <property name="left-attach">0</property>
-                <property name="top-attach">5</property>
+                <property name="top-attach">3</property>
               </packing>
             </child>
             <child>
@@ -154,7 +154,7 @@
               </object>
               <packing>
                 <property name="left-attach">0</property>
-                <property name="top-attach">4</property>
+                <property name="top-attach">2</property>
               </packing>
             </child>
             <child>
@@ -174,7 +174,7 @@
               </object>
               <packing>
                 <property name="left-attach">0</property>
-                <property name="top-attach">3</property>
+                <property name="top-attach">1</property>
               </packing>
             </child>
             <child>
@@ -188,7 +188,7 @@
               </object>
               <packing>
                 <property name="left-attach">1</property>
-                <property name="top-attach">3</property>
+                <property name="top-attach">1</property>
               </packing>
             </child>
             <child>
@@ -202,7 +202,7 @@
               </object>
               <packing>
                 <property name="left-attach">1</property>
-                <property name="top-attach">4</property>
+                <property name="top-attach">2</property>
               </packing>
             </child>
             <child>
@@ -216,7 +216,7 @@
               </object>
               <packing>
                 <property name="left-attach">1</property>
-                <property name="top-attach">5</property>
+                <property name="top-attach">3</property>
               </packing>
             </child>
             <child>
@@ -230,37 +230,7 @@
               </object>
               <packing>
                 <property name="left-attach">1</property>
-                <property name="top-attach">6</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkCheckButton" id="checkNoCaps">
-                <property name="label" translatable="yes" 
context="textflowpage|checkNoCaps">Don't hyphenate words in _CAPS</property>
-                <property name="visible">True</property>
-                <property name="can-focus">True</property>
-                <property name="receives-default">False</property>
-                <property name="use-underline">True</property>
-                <property name="draw-indicator">True</property>
-              </object>
-              <packing>
-                <property name="left-attach">0</property>
-                <property name="top-attach">1</property>
-                <property name="width">2</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkCheckButton" id="checkNoLastWord">
-                <property name="label" translatable="yes" 
context="textflowpage|checkNoLastWord">Don't hyphenate the last word</property>
-                <property name="visible">True</property>
-                <property name="can-focus">True</property>
-                <property name="receives-default">False</property>
-                <property name="use-underline">True</property>
-                <property name="draw-indicator">True</property>
-              </object>
-              <packing>
-                <property name="left-attach">0</property>
-                <property name="top-attach">2</property>
-                <property name="width">2</property>
+                <property name="top-attach">4</property>
               </packing>
             </child>
             <child>
@@ -306,6 +276,57 @@
                 <property name="width">2</property>
               </packing>
             </child>
+            <child>
+              <object class="GtkCheckButton" id="checkNoCaps">
+                <property name="label" translatable="yes" 
context="textflowpage|checkNoCaps">Hyphenate words in _CAPS</property>
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="receives-default">False</property>
+                <property name="use-underline">True</property>
+                <property name="draw-indicator">True</property>
+              </object>
+              <packing>
+                <property name="left-attach">0</property>
+                <property name="top-attach">8</property>
+                <property name="width">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkCheckButton" id="checkNoLastWord">
+                <property name="label" translatable="yes" 
context="textflowpage|checkNoLastWord">Hyphenate last word</property>
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="receives-default">False</property>
+                <property name="use-underline">True</property>
+                <property name="draw-indicator">True</property>
+              </object>
+              <packing>
+                <property name="left-attach">0</property>
+                <property name="top-attach">9</property>
+                <property name="width">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkCheckButton" id="checkKeep">
+                <property name="label" translatable="yes" 
context="textflowpage|checkKeep">Hyphenate _across column and page</property>
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="receives-default">False</property>
+                <property name="use-underline">True</property>
+                <property name="inconsistent">True</property>
+                <property name="draw-indicator">True</property>
+                <child internal-child="accessible">
+                  <object class="AtkObject" id="checkKeep-atkobject">
+                    <property name="AtkObject::accessible-description" 
translatable="yes" context="textflowpage|extended_tip|checkKeep">Deselect this 
check box, if you don't want to hyphenate across column and page.</property>
+                  </object>
+                </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">
@@ -720,9 +741,6 @@
         <property name="top-attach">1</property>
       </packing>
     </child>
-    <child>
-      <placeholder/>
-    </child>
     <child internal-child="accessible">
       <object class="AtkObject" id="TextFlowPage-atkobject">
         <property name="AtkObject::accessible-description" translatable="yes" 
context="textflowpage|extended_tip|TextFlowPage">Specify hyphenation and 
pagination options.</property>
diff --git a/editeng/source/items/paraitem.cxx 
b/editeng/source/items/paraitem.cxx
index e080b517eb43..40a057abf1b1 100644
--- a/editeng/source/items/paraitem.cxx
+++ b/editeng/source/items/paraitem.cxx
@@ -567,7 +567,8 @@ SvxHyphenZoneItem::SvxHyphenZoneItem( const bool bHyph, 
const sal_uInt16 nId ) :
     nMinTrail(0),
     nMaxHyphens(255),
     nMinWordLength(0),
-    nTextHyphenZone(0)
+    nTextHyphenZone(0),
+    nKeep(0) // TODO change default value to COLUMN
 {
 }
 
@@ -601,6 +602,9 @@ bool    SvxHyphenZoneItem::QueryValue( uno::Any& rVal, 
sal_uInt8 nMemberId ) con
         case MID_HYPHEN_ZONE:
             rVal <<= static_cast<sal_Int16>(nTextHyphenZone);
         break;
+        case MID_HYPHEN_KEEP:
+            rVal <<= static_cast<sal_Int16>(nKeep);
+        break;
     }
     return true;
 }
@@ -643,6 +647,9 @@ bool SvxHyphenZoneItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
         case MID_HYPHEN_ZONE:
             nTextHyphenZone = nNewVal;
         break;
+        case MID_HYPHEN_KEEP:
+            nKeep = static_cast<sal_uInt8>(nNewVal);
+        break;
     }
     return true;
 }
@@ -661,7 +668,8 @@ bool SvxHyphenZoneItem::operator==( const SfxPoolItem& 
rAttr ) const
             && rItem.nMinTrail == nMinTrail
             && rItem.nMaxHyphens == nMaxHyphens
             && rItem.nMinWordLength == nMinWordLength
-            && rItem.nTextHyphenZone == nTextHyphenZone );
+            && rItem.nTextHyphenZone == nTextHyphenZone
+            && rItem.nKeep == nKeep );
 }
 
 SvxHyphenZoneItem* SvxHyphenZoneItem::Clone( SfxItemPool * ) const
@@ -705,6 +713,7 @@ bool SvxHyphenZoneItem::GetPresentation
             if ( bNoLastWordHyphenation )
                 rText += cpDelimTmp + 
EditResId(RID_SVXITEMS_HYPHEN_LAST_WORD_TRUE);
 
+            rText += OUString::number( nKeep );
             return true;
         }
         case SfxItemPresentation::Complete:
@@ -741,6 +750,25 @@ bool SvxHyphenZoneItem::GetPresentation
             if ( bNoLastWordHyphenation )
                 rText += cpDelimTmp + 
EditResId(RID_SVXITEMS_HYPHEN_LAST_WORD_TRUE);
 
+            switch ( nKeep )
+            {
+                case 0:
+                    rText += cpDelimTmp + 
EditResId(RID_SVXITEMS_HYPHEN_KEEP_AUTO);
+                    break;
+                case 1:
+                    rText += cpDelimTmp + 
EditResId(RID_SVXITEMS_HYPHEN_KEEP_SPREAD);
+                    break;
+                case 2:
+                    rText += cpDelimTmp + 
EditResId(RID_SVXITEMS_HYPHEN_KEEP_PAGE);
+                    break;
+                case 3:
+                    rText += cpDelimTmp + 
EditResId(RID_SVXITEMS_HYPHEN_KEEP_COLUMN);
+                    break;
+                case 4:
+                    rText += cpDelimTmp + 
EditResId(RID_SVXITEMS_HYPHEN_KEEP_ALWAYS);
+                    break;
+            }
+
             return true;
         }
         default: ;//prevent warning
diff --git a/include/editeng/editrids.hrc b/include/editeng/editrids.hrc
index 8d45a7c8a9da..2fd74999c980 100644
--- a/include/editeng/editrids.hrc
+++ b/include/editeng/editrids.hrc
@@ -235,6 +235,11 @@
 #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_KEEP_AUTO           
NC_("RID_SVXITEMS_HYPHEN_KEEP_AUTO", "Automatic hyphenation across page")
+#define RID_SVXITEMS_HYPHEN_KEEP_SPREAD         
NC_("RID_SVXITEMS_HYPHEN_KEEP_SPREAD", "Avoid hyphenation between pages which 
are 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")
+#define RID_SVXITEMS_HYPHEN_KEEP_COLUMN         
NC_("RID_SVXITEMS_HYPHEN_KEEP_COLUMN", "Avoid hyphenation across column and 
page")
+#define RID_SVXITEMS_HYPHEN_KEEP_ALWAYS         
NC_("RID_SVXITEMS_HYPHEN_KEEP_ALWAYS", "Avoid hyphenation across last full 
paragraph line, column and page")
 #define RID_SVXITEMS_PAGEMODEL_COMPLETE         
NC_("RID_SVXITEMS_PAGEMODEL_COMPLETE", "Page Style: ")
 #define RID_SVXITEMS_KERNING_COMPLETE           
NC_("RID_SVXITEMS_KERNING_COMPLETE", "Kerning ")
 #define RID_SVXITEMS_KERNING_EXPANDED           
NC_("RID_SVXITEMS_KERNING_EXPANDED", "locked ")
diff --git a/include/editeng/hyphenzoneitem.hxx 
b/include/editeng/hyphenzoneitem.hxx
index 7104d2d7db58..cbe4a503a0a6 100644
--- a/include/editeng/hyphenzoneitem.hxx
+++ b/include/editeng/hyphenzoneitem.hxx
@@ -41,6 +41,7 @@ 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_uInt8 nKeep;            // avoid hyphenation across page etc., see 
ParagraphHyphenationKeep
 
 public:
     static SfxPoolItem* CreateDefault();
@@ -85,6 +86,9 @@ public:
 
     sal_uInt16 &GetTextHyphenZone() { return nTextHyphenZone; }
     sal_uInt16 GetTextHyphenZone() const { return nTextHyphenZone; }
+
+    sal_uInt8 &GetKeep() { return nKeep; }
+    sal_uInt8 GetKeep() const { return nKeep; }
 };
 
 #endif
diff --git a/include/editeng/memberids.h b/include/editeng/memberids.h
index b44a1486ac16..d1245172eee5 100644
--- a/include/editeng/memberids.h
+++ b/include/editeng/memberids.h
@@ -51,6 +51,7 @@
 #define MID_HYPHEN_NO_LAST_WORD 5
 #define MID_HYPHEN_MIN_WORD_LENGTH 6
 #define MID_HYPHEN_ZONE         7
+#define MID_HYPHEN_KEEP         8
 
 // SvxBoxInfoItem
 #define MID_HORIZONTAL          1
diff --git a/include/unotools/linguprops.hxx b/include/unotools/linguprops.hxx
index 1f182cd3d487..81d2748eef3b 100644
--- a/include/unotools/linguprops.hxx
+++ b/include/unotools/linguprops.hxx
@@ -42,6 +42,7 @@ 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_KEEP                       = 
u"HyphKeep"_ustr;
 
 // UNO property names for Lingu
 // (those not covered by the SpellChecker and Hyphenator
@@ -109,6 +110,7 @@ inline constexpr OUString UPN_IS_GRAMMAR_INTERACTIVE        
  = u"IsInteractiveG
 #define UPH_HYPH_NO_CAPS                    32
 #define UPH_HYPH_NO_LAST_WORD               33
 #define UPH_HYPH_ZONE                       34
+#define UPH_HYPH_KEEP                       35
 
 #ifdef __GNUC__
 #pragma GCC diagnostic pop
diff --git a/include/xmloff/xmltypes.hxx b/include/xmloff/xmltypes.hxx
index 8c9b001b061d..313591c730d0 100644
--- a/include/xmloff/xmltypes.hxx
+++ b/include/xmloff/xmltypes.hxx
@@ -296,6 +296,7 @@
 #define XML_SW_TYPE_PRESPAGE_BACKSIZE   (XML_TEXT_TYPES_START + 128)
 #define XML_SW_TYPE_RTLGUTTER           (XML_TEXT_TYPES_START + 129)
 #define XML_TYPE_COMPLEX_COLOR          (XML_TEXT_TYPES_START + 130)
+#define XML_TYPE_HYPHENATION_KEEP       (XML_TEXT_TYPES_START + 131)
 
 #endif // INCLUDED_XMLOFF_XMLTYPES_HXX
 
diff --git a/offapi/UnoApi_offapi.mk b/offapi/UnoApi_offapi.mk
index 8d2509769466..8a9cb2f58757 100644
--- a/offapi/UnoApi_offapi.mk
+++ b/offapi/UnoApi_offapi.mk
@@ -3760,6 +3760,7 @@ $(eval $(call 
gb_UnoApi_add_idlfiles,offapi,com/sun/star/text,\
        ModuleDispatcher \
        NotePrintMode \
        PageNumberType \
+       ParagraphHyphenationKeepType \
        ParagraphVertAlign \
        PlaceholderType \
        PositionAndSpaceMode \
diff --git a/offapi/com/sun/star/style/ParagraphProperties.idl 
b/offapi/com/sun/star/style/ParagraphProperties.idl
index 49fa8d50d4d6..0d977f4b8e99 100644
--- a/offapi/com/sun/star/style/ParagraphProperties.idl
+++ b/offapi/com/sun/star/style/ParagraphProperties.idl
@@ -428,6 +428,14 @@ published service ParagraphProperties
             @since LibreOffice 7.6
          */
         [optional, property, readonly] long SortedTextId;
+
+        /** Specifies how last word on a page or on other regions is 
hyphenated.
+
+            @see com::sun::star::text::ParagraphHyphenationKeepType
+
+            @since LibreOffice 24.8
+         */
+        [optional, property] long ParaHyphenationKeep;
 };
 
 
diff --git a/offapi/com/sun/star/text/ParagraphHyphenationKeepType.idl 
b/offapi/com/sun/star/text/ParagraphHyphenationKeepType.idl
new file mode 100644
index 000000000000..42ad12077451
--- /dev/null
+++ b/offapi/com/sun/star/text/ParagraphHyphenationKeepType.idl
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+
+ module com {  module sun {  module star {  module text {
+
+/** Specifies how last word on a page or on other regions is hyphenated.
+    The hyphenation methods closely follow the methods described
+    under the hyphenate-limit-last property of the CSS Text Level 4 
specification.
+    The latest version of the aforementioned
+    specification is found here http://www.w3.org/TR/css-text-4/.
+
+    @since LibreOffice 24.8
+ */
+
+published constants ParagraphHyphenationKeepType
+{
+    /** No restriction applies. The word may be hyphenated at the end of any 
region.
+     */
+    const long AUTO = 0;
+
+    /** The last line before any spread break inside the element should not be 
hyphenated.
+        (A spread is a set of two pages that are visible to the reader at the 
same time.)
+     */
+    const long SPREAD = 1;
+
+    /** The last line before page or spread break inside the
+        paragraph should not be hyphenated.
+     */
+    const long PAGE = 2;
+
+    /** The last line before any column, page, or spread break inside the
+        paragraph should not be hyphenated.
+     */
+    const long COLUMN = 3;
+
+    /** The last full line of the paragraph, or the last line before any 
column,
+        page, or spread break inside the paragraph should not be hyphenated.
+     */
+    const long ALWAYS = 4;
+};
+
+
+}; }; }; };
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/sdi/svxitems.sdi b/svx/sdi/svxitems.sdi
index 6e8773315d29..f965dcc00780 100644
--- a/svx/sdi/svxitems.sdi
+++ b/svx/sdi/svxitems.sdi
@@ -275,6 +275,7 @@ struct SvxHyphenZone
     INT16       MaxHyphens    MID_HYPHEN_MAX_HYPHENS;
     INT16       MinWordLength MID_HYPHEN_MIN_WORD_LENGTH;
     INT16       HyphenZone    MID_HYPHEN_ZONE;
+    INT16       HyphenKeep    MID_HYPHEN_KEEP;
 };
 item SvxHyphenZone SvxHyphenZoneItem;
 
diff --git a/sw/CppunitTest_sw_odfexport2.mk b/sw/CppunitTest_sw_odfexport2.mk
index 6ef306d5cd87..43a35b404559 100644
--- a/sw/CppunitTest_sw_odfexport2.mk
+++ b/sw/CppunitTest_sw_odfexport2.mk
@@ -21,6 +21,7 @@ $(eval $(call gb_CppunitTest_use_libraries,sw_odfexport2, \
     comphelper \
     cppu \
     cppuhelper \
+    editeng \
     sal \
     sfx \
     subsequenttest \
diff --git a/sw/inc/inspectorproperties.hrc b/sw/inc/inspectorproperties.hrc
index e232ea1d3f33..bc9686b0aa91 100644
--- a/sw/inc/inspectorproperties.hrc
+++ b/sw/inc/inspectorproperties.hrc
@@ -209,6 +209,7 @@
 #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_KEEP                           
NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Hyphenation Keep")
 #define RID_PARA_INTEROP_GRAB_BAG                           
NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Interop Grab Bag")
 #define RID_PARA_IS_AUTO_FIRST_LINE_INDENT                  
NC_("RID_ATTRIBUTE_NAMES_MAP", "Para is Auto First Line Indent")
 #define RID_PARA_IS_CHARACTER_DISTANCE                      
NC_("RID_ATTRIBUTE_NAMES_MAP", "Para is Character Distance")
diff --git a/sw/inc/unoprnms.hxx b/sw/inc/unoprnms.hxx
index a6a2cf373e87..b97e2a81dc5e 100644
--- a/sw/inc/unoprnms.hxx
+++ b/sw/inc/unoprnms.hxx
@@ -76,6 +76,7 @@ inline constexpr OUString UNO_NAME_PARA_HYPHENATION_ZONE = 
u"ParaHyphenationZone
 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;
+inline constexpr OUString UNO_NAME_PARA_HYPHENATION_KEEP = 
u"ParaHyphenationKeep"_ustr;
 inline constexpr OUString UNO_NAME_LEFT_MARGIN = u"LeftMargin"_ustr;
 inline constexpr OUString UNO_NAME_RIGHT_MARGIN = u"RightMargin"_ustr;
 inline constexpr OUString UNO_NAME_GUTTER_MARGIN = u"GutterMargin"_ustr;
diff --git a/sw/qa/extras/odfexport/data/tdf132599_auto.fodt 
b/sw/qa/extras/odfexport/data/tdf132599_auto.fodt
new file mode 100644
index 000000000000..65b6ddccc295
--- /dev/null
+++ b/sw/qa/extras/odfexport/data/tdf132599_auto.fodt
@@ -0,0 +1,49 @@
+<?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:dom="http://www.w3.org/2001/xml-events"; 
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:calcext="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:oas
 is: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"; office:version="1.3" 
office:mimetype="application/vnd.oasis.opendocument.text">
+ <office:font-face-decls>
+  <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="paragraph">
+   <style:paragraph-properties fo:hyphenation-ladder-count="no-limit"/>
+   <style:text-properties style:use-window-font-color="true" 
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"/>
+  </style:default-style>
+  <style:style style:name="Standard" style:family="paragraph" 
style:class="text">
+   <style:paragraph-properties fo:margin-top="0pt" fo:margin-bottom="0pt" 
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"/>
+   <style:text-properties fo:hyphenate="true" 
fo:hyphenation-remain-char-count="2" fo:hyphenation-push-char-count="2"/>
+  </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="0pt" fo:margin-bottom="7pt" 
style:contextual-spacing="false" fo:line-height="115%" fo:text-align="justify" 
style:justify-single-word="false"/>
+  </style:style>
+  <style:style style:name="Heading_20_1" style:display-name="Heading 1" 
style:family="paragraph" style:parent-style-name="Heading" 
style:next-style-name="Text_20_body" style:class="text">
+   <style:text-properties fo:font-size="18pt" fo:font-weight="bold"/>
+  </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" style:page-number="auto"/>
+   <style:text-properties fo:hyphenate="true" 
fo:hyphenation-remain-char-count="2" fo:hyphenation-push-char-count="2"/>
+  </style:style>
+  <style:style style:name="P3" style:family="paragraph" 
style:parent-style-name="Heading_20_1"/>
+  <style:style style:name="T1" style:family="text">
+   <style:text-properties fo:font-size="49pt"/>
+  </style:style>
+  <style:style style:name="T2" style:family="text">
+   <style:text-properties fo:font-size="49pt"/>
+  </style:style>
+  <style:page-layout style:name="pm1">
+   <style:page-layout-properties fo:page-width="590pt" fo:page-height="792pt" 
style:num-format="1" style:print-orientation="portrait" fo:margin-top="56.69pt" 
fo:margin-bottom="56.69pt" fo:margin-left="56.69pt" fo:margin-right="56.69pt">
+   </style:page-layout-properties>
+  </style:page-layout>
+ </office:automatic-styles>
+ <office:master-styles>
+  <style:master-page style:name="Standard" style:page-layout-name="pm1" 
draw:style-name="dp1"/>
+ </office:master-styles>
+ <office:body>
+  <office:text text:use-soft-page-breaks="true">
+   <text:h text:style-name="P3" text:outline-level="1"><text:span 
text:style-name="T2">Hyphenate last word of the page</text:span><text:span 
text:style-name="T2"/></text:h>
+   <text:p text:style-name="P1"><text:span text:style-name="T1">The Earth is 
no 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:span><text:soft-page-break/><text:span 
text:style-name="T1">except that it has an atmosphere. The Earth is no 
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, except that it has an atmosphere. The Earth is 
</text:span><text:soft-page-break/><text:span text:style-name="T1">no different 
to any other celestial body out…</text:span></text:p>
+  </office:text>
+ </office:body>
+</office:document>
diff --git a/sw/qa/extras/odfexport/data/tdf132599_page.fodt 
b/sw/qa/extras/odfexport/data/tdf132599_page.fodt
new file mode 100644
index 000000000000..45393ad7dc43
--- /dev/null
+++ b/sw/qa/extras/odfexport/data/tdf132599_page.fodt
@@ -0,0 +1,49 @@
+<?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:dom="http://www.w3.org/2001/xml-events"; 
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:calcext="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:oas
 is: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"; office:version="1.3" 
office:mimetype="application/vnd.oasis.opendocument.text">
+ <office:font-face-decls>
+  <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="paragraph">
+   <style:paragraph-properties fo:hyphenation-ladder-count="no-limit"/>
+   <style:text-properties style:use-window-font-color="true" 
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"/>
+  </style:default-style>
+  <style:style style:name="Standard" style:family="paragraph" 
style:class="text">
+   <style:paragraph-properties fo:margin-top="0pt" fo:margin-bottom="0pt" 
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"/>
+   <style:text-properties fo:hyphenate="true" 
fo:hyphenation-remain-char-count="2" fo:hyphenation-push-char-count="2"/>
+  </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="0pt" fo:margin-bottom="7pt" 
style:contextual-spacing="false" fo:line-height="115%" fo:text-align="justify" 
style:justify-single-word="false"/>
+  </style:style>
+  <style:style style:name="Heading_20_1" style:display-name="Heading 1" 
style:family="paragraph" style:parent-style-name="Heading" 
style:next-style-name="Text_20_body" style:class="text">
+   <style:text-properties fo:font-size="18pt" fo:font-weight="bold"/>
+  </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="page" style:page-number="auto"/>
+   <style:text-properties fo:hyphenate="true" 
fo:hyphenation-remain-char-count="2" fo:hyphenation-push-char-count="2"/>
+  </style:style>
+  <style:style style:name="P3" style:family="paragraph" 
style:parent-style-name="Heading_20_1"/>
+  <style:style style:name="T1" style:family="text">
+   <style:text-properties fo:font-size="49pt"/>
+  </style:style>
+  <style:style style:name="T2" style:family="text">
+   <style:text-properties fo:font-size="49pt"/>
+  </style:style>
+  <style:page-layout style:name="pm1">
+   <style:page-layout-properties fo:page-width="590pt" fo:page-height="792pt" 
style:num-format="1" style:print-orientation="portrait" fo:margin-top="56.69pt" 
fo:margin-bottom="56.69pt" fo:margin-left="56.69pt" fo:margin-right="56.69pt">
+   </style:page-layout-properties>
+  </style:page-layout>
+ </office:automatic-styles>
+ <office:master-styles>
+  <style:master-page style:name="Standard" style:page-layout-name="pm1" 
draw:style-name="dp1"/>
+ </office:master-styles>
+ <office:body>
+  <office:text text:use-soft-page-breaks="true">
+   <text:h text:style-name="P3" text:outline-level="1"><text:span 
text:style-name="T2">Hyphenate last word of the page</text:span><text:span 
text:style-name="T2"/></text:h>
+   <text:p text:style-name="P1"><text:span text:style-name="T1">The Earth is 
no 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:span><text:soft-page-break/><text:span 
text:style-name="T1">except that it has an atmosphere. The Earth is no 
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, except that it has an atmosphere. The Earth is 
</text:span><text:soft-page-break/><text:span text:style-name="T1">no different 
to any other celestial body out…</text:span></text:p>
+  </office:text>
+ </office:body>
+</office:document>
diff --git a/sw/qa/extras/odfexport/odfexport2.cxx 
b/sw/qa/extras/odfexport/odfexport2.cxx
index 53431d985e1a..eea133b1d274 100644
--- a/sw/qa/extras/odfexport/odfexport2.cxx
+++ b/sw/qa/extras/odfexport/odfexport2.cxx
@@ -14,6 +14,7 @@
 #include <com/sun/star/drawing/GraphicExportFilter.hpp>
 #include <com/sun/star/drawing/XGraphicExportFilter.hpp>
 #include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/linguistic2/XHyphenator.hpp>
 #include <com/sun/star/style/VerticalAlignment.hpp>
 #include <com/sun/star/text/ColumnSeparatorStyle.hpp>
 #include <com/sun/star/text/XBookmarksSupplier.hpp>
@@ -31,6 +32,7 @@
 #include <comphelper/processfactory.hxx>
 #include <comphelper/propertysequence.hxx>
 #include <comphelper/sequenceashashmap.hxx>
+#include <editeng/unolingu.hxx>
 #include <officecfg/Office/Common.hxx>
 #include <unoprnms.hxx>
 #include <unotxdoc.hxx>
@@ -126,6 +128,31 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf106733)
         "hyphenate"_ostr, "false");
 }
 
+
+CPPUNIT_TEST_FIXTURE(Test, testTdf132599_page)
+{
+    uno::Reference<linguistic2::XHyphenator> xHyphenator = 
LinguMgr::GetHyphenator();
+    if (!xHyphenator->hasLocale(lang::Locale("en", "US", OUString())))
+        return;
+
+    // fo:hyphenation-keep="page"
+    loadAndReload("tdf132599_page.fodt");
+    // This was 2 (not truncated hyphenated line)
+    CPPUNIT_ASSERT_EQUAL(3, getPages());
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testTdf132599_auto)
+{
+    uno::Reference<linguistic2::XHyphenator> xHyphenator = 
LinguMgr::GetHyphenator();
+    if (!xHyphenator->hasLocale(lang::Locale("en", "US", OUString())))
+        return;
+
+    // fo:hyphenation-keep="auto"
+    loadAndReload("tdf132599_auto.fodt");
+    // not truncated hyphenated line
+    CPPUNIT_ASSERT_EQUAL(2, getPages());
+}
+
 DECLARE_ODFEXPORT_TEST(testReferenceLanguage, "referencelanguage.odt")
 {
     CPPUNIT_ASSERT_EQUAL(2, getPages());
diff --git a/sw/qa/uitest/styleInspector/styleInspector.py 
b/sw/qa/uitest/styleInspector/styleInspector.py
index b10c40e71311..46baf8881d2a 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(140, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(141, 
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(140, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(141, 
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(140, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(141, 
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(140, len(xParStyle.getChild('0').getChildren()))
+            self.assertEqual(141, 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(140, len(xParStyle.getChild('0').getChildren()))
+            self.assertEqual(141, 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(140, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(141, 
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(140, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(141, 
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(140, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(141, 
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(140, 
len(xListBox.getChild('1').getChild('0').getChildren()))
+            self.assertEqual(141, 
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(140, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(141, 
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 a0c9ddaafed0..54d5764909c5 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(140, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(141, 
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/text/inftxt.cxx b/sw/source/core/text/inftxt.cxx
index 8eced32e8990..2bbfcd93c99a 100644
--- a/sw/source/core/text/inftxt.cxx
+++ b/sw/source/core/text/inftxt.cxx
@@ -1462,13 +1462,13 @@ void SwTextPaintInfo::DrawViewOpt( const SwLinePortion 
&rPor,
 static void lcl_InitHyphValues( PropertyValues &rVals,
             sal_Int16 nMinLeading, sal_Int16 nMinTrailing,
             bool bNoCapsHyphenation, bool bNoLastWordHyphenation,
-            sal_Int16 nMinWordLength, sal_Int16 nTextHyphZone )
+            sal_Int16 nMinWordLength, sal_Int16 nTextHyphZone, sal_Int16 nKeep 
)
 {
     sal_Int32 nLen = rVals.getLength();
 
     if (0 == nLen)  // yet to be initialized?
     {
-        rVals.realloc( 6 );
+        rVals.realloc( 7 );
         PropertyValue *pVal = rVals.getArray();
 
         pVal[0].Name    = UPN_HYPH_MIN_LEADING;
@@ -1494,8 +1494,12 @@ static void lcl_InitHyphValues( PropertyValues &rVals,
         pVal[5].Name    = UPN_HYPH_ZONE;
         pVal[5].Handle  = UPH_HYPH_ZONE;
         pVal[5].Value   <<= nTextHyphZone;
+
+        pVal[6].Name    = UPN_HYPH_KEEP;
+        pVal[6].Handle  = UPH_HYPH_KEEP;
+        pVal[6].Value   <<= nKeep;
     }
-    else if (6 == nLen) // already initialized once?
+    else if (7 == nLen) // already initialized once?
     {
         PropertyValue *pVal = rVals.getArray();
         pVal[0].Value <<= nMinLeading;
@@ -1504,6 +1508,7 @@ static void lcl_InitHyphValues( PropertyValues &rVals,
         pVal[3].Value <<= bNoLastWordHyphenation;
         pVal[4].Value <<= nMinWordLength;
         pVal[5].Value <<= nTextHyphZone;
+        pVal[6].Value <<= nKeep;
     }
     else {
         OSL_FAIL( "unexpected size of sequence" );
@@ -1512,7 +1517,7 @@ static void lcl_InitHyphValues( PropertyValues &rVals,
 
 const PropertyValues & SwTextFormatInfo::GetHyphValues() const
 {
-    OSL_ENSURE( 6 == m_aHyphVals.getLength(),
+    OSL_ENSURE( 7 == m_aHyphVals.getLength(),
             "hyphenation values not yet initialized" );
     return m_aHyphVals;
 }
@@ -1534,9 +1539,10 @@ 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 nKeep = rAttr.GetKeep();
         lcl_InitHyphValues( m_aHyphVals, nMinimalLeading, nMinimalTrailing,
                  bNoCapsHyphenation, bNoLastWordHyphenation,
-                 nMinimalWordLength, nTextHyphZone );
+                 nMinimalWordLength, nTextHyphZone, nKeep );
     }
     return bAuto;
 }
diff --git a/sw/source/core/text/itrform2.cxx b/sw/source/core/text/itrform2.cxx
index 2d8c3978d73c..6f5fa905665f 100644
--- a/sw/source/core/text/itrform2.cxx
+++ b/sw/source/core/text/itrform2.cxx
@@ -1905,6 +1905,7 @@ TextFrameIndex SwTextFormatter::FormatLine(TextFrameIndex 
const nStartPos)
 
     m_pCurr->SetEndHyph( false );
     m_pCurr->SetMidHyph( false );
+    m_pCurr->SetLastHyph( false );
 
     // fly positioning can make it necessary format a line several times
     // for this, we have to keep a copy of our rest portion
diff --git a/sw/source/core/text/porlay.hxx b/sw/source/core/text/porlay.hxx
index 3b07b7016118..52c4f856095f 100644
--- a/sw/source/core/text/porlay.hxx
+++ b/sw/source/core/text/porlay.hxx
@@ -87,6 +87,7 @@ private:
     bool m_bDummy     : 1;
     bool m_bEndHyph   : 1;
     bool m_bMidHyph   : 1;
+    bool m_bLastHyph  : 1;
     bool m_bFly       : 1;
     bool m_bRest      : 1;
     bool m_bBlinking  : 1;
@@ -123,6 +124,8 @@ public:
     bool IsEndHyph() const { return m_bEndHyph; }
     void SetMidHyph( const bool bNew ) { m_bMidHyph = bNew; }
     bool IsMidHyph() const { return m_bMidHyph; }
+    void SetLastHyph( const bool bNew ) { m_bLastHyph = bNew; }
+    bool IsLastHyph() const { return m_bLastHyph; }
     void SetFly( const bool bNew ) { m_bFly = bNew; }
     bool IsFly() const { return m_bFly; }
     void SetRest( const bool bNew ) { m_bRest = bNew; }
diff --git a/sw/source/core/text/widorp.cxx b/sw/source/core/text/widorp.cxx
index 11e4193d7bd0..bc6586c2f0f6 100644
--- a/sw/source/core/text/widorp.cxx
+++ b/sw/source/core/text/widorp.cxx
@@ -463,15 +463,51 @@ bool WidowsAndOrphans::FindWidows( SwTextFrame *pFrame, 
SwTextMargin &rLine )
 
     const SwTwips nChg = aRectFnSet.YDiff( nTmpY, nDocPrtTop + nOldHeight );
 
-    // below the Widows-threshold...
-    if( rLine.GetLineNr() >= m_nWidLines )
+    // hyphenation-keep: truncate a hyphenated line at the end of the page 
(but not more)
+    int nExtraWidLines = 0;
+    if( rLine.GetLineNr() >= m_nWidLines && pMaster->HasPara() &&
+        ( rLine.GetLineNr() == m_nWidLines || !rLine.GetCurr()->IsEndHyph() ) )
+    {
+        SwParaPortion *pMasterPara = pMaster->GetPara();
+        const SwAttrSet& rSet = 
pFrame->GetTextNodeForParaProps()->GetSwAttrSet();
+        const SvxHyphenZoneItem &rAttr = rSet.GetHyphenZone();
+        if ( pMasterPara && pMasterPara->GetNext() && rAttr.IsHyphen() && 
rAttr.GetKeep() )
+        {
+            SwLineLayout * pNext = pMasterPara->GetNext();
+            SwLineLayout * pCurr = pNext;
+            SwLineLayout * pPrev = pNext;
+            while ( pNext->GetNext() )
+            {
+                pPrev = pCurr;
+                pCurr = pNext;
+                pNext = pNext->GetNext();
+            }
+            // hyphenated line, but not the last remaining one
+            if ( pNext->IsEndHyph() && !pNext->IsLastHyph() )
+            {
+                nExtraWidLines = rLine.GetLineNr() - m_nWidLines + 1;
+                // set remaining line to "last remaining hyphenated line"
+                // to avoid truncating multiple hyphenated lines instead
+                // of a single one
+                if ( pCurr->IsEndHyph() )
+                    pCurr->SetLastHyph( true );
+                // also unset the line before the remaining one
+                // TODO: check also the line after the truncated line?
+                if ( pPrev->IsLastHyph() )
+                    pPrev->SetLastHyph( false );
+            }
+        }
+    }
+
+    // below the Widows-threshold..., with an extra hyphenated line
+    if( rLine.GetLineNr() >= m_nWidLines + nExtraWidLines )
     {
         // Follow to Master I
         // If the Follow *grows*, there is the chance for the Master to
         // receive lines, that it was forced to hand over to the Follow lately:
         // Prepare(Need); check that below nChg!
         // (0W, 2O, 2M, 2F) + 1F = 3M, 2F
-        if( rLine.GetLineNr() > m_nWidLines && pFrame->IsJustWidow() )
+        if( rLine.GetLineNr() > m_nWidLines + nExtraWidLines && 
pFrame->IsJustWidow() )
         {
             // If the Master is locked, it has probably just donated a line
             // to us, we don't return that just because we turned it into
diff --git a/sw/source/core/unocore/unomapproperties.hxx 
b/sw/source/core/unocore/unomapproperties.hxx
index 6d7af3e735bd..976910e22e9f 100644
--- a/sw/source/core/unocore/unomapproperties.hxx
+++ b/sw/source/core/unocore/unomapproperties.hxx
@@ -120,6 +120,7 @@
         { UNO_NAME_PARA_HYPHENATION_MAX_HYPHENS,        RES_PARATR_HYPHENZONE, 
        cppu::UnoType<sal_Int16>::get(),         PropertyAttribute::MAYBEVOID, 
MID_HYPHEN_MAX_HYPHENS                 }, \
         { UNO_NAME_PARA_HYPHENATION_MIN_WORD_LENGTH,    RES_PARATR_HYPHENZONE, 
        cppu::UnoType<sal_Int16>::get(),         PropertyAttribute::MAYBEVOID, 
MID_HYPHEN_MIN_WORD_LENGTH             }, \
         { UNO_NAME_PARA_HYPHENATION_ZONE,               RES_PARATR_HYPHENZONE, 
        cppu::UnoType<sal_Int16>::get(),         PropertyAttribute::MAYBEVOID, 
MID_HYPHEN_ZONE}, \
+        { UNO_NAME_PARA_HYPHENATION_KEEP,               RES_PARATR_HYPHENZONE, 
        cppu::UnoType<sal_Int16>::get(),         PropertyAttribute::MAYBEVOID, 
MID_HYPHEN_KEEP                        }, \
         { UNO_NAME_CHAR_AUTO_KERNING,                   RES_CHRATR_AUTOKERN,   
        cppu::UnoType<bool>::get(),       PropertyAttribute::MAYBEVOID, 0       
                               }, \
         { UNO_NAME_CHAR_BACK_COLOR,                     RES_CHRATR_BACKGROUND, 
        cppu::UnoType<sal_Int32>::get(),         PropertyAttribute::MAYBEVOID, 
MID_BACK_COLOR                         }, \
         { UNO_NAME_CHAR_BACKGROUND_COMPLEX_COLOR,       RES_CHRATR_BACKGROUND, 
        cppu::UnoType<css::util::XComplexColor>::get(), PROPERTY_NONE, 
MID_BACKGROUND_COMPLEX_COLOR },\
@@ -481,6 +482,7 @@
                     { UNO_NAME_PARA_HYPHENATION_MAX_HYPHENS, 
RES_PARATR_HYPHENZONE,       cppu::UnoType<sal_Int16>::get(),   
PropertyAttribute::MAYBEVOID, MID_HYPHEN_MAX_HYPHENS},\
                     { UNO_NAME_PARA_HYPHENATION_MIN_WORD_LENGTH, 
RES_PARATR_HYPHENZONE,       cppu::UnoType<sal_Int16>::get(),   
PropertyAttribute::MAYBEVOID, MID_HYPHEN_MIN_WORD_LENGTH},\
                     { UNO_NAME_PARA_HYPHENATION_ZONE, RES_PARATR_HYPHENZONE,   
                   cppu::UnoType<sal_Int16>::get(),   
PropertyAttribute::MAYBEVOID, MID_HYPHEN_ZONE},\
+                    { UNO_NAME_PARA_HYPHENATION_KEEP, RES_PARATR_HYPHENZONE,   
                   cppu::UnoType<sal_Int16>::get(),   
PropertyAttribute::MAYBEVOID, MID_HYPHEN_KEEP},\
                     { UNO_NAME_NUMBERING_STYLE_NAME, RES_PARATR_NUMRULE,  
cppu::UnoType<OUString>::get(),         PropertyAttribute::MAYBEVOID,   0},\
                     { UNO_NAME_NUMBERING_LEVEL, RES_PARATR_LIST_LEVEL,    
cppu::UnoType<sal_Int16>::get(),        PropertyAttribute::MAYBEVOID,   0},\
                     { UNO_NAME_PARA_USER_DEFINED_ATTRIBUTES, 
RES_UNKNOWNATR_CONTAINER, cppu::UnoType<css::container::XNameContainer>::get(), 
PropertyAttribute::MAYBEVOID, 0 },\
diff --git a/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx 
b/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx
index c6376910f1a5..94dd4a9e8a87 100644
--- a/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx
+++ b/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx
@@ -278,6 +278,7 @@ static OUString PropertyNametoRID(const OUString& rName)
         { "ParaHyphenationNoLastWord", RID_PARA_HYPHENATION_NO_LAST_WORD },
         { "ParaHyphenationMinWordLength", RID_PARA_HYPHENATION_MIN_WORD_LENGTH 
},
         { "ParaHyphenationZone", RID_PARA_HYPHENATION_ZONE },
+        { "ParaHyphenationKeep", RID_PARA_HYPHENATION_KEEP },
         { "ParaInteropGrabBag", RID_PARA_INTEROP_GRAB_BAG },
         { "ParaIsAutoFirstLineIndent", RID_PARA_IS_AUTO_FIRST_LINE_INDENT },
         { "ParaIsCharacterDistance", RID_PARA_IS_CHARACTER_DISTANCE },
diff --git a/xmloff/inc/xmlprop.hxx b/xmloff/inc/xmlprop.hxx
index b442dcb2b45c..273e91404e21 100644
--- a/xmloff/inc/xmlprop.hxx
+++ b/xmloff/inc/xmlprop.hxx
@@ -495,6 +495,7 @@ inline constexpr OUString PROP_ParaHyphenationMinWordLength 
= u"ParaHyphenationM
 inline constexpr OUString PROP_ParaHyphenationNoCaps = 
u"ParaHyphenationNoCaps"_ustr;
 inline constexpr OUString PROP_ParaHyphenationNoLastWord = 
u"ParaHyphenationNoLastWord"_ustr;
 inline constexpr OUString PROP_ParaHyphenationZone = 
u"ParaHyphenationZone"_ustr;
+inline constexpr OUString PROP_ParaHyphenationKeep = 
u"ParaHyphenationKeep"_ustr;
 inline constexpr OUString PROP_ParaIsAutoFirstLineIndent = 
u"ParaIsAutoFirstLineIndent"_ustr;
 inline constexpr OUString PROP_ParaIsCharacterDistance = 
u"ParaIsCharacterDistance"_ustr;
 inline constexpr OUString PROP_ParaIsConnectBorder = 
u"ParaIsConnectBorder"_ustr;
diff --git a/xmloff/source/text/txtprhdl.cxx b/xmloff/source/text/txtprhdl.cxx
index f7c3a9790dda..a80a2804b0ea 100644
--- a/xmloff/source/text/txtprhdl.cxx
+++ b/xmloff/source/text/txtprhdl.cxx
@@ -36,6 +36,7 @@
 #include <com/sun/star/text/RubyAdjust.hpp>
 #include <com/sun/star/text/RubyPosition.hpp>
 #include <com/sun/star/text/FontEmphasis.hpp>
+#include <com/sun/star/text/ParagraphHyphenationKeepType.hpp>
 #include <com/sun/star/text/ParagraphVertAlign.hpp>
 #include <com/sun/star/graphic/XGraphic.hpp>
 #include <sax/tools/converter.hxx>
@@ -231,6 +232,14 @@ SvXMLEnumMapEntry<sal_uInt16> const 
pXML_ParaVerticalAlign_Enum[] =
     { XML_TOKEN_INVALID, 0 }
 };
 
+SvXMLEnumMapEntry<sal_uInt16> const pXML_ParaHyphenationKeep_Enum[] =
+{
+    { XML_AUTO,         ParagraphHyphenationKeepType::AUTO    },
+    // keep page and column for interoperability
+    { XML_PAGE,         ParagraphHyphenationKeepType::COLUMN  },
+    { XML_TOKEN_INVALID, 0 }
+};
+
 // OD 2004-05-05 #i28701#
 SvXMLEnumMapEntry<sal_uInt16> const pXML_WrapInfluenceOnPosition_Enum[] =
 {
@@ -1415,6 +1424,10 @@ static const XMLPropertyHandler *GetPropertyHandler
     case XML_TYPE_COMPLEX_COLOR:
         pHdl = new XMLComplexColorHandler;
         break;
+    case XML_TYPE_HYPHENATION_KEEP:
+        pHdl = new XMLConstantsPropertyHandler( pXML_ParaHyphenationKeep_Enum, 
XML_TOKEN_INVALID );
+        break;
+
     default:
     {
         OSL_ENSURE(false, "XMLPropertyHandler missing (!)");
diff --git a/xmloff/source/text/txtprmap.cxx b/xmloff/source/text/txtprmap.cxx
index 98d347d9edbe..05a3284ffbc3 100644
--- a/xmloff/source/text/txtprmap.cxx
+++ b/xmloff/source/text/txtprmap.cxx
@@ -345,6 +345,7 @@ XMLPropertyMapEntry constexpr aXMLParaPropMap[] =
     MAP_EXT( PROP_ParaHyphenationNoLastWord,    XML_NAMESPACE_LO_EXT, 
XML_HYPHENATION_NO_LAST_WORD,      XML_TYPE_BOOL|XML_TYPE_PROP_TEXT,          0 
),
     MAP_EXT( PROP_ParaHyphenationMinWordLength, XML_NAMESPACE_LO_EXT, 
XML_HYPHENATION_WORD_CHAR_COUNT,   XML_TYPE_NUMBER16_NONE|XML_TYPE_PROP_TEXT, 0 
),
     MAP_EXT( PROP_ParaHyphenationZone,          XML_NAMESPACE_LO_EXT, 
XML_HYPHENATION_ZONE,              XML_TYPE_NUMBER16_NONE|XML_TYPE_PROP_TEXT, 0 
),
+    MP_E( PROP_ParaHyphenationKeep,             XML_NAMESPACE_FO,     
XML_HYPHENATION_KEEP,              XML_TYPE_HYPHENATION_KEEP,                 0 
),
     // RES_PARATR_DROP
     MP_E( PROP_DropCapWholeWord,     XML_NAMESPACE_STYLE, XML_LENGTH,     
MID_FLAG_SPECIAL_ITEM|XML_TYPE_BOOL,         CTF_DROPCAPWHOLEWORD ),
     MP_E( PROP_DropCapCharStyleName, XML_NAMESPACE_STYLE, XML_STYLE_NAME, 
MID_FLAG_SPECIAL_ITEM|XML_TYPE_STRING,       CTF_DROPCAPCHARSTYLE ),

Reply via email to