cui/source/inc/paragrph.hxx                                 |    1 
 cui/source/tabpages/paragrph.cxx                            |    9 +
 cui/uiconfig/ui/textflowpage.ui                             |   27 ++-
 editeng/source/editeng/editdoc.cxx                          |    1 
 editeng/source/editeng/eerdll.cxx                           |   99 ++++++------
 editeng/source/items/paraitem.cxx                           |   13 +
 include/editeng/eeitem.hxx                                  |   25 +--
 include/editeng/hyphenzoneitem.hxx                          |    3 
 include/editeng/memberids.h                                 |    1 
 include/editeng/unotext.hxx                                 |    1 
 include/unotools/linguprops.hxx                             |    2 
 include/xmloff/xmltoken.hxx                                 |    1 
 offapi/com/sun/star/style/ParagraphProperties.idl           |    7 
 schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng |    9 +
 sw/inc/inspectorproperties.hrc                              |    1 
 sw/inc/unoprnms.hxx                                         |    1 
 sw/qa/extras/layout/data/tdf149248.odt                      |binary
 sw/qa/extras/layout/layout.cxx                              |   15 +
 sw/qa/extras/odfexport/data/tdf149248.odt                   |binary
 sw/qa/extras/odfexport/odfexport.cxx                        |    7 
 sw/qa/uitest/styleInspector/styleInspector.py               |   20 +-
 sw/qa/uitest/styleInspector/tdf137513.py                    |    2 
 sw/source/core/text/guess.cxx                               |   23 ++
 sw/source/core/text/inftxt.cxx                              |   18 +-
 sw/source/core/unocore/unomapproperties.hxx                 |    2 
 sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx       |    1 
 xmloff/source/core/xmltoken.cxx                             |    1 
 xmloff/source/text/txtprmap.cxx                             |    1 
 xmloff/source/token/tokens.txt                              |    1 
 29 files changed, 208 insertions(+), 84 deletions(-)

New commits:
commit 8c018910ae4d8701b1ce2a95727b9baed4016da3
Author:     László Németh <nem...@numbertext.org>
AuthorDate: Thu May 26 09:09:38 2022 +0200
Commit:     László Németh <nem...@numbertext.org>
CommitDate: Thu May 26 16:37:02 2022 +0200

    tdf#149248 sw offapi xmloff: add option to not hyphenate last word
    
    Add option to disable automatic hyphenation of the last word of
    paragraphs for better typography.
    
    Note: the same option used e.g. in Adobe InDesign, and a
    similar one in CSS Text Module Level 4 (hyphenate-limit-last).
    
    * Add checkbox to Text Flow in paragraph dialog
    * Store property in paragraph model 
(com::sun::star::style::ParagraphProperties::ParaHyphenationNoLastWord)
    * Add ODF import/export
    * Add ODF unit test
    * Add layout test
    
    Follow-up to commit 72bd0df107ee47c4d54fa88b4960d32ea03e9f69
    "tdf#121658 Add option to not hyphenate words in CAPS".
    
    Change-Id: Ida29c65b5a7cbfd7c399c342781531a6fb86f639
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/134985
    Tested-by: Jenkins
    Reviewed-by: László Németh <nem...@numbertext.org>

diff --git a/cui/source/inc/paragrph.hxx b/cui/source/inc/paragrph.hxx
index 73cc5faf32cd..0967c32a060e 100644
--- a/cui/source/inc/paragrph.hxx
+++ b/cui/source/inc/paragrph.hxx
@@ -225,6 +225,7 @@ private:
     // hyphenation
     std::unique_ptr<weld::CheckButton> m_xHyphenBox;
     std::unique_ptr<weld::CheckButton> m_xHyphenNoCapsBox;
+    std::unique_ptr<weld::CheckButton> m_xHyphenNoLastWordBox;
     std::unique_ptr<weld::Label> m_xBeforeText;
     std::unique_ptr<weld::SpinButton> m_xExtHyphenBeforeBox;
     std::unique_ptr<weld::Label> m_xAfterText;
diff --git a/cui/source/tabpages/paragrph.cxx b/cui/source/tabpages/paragrph.cxx
index 14a43480caa5..29bc2fc76b43 100644
--- a/cui/source/tabpages/paragrph.cxx
+++ b/cui/source/tabpages/paragrph.cxx
@@ -1352,6 +1352,7 @@ bool SvxExtParagraphTabPage::FillItemSet( SfxItemSet* 
rOutSet )
 
     if ( m_xHyphenBox->get_state_changed_from_saved() ||
          m_xHyphenNoCapsBox->get_state_changed_from_saved() ||
+         m_xHyphenNoLastWordBox->get_state_changed_from_saved() ||
          m_xExtHyphenBeforeBox->get_value_changed_from_saved() ||
          m_xExtHyphenAfterBox->get_value_changed_from_saved() ||
          m_xMaxHyphenEdit->get_value_changed_from_saved() )
