tdf#38159 cui sw offapi xmloff: add paragraph composer

Balance word spacing within the consecutive lines
to get uniform grayness and less rivers for better
readability, enabled by the new paragraph property

com::sun::star::text::ParaComposer

and the following developments:

* Add Paragraph Composer checkbox on Alignment pane of the
  paragraph formatting dialog window (not default yet);

* Implement paragraph composer: big word spacing of a
  line is decreased by shifting the last word from the
  previous line, only if the word spacing is better, than
  before, and the increased word spacing in the previous
  line doesn't exceed the Maximum word spacing (limited
  paragraph composer);

* When Maximum word spacing is not greater than the Desired
  word spacing, shifting of the last word is not limited
  by the Maximum word spacing (unlimited paragraph composer)

* Add loext:paragraph-composer attribute, as LibreOffice
  ODF extension;

* Add ODF unit tests for the limited and not limited
  paragraph composer.

Note: "Smart justify" line breaking algorithm chooses
space shrinking (limited by Minimum word spacing)
only if shifting the last word isn't better based
on the desired word spacing. This was still a greedy
line breaking algorithm, resulting big word spacing
in the lines before a longer word.

The paragraph composer shifts the last word (or the
first part of the hyphenated last word) from the previous
line to decrease big word spacing in the actual line.

Note: balancing very big word spacing in narrow consecutive
lines can result vertical rivers. To avoid this, use limited
paragraph composer by setting maximum word spacing.

Follow-up to "smart justify", word spacing, letter spacing
and glyph scaling developments. Selected commits:
commit 529755f091 "tdf#166113
sw smart justify: adjust algorithm for interoperability",
commit 7d08767b89 "tdf#130088
"tdf#119908 smart justify: fix DOCX line count + compat opt.",
commit 7d6696757d "tdf#159923
sw cui offapi xmloff: add custom word spacing",
commit 5a48070f59 "tdf#126154
sw offapi xmloff cui: add min/max word spacing",
commit f83a04c510 "tdf#167648
cui offapi xmloff sw: add DTP-feature maximum letter spacing",
commit 3c53797210 "tdf#167648
sw letter spacing: implement minimum letter spacing",
commit 45ec7bd761 "tdf#168251
cui offapi xmloff sw glyph scaling: extend UNO/UX/ODF".

