cui/source/inc/paragrph.hxx                                 |    1 
 cui/source/tabpages/paragrph.cxx                            |   10 ++
 cui/uiconfig/ui/textflowpage.ui                             |   41 ++++++++++++
 editeng/source/items/paraitem.cxx                           |   19 ++++-
 include/editeng/editrids.hrc                                |    1 
 include/editeng/hyphenzoneitem.hxx                          |    4 +
 include/editeng/memberids.h                                 |    1 
 include/xmloff/xmltoken.hxx                                 |    1 
 offapi/com/sun/star/style/ParagraphProperties.idl           |    5 +
 schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng |   12 +++
 svx/sdi/svxitems.sdi                                        |    9 +-
 sw/inc/inspectorproperties.hrc                              |    1 
 sw/inc/unoprnms.hxx                                         |    1 
 sw/qa/extras/layout/data/tdf149324.odt                      |binary
 sw/qa/extras/layout/layout.cxx                              |   15 ++++
 sw/qa/extras/odfexport/data/tdf149324.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/inftxt.cxx                              |   14 ++--
 sw/source/core/unocore/unomapproperties.hxx                 |    2 
 sw/source/uibase/app/docshini.cxx                           |    1 
 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 
 26 files changed, 147 insertions(+), 24 deletions(-)

New commits:
commit 29359fc15c435cec17987fd6221ab6833d38746e
Author:     László Németh <nem...@numbertext.org>
AuthorDate: Thu May 26 19:01:07 2022 +0200
Commit:     László Németh <nem...@numbertext.org>
CommitDate: Thu May 26 20:25:35 2022 +0200

    tdf#149324 sw offapi xmloff: add option to not hyphenate short words
    
    Add paragraph property to disable automatic hyphenation of short
    words based on a minimum character count.
    
    Note: there is a (broken) global option for Minimum Word Length
    at hyphenation, see "Minimal number of characters for hyphenation"
    in Tools->Options->Language Settings->Writing Aids), but
    for better/comfortable paragraph-level adjustment of typesetting,
    add a paragraph property for it. The same option is available e.g.
    in Adobe InDesign and in CSS Text Module Level 4 (hyphenate-limit-chars).
    
    * Add checkbox to Text Flow in paragraph dialog
    * Store property in paragraph model 
(com::sun::star::style::ParagraphProperties::ParaHyphenationMinWordLength)
    * Add ODF import/export
    * Add ODF unit test
    * Add layout test
    
    Follow-up to commit 8c018910ae4d8701b1ce2a95727b9baed4016da3
    "tdf#149248 sw offapi xmloff: add option to not hyphenate last word".
    
    Change-Id: I68715f47d17b5c022430bd0e74c88a97bc7f81f9
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/135028
    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 0967c32a060e..f0c56775fc2c 100644
--- a/cui/source/inc/paragrph.hxx
+++ b/cui/source/inc/paragrph.hxx
@@ -232,6 +232,7 @@ private:
     std::unique_ptr<weld::SpinButton> m_xExtHyphenAfterBox;
     std::unique_ptr<weld::Label> m_xMaxHyphenLabel;
     std::unique_ptr<weld::SpinButton> m_xMaxHyphenEdit;
+    std::unique_ptr<weld::SpinButton> m_xMinWordLength;
 
     // pagebreak
     std::unique_ptr<weld::CheckButton> m_xPageBreakBox;
diff --git a/cui/source/tabpages/paragrph.cxx b/cui/source/tabpages/paragrph.cxx
index 29bc2fc76b43..2d651fedae6a 100644
--- a/cui/source/tabpages/paragrph.cxx
+++ b/cui/source/tabpages/paragrph.cxx
@@ -1355,7 +1355,8 @@ bool SvxExtParagraphTabPage::FillItemSet( SfxItemSet* 
rOutSet )
          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() )
