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