diff --git a/cui/source/inc/paragrph.hxx b/cui/source/inc/paragrph.hxx index ad4398495f3f..fdfa4f4a18e8 100644 --- a/cui/source/inc/paragrph.hxx +++ b/cui/source/inc/paragrph.hxx @@ -164,6 +164,9 @@ class SvxParaAlignTabPage : public SfxTabPage std::unique_ptr m_xWordSpacingMinimum; std::unique_ptr m_xWordSpacingMaximum; + /// paragraph composer + std::unique_ptr m_xParagraphComposer; + /// letter spacing std::unique_ptr m_xLabelLetterSpacing; std::unique_ptr m_xLetterSpacingMinimum; diff --git a/cui/source/tabpages/paragrph.cxx b/cui/source/tabpages/paragrph.cxx index 46b9a83c9690..1f3d058f254e 100644 --- a/cui/source/tabpages/paragrph.cxx +++ b/cui/source/tabpages/paragrph.cxx @@ -1295,6 +1295,7 @@ SvxParaAlignTabPage::SvxParaAlignTabPage(weld::Container* pPage, weld::DialogCon , m_xWordSpacing(m_xBuilder->weld_metric_spin_button(u"spin_WORD_SPACING"_ustr, FieldUnit::PERCENT)) , m_xWordSpacingMinimum(m_xBuilder->weld_metric_spin_button(u"spin_WORD_SPACING_MIN"_ustr, FieldUnit::PERCENT)) , m_xWordSpacingMaximum(m_xBuilder->weld_metric_spin_button(u"spin_WORD_SPACING_MAX"_ustr, FieldUnit::PERCENT)) + , m_xParagraphComposer(m_xBuilder->weld_check_button(u"checkParaComposer"_ustr)) , 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)) @@ -1405,6 +1406,7 @@ bool SvxParaAlignTabPage::FillItemSet( SfxItemSet* rOutSet ) m_xWordSpacing->get_value_changed_from_saved() || m_xWordSpacingMinimum->get_value_changed_from_saved() || m_xWordSpacingMaximum->get_value_changed_from_saved() || + m_xParagraphComposer->get_state_changed_from_saved() || m_xLetterSpacingMinimum->get_value_changed_from_saved() || m_xLetterSpacingMaximum->get_value_changed_from_saved() || m_xGlyphScalingMinimum->get_value_changed_from_saved() || @@ -1431,6 +1433,7 @@ bool SvxParaAlignTabPage::FillItemSet( SfxItemSet* rOutSet ) aAdj.SetPropWordSpacing( m_xWordSpacing->get_value(FieldUnit::PERCENT) ); aAdj.SetPropWordSpacingMinimum( m_xWordSpacingMinimum->get_value(FieldUnit::PERCENT) ); aAdj.SetPropWordSpacingMaximum( m_xWordSpacingMaximum->get_value(FieldUnit::PERCENT) ); + aAdj.SetParagraphComposer( m_xParagraphComposer->get_active() ); aAdj.SetPropLetterSpacingMinimum( m_xLetterSpacingMinimum->get_value(FieldUnit::PERCENT) ); aAdj.SetPropLetterSpacingMaximum( m_xLetterSpacingMaximum->get_value(FieldUnit::PERCENT) ); aAdj.SetPropScaleWidthMinimum( m_xGlyphScalingMinimum->get_value(FieldUnit::PERCENT) ); @@ -1532,6 +1535,8 @@ void SvxParaAlignTabPage::Reset( const SfxItemSet* rSet ) m_xWordSpacing->set_value(rAdj.GetPropWordSpacing(), FieldUnit::PERCENT); m_xWordSpacingMinimum->set_value(rAdj.GetPropWordSpacingMinimum(), FieldUnit::PERCENT); m_xWordSpacingMaximum->set_value(rAdj.GetPropWordSpacingMaximum(), FieldUnit::PERCENT); + m_xParagraphComposer->set_sensitive(true); + m_xParagraphComposer->set_active(rAdj.GetParagraphComposer()); m_xLabelLetterSpacing->set_sensitive(true); // TODO add LetterSpacing (CharKern) and LetterSpacingMinimum m_xLetterSpacingMaximum->set_sensitive(true); @@ -1553,6 +1558,8 @@ void SvxParaAlignTabPage::Reset( const SfxItemSet* rSet ) m_xWordSpacing->set_sensitive(false); 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); @@ -1654,6 +1661,7 @@ void SvxParaAlignTabPage::Reset( const SfxItemSet* rSet ) m_xWordSpacing->save_value(); m_xWordSpacingMinimum->save_value(); m_xWordSpacingMaximum->save_value(); + m_xParagraphComposer->save_state(); m_xLetterSpacingMinimum->save_value(); m_xLetterSpacingMaximum->save_value(); m_xGlyphScalingMinimum->save_value(); @@ -1679,6 +1687,7 @@ void SvxParaAlignTabPage::ChangesApplied() m_xWordSpacing->save_value(); m_xWordSpacingMinimum->save_value(); m_xWordSpacingMaximum->save_value(); + m_xParagraphComposer->save_state(); m_xLetterSpacingMinimum->save_value(); m_xLetterSpacingMaximum->save_value(); m_xGlyphScalingMinimum->save_value(); @@ -1697,7 +1706,8 @@ IMPL_LINK_NOARG(SvxParaAlignTabPage, AlignHdl_Impl, weld::Toggleable&, void) m_xWordSpacing->set_sensitive(bJustify); m_xWordSpacingMinimum->set_sensitive(bJustify); m_xWordSpacingMaximum->set_sensitive(bJustify); - m_xLabelLetterSpacing->set_sensitive(bJustify); + m_xWordSpacingMaximum->set_sensitive(bJustify); + m_xParagraphComposer->set_sensitive(bJustify); // TODO visualize CharKerning with percentage m_xLetterSpacingMinimum->set_sensitive(bJustify); m_xLetterSpacingMaximum->set_sensitive(bJustify); diff --git a/cui/uiconfig/ui/paragalignpage.ui b/cui/uiconfig/ui/paragalignpage.ui index aa40705593e9..a84b064f9074 100644 --- a/cui/uiconfig/ui/paragalignpage.ui +++ b/cui/uiconfig/ui/paragalignpage.ui @@ -671,6 +671,26 @@ 1 + + + Paragraph Composer + True + True + False + True + True + True + + + Select this check box, if you want to get evenly spaced words in the consecutive lines. + + + + + 4 + 1 + + True diff --git a/editeng/source/items/paraitem.cxx b/editeng/source/items/paraitem.cxx index 9eda95057d76..527e590d441e 100644 --- a/editeng/source/items/paraitem.cxx +++ b/editeng/source/items/paraitem.cxx @@ -321,7 +321,8 @@ SvxAdjustItem::SvxAdjustItem(const SvxAdjust eAdjst, const sal_uInt16 nId ) nPropLetterSpacingMinimum(0), nPropLetterSpacingMaximum(0), nPropScaleWidthMinimum(100), - nPropScaleWidthMaximum(100) + nPropScaleWidthMaximum(100), + bParagraphComposer(false) { SetAdjust( eAdjst ); } @@ -338,6 +339,7 @@ bool SvxAdjustItem::operator==( const SfxPoolItem& rAttr ) const nPropWordSpacingMinimum == rItem.nPropWordSpacingMinimum && nPropWordSpacingMaximum == rItem.nPropWordSpacingMaximum && nPropWordSpacing == rItem.nPropWordSpacing && + bParagraphComposer == rItem.bParagraphComposer && nPropLetterSpacingMinimum == rItem.nPropLetterSpacingMinimum && nPropLetterSpacingMaximum == rItem.nPropLetterSpacingMaximum && nPropScaleWidthMinimum == rItem.nPropScaleWidthMinimum && @@ -354,6 +356,7 @@ size_t SvxAdjustItem::hashCode() const o3tl::hash_combine(seed, nPropWordSpacing); o3tl::hash_combine(seed, nPropWordSpacingMinimum); o3tl::hash_combine(seed, nPropWordSpacingMaximum); + o3tl::hash_combine(seed, bParagraphComposer); o3tl::hash_combine(seed, nPropLetterSpacingMinimum); o3tl::hash_combine(seed, nPropLetterSpacingMaximum); o3tl::hash_combine(seed, nPropScaleWidthMinimum); @@ -380,6 +383,11 @@ bool SvxAdjustItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const rVal <<= bOneBlock; break; } + case MID_PARAGRAPH_COMPOSER : + { + rVal <<= bParagraphComposer; + break; + } default: ;//prevent warning } return true; @@ -457,6 +465,12 @@ bool SvxAdjustItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId ) SetPropScaleWidthMaximum(nVal); } break; + case MID_PARAGRAPH_COMPOSER : + { + ASSERT_CHANGE_REFCOUNTED_ITEM; + bParagraphComposer = Any2Bool(rVal); + } + break; case MID_EXPAND_SINGLE : ASSERT_CHANGE_REFCOUNTED_ITEM; bOneBlock = Any2Bool(rVal); diff --git a/include/editeng/adjustitem.hxx b/include/editeng/adjustitem.hxx index 9a82e4334214..630058f259e4 100644 --- a/include/editeng/adjustitem.hxx +++ b/include/editeng/adjustitem.hxx @@ -60,6 +60,9 @@ class EDITENG_DLLPUBLIC SvxAdjustItem final : public SfxPoolItem sal_Int16 nPropScaleWidthMinimum; sal_Int16 nPropScaleWidthMaximum; + // paragraph composer + bool bParagraphComposer : 1; + protected: virtual ItemInstanceManager* getItemInstanceManager() const override; @@ -146,6 +149,17 @@ public: return eRet; } + bool GetParagraphComposer() const + { + return bParagraphComposer; + } + + void SetParagraphComposer( const bool bValue ) + { + ASSERT_CHANGE_REFCOUNTED_ITEM; + bParagraphComposer = bValue; + } + sal_uInt16 GetPropWordSpacing() const { return nPropWordSpacing; diff --git a/include/editeng/memberids.h b/include/editeng/memberids.h index 3fc273b9fc9b..19ea071076a7 100644 --- a/include/editeng/memberids.h +++ b/include/editeng/memberids.h @@ -77,6 +77,7 @@ #define MID_LETTER_SPACING_MAX 7 #define MID_SCALE_WIDTH_MIN 8 #define MID_SCALE_WIDTH_MAX 9 +#define MID_PARAGRAPH_COMPOSER 10 //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 b20ba26ec8c7..b1bc3d1f6667 100644 --- a/include/editeng/unotext.hxx +++ b/include/editeng/unotext.hxx @@ -172,6 +172,7 @@ struct SfxItemPropertyMapEntry; {u"ParaLetterSpacingMaximum"_ustr, EE_PARA_JUST, ::cppu::UnoType::get(), 0, MID_LETTER_SPACING_MAX }, \ {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"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/xmloff/xmltoken.hxx b/include/xmloff/xmltoken.hxx index 39f7a1ff1665..85ea12c0478c 100644 --- a/include/xmloff/xmltoken.hxx +++ b/include/xmloff/xmltoken.hxx @@ -1519,6 +1519,7 @@ namespace xmloff::token { XML_PAGES, XML_PAPER_TRAY_NUMBER, XML_PARAGRAPH, + XML_PARAGRAPH_COMPOSER, XML_PARAGRAPH_CONTENT, XML_PARAGRAPH_COUNT, XML_PARAGRAPH_END_MARGIN, diff --git a/offapi/com/sun/star/style/ParagraphProperties.idl b/offapi/com/sun/star/style/ParagraphProperties.idl index 6e391ee56096..bef8f8113357 100644 --- a/offapi/com/sun/star/style/ParagraphProperties.idl +++ b/offapi/com/sun/star/style/ParagraphProperties.idl @@ -616,6 +616,20 @@ published service ParagraphProperties @since LibreOffice 26.8 */ [optional, property] short ParaHyphenationCompoundMinTrailingChars; + + /** This value is `TRUE` if the paragraph composer is set, optimizing + the 1-line level greedy line breaking, now only by balancing word + spacing better in the consecutive lines. ParaWordSpacingMaximum + can limit this only if it is enabled, i.e. it is greater than + ParaWordSpacing. + + @see ParaWordSpacing + + @see ParaWordSpacingMaximum + + @since LibreOffice 26.8 + */ + [optional, property] boolean ParaComposer; }; diff --git a/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng b/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng index d89ccfe6c0c0..139947b98310 100644 --- a/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng +++ b/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng @@ -3220,6 +3220,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 c8f298648760..bb083d49b161 100644 --- a/svx/sdi/svxitems.sdi +++ b/svx/sdi/svxitems.sdi @@ -64,6 +64,7 @@ struct SvxAdjustStruct INT16 LetterSpacingMaximum MID_LETTER_SPACING_MAX; INT16 ScaleWidthMinimum MID_SCALE_WIDTH_MIN; INT16 ScaleWidthMaximum MID_SCALE_WIDTH_MAX; + BOOL ParagraphComposer MID_PARAGRAPH_COMPOSER; }; item SvxAdjustStruct SvxAdjustItem; diff --git a/sw/inc/inspectorproperties.hrc b/sw/inc/inspectorproperties.hrc index 8fc10dc5071b..906617efd7b3 100644 --- a/sw/inc/inspectorproperties.hrc +++ b/sw/inc/inspectorproperties.hrc @@ -198,6 +198,7 @@ #define RID_PARA_BOTTOM_MARGIN NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Bottom Margin") #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_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 8480bf391350..93155019afd2 100644 --- a/sw/inc/unoprnms.hxx +++ b/sw/inc/unoprnms.hxx @@ -515,6 +515,7 @@ inline constexpr OUString UNO_NAME_USER_DEFINED_ATTRIBUTES = u"UserDefinedAttrib inline constexpr OUString UNO_NAME_TEXT_USER_DEFINED_ATTRIBUTES = u"TextUserDefinedAttributes"_ustr; 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_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/tdf38159.fodt b/sw/qa/extras/layout/data/tdf38159.fodt new file mode 100644 index 000000000000..a72a44618dc6 --- /dev/null +++ b/sw/qa/extras/layout/data/tdf38159.fodt @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum consequat mi quis pretium semper. Proin luctus orci ac neque venenatis, quis commodo dolor posuere. Curabitur dignissim sapien quis cursus egestas. Donec blandit auctor arcu, nec pellentesque eros molestie eget. In consectetur aliquam hendrerit. Sed cursus mauris vitae ligula pellentesque, non pellentesque urna aliquet. Fusce placerat mauris enim, nec rutrum purus semper vel. Praesent tincidunt neque eu pellentesque pharetra. Fusce pellentesque est orci. + + + diff --git a/sw/qa/extras/layout/data/tdf38159_disabled.fodt b/sw/qa/extras/layout/data/tdf38159_disabled.fodt new file mode 100644 index 000000000000..b31bfecee75c --- /dev/null +++ b/sw/qa/extras/layout/data/tdf38159_disabled.fodt @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum consequat mi quis pretium semper. Proin luctus orci ac neque venenatis, quis commodo dolor posuere. Curabitur dignissim sapien quis cursus egestas. Donec blandit auctor arcu, nec pellentesque eros molestie eget. In consectetur aliquam hendrerit. Sed cursus mauris vitae ligula pellentesque, non pellentesque urna aliquet. Fusce placerat mauris enim, nec rutrum purus semper vel. Praesent tincidunt neque eu pellentesque pharetra. Fusce pellentesque est orci. + + + diff --git a/sw/qa/extras/layout/data/tdf38159_limited.fodt b/sw/qa/extras/layout/data/tdf38159_limited.fodt new file mode 100644 index 000000000000..669115d88324 --- /dev/null +++ b/sw/qa/extras/layout/data/tdf38159_limited.fodt @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum consequat mi quis pretium semper. Proin luctus orci ac neque venenatis, quis commodo dolor posuere. Curabitur dignissim sapien quis cursus egestas. Donec blandit auctor arcu, nec pellentesque eros molestie eget. In consectetur aliquam hendrerit. Sed cursus mauris vitae ligula pellentesque, non pellentesque urna aliquet. Fusce placerat mauris enim, nec rutrum purus semper vel. Praesent tincidunt neque eu pellentesque pharetra. Fusce pellentesque est orci. + + + diff --git a/sw/qa/extras/layout/layout3.cxx b/sw/qa/extras/layout/layout3.cxx index 48d3d4716bfe..13af5f1fa60c 100644 --- a/sw/qa/extras/layout/layout3.cxx +++ b/sw/qa/extras/layout/layout3.cxx @@ -882,6 +882,134 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf171161) } } +CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf38159) +{ + createSwDoc("tdf38159.fodt"); + // Ensure that all text portions are calculated before testing. + SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell(); + CPPUNIT_ASSERT(pViewShell); + pViewShell->Reformat(); + + xmlDocUniquePtr pXmlDoc = parseLayoutDump(); + + // Test paragraph composer without limiting maximum word spacing + // (Desired and Maximum word spacing are not different) + // + // BEFORE AFTER + // + // Lorem ipsum dolor sit -> Lorem ipsum dolor + // amet, consectetur sit amet, consectetur + // + assertXPath(pXmlDoc, "/root/page/body/txt/SwParaPortion/SwLineLayout[1]", "portion", + u"Lorem ipsum dolor "); + assertXPath(pXmlDoc, "/root/page/body/txt/SwParaPortion/SwLineLayout[2]", "portion", + u"sit amet, consectetur "); + + // BEFORE AFTER + // + // semper. Proin luctus orci ac -> semper. Proin luctus orci + // neque venenatis, quis ac neque venenatis, quis + // + assertXPath(pXmlDoc, "/root/page/body/txt/SwParaPortion/SwLineLayout[5]", "portion", + u"semper. Proin luctus orci "); + assertXPath(pXmlDoc, "/root/page/body/txt/SwParaPortion/SwLineLayout[6]", "portion", + u"ac neque venenatis, quis "); + + // BEFORE AFTER + // + // cursus mauris vitae ligula -> cursus mauris vitae + // pellentesque, non ligula pellentesque, non + assertXPath(pXmlDoc, "/root/page/body/txt/SwParaPortion/SwLineLayout[14]", "portion", + u"cursus mauris vitae "); + assertXPath(pXmlDoc, "/root/page/body/txt/SwParaPortion/SwLineLayout[15]", "portion", + u"ligula pellentesque, non "); +} + +CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf38159_disabled) +{ + createSwDoc("tdf38159_disabled.fodt"); + // Ensure that all text portions are calculated before testing. + SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell(); + CPPUNIT_ASSERT(pViewShell); + pViewShell->Reformat(); + + xmlDocUniquePtr pXmlDoc = parseLayoutDump(); + + // Test disabled paragraph composer + // + // DISABLED ENABLED + // + // Lorem ipsum dolor sit -> Lorem ipsum dolor + // amet, consectetur sit amet, consectetur + // + assertXPath(pXmlDoc, "/root/page/body/txt/SwParaPortion/SwLineLayout[1]", "portion", + u"Lorem ipsum dolor sit "); + assertXPath(pXmlDoc, "/root/page/body/txt/SwParaPortion/SwLineLayout[2]", "portion", + u"amet, consectetur "); + + // DISABLED ENABLED + // + // semper. Proin luctus orci ac -> semper. Proin luctus orci + // neque venenatis, quis ac neque venenatis, quis + // + assertXPath(pXmlDoc, "/root/page/body/txt/SwParaPortion/SwLineLayout[5]", "portion", + u"semper. Proin luctus orci ac "); + assertXPath(pXmlDoc, "/root/page/body/txt/SwParaPortion/SwLineLayout[6]", "portion", + u"neque venenatis, quis "); + + // DISABLED ENABLED + // + // cursus mauris vitae ligula -> cursus mauris vitae + // pellentesque, non ligula pellentesque, non + assertXPath(pXmlDoc, "/root/page/body/txt/SwParaPortion/SwLineLayout[14]", "portion", + u"cursus mauris vitae ligula "); + assertXPath(pXmlDoc, "/root/page/body/txt/SwParaPortion/SwLineLayout[15]", "portion", + u"pellentesque, non "); +} + +CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf38159_limited) +{ + createSwDoc("tdf38159_limited.fodt"); + // Ensure that all text portions are calculated before testing. + SwViewShell* pViewShell = getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell(); + CPPUNIT_ASSERT(pViewShell); + pViewShell->Reformat(); + + xmlDocUniquePtr pXmlDoc = parseLayoutDump(); + + // Test paragraph composer limited by maximum word spacing + // (Maximum word spacing is greater than the desired word spacing) + // + // LIMITED BY MAXIMUM WORD SPACING=300% + // + // Lorem ipsum dolor sit <- Lorem ipsum dolor + // amet, consectetur sit amet, consectetur + // + assertXPath(pXmlDoc, "/root/page/body/txt/SwParaPortion/SwLineLayout[1]", "portion", + u"Lorem ipsum dolor sit "); + assertXPath(pXmlDoc, "/root/page/body/txt/SwParaPortion/SwLineLayout[2]", "portion", + u"amet, consectetur "); + + // NOT LIMITED BY MAXIMUM WORD SPACING=300% + // + // semper. Proin luctus orci ac -> semper. Proin luctus orci + // neque venenatis, quis ac neque venenatis, quis + // + assertXPath(pXmlDoc, "/root/page/body/txt/SwParaPortion/SwLineLayout[5]", "portion", + u"semper. Proin luctus orci "); + assertXPath(pXmlDoc, "/root/page/body/txt/SwParaPortion/SwLineLayout[6]", "portion", + u"ac neque venenatis, quis "); + + // LIMITED BY MAXIMUM WORD SPACING=300% + // + // cursus mauris vitae ligula <- cursus mauris vitae + // pellentesque, non ligula pellentesque, non + assertXPath(pXmlDoc, "/root/page/body/txt/SwParaPortion/SwLineLayout[14]", "portion", + u"cursus mauris vitae ligula "); + assertXPath(pXmlDoc, "/root/page/body/txt/SwParaPortion/SwLineLayout[15]", "portion", + u"pellentesque, non "); +} + CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf169168_scaling) { createSwDoc("tdf169168_scaling.fodt"); diff --git a/sw/qa/uitest/styleInspector/styleInspector.py b/sw/qa/uitest/styleInspector/styleInspector.py index fec8f514abc0..b7f1edca088f 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(158, len(xListBox.getChild('0').getChild('0').getChildren())) + self.assertEqual(159, 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(158, len(xListBox.getChild('0').getChild('0').getChildren())) + self.assertEqual(159, 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(158, len(xListBox.getChild('0').getChild('0').getChildren())) + self.assertEqual(159, 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(158, len(xParStyle.getChild('0').getChildren())) + self.assertEqual(159, 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(23, len(xTitleStyle.getChildren())) + self.assertEqual(24, 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']) @@ -96,15 +96,16 @@ class styleNavigator(UITestCase): self.assertEqual("Char Weight Complex\tBold", get_state_as_dict(xTitleStyle.getChild('11'))['Text']) 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 Expand Single Word\tFalse", get_state_as_dict(xTitleStyle.getChild('14'))['Text']) - self.assertEqual("Para Last Line Adjust\t0", get_state_as_dict(xTitleStyle.getChild('15'))['Text']) - self.assertEqual("Para Letter Spacing Maximum\t0", get_state_as_dict(xTitleStyle.getChild('16'))['Text']) - self.assertEqual("Para Letter Spacing Minimum\t0", get_state_as_dict(xTitleStyle.getChild('17'))['Text']) - self.assertEqual("Para Scale Width Maximum\t100", get_state_as_dict(xTitleStyle.getChild('18'))['Text']) - self.assertEqual("Para Scale Width Minimum\t100", get_state_as_dict(xTitleStyle.getChild('19'))['Text']) - self.assertEqual("Para Word Spacing\t100", get_state_as_dict(xTitleStyle.getChild('20'))['Text']) - self.assertEqual("Para Word Spacing Maximum\t100", get_state_as_dict(xTitleStyle.getChild('21'))['Text']) - self.assertEqual("Para Word Spacing Minimum\t100", get_state_as_dict(xTitleStyle.getChild('22'))['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(0, len(xListBox.getChild('1').getChildren())) self.assertEqual(0, len(xListBox.getChild('2').getChildren())) @@ -116,7 +117,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(158, len(xParStyle.getChild('0').getChildren())) + self.assertEqual(159, 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())) @@ -151,7 +152,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(158, len(xListBox.getChild('0').getChild('0').getChildren())) + self.assertEqual(159, 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())) @@ -161,7 +162,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(158, len(xListBox.getChild('0').getChild('0').getChildren())) + self.assertEqual(159, len(xListBox.getChild('0').getChild('0').getChildren())) xParDirFormatting = xListBox.getChild('1') self.assertEqual(1, len(xParDirFormatting.getChildren())) @@ -214,7 +215,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(158, len(xListBox.getChild('0').getChild('0').getChildren())) + self.assertEqual(159, 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())) @@ -224,7 +225,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(158, len(xListBox.getChild('1').getChild('0').getChildren())) + self.assertEqual(159, len(xListBox.getChild('1').getChild('0').getChildren())) # Outer bookmark xBookmarkFormatting = xListBox.getChild('0') @@ -271,7 +272,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(158, len(xListBox.getChild('0').getChild('0').getChildren())) + self.assertEqual(159, 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 b499dd5bc5e1..4adafbd796cf 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(158, len(xListBox.getChild('0').getChild('0').getChildren())) + self.assertEqual(159, 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/inc/txtfrm.hxx b/sw/source/core/inc/txtfrm.hxx index b68ec8d00c1a..d59b3c029ff1 100644 --- a/sw/source/core/inc/txtfrm.hxx +++ b/sw/source/core/inc/txtfrm.hxx @@ -20,6 +20,7 @@ #define INCLUDED_SW_SOURCE_CORE_INC_TXTFRM_HXX #include +#include #include #include "cntfrm.hxx" #include "TextFrameIndex.hxx" @@ -267,6 +268,11 @@ class SW_DLLPUBLIC SwTextFrame final : public SwContentFrame // Format exactly one Line bool FormatLine( SwTextFormatter &rLine, const bool bPrev ); + // Apply optional paragraph composer for the line, which sets a OnceMore run + // to balance word spacing within this line and the previous one, if needed + void ParagraphComposer( SwTextFormatter &rLine, SwTextFormatInfo &rInf, + const SvxAdjustItem& rAdjust ); + // In order to safe stack space, we split this method: // Format_ calls Format_ with parameters void FormatImpl( vcl::RenderContext* pRenderContext, SwParaPortion *pPara, diff --git a/sw/source/core/text/frmform.cxx b/sw/source/core/text/frmform.cxx index 7afd83430384..d8f519676648 100644 --- a/sw/source/core/text/frmform.cxx +++ b/sw/source/core/text/frmform.cxx @@ -1594,6 +1594,95 @@ bool SwTextFrame::FormatLine( SwTextFormatter &rLine, const bool bPrev ) return 0 != pPara->GetDelta(); } +void SwTextFrame::ParagraphComposer( SwTextFormatter &rLine, SwTextFormatInfo &rInf, + const SvxAdjustItem& rAdjust ) +{ + const SwLineLayout *pCurr = rLine.GetCurr(); + const OUString & rString = GetText(); + + // remove paragraph composer setting after formatting the line + if ( pCurr->GetParaComposerBreak() > 0 ) + const_cast(pCurr)->SetParaComposerBreak(0); + + // Apply paragraph composer only if there are two or more spaces in the previous line. + // Do not apply it, if the previous line will be filled by the paragraph composer + // already (marked by the special value 1), or if its word spacing is greater or + // equal than the word spacing of the actual line. + // TODO handle single word in the line? + const SwLineLayout *pPrevLine = rLine.GetPrev(); + if ( pPrevLine && pPrevLine->GetSpaceCount() >= 2 && pPrevLine->GetParaComposerBreak() != 1 && + pPrevLine->GetFirstPortion()->ExtraSpaceSize() < + pCurr->GetFirstPortion()->ExtraSpaceSize() ) + { + sal_Int32 nWidthOf10Spaces = rInf.GetTextSize(" ").Width(); + // maximum allowed single space size after shifting the last word in the previous line + // (except if maximum word spacing is not different from the desired word spacing) + float fSpaceSizeMax = nWidthOf10Spaces / 10.0 * rAdjust.GetPropWordSpacingMaximum() / 100.0; + + // size of a single expanded space in the actual line + float fActualSingleSpaceSize = pCurr->GetFirstPortion()->ExtraSpaceSize() + fSpaceSizeMax; + + // size of all the available space in the previous line + float fPreviousAllSpaceSize = pPrevLine->GetSpaceCount() * + ( pPrevLine->GetFirstPortion()->ExtraSpaceSize() + fSpaceSizeMax ); + + // new break position in the previous line (space before the last word) + // TODO allow to break inside the last word + sal_Int32 nNewBreakPos = rString.lastIndexOf(CH_BLANK, sal_Int32(rLine.GetStart()) - 1); + if ( nNewBreakPos <= 0 ) + return; + + // calculate the size of the shifted word (plus a space if it the word is not hyphenated) + OUString sLastWord = rString.copy(nNewBreakPos + 1, + sal_Int32(rLine.GetStart()) - nNewBreakPos - 1); + sal_Int32 nLastWordSize = rInf.GetTextSize(sLastWord).Width(); + + // the word is hyphenated? + bool bHyphenated = rString[sal_Int32(rLine.GetStart()) - 1] != CH_BLANK; + + // all the new available space size in the previous line after shifting its last word + // (calculate with the size of the hyphen in the case of hyphenation, + // otherwise with the extra space of sLastWord) + float fNewPreviousAllSpaceSize = fPreviousAllSpaceSize + nLastWordSize + ( bHyphenated + ? rInf.GetTextSize("-").Width() + : -nWidthOf10Spaces/10.0 ); + + // new single space size in the previous line after shifting its last word, + // calculate also with the less spaces + float fNewPreviousSingleSpaceSize = fNewPreviousAllSpaceSize / + ( pPrevLine->GetSpaceCount() - 1 ); + + // the new space size in the previous line is still smaller, + // than in the actual line before its filling + if ( fNewPreviousSingleSpaceSize < fActualSingleSpaceSize && + // and spacing maximum is not limited (i.e. desired and maximum word spacing values + // are not different) or spacing maximum is not exceeded with the new line break + // in the previous line + ( rAdjust.GetPropWordSpacing() == rAdjust.GetPropWordSpacingMaximum() || + fNewPreviousSingleSpaceSize <= fSpaceSizeMax ) ) + { + // set new break in the previous line: SetParaComposerBreak() sets + // a value, which will decrease the line width to get a line break + // before the last word + float fSpaceSizeDesired = nWidthOf10Spaces/10.0 * rAdjust.GetPropWordSpacing()/100.0; + sal_Int32 nNewBreak = + // extra space over the desired word spacing + fPreviousAllSpaceSize - fSpaceSizeDesired * pPrevLine->GetSpaceCount() + + // plus the size of the last word minus a space size + // to avoid of breaking to much + nLastWordSize - nWidthOf10Spaces/10.0; + + const_cast(pPrevLine)->SetParaComposerBreak( nNewBreak ); + + // set special value for the actual line to avoid of filling from the filled line + const_cast(pCurr)->SetParaComposerBreak(1); + + // set new run for the paragraph lines to apply the new line break(s) + rLine.SetOnceMore(true); + } + } +} + void SwTextFrame::Format_( SwTextFormatter &rLine, SwTextFormatInfo &rInf, const bool bAdjust ) { @@ -1771,6 +1860,10 @@ void SwTextFrame::Format_( SwTextFormatter &rLine, SwTextFormatInfo &rInf, * the lines, because it could happen that one line can overflow * from the Follow to the Master. */ + + SvxAdjustItem aAdjustItem = GetTextNodeForParaProps()->GetSwAttrSet().GetAdjust(); + bool bParagraphComposer = aAdjustItem.GetParagraphComposer(); + do { if( bFirst ) @@ -1833,6 +1926,13 @@ void SwTextFrame::Format_( SwTextFormatter &rLine, SwTextFormatInfo &rInf, const bool bOldEndHyph = rLine.GetCurr()->IsEndHyph(); const bool bOldMidHyph = rLine.GetCurr()->IsMidHyph(); bFormat = FormatLine( rLine, bPrev ); + + // prepare OnceMore run with optimized line break, + // if it's possible to balance word spacing better + // in the actual and the previous lines + if ( bParagraphComposer ) + ParagraphComposer( rLine, rInf, aAdjustItem ); + // There can only be one bPrev ... (???) bPrev = false; if ( bMaxHyph ) diff --git a/sw/source/core/text/frmpaint.cxx b/sw/source/core/text/frmpaint.cxx index eaad242df740..5ba872f0bc27 100644 --- a/sw/source/core/text/frmpaint.cxx +++ b/sw/source/core/text/frmpaint.cxx @@ -409,7 +409,8 @@ void SwTextFrame::PaintExtraData( const SwRect &rRect ) const >= TextFrameIndex(aLine.GetTextFrame()->GetText().getLength()))) { SwTwips nExtraSpaceSize = aLine.GetCurr()->GetFirstPortion()->ExtraSpaceSize(); - if ( nExtraSpaceSize && bWordSpacingIndicator ) + // draw word spacing indicator for enlarged spaces, if it is enabled + if ( nExtraSpaceSize > 0 && bWordSpacingIndicator ) aExtra.PaintRedline( aLine.Y(), aLine.GetLineHeight(), nExtraSpaceSize ); bool bRed = bRedLine && aLine.GetCurr()->HasRedline(); diff --git a/sw/source/core/text/guess.cxx b/sw/source/core/text/guess.cxx index 3480d54d0ea6..30ae99df68ab 100644 --- a/sw/source/core/text/guess.cxx +++ b/sw/source/core/text/guess.cxx @@ -183,6 +183,14 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf, SwTwips nLineWidth = rInf.GetLineWidth(); TextFrameIndex nMaxLen = TextFrameIndex(rInf.GetText().getLength()) - rInf.GetIdx(); + // paragraph composer: break before the optimal line break + // to remove the huge word spacing in the next line + // Note: special value 1 means filled lines, i.e. + // the lines filled from the previous lines + SwLineLayout *pLay = rInf.GetRoot(); + if ( pLay && pLay->GetParaComposerBreak() > 1 ) + nLineWidth -= pLay->GetParaComposerBreak(); + SvxAdjustItem aAdjustItem = rInf.GetTextFrame()->GetTextNodeForParaProps()->GetSwAttrSet().GetAdjust(); const SvxAdjust aAdjust = aAdjustItem.GetAdjust(); // Maximum word spacing allows bigger spaces to limit hyphenation, diff --git a/sw/source/core/text/porlay.hxx b/sw/source/core/text/porlay.hxx index 76c14ede874a..dc82ac60df6b 100644 --- a/sw/source/core/text/porlay.hxx +++ b/sw/source/core/text/porlay.hxx @@ -90,6 +90,9 @@ private: SwTwips m_nLetterSpacing = 0; // custom letter spacing SwTwips m_nScaleWidth = 100; // glyph scaling (grain resolution to limit font generation) float m_fScaleWidthSpacing = 0.0; // extra space filled by glyph scaling + // OnceMore run of the paragraph composer: to minimize huge word spacing of the next line, + // adjust this new break (which was calculated during the first run) + sal_Int32 m_nParaComposerBreak = 0; // new break point to fill the next line better bool m_bFormatAdj : 1; bool m_bDummy : 1; @@ -196,6 +199,9 @@ public: void SetScaleWidthSpacing(SwTwips nNew) { m_fScaleWidthSpacing = nNew; } float GetScaleWidthSpacing() const { return m_fScaleWidthSpacing; } + void SetParaComposerBreak(sal_Int32 nNew) { m_nParaComposerBreak = nNew; } + sal_Int32 GetParaComposerBreak() const { return m_nParaComposerBreak; } + // Creates the glue chain for short lines SwMarginPortion *CalcLeftMargin(); diff --git a/sw/source/core/text/portxt.cxx b/sw/source/core/text/portxt.cxx index 04d425687ea0..ce12874a0a4a 100644 --- a/sw/source/core/text/portxt.cxx +++ b/sw/source/core/text/portxt.cxx @@ -405,9 +405,8 @@ void SwTextPortion::SetSpacing( SwTextFormatInfo &rInf, SwTextGuess const &rGues ( nStartWidth + nBreakWidth - nSpaces * nWidthOf10Spaces/10.0 ) ) / nSpaces; // the part to be removed: the previous width minus the maximum allowed space width - float fExpansionOverMax = - fSpaceNormal - nWidthOf10Spaces / 10.0 * aAdjustItem.GetPropWordSpacingMaximum() / 100.0; - ExtraSpaceSize( fExpansionOverMax > 0 ? fExpansionOverMax : 0 ); + ExtraSpaceSize( fSpaceNormal - + nWidthOf10Spaces / 10.0 * aAdjustItem.GetPropWordSpacingMaximum() / 100.0 ); sal_Int32 nLetterCount = sal_Int32(rGuess.BreakPos()) - sal_Int32(rInf.GetLineStart()) - nZeroWidthCharCount; @@ -550,7 +549,8 @@ bool SwTextPortion::Format_( SwTextFormatInfo &rInf ) aAdjustItem.GetPropScaleWidthMinimum() == 100 && aAdjustItem.GetPropScaleWidthMaximum() == 100 && aAdjustItem.GetPropLetterSpacingMinimum() == 0 && - aAdjustItem.GetPropLetterSpacingMaximum() == 0; + aAdjustItem.GetPropLetterSpacingMaximum() == 0 && + !aAdjustItem.GetParagraphComposer(); // support old ODT documents, where only JustifyLinesWithShrinking was set const bool bOldInterop = bInteropSmartJustify && bNoWordSpacing; const bool bWordSpacing = bFullJustified && (!bNoWordSpacing || bOldInterop); diff --git a/sw/source/core/unocore/unomapproperties.hxx b/sw/source/core/unocore/unomapproperties.hxx index 5e177eb1d29e..39b2878b7f18 100644 --- a/sw/source/core/unocore/unomapproperties.hxx +++ b/sw/source/core/unocore/unomapproperties.hxx @@ -194,6 +194,7 @@ { UNO_NAME_PARA_LETTER_SPACING_MAX, RES_PARATR_ADJUST, cppu::UnoType::get(), PropertyAttribute::MAYBEVOID, MID_LETTER_SPACING_MAX }, \ { 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_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 }, \ @@ -468,6 +469,7 @@ { UNO_NAME_PARA_LETTER_SPACING_MAX, RES_PARATR_ADJUST, cppu::UnoType::get(), PROPERTY_NONE, MID_LETTER_SPACING_MAX},\ { 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_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 d097c06c1965..8514296548ce 100644 --- a/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx +++ b/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx @@ -266,6 +266,7 @@ static OUString PropertyNametoRID(const OUString& rName) { "ParaBottomMargin", RID_PARA_BOTTOM_MARGIN }, { "ParaBottomMarginRelative", RID_PARA_BOTTOM_MARGIN_RELATIVE }, { "ParaChapterNumberingLevel", RID_PARA_CHAPTER_NUMBERING_LEVEL }, + { "ParaComposer", RID_PARA_COMPOSER }, { "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 54a8d02e104c..af82ce7f434e 100644 --- a/xmloff/inc/xmlprop.hxx +++ b/xmloff/inc/xmlprop.hxx @@ -489,6 +489,7 @@ inline constexpr OUString PROP_ParaBackGraphicLocation = u"ParaBackGraphicLocati 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_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 671628a01a72..95128a191501 100644 --- a/xmloff/source/core/xmltoken.cxx +++ b/xmloff/source/core/xmltoken.cxx @@ -1532,6 +1532,7 @@ namespace xmloff::token { TOKEN( "pages", XML_PAGES ), TOKEN( "paper-tray-number", XML_PAPER_TRAY_NUMBER ), TOKEN( "paragraph", XML_PARAGRAPH ), + TOKEN( "paragraph-composer", XML_PARAGRAPH_COMPOSER ), TOKEN( "paragraph-content", XML_PARAGRAPH_CONTENT ), TOKEN( "paragraph-count", XML_PARAGRAPH_COUNT ), TOKEN( "paragraph-end-margin", XML_PARAGRAPH_END_MARGIN ), diff --git a/xmloff/source/text/txtprmap.cxx b/xmloff/source/text/txtprmap.cxx index 17dbf0557a0e..751b22971af6 100644 --- a/xmloff/source/text/txtprmap.cxx +++ b/xmloff/source/text/txtprmap.cxx @@ -495,6 +495,7 @@ XMLPropertyMapEntry constexpr aXMLParaPropMap[] = MAP_EXT( PROP_ParaLetterSpacingMaximum, XML_NAMESPACE_LO_EXT, XML_LETTER_SPACING_MAXIMUM, XML_TYPE_PROP_PARAGRAPH|XML_TYPE_PERCENT16, 0 ), 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 ), 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 33de01cf350c..45c1b5391aa2 100644 --- a/xmloff/source/token/tokens.txt +++ b/xmloff/source/token/tokens.txt @@ -1432,6 +1432,7 @@ page-width pages paper-tray-number paragraph +paragraph-composer paragraph-content paragraph-count paragraph-end-margin