+         m_xMaxHyphenEdit->get_value_changed_from_saved() ||
+         m_xMinWordLength->get_value_changed_from_saved() )
     {
         SvxHyphenZoneItem aHyphen(
             static_cast<const SvxHyphenZoneItem&>(GetItemSet().Get( _nWhich )) 
);
@@ -1367,6 +1368,7 @@ bool SvxExtParagraphTabPage::FillItemSet( SfxItemSet* 
rOutSet )
         {
             aHyphen.GetMinLead() = 
static_cast<sal_uInt8>(m_xExtHyphenBeforeBox->get_value());
             aHyphen.GetMinTrail() = 
static_cast<sal_uInt8>(m_xExtHyphenAfterBox->get_value());
+            aHyphen.GetMinWordLength() = 
static_cast<sal_uInt8>(m_xMinWordLength->get_value());
         }
         aHyphen.GetMaxHyphens() = 
static_cast<sal_uInt8>(m_xMaxHyphenEdit->get_value());
 
@@ -1577,6 +1579,7 @@ void SvxExtParagraphTabPage::Reset( const SfxItemSet* 
rSet )
         m_xExtHyphenBeforeBox->set_value(rHyphen.GetMinLead());
         m_xExtHyphenAfterBox->set_value(rHyphen.GetMinTrail());
         m_xMaxHyphenEdit->set_value(rHyphen.GetMaxHyphens());
+        m_xMinWordLength->set_value(rHyphen.GetMinWordLength());
     }
     else
     {
@@ -1593,6 +1596,7 @@ void SvxExtParagraphTabPage::Reset( const SfxItemSet* 
rSet )
     m_xAfterText->set_sensitive(bEnable);
     m_xMaxHyphenLabel->set_sensitive(bEnable);
     m_xMaxHyphenEdit->set_sensitive(bEnable);
+    m_xMinWordLength->set_sensitive(bEnable);
 
     switch (rSet->GetItemState(SID_ATTR_PARA_PAGENUM))
     {
@@ -1852,6 +1856,7 @@ void SvxExtParagraphTabPage::ChangesApplied()
     m_xExtHyphenBeforeBox->set_value(m_xExtHyphenBeforeBox->get_value());
     m_xExtHyphenAfterBox->set_value(m_xExtHyphenAfterBox->get_value());
     m_xMaxHyphenEdit->set_value(m_xMaxHyphenEdit->get_value());
+    m_xMinWordLength->set_value(m_xMinWordLength->get_value());
     m_xPageBreakBox->save_state();
     m_xBreakPositionLB->save_value();
     m_xBreakTypeLB->save_value();
@@ -1902,6 +1907,7 @@ 
SvxExtParagraphTabPage::SvxExtParagraphTabPage(weld::Container* pPage, weld::Dia
     , m_xExtHyphenAfterBox(m_xBuilder->weld_spin_button("spinLineBegin"))
     , m_xMaxHyphenLabel(m_xBuilder->weld_label("labelMaxNum"))
     , m_xMaxHyphenEdit(m_xBuilder->weld_spin_button("spinMaxNum"))
+    , m_xMinWordLength(m_xBuilder->weld_spin_button("spinMinLen"))
     //Page break
     , m_xPageBreakBox(m_xBuilder->weld_check_button("checkInsert"))
     , m_xBreakTypeFT(m_xBuilder->weld_label("labelType"))
@@ -1970,6 +1976,7 @@ 
SvxExtParagraphTabPage::SvxExtParagraphTabPage(weld::Container* pPage, weld::Dia
     m_xExtHyphenAfterBox->set_sensitive(false);
     m_xMaxHyphenLabel->set_sensitive(false);
     m_xMaxHyphenEdit->set_sensitive(false);
+    m_xMinWordLength->set_sensitive(false);
     m_xPageNumBox->set_sensitive(false);
     m_xPagenumEdit->set_sensitive(false);
     // no column break in HTML
@@ -2103,6 +2110,7 @@ void SvxExtParagraphTabPage::HyphenClickHdl()
     m_xExtHyphenAfterBox->set_sensitive(bEnable);
     m_xMaxHyphenLabel->set_sensitive(bEnable);
     m_xMaxHyphenEdit->set_sensitive(bEnable);
+    m_xMinWordLength->set_sensitive(bEnable);
     m_xHyphenBox->set_state(bEnable ? TRISTATE_TRUE : TRISTATE_FALSE);
 }
 
diff --git a/cui/uiconfig/ui/textflowpage.ui b/cui/uiconfig/ui/textflowpage.ui
index 9ff931ddc6b8..89ac8a3a22dd 100644
--- a/cui/uiconfig/ui/textflowpage.ui
+++ b/cui/uiconfig/ui/textflowpage.ui
@@ -41,6 +41,13 @@
     <property name="step_increment">1</property>
     <property name="page_increment">10</property>
   </object>
+  <object class="GtkAdjustment" id="adjustment7">
+    <property name="lower">4</property>
+    <property name="upper">99</property>
+    <property name="value">4</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
   <!-- n-columns=1 n-rows=1 -->
   <object class="GtkGrid" id="TextFlowPage">
     <property name="visible">True</property>
@@ -85,6 +92,26 @@
                 <property name="width">2</property>
               </packing>
             </child>
+            <child>
+              <object class="GtkSpinButton" id="spinMinLen">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="halign">start</property>
+                <property name="margin-start">25</property>
+                <property name="activates_default">True</property>
+                <property name="adjustment">adjustment7</property>
+                <property name="truncate-multiline">True</property>
+                <child internal-child="accessible">
+                  <object class="AtkObject" id="spinMinLen-atkobject">
+                    <property name="AtkObject::accessible-description" 
translatable="yes" context="textflowpage|extended_tip|spinMinLen">Enter the 
minimum word length in characters that can be hyphenated.</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">6</property>
+              </packing>
+            </child>
             <child>
               <object class="GtkSpinButton" id="spinMaxNum">
                 <property name="visible">True</property>
@@ -187,6 +214,20 @@
                 <property name="top_attach">5</property>
               </packing>
             </child>
+            <child>
+              <object class="GtkLabel" id="labelMinLen">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="halign">start</property>
+                <property name="label" translatable="yes" 
context="textflowpage|labelMinLen">_Minimum word length in characters</property>
+                <property name="use_underline">True</property>
+                <property name="mnemonic_widget">spinMinLen</property>
+              </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>
diff --git a/editeng/source/items/paraitem.cxx 
b/editeng/source/items/paraitem.cxx
index 56d9628276b3..202341caa949 100644
--- a/editeng/source/items/paraitem.cxx
+++ b/editeng/source/items/paraitem.cxx
@@ -558,7 +558,8 @@ SvxHyphenZoneItem::SvxHyphenZoneItem( const bool bHyph, 
const sal_uInt16 nId ) :
     bNoLastWordHyphenation(false),
     nMinLead(0),
     nMinTrail(0),
-    nMaxHyphens(255)
+    nMaxHyphens(255),
+    nMinWordLength(0)
 {
 }
 
@@ -586,6 +587,9 @@ bool    SvxHyphenZoneItem::QueryValue( uno::Any& rVal, 
sal_uInt8 nMemberId ) con
         case MID_HYPHEN_NO_LAST_WORD:
             rVal <<= bNoLastWordHyphenation;
         break;
+        case MID_HYPHEN_MIN_WORD_LENGTH:
+            rVal <<= static_cast<sal_Int16>(nMinWordLength);
+        break;
     }
     return true;
 }
@@ -622,6 +626,9 @@ bool SvxHyphenZoneItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
         case MID_HYPHEN_NO_LAST_WORD:
             bNoLastWordHyphenation = Any2Bool(rVal);
         break;
+        case MID_HYPHEN_MIN_WORD_LENGTH:
+            nMinWordLength = static_cast<sal_uInt8>(nNewVal);
+        break;
     }
     return true;
 }