Change-Id: I727caad70536c86be524bc9d9b52bc71208c202a
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/203288
Reviewed-by: László Németh <nemeth@numbertext.org>
Tested-by: Jenkins
This commit is contained in:
László Németh
2026-04-09 14:42:07 +02:00
parent e41f1a246f
commit 62aecba14c
31 changed files with 453 additions and 28 deletions
+3
View File
@@ -164,6 +164,9 @@ class SvxParaAlignTabPage : public SfxTabPage
std::unique_ptr<weld::MetricSpinButton> m_xWordSpacingMinimum;
std::unique_ptr<weld::MetricSpinButton> m_xWordSpacingMaximum;
/// paragraph composer
std::unique_ptr<weld::CheckButton> m_xParagraphComposer;
/// letter spacing
std::unique_ptr<weld::Label> m_xLabelLetterSpacing;
std::unique_ptr<weld::MetricSpinButton> m_xLetterSpacingMinimum;
+11 -1
View File
@@ -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);
+20
View File
@@ -671,6 +671,26 @@
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="checkParaComposer">
<property name="label" translatable="yes" context="paralignpage|checkParaComposer">Paragraph Composer</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="use-underline">True</property>
<property name="inconsistent">True</property>
<property name="draw-indicator">True</property>
<child internal-child="accessible">
<object class="AtkObject" id="checkParaComposer-atkobject">
<property name="AtkObject::accessible-description" translatable="yes" context="textflowpage|extended_tip|checkParaComposer">Select this check box, if you want to get evenly spaced words in the consecutive lines.</property>
</object>
</child>
</object>
<packing>
<property name="left-attach">4</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="labelLetterSpacing">
<property name="visible">True</property>
+15 -1
View File
@@ -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);
+14
View File
@@ -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;
+1
View File
@@ -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
+1
View File
@@ -172,6 +172,7 @@ struct SfxItemPropertyMapEntry;
{u"ParaLetterSpacingMaximum"_ustr, EE_PARA_JUST, ::cppu::UnoType<sal_Int16>::get(), 0, MID_LETTER_SPACING_MAX }, \
{u"ParaScaleWidthMinimum"_ustr, EE_PARA_JUST, ::cppu::UnoType<sal_Int16>::get(), 0, MID_SCALE_WIDTH_MIN }, \
{u"ParaScaleWidthMaximum"_ustr, EE_PARA_JUST, ::cppu::UnoType<sal_Int16>::get(), 0, MID_SCALE_WIDTH_MAX }, \
{u"ParaComposer"_ustr, EE_PARA_JUST, ::cppu::UnoType<bool>::get(), 0, MID_PARAGRAPH_COMPOSER }, \
{u"WritingMode"_ustr, EE_PARA_WRITINGDIR, ::cppu::UnoType<sal_Int16>::get(), 0, 0 }, \
{u"WritingModeAutomatic"_ustr, EE_PARA_AUTOWRITINGDIR, ::cppu::UnoType<bool>::get(), 0, 0 }
+1
View File
@@ -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,
@@ -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;
};
@@ -3220,6 +3220,15 @@ xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.
</rng:optional>
</rng:define>
<!-- TODO no proposal -->
<rng:define name="style-paragraph-properties-attlist" combine="interleave">
<rng:optional>
<rng:attribute name="loext:paragraph-composer">
<rng:ref name="boolean"/>
</rng:attribute>
</rng:optional>
</rng:define>
<!-- TODO no proposal -->
<rng:define name="style-paragraph-properties-attlist" combine="interleave">
<rng:optional>
+1
View File
@@ -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;
+1
View File
@@ -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")
+1
View File
@@ -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;
+26
View File
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" office:mimetype="application/vnd.oasis.opendocument.text" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0">
<office:styles>
<style:default-style style:family="paragraph">
<style:text-properties style:font-name="Liberation Serif" fo:font-size="12pt" fo:language="en" fo:country="US" style:letter-kerning="true"/>
</style:default-style>
<style:style style:name="Standard" style:family="paragraph" style:class="text"/>
</office:styles>
<office:automatic-styles>
<style:style style:name="P1" style:family="paragraph" style:parent-style-name="Standard">
<style:paragraph-properties fo:margin-left="0pt" fo:margin-right="348.01pt" fo:text-align="justify" style:justify-single-word="false" fo:text-indent="0pt" style:auto-text-indent="false" loext:paragraph-composer="true"/>
</style:style>
<style:page-layout style:name="pm1">
<style:page-layout-properties fo:page-width="595.3pt" fo:page-height="841.89pt" style:print-orientation="portrait" fo:margin-top="56.69pt" fo:margin-bottom="56.69pt" fo:margin-left="56.69pt" fo:margin-right="56.69pt">
</style:page-layout-properties>
</style:page-layout>
</office:automatic-styles>
<office:master-styles>
<style:master-page style:name="Standard" style:page-layout-name="pm1"/>
</office:master-styles>
<office:body>
<office:text>
<text:p text:style-name="P1">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.</text:p>
</office:text>
</office:body>
</office:document>
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" office:mimetype="application/vnd.oasis.opendocument.text" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0">
<office:styles>
<style:default-style style:family="paragraph">
<style:text-properties style:font-name="Liberation Serif" fo:font-size="12pt" fo:language="en" fo:country="US" style:letter-kerning="true"/>
</style:default-style>
<style:style style:name="Standard" style:family="paragraph" style:class="text"/>
</office:styles>
<office:automatic-styles>
<style:style style:name="P1" style:family="paragraph" style:parent-style-name="Standard">
<style:paragraph-properties fo:margin-left="0pt" fo:margin-right="348.01pt" fo:text-align="justify" style:justify-single-word="false" fo:text-indent="0pt" style:auto-text-indent="false"/>
</style:style>
<style:page-layout style:name="pm1">
<style:page-layout-properties fo:page-width="595.3pt" fo:page-height="841.89pt" style:print-orientation="portrait" fo:margin-top="56.69pt" fo:margin-bottom="56.69pt" fo:margin-left="56.69pt" fo:margin-right="56.69pt">
</style:page-layout-properties>
</style:page-layout>
</office:automatic-styles>
<office:master-styles>
<style:master-page style:name="Standard" style:page-layout-name="pm1"/>
</office:master-styles>
<office:body>
<office:text>
<text:p text:style-name="P1">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.</text:p>
</office:text>
</office:body>
</office:document>
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" office:mimetype="application/vnd.oasis.opendocument.text" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0">
<office:styles>
<style:default-style style:family="paragraph">
<style:text-properties style:font-name="Liberation Serif" fo:font-size="12pt" fo:language="en" fo:country="US" style:letter-kerning="true"/>
</style:default-style>
<style:style style:name="Standard" style:family="paragraph" style:class="text"/>
</office:styles>
<office:automatic-styles>
<style:style style:name="P1" style:family="paragraph" style:parent-style-name="Standard">
<style:paragraph-properties fo:margin-left="0pt" fo:margin-right="348.01pt" fo:text-align="justify" style:justify-single-word="false" fo:text-indent="0pt" style:auto-text-indent="false" loext:paragraph-composer="true" loext:word-spacing-maximum="300%"/>
</style:style>
<style:page-layout style:name="pm1">
<style:page-layout-properties fo:page-width="595.3pt" fo:page-height="841.89pt" style:print-orientation="portrait" fo:margin-top="56.69pt" fo:margin-bottom="56.69pt" fo:margin-left="56.69pt" fo:margin-right="56.69pt">
</style:page-layout-properties>
</style:page-layout>
</office:automatic-styles>
<office:master-styles>
<style:master-page style:name="Standard" style:page-layout-name="pm1"/>
</office:master-styles>
<office:body>
<office:text>
<text:p text:style-name="P1">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.</text:p>
</office:text>
</office:body>
</office:document>
+128
View File
@@ -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");
+21 -20
View File
@@ -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()))
+1 -1
View File
@@ -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()))
+6
View File
@@ -20,6 +20,7 @@
#define INCLUDED_SW_SOURCE_CORE_INC_TXTFRM_HXX
#include <com/sun/star/uno/Sequence.hxx>
#include <editeng/adjustitem.hxx>
#include <svl/ctloptions.hxx>
#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,
+100
View File
@@ -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<SwLineLayout*>(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<SwLineLayout*>(pPrevLine)->SetParaComposerBreak( nNewBreak );
// set special value for the actual line to avoid of filling from the filled line
const_cast<SwLineLayout*>(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 )
+2 -1
View File
@@ -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();
+8
View File
@@ -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,
+6
View File
@@ -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();
+4 -4
View File
@@ -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);
@@ -194,6 +194,7 @@
{ UNO_NAME_PARA_LETTER_SPACING_MAX, RES_PARATR_ADJUST, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_LETTER_SPACING_MAX }, \
{ UNO_NAME_PARA_SCALE_WIDTH_MIN, RES_PARATR_ADJUST, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_SCALE_WIDTH_MIN }, \
{ UNO_NAME_PARA_SCALE_WIDTH_MAX, RES_PARATR_ADJUST, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_SCALE_WIDTH_MAX }, \
{ UNO_NAME_PARA_COMPOSER, RES_PARATR_ADJUST, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_PARAGRAPH_COMPOSER }, \
{ UNO_NAME_PARA_LINE_NUMBER_COUNT, RES_LINENUMBER, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_LINENUMBER_COUNT }, \
{ UNO_NAME_PARA_LINE_NUMBER_START_VALUE, RES_LINENUMBER, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_LINENUMBER_STARTVALUE }, \
{ UNO_NAME_PARA_LINE_SPACING, RES_PARATR_LINESPACING, cppu::UnoType<css::style::LineSpacing>::get(), PropertyAttribute::MAYBEVOID, CONVERT_TWIPS }, \
@@ -468,6 +469,7 @@
{ UNO_NAME_PARA_LETTER_SPACING_MAX, RES_PARATR_ADJUST, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_LETTER_SPACING_MAX},\
{ UNO_NAME_PARA_SCALE_WIDTH_MIN, RES_PARATR_ADJUST, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_SCALE_WIDTH_MIN},\
{ UNO_NAME_PARA_SCALE_WIDTH_MAX, RES_PARATR_ADJUST, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_SCALE_WIDTH_MAX},\
{ UNO_NAME_PARA_COMPOSER, RES_PARATR_ADJUST, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_PARAGRAPH_COMPOSER},\
{ UNO_NAME_PARA_LINE_NUMBER_COUNT, RES_LINENUMBER, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_LINENUMBER_COUNT },\
{ UNO_NAME_PARA_LINE_NUMBER_START_VALUE, RES_LINENUMBER, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_LINENUMBER_STARTVALUE},\
{ UNO_NAME_PARA_LINE_SPACING, RES_PARATR_LINESPACING, cppu::UnoType<css::style::LineSpacing>::get(),PROPERTY_NONE, CONVERT_TWIPS},\
@@ -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 },
+1
View File
@@ -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;
+1
View File
@@ -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 ),
+1
View File
@@ -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 ),
+1
View File
@@ -1432,6 +1432,7 @@ page-width
pages
paper-tray-number
paragraph
paragraph-composer
paragraph-content
paragraph-count
paragraph-end-margin