diff --git a/cui/source/inc/paragrph.hxx b/cui/source/inc/paragrph.hxx index af1369fd56dd..222c11e41a51 100644 --- a/cui/source/inc/paragrph.hxx +++ b/cui/source/inc/paragrph.hxx @@ -177,6 +177,9 @@ class SvxParaAlignTabPage : public SfxTabPage std::unique_ptr m_xLetterSpacingMinimum; std::unique_ptr m_xLetterSpacingMaximum; + /// compound-based hyphenation + std::unique_ptr m_xCompoundBased; + /// glyph scaling std::unique_ptr m_xLabelGlyphScaling; std::unique_ptr m_xGlyphScalingMinimum; diff --git a/cui/source/tabpages/paragrph.cxx b/cui/source/tabpages/paragrph.cxx index 8c4917c1a9c6..a9e246b4efce 100644 --- a/cui/source/tabpages/paragrph.cxx +++ b/cui/source/tabpages/paragrph.cxx @@ -1299,6 +1299,7 @@ SvxParaAlignTabPage::SvxParaAlignTabPage(weld::Container* pPage, weld::DialogCon , m_xLabelLetterSpacing(m_xBuilder->weld_label(u"labelLetterSpacing"_ustr)) , m_xLetterSpacingMinimum(m_xBuilder->weld_metric_spin_button(u"spin_LETTER_SPACING_MIN"_ustr, FieldUnit::PERCENT)) , m_xLetterSpacingMaximum(m_xBuilder->weld_metric_spin_button(u"spin_LETTER_SPACING_MAX"_ustr, FieldUnit::PERCENT)) + , m_xCompoundBased(m_xBuilder->weld_check_button(u"checkCompoundBased"_ustr)) , m_xLabelGlyphScaling(m_xBuilder->weld_label(u"labelGlyphScaling"_ustr)) , m_xGlyphScalingMinimum(m_xBuilder->weld_metric_spin_button(u"spin_GLYPH_SCALING_MIN"_ustr, FieldUnit::PERCENT)) , m_xGlyphScalingMaximum(m_xBuilder->weld_metric_spin_button(u"spin_GLYPH_SCALING_MAX"_ustr, FieldUnit::PERCENT)) @@ -1409,6 +1410,7 @@ bool SvxParaAlignTabPage::FillItemSet( SfxItemSet* rOutSet ) m_xParagraphComposer->get_state_changed_from_saved() || m_xLetterSpacingMinimum->get_value_changed_from_saved() || m_xLetterSpacingMaximum->get_value_changed_from_saved() || + m_xCompoundBased->get_state_changed_from_saved() || m_xGlyphScalingMinimum->get_value_changed_from_saved() || m_xGlyphScalingMaximum->get_value_changed_from_saved(); } @@ -1436,6 +1438,7 @@ bool SvxParaAlignTabPage::FillItemSet( SfxItemSet* rOutSet ) aAdj.SetParagraphComposer( m_xParagraphComposer->get_active() ); aAdj.SetPropLetterSpacingMinimum( m_xLetterSpacingMinimum->get_value(FieldUnit::PERCENT) ); aAdj.SetPropLetterSpacingMaximum( m_xLetterSpacingMaximum->get_value(FieldUnit::PERCENT) ); + aAdj.SetCompoundBased( m_xCompoundBased->get_active() ); aAdj.SetPropScaleWidthMinimum( m_xGlyphScalingMinimum->get_value(FieldUnit::PERCENT) ); aAdj.SetPropScaleWidthMaximum( m_xGlyphScalingMaximum->get_value(FieldUnit::PERCENT) ); rOutSet->Put( aAdj ); @@ -1543,6 +1546,8 @@ void SvxParaAlignTabPage::Reset( const SfxItemSet* rSet ) m_xLetterSpacingMinimum->set_sensitive(true); m_xLetterSpacingMinimum->set_value(rAdj.GetPropLetterSpacingMinimum(), FieldUnit::PERCENT); m_xLetterSpacingMaximum->set_value(rAdj.GetPropLetterSpacingMaximum(), FieldUnit::PERCENT); + m_xCompoundBased->set_sensitive(true); + m_xCompoundBased->set_active(rAdj.GetCompoundBased()); // TODO add GlyphScaling (CharScaleWidth) m_xGlyphScalingMaximum->set_sensitive(true); m_xGlyphScalingMinimum->set_sensitive(true); @@ -1559,10 +1564,10 @@ void SvxParaAlignTabPage::Reset( const SfxItemSet* rSet ) m_xWordSpacingMinimum->set_sensitive(false); m_xWordSpacingMaximum->set_sensitive(false); m_xParagraphComposer->set_sensitive(false); - m_xParagraphComposer->set_sensitive(false); m_xLabelLetterSpacing->set_sensitive(false); m_xLetterSpacingMinimum->set_sensitive(false); m_xLetterSpacingMaximum->set_sensitive(false); + m_xCompoundBased->set_sensitive(false); m_xLabelGlyphScaling->set_sensitive(false); m_xGlyphScalingMinimum->set_sensitive(false); m_xGlyphScalingMaximum->set_sensitive(false); @@ -1664,6 +1669,7 @@ void SvxParaAlignTabPage::Reset( const SfxItemSet* rSet ) m_xParagraphComposer->save_state(); m_xLetterSpacingMinimum->save_value(); m_xLetterSpacingMaximum->save_value(); + m_xCompoundBased->save_state(); m_xGlyphScalingMinimum->save_value(); m_xGlyphScalingMaximum->save_value(); @@ -1690,6 +1696,7 @@ void SvxParaAlignTabPage::ChangesApplied() m_xParagraphComposer->save_state(); m_xLetterSpacingMinimum->save_value(); m_xLetterSpacingMaximum->save_value(); + m_xCompoundBased->save_state(); m_xGlyphScalingMinimum->save_value(); m_xGlyphScalingMaximum->save_value(); } @@ -1711,6 +1718,7 @@ IMPL_LINK_NOARG(SvxParaAlignTabPage, AlignHdl_Impl, weld::Toggleable&, void) // TODO visualize CharKerning with percentage m_xLetterSpacingMinimum->set_sensitive(bJustify); m_xLetterSpacingMaximum->set_sensitive(bJustify); + m_xCompoundBased->set_sensitive(bJustify); m_xLabelGlyphScaling->set_sensitive(bJustify); // TODO visualize CharScaleWidth with percentage m_xGlyphScalingMinimum->set_sensitive(bJustify); diff --git a/cui/uiconfig/ui/paragalignpage.ui b/cui/uiconfig/ui/paragalignpage.ui index a84b064f9074..7f75098f7373 100644 --- a/cui/uiconfig/ui/paragalignpage.ui +++ b/cui/uiconfig/ui/paragalignpage.ui @@ -740,6 +740,26 @@ 2 + + + Split compound words + True + True + False + True + True + True + + + Select this check box, if you want to prefer compound word splitting during justification. + + + + + 4 + 2 + + True diff --git a/editeng/source/items/paraitem.cxx b/editeng/source/items/paraitem.cxx index aa46545a8a1c..ce9d574af129 100644 --- a/editeng/source/items/paraitem.cxx +++ b/editeng/source/items/paraitem.cxx @@ -322,7 +322,8 @@ SvxAdjustItem::SvxAdjustItem(const SvxAdjust eAdjst, const sal_uInt16 nId ) nPropLetterSpacingMaximum(0), nPropScaleWidthMinimum(100), nPropScaleWidthMaximum(100), - bParagraphComposer(false) + bParagraphComposer(false), + bCompoundBased(false) { SetAdjust( eAdjst ); } @@ -342,6 +343,7 @@ bool SvxAdjustItem::operator==( const SfxPoolItem& rAttr ) const bParagraphComposer == rItem.bParagraphComposer && nPropLetterSpacingMinimum == rItem.nPropLetterSpacingMinimum && nPropLetterSpacingMaximum == rItem.nPropLetterSpacingMaximum && + bCompoundBased == rItem.bCompoundBased && nPropScaleWidthMinimum == rItem.nPropScaleWidthMinimum && nPropScaleWidthMaximum == rItem.nPropScaleWidthMaximum; } @@ -359,6 +361,7 @@ size_t SvxAdjustItem::hashCode() const o3tl::hash_combine(seed, bParagraphComposer); o3tl::hash_combine(seed, nPropLetterSpacingMinimum); o3tl::hash_combine(seed, nPropLetterSpacingMaximum); + o3tl::hash_combine(seed, bCompoundBased); o3tl::hash_combine(seed, nPropScaleWidthMinimum); o3tl::hash_combine(seed, nPropScaleWidthMaximum); return seed; @@ -388,6 +391,11 @@ bool SvxAdjustItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const rVal <<= bParagraphComposer; break; } + case MID_COMPOUND_BASED : + { + rVal <<= bCompoundBased; + break; + } default: ;//prevent warning } return true; @@ -471,6 +479,12 @@ bool SvxAdjustItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId ) bParagraphComposer = Any2Bool(rVal); } break; + case MID_COMPOUND_BASED : + { + ASSERT_CHANGE_REFCOUNTED_ITEM; + bCompoundBased = Any2Bool(rVal); + } + break; case MID_EXPAND_SINGLE : ASSERT_CHANGE_REFCOUNTED_ITEM; bOneBlock = Any2Bool(rVal); diff --git a/external/hunspell/UnpackedTarball_hunspell.mk b/external/hunspell/UnpackedTarball_hunspell.mk index 6c664f830d25..49f94f916f82 100644 --- a/external/hunspell/UnpackedTarball_hunspell.mk +++ b/external/hunspell/UnpackedTarball_hunspell.mk @@ -19,4 +19,9 @@ $(eval $(call gb_UnpackedTarball_set_post_action,hunspell,\ )) endif +$(eval $(call gb_UnpackedTarball_add_patches,hunspell, \ + external/hunspell/fix_morphological_analysis_for_compound_words.patch \ +)) + + # vim: set noet sw=4 ts=4: diff --git a/external/hunspell/fix_morphological_analysis_for_compound_words.patch b/external/hunspell/fix_morphological_analysis_for_compound_words.patch new file mode 100644 index 000000000000..b4ed5bff65c6 --- /dev/null +++ b/external/hunspell/fix_morphological_analysis_for_compound_words.patch @@ -0,0 +1,25 @@ +--- a/src/hunspell/affixmgr.cxx 2026-05-04 16:31:57.000000000 +0200 ++++ b/src/hunspell/affixmgr.cxx 2026-09-16 00:10:58.854821764 +0200 +@@ -2338,10 +2338,10 @@ + p = affix_check_morph(st, 0, i, compoundmiddle); + } + } ++ presult.push_back(MSEP_FLD); ++ presult.append(MORPH_PART); ++ presult.append(st, 0, i); + if (!p.empty()) { +- presult.push_back(MSEP_FLD); +- presult.append(MORPH_PART); +- presult.append(st, 0, i); + line_uniq_app(p, MSEP_REC); + presult.append(p); + } +@@ -2540,7 +2540,7 @@ + if (!rv && compoundend && !onlycpdrule) { + sfx = nullptr; + pfx = nullptr; +- rv = affix_check(word, i, word.size() - i, compoundend); ++ rv = affix_check(word, i, word.size() - i, compoundend, IN_CPD_END); + } + + if (!rv && !defcpdtable.empty() && words) { diff --git a/include/editeng/adjustitem.hxx b/include/editeng/adjustitem.hxx index 630058f259e4..23874f65efe6 100644 --- a/include/editeng/adjustitem.hxx +++ b/include/editeng/adjustitem.hxx @@ -63,6 +63,9 @@ class EDITENG_DLLPUBLIC SvxAdjustItem final : public SfxPoolItem // paragraph composer bool bParagraphComposer : 1; + // compound-based hyphenation + bool bCompoundBased : 1; + protected: virtual ItemInstanceManager* getItemInstanceManager() const override; @@ -160,6 +163,19 @@ public: bParagraphComposer = bValue; } + + bool GetCompoundBased() const + { + return bCompoundBased; + } + + void SetCompoundBased( const bool bValue ) + { + ASSERT_CHANGE_REFCOUNTED_ITEM; + bCompoundBased = bValue; + } + + sal_uInt16 GetPropWordSpacing() const { return nPropWordSpacing; diff --git a/include/editeng/memberids.h b/include/editeng/memberids.h index fa9306e52440..709261c9377a 100644 --- a/include/editeng/memberids.h +++ b/include/editeng/memberids.h @@ -79,6 +79,7 @@ #define MID_SCALE_WIDTH_MIN 8 #define MID_SCALE_WIDTH_MAX 9 #define MID_PARAGRAPH_COMPOSER 10 +#define MID_COMPOUND_BASED 11 //SvxFontItem // Don't use 0 as it used for the whole struct diff --git a/include/editeng/unotext.hxx b/include/editeng/unotext.hxx index d5a219d4e750..da1708444a0a 100644 --- a/include/editeng/unotext.hxx +++ b/include/editeng/unotext.hxx @@ -175,6 +175,7 @@ struct SfxItemPropertyMapEntry; {u"ParaScaleWidthMinimum"_ustr, EE_PARA_JUST, ::cppu::UnoType::get(), 0, MID_SCALE_WIDTH_MIN }, \ {u"ParaScaleWidthMaximum"_ustr, EE_PARA_JUST, ::cppu::UnoType::get(), 0, MID_SCALE_WIDTH_MAX }, \ {u"ParaComposer"_ustr, EE_PARA_JUST, ::cppu::UnoType::get(), 0, MID_PARAGRAPH_COMPOSER }, \ + {u"ParaIsCompoundBased"_ustr, EE_PARA_JUST, ::cppu::UnoType::get(), 0, MID_COMPOUND_BASED }, \ {u"WritingMode"_ustr, EE_PARA_WRITINGDIR, ::cppu::UnoType::get(), 0, 0 }, \ {u"WritingModeAutomatic"_ustr, EE_PARA_AUTOWRITINGDIR, ::cppu::UnoType::get(), 0, 0 } diff --git a/include/sfx2/msg.hxx b/include/sfx2/msg.hxx index 3f188a823e9b..35d0809793a4 100644 --- a/include/sfx2/msg.hxx +++ b/include/sfx2/msg.hxx @@ -140,7 +140,7 @@ SFX_DECL_TYPE(7); SFX_DECL_TYPE(8); SFX_DECL_TYPE(10); // for SfxDocInfoItem SFX_DECL_TYPE(11); - +SFX_DECL_TYPE(12); // for SvxAdjustStruct SFX_DECL_TYPE(13); // for SwAddPrinterItem, Sd... SFX_DECL_TYPE(14); SFX_DECL_TYPE(16); // for SwDocDisplayItem diff --git a/include/xmloff/xmltoken.hxx b/include/xmloff/xmltoken.hxx index abf0ec8e73ff..1313a9b21404 100644 --- a/include/xmloff/xmltoken.hxx +++ b/include/xmloff/xmltoken.hxx @@ -490,6 +490,7 @@ namespace xmloff::token { XML_COMMAND, XML_COMMENT, XML_COMPOSE, + XML_COMPOUND_BASED, XML_COND_STYLE_NAME, XML_CONDITION, XML_CONDITION_SOURCE, diff --git a/lingucomponent/source/hyphenator/hyphen/hyphenimp.cxx b/lingucomponent/source/hyphenator/hyphen/hyphenimp.cxx index f997a44b8873..48db82ec269a 100644 --- a/lingucomponent/source/hyphenator/hyphen/hyphenimp.cxx +++ b/lingucomponent/source/hyphenator/hyphen/hyphenimp.cxx @@ -517,6 +517,7 @@ Reference< XHyphenatedWord > SAL_CALL Hyphenator::hyphenate( const OUString& aWo sal_Int32 nPa = -1; while ( (nPa = morph.indexOf(u" pa:", nPa + 1)) > -1 ) { + sal_Int32 nPaTemp(nPa); // use hy: field of the actual stem, if it exists // pa:stem1 hy:st|em1 pa:stem2 -> st|em1||stem2 sal_Int32 nHy = morph.indexOf(u" hy:", nPa + 3); @@ -529,7 +530,7 @@ Reference< XHyphenatedWord > SAL_CALL Hyphenator::hyphenate( const OUString& aWo sStems += sStems2+ u"||"; else if ( sal_Int32 nBreak = o3tl::toInt32(sStems2) ) { - OUString sPa(morph.getToken(1, ' ', nPa).copy(3)); + OUString sPa(morph.getToken(1, ' ', nPaTemp).copy(3)); if ( nBreak < sPa.getLength() ) sStems += OUString::Concat(sPa.subView(0, nBreak)) + u"|" + sPa.subView(nBreak); @@ -537,7 +538,7 @@ Reference< XHyphenatedWord > SAL_CALL Hyphenator::hyphenate( const OUString& aWo } else { - OUString sPa(morph.getToken(1, ' ', nPa).copy(3)); + OUString sPa(morph.getToken(1, ' ', nPaTemp).copy(3)); // handle special case: missing pa: in morphological analysis // before in-word suffixes (German, Sweden etc. dictionaries) @@ -563,9 +564,6 @@ Reference< XHyphenatedWord > SAL_CALL Hyphenator::hyphenate( const OUString& aWo nSuffixLen = sPa.getLength() - nStemLen; } } - - if ( nPa == -1 ) // getToken() can modify nPa - break; } // handle verb and leg-, legesleg- prefixes (Hungarian-only) diff --git a/offapi/com/sun/star/style/ParagraphProperties.idl b/offapi/com/sun/star/style/ParagraphProperties.idl index 36ae0d4a5312..5539b540b164 100644 --- a/offapi/com/sun/star/style/ParagraphProperties.idl +++ b/offapi/com/sun/star/style/ParagraphProperties.idl @@ -644,6 +644,12 @@ published service ParagraphProperties @since LibreOffice 27.2 */ [optional, property] long ParaHyphenationLevel; + + /** specifies if automatic compound-based "smart" hyphenation is applied. + + @since LibreOffice 27.2 + */ + [optional, property] boolean ParaIsCompoundBased; }; diff --git a/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng b/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng index 710efd324ce9..71f4006ce7b5 100644 --- a/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng +++ b/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng @@ -3381,6 +3381,15 @@ xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1. + + + + + + + + + diff --git a/svx/sdi/svxitems.sdi b/svx/sdi/svxitems.sdi index a93e07985957..9b515767c521 100644 --- a/svx/sdi/svxitems.sdi +++ b/svx/sdi/svxitems.sdi @@ -65,6 +65,7 @@ struct SvxAdjustStruct INT16 ScaleWidthMinimum MID_SCALE_WIDTH_MIN; INT16 ScaleWidthMaximum MID_SCALE_WIDTH_MAX; BOOL ParagraphComposer MID_PARAGRAPH_COMPOSER; + BOOL CompoundBased MID_COMPOUND_BASED; }; item SvxAdjustStruct SvxAdjustItem; diff --git a/sw/inc/inspectorproperties.hrc b/sw/inc/inspectorproperties.hrc index deff9bca1a17..a125e88f040a 100644 --- a/sw/inc/inspectorproperties.hrc +++ b/sw/inc/inspectorproperties.hrc @@ -199,6 +199,7 @@ #define RID_PARA_BOTTOM_MARGIN_RELATIVE NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Bottom Margin Relative") #define RID_PARA_CHAPTER_NUMBERING_LEVEL NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Chapter Numbering Level") #define RID_PARA_COMPOSER NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Composer") +#define RID_PARA_COMPOUND_BASED_HYPHENATION NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Compound-based Hyphenation") #define RID_PARA_CONDITIONAL_STYLE_NAME NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Conditional Style Name") #define RID_PARA_CONTEXT_MARGIN NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Context Margin") #define RID_PARA_EXPAND_SINGLE_WORD NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Expand Single Word") diff --git a/sw/inc/unoprnms.hxx b/sw/inc/unoprnms.hxx index f82f78f15fa8..4f379e1a6e6a 100644 --- a/sw/inc/unoprnms.hxx +++ b/sw/inc/unoprnms.hxx @@ -519,6 +519,7 @@ inline constexpr OUString UNO_NAME_TEXT_USER_DEFINED_ATTRIBUTES = u"TextUserDefi inline constexpr OUString UNO_NAME_FILE_PATH = u"FilePath"_ustr; inline constexpr OUString UNO_NAME_PARA_CHAPTER_NUMBERING_LEVEL = u"ParaChapterNumberingLevel"_ustr; inline constexpr OUString UNO_NAME_PARA_COMPOSER = u"ParaComposer"_ustr; +inline constexpr OUString UNO_NAME_PARA_IS_COMPOUND_BASED = u"ParaIsCompoundBased"_ustr; inline constexpr OUString UNO_NAME_PARA_CONDITIONAL_STYLE_NAME = u"ParaConditionalStyleName"_ustr; inline constexpr OUString UNO_NAME_CHAPTER_NUMBERING_LEVEL = u"ChapterNumberingLevel"_ustr; inline constexpr OUString UNO_NAME_NUMBERING_SEPARATOR = u"NumberingSeparator"_ustr; diff --git a/sw/qa/extras/layout/data/tdf119908_smart_hyphenation_nl.odt b/sw/qa/extras/layout/data/tdf119908_smart_hyphenation_nl.odt new file mode 100644 index 000000000000..04a82426cff3 Binary files /dev/null and b/sw/qa/extras/layout/data/tdf119908_smart_hyphenation_nl.odt differ diff --git a/sw/qa/extras/layout/data/tdf173389_smart_hyphenation_hu.odt b/sw/qa/extras/layout/data/tdf173389_smart_hyphenation_hu.odt new file mode 100644 index 000000000000..9ece0b297d86 Binary files /dev/null and b/sw/qa/extras/layout/data/tdf173389_smart_hyphenation_hu.odt differ diff --git a/sw/qa/extras/layout/data/tdf173389_smart_hyphenation_nl.odt b/sw/qa/extras/layout/data/tdf173389_smart_hyphenation_nl.odt new file mode 100644 index 000000000000..9edd6bd190f7 Binary files /dev/null and b/sw/qa/extras/layout/data/tdf173389_smart_hyphenation_nl.odt differ diff --git a/sw/qa/extras/layout/layout3.cxx b/sw/qa/extras/layout/layout3.cxx index 97cf32dfd060..4c8fe859706a 100644 --- a/sw/qa/extras/layout/layout3.cxx +++ b/sw/qa/extras/layout/layout3.cxx @@ -851,7 +851,7 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf172674_less_hyphenation) assertXPath(pXmlDoc, "/root/page[1]/body/txt/SwParaPortion/SwLineLayout/SwHyphPortion", 3); } -// check smart hyphenation and compound-based smart hyphenation +// check smart hyphenation and disabled compound-based smart hyphenation CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf119908_smart_hyphenation_hu) { uno::Reference xHyphenator = LinguMgr::GetHyphenator(); @@ -1030,6 +1030,520 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf119908_smart_hyphenation_hu) pViewShell->Reformat(); pXmlDoc = parseLayoutDump(); + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[2]/SwParaPortion/SwLineLayout[1]", "portion", + u"Senkit sem lehet rabszolgaságban vagy szolgaságban tartani; a rabszolgaság és a rab"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[4]/SwParaPortion/SwLineLayout[1]", "portion", + u"Senkit sem lehet kínvallatásnak, avagy kegyetlen, embertelen vagy lealacsonyító bá"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[4]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[8]/SwParaPortion/SwLineLayout[1]", "portion", + u"A törvény előtt mindenki egyenlő, és minden megkülönböztetés nélkül joga van a tör"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[8]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[8]/SwParaPortion/SwLineLayout[3]", "portion", + u"sértő minden megkülönböztetéssel és minden ilyen megkülönböztetésre irányuló fel"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[8]/SwParaPortion/SwLineLayout[3]/SwHyphPortion", + 1); + + // no compound-based "smart" hyphenation of the compound "jog|orvoslat" + // This is hyphenated as "jogor-voslat", not "jog-orvoslat" + + if (bHunspell && aTests[0]) + { + assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/SwParaPortion/SwLineLayout[2]", "portion", + u"alapvető jogokat sértő eljárások ellen az illetékes hazai bíróságokhoz " + u"tényleges jogor"); + } + assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/SwParaPortion/SwLineLayout[2]/SwHyphPortion", + 1); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[14]/SwParaPortion/SwLineLayout[2]", "portion", + u"bíróság méltányosan és nyilvánosan tárgyalja, s ez határozzon egyrészt jogai és köte"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[14]/SwParaPortion/SwLineLayout[2]/SwHyphPortion", + 1); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[16]/SwParaPortion/SwLineLayout[1]", "portion", + u"Minden büntetendő cselekménnyel vádolt személyt ártatlannak kell vélelmezni mind"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[16]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[16]/SwParaPortion/SwLineLayout[2]", "portion", + u"addig, amíg bűnösségét nyilvánosan lefolytatott perben, a védelméhez szükséges vala"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[16]/SwParaPortion/SwLineLayout[2]/SwHyphPortion", + 1); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[17]/SwParaPortion/SwLineLayout[1]", "portion", + u"Senkit sem szabad elítélni oly cselekményért vagy mulasztásért, amely elkövetése pil"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[17]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[17]/SwParaPortion/SwLineLayout[3]", "portion", + u"Ugyancsak nem szabad súlyosabb büntetést kiszabni, mint amely a büntetendő cselek"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[17]/SwParaPortion/SwLineLayout[3]/SwHyphPortion", + 1); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[19]/SwParaPortion/SwLineLayout[1]", "portion", + u"Senkinek magánéletébe, családi ügyeibe, otthonába vagy levelezésébe nem szabad ön"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[19]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); + + // "smart" hyphenation: disabled hyphenation of "minden" by shrinking + // This was "Min-den" + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[19]/SwParaPortion/SwLineLayout[2]", "portion", + u"kényesen beavatkozni, sem pedig becsületében vagy jó hírnevében megsérteni. Minden "); + + assertXPath(pXmlDoc, "/root/page[1]/body/txt[21]/SwParaPortion/SwLineLayout[1]", "portion", + u"Az államon belül minden személynek joga van szabadon mozogni és lakóhelyét sza"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[21]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); + + // no compound-based "smart" hyphenation of the compound "vala|mint" + // This is hyphenated as "va-lamint" without enabled compound-based "smart" hyphenation + + if (bHunspell && aTests[1]) + { + assertXPath(pXmlDoc, "/root/page[1]/body/txt[22]/SwParaPortion/SwLineLayout[1]", "portion", + u"Minden személynek joga van minden országot, ideértve saját hazáját is, " + u"elhagyni, va"); + } + assertXPath(pXmlDoc, "/root/page[1]/body/txt[22]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); + + assertXPath(pXmlDoc, "/root/page[1]/body/txt/SwParaPortion/SwLineLayout/SwHyphPortion", 13); + + // delete page 2 to update hyphenation on the next page + + pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 2297, /*bBasicCall=*/false); + pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/true, 2297, /*bBasicCall=*/false); + pWrtShell->Delete(); + + // 10 hyphenations on page 3 + + pViewShell->Reformat(); + pXmlDoc = parseLayoutDump(); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[1]/SwParaPortion/SwLineLayout[1]", "portion", + u"Erre a jogra nem lehet hivatkozni közönséges bűncselekmény miatti, kellőképpen meg"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[1]/SwParaPortion/SwLineLayout[2]", "portion", + u"alapozott üldözés, sem pedig az Egyesült Nemzetek céljaival és elveivel ellentétes te"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/SwParaPortion/SwLineLayout[2]/SwHyphPortion", + 1); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[4]/SwParaPortion/SwLineLayout[1]", "portion", + u"Senkit sem lehet sem állampolgárságától, sem állampolgársága megváltoztatásának jo"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[4]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[6]/SwParaPortion/SwLineLayout[1]", "portion", + u"Mind a férfinak, mind a nőnek a nagykorúság elérésétől kezdve joga van fajon, nem"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[6]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[6]/SwParaPortion/SwLineLayout[2]", "portion", + u"zetiségen vagy valláson alapuló korlátozás nélkül házasságot kötni és családot alapí"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[6]/SwParaPortion/SwLineLayout[2]/SwHyphPortion", + 1); + + assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/SwParaPortion/SwLineLayout[1]", "portion", + u"Minden személynek joga van a tulajdonhoz, mind egyénileg, mind másokkal együtte"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); + + // "smart" hyphenation: disabled hyphenation of "valamint" by shifting + // This was "va-lamint" + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[2]", "portion", + u"a jog magában foglalja a vallás és a meggyőződés megváltoztatásának szabadságát, "); + + assertXPath(pXmlDoc, "/root/page[1]/body/txt[15]/SwParaPortion/SwLineLayout[1]", "portion", + u"Minden személynek joga van a vélemény és a kifejezés szabadságához, amely magá"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[15]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[15]/SwParaPortion/SwLineLayout[2]", "portion", + u"ban foglalja azt a jogot, hogy véleménye miatt ne szenvedjen zaklatást, és hogy hatá"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[15]/SwParaPortion/SwLineLayout[2]/SwHyphPortion", + 1); + + // "smart" hyphenation: disabled hyphenation of "információkat" by shrinking + // This was "információ-kat" + + assertXPath(pXmlDoc, "/root/page[1]/body/txt[15]/SwParaPortion/SwLineLayout[3]", "portion", + u"rokra való tekintet nélkül kutathasson, tájékozódhasson és terjeszthessen " + u"információkat "); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[22]/SwParaPortion/SwLineLayout[2]", "portion", + u"rendszeres, az általános és egyenlő szavazati jogot és a titkos szavazást biztosító vá"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[22]/SwParaPortion/SwLineLayout[2]/SwHyphPortion", + 1); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[24]/SwParaPortion/SwLineLayout[1]", "portion", + u"Minden személynek mint a társadalom tagjának joga van a szociális biztonsághoz, to"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[24]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); + + assertXPath(pXmlDoc, "/root/page[1]/body/txt/SwParaPortion/SwLineLayout/SwHyphPortion", 10); + + // delete page 3 to update hyphenation on the next page + + pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 2566, /*bBasicCall=*/false); + pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/true, 2566, /*bBasicCall=*/false); + pWrtShell->Delete(); + + // 12 hyphenations on page 4 + + pViewShell->Reformat(); + pXmlDoc = parseLayoutDump(); + + assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/SwParaPortion/SwLineLayout[1]", "portion", + u"Minden személynek joga van a munkához, a munka szabad megválasztásához, a mél"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); + + assertXPath(pXmlDoc, "/root/page[1]/body/txt[4]/SwParaPortion/SwLineLayout[1]", "portion", + u"Mindenkinek joga van egyenlő munkáért egyenlő bért kapni, bárminemű megkülön"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[4]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[8]/SwParaPortion/SwLineLayout[1]", "portion", + u"Minden személynek joga van a pihenéshez, a szabadidőhöz, beleértve a munkaidő ész"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[8]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[10]/SwParaPortion/SwLineLayout[1]", "portion", + u"Minden személynek joga van saját maga és családja egészségének és jólétének bizto"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[10]/SwParaPortion/SwLineLayout[3]", "portion", + u"ellátáshoz, valamint a szükséges szociális szolgáltatásokhoz, továbbá joga van a szoci"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/SwParaPortion/SwLineLayout[3]/SwHyphPortion", + 1); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[11]/SwParaPortion/SwLineLayout[2]", "portion", + u"gyermek, akár házasságból, akár házasságon kívül született, azonos szociális védelem"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[11]/SwParaPortion/SwLineLayout[2]/SwHyphPortion", + 1); + + // no compound-based "smart" hyphenation of the compound "alap|fokon" + // This is hyphenated as "alapfo-kon", not "alap-fokon" + + if (bHunspell && aTests[2]) + { + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[1]", "portion", + u"Minden személynek joga van az oktatáshoz. Az oktatásnak legalábbis elemi és alapfo"); + } + assertXPath(pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[14]/SwParaPortion/SwLineLayout[1]", "portion", + u"A nevelésnek az emberi személyiség teljes kibontakoztatására, valamint az emberi jo"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[14]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[14]/SwParaPortion/SwLineLayout[4]", "portion", + u"közötti megértést, türelmet és barátságot, valamint támogatnia kell az Egyesült Nem"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[14]/SwParaPortion/SwLineLayout[4]/SwHyphPortion", + 1); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[15]/SwParaPortion/SwLineLayout[1]", "portion", + u"A szülőket elsőbbségi jog illeti meg a gyermekeiknek adandó oktatás megválasztásá"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[15]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); + + // no compound-based "smart" hyphenation of the compound "jó|téteményekben" + // This is not hyphenated, when compound-based smart hyphenation is disabled + + if (bHunspell && aTests[3]) + { + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[17]/SwParaPortion/SwLineLayout[2]", "portion", + u"a művészetek élvezéséhez, valamint a tudomány haladásában és az abból származó "); + assertXPath(pXmlDoc, + "/root/page[1]/body/txt[4]/SwParaPortion/SwLineLayout[2]/SwHyphPortion", 0); + } + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[18]/SwParaPortion/SwLineLayout[1]", "portion", + u"Mindenkinek joga van minden általa alkotott tudományos, irodalmi és művészeti ter"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[18]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); + + assertXPath(pXmlDoc, "/root/page[1]/body/txt/SwParaPortion/SwLineLayout/SwHyphPortion", 12); + + // delete page 4 to update hyphenation on the next page + + pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 2766, /*bBasicCall=*/false); + pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/true, 2766, /*bBasicCall=*/false); + pWrtShell->Delete(); + + // 5 hyphenations on page 5 + + pViewShell->Reformat(); + pXmlDoc = parseLayoutDump(); + + assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/SwParaPortion/SwLineLayout[1]", "portion", + u"Minden személynek joga van ahhoz, hogy mind a társadalmi, mind a nemzetközi vi"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[1]/SwParaPortion/SwLineLayout[2]", "portion", + u"szonyok tekintetében olyan rendszer uralkodjék, amelyben a jelen Nyilatkozatban ki"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/SwParaPortion/SwLineLayout[2]/SwHyphPortion", + 1); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[4]/SwParaPortion/SwLineLayout[1]", "portion", + u"Jogainak gyakorlásában és szabadságainak élvezetében mindenki csak olyan korláto"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[4]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); + + // no compound-based "smart" hyphenation of the compound "el|ismerésének" + // This is not hyphenated by the plain, i.e. not compound-based "smart" hyphenation. + + if (bHunspell && aTests[4]) + { + assertXPath(pXmlDoc, "/root/page[1]/body/txt[4]/SwParaPortion/SwLineLayout[2]", "portion", + u"zásoknak lehet alávetve, amelyeket a törvény kizárólag mások jogai és " + u"szabadságai "); + assertXPath(pXmlDoc, + "/root/page[1]/body/txt[4]/SwParaPortion/SwLineLayout[2]/SwHyphPortion", 0); + } + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[5]/SwParaPortion/SwLineLayout[1]", "portion", + u"Ezeket a jogokat és szabadságokat semmi esetre sem lehet az Egyesült Nemzetek cél"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[5]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); + + // "smart" hyphenation: disabled hyphenation of "állam" by shifting + // This was "ál-lam" + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[7]/SwParaPortion/SwLineLayout[1]", "portion", + u"A jelen Nyilatkozat egyetlen rendelkezése sem értelmezhető úgy, hogy az valamely "); + + if (bHunspell && aTests[4]) + assertXPath(pXmlDoc, "/root/page[1]/body/txt/SwParaPortion/SwLineLayout/SwHyphPortion", 5); +} + +// check smart hyphenation and compound-based smart hyphenation +CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf173389_smart_hyphenation_hu) +{ + uno::Reference xHyphenator = LinguMgr::GetHyphenator(); + if (!xHyphenator->hasLocale(lang::Locale(u"hu"_ustr, u"HU"_ustr, OUString()))) + return; + + // compound-based hyphenation needs Hunspell morphological analysis + // (and an up-to-date Hungarian dictionary for full testing, which + // contains compound splitting information. E.g. dictionary version 1.7 + // didn't contain data for the words "valamint" and "elismerésének") + uno::Reference xSpell = LinguMgr::GetSpellChecker(); + LanguageType eLang + = LanguageTag::convertToLanguageType(lang::Locale(u"hu"_ustr, u"HU"_ustr, OUString())); + if (!xSpell.is() || !xSpell->hasLanguage(static_cast(eLang))) + return; + + std::vector> aCompound = { + { u"jogorvoslatért"_ustr, "hy:3" }, { u"valamint"_ustr, "hy:4" }, + { u"alapfokon"_ustr, "pa:fokon" }, { u"jótéteményekben"_ustr, "hy:2" }, + { u"elismerésének"_ustr, "hy:2" }, + }; + + // tests supported by the actual spelling dictionary + bool aTests[5]; + + // check Hunspell-based morphological analysis & hyphenation + bool bHunspell = xSpell->isValid("", static_cast(eLang), + uno::Sequence()); + if (bHunspell) + { + // check dictionary support of compound splitting + for (size_t i = 0; i < aCompound.size(); ++i) + { + aTests[i] = false; + // get morphological analysis of the test word + uno::Reference xTmpRes = xSpell->spell( + "" + aCompound[i].first + "", + static_cast(eLang), uno::Sequence()); + if (xTmpRes.is()) + { + uno::Sequence seq = xTmpRes->getAlternatives(); + if (seq.hasElements()) + { + sal_Int32 nEndOfFirstAnalysis = seq[0].indexOf(""); + // use only the first analysis, like the lingucomponent implementation + OUString morph(seq[0].copy(0, nEndOfFirstAnalysis)); + + // check compound data + aTests[i] = morph.indexOf(aCompound[i].second) > -1; + } + } + } + } + + createSwDoc("tdf173389_smart_hyphenation_hu.odt"); + // Ensure that all text portions are calculated before testing. + SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell(); + + CPPUNIT_ASSERT(pViewShell); + pViewShell->Reformat(); + + xmlDocUniquePtr pXmlDoc = parseLayoutDump(); + + // 53 hyphenations on 5 pages using compound-based smart hyphenation + // This was 67 without smart hyphenation and 53 without compound-based smart hyphenation + // (using only the plain or interoperable smart hyphenation). + + // 13 hyphenations on page 1 + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[3]/SwParaPortion/SwLineLayout[1]", "portion", + u"Tekintettel arra, hogy az emberiség családja minden egyes tagja méltóságának, vala"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[3]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); + + // "smart" hyphenation: disabled hyphenation of "emberiség" by shrinking + // This was "emberi-ség" + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[4]/SwParaPortion/SwLineLayout[1]", "portion", + u"Tekintettel arra, hogy az emberi jogok el nem ismerése és semmibevevése az emberiség "); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[5]/SwParaPortion/SwLineLayout[1]", "portion", + u"Tekintettel annak fontosságára, hogy az emberi jogokat a jog uralma védelmezze, ne"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[5]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[5]/SwParaPortion/SwLineLayout[2]", "portion", + u"hogy az ember végső szükségében a zsarnokság és az elnyomás elleni lázadásra kény"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[5]/SwParaPortion/SwLineLayout[2]/SwHyphPortion", + 1); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[6]/SwParaPortion/SwLineLayout[1]", "portion", + u"Tekintettel arra, hogy igen lényeges a nemzetek közötti baráti kapcsolatok kifejlődésé"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[6]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); + + // "smart" hyphenation: disabled hyphenation of "tettek" by shrinking + // This was "tet-tek" + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[7]/SwParaPortion/SwLineLayout[1]", "portion", + u"Tekintettel arra, hogy az Alapokmányban az Egyesült Nemzetek népei újból hitet tettek "); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[7]/SwParaPortion/SwLineLayout[3]", "portion", + u"egyenjogúsága mellett, valamint kinyilvánították azt az elhatározásukat, hogy elősegí"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[7]/SwParaPortion/SwLineLayout[3]/SwHyphPortion", + 1); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[8]/SwParaPortion/SwLineLayout[1]", "portion", + u"Tekintettel arra, hogy a tagállamok kötelezték magukat arra, hogy az Egyesült Nemze"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[8]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); + + assertXPath(pXmlDoc, "/root/page[1]/body/txt[9]/SwParaPortion/SwLineLayout[1]", "portion", + u"A KÖZGYŰLÉS kinyilvánítja az EMBERI JOGOK EGYETEMES NYILATKOZA"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[9]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); + + // "smart" hyphenation: disabled hyphenation of "törekednie" by shifting + // This was "tö-rekednie" + + assertXPath(pXmlDoc, "/root/page[1]/body/txt[9]/SwParaPortion/SwLineLayout[2]", "portion", + u"TÁT, mint azt a közös eszményt, amelynek elérésére minden népnek és nemzetnek "); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[11]/SwParaPortion/SwLineLayout[1]", "portion", + u"Minden emberi lény szabadnak születik, és egyenlő méltósága és joga van. Az embe"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[11]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[2]", "portion", + u"nyelvre, vallásra, politikai vagy más véleményre, nemzeti vagy társadalmi szárma"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[2]/SwHyphPortion", + 1); + + assertXPath(pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[3]", "portion", + u"zásra, vagyonra, születésre vagy más körülményre vonatkozó mindennemű megkülön"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[3]/SwHyphPortion", + 1); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[4]", "portion", + u"böztetés nélkül mindenkit megilletnek. Ezenfelül nem lehet semmiféle megkülönböz"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[4]/SwHyphPortion", + 1); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[5]", "portion", + u"tetést tenni annak az országnak vagy területnek a politikai, jogi vagy nemzetközi hely"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[5]/SwHyphPortion", + 1); + + assertXPath( + pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[7]", "portion", + u"terület független, gyámság alatt áll, nem autonóm vagy szuverenitása bármely vonat"); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[7]/SwHyphPortion", + 1); + + assertXPath(pXmlDoc, "/root/page[1]/body/txt/SwParaPortion/SwLineLayout/SwHyphPortion", 13); + + // delete page 1 to update hyphenation on the next page + + SwWrtShell* const pWrtShell = getSwDocShell()->GetWrtShell(); + pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/true, 2868, /*bBasicCall=*/false); + pWrtShell->Delete(); + + // 13 hyphenations on page 2 + + pViewShell->Reformat(); + pXmlDoc = parseLayoutDump(); + assertXPath( pXmlDoc, "/root/page[1]/body/txt[2]/SwParaPortion/SwLineLayout[1]", "portion", u"Senkit sem lehet rabszolgaságban vagy szolgaságban tartani; a rabszolgaság és a rab"); @@ -1365,6 +1879,216 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf119908_smart_hyphenation_hu) assertXPath(pXmlDoc, "/root/page[1]/body/txt/SwParaPortion/SwLineLayout/SwHyphPortion", 5); } +// check smart hyphenation and disabled compound-based smart hyphenation +CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf119908_smart_hyphenation_nl) +{ + uno::Reference xHyphenator = LinguMgr::GetHyphenator(); + if (!xHyphenator->hasLocale(lang::Locale(u"nl"_ustr, u"NL"_ustr, OUString()))) + return; + + // compound-based hyphenation needs fixed Hunspell morphological analysis + uno::Reference xSpell = LinguMgr::GetSpellChecker(); + LanguageType eLang + = LanguageTag::convertToLanguageType(lang::Locale(u"nl"_ustr, u"NL"_ustr, OUString())); + if (!xSpell.is() || !xSpell->hasLanguage(static_cast(eLang))) + return; + + std::vector> aCompound = { + { u"arbeidsvoorwaarden"_ustr, "pa:arbeids" }, + { u"doeleinden"_ustr, "pa:doel" }, + }; + + // tests supported by the actual spelling dictionary + bool aTests[2]; + + // check Hunspell-based morphological analysis & hyphenation + bool bHunspell = xSpell->isValid("", static_cast(eLang), + uno::Sequence()); + if (bHunspell) + { + // check dictionary support of compound splitting + for (size_t i = 0; i < aCompound.size(); ++i) + { + aTests[i] = false; + // get morphological analysis of the test word + uno::Reference xTmpRes = xSpell->spell( + "" + aCompound[i].first + "", + static_cast(eLang), uno::Sequence()); + if (xTmpRes.is()) + { + uno::Sequence seq = xTmpRes->getAlternatives(); + if (seq.hasElements()) + { + sal_Int32 nEndOfFirstAnalysis = seq[0].indexOf(""); + // use only the first analysis, like the lingucomponent implementation + OUString morph(seq[0].copy(0, nEndOfFirstAnalysis)); + + // check compound data + aTests[i] = morph.indexOf(aCompound[i].second) > -1; + } + } + } + } + + createSwDoc("tdf119908_smart_hyphenation_ml.odt"); + // Ensure that all text portions are calculated before testing. + SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell(); + + CPPUNIT_ASSERT(pViewShell); + pViewShell->Reformat(); + + xmlDocUniquePtr pXmlDoc = parseLayoutDump(); + + // delete page 1-2 to update hyphenation on the next page + + SwWrtShell* const pWrtShell = getSwDocShell()->GetWrtShell(); + pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/true, 6151, /*bBasicCall=*/false); + pWrtShell->Delete(); + + pViewShell->Reformat(); + pXmlDoc = parseLayoutDump(); + + // no compound-based "smart" hyphenation of the compound "arbeids|voorwaarden" + // This is hyphenated as "ar-beidsvoorwaarden" previously, not "arbeids-voorwaarden" + + if (bHunspell && aTests[0]) + { + assertXPath(pXmlDoc, "/root/page[1]/body/txt[25]/SwParaPortion/SwLineLayout[1]", "portion", + u"Een ieder heeft recht op arbeid, op vrije keuze van beroep, op rechtmatige " + u"en gunstige ar"); + } + assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/SwParaPortion/SwLineLayout[2]/SwHyphPortion", + 1); + + // delete page 2 to update hyphenation on the next page + + pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 6179, /*bBasicCall=*/false); + pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/true, 6179, /*bBasicCall=*/false); + pWrtShell->Delete(); + + // 10 hyphenations on page 3 + + pViewShell->Reformat(); + pXmlDoc = parseLayoutDump(); + + // compound-based "smart" hyphenation of the compound "doel|einden" + // This is hyphenated as "doelein-den" previously, not "doel-einden" + + if (bHunspell && aTests[1]) + { + assertXPath(pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[1]", "portion", + u"Deze rechten en vrijheden mogen in geen geval worden uitgeoefend in strijd " + u"met de doelein"); + } + assertXPath(pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); +} + +// check smart hyphenation and compound-based smart hyphenation +CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf173389_smart_hyphenation_nl) +{ + uno::Reference xHyphenator = LinguMgr::GetHyphenator(); + if (!xHyphenator->hasLocale(lang::Locale(u"nl"_ustr, u"NL"_ustr, OUString()))) + return; + + // compound-based hyphenation needs fixed Hunspell morphological analysis + uno::Reference xSpell = LinguMgr::GetSpellChecker(); + LanguageType eLang + = LanguageTag::convertToLanguageType(lang::Locale(u"nl"_ustr, u"NL"_ustr, OUString())); + if (!xSpell.is() || !xSpell->hasLanguage(static_cast(eLang))) + return; + + std::vector> aCompound = { + { u"arbeidsvoorwaarden"_ustr, "pa:arbeids" }, + { u"doeleinden"_ustr, "pa:doel" }, + }; + + // tests supported by the actual spelling dictionary + bool aTests[2]; + + // check Hunspell-based morphological analysis & hyphenation + bool bHunspell = xSpell->isValid("", static_cast(eLang), + uno::Sequence()); + if (bHunspell) + { + // check dictionary support of compound splitting + for (size_t i = 0; i < aCompound.size(); ++i) + { + aTests[i] = false; + // get morphological analysis of the test word + uno::Reference xTmpRes = xSpell->spell( + "" + aCompound[i].first + "", + static_cast(eLang), uno::Sequence()); + if (xTmpRes.is()) + { + uno::Sequence seq = xTmpRes->getAlternatives(); + if (seq.hasElements()) + { + sal_Int32 nEndOfFirstAnalysis = seq[0].indexOf(""); + // use only the first analysis, like the lingucomponent implementation + OUString morph(seq[0].copy(0, nEndOfFirstAnalysis)); + + // check compound data + aTests[i] = morph.indexOf(aCompound[i].second) > -1; + } + } + } + } + + createSwDoc("tdf173389_smart_hyphenation_ml.odt"); + // Ensure that all text portions are calculated before testing. + SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell(); + + CPPUNIT_ASSERT(pViewShell); + pViewShell->Reformat(); + + xmlDocUniquePtr pXmlDoc = parseLayoutDump(); + + // delete page 1-2 to update hyphenation on the next page + + SwWrtShell* const pWrtShell = getSwDocShell()->GetWrtShell(); + pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/true, 6151, /*bBasicCall=*/false); + pWrtShell->Delete(); + + pViewShell->Reformat(); + pXmlDoc = parseLayoutDump(); + + // compound-based "smart" hyphenation of the compound "arbeids|voorwaarden" + // This was hyphenated as "ar-beidsvoorwaarden" previously, now "arbeids-voorwaarden" + + if (bHunspell && aTests[0]) + { + assertXPath(pXmlDoc, "/root/page[1]/body/txt[25]/SwParaPortion/SwLineLayout[1]", "portion", + u"Een ieder heeft recht op arbeid, op vrije keuze van beroep, op rechtmatige " + u"en gunstige arbeids"); + } + assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/SwParaPortion/SwLineLayout[2]/SwHyphPortion", + 1); + + // delete page 2 to update hyphenation on the next page + + pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 6179, /*bBasicCall=*/false); + pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/true, 6179, /*bBasicCall=*/false); + pWrtShell->Delete(); + + // 10 hyphenations on page 3 + + pViewShell->Reformat(); + pXmlDoc = parseLayoutDump(); + + // compound-based "smart" hyphenation of the compound "doel|einden" + // This was hyphenated as "doelein-den" previously, now "doel-einden" + + if (bHunspell && aTests[1]) + { + assertXPath(pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[1]", "portion", + u"Deze rechten en vrijheden mogen in geen geval worden uitgeoefend in strijd " + u"met de doel"); + } + assertXPath(pXmlDoc, "/root/page[1]/body/txt[13]/SwParaPortion/SwLineLayout[1]/SwHyphPortion", + 1); +} + CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf158333) { createSwDoc("tdf130088.docx"); diff --git a/sw/qa/uitest/styleInspector/styleInspector.py b/sw/qa/uitest/styleInspector/styleInspector.py index 3995c8be557f..1f808c878835 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(160, len(xListBox.getChild('0').getChild('0').getChildren())) + self.assertEqual(161, 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(160, len(xListBox.getChild('0').getChild('0').getChildren())) + self.assertEqual(161, 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(160, len(xListBox.getChild('0').getChild('0').getChildren())) + self.assertEqual(161, len(xListBox.getChild('0').getChild('0').getChildren())) xParDirFormatting = xListBox.getChild('1') self.assertEqual(7, len(xParDirFormatting.getChildren())) @@ -75,13 +75,13 @@ 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(160, len(xParStyle.getChild('0').getChildren())) + self.assertEqual(161, 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())) xTitleStyle = xParStyle.getChild('2') self.assertEqual("Title\t", get_state_as_dict(xTitleStyle)['Text']) - self.assertEqual(24, len(xTitleStyle.getChildren())) + self.assertEqual(25, len(xTitleStyle.getChildren())) self.assertEqual("Char Difference Height\t0", get_state_as_dict(xTitleStyle.getChild('0'))['Text']) self.assertEqual("Char Difference Height Asian\t0", get_state_as_dict(xTitleStyle.getChild('1'))['Text']) self.assertEqual("Char Difference Height Complex\t0", get_state_as_dict(xTitleStyle.getChild('2'))['Text']) @@ -97,15 +97,16 @@ class styleNavigator(UITestCase): self.assertEqual("Follow Style\tText body", get_state_as_dict(xTitleStyle.getChild('12'))['Text']) self.assertEqual("Para Adjust\t3", get_state_as_dict(xTitleStyle.getChild('13'))['Text']) self.assertEqual("Para Composer\tFalse", get_state_as_dict(xTitleStyle.getChild('14'))['Text']) - self.assertEqual("Para Expand Single Word\tFalse", get_state_as_dict(xTitleStyle.getChild('15'))['Text']) - self.assertEqual("Para Last Line Adjust\t0", get_state_as_dict(xTitleStyle.getChild('16'))['Text']) - self.assertEqual("Para Letter Spacing Maximum\t0", get_state_as_dict(xTitleStyle.getChild('17'))['Text']) - self.assertEqual("Para Letter Spacing Minimum\t0", get_state_as_dict(xTitleStyle.getChild('18'))['Text']) - self.assertEqual("Para Scale Width Maximum\t100", get_state_as_dict(xTitleStyle.getChild('19'))['Text']) - self.assertEqual("Para Scale Width Minimum\t100", get_state_as_dict(xTitleStyle.getChild('20'))['Text']) - self.assertEqual("Para Word Spacing\t100", get_state_as_dict(xTitleStyle.getChild('21'))['Text']) - self.assertEqual("Para Word Spacing Maximum\t100", get_state_as_dict(xTitleStyle.getChild('22'))['Text']) - self.assertEqual("Para Word Spacing Minimum\t100", get_state_as_dict(xTitleStyle.getChild('23'))['Text']) + self.assertEqual("Para Compound-based Hyphenation\tFalse", get_state_as_dict(xTitleStyle.getChild('15'))['Text']) + self.assertEqual("Para Expand Single Word\tFalse", get_state_as_dict(xTitleStyle.getChild('16'))['Text']) + self.assertEqual("Para Last Line Adjust\t0", get_state_as_dict(xTitleStyle.getChild('17'))['Text']) + self.assertEqual("Para Letter Spacing Maximum\t0", get_state_as_dict(xTitleStyle.getChild('18'))['Text']) + self.assertEqual("Para Letter Spacing Minimum\t0", get_state_as_dict(xTitleStyle.getChild('19'))['Text']) + self.assertEqual("Para Scale Width Maximum\t100", get_state_as_dict(xTitleStyle.getChild('20'))['Text']) + self.assertEqual("Para Scale Width Minimum\t100", get_state_as_dict(xTitleStyle.getChild('21'))['Text']) + self.assertEqual("Para Word Spacing\t100", get_state_as_dict(xTitleStyle.getChild('22'))['Text']) + self.assertEqual("Para Word Spacing Maximum\t100", get_state_as_dict(xTitleStyle.getChild('23'))['Text']) + self.assertEqual("Para Word Spacing Minimum\t100", get_state_as_dict(xTitleStyle.getChild('24'))['Text']) self.assertEqual(0, len(xListBox.getChild('1').getChildren())) self.assertEqual(0, len(xListBox.getChild('2').getChildren())) @@ -117,7 +118,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(160, len(xParStyle.getChild('0').getChildren())) + self.assertEqual(161, len(xParStyle.getChild('0').getChildren())) self.assertEqual("Body Text\t", get_state_as_dict(xParStyle.getChild('1'))['Text']) self.assertEqual(6, len(xParStyle.getChild('1').getChildren())) @@ -152,7 +153,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(160, len(xListBox.getChild('0').getChild('0').getChildren())) + self.assertEqual(161, 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())) @@ -162,7 +163,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(160, len(xListBox.getChild('0').getChild('0').getChildren())) + self.assertEqual(161, len(xListBox.getChild('0').getChild('0').getChildren())) xParDirFormatting = xListBox.getChild('1') self.assertEqual(1, len(xParDirFormatting.getChildren())) @@ -215,7 +216,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(160, len(xListBox.getChild('0').getChild('0').getChildren())) + self.assertEqual(161, 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())) @@ -225,7 +226,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(160, len(xListBox.getChild('1').getChild('0').getChildren())) + self.assertEqual(161, len(xListBox.getChild('1').getChild('0').getChildren())) # Outer bookmark xBookmarkFormatting = xListBox.getChild('0') @@ -272,7 +273,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(160, len(xListBox.getChild('0').getChild('0').getChildren())) + self.assertEqual(161, 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 0599e4dc1007..33a597b41988 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(160, len(xListBox.getChild('0').getChild('0').getChildren())) + self.assertEqual(161, 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/portxt.cxx b/sw/source/core/text/portxt.cxx index 2432bf6f023b..a96c964fd3e5 100644 --- a/sw/source/core/text/portxt.cxx +++ b/sw/source/core/text/portxt.cxx @@ -325,10 +325,37 @@ bool SwTextPortion::IsCompoundSplit( SwTextFormatInfo &rInf, const SwTextGuess & LanguageType aLang = rInf.GetTextFrame()->GetLangOfChar(rInf.GetIdx(), 1, true); - // Hungarian-only yet - if ( LANGUAGE_HUNGARIAN != aLang ) + // is compound-based "smart" hyphenation enabled? + SvxAdjustItem aAdjustItem = + rInf.GetTextFrame()->GetTextNodeForParaProps()->GetSwAttrSet().GetAdjust(); + if ( !aAdjustItem.GetCompoundBased() ) return false; + // need Hunspell dictionaries with compound-based heuristics + // TODO update the list according to the dictionary update + if ( LANGUAGE_ICELANDIC != aLang && + LANGUAGE_DANISH != aLang && + LANGUAGE_DUTCH != aLang && + LANGUAGE_DUTCH_BELGIAN != aLang && + LANGUAGE_ESTONIAN != aLang && + LANGUAGE_FINNISH != aLang && + LANGUAGE_LATVIAN != aLang && + LANGUAGE_GERMAN != aLang && + LANGUAGE_GERMAN_AUSTRIAN != aLang && + LANGUAGE_GERMAN_LIECHTENSTEIN != aLang && + LANGUAGE_GERMAN_LUXEMBOURG != aLang && + LANGUAGE_GERMAN_SWISS != aLang && + LANGUAGE_HUNGARIAN != aLang && + LANGUAGE_MONGOLIAN_CYRILLIC_MONGOLIA != aLang && + LANGUAGE_NORWEGIAN_BOKMAL != aLang && + LANGUAGE_NORWEGIAN_NYNORSK != aLang && + LANGUAGE_SWEDISH != aLang && + LANGUAGE_SWEDISH_FINLAND != aLang && + LANGUAGE_USER_ESPERANTO != aLang ) + { + return false; + } + LanguageTag aLanguageTag(aLang); uno::Reference< linguistic2::XHyphenator > xHyph; @@ -733,6 +760,7 @@ bool SwTextPortion::Format_( SwTextFormatInfo &rInf ) // perhaps there is a compound splitting in the same word before the plain hyphenation bool bPossibleCompoundSplit = bOrigHyphenated3 && + aAdjustItem.GetCompoundBased() && pGuess3->HyphWord()->getHyphenationPos() < pGuess2->HyphWord()->getHyphenationPos() && pGuess3->HyphWord()->getWord().equals(pGuess2->HyphWord()->getWord()); diff --git a/sw/source/core/unocore/unomapproperties.hxx b/sw/source/core/unocore/unomapproperties.hxx index 0ec513ad13f0..4d18da466b02 100644 --- a/sw/source/core/unocore/unomapproperties.hxx +++ b/sw/source/core/unocore/unomapproperties.hxx @@ -198,6 +198,7 @@ { UNO_NAME_PARA_SCALE_WIDTH_MIN, RES_PARATR_ADJUST, cppu::UnoType::get(), PropertyAttribute::MAYBEVOID, MID_SCALE_WIDTH_MIN }, \ { UNO_NAME_PARA_SCALE_WIDTH_MAX, RES_PARATR_ADJUST, cppu::UnoType::get(), PropertyAttribute::MAYBEVOID, MID_SCALE_WIDTH_MAX }, \ { UNO_NAME_PARA_COMPOSER, RES_PARATR_ADJUST, cppu::UnoType::get(), PropertyAttribute::MAYBEVOID, MID_PARAGRAPH_COMPOSER }, \ + { UNO_NAME_PARA_IS_COMPOUND_BASED, RES_PARATR_ADJUST, cppu::UnoType::get(), PropertyAttribute::MAYBEVOID, MID_COMPOUND_BASED }, \ { UNO_NAME_PARA_LINE_NUMBER_COUNT, RES_LINENUMBER, cppu::UnoType::get(), PropertyAttribute::MAYBEVOID, MID_LINENUMBER_COUNT }, \ { UNO_NAME_PARA_LINE_NUMBER_START_VALUE, RES_LINENUMBER, cppu::UnoType::get(), PropertyAttribute::MAYBEVOID, MID_LINENUMBER_STARTVALUE }, \ { UNO_NAME_PARA_LINE_SPACING, RES_PARATR_LINESPACING, cppu::UnoType::get(), PropertyAttribute::MAYBEVOID, CONVERT_TWIPS }, \ @@ -475,6 +476,7 @@ { UNO_NAME_PARA_SCALE_WIDTH_MIN, RES_PARATR_ADJUST, cppu::UnoType::get(), PROPERTY_NONE, MID_SCALE_WIDTH_MIN},\ { UNO_NAME_PARA_SCALE_WIDTH_MAX, RES_PARATR_ADJUST, cppu::UnoType::get(), PROPERTY_NONE, MID_SCALE_WIDTH_MAX},\ { UNO_NAME_PARA_COMPOSER, RES_PARATR_ADJUST, cppu::UnoType::get(), PROPERTY_NONE, MID_PARAGRAPH_COMPOSER},\ + { UNO_NAME_PARA_IS_COMPOUND_BASED, RES_PARATR_ADJUST, cppu::UnoType::get(), PROPERTY_NONE, MID_COMPOUND_BASED},\ { UNO_NAME_PARA_LINE_NUMBER_COUNT, RES_LINENUMBER, cppu::UnoType::get(), PROPERTY_NONE ,MID_LINENUMBER_COUNT },\ { UNO_NAME_PARA_LINE_NUMBER_START_VALUE, RES_LINENUMBER, cppu::UnoType::get(), PROPERTY_NONE ,MID_LINENUMBER_STARTVALUE},\ { UNO_NAME_PARA_LINE_SPACING, RES_PARATR_LINESPACING, cppu::UnoType::get(),PROPERTY_NONE, CONVERT_TWIPS},\ diff --git a/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx b/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx index 54b80a085da0..352792ec3b75 100644 --- a/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx +++ b/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx @@ -267,6 +267,7 @@ static OUString PropertyNametoRID(const OUString& rName) { "ParaBottomMarginRelative", RID_PARA_BOTTOM_MARGIN_RELATIVE }, { "ParaChapterNumberingLevel", RID_PARA_CHAPTER_NUMBERING_LEVEL }, { "ParaComposer", RID_PARA_COMPOSER }, + { "ParaIsCompoundBased", RID_PARA_COMPOUND_BASED_HYPHENATION }, { "ParaConditionalStyleName", RID_PARA_CONDITIONAL_STYLE_NAME }, { "ParaContextMargin", RID_PARA_CONTEXT_MARGIN }, { "ParaExpandSingleWord", RID_PARA_EXPAND_SINGLE_WORD }, diff --git a/xmloff/inc/xmlprop.hxx b/xmloff/inc/xmlprop.hxx index bd46d1de9060..22b7c31a9d84 100644 --- a/xmloff/inc/xmlprop.hxx +++ b/xmloff/inc/xmlprop.hxx @@ -498,6 +498,7 @@ inline constexpr OUString PROP_ParaBackTransparent = u"ParaBackTransparent"_ustr inline constexpr OUString PROP_ParaBottomMargin = u"ParaBottomMargin"_ustr; inline constexpr OUString PROP_ParaBottomMarginRelative = u"ParaBottomMarginRelative"_ustr; inline constexpr OUString PROP_ParaComposer = u"ParaComposer"_ustr; +inline constexpr OUString PROP_ParaIsCompoundBased = u"ParaIsCompoundBased"_ustr; inline constexpr OUString PROP_ParaContextMargin = u"ParaContextMargin"_ustr; inline constexpr OUString PROP_ParaExpandSingleWord = u"ParaExpandSingleWord"_ustr; inline constexpr OUString PROP_ParaFirstLineIndent = u"ParaFirstLineIndent"_ustr; diff --git a/xmloff/source/core/xmltoken.cxx b/xmloff/source/core/xmltoken.cxx index b40ff54681e7..8e492b0fcbda 100644 --- a/xmloff/source/core/xmltoken.cxx +++ b/xmloff/source/core/xmltoken.cxx @@ -501,6 +501,7 @@ namespace xmloff::token { TOKEN( "command", XML_COMMAND ), TOKEN( "comment", XML_COMMENT ), TOKEN( "compose", XML_COMPOSE ), + TOKEN( "compound-based", XML_COMPOUND_BASED ), TOKEN( "cond-style-name", XML_COND_STYLE_NAME ), TOKEN( "condition", XML_CONDITION ), TOKEN( "condition-source", XML_CONDITION_SOURCE ), diff --git a/xmloff/source/text/txtprmap.cxx b/xmloff/source/text/txtprmap.cxx index ffc15c274a3b..7affff4b43db 100644 --- a/xmloff/source/text/txtprmap.cxx +++ b/xmloff/source/text/txtprmap.cxx @@ -500,6 +500,7 @@ XMLPropertyMapEntry constexpr aXMLParaPropMap[] = MAP_EXT( PROP_ParaScaleWidthMinimum, XML_NAMESPACE_LO_EXT, XML_TEXT_SCALE_MINIMUM, XML_TYPE_PROP_PARAGRAPH|XML_TYPE_PERCENT16, 0 ), MAP_EXT( PROP_ParaScaleWidthMaximum, XML_NAMESPACE_LO_EXT, XML_TEXT_SCALE_MAXIMUM, XML_TYPE_PROP_PARAGRAPH|XML_TYPE_PERCENT16, 0 ), MAP_EXT( PROP_ParaComposer, XML_NAMESPACE_LO_EXT, XML_PARAGRAPH_COMPOSER, XML_TYPE_PROP_PARAGRAPH|XML_TYPE_BOOL, 0 ), + MAP_EXT( PROP_ParaIsCompoundBased, XML_NAMESPACE_LO_EXT, XML_COMPOUND_BASED, XML_TYPE_PROP_PARAGRAPH|XML_TYPE_BOOL, 0 ), MT_ED( PROP_CharScriptHint, XML_NAMESPACE_STYLE, XML_SCRIPT_TYPE, XML_TYPE_TEXT_SCRIPT_TYPE|MID_FLAG_MERGE_PROPERTY, 0 ), diff --git a/xmloff/source/token/tokens.txt b/xmloff/source/token/tokens.txt index bc5dd85f814a..52810244381a 100644 --- a/xmloff/source/token/tokens.txt +++ b/xmloff/source/token/tokens.txt @@ -405,6 +405,7 @@ comma-separated command comment compose +compound-based cond-style-name condition condition-source