@@ -638,7 +645,8 @@ bool SvxHyphenZoneItem::operator==( const SfxPoolItem& 
rAttr ) const
             && rItem.bPageEnd == bPageEnd
             && rItem.nMinLead == nMinLead
             && rItem.nMinTrail == nMinTrail
-            && rItem.nMaxHyphens == nMaxHyphens );
+            && rItem.nMaxHyphens == nMaxHyphens
+            && rItem.nMinWordLength == nMinWordLength );
 }
 
 SvxHyphenZoneItem* SvxHyphenZoneItem::Clone( SfxItemPool * ) const
@@ -671,7 +679,8 @@ bool SvxHyphenZoneItem::GetPresentation
             rText += EditResId(pId) + cpDelimTmp +
                     OUString::number( nMinLead ) + cpDelimTmp +
                     OUString::number( nMinTrail ) + cpDelimTmp +
-                    OUString::number( nMaxHyphens );
+                    OUString::number( nMaxHyphens ) + cpDelimTmp +
+                    OUString::number( nMinWordLength );
             return true;
         }
         case SfxItemPresentation::Complete:
@@ -691,7 +700,9 @@ bool SvxHyphenZoneItem::GetPresentation
                     cpDelimTmp +
                     EditResId(RID_SVXITEMS_HYPHEN_MINTRAIL).replaceAll("%1", 
OUString::number(nMinTrail)) +
                     cpDelimTmp +