@@ -1360,6 +1361,7 @@ bool SvxExtParagraphTabPage::FillItemSet( SfxItemSet* 
rOutSet )
             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);
 
         if ( eHyphenState == TRISTATE_TRUE )
         {
@@ -1570,6 +1572,7 @@ 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_xExtHyphenBeforeBox->set_value(rHyphen.GetMinLead());
         m_xExtHyphenAfterBox->set_value(rHyphen.GetMinTrail());
@@ -1579,9 +1582,11 @@ void SvxExtParagraphTabPage::Reset( const SfxItemSet* 
rSet )
     {
         m_xHyphenBox->set_state(TRISTATE_INDET);
         m_xHyphenNoCapsBox->set_state(TRISTATE_INDET);
+        m_xHyphenNoLastWordBox->set_state(TRISTATE_INDET);
     }
     bool bEnable = bItemAvailable && bIsHyphen;
     m_xHyphenNoCapsBox->set_sensitive(bEnable);
+    m_xHyphenNoLastWordBox->set_sensitive(bEnable);
     m_xExtHyphenBeforeBox->set_sensitive(bEnable);
     m_xExtHyphenAfterBox->set_sensitive(bEnable);
     m_xBeforeText->set_sensitive(bEnable);
@@ -1843,6 +1848,7 @@ void SvxExtParagraphTabPage::ChangesApplied()
 {
     m_xHyphenBox->save_state();
     m_xHyphenNoCapsBox->save_state();
+    m_xHyphenNoLastWordBox->save_state();
     m_xExtHyphenBeforeBox->set_value(m_xExtHyphenBeforeBox->get_value());
     m_xExtHyphenAfterBox->set_value(m_xExtHyphenAfterBox->get_value());
     m_xMaxHyphenEdit->set_value(m_xMaxHyphenEdit->get_value());
@@ -1889,6 +1895,7 @@ 
SvxExtParagraphTabPage::SvxExtParagraphTabPage(weld::Container* pPage, weld::Dia
     // Hyphenation
     , m_xHyphenBox(m_xBuilder->weld_check_button("checkAuto"))
     , m_xHyphenNoCapsBox(m_xBuilder->weld_check_button("checkNoCaps"))
+    , m_xHyphenNoLastWordBox(m_xBuilder->weld_check_button("checkNoLastWord"))
     , m_xBeforeText(m_xBuilder->weld_label("labelLineBegin"))
     , m_xExtHyphenBeforeBox(m_xBuilder->weld_spin_button("spinLineEnd"))
     , m_xAfterText(m_xBuilder->weld_label("labelLineEnd"))
@@ -1956,6 +1963,7 @@ 
SvxExtParagraphTabPage::SvxExtParagraphTabPage(weld::Container* pPage, weld::Dia
     bHtmlMode = true;
     m_xHyphenBox->set_sensitive(false);
     m_xHyphenNoCapsBox->set_sensitive(false);
+    m_xHyphenNoLastWordBox->set_sensitive(false);
     m_xBeforeText->set_sensitive(false);
     m_xExtHyphenBeforeBox->set_sensitive(false);
     m_xAfterText->set_sensitive(false);
@@ -2088,6 +2096,7 @@ void SvxExtParagraphTabPage::HyphenClickHdl()
 {
     bool bEnable = m_xHyphenBox->get_state() == TRISTATE_TRUE;
     m_xHyphenNoCapsBox->set_sensitive(bEnable);
+    m_xHyphenNoLastWordBox->set_sensitive(bEnable);
     m_xBeforeText->set_sensitive(bEnable);
     m_xExtHyphenBeforeBox->set_sensitive(bEnable);
     m_xAfterText->set_sensitive(bEnable);
diff --git a/cui/uiconfig/ui/textflowpage.ui b/cui/uiconfig/ui/textflowpage.ui
index a0b307c674aa..9ff931ddc6b8 100644
--- a/cui/uiconfig/ui/textflowpage.ui
+++ b/cui/uiconfig/ui/textflowpage.ui
@@ -102,7 +102,7 @@
               </object>
               <packing>
                 <property name="left_attach">0</property>
-                <property name="top_attach">4</property>
+                <property name="top_attach">5</property>
               </packing>
             </child>
             <child>
@@ -122,7 +122,7 @@
               </object>
               <packing>
                 <property name="left_attach">0</property>
-                <property name="top_attach">3</property>
+                <property name="top_attach">4</property>
               </packing>
             </child>
             <child>
@@ -142,7 +142,7 @@
               </object>
               <packing>
                 <property name="left_attach">0</property>
-                <property name="top_attach">2</property>
+                <property name="top_attach">3</property>
               </packing>
             </child>
             <child>
@@ -156,7 +156,7 @@
               </object>
               <packing>
                 <property name="left_attach">1</property>
-                <property name="top_attach">2</property>
+                <property name="top_attach">3</property>
               </packing>
             </child>
             <child>
@@ -170,7 +170,7 @@
               </object>
               <packing>
                 <property name="left_attach">1</property>
-                <property name="top_attach">3</property>
+                <property name="top_attach">4</property>
               </packing>
             </child>
             <child>
@@ -184,7 +184,7 @@
               </object>
               <packing>
                 <property name="left_attach">1</property>
-                <property name="top_attach">4</property>
+                <property name="top_attach">5</property>
               </packing>
             </child>
             <child>
@@ -202,6 +202,21 @@
                 <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>
+              </packing>
+            </child>
           </object>
         </child>
         <child type="label">
diff --git a/editeng/source/editeng/editdoc.cxx 
b/editeng/source/editeng/editdoc.cxx
index b0df5683b03a..0b8e41f48976 100644
--- a/editeng/source/editeng/editdoc.cxx
+++ b/editeng/source/editeng/editdoc.cxx
@@ -164,6 +164,7 @@ const SfxItemInfo aItemInfos[EDITITEMCOUNT] = {
         { SID_ATTR_NUMBERING_RULE, true },         // EE_PARA_NUMBULL
         { 0, true },                               // EE_PARA_HYPHENATE
         { 0, true },                               // EE_PARA_HYPHENATE_NO_CAPS
+        { 0, true },                               // 
EE_PARA_HYPHENATE_NO_LAST_WORD
         { 0, true },                               // EE_PARA_BULLETSTATE
         { 0, true },                               // EE_PARA_OUTLLRSPACE
         { SID_ATTR_PARA_OUTLLEVEL, true },         // EE_PARA_OUTLLEVEL
diff --git a/editeng/source/editeng/eerdll.cxx 
b/editeng/source/editeng/eerdll.cxx
index 0002d68caafb..b7120bd1befd 100644
--- a/editeng/source/editeng/eerdll.cxx
+++ b/editeng/source/editeng/eerdll.cxx
@@ -89,59 +89,60 @@ DefItems::DefItems()
     rDefItems[5]  = new SvxNumBulletItem( aDefaultNumRule, EE_PARA_NUMBULLET );
     rDefItems[6]  = new SfxBoolItem( EE_PARA_HYPHENATE, false );
     rDefItems[7]  = new SfxBoolItem( EE_PARA_HYPHENATE_NO_CAPS, false );
-    rDefItems[8]  = new SfxBoolItem( EE_PARA_BULLETSTATE, true );
-    rDefItems[9]  = new SvxLRSpaceItem( EE_PARA_OUTLLRSPACE );
-    rDefItems[10]  = new SfxInt16Item( EE_PARA_OUTLLEVEL, -1 );
-    rDefItems[11] = new SvxBulletItem( EE_PARA_BULLET );
-    rDefItems[12] = new SvxLRSpaceItem( EE_PARA_LRSPACE );
-    rDefItems[13] = new SvxULSpaceItem( EE_PARA_ULSPACE );
-    rDefItems[14] = new SvxLineSpacingItem( 0, EE_PARA_SBL );
-    rDefItems[15] = new SvxAdjustItem( SvxAdjust::Left, EE_PARA_JUST );
-    rDefItems[16] = new SvxTabStopItem( 0, 0, SvxTabAdjust::Left, EE_PARA_TABS 
);
-    rDefItems[17] = new SvxJustifyMethodItem( SvxCellJustifyMethod::Auto, 
EE_PARA_JUST_METHOD );
-    rDefItems[18] = new SvxVerJustifyItem( SvxCellVerJustify::Standard, 
EE_PARA_VER_JUST );
+    rDefItems[8]  = new SfxBoolItem( EE_PARA_HYPHENATE_NO_LAST_WORD, false );
+    rDefItems[9]  = new SfxBoolItem( EE_PARA_BULLETSTATE, true );
+    rDefItems[10]  = new SvxLRSpaceItem( EE_PARA_OUTLLRSPACE );
+    rDefItems[11]  = new SfxInt16Item( EE_PARA_OUTLLEVEL, -1 );
+    rDefItems[12] = new SvxBulletItem( EE_PARA_BULLET );
+    rDefItems[13] = new SvxLRSpaceItem( EE_PARA_LRSPACE );
+    rDefItems[14] = new SvxULSpaceItem( EE_PARA_ULSPACE );
+    rDefItems[15] = new SvxLineSpacingItem( 0, EE_PARA_SBL );
+    rDefItems[16] = new SvxAdjustItem( SvxAdjust::Left, EE_PARA_JUST );
+    rDefItems[17] = new SvxTabStopItem( 0, 0, SvxTabAdjust::Left, EE_PARA_TABS 
);
+    rDefItems[18] = new SvxJustifyMethodItem( SvxCellJustifyMethod::Auto, 
EE_PARA_JUST_METHOD );
+    rDefItems[19] = new SvxVerJustifyItem( SvxCellVerJustify::Standard, 
EE_PARA_VER_JUST );
 
     // Character attributes:
-    rDefItems[19] = new SvxColorItem( COL_AUTO, EE_CHAR_COLOR );
-    rDefItems[20] = new SvxFontItem( EE_CHAR_FONTINFO );
-    rDefItems[21] = new SvxFontHeightItem( 240, 100, EE_CHAR_FONTHEIGHT );
-    rDefItems[22] = new SvxCharScaleWidthItem( 100, EE_CHAR_FONTWIDTH );
-    rDefItems[23] = new SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT );
-    rDefItems[24] = new SvxUnderlineItem( LINESTYLE_NONE, EE_CHAR_UNDERLINE );
-    rDefItems[25] = new SvxCrossedOutItem( STRIKEOUT_NONE, EE_CHAR_STRIKEOUT );
-    rDefItems[26] = new SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC );
-    rDefItems[27] = new SvxContourItem( false, EE_CHAR_OUTLINE );
-    rDefItems[28] = new SvxShadowedItem( false, EE_CHAR_SHADOW );
-    rDefItems[29] = new SvxEscapementItem( 0, 100, EE_CHAR_ESCAPEMENT );
-    rDefItems[30] = new SvxAutoKernItem( false, EE_CHAR_PAIRKERNING );
-    rDefItems[31] = new SvxKerningItem( 0, EE_CHAR_KERNING );
-    rDefItems[32] = new SvxWordLineModeItem( false, EE_CHAR_WLM );
-    rDefItems[33] = new SvxLanguageItem( LANGUAGE_DONTKNOW, EE_CHAR_LANGUAGE );
-    rDefItems[34] = new SvxLanguageItem( LANGUAGE_DONTKNOW, 
EE_CHAR_LANGUAGE_CJK );
-    rDefItems[35] = new SvxLanguageItem( LANGUAGE_DONTKNOW, 
EE_CHAR_LANGUAGE_CTL );
-    rDefItems[36] = new SvxFontItem( EE_CHAR_FONTINFO_CJK );
-    rDefItems[37] = new SvxFontItem( EE_CHAR_FONTINFO_CTL );
-    rDefItems[38] = new SvxFontHeightItem( 240, 100, EE_CHAR_FONTHEIGHT_CJK );
-    rDefItems[39] = new SvxFontHeightItem( 240, 100, EE_CHAR_FONTHEIGHT_CTL );
-    rDefItems[40] = new SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT_CJK );
-    rDefItems[41] = new SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT_CTL );
-    rDefItems[42] = new SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC_CJK );
-    rDefItems[43] = new SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC_CTL );
-    rDefItems[44] = new SvxEmphasisMarkItem( FontEmphasisMark::NONE, 
EE_CHAR_EMPHASISMARK );
-    rDefItems[45] = new SvxCharReliefItem( FontRelief::NONE, EE_CHAR_RELIEF );
-    rDefItems[46] = new SfxVoidItem( EE_CHAR_RUBI_DUMMY );
-    rDefItems[47] = new SvXMLAttrContainerItem( EE_CHAR_XMLATTRIBS );
-    rDefItems[48] = new SvxOverlineItem( LINESTYLE_NONE, EE_CHAR_OVERLINE );
-    rDefItems[49] = new SvxCaseMapItem( SvxCaseMap::NotMapped, EE_CHAR_CASEMAP 
);
-    rDefItems[50] = new SfxGrabBagItem( EE_CHAR_GRABBAG );
-    rDefItems[51] = new SvxColorItem( COL_AUTO, EE_CHAR_BKGCOLOR );
+    rDefItems[20] = new SvxColorItem( COL_AUTO, EE_CHAR_COLOR );
+    rDefItems[21] = new SvxFontItem( EE_CHAR_FONTINFO );
+    rDefItems[22] = new SvxFontHeightItem( 240, 100, EE_CHAR_FONTHEIGHT );
+    rDefItems[23] = new SvxCharScaleWidthItem( 100, EE_CHAR_FONTWIDTH );
+    rDefItems[24] = new SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT );
+    rDefItems[25] = new SvxUnderlineItem( LINESTYLE_NONE, EE_CHAR_UNDERLINE );
+    rDefItems[26] = new SvxCrossedOutItem( STRIKEOUT_NONE, EE_CHAR_STRIKEOUT );
+    rDefItems[27] = new SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC );
+    rDefItems[28] = new SvxContourItem( false, EE_CHAR_OUTLINE );
+    rDefItems[29] = new SvxShadowedItem( false, EE_CHAR_SHADOW );
+    rDefItems[30] = new SvxEscapementItem( 0, 100, EE_CHAR_ESCAPEMENT );
+    rDefItems[31] = new SvxAutoKernItem( false, EE_CHAR_PAIRKERNING );
+    rDefItems[32] = new SvxKerningItem( 0, EE_CHAR_KERNING );
+    rDefItems[33] = new SvxWordLineModeItem( false, EE_CHAR_WLM );
+    rDefItems[34] = new SvxLanguageItem( LANGUAGE_DONTKNOW, EE_CHAR_LANGUAGE );
+    rDefItems[35] = new SvxLanguageItem( LANGUAGE_DONTKNOW, 
EE_CHAR_LANGUAGE_CJK );
+    rDefItems[36] = new SvxLanguageItem( LANGUAGE_DONTKNOW, 
EE_CHAR_LANGUAGE_CTL );
+    rDefItems[37] = new SvxFontItem( EE_CHAR_FONTINFO_CJK );
+    rDefItems[38] = new SvxFontItem( EE_CHAR_FONTINFO_CTL );
+    rDefItems[39] = new SvxFontHeightItem( 240, 100, EE_CHAR_FONTHEIGHT_CJK );
+    rDefItems[40] = new SvxFontHeightItem( 240, 100, EE_CHAR_FONTHEIGHT_CTL );
+    rDefItems[41] = new SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT_CJK );
+    rDefItems[42] = new SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT_CTL );
+    rDefItems[43] = new SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC_CJK );
+    rDefItems[44] = new SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC_CTL );
+    rDefItems[45] = new SvxEmphasisMarkItem( FontEmphasisMark::NONE, 
EE_CHAR_EMPHASISMARK );
+    rDefItems[46] = new SvxCharReliefItem( FontRelief::NONE, EE_CHAR_RELIEF );
+    rDefItems[47] = new SfxVoidItem( EE_CHAR_RUBI_DUMMY );
+    rDefItems[48] = new SvXMLAttrContainerItem( EE_CHAR_XMLATTRIBS );
+    rDefItems[49] = new SvxOverlineItem( LINESTYLE_NONE, EE_CHAR_OVERLINE );
+    rDefItems[50] = new SvxCaseMapItem( SvxCaseMap::NotMapped, EE_CHAR_CASEMAP 
);
+    rDefItems[51] = new SfxGrabBagItem( EE_CHAR_GRABBAG );
+    rDefItems[52] = new SvxColorItem( COL_AUTO, EE_CHAR_BKGCOLOR );
     // Features
