diff options
27 files changed, 257 insertions, 42 deletions
diff --git a/cui/source/inc/paragrph.hxx b/cui/source/inc/paragrph.hxx index f0c56775fc2c..3947c14d10c8 100644 --- a/cui/source/inc/paragrph.hxx +++ b/cui/source/inc/paragrph.hxx @@ -233,6 +233,7 @@ private: std::unique_ptr<weld::Label> m_xMaxHyphenLabel; std::unique_ptr<weld::SpinButton> m_xMaxHyphenEdit; std::unique_ptr<weld::SpinButton> m_xMinWordLength; + std::unique_ptr<SvxRelativeField> m_xHyphenZone; // pagebreak std::unique_ptr<weld::CheckButton> m_xPageBreakBox; diff --git a/cui/source/tabpages/paragrph.cxx b/cui/source/tabpages/paragrph.cxx index 2d651fedae6a..3fa77d9fb791 100644 --- a/cui/source/tabpages/paragrph.cxx +++ b/cui/source/tabpages/paragrph.cxx @@ -1356,7 +1356,8 @@ bool SvxExtParagraphTabPage::FillItemSet( SfxItemSet* rOutSet ) m_xExtHyphenBeforeBox->get_value_changed_from_saved() || m_xExtHyphenAfterBox->get_value_changed_from_saved() || m_xMaxHyphenEdit->get_value_changed_from_saved() || - m_xMinWordLength->get_value_changed_from_saved() ) + m_xMinWordLength->get_value_changed_from_saved() || + m_xHyphenZone->get_value_changed_from_saved() ) { SvxHyphenZoneItem aHyphen( static_cast<const SvxHyphenZoneItem&>(GetItemSet().Get( _nWhich )) ); @@ -1372,6 +1373,11 @@ bool SvxExtParagraphTabPage::FillItemSet( SfxItemSet* rOutSet ) } aHyphen.GetMaxHyphens() = static_cast<sal_uInt8>(m_xMaxHyphenEdit->get_value()); + SfxItemPool* pPool = GetItemSet().GetPool(); + DBG_ASSERT( pPool, "Where is the pool?" ); + MapUnit eUnit = pPool->GetMetric( _nWhich ); + aHyphen.GetTextHyphenZone() = static_cast<sal_uInt16>(m_xHyphenZone->GetCoreValue(eUnit)); + if ( !pOld || *static_cast<const SvxHyphenZoneItem*>(pOld) != aHyphen || m_xHyphenBox->get_state_changed_from_saved()) @@ -1560,6 +1566,17 @@ bool SvxExtParagraphTabPage::FillItemSet( SfxItemSet* rOutSet ) } void SvxExtParagraphTabPage::Reset( const SfxItemSet* rSet ) { + SfxItemPool* pPool = rSet->GetPool(); + DBG_ASSERT( pPool, "Where is the pool?" ); + + // adjust metric + FieldUnit eFUnit = GetModuleFieldUnit( *rSet ); + + bool bApplyCharUnit = GetApplyCharUnit( *rSet ); + + if( SvtCJKOptions::IsAsianTypographyEnabled() && bApplyCharUnit ) + eFUnit = FieldUnit::CHAR; + sal_uInt16 _nWhich = GetWhich( SID_ATTR_PARA_HYPHENZONE ); SfxItemState eItemState = rSet->GetItemState( _nWhich ); @@ -1580,6 +1597,8 @@ void SvxExtParagraphTabPage::Reset( const SfxItemSet* rSet ) m_xExtHyphenAfterBox->set_value(rHyphen.GetMinTrail()); m_xMaxHyphenEdit->set_value(rHyphen.GetMaxHyphens()); m_xMinWordLength->set_value(rHyphen.GetMinWordLength()); + m_xHyphenZone->SetFieldUnit(eFUnit); + m_xHyphenZone->SetMetricValue(rHyphen.GetTextHyphenZone(), MapUnit::MapTwip); } else { @@ -1597,6 +1616,7 @@ void SvxExtParagraphTabPage::Reset( const SfxItemSet* rSet ) m_xMaxHyphenLabel->set_sensitive(bEnable); m_xMaxHyphenEdit->set_sensitive(bEnable); m_xMinWordLength->set_sensitive(bEnable); + m_xHyphenZone->set_sensitive(bEnable); switch (rSet->GetItemState(SID_ATTR_PARA_PAGENUM)) { @@ -1857,6 +1877,11 @@ void SvxExtParagraphTabPage::ChangesApplied() m_xExtHyphenAfterBox->set_value(m_xExtHyphenAfterBox->get_value()); m_xMaxHyphenEdit->set_value(m_xMaxHyphenEdit->get_value()); m_xMinWordLength->set_value(m_xMinWordLength->get_value()); + SfxItemPool* pPool = GetItemSet().GetPool(); + DBG_ASSERT( pPool, "Where is the pool?" ); + FieldUnit eUnit = + MapToFieldUnit( pPool->GetMetric( GetWhich( SID_ATTR_PARA_HYPHENZONE ) ) ); + m_xHyphenZone->set_value(m_xHyphenZone->get_value(eUnit), eUnit); m_xPageBreakBox->save_state(); m_xBreakPositionLB->save_value(); m_xBreakTypeLB->save_value(); @@ -1908,6 +1933,7 @@ SvxExtParagraphTabPage::SvxExtParagraphTabPage(weld::Container* pPage, weld::Dia , m_xMaxHyphenLabel(m_xBuilder->weld_label("labelMaxNum")) , m_xMaxHyphenEdit(m_xBuilder->weld_spin_button("spinMaxNum")) , m_xMinWordLength(m_xBuilder->weld_spin_button("spinMinLen")) + , m_xHyphenZone(new SvxRelativeField(m_xBuilder->weld_metric_spin_button("spinHyphenZone", FieldUnit::CM))) //Page break , m_xPageBreakBox(m_xBuilder->weld_check_button("checkInsert")) , m_xBreakTypeFT(m_xBuilder->weld_label("labelType")) diff --git a/cui/uiconfig/ui/textflowpage.ui b/cui/uiconfig/ui/textflowpage.ui index 89ac8a3a22dd..496f22f05619 100644 --- a/cui/uiconfig/ui/textflowpage.ui +++ b/cui/uiconfig/ui/textflowpage.ui @@ -48,6 +48,11 @@ <property name="step_increment">1</property> <property name="page_increment">10</property> </object> + <object class="GtkAdjustment" id="adjustment8"> + <property name="upper">55.88</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> @@ -258,6 +263,49 @@ <property name="width">2</property> </packing> </child> + <child> + <!-- n-columns=1 n-rows=1 --> + <object class="GtkGrid" id="gridHyphenZone"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + <property name="margin-start">12</property> + <property name="margin-top">6</property> + <child> + <object class="GtkSpinButton" id="spinHyphenZone"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="activates_default">True</property> + <property name="truncate_multiline">True</property> + <property name="adjustment">adjustment8</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="labelHyphenZone"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="textflowpage|labelHyphenZone">Hyphenation _zone:</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">spinHyphenZone</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">7</property> + <property name="width">2</property> + </packing> + </child> </object> </child> <child type="label"> diff --git a/editeng/source/items/paraitem.cxx b/editeng/source/items/paraitem.cxx index 202341caa949..9368dfdf3c2a 100644 --- a/editeng/source/items/paraitem.cxx +++ b/editeng/source/items/paraitem.cxx @@ -559,7 +559,8 @@ SvxHyphenZoneItem::SvxHyphenZoneItem( const bool bHyph, const sal_uInt16 nId ) : nMinLead(0), nMinTrail(0), nMaxHyphens(255), - nMinWordLength(0) + nMinWordLength(0), + nTextHyphenZone(0) { } @@ -590,6 +591,9 @@ bool SvxHyphenZoneItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) con case MID_HYPHEN_MIN_WORD_LENGTH: rVal <<= static_cast<sal_Int16>(nMinWordLength); break; + case MID_HYPHEN_ZONE: + rVal <<= static_cast<sal_Int16>(nTextHyphenZone); + break; } return true; } @@ -629,6 +633,9 @@ bool SvxHyphenZoneItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId ) case MID_HYPHEN_MIN_WORD_LENGTH: nMinWordLength = static_cast<sal_uInt8>(nNewVal); break; + case MID_HYPHEN_ZONE: + nTextHyphenZone = nNewVal; + break; } return true; } @@ -646,7 +653,8 @@ bool SvxHyphenZoneItem::operator==( const SfxPoolItem& rAttr ) const && rItem.nMinLead == nMinLead && rItem.nMinTrail == nMinTrail && rItem.nMaxHyphens == nMaxHyphens - && rItem.nMinWordLength == nMinWordLength ); + && rItem.nMinWordLength == nMinWordLength + && rItem.nTextHyphenZone == nTextHyphenZone ); } SvxHyphenZoneItem* SvxHyphenZoneItem::Clone( SfxItemPool * ) const @@ -657,9 +665,9 @@ SvxHyphenZoneItem* SvxHyphenZoneItem::Clone( SfxItemPool * ) const bool SvxHyphenZoneItem::GetPresentation ( SfxItemPresentation ePres, - MapUnit /*eCoreUnit*/, - MapUnit /*ePresUnit*/, - OUString& rText, const IntlWrapper& + MapUnit eCoreUnit, + MapUnit ePresUnit, + OUString& rText, const IntlWrapper& rIntl ) const { OUString cpDelimTmp(cpDelim); @@ -680,7 +688,16 @@ bool SvxHyphenZoneItem::GetPresentation OUString::number( nMinLead ) + cpDelimTmp + OUString::number( nMinTrail ) + cpDelimTmp + OUString::number( nMaxHyphens ) + cpDelimTmp + - OUString::number( nMinWordLength ); + OUString::number( nMinWordLength ) + cpDelimTmp + + GetMetricText( nTextHyphenZone, eCoreUnit, ePresUnit, &rIntl ) + + " " + EditResId(GetMetricId(ePresUnit)); + + if ( bNoCapsHyphenation ) + rText += cpDelimTmp + EditResId(RID_SVXITEMS_HYPHEN_NO_CAPS_TRUE); + + if ( bNoLastWordHyphenation ) + rText += cpDelimTmp + EditResId(RID_SVXITEMS_HYPHEN_LAST_WORD_TRUE); + return true; } case SfxItemPresentation::Complete: @@ -703,6 +720,20 @@ bool SvxHyphenZoneItem::GetPresentation EditResId(RID_SVXITEMS_HYPHEN_MAX).replaceAll("%1", OUString::number(nMaxHyphens)) + cpDelimTmp + EditResId(RID_SVXITEMS_HYPHEN_MINWORDLEN).replaceAll("%1", OUString::number(nMinWordLength)); + + if ( nTextHyphenZone > 0 ) + { + rText += cpDelimTmp + EditResId(RID_SVXITEMS_HYPHEN_ZONE) + + GetMetricText( nTextHyphenZone, eCoreUnit, ePresUnit, &rIntl ) + + " " + EditResId(GetMetricId(ePresUnit)); + } + + if ( bNoCapsHyphenation ) + rText += cpDelimTmp + EditResId(RID_SVXITEMS_HYPHEN_NO_CAPS_TRUE); + + if ( bNoLastWordHyphenation ) + rText += cpDelimTmp + EditResId(RID_SVXITEMS_HYPHEN_LAST_WORD_TRUE); + return true; } default: ;//prevent warning diff --git a/include/editeng/editrids.hrc b/include/editeng/editrids.hrc index 57342e7c1deb..31ac07a1d22b 100644 --- a/include/editeng/editrids.hrc +++ b/include/editeng/editrids.hrc @@ -230,7 +230,10 @@ #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_HYPHEN_NO_CAPS_TRUE NC_("RID_SVXITEMS_HYPHEN_NO_CAPS_TRUE", "Not hyphenated CAPS") +#define RID_SVXITEMS_HYPHEN_LAST_WORD_TRUE NC_("RID_SVXITEMS_HYPHEN_NO_CAPS_FALSE", "Not hyphenated last word") +#define RID_SVXITEMS_HYPHEN_MINWORDLEN NC_("RID_SVXITEMS_HYPHEN_MINWORDLEN", "%1 characters in words") +#define RID_SVXITEMS_HYPHEN_ZONE NC_("RID_SVXITEMS_HYPHEN_ZONE", "Hyphenation zone ") #define RID_SVXITEMS_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 b1ec7cba3a45..7104d2d7db58 100644 --- a/include/editeng/hyphenzoneitem.hxx +++ b/include/editeng/hyphenzoneitem.hxx @@ -38,8 +38,9 @@ class EDITENG_DLLPUBLIC SvxHyphenZoneItem final : public SfxPoolItem bool bNoLastWordHyphenation : 1; sal_uInt8 nMinLead; sal_uInt8 nMinTrail; - sal_uInt8 nMaxHyphens; - sal_uInt8 nMinWordLength; + sal_uInt8 nMaxHyphens; // max. consecutive lines with hyphenation + sal_uInt8 nMinWordLength; // hyphenate only words with at least nMinWordLength characters + sal_uInt16 nTextHyphenZone; // don't force hyphenation at line end, allow this extra white space public: static SfxPoolItem* CreateDefault(); @@ -81,6 +82,9 @@ public: sal_uInt8 &GetMinWordLength() { return nMinWordLength; } sal_uInt8 GetMinWordLength() const { return nMinWordLength; } + + sal_uInt16 &GetTextHyphenZone() { return nTextHyphenZone; } + sal_uInt16 GetTextHyphenZone() const { return nTextHyphenZone; } }; #endif diff --git a/include/editeng/memberids.h b/include/editeng/memberids.h index 4ec470f31db7..9b89ebafaefb 100644 --- a/include/editeng/memberids.h +++ b/include/editeng/memberids.h @@ -49,6 +49,7 @@ #define MID_HYPHEN_NO_CAPS 4 #define MID_HYPHEN_NO_LAST_WORD 5 #define MID_HYPHEN_MIN_WORD_LENGTH 6 +#define MID_HYPHEN_ZONE 7 // SvxBoxInfoItem #define MID_HORIZONTAL 1 diff --git a/include/unotools/linguprops.hxx b/include/unotools/linguprops.hxx index adede0b91777..971c0280444c 100644 --- a/include/unotools/linguprops.hxx +++ b/include/unotools/linguprops.hxx @@ -42,6 +42,7 @@ inline constexpr OUStringLiteral UPN_HYPH_MIN_TRAILING = u"HyphMin 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"; +inline constexpr OUStringLiteral UPN_HYPH_ZONE = u"HyphZone"; // UNO property names for Lingu // (those not covered by the SpellChecker and Hyphenator @@ -109,6 +110,7 @@ inline constexpr OUStringLiteral UPN_IS_GRAMMAR_INTERACTIVE = u"IsInter #define UPH_IS_GRAMMAR_INTERACTIVE 35 #define UPH_HYPH_NO_CAPS 36 #define UPH_HYPH_NO_LAST_WORD 37 +#define UPH_HYPH_ZONE 38 #ifdef __GNUC__ #pragma GCC diagnostic pop diff --git a/include/xmloff/xmltoken.hxx b/include/xmloff/xmltoken.hxx index 4592c01c75fb..69a495687da6 100644 --- a/include/xmloff/xmltoken.hxx +++ b/include/xmloff/xmltoken.hxx @@ -1053,6 +1053,7 @@ namespace xmloff::token { XML_HYPHENATION_NO_CAPS, XML_HYPHENATION_NO_LAST_WORD, XML_HYPHENATION_WORD_CHAR_COUNT, + XML_HYPHENATION_ZONE, 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 e9522d77d1cd..32a63282d54e 100644 --- a/offapi/com/sun/star/style/ParagraphProperties.idl +++ b/offapi/com/sun/star/style/ParagraphProperties.idl @@ -428,6 +428,13 @@ published service ParagraphProperties @since LibreOffice 7.4 */ [optional, property] short ParaHyphenationMinWordLength; + + /** specifies the hyphenation zone, i.e. allowed extra white space + in the line before applying hyphenation. + + @since LibreOffice 7.4 + */ + [optional, property] long ParaHyphenationZone; }; diff --git a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng index d5857c132e37..293e40d9601c 100644 --- a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng +++ b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng @@ -2727,6 +2727,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-zone"> + <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 00e4b74237a2..df389994a868 100644 --- a/svx/sdi/svxitems.sdi +++ b/svx/sdi/svxitems.sdi @@ -260,6 +260,7 @@ struct SvxHyphenZone INT16 MinTrail MID_HYPHEN_MIN_TRAIL; INT16 MaxHyphens MID_HYPHEN_MAX_HYPHENS; INT16 MinWordLength MID_HYPHEN_MIN_WORD_LENGTH; + INT16 HyphenZone MID_HYPHEN_ZONE; }; item SvxHyphenZone SvxHyphenZoneItem; diff --git a/sw/inc/inspectorproperties.hrc b/sw/inc/inspectorproperties.hrc index f1327ec348c1..c2fd2a106f63 100644 --- a/sw/inc/inspectorproperties.hrc +++ b/sw/inc/inspectorproperties.hrc @@ -208,6 +208,7 @@ #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_HYPHENATION_ZONE NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Hyphenation Zone") #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 14da263acd96..c3a7d97b32e8 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_MIN_WORD_LENGTH "ParaHyphenationMinWordLength" +#define UNO_NAME_PARA_HYPHENATION_ZONE "ParaHyphenationZone" #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/tdf149420.odt b/sw/qa/extras/layout/data/tdf149420.odt Binary files differnew file mode 100644 index 000000000000..249d7267eb27 --- /dev/null +++ b/sw/qa/extras/layout/data/tdf149420.odt diff --git a/sw/qa/extras/layout/layout.cxx b/sw/qa/extras/layout/layout.cxx index e42f5c22b83a..2a6b2ca099c9 100644 --- a/sw/qa/extras/layout/layout.cxx +++ b/sw/qa/extras/layout/layout.cxx @@ -4379,6 +4379,21 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf121658) assertXPath(pXmlDoc, "//Special[@nType='PortionType::Hyphen']", 2); } +CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf149420) +{ + uno::Reference<linguistic2::XHyphenator> xHyphenator = LinguMgr::GetHyphenator(); + if (!xHyphenator->hasLocale(lang::Locale("en", "US", OUString()))) + return; + + createSwDoc(DATA_DIRECTORY, "tdf149420.odt"); + xmlDocUniquePtr pXmlDoc = parseLayoutDump(); + + // Only 3 hyphenated words should appear in the document (last paragraph + // has got a 1 cm hyphenation zone, removing two hyphenations, which visible + // in the second paragraph). + assertXPath(pXmlDoc, "//Special[@nType='PortionType::Hyphen']", 8); +} + CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf149324) { uno::Reference<linguistic2::XHyphenator> xHyphenator = LinguMgr::GetHyphenator(); diff --git a/sw/qa/extras/odfexport/data/tdf149420.odt b/sw/qa/extras/odfexport/data/tdf149420.odt Binary files differnew file mode 100644 index 000000000000..249d7267eb27 --- /dev/null +++ b/sw/qa/extras/odfexport/data/tdf149420.odt diff --git a/sw/qa/extras/odfexport/odfexport.cxx b/sw/qa/extras/odfexport/odfexport.cxx index 6f85db1d99a8..e58b772b8cdf 100644 --- a/sw/qa/extras/odfexport/odfexport.cxx +++ b/sw/qa/extras/odfexport/odfexport.cxx @@ -3052,6 +3052,13 @@ DECLARE_ODFEXPORT_TEST(tdf149324, "tdf149324.odt") CPPUNIT_ASSERT_EQUAL(sal_uInt16(7), getProperty<sal_uInt16>(getParagraph(4), "ParaHyphenationMinWordLength")); } +DECLARE_ODFEXPORT_TEST(tdf149420, "tdf149420.odt") +{ + CPPUNIT_ASSERT_EQUAL(1, getPages()); + CPPUNIT_ASSERT_EQUAL(sal_uInt16(0), getProperty<sal_uInt16>(getParagraph(2), "ParaHyphenationZone")); + CPPUNIT_ASSERT_EQUAL(sal_uInt16(567), getProperty<sal_uInt16>(getParagraph(4), "ParaHyphenationZone")); +} + 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 29300f60ff17..f5ce2cd6bc75 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(139, len(xListBox.getChild('0').getChild('0').getChildren())) + self.assertEqual(140, 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(139, len(xListBox.getChild('0').getChild('0').getChildren())) + self.assertEqual(140, 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(139, len(xListBox.getChild('0').getChild('0').getChildren())) + self.assertEqual(140, 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(139, len(xParStyle.getChild('0').getChildren())) + self.assertEqual(140, 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(139, len(xParStyle.getChild('0').getChildren())) + self.assertEqual(140, 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(139, len(xListBox.getChild('0').getChild('0').getChildren())) + self.assertEqual(140, 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(139, len(xListBox.getChild('0').getChild('0').getChildren())) + self.assertEqual(140, 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(139, len(xListBox.getChild('0').getChild('0').getChildren())) + self.assertEqual(140, 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(139, len(xListBox.getChild('1').getChild('0').getChildren())) + self.assertEqual(140, 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(139, len(xListBox.getChild('0').getChild('0').getChildren())) + self.assertEqual(140, 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 8dfc5929c0c1..a0c9ddaafed0 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(139, len(xListBox.getChild('0').getChild('0').getChildren())) + self.assertEqual(140, 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 d2711d4087d2..3055c69957cc 100644 --- a/sw/source/core/text/guess.cxx +++ b/sw/source/core/text/guess.cxx @@ -196,29 +196,69 @@ 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 = false; + // nHyphZone is the first character not fitting in the hyphenation zone, + // or 0, if the whole line in the hyphenation zone, + // or -1, if no hyphenation zone defined (i.e. it is 0) + sal_Int32 nHyphZone = -1; const css::beans::PropertyValues & rHyphValues = rInf.GetHyphValues(); - assert( rHyphValues.getLength() > 3 && rHyphValues[3].Name == UPN_HYPH_NO_LAST_WORD ); - if ( rHyphValues[3].Value >>= bHyphenationNoLastWord ) + assert( rHyphValues.getLength() > 5 && rHyphValues[5].Name == UPN_HYPH_ZONE ); + // hyphenation zone (distance from the line end in twips) + sal_uInt16 nTextHyphenZone; + if ( rHyphValues[5].Value >>= nTextHyphenZone ) + nHyphZone = nTextHyphenZone >= nLineWidth + ? 0 + : sal_Int32(rInf.GetTextBreak( nLineWidth - nTextHyphenZone, + nMaxLen, nMaxComp, rInf.GetCachedVclData().get() )); + + m_nCutPos = rInf.GetTextBreak( nLineWidth, nMaxLen, nMaxComp, nHyphPos, rInf.GetCachedVclData().get() ); + + // don't try to hyphenate in the hyphenation zone + if ( nHyphZone != -1 && TextFrameIndex(COMPLETE_STRING) != m_nCutPos ) { - bool bCutBlank = false; - for (; sal_Int32(rInf.GetIdx()) <= nLastWord; --nLastWord ) + sal_Int32 nZonePos = sal_Int32(m_nCutPos); + // disable hyphenation, if there is a space within the hyphenation zone + // Note: for better interoperability, not fitting space character at + // rInf.GetIdx()[nHyphZone] always disables the hyphenation, don't need to calculate + // with its fitting part. Moreover, do not check double or more spaces there, they + // are accepted outside of the hyphenation zone, too. + for (; sal_Int32(rInf.GetIdx()) <= nZonePos && nHyphZone <= nZonePos; --nZonePos ) { - 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; + sal_Unicode cChar = rInf.GetText()[nZonePos]; + if ( cChar == CH_BLANK || cChar == CH_FULL_BLANK || cChar == CH_SIX_PER_EM ) + { + bHyph = false; + } } } - m_nCutPos = rInf.GetTextBreak( nLineWidth, nMaxLen, nMaxComp, nHyphPos, rInf.GetCachedVclData().get() ); + // search start of the last word, if needed + if ( bHyph ) + { + // nLastWord is the space character before the last word + sal_Int32 nLastWord = rInf.GetText().getLength() - 1; + bool bHyphenationNoLastWord = false; + assert( rHyphValues.getLength() > 3 && rHyphValues[3].Name == UPN_HYPH_NO_LAST_WORD ); + if ( rHyphValues[3].Value >>= bHyphenationNoLastWord ) + { + // skip spaces after the last word + 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; + } + } - // don't hyphenate last word of the paragraph - if ( bHyphenationNoLastWord && sal_Int32(m_nCutPos) > nLastWord ) - m_nCutPos = TextFrameIndex(nLastWord); + // don't hyphenate the last word of the paragraph + if ( bHyphenationNoLastWord && sal_Int32(m_nCutPos) > nLastWord && + TextFrameIndex(COMPLETE_STRING) != m_nCutPos ) + { + 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 5a794370b6da..c3a5068b3cf9 100644 --- a/sw/source/core/text/inftxt.cxx +++ b/sw/source/core/text/inftxt.cxx @@ -1361,13 +1361,14 @@ void SwTextPaintInfo::DrawViewOpt( const SwLinePortion &rPor, static void lcl_InitHyphValues( PropertyValues &rVals, sal_Int16 nMinLeading, sal_Int16 nMinTrailing, - bool bNoCapsHyphenation, bool bNoLastWordHyphenation, sal_Int16 nMinWordLength ) + bool bNoCapsHyphenation, bool bNoLastWordHyphenation, + sal_Int16 nMinWordLength, sal_Int16 nTextHyphZone ) { sal_Int32 nLen = rVals.getLength(); if (0 == nLen) // yet to be initialized? { - rVals.realloc( 5 ); + rVals.realloc( 6 ); PropertyValue *pVal = rVals.getArray(); pVal[0].Name = UPN_HYPH_MIN_LEADING; @@ -1389,8 +1390,12 @@ static void lcl_InitHyphValues( PropertyValues &rVals, pVal[4].Name = UPN_HYPH_MIN_WORD_LENGTH; pVal[4].Handle = UPH_HYPH_MIN_WORD_LENGTH; pVal[4].Value <<= nMinWordLength; + + pVal[5].Name = UPN_HYPH_ZONE; + pVal[5].Handle = UPH_HYPH_ZONE; + pVal[5].Value <<= nTextHyphZone; } - else if (5 == nLen) // already initialized once? + else if (6 == nLen) // already initialized once? { PropertyValue *pVal = rVals.getArray(); pVal[0].Value <<= nMinLeading; @@ -1398,6 +1403,7 @@ static void lcl_InitHyphValues( PropertyValues &rVals, pVal[2].Value <<= bNoCapsHyphenation; pVal[3].Value <<= bNoLastWordHyphenation; pVal[4].Value <<= nMinWordLength; + pVal[5].Value <<= nTextHyphZone; } else { OSL_FAIL( "unexpected size of sequence" ); @@ -1406,7 +1412,7 @@ static void lcl_InitHyphValues( PropertyValues &rVals, const PropertyValues & SwTextFormatInfo::GetHyphValues() const { - OSL_ENSURE( 4 == m_aHyphVals.getLength(), + OSL_ENSURE( 6 == m_aHyphVals.getLength(), "hyphenation values not yet initialized" ); return m_aHyphVals; } @@ -1427,8 +1433,10 @@ bool SwTextFormatInfo::InitHyph( const bool bAutoHyphen ) const sal_Int16 nMinimalWordLength = rAttr.GetMinWordLength(); const bool bNoCapsHyphenation = rAttr.IsNoCapsHyphenation(); const bool bNoLastWordHyphenation = rAttr.IsNoLastWordHyphenation(); + const sal_Int16 nTextHyphZone = rAttr.GetTextHyphenZone(); lcl_InitHyphValues( m_aHyphVals, nMinimalLeading, nMinimalTrailing, - bNoCapsHyphenation, bNoLastWordHyphenation, nMinimalWordLength ); + bNoCapsHyphenation, bNoLastWordHyphenation, + nMinimalWordLength, nTextHyphZone ); } return bAuto; } diff --git a/sw/source/core/unocore/unomapproperties.hxx b/sw/source/core/unocore/unomapproperties.hxx index 1f679b2e38fd..da2d4d907adf 100644 --- a/sw/source/core/unocore/unomapproperties.hxx +++ b/sw/source/core/unocore/unomapproperties.hxx @@ -118,6 +118,7 @@ { 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_PARA_HYPHENATION_ZONE, RES_PARATR_HYPHENZONE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_HYPHEN_ZONE}, \ { 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 }, \ @@ -442,6 +443,7 @@ { 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_PARA_HYPHENATION_ZONE, RES_PARATR_HYPHENZONE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_HYPHEN_ZONE},\ { 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/sidebar/WriterInspectorTextPanel.cxx b/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx index 530f72cdc923..913703366451 100644 --- a/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx +++ b/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx @@ -258,6 +258,7 @@ static OUString PropertyNametoRID(const OUString& rName) { "ParaHyphenationNoCaps", RID_PARA_HYPHENATION_NO_CAPS }, { "ParaHyphenationNoLastWord", RID_PARA_HYPHENATION_NO_LAST_WORD }, { "ParaHyphenationMinWordLength", RID_PARA_HYPHENATION_MIN_WORD_LENGTH }, + { "ParaHyphenationZone", RID_PARA_HYPHENATION_ZONE }, { "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 356b98051ec0..efdd8a912cf8 100644 --- a/xmloff/source/core/xmltoken.cxx +++ b/xmloff/source/core/xmltoken.cxx @@ -1066,6 +1066,7 @@ namespace xmloff::token { 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( "hyphenation-zone", XML_HYPHENATION_ZONE ), 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 a2e110e5bdb0..66bda55a8e3f 100644 --- a/xmloff/source/text/txtprmap.cxx +++ b/xmloff/source/text/txtprmap.cxx @@ -345,6 +345,7 @@ XMLPropertyMapEntry const aXMLParaPropMap[] = 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 ), + MAP_EXT( "ParaHyphenationZone", XML_NAMESPACE_LO_EXT, XML_HYPHENATION_ZONE, 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 ee0adc36055d..aaf1442d1dc6 100644 --- a/xmloff/source/token/tokens.txt +++ b/xmloff/source/token/tokens.txt @@ -966,6 +966,7 @@ hyphenation-remain-char-count hyphenation-no-caps hyphenation-no-last-word hyphenation-word-char-count +hyphenation-zone i icon icon-set |