-                    EditResId(RID_SVXITEMS_HYPHEN_MAX).replaceAll("%1", 
OUString::number(nMaxHyphens));
+                    EditResId(RID_SVXITEMS_HYPHEN_MAX).replaceAll("%1", 
OUString::number(nMaxHyphens)) +
+                    cpDelimTmp +
+                    EditResId(RID_SVXITEMS_HYPHEN_MINWORDLEN).replaceAll("%1", 
OUString::number(nMinWordLength));
             return true;
         }
         default: ;//prevent warning
diff --git a/include/editeng/editrids.hrc b/include/editeng/editrids.hrc
index 556ee042aeca..57342e7c1deb 100644
--- a/include/editeng/editrids.hrc
+++ b/include/editeng/editrids.hrc
@@ -230,6 +230,7 @@
 #define RID_SVXITEMS_HYPHEN_MINLEAD             
NC_("RID_SVXITEMS_HYPHEN_MINLEAD", "%1 characters at end of line")
 #define RID_SVXITEMS_HYPHEN_MINTRAIL            
NC_("RID_SVXITEMS_HYPHEN_MINTRAIL", "%1 characters at beginning of line")
 #define RID_SVXITEMS_HYPHEN_MAX                 NC_("RID_SVXITEMS_HYPHEN_MAX", 
"%1 hyphens")
+#define RID_SVXITEMS_HYPHEN_MINWORDLEN          
NC_("RID_SVXITEMS_HYPHEN_MINWORDLEN", "Words with at least %1 characters")
 #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 26dd9a1b31f0..b1ec7cba3a45 100644
--- a/include/editeng/hyphenzoneitem.hxx
+++ b/include/editeng/hyphenzoneitem.hxx
@@ -39,6 +39,7 @@ class EDITENG_DLLPUBLIC SvxHyphenZoneItem final : public 
SfxPoolItem
     sal_uInt8 nMinLead;
     sal_uInt8 nMinTrail;
     sal_uInt8 nMaxHyphens;
+    sal_uInt8 nMinWordLength;
 
 public:
     static SfxPoolItem* CreateDefault();
@@ -77,6 +78,9 @@ public:
 
     sal_uInt8 &GetMaxHyphens() { return nMaxHyphens; }
     sal_uInt8 GetMaxHyphens() const { return nMaxHyphens; }
+
+    sal_uInt8 &GetMinWordLength() { return nMinWordLength; }
+    sal_uInt8 GetMinWordLength() const { return nMinWordLength; }
 };
 
 #endif
diff --git a/include/editeng/memberids.h b/include/editeng/memberids.h
index a40d24cd6211..4ec470f31db7 100644
--- a/include/editeng/memberids.h
+++ b/include/editeng/memberids.h
@@ -48,6 +48,7 @@
 #define MID_HYPHEN_MAX_HYPHENS  3
 #define MID_HYPHEN_NO_CAPS      4
 #define MID_HYPHEN_NO_LAST_WORD 5