-    rDefItems[52] = new SfxVoidItem( EE_FEATURE_TAB );
-    rDefItems[53] = new SfxVoidItem( EE_FEATURE_LINEBR );
-    rDefItems[54] = new SvxColorItem( COL_RED, EE_FEATURE_NOTCONV );
-    rDefItems[55] = new SvxFieldItem( SvxFieldData(), EE_FEATURE_FIELD );
+    rDefItems[53] = new SfxVoidItem( EE_FEATURE_TAB );
+    rDefItems[54] = new SfxVoidItem( EE_FEATURE_LINEBR );
+    rDefItems[55] = new SvxColorItem( COL_RED, EE_FEATURE_NOTCONV );
+    rDefItems[56] = new SvxFieldItem( SvxFieldData(), EE_FEATURE_FIELD );
 
-    assert(EDITITEMCOUNT == 56 && "ITEMCOUNT changed, adjust DefItems!");
+    assert(EDITITEMCOUNT == 57 && "ITEMCOUNT changed, adjust DefItems!");
 
     // Init DefFonts:
     GetDefaultFonts( *static_cast<SvxFontItem*>(rDefItems[EE_CHAR_FONTINFO - 
EE_ITEMS_START]),
diff --git a/editeng/source/items/paraitem.cxx 
b/editeng/source/items/paraitem.cxx
index 6f5e5f9acd80..56d9628276b3 100644
--- a/editeng/source/items/paraitem.cxx
+++ b/editeng/source/items/paraitem.cxx
@@ -555,6 +555,7 @@ SvxHyphenZoneItem::SvxHyphenZoneItem( const bool bHyph, 
const sal_uInt16 nId ) :
     bHyphen(bHyph),
     bPageEnd(true),
     bNoCapsHyphenation(false),
+    bNoLastWordHyphenation(false),
     nMinLead(0),
     nMinTrail(0),
     nMaxHyphens(255)
@@ -582,6 +583,9 @@ bool    SvxHyphenZoneItem::QueryValue( uno::Any& rVal, 
sal_uInt8 nMemberId ) con
         case MID_HYPHEN_NO_CAPS:
             rVal <<= bNoCapsHyphenation;
         break;
+        case MID_HYPHEN_NO_LAST_WORD:
+            rVal <<= bNoLastWordHyphenation;
+        break;
     }
     return true;
 }
