summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLászló Németh <nemeth@numbertext.org>2022-05-26 19:01:07 +0200
committerLászló Németh <nemeth@numbertext.org>2022-05-26 20:25:35 +0200
commit29359fc15c435cec17987fd6221ab6833d38746e (patch)
treec4bc33e84fee7414c0e0d5e9e6b9c77526e2aa68
parent3cde7345199b535763bb7d83a87096e9157b7317 (diff)
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 <nemeth@numbertext.org>
-rw-r--r--cui/source/inc/paragrph.hxx1
-rw-r--r--cui/source/tabpages/paragrph.cxx10
-rw-r--r--cui/uiconfig/ui/textflowpage.ui41
-rw-r--r--editeng/source/items/paraitem.cxx19
-rw-r--r--include/editeng/editrids.hrc1
-rw-r--r--include/editeng/hyphenzoneitem.hxx4
-rw-r--r--include/editeng/memberids.h1
-rw-r--r--include/xmloff/xmltoken.hxx1
-rw-r--r--offapi/com/sun/star/style/ParagraphProperties.idl5
-rw-r--r--schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng12
-rw-r--r--svx/sdi/svxitems.sdi9
-rw-r--r--sw/inc/inspectorproperties.hrc1
-rw-r--r--sw/inc/unoprnms.hxx1
-rw-r--r--sw/qa/extras/layout/data/tdf149324.odtbin0 -> 20206 bytes
-rw-r--r--sw/qa/extras/layout/layout.cxx15
-rw-r--r--sw/qa/extras/odfexport/data/tdf149324.odtbin0 -> 20206 bytes
-rw-r--r--sw/qa/extras/odfexport/odfexport.cxx7
-rw-r--r--sw/qa/uitest/styleInspector/styleInspector.py20
-rw-r--r--sw/qa/uitest/styleInspector/tdf137513.py2
-rw-r--r--sw/source/core/text/inftxt.cxx14
-rw-r--r--sw/source/core/unocore/unomapproperties.hxx2
-rw-r--r--sw/source/uibase/app/docshini.cxx1
-rw-r--r--sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx1
-rw-r--r--xmloff/source/core/xmltoken.cxx1
-rw-r--r--xmloff/source/text/txtprmap.cxx1
-rw-r--r--xmloff/source/token/tokens.txt1
26 files changed, 147 insertions, 24 deletions
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>
@@ -86,6 +93,26 @@
</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>
<property name="can_focus">True</property>
@@ -188,6 +215,20 @@
</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>
<property name="visible">True</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
@@ -2715,6 +2715,18 @@ xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.
</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>
<rng:attribute name="loext:custom-label-pos-x">
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
--- /dev/null
+++ b/sw/qa/extras/layout/data/tdf149324.odt
Binary files 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
--- /dev/null
+++ b/sw/qa/extras/odfexport/data/tdf149324.odt
Binary files 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