+#define MID_HYPHEN_MIN_WORD_LENGTH 6
 
 // SvxBoxInfoItem
 #define MID_HORIZONTAL          1
diff --git a/include/xmloff/xmltoken.hxx b/include/xmloff/xmltoken.hxx
index 469a0c5ad2ea..4592c01c75fb 100644
--- a/include/xmloff/xmltoken.hxx
+++ b/include/xmloff/xmltoken.hxx
@@ -1052,6 +1052,7 @@ namespace xmloff::token {
         XML_HYPHENATION_REMAIN_CHAR_COUNT,
         XML_HYPHENATION_NO_CAPS,
         XML_HYPHENATION_NO_LAST_WORD,
+        XML_HYPHENATION_WORD_CHAR_COUNT,
         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 975c1c0a76ad..e9522d77d1cd 100644
--- a/offapi/com/sun/star/style/ParagraphProperties.idl
+++ b/offapi/com/sun/star/style/ParagraphProperties.idl
@@ -423,6 +423,11 @@ published service ParagraphProperties
          */
         [optional, property] boolean ParaHyphenationNoLastWord;
 
+        /** specifies the minimum word length in characters, when hyphenation 
is applied.
+
+            @since LibreOffice 7.4
+         */
+        [optional, property] short ParaHyphenationMinWordLength;
 };
 
 
diff --git a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng 
b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
index 2fe197e92e79..0f2ea32c300e 100644
--- a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
+++ b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
@@ -2714,6 +2714,18 @@ 
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-word-char-count">
+        <rng:choice>
+          <rng:value>no-limit</rng:value>
+          <rng:ref name="positiveInteger"/>
+        </rng:choice>
+      </rng:attribute>
+    </rng:optional>
+  </rng:define>
+
   <!-- TODO no proposal -->
   <rng:define name="chart-data-point-attlist" combine="interleave">
     <rng:optional>
diff --git a/svx/sdi/svxitems.sdi b/svx/sdi/svxitems.sdi
index a7d376130539..00e4b74237a2 100644
--- a/svx/sdi/svxitems.sdi
+++ b/svx/sdi/svxitems.sdi
@@ -255,10 +255,11 @@ item SvxFontHeight SvxFontHeightItem;
 
 struct SvxHyphenZone
 {
-    BOOL        Hyphen      MID_IS_HYPHEN;
-    INT16       MinLead     MID_HYPHEN_MIN_LEAD;
-    INT16       MinTrail    MID_HYPHEN_MIN_TRAIL;
-    INT16       MaxHyphens  MID_HYPHEN_MAX_HYPHENS;
+    BOOL        Hyphen        MID_IS_HYPHEN;
+    INT16       MinLead       MID_HYPHEN_MIN_LEAD;
+    INT16       MinTrail      MID_HYPHEN_MIN_TRAIL;
+    INT16       MaxHyphens    MID_HYPHEN_MAX_HYPHENS;
+    INT16       MinWordLength MID_HYPHEN_MIN_WORD_LENGTH;
 };
 item SvxHyphenZone SvxHyphenZoneItem;
 
diff --git a/sw/inc/inspectorproperties.hrc b/sw/inc/inspectorproperties.hrc
index 89deab1418cc..f1327ec348c1 100644
--- a/sw/inc/inspectorproperties.hrc
+++ b/sw/inc/inspectorproperties.hrc
@@ -207,6 +207,7 @@
 #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_HYPHENATION_MIN_WORD_LENGTH                
NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Hyphenation Min Word Length")
 #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 abb9bb8ea4d9..6e3330e65126 100644
--- a/sw/inc/unoprnms.hxx
+++ b/sw/inc/unoprnms.hxx
@@ -64,6 +64,7 @@
 #define UNO_NAME_PARA_HYPHENATION_MAX_LEADING_CHARS 
"ParaHyphenationMaxLeadingChars"
 #define UNO_NAME_PARA_HYPHENATION_MAX_TRAILING_CHARS 