@@ -591,9 +595,12 @@ bool SvxHyphenZoneItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
     nMemberId &= ~CONVERT_TWIPS;
     sal_Int16 nNewVal = 0;
 
-    if( nMemberId != MID_IS_HYPHEN && nMemberId != MID_HYPHEN_NO_CAPS )
+    if( nMemberId != MID_IS_HYPHEN && nMemberId != MID_HYPHEN_NO_CAPS &&
+                nMemberId != MID_HYPHEN_NO_LAST_WORD )
+    {
         if(!(rVal >>= nNewVal))
             return false;
+    }
 
     switch(nMemberId)
     {
@@ -612,6 +619,9 @@ bool SvxHyphenZoneItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
         case MID_HYPHEN_NO_CAPS:
             bNoCapsHyphenation = Any2Bool(rVal);
         break;
+        case MID_HYPHEN_NO_LAST_WORD:
+            bNoLastWordHyphenation = Any2Bool(rVal);
+        break;
     }
     return true;
 }
@@ -624,6 +634,7 @@ bool SvxHyphenZoneItem::operator==( const SfxPoolItem& 
rAttr ) const
     const SvxHyphenZoneItem& rItem = static_cast<const 
SvxHyphenZoneItem&>(rAttr);
     return ( rItem.bHyphen == bHyphen
             && rItem.bNoCapsHyphenation == bNoCapsHyphenation
+            && rItem.bNoLastWordHyphenation == bNoLastWordHyphenation
             && rItem.bPageEnd == bPageEnd
             && rItem.nMinLead == nMinLead
             && rItem.nMinTrail == nMinTrail
diff --git a/include/editeng/eeitem.hxx b/include/editeng/eeitem.hxx
index 28e5d8a0dd80..f862244865c1 100644
--- a/include/editeng/eeitem.hxx
+++ b/include/editeng/eeitem.hxx
@@ -80,18 +80,19 @@ constexpr TypedWhichId<SvxScriptSpaceItem>        
EE_PARA_ASIANCJKSPACING    (EE
 constexpr TypedWhichId<SvxNumBulletItem>          EE_PARA_NUMBULLET          
(EE_PARA_START+5);
 constexpr TypedWhichId<SfxBoolItem>               EE_PARA_HYPHENATE          
(EE_PARA_START+6);
 constexpr TypedWhichId<SfxBoolItem>               EE_PARA_HYPHENATE_NO_CAPS  
(EE_PARA_START+7);
-constexpr TypedWhichId<SfxBoolItem>               EE_PARA_BULLETSTATE        
(EE_PARA_START+8);
-constexpr TypedWhichId<SvxLRSpaceItem>            EE_PARA_OUTLLRSPACE        
(EE_PARA_START+9);
-constexpr TypedWhichId<SfxInt16Item>              EE_PARA_OUTLLEVEL          
(EE_PARA_START+10);
-constexpr TypedWhichId<SvxBulletItem>             EE_PARA_BULLET             
(EE_PARA_START+11);
-constexpr TypedWhichId<SvxLRSpaceItem>            EE_PARA_LRSPACE            
(EE_PARA_START+12);
-constexpr TypedWhichId<SvxULSpaceItem>            EE_PARA_ULSPACE            
(EE_PARA_START+13);
-constexpr TypedWhichId<SvxLineSpacingItem>        EE_PARA_SBL                
(EE_PARA_START+14);
-constexpr TypedWhichId<SvxAdjustItem>             EE_PARA_JUST               
(EE_PARA_START+15);
-constexpr TypedWhichId<SvxTabStopItem>            EE_PARA_TABS               
(EE_PARA_START+16);
-constexpr TypedWhichId<SvxJustifyMethodItem>      EE_PARA_JUST_METHOD        
(EE_PARA_START+17);
-constexpr TypedWhichId<SvxVerJustifyItem>         EE_PARA_VER_JUST           
(EE_PARA_START+18);
-constexpr sal_uInt16                              EE_PARA_END                
(EE_PARA_START + 18);
+constexpr TypedWhichId<SfxBoolItem>               
EE_PARA_HYPHENATE_NO_LAST_WORD  (EE_PARA_START+8);
+constexpr TypedWhichId<SfxBoolItem>               EE_PARA_BULLETSTATE        
(EE_PARA_START+9);
+constexpr TypedWhichId<SvxLRSpaceItem>            EE_PARA_OUTLLRSPACE        
(EE_PARA_START+10);
+constexpr TypedWhichId<SfxInt16Item>              EE_PARA_OUTLLEVEL          
(EE_PARA_START+11);
+constexpr TypedWhichId<SvxBulletItem>             EE_PARA_BULLET             
(EE_PARA_START+12);
+constexpr TypedWhichId<SvxLRSpaceItem>            EE_PARA_LRSPACE            
(EE_PARA_START+13);
+constexpr TypedWhichId<SvxULSpaceItem>            EE_PARA_ULSPACE            
(EE_PARA_START+14);
+constexpr TypedWhichId<SvxLineSpacingItem>        EE_PARA_SBL                
(EE_PARA_START+15);
+constexpr TypedWhichId<SvxAdjustItem>             EE_PARA_JUST               
(EE_PARA_START+16);
+constexpr TypedWhichId<SvxTabStopItem>            EE_PARA_TABS               
(EE_PARA_START+17);
+constexpr TypedWhichId<SvxJustifyMethodItem>      EE_PARA_JUST_METHOD        
(EE_PARA_START+18);
+constexpr TypedWhichId<SvxVerJustifyItem>         EE_PARA_VER_JUST           
(EE_PARA_START+19);
+constexpr sal_uInt16                              EE_PARA_END                
(EE_PARA_START + 19);
 
 // Character attributes:
 constexpr sal_uInt16                           EE_CHAR_START          
(EE_PARA_END + 1);
diff --git a/include/editeng/hyphenzoneitem.hxx 
b/include/editeng/hyphenzoneitem.hxx
index 9b3abd338839..26dd9a1b31f0 100644
--- a/include/editeng/hyphenzoneitem.hxx
+++ b/include/editeng/hyphenzoneitem.hxx
@@ -35,6 +35,7 @@ class EDITENG_DLLPUBLIC SvxHyphenZoneItem final : public 
SfxPoolItem
     bool      bHyphen  : 1;
     bool      bPageEnd : 1;
     bool      bNoCapsHyphenation : 1;
+    bool      bNoLastWordHyphenation : 1;
     sal_uInt8 nMinLead;
     sal_uInt8 nMinTrail;
     sal_uInt8 nMaxHyphens;
@@ -65,6 +66,8 @@ public:
 
     void SetNoCapsHyphenation( const bool bNew ) { bNoCapsHyphenation = bNew; }
     bool IsNoCapsHyphenation() const { return bNoCapsHyphenation; }
+    void SetNoLastWordHyphenation( const bool bNew ) { bNoLastWordHyphenation 
= bNew; }
+    bool IsNoLastWordHyphenation() const { return bNoLastWordHyphenation; }
 
     sal_uInt8 &GetMinLead() { return nMinLead; }
     sal_uInt8 GetMinLead() const { return nMinLead; }
diff --git a/include/editeng/memberids.h b/include/editeng/memberids.h
index 1b50ab467229..a40d24cd6211 100644
--- a/include/editeng/memberids.h
+++ b/include/editeng/memberids.h
@@ -47,6 +47,7 @@
 #define MID_HYPHEN_MIN_TRAIL    2
 #define MID_HYPHEN_MAX_HYPHENS  3
 #define MID_HYPHEN_NO_CAPS      4
+#define MID_HYPHEN_NO_LAST_WORD 5
 
 // SvxBoxInfoItem
 #define MID_HORIZONTAL          1
diff --git a/include/editeng/unotext.hxx b/include/editeng/unotext.hxx
index 87dc02a37c09..3c8f95368473 100644
--- a/include/editeng/unotext.hxx
+++ b/include/editeng/unotext.hxx
@@ -140,6 +140,7 @@ struct SfxItemPropertyMapEntry;
     { UNO_NAME_EDIT_PARA_BMARGIN,      EE_PARA_ULSPACE,            
::cppu::UnoType<sal_Int32>::get(),            0, MID_LO_MARGIN, 
PropertyMoreFlags::METRIC_ITEM }, \
     { UNO_NAME_EDIT_PARA_IS_HYPHEN,    EE_PARA_HYPHENATE,  
::cppu::UnoType<bool>::get(),                0, 0 }, \
     {u"ParaHyphenationNoCaps",         EE_PARA_HYPHENATE_NO_CAPS, 
::cppu::UnoType<bool>::get(),       0, 0 }, \
+    {u"ParaHyphenationNoLastWord",     EE_PARA_HYPHENATE_NO_LAST_WORD, 
::cppu::UnoType<bool>::get(),       0, 0 }, \
     { UNO_NAME_EDIT_PARA_LASTLINEADJ,  EE_PARA_JUST,               
::cppu::UnoType<sal_Int16>::get(),            0, MID_LAST_LINE_ADJUST }, \
     { UNO_NAME_EDIT_PARA_LMARGIN,      EE_PARA_LRSPACE,    
::cppu::UnoType<sal_Int32>::get(),            0, MID_TXT_LMARGIN, 
PropertyMoreFlags::METRIC_ITEM }, \
     { UNO_NAME_EDIT_PARA_LINESPACING,  EE_PARA_SBL,                
cppu::UnoType<css::style::LineSpacing>::get(),     0, CONVERT_TWIPS}, \
diff --git a/include/unotools/linguprops.hxx b/include/unotools/linguprops.hxx
index 9cc88e82d7f4..adede0b91777 100644
--- a/include/unotools/linguprops.hxx
+++ b/include/unotools/linguprops.hxx
@@ -41,6 +41,7 @@ inline constexpr OUStringLiteral UPN_HYPH_MIN_LEADING         
       = u"HyphMin
 inline constexpr OUStringLiteral UPN_HYPH_MIN_TRAILING               = 
u"HyphMinTrailing";
 inline constexpr OUStringLiteral UPN_HYPH_MIN_WORD_LENGTH            = 
u"HyphMinWordLength";
 inline constexpr OUStringLiteral UPN_HYPH_NO_CAPS                    = 
u"HyphNoCaps";
+inline constexpr OUStringLiteral UPN_HYPH_NO_LAST_WORD               = 
u"HyphNoLastWord";
 
 // UNO property names for Lingu
 // (those not covered by the SpellChecker and Hyphenator
@@ -107,6 +108,7 @@ inline constexpr OUStringLiteral UPN_IS_GRAMMAR_INTERACTIVE 
         = u"IsInter
 #define UPH_IS_GRAMMAR_AUTO                 34
 #define UPH_IS_GRAMMAR_INTERACTIVE          35
 #define UPH_HYPH_NO_CAPS                    36
+#define UPH_HYPH_NO_LAST_WORD               37
 
 #ifdef __GNUC__
 #pragma GCC diagnostic pop
diff --git a/include/xmloff/xmltoken.hxx b/include/xmloff/xmltoken.hxx
index 8371c96e5839..469a0c5ad2ea 100644
--- a/include/xmloff/xmltoken.hxx
+++ b/include/xmloff/xmltoken.hxx
@@ -1051,6 +1051,7 @@ namespace xmloff::token {
         XML_HYPHENATION_PUSH_CHAR_COUNT,
         XML_HYPHENATION_REMAIN_CHAR_COUNT,
         XML_HYPHENATION_NO_CAPS,
+        XML_HYPHENATION_NO_LAST_WORD,
         XML_I,
         XML_ICON,
         XML_ICON_SET,
diff --git a/offapi/com/sun/star/style/ParagraphProperties.idl 
b/offapi/com/sun/star/style/ParagraphProperties.idl
index 886234d62b31..975c1c0a76ad 100644
--- a/offapi/com/sun/star/style/ParagraphProperties.idl
+++ b/offapi/com/sun/star/style/ParagraphProperties.idl
@@ -416,6 +416,13 @@ published service ParagraphProperties
          */
         [optional, property] boolean ParaHyphenationNoCaps;
 
+        /** Specifies whether last word of paragraph will be hyphenated.
+            Setting to `true` will disable hyphenation of last word for this 
paragraph.
+
+            @since LibreOffice 7.4
+         */
+        [optional, property] boolean ParaHyphenationNoLastWord;
+
 };
 
 
diff --git a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng 
b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
index 6f1a298cdd8e..2fe197e92e79 100644
--- a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
+++ b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
@@ -2705,6 +2705,15 @@ 
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-no-last-word">
+        <rng:ref name="boolean"/>
+      </rng:attribute>
+    </rng:optional>
+  </rng:define>
+
   <!-- TODO no proposal -->
   <rng:define name="chart-data-point-attlist" combine="interleave">
     <rng:optional>
diff --git a/sw/inc/inspectorproperties.hrc b/sw/inc/inspectorproperties.hrc
index 03c2cc8c60b4..89deab1418cc 100644
--- a/sw/inc/inspectorproperties.hrc
+++ b/sw/inc/inspectorproperties.hrc
@@ -206,6 +206,7 @@
 #define RID_PARA_HYPHENATION_MAX_LEADING_CHARS              
NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Hyphenation Max Leading Chars")
 #define RID_PARA_HYPHENATION_MAX_TRAILING_CHARS             
NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Hyphenation Max Trailing Chars")
 #define RID_PARA_HYPHENATION_NO_CAPS                        
NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Hyphenation No Caps")
+#define RID_PARA_HYPHENATION_NO_LAST_WORD                   
NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Hyphenation No Last Word")
 #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 fa9d86992809..abb9bb8ea4d9 100644
--- a/sw/inc/unoprnms.hxx
+++ b/sw/inc/unoprnms.hxx
@@ -65,6 +65,7 @@
 #define UNO_NAME_PARA_HYPHENATION_MAX_TRAILING_CHARS 
"ParaHyphenationMaxTrailingChars"
 #define UNO_NAME_PARA_HYPHENATION_MAX_HYPHENS "ParaHyphenationMaxHyphens"
 #define UNO_NAME_PARA_HYPHENATION_NO_CAPS "ParaHyphenationNoCaps"
+#define UNO_NAME_PARA_HYPHENATION_NO_LAST_WORD "ParaHyphenationNoLastWord"
 #define UNO_NAME_LEFT_MARGIN "LeftMargin"
 #define UNO_NAME_RIGHT_MARGIN "RightMargin"
 #define UNO_NAME_GUTTER_MARGIN "GutterMargin"
diff --git a/sw/qa/extras/layout/data/tdf149248.odt 
b/sw/qa/extras/layout/data/tdf149248.odt
new file mode 100644
index 000000000000..18685bd0a585
Binary files /dev/null and b/sw/qa/extras/layout/data/tdf149248.odt differ
diff --git a/sw/qa/extras/layout/layout.cxx b/sw/qa/extras/layout/layout.cxx
index cca5636d933c..93dbd19184a4 100644
--- a/sw/qa/extras/layout/layout.cxx
+++ b/sw/qa/extras/layout/layout.cxx
@@ -4161,6 +4161,21 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf121658)
     assertXPath(pXmlDoc, "//Special[@nType='PortionType::Hyphen']", 2);
 }
 
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf149248)
+{
+    uno::Reference<linguistic2::XHyphenator> xHyphenator = 
LinguMgr::GetHyphenator();
+    if (!xHyphenator->hasLocale(lang::Locale("en", "US", OUString())))
+        return;
+
+    createSwDoc(DATA_DIRECTORY, "tdf149248.odt");
+    xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+    // Only 1 hyphenated word should appear in the document (last word of the 
second
+    // paragraph). Last word should not be hyphenated for the fourth paragraph
+    // (the same paragraph, but with forbidden hyphenation of the last word).
+    assertXPath(pXmlDoc, "//Special[@nType='PortionType::Hyphen']", 1);
+}
+
 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testWriterImageNoCapture)
 {
     createSwDoc(DATA_DIRECTORY, "writer-image-no-capture.docx");
diff --git a/sw/qa/extras/odfexport/data/tdf149248.odt 
b/sw/qa/extras/odfexport/data/tdf149248.odt
new file mode 100644
index 000000000000..18685bd0a585
Binary files /dev/null and b/sw/qa/extras/odfexport/data/tdf149248.odt differ
diff --git a/sw/qa/extras/odfexport/odfexport.cxx 
b/sw/qa/extras/odfexport/odfexport.cxx
index bd11c7406a68..743ee2b475af 100644
--- a/sw/qa/extras/odfexport/odfexport.cxx
+++ b/sw/qa/extras/odfexport/odfexport.cxx
@@ -3038,6 +3038,13 @@ DECLARE_ODFEXPORT_TEST(tdf121658, "tdf121658.odt")
     CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(xStyle1, 
"ParaHyphenationNoCaps"));
 }
 
+DECLARE_ODFEXPORT_TEST(tdf149248, "tdf149248.odt")
+{
+    CPPUNIT_ASSERT_EQUAL(1, getPages());
+    CPPUNIT_ASSERT_EQUAL(false, getProperty<bool>(getParagraph(2), 
"ParaHyphenationNoLastWord"));
+    CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(getParagraph(4), 
"ParaHyphenationNoLastWord"));
+}
+
 DECLARE_ODFEXPORT_TEST(testArabicZeroNumbering, "arabic-zero-numbering.odt")
 {
     CPPUNIT_ASSERT_EQUAL(1, getPages());
diff --git a/sw/qa/uitest/styleInspector/styleInspector.py 
b/sw/qa/uitest/styleInspector/styleInspector.py
index 14b4590582cf..49d02fc01c9b 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\t", 
get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text'])
-            self.assertEqual(137, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(138, 
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\t", 
get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text'])
-            self.assertEqual(137, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(138, 
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\t", 
get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text'])
-            self.assertEqual(137, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(138, 
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\t", 
get_state_as_dict(xParStyle.getChild('0'))['Text'])
-            self.assertEqual(137, len(xParStyle.getChild('0').getChildren()))
+            self.assertEqual(138, len(xParStyle.getChild('0').getChildren()))
             self.assertEqual("Heading\t", 
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\t", 
get_state_as_dict(xParStyle.getChild('0'))['Text'])
-            self.assertEqual(137, len(xParStyle.getChild('0').getChildren()))
+            self.assertEqual(138, len(xParStyle.getChild('0').getChildren()))
             self.assertEqual("Text Body\t", 
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\t", 
get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text'])
-            self.assertEqual(137, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(138, 
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\t", 
get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text'])
-            self.assertEqual(137, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(138, 
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\t", 
get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text'])
-            self.assertEqual(137, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(138, 
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\t", 
get_state_as_dict(xListBox.getChild('1').getChild('0'))['Text'])
-            self.assertEqual(137, 
len(xListBox.getChild('1').getChild('0').getChildren()))
+            self.assertEqual(138, 
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\t", 
get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text'])
-            self.assertEqual(137, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(138, 
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 63d208dc2d86..f04f6106cf7c 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\t", 
get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text'])
             self.assertEqual("Table Contents\t", 
get_state_as_dict(xListBox.getChild('0').getChild('1'))['Text'])
-            self.assertEqual(137, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(138, 
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/guess.cxx b/sw/source/core/text/guess.cxx
index 8bf9819c4d66..907502ed0063 100644
--- a/sw/source/core/text/guess.cxx
+++ b/sw/source/core/text/guess.cxx
@@ -31,6 +31,7 @@
 #include "porfld.hxx"
 #include <paratr.hxx>
 #include <doc.hxx>
+#include <unotools/linguprops.hxx>
 
 using namespace ::com::sun::star;
 using namespace ::com::sun::star::uno;
@@ -195,8 +196,30 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, 
SwTextFormatInfo &rInf,
     // considering an additional "-" for hyphenation
     if( bHyph )
     {
+        // search start of the last word, if needed
+        sal_Int32 nLastWord = rInf.GetText().getLength() - 1;
+        bool bHyphenationNoLastWord;
+        const css::beans::PropertyValues & rHyphValues = rInf.GetHyphValues();
+        assert( rHyphValues.getLength() > 3 && rHyphValues[3].Name == 
UPN_HYPH_NO_LAST_WORD );
+        if ( rHyphValues[3].Value >>= bHyphenationNoLastWord )
+        {
+            bool bCutBlank = false;
+            for (; sal_Int32(rInf.GetIdx()) <= nLastWord; --nLastWord )
+            {
+                sal_Unicode cChar = rInf.GetText()[nLastWord];
+                if ( cChar != CH_BLANK && cChar != CH_FULL_BLANK && cChar != 
CH_SIX_PER_EM )
+                    bCutBlank = true;
+                else if ( bCutBlank )
+                    break;
+            }
+        }
+
         m_nCutPos = rInf.GetTextBreak( nLineWidth, nMaxLen, nMaxComp, 
nHyphPos, rInf.GetCachedVclData().get() );
 
+        // don't hyphenate last word of the paragraph
+        if ( bHyphenationNoLastWord && sal_Int32(m_nCutPos) > nLastWord )
+            m_nCutPos = TextFrameIndex(nLastWord);
+
         if ( !nHyphPos && rInf.GetIdx() )
             nHyphPos = rInf.GetIdx() - TextFrameIndex(1);
     }
diff --git a/sw/source/core/text/inftxt.cxx b/sw/source/core/text/inftxt.cxx
index 5f4cbb95b6fc..6f22e920d135 100644
--- a/sw/source/core/text/inftxt.cxx
+++ b/sw/source/core/text/inftxt.cxx
@@ -1360,13 +1360,14 @@ void SwTextPaintInfo::DrawViewOpt( const SwLinePortion 
&rPor,
 }
 
 static void lcl_InitHyphValues( PropertyValues &rVals,
-            sal_Int16 nMinLeading, sal_Int16 nMinTrailing, bool 
bNoCapsHyphenation )
+            sal_Int16 nMinLeading, sal_Int16 nMinTrailing,
+            bool bNoCapsHyphenation, bool bNoLastWordHyphenation )
 {
     sal_Int32 nLen = rVals.getLength();
 
     if (0 == nLen)  // yet to be initialized?
     {
-        rVals.realloc( 3 );
+        rVals.realloc( 4 );
         PropertyValue *pVal = rVals.getArray();
 
         pVal[0].Name    = UPN_HYPH_MIN_LEADING;
@@ -1380,13 +1381,18 @@ static void lcl_InitHyphValues( PropertyValues &rVals,
         pVal[2].Name    = UPN_HYPH_NO_CAPS;
         pVal[2].Handle  = UPH_HYPH_NO_CAPS;
         pVal[2].Value   <<= bNoCapsHyphenation;
+
+        pVal[3].Name    = UPN_HYPH_NO_LAST_WORD;
+        pVal[3].Handle  = UPH_HYPH_NO_LAST_WORD;
+        pVal[3].Value   <<= bNoLastWordHyphenation;
     }
-    else if (3 == nLen) // already initialized once?
+    else if (4 == nLen) // already initialized once?
     {
         PropertyValue *pVal = rVals.getArray();
         pVal[0].Value <<= nMinLeading;
         pVal[1].Value <<= nMinTrailing;
         pVal[2].Value <<= bNoCapsHyphenation;
+        pVal[3].Value <<= bNoLastWordHyphenation;
     }
     else {
         OSL_FAIL( "unexpected size of sequence" );
@@ -1395,7 +1401,7 @@ static void lcl_InitHyphValues( PropertyValues &rVals,
 
 const PropertyValues & SwTextFormatInfo::GetHyphValues() const
 {
-    OSL_ENSURE( 3 == m_aHyphVals.getLength(),
+    OSL_ENSURE( 4 == m_aHyphVals.getLength(),
             "hyphenation values not yet initialized" );
     return m_aHyphVals;
 }
@@ -1414,7 +1420,9 @@ bool SwTextFormatInfo::InitHyph( const bool bAutoHyphen )
         const sal_Int16 nMinimalLeading  = std::max(rAttr.GetMinLead(), 
sal_uInt8(2));
         const sal_Int16 nMinimalTrailing = rAttr.GetMinTrail();
         const bool bNoCapsHyphenation = rAttr.IsNoCapsHyphenation();
-        lcl_InitHyphValues( m_aHyphVals, nMinimalLeading, nMinimalTrailing, 
bNoCapsHyphenation);
+        const bool bNoLastWordHyphenation = rAttr.IsNoLastWordHyphenation();
+        lcl_InitHyphValues( m_aHyphVals, nMinimalLeading, nMinimalTrailing,
+                 bNoCapsHyphenation, bNoLastWordHyphenation );
     }
     return bAuto;
 }
diff --git a/sw/source/core/unocore/unomapproperties.hxx 
b/sw/source/core/unocore/unomapproperties.hxx
index e2e1ce136553..ec9ce71977af 100644
--- a/sw/source/core/unocore/unomapproperties.hxx
+++ b/sw/source/core/unocore/unomapproperties.hxx
@@ -113,6 +113,7 @@
         { u"" UNO_NAME_PARRSID,                             RES_PARATR_RSID,   
            cppu::UnoType<sal_Int32>::get(),         
PropertyAttribute::MAYBEVOID, 0                                      }, \
         { u"" UNO_NAME_PARA_IS_HYPHENATION,                 
RES_PARATR_HYPHENZONE,         cppu::UnoType<bool>::get(),       
PropertyAttribute::MAYBEVOID, MID_IS_HYPHEN                          }, \
         { u"" UNO_NAME_PARA_HYPHENATION_NO_CAPS,            
RES_PARATR_HYPHENZONE,         cppu::UnoType<bool>::get(),       
PropertyAttribute::MAYBEVOID, MID_HYPHEN_NO_CAPS                     }, \
+        { u"" UNO_NAME_PARA_HYPHENATION_NO_LAST_WORD,       
RES_PARATR_HYPHENZONE,         cppu::UnoType<bool>::get(),       
PropertyAttribute::MAYBEVOID, MID_HYPHEN_NO_LAST_WORD                }, \
         { u"" UNO_NAME_PARA_HYPHENATION_MAX_LEADING_CHARS,  
RES_PARATR_HYPHENZONE,         cppu::UnoType<sal_Int16>::get(),         
PropertyAttribute::MAYBEVOID, MID_HYPHEN_MIN_LEAD                    }, \
         { u"" UNO_NAME_PARA_HYPHENATION_MAX_TRAILING_CHARS, 
RES_PARATR_HYPHENZONE,         cppu::UnoType<sal_Int16>::get(),         
PropertyAttribute::MAYBEVOID, MID_HYPHEN_MIN_TRAIL                   }, \
         { u"" UNO_NAME_PARA_HYPHENATION_MAX_HYPHENS,        
RES_PARATR_HYPHENZONE,         cppu::UnoType<sal_Int16>::get(),         
PropertyAttribute::MAYBEVOID, MID_HYPHEN_MAX_HYPHENS                 }, \
@@ -435,6 +436,7 @@
                     { u"" UNO_NAME_BOTTOM_BORDER_DISTANCE, RES_BOX,            
   cppu::UnoType<sal_Int32>::get(),   0, BOTTOM_BORDER_DISTANCE|CONVERT_TWIPS 
},\
                     { u"" UNO_NAME_PARA_IS_HYPHENATION, RES_PARATR_HYPHENZONE, 
     cppu::UnoType<bool>::get(),  PropertyAttribute::MAYBEVOID, MID_IS_HYPHEN   
      },\
                     { u"" UNO_NAME_PARA_HYPHENATION_NO_CAPS, 
RES_PARATR_HYPHENZONE,      cppu::UnoType<bool>::get(),  
PropertyAttribute::MAYBEVOID, MID_HYPHEN_NO_CAPS },\
+                    { u"" UNO_NAME_PARA_HYPHENATION_NO_LAST_WORD, 
RES_PARATR_HYPHENZONE,      cppu::UnoType<bool>::get(),  
PropertyAttribute::MAYBEVOID, MID_HYPHEN_NO_LAST_WORD },\
                     { u"" UNO_NAME_PARA_HYPHENATION_MAX_LEADING_CHARS, 
RES_PARATR_HYPHENZONE,         cppu::UnoType<sal_Int16>::get(),   
PropertyAttribute::MAYBEVOID, MID_HYPHEN_MIN_LEAD   },\
                     { u"" UNO_NAME_PARA_HYPHENATION_MAX_TRAILING_CHARS, 
RES_PARATR_HYPHENZONE,        cppu::UnoType<sal_Int16>::get(),   
PropertyAttribute::MAYBEVOID, MID_HYPHEN_MIN_TRAIL  },\
                     { u"" UNO_NAME_PARA_HYPHENATION_MAX_HYPHENS, 
RES_PARATR_HYPHENZONE,       cppu::UnoType<sal_Int16>::get(),   
PropertyAttribute::MAYBEVOID, MID_HYPHEN_MAX_HYPHENS},\
diff --git a/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx 
b/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx
index 72a9238c7c66..59108e4ed1f8 100644
--- a/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx
+++ b/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx
@@ -252,6 +252,7 @@ static OUString PropertyNametoRID(const OUString& rName)
         { "ParaHyphenationMaxLeadingChars", 
RID_PARA_HYPHENATION_MAX_LEADING_CHARS },
         { "ParaHyphenationMaxTrailingChars", 
RID_PARA_HYPHENATION_MAX_TRAILING_CHARS },
         { "ParaHyphenationNoCaps", RID_PARA_HYPHENATION_NO_CAPS },
+        { "ParaHyphenationNoLastWord", RID_PARA_HYPHENATION_NO_LAST_WORD },
         { "ParaInteropGrabBag", RID_PARA_INTEROP_GRAB_BAG },
         { "ParaIsAutoFirstLineIndent", RID_PARA_IS_AUTO_FIRST_LINE_INDENT },
         { "ParaIsCharacterDistance", RID_PARA_IS_CHARACTER_DISTANCE },
diff --git a/xmloff/source/core/xmltoken.cxx b/xmloff/source/core/xmltoken.cxx
index 949178c67f09..34371453c815 100644
--- a/xmloff/source/core/xmltoken.cxx
+++ b/xmloff/source/core/xmltoken.cxx
@@ -1064,6 +1064,7 @@ namespace xmloff::token {
         TOKEN( "hyphenation-push-char-count",     
XML_HYPHENATION_PUSH_CHAR_COUNT ),
         TOKEN( "hyphenation-remain-char-count",       
XML_HYPHENATION_REMAIN_CHAR_COUNT ),
         TOKEN( "hyphenation-no-caps",             XML_HYPHENATION_NO_CAPS ),
+        TOKEN( "hyphenation-no-last-word",        XML_HYPHENATION_NO_LAST_WORD 
),
         TOKEN( "i",                               XML_I ),
         TOKEN( "icon",                            XML_ICON ),
         TOKEN( "icon-set",                        XML_ICON_SET ),
diff --git a/xmloff/source/text/txtprmap.cxx b/xmloff/source/text/txtprmap.cxx
index ce09a85c917a..b116b16c836e 100644
--- a/xmloff/source/text/txtprmap.cxx
+++ b/xmloff/source/text/txtprmap.cxx
@@ -343,6 +343,7 @@ XMLPropertyMapEntry const aXMLParaPropMap[] =
     MT_E( "ParaHyphenationMaxTrailingChars",FO, HYPHENATION_PUSH_CHAR_COUNT, 
XML_TYPE_NUMBER16_NO_ZERO, 0 ),
     MP_E( "ParaHyphenationMaxHyphens",  FO, HYPHENATION_LADDER_COUNT, 
XML_TYPE_NUMBER16_NONE, 0 ),
     MAP_EXT( "ParaHyphenationNoCaps",  XML_NAMESPACE_LO_EXT, 
XML_HYPHENATION_NO_CAPS, XML_TYPE_BOOL|XML_TYPE_PROP_TEXT, 0 ),
+    MAP_EXT( "ParaHyphenationNoLastWord",  XML_NAMESPACE_LO_EXT, 
XML_HYPHENATION_NO_LAST_WORD, XML_TYPE_BOOL|XML_TYPE_PROP_TEXT, 0 ),
     // RES_PARATR_DROP
     MP_E( "DropCapWholeWord",   STYLE,  LENGTH,     
MID_FLAG_SPECIAL_ITEM|XML_TYPE_BOOL, CTF_DROPCAPWHOLEWORD ),
     MP_E( "DropCapCharStyleName",   STYLE,  STYLE_NAME, 
MID_FLAG_SPECIAL_ITEM|XML_TYPE_STRING, CTF_DROPCAPCHARSTYLE ),
diff --git a/xmloff/source/token/tokens.txt b/xmloff/source/token/tokens.txt
index 04b8b62de5d3..cc365e61d3a0 100644
--- a/xmloff/source/token/tokens.txt
+++ b/xmloff/source/token/tokens.txt
@@ -964,6 +964,7 @@ hyphenation-ladder-count
 hyphenation-push-char-count
 hyphenation-remain-char-count
 hyphenation-no-caps
+hyphenation-no-last-word
 i
 icon
 icon-set

Reply via email to