"ParaHyphenationMaxTrailingChars"
 #define UNO_NAME_PARA_HYPHENATION_MAX_HYPHENS "ParaHyphenationMaxHyphens"
+#define UNO_NAME_PARA_HYPHENATION_MIN_WORD_LENGTH 
"ParaHyphenationMinWordLength"
 #define UNO_NAME_PARA_HYPHENATION_NO_CAPS "ParaHyphenationNoCaps"
 #define UNO_NAME_PARA_HYPHENATION_NO_LAST_WORD "ParaHyphenationNoLastWord"
 #define UNO_NAME_LEFT_MARGIN "LeftMargin"
diff --git a/sw/qa/extras/layout/data/tdf149324.odt 
b/sw/qa/extras/layout/data/tdf149324.odt
new file mode 100644
index 000000000000..da4b87d89ecc
Binary files /dev/null and b/sw/qa/extras/layout/data/tdf149324.odt differ
diff --git a/sw/qa/extras/layout/layout.cxx b/sw/qa/extras/layout/layout.cxx
index 93dbd19184a4..e3af733c48f1 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, testTdf149324)
+{
+    uno::Reference<linguistic2::XHyphenator> xHyphenator = 
LinguMgr::GetHyphenator();
+    if (!xHyphenator->hasLocale(lang::Locale("en", "US", OUString())))
+        return;
+
+    createSwDoc(DATA_DIRECTORY, "tdf149324.odt");
+    xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+    // Only 3 hyphenated words should appear in the document (last paragraph
+    // has got a 7-character word limit for hyphenation, removing the
+    // hyphenation "ex-cept".
+    assertXPath(pXmlDoc, "//Special[@nType='PortionType::Hyphen']", 3);
+}
+
 CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf149248)
 {
     uno::Reference<linguistic2::XHyphenator> xHyphenator = 
LinguMgr::GetHyphenator();
diff --git a/sw/qa/extras/odfexport/data/tdf149324.odt 
b/sw/qa/extras/odfexport/data/tdf149324.odt
new file mode 100644
index 000000000000..da4b87d89ecc
Binary files /dev/null and b/sw/qa/extras/odfexport/data/tdf149324.odt differ
diff --git a/sw/qa/extras/odfexport/odfexport.cxx 
b/sw/qa/extras/odfexport/odfexport.cxx
index 743ee2b475af..6f85db1d99a8 100644
--- a/sw/qa/extras/odfexport/odfexport.cxx
+++ b/sw/qa/extras/odfexport/odfexport.cxx
@@ -3045,6 +3045,13 @@ DECLARE_ODFEXPORT_TEST(tdf149248, "tdf149248.odt")
     CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(getParagraph(4), 
"ParaHyphenationNoLastWord"));
 }
 
+DECLARE_ODFEXPORT_TEST(tdf149324, "tdf149324.odt")
+{
+    CPPUNIT_ASSERT_EQUAL(1, getPages());
+    CPPUNIT_ASSERT_EQUAL(sal_uInt16(0), 
getProperty<sal_uInt16>(getParagraph(2), "ParaHyphenationMinWordLength"));
+    CPPUNIT_ASSERT_EQUAL(sal_uInt16(7), 
getProperty<sal_uInt16>(getParagraph(4), "ParaHyphenationMinWordLength"));
+}
+
 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 49d02fc01c9b..29300f60ff17 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(138, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(139, 
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(138, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(139, 
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(138, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(139, 
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(138, len(xParStyle.getChild('0').getChildren()))
+            self.assertEqual(139, 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(138, len(xParStyle.getChild('0').getChildren()))
+            self.assertEqual(139, 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(138, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(139, 
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(138, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(139, 
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(138, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(139, 
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(138, 
len(xListBox.getChild('1').getChild('0').getChildren()))
+            self.assertEqual(139, 
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(138, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(139, 
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 f04f6106cf7c..8dfc5929c0c1 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(138, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(139, 
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 6f22e920d135..5a794370b6da 100644
--- a/sw/source/core/text/inftxt.cxx
+++ b/sw/source/core/text/inftxt.cxx
@@ -1361,13 +1361,13 @@ void SwTextPaintInfo::DrawViewOpt( const SwLinePortion 
&rPor,
 
 static void lcl_InitHyphValues( PropertyValues &rVals,
             sal_Int16 nMinLeading, sal_Int16 nMinTrailing,
-            bool bNoCapsHyphenation, bool bNoLastWordHyphenation )
+            bool bNoCapsHyphenation, bool bNoLastWordHyphenation, sal_Int16 
nMinWordLength )
 {
     sal_Int32 nLen = rVals.getLength();
 
     if (0 == nLen)  // yet to be initialized?
     {
-        rVals.realloc( 4 );
+        rVals.realloc( 5 );
         PropertyValue *pVal = rVals.getArray();
 
         pVal[0].Name    = UPN_HYPH_MIN_LEADING;
@@ -1385,14 +1385,19 @@ static void lcl_InitHyphValues( PropertyValues &rVals,
         pVal[3].Name    = UPN_HYPH_NO_LAST_WORD;
         pVal[3].Handle  = UPH_HYPH_NO_LAST_WORD;
         pVal[3].Value   <<= bNoLastWordHyphenation;
+
+        pVal[4].Name    = UPN_HYPH_MIN_WORD_LENGTH;
+        pVal[4].Handle  = UPH_HYPH_MIN_WORD_LENGTH;
+        pVal[4].Value   <<= nMinWordLength;
     }
-    else if (4 == nLen) // already initialized once?
+    else if (5 == nLen) // already initialized once?
     {
         PropertyValue *pVal = rVals.getArray();
         pVal[0].Value <<= nMinLeading;
         pVal[1].Value <<= nMinTrailing;
         pVal[2].Value <<= bNoCapsHyphenation;
         pVal[3].Value <<= bNoLastWordHyphenation;
+        pVal[4].Value <<= nMinWordLength;
     }
     else {
         OSL_FAIL( "unexpected size of sequence" );
@@ -1419,10 +1424,11 @@ bool SwTextFormatInfo::InitHyph( const bool bAutoHyphen 
)
     {
         const sal_Int16 nMinimalLeading  = std::max(rAttr.GetMinLead(), 
sal_uInt8(2));
         const sal_Int16 nMinimalTrailing = rAttr.GetMinTrail();
+        const sal_Int16 nMinimalWordLength = rAttr.GetMinWordLength();
         const bool bNoCapsHyphenation = rAttr.IsNoCapsHyphenation();
         const bool bNoLastWordHyphenation = rAttr.IsNoLastWordHyphenation();
         lcl_InitHyphValues( m_aHyphVals, nMinimalLeading, nMinimalTrailing,
-                 bNoCapsHyphenation, bNoLastWordHyphenation );
+                 bNoCapsHyphenation, bNoLastWordHyphenation, 
nMinimalWordLength );
     }
     return bAuto;
 }
diff --git a/sw/source/core/unocore/unomapproperties.hxx 
b/sw/source/core/unocore/unomapproperties.hxx
index ec9ce71977af..1f679b2e38fd 100644
--- a/sw/source/core/unocore/unomapproperties.hxx
+++ b/sw/source/core/unocore/unomapproperties.hxx
@@ -117,6 +117,7 @@
         { 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                 }, \
+        { u"" UNO_NAME_PARA_HYPHENATION_MIN_WORD_LENGTH,    
RES_PARATR_HYPHENZONE,         cppu::UnoType<sal_Int16>::get(),         
PropertyAttribute::MAYBEVOID, MID_HYPHEN_MIN_WORD_LENGTH             }, \
         { u"" UNO_NAME_CHAR_AUTO_KERNING,                   
RES_CHRATR_AUTOKERN,           cppu::UnoType<bool>::get(),       
PropertyAttribute::MAYBEVOID, 0                                      }, \
         { u"" UNO_NAME_CHAR_BACK_COLOR,                     
RES_CHRATR_BACKGROUND,         cppu::UnoType<sal_Int32>::get(),         
PropertyAttribute::MAYBEVOID, MID_BACK_COLOR                         }, \
         { u"" UNO_NAME_CHAR_HIGHLIGHT,                      
RES_CHRATR_HIGHLIGHT,          cppu::UnoType<sal_Int32>::get(), 
PropertyAttribute::MAYBEVOID, MID_BACK_COLOR                         }, \
@@ -440,6 +441,7 @@
                     { 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},\
+                    { u"" UNO_NAME_PARA_HYPHENATION_MIN_WORD_LENGTH, 
RES_PARATR_HYPHENZONE,       cppu::UnoType<sal_Int16>::get(),   
PropertyAttribute::MAYBEVOID, MID_HYPHEN_MIN_WORD_LENGTH},\
                     { u"" 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},\
                     { u"" UNO_NAME_PARA_USER_DEFINED_ATTRIBUTES, 
RES_UNKNOWNATR_CONTAINER, cppu::UnoType<css::container::XNameContainer>::get(), 
PropertyAttribute::MAYBEVOID, 0 },\
diff --git a/sw/source/uibase/app/docshini.cxx 
b/sw/source/uibase/app/docshini.cxx
index 21cefad272cd..c5432a98af29 100644
--- a/sw/source/uibase/app/docshini.cxx
+++ b/sw/source/uibase/app/docshini.cxx
@@ -659,6 +659,7 @@ void SwDocShell::SubInitNew()
         SvxHyphenZoneItem aHyp( m_xDoc->GetDefault(RES_PARATR_HYPHENZONE)  );
         aHyp.GetMinLead()   = static_cast< sal_uInt8 
>(aLinguOpt.nHyphMinLeading);
         aHyp.GetMinTrail()  = static_cast< sal_uInt8 
>(aLinguOpt.nHyphMinTrailing);
+        aHyp.GetMinWordLength()  = static_cast< sal_uInt8 
>(aLinguOpt.nHyphMinWordLength);
 
         aDfltSet.Put( aHyp );
 
diff --git a/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx 
b/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx
index 59108e4ed1f8..46b1486f5e57 100644
--- a/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx
+++ b/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx
@@ -253,6 +253,7 @@ static OUString PropertyNametoRID(const OUString& rName)
         { "ParaHyphenationMaxTrailingChars", 
RID_PARA_HYPHENATION_MAX_TRAILING_CHARS },
         { "ParaHyphenationNoCaps", RID_PARA_HYPHENATION_NO_CAPS },
         { "ParaHyphenationNoLastWord", RID_PARA_HYPHENATION_NO_LAST_WORD },
+        { "ParaHyphenationMinWordLength", RID_PARA_HYPHENATION_MIN_WORD_LENGTH 
},
         { "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 34371453c815..356b98051ec0 100644
--- a/xmloff/source/core/xmltoken.cxx
+++ b/xmloff/source/core/xmltoken.cxx
@@ -1065,6 +1065,7 @@ namespace xmloff::token {
         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( "hyphenation-word-char-count",     
XML_HYPHENATION_WORD_CHAR_COUNT ),
         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 b116b16c836e..a2e110e5bdb0 100644
--- a/xmloff/source/text/txtprmap.cxx
+++ b/xmloff/source/text/txtprmap.cxx
@@ -344,6 +344,7 @@ XMLPropertyMapEntry const aXMLParaPropMap[] =
     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 ),
+    MAP_EXT( "ParaHyphenationMinWordLength",  XML_NAMESPACE_LO_EXT, 
XML_HYPHENATION_WORD_CHAR_COUNT, XML_TYPE_NUMBER16_NONE|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 cc365e61d3a0..ee0adc36055d 100644
--- a/xmloff/source/token/tokens.txt
+++ b/xmloff/source/token/tokens.txt
@@ -965,6 +965,7 @@ hyphenation-push-char-count
 hyphenation-remain-char-count
 hyphenation-no-caps
 hyphenation-no-last-word
+hyphenation-word-char-count
 i
 icon
 icon-set

Reply via email to