tdf#168251 cui offapi xmloff sw glyph scaling: extend UNO/UX/ODF

Add new paragraph justification options "Minimum glyph scaling"
and "Maximum glyph scaling" to improve typography, like DTP software
do, allowing to typeset visually better paragraphs, especially narrow
columns without unacceptably big word spacing.

* Add spin boxes to Alignment in paragraph formatting dialog window;

* Store properties in paragraph model:

  css::style::ParagraphProperties::ParaPropScaleWidthMinimum

  css::style::ParagraphProperties::ParaPropScaleWidthMaximum

  Note: desired glyph scaling is already supported by ODF/UNO,
  see css::style::CharacterProperties::CharScaleWidth.

* Implement visual layout of minimum and maximum glyph scaling,
  limited by the available extra word spacing and letter spacing,
  too.

  Note: last line can exceed the paragraph width, because minimum
  glyph scaling, also minimum letter scaling of the last line
  haven't been applied there, yet.

* Add ODF import/export (loext:text-scale-maximum and
  loext:text-scale-minimum, where style:text-scale is for the
  desired text scaling).

* Add ODF unit test.

Note: hyphenated lines, lines with multiple portions haven't
been using custom glyph scaling, yet.

Note: resolution of glyph scaling is 1 percent, which will
avoid of overgeneration of font types using variable fonts.

Change-Id: I9b4576dde4303437b64e37202c47f888f94c3f67
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/190534
Tested-by: Jenkins
Reviewed-by: László Németh <nemeth@numbertext.org>
This commit is contained in:
László Németh
2025-09-03 15:22:37 +02:00
parent 3c53797210
commit 45ec7bd761
31 changed files with 448 additions and 23 deletions
+5
View File
@@ -164,6 +164,11 @@ class SvxParaAlignTabPage : public SfxTabPage
std::unique_ptr<weld::MetricSpinButton> m_xLetterSpacingMinimum;
std::unique_ptr<weld::MetricSpinButton> m_xLetterSpacingMaximum;
/// glyph scaling
std::unique_ptr<weld::Label> m_xLabelGlyphScaling;
std::unique_ptr<weld::MetricSpinButton> m_xGlyphScalingMinimum;
std::unique_ptr<weld::MetricSpinButton> m_xGlyphScalingMaximum;
DECL_LINK(AlignHdl_Impl, weld::Toggleable&, void);
DECL_LINK(LastLineHdl_Impl, weld::ComboBox&, void);
DECL_LINK(TextDirectionHdl_Impl, weld::ComboBox&, void);
+27 -1
View File
@@ -1290,6 +1290,9 @@ SvxParaAlignTabPage::SvxParaAlignTabPage(weld::Container* pPage, weld::DialogCon
, m_xLabelLetterSpacing(m_xBuilder->weld_label(u"labelLetterSpacing"_ustr))
, m_xLetterSpacingMinimum(m_xBuilder->weld_metric_spin_button(u"spin_LETTER_SPACING_MIN"_ustr, FieldUnit::PERCENT))
, m_xLetterSpacingMaximum(m_xBuilder->weld_metric_spin_button(u"spin_LETTER_SPACING_MAX"_ustr, FieldUnit::PERCENT))
, m_xLabelGlyphScaling(m_xBuilder->weld_label(u"labelGlyphScaling"_ustr))
, m_xGlyphScalingMinimum(m_xBuilder->weld_metric_spin_button(u"spin_GLYPH_SCALING_MIN"_ustr, FieldUnit::PERCENT))
, m_xGlyphScalingMaximum(m_xBuilder->weld_metric_spin_button(u"spin_GLYPH_SCALING_MAX"_ustr, FieldUnit::PERCENT))
{
SetExchangeSupport();
@@ -1383,7 +1386,9 @@ bool SvxParaAlignTabPage::FillItemSet( SfxItemSet* rOutSet )
m_xWordSpacingMinimum->get_value_changed_from_saved() ||
m_xWordSpacingMaximum->get_value_changed_from_saved() ||
m_xLetterSpacingMinimum->get_value_changed_from_saved() ||
m_xLetterSpacingMaximum->get_value_changed_from_saved();
m_xLetterSpacingMaximum->get_value_changed_from_saved() ||
m_xGlyphScalingMinimum->get_value_changed_from_saved() ||
m_xGlyphScalingMaximum->get_value_changed_from_saved();
}
sal_uInt16 _nWhich = GetWhich( SID_ATTR_PARA_ADJUST );
@@ -1408,6 +1413,8 @@ bool SvxParaAlignTabPage::FillItemSet( SfxItemSet* rOutSet )
aAdj.SetPropWordSpacingMaximum( m_xWordSpacingMaximum->get_value(FieldUnit::PERCENT) );
aAdj.SetPropLetterSpacingMinimum( m_xLetterSpacingMinimum->get_value(FieldUnit::PERCENT) );
aAdj.SetPropLetterSpacingMaximum( m_xLetterSpacingMaximum->get_value(FieldUnit::PERCENT) );
aAdj.SetPropScaleWidthMinimum( m_xGlyphScalingMinimum->get_value(FieldUnit::PERCENT) );
aAdj.SetPropScaleWidthMaximum( m_xGlyphScalingMaximum->get_value(FieldUnit::PERCENT) );
rOutSet->Put( aAdj );
bModified = true;
}
@@ -1500,6 +1507,11 @@ void SvxParaAlignTabPage::Reset( const SfxItemSet* rSet )
m_xLetterSpacingMinimum->set_sensitive(true);
m_xLetterSpacingMinimum->set_value(rAdj.GetPropLetterSpacingMinimum(), FieldUnit::PERCENT);
m_xLetterSpacingMaximum->set_value(rAdj.GetPropLetterSpacingMaximum(), FieldUnit::PERCENT);
// TODO add GlyphScaling (CharScaleWidth)
m_xGlyphScalingMaximum->set_sensitive(true);
m_xGlyphScalingMinimum->set_sensitive(true);
m_xGlyphScalingMinimum->set_value(rAdj.GetPropScaleWidthMinimum(), FieldUnit::PERCENT);
m_xGlyphScalingMaximum->set_value(rAdj.GetPropScaleWidthMaximum(), FieldUnit::PERCENT);
}
else
{
@@ -1513,6 +1525,9 @@ void SvxParaAlignTabPage::Reset( const SfxItemSet* rSet )
m_xLabelLetterSpacing->set_sensitive(false);
m_xLetterSpacingMinimum->set_sensitive(false);
m_xLetterSpacingMaximum->set_sensitive(false);
m_xLabelGlyphScaling->set_sensitive(false);
m_xGlyphScalingMinimum->set_sensitive(false);
m_xGlyphScalingMaximum->set_sensitive(false);
}
}
else
@@ -1531,6 +1546,9 @@ void SvxParaAlignTabPage::Reset( const SfxItemSet* rSet )
m_xLabelLetterSpacing->set_sensitive(false);
m_xLetterSpacingMinimum->set_sensitive(false);
m_xLetterSpacingMaximum->set_sensitive(false);
m_xLabelGlyphScaling->set_sensitive(false);
m_xGlyphScalingMinimum->set_sensitive(false);
m_xGlyphScalingMaximum->set_sensitive(false);
}
m_xLastLineLB->set_active(nLBSelect);
@@ -1593,6 +1611,8 @@ void SvxParaAlignTabPage::Reset( const SfxItemSet* rSet )
m_xWordSpacingMaximum->save_value();
m_xLetterSpacingMinimum->save_value();
m_xLetterSpacingMaximum->save_value();
m_xGlyphScalingMinimum->save_value();
m_xGlyphScalingMaximum->save_value();
UpdateExample_Impl();
}
@@ -1613,6 +1633,8 @@ void SvxParaAlignTabPage::ChangesApplied()
m_xWordSpacingMaximum->save_value();
m_xLetterSpacingMinimum->save_value();
m_xLetterSpacingMaximum->save_value();
m_xGlyphScalingMinimum->save_value();
m_xGlyphScalingMaximum->save_value();
}
IMPL_LINK_NOARG(SvxParaAlignTabPage, AlignHdl_Impl, weld::Toggleable&, void)
@@ -1631,6 +1653,10 @@ IMPL_LINK_NOARG(SvxParaAlignTabPage, AlignHdl_Impl, weld::Toggleable&, void)
// TODO visualize CharKerning with percentage
m_xLetterSpacingMinimum->set_sensitive(bJustify);
m_xLetterSpacingMaximum->set_sensitive(bJustify);
m_xLabelGlyphScaling->set_sensitive(bJustify);
// TODO visualize CharScaleWidth with percentage
m_xGlyphScalingMinimum->set_sensitive(bJustify);
m_xGlyphScalingMaximum->set_sensitive(bJustify);
bool bLastLineIsBlock = m_xLastLineLB->get_active() == 2;
m_xExpandCB->set_sensitive(bJustify && bLastLineIsBlock);
+64
View File
@@ -34,6 +34,20 @@
<property name="step-increment">1</property>
<property name="page-increment">10</property>
</object>
<object class="GtkAdjustment" id="adjustmentPercent6">
<property name="upper">100</property>
<property name="lower">50</property>
<property name="value">100</property>
<property name="step-increment">1</property>
<property name="page-increment">10</property>
</object>
<object class="GtkAdjustment" id="adjustmentPercent7">
<property name="upper">200</property>
<property name="lower">100</property>
<property name="value">100</property>
<property name="step-increment">1</property>
<property name="page-increment">10</property>
</object>
<!-- n-columns=2 n-rows=1 -->
<object class="GtkGrid" id="ParaAlignPage">
<property name="visible">True</property>
@@ -594,6 +608,56 @@
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="labelGlyphScaling">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes" context="paragalignpage|labelGlyphScaling">_Glyph scaling:</property>
<property name="use-underline">True</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">3</property>
</packing>
</child>
<child>
<object class="GtkSpinButton" id="spin_GLYPH_SCALING_MIN">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="activates-default">True</property>
<property name="truncate-multiline">True</property>
<property name="adjustment">adjustmentPercent6</property>
<child internal-child="accessible">
<object class="AtkObject" id="spin_GLYPH_SCALING_MIN-atkobject">
<property name="AtkObject::accessible-description" translatable="yes" context="paralignpage|extended_tip|GLYPH-SCALING-MIN">Adjusts the minimum glyph scaling. Enter a number between 50% (ultra-condensed) and 100% (regular).</property>
</object>
</child>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">3</property>
</packing>
</child>
<child>
<object class="GtkSpinButton" id="spin_GLYPH_SCALING_MAX">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="activates-default">True</property>
<property name="truncate-multiline">True</property>
<property name="adjustment">adjustmentPercent7</property>
<child internal-child="accessible">
<object class="AtkObject" id="spin_GLYPH_SCALING_MAX-atkobject">
<property name="AtkObject::accessible-description" translatable="yes" context="paralignpage|extended_tip|GLYPH-SCALING-MMAX">Adjusts the maximum glyph scaling. Enter a number between 100% (regular) and 200% (ultra-expanded).</property>
</object>
</child>
</object>
<packing>
<property name="left-attach">3</property>
<property name="top-attach">3</property>
</packing>
</child>
<child>
<placeholder/>
</child>
+24 -2
View File
@@ -334,7 +334,9 @@ SvxAdjustItem::SvxAdjustItem(const SvxAdjust eAdjst, const sal_uInt16 nId )
nPropWordSpacingMinimum(100),
nPropWordSpacingMaximum(100),
nPropLetterSpacingMinimum(0),
nPropLetterSpacingMaximum(0)
nPropLetterSpacingMaximum(0),
nPropScaleWidthMinimum(100),
nPropScaleWidthMaximum(100)
{
SetAdjust( eAdjst );
}
@@ -352,7 +354,9 @@ bool SvxAdjustItem::operator==( const SfxPoolItem& rAttr ) const
nPropWordSpacingMaximum == rItem.nPropWordSpacingMaximum &&
nPropWordSpacing == rItem.nPropWordSpacing &&
nPropLetterSpacingMinimum == rItem.nPropLetterSpacingMinimum &&
nPropLetterSpacingMaximum == rItem.nPropLetterSpacingMaximum;
nPropLetterSpacingMaximum == rItem.nPropLetterSpacingMaximum &&
nPropScaleWidthMinimum == rItem.nPropScaleWidthMinimum &&
nPropScaleWidthMaximum == rItem.nPropScaleWidthMaximum;
}
size_t SvxAdjustItem::hashCode() const
@@ -367,6 +371,8 @@ size_t SvxAdjustItem::hashCode() const
o3tl::hash_combine(seed, nPropWordSpacingMaximum);
o3tl::hash_combine(seed, nPropLetterSpacingMinimum);
o3tl::hash_combine(seed, nPropLetterSpacingMaximum);
o3tl::hash_combine(seed, nPropScaleWidthMinimum);
o3tl::hash_combine(seed, nPropScaleWidthMaximum);
return seed;
}
@@ -382,6 +388,8 @@ bool SvxAdjustItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
case MID_WORD_SPACING_MAX : rVal <<= static_cast<sal_Int16>(GetPropWordSpacingMaximum()); break;
case MID_LETTER_SPACING_MIN : rVal <<= GetPropLetterSpacingMinimum(); break;
case MID_LETTER_SPACING_MAX : rVal <<= GetPropLetterSpacingMaximum(); break;
case MID_SCALE_WIDTH_MIN : rVal <<= GetPropScaleWidthMinimum(); break;
case MID_SCALE_WIDTH_MAX : rVal <<= GetPropScaleWidthMaximum(); break;
case MID_EXPAND_SINGLE :
{
rVal <<= bOneBlock;
@@ -450,6 +458,20 @@ bool SvxAdjustItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
SetPropLetterSpacingMaximum(nVal);
}
break;
case MID_SCALE_WIDTH_MIN :
{
sal_Int16 nVal = -1;
rVal >>= nVal;
SetPropScaleWidthMinimum(nVal);
}
break;
case MID_SCALE_WIDTH_MAX :
{
sal_Int16 nVal = -1;
rVal >>= nVal;
SetPropScaleWidthMaximum(nVal);
}
break;
case MID_EXPAND_SINGLE :
ASSERT_CHANGE_REFCOUNTED_ITEM;
bOneBlock = Any2Bool(rVal);
+25
View File
@@ -55,6 +55,10 @@ class EDITENG_DLLPUBLIC SvxAdjustItem final : public SfxPoolItem
sal_Int16 nPropLetterSpacingMinimum;
sal_Int16 nPropLetterSpacingMaximum;
// minimum and maximum glyph scaling values in percent
sal_Int16 nPropScaleWidthMinimum;
sal_Int16 nPropScaleWidthMaximum;
protected:
virtual ItemInstanceManager* getItemInstanceManager() const override;
@@ -207,6 +211,27 @@ public:
{
nPropLetterSpacingMaximum = nVal;
}
sal_Int16 GetPropScaleWidthMaximum() const
{
return nPropScaleWidthMaximum;
}
void SetPropScaleWidthMaximum( sal_Int16 nVal )
{
nPropScaleWidthMaximum = nVal;
}
sal_Int16 GetPropScaleWidthMinimum() const
{
return nPropScaleWidthMinimum;
}
void SetPropScaleWidthMinimum( sal_Int16 nVal )
{
nPropScaleWidthMinimum = nVal;
}
};
#endif
+2
View File
@@ -74,6 +74,8 @@
#define MID_WORD_SPACING_MAX 5
#define MID_LETTER_SPACING_MIN 6
#define MID_LETTER_SPACING_MAX 7
#define MID_SCALE_WIDTH_MIN 8
#define MID_SCALE_WIDTH_MAX 9
//SvxFontItem
// Don't use 0 as it used for the whole struct
+2
View File
@@ -169,6 +169,8 @@ struct SfxItemPropertyMapEntry;
{u"ParaWordSpacingMaximum"_ustr, EE_PARA_JUST, ::cppu::UnoType<sal_Int16>::get(), 0, MID_WORD_SPACING_MAX }, \
{u"ParaLetterSpacingMinimum"_ustr, EE_PARA_JUST, ::cppu::UnoType<sal_Int16>::get(), 0, MID_LETTER_SPACING_MIN }, \
{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"WritingMode"_ustr, EE_PARA_WRITINGDIR, ::cppu::UnoType<sal_Int16>::get(), 0, 0 }
class SvxFieldData;
+2
View File
@@ -2021,6 +2021,8 @@ namespace xmloff::token {
XML_TEXT_ROTATION_ANGLE,
XML_TEXT_ROTATION_SCALE,
XML_TEXT_SCALE,
XML_TEXT_SCALE_MAXIMUM,
XML_TEXT_SCALE_MINIMUM,
XML_TEXT_SHADOW,
XML_TEXT_STYLE,
XML_TEXT_TRANSFORM,
@@ -580,6 +580,34 @@ published service ParagraphProperties
@since LibreOffice 26.3
*/
[optional, property] long ParaLetterSpacingMaximum;
/** specifies the minimum glyph scale width as percentage value relative
to the width of the glyph.
<p>It takes a percent value between [50, 100], where the original
width is denoted by 100.</p>
@see CharScaleWidth
@see ParaScaleWidthMaximum
@since LibreOffice 26.3
*/
[optional, property] long ParaScaleWidthMinimum;
/** specifies the maximum glyph scale width as percentage value relative
to the width of the glyph.
<p>It takes a percent value between [100, 200], where the original
width is denoted by 100.</p>
@see CharScaleWidth
@see ParaScaleWidthMininum
@since LibreOffice 26.3
*/
[optional, property] long ParaScaleWidthMaximum;
};
@@ -3220,6 +3220,30 @@ 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:text-scale-minimum">
<rng:choice>
<rng:ref name="length"/>
<rng:ref name="percent"/>
</rng:choice>
</rng:attribute>
</rng:optional>
</rng:define>
<!-- TODO no proposal -->
<rng:define name="style-paragraph-properties-attlist" combine="interleave">
<rng:optional>
<rng:attribute name="loext:text-scale-maximum">
<rng:choice>
<rng:ref name="length"/>
<rng:ref name="percent"/>
</rng:choice>
</rng:attribute>
</rng:optional>
</rng:define>
<!-- TODO no proposal -->
<rng:define name="style-text-properties-attlist" combine="interleave">
<rng:optional>
+2
View File
@@ -62,6 +62,8 @@ struct SvxAdjustStruct
INT16 WordSpacingMaximum MID_WORD_SPACING_MAX;
INT16 LetterSpacingMinimum MID_LETTER_SPACING_MIN;
INT16 LetterSpacingMaximum MID_LETTER_SPACING_MAX;
INT16 ScaleWidthMinimum MID_SCALE_WIDTH_MIN;
INT16 ScaleWidthMaximum MID_SCALE_WIDTH_MAX;
};
item SvxAdjustStruct SvxAdjustItem;
+2
View File
@@ -243,6 +243,8 @@
#define RID_PARA_SPLIT NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Split")
#define RID_PARA_STYLE_NAME NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Style Name")
#define RID_PARA_TAB_STOPS NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Tab Stops")
#define RID_PARA_SCALE_WIDTH_MIN NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Scale Width Minimum")
#define RID_PARA_SCALE_WIDTH_MAX NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Scale Width Maximum")
#define RID_PARA_TOP_MARGIN NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Top Margin")
#define RID_PARA_TOP_MARGIN_RELATIVE NC_("RID_ATTRIBUTE_NAMES_MAP", "Para Top Margin Relative")
#define RID_PARA_USER_DEFINED_ATTRIBUTES NC_("RID_ATTRIBUTE_NAMES_MAP", "Para User Defined Attributes")
+2
View File
@@ -208,6 +208,8 @@ inline constexpr OUString UNO_NAME_PARA_WORD_SPACING_MIN = u"ParaWordSpacingMini
inline constexpr OUString UNO_NAME_PARA_WORD_SPACING_MAX = u"ParaWordSpacingMaximum"_ustr;
inline constexpr OUString UNO_NAME_PARA_LETTER_SPACING_MIN = u"ParaLetterSpacingMinimum"_ustr;
inline constexpr OUString UNO_NAME_PARA_LETTER_SPACING_MAX = u"ParaLetterSpacingMaximum"_ustr;
inline constexpr OUString UNO_NAME_PARA_SCALE_WIDTH_MIN = u"ParaScaleWidthMinimum"_ustr;
inline constexpr OUString UNO_NAME_PARA_SCALE_WIDTH_MAX = u"ParaScaleWidthMaximum"_ustr;
inline constexpr OUString UNO_NAME_PARA_LINE_NUMBER_COUNT = u"ParaLineNumberCount"_ustr;
inline constexpr OUString UNO_NAME_PARA_LINE_NUMBER_START_VALUE = u"ParaLineNumberStartValue"_ustr;
inline constexpr OUString UNO_NAME_BACK_COLOR = u"BackColor"_ustr;
+56
View File
@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<office:document xmlns:css3t="http://www.w3.org/TR/css3-text/" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" 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:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" 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:tableooo="http://openoffice.org/2009/table" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:rpt="http://openoffice.org/2005/report" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:officeooo="http://openoffice.org/2009/office" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" office:version="1.4" office:mimetype="application/vnd.oasis.opendocument.text">
<office:font-face-decls>
<style:font-face style:name="Liberation Serif" svg:font-family="&apos;Liberation Serif&apos;" style:font-family-generic="modern" style:font-pitch="fixed"/>
</office:font-face-decls>
<office:styles>
<style:default-style style:family="graphic">
<style:graphic-properties svg:stroke-color="#3465a4" draw:fill-color="#729fcf" fo:wrap-option="no-wrap" draw:shadow-offset-x="0.3cm" draw:shadow-offset-y="0.3cm" draw:start-line-spacing-horizontal="0.283cm" draw:start-line-spacing-vertical="0.283cm" draw:end-line-spacing-horizontal="0.283cm" draw:end-line-spacing-vertical="0.283cm" style:writing-mode="lr-tb" style:flow-with-text="false"/>
<style:paragraph-properties style:text-autospace="ideograph-alpha" style:line-break="strict" loext:tab-stop-distance="0cm" style:writing-mode="lr-tb" style:font-independent-line-spacing="false">
<style:tab-stops/>
</style:paragraph-properties>
<style:text-properties style:use-window-font-color="true" loext:opacity="0%" style:font-name="Liberation Serif" fo:font-size="12pt" fo:language="en" fo:country="US" style:letter-kerning="true" style:font-name-asian="Noto Serif CJK SC" style:font-size-asian="10.5pt" style:language-asian="zh" style:country-asian="CN" style:font-name-complex="Lohit Devanagari1" style:font-size-complex="12pt" style:language-complex="hi" style:country-complex="IN"/>
</style:default-style>
<style:default-style style:family="paragraph">
<style:paragraph-properties fo:hyphenation-ladder-count="no-limit" fo:hyphenation-keep="auto" loext:hyphenation-keep-type="column" loext:hyphenation-keep-line="false" style:text-autospace="ideograph-alpha" style:punctuation-wrap="hanging" style:line-break="strict" style:tab-stop-distance="1.251cm" style:writing-mode="page"/>
<style:text-properties style:use-window-font-color="true" loext:opacity="0%" style:font-name="Liberation Serif" fo:font-size="12pt" fo:language="en" fo:country="US" style:font-name-asian="Noto Serif CJK SC" style:font-size-asian="10.5pt" style:language-asian="zh" style:country-asian="CN" style:font-name-complex="Lohit Devanagari1" style:font-size-complex="12pt" style:language-complex="hi" style:country-complex="IN" fo:hyphenate="false" fo:hyphenation-remain-char-count="2" fo:hyphenation-push-char-count="2" loext:hyphenation-no-caps="false" loext:hyphenation-no-last-word="false" loext:hyphenation-word-char-count="no-limit" loext:hyphenation-zone="no-limit"/>
</style:default-style>
<style:default-style style:family="table">
<style:table-properties table:border-model="collapsing"/>
</style:default-style>
<style:default-style style:family="table-row">
<style:table-row-properties fo:keep-together="auto"/>
</style:default-style>
<style:style style:name="Standard" style:family="paragraph" style:class="text"/>
<style:style style:name="Text_20_body" style:display-name="Text body" style:family="paragraph" style:parent-style-name="Standard" style:class="text" style:master-page-name="">
<style:paragraph-properties fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-top="0cm" fo:margin-bottom="0cm" style:contextual-spacing="false" fo:line-height="100%" fo:text-align="start" style:justify-single-word="false" fo:orphans="2" fo:widows="2" fo:hyphenation-ladder-count="no-limit" fo:hyphenation-keep="auto" loext:hyphenation-keep-type="column" loext:hyphenation-keep-line="false" fo:text-indent="0.423cm" style:auto-text-indent="false" style:page-number="auto"/>
<style:text-properties fo:hyphenate="true" fo:hyphenation-remain-char-count="2" fo:hyphenation-push-char-count="2" loext:hyphenation-no-caps="false" loext:hyphenation-no-last-word="false" loext:hyphenation-word-char-count="no-limit" loext:hyphenation-zone="no-limit"/>
</style:style>
</office:styles>
<office:automatic-styles>
<style:style style:name="P1" style:family="paragraph" style:parent-style-name="Text_20_body" style:master-page-name="">
<style:paragraph-properties fo:text-align="justify" style:justify-single-word="false" fo:hyphenation-ladder-count="no-limit" fo:hyphenation-keep="auto" loext:hyphenation-keep-type="column" loext:hyphenation-keep-line="false" style:page-number="auto" loext:letter-spacing-maximum="10%" loext:text-scale-minimum="99%" loext:text-scale-maximum="110%"/>
<style:text-properties officeooo:paragraph-rsid="001f2a0b" fo:hyphenate="false" fo:hyphenation-remain-char-count="2" fo:hyphenation-push-char-count="2" loext:hyphenation-no-caps="false" loext:hyphenation-no-last-word="false" loext:hyphenation-word-char-count="no-limit" loext:hyphenation-zone="no-limit"/>
</style:style>
<style:page-layout style:name="pm1">
<style:page-layout-properties fo:page-width="594.99pt" fo:page-height="842pt" style:num-format="1" style:print-orientation="portrait" fo:margin-top="72pt" fo:margin-bottom="72pt" fo:margin-left="195pt" fo:margin-right="195pt" style:writing-mode="lr-tb" style:layout-grid-color="#c0c0c0" style:layout-grid-lines="20" style:layout-grid-base-height="20.01pt" style:layout-grid-ruby-height="10.01pt" style:layout-grid-mode="none" style:layout-grid-ruby-below="false" style:layout-grid-print="false" style:layout-grid-display="false" style:footnote-max-height="0pt" loext:margin-gutter="0pt">
<style:footnote-sep style:width="0.018cm" style:distance-before-sep="0.101cm" style:distance-after-sep="0.101cm" style:line-style="solid" style:adjustment="left" style:rel-width="25%" style:color="#000000"/>
</style:page-layout-properties>
</style:page-layout>
</office:automatic-styles>
<office:master-styles>
<style:master-page style:name="Standard" style:page-layout-name="pm1" draw:style-name="dp1">
<style:header>
<text:p text:style-name="Header"/>
</style:header>
</style:master-page>
<style:master-page style:name="Footnote" style:page-layout-name="pm2" draw:style-name="dp1"/>
<style:master-page style:name="Endnote" style:page-layout-name="pm2" draw:style-name="dp1"/>
</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.</text:p>
</office:text>
</office:body>
</office:document>
+68
View File
@@ -629,6 +629,74 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf167648_minimum)
}
}
CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf168251)
{
createSwDoc("tdf168251.fodt");
// Ensure that all text portions are calculated before testing.
SwDocShell* pShell = getSwDocShell();
// Dump the rendering of the first page as an XML file.
std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
MetafileXmlDump dumper;
xmlDocUniquePtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
CPPUNIT_ASSERT(pXmlDoc);
// Find the first text array action
for (size_t nAction = 0; nAction < xMetaFile->GetActionSize(); nAction++)
{
auto pAction = xMetaFile->GetAction(nAction);
if (pAction->GetType() == MetaActionType::TEXTARRAY)
{
auto pTextArrayAction = static_cast<MetaTextArrayAction*>(pAction);
auto pDXArray = pTextArrayAction->GetDXArray();
// There should be 39 characters on the first line
// This was 27 characters, but setting minimum glyph scaling
// to 99% allows more words in the line
CPPUNIT_ASSERT_EQUAL(size_t(39), pDXArray.size());
// Assert we are using the expected position for the
// second character of the first word with enlarged letter-spacing
// This was 286, now 266, according to the -25% minimum letter spacing
CPPUNIT_ASSERT_LESS(sal_Int32(270), sal_Int32(pDXArray[1]));
break;
}
}
// Find the fourth text array action
int nLine = 0;
for (size_t nAction = 0; nAction < xMetaFile->GetActionSize(); nAction++)
{
auto pAction = xMetaFile->GetAction(nAction);
if (pAction->GetType() == MetaActionType::TEXTARRAY)
{
if (++nLine < 6)
continue;
auto pTextArrayAction = static_cast<MetaTextArrayAction*>(pAction);
auto pDXArray = pTextArrayAction->GetDXArray();
// There should be 35 characters on the first line
CPPUNIT_ASSERT_EQUAL(size_t(35), pDXArray.size());
// Assert we are using the expected position for the
// second character of the first word with enlarged glyph width
// This was 238, now 251, according to the 110% maximum glyph scaling
// (and no changes in letter spacing)
CPPUNIT_ASSERT_GREATER(sal_Int32(245), sal_Int32(pDXArray[1]));
// Assert we are using the expected position for the
// first character of the last word with enlarged glyph width
// This was 3689, now 3659, according to the 110% maximum glyph scaling
CPPUNIT_ASSERT_LESS(sal_Int32(3665), sal_Int32(pDXArray[30]));
break;
}
}
}
CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf164499)
{
createSwDoc("tdf164499.docx");
+16 -14
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(153, len(xListBox.getChild('0').getChild('0').getChildren()))
self.assertEqual(155, 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(153, len(xListBox.getChild('0').getChild('0').getChildren()))
self.assertEqual(155, 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(153, len(xListBox.getChild('0').getChild('0').getChildren()))
self.assertEqual(155, 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(153, len(xParStyle.getChild('0').getChildren()))
self.assertEqual(155, 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(21, len(xTitleStyle.getChildren()))
self.assertEqual(23, 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'])
@@ -100,9 +100,11 @@ class styleNavigator(UITestCase):
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 Word Spacing\t100", get_state_as_dict(xTitleStyle.getChild('18'))['Text'])
self.assertEqual("Para Word Spacing Maximum\t100", get_state_as_dict(xTitleStyle.getChild('19'))['Text'])
self.assertEqual("Para Word Spacing Minimum\t100", get_state_as_dict(xTitleStyle.getChild('20'))['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(0, len(xListBox.getChild('1').getChildren()))
self.assertEqual(0, len(xListBox.getChild('2').getChildren()))
@@ -114,7 +116,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(153, len(xParStyle.getChild('0').getChildren()))
self.assertEqual(155, 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()))
@@ -149,7 +151,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(153, len(xListBox.getChild('0').getChild('0').getChildren()))
self.assertEqual(155, 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()))
@@ -159,7 +161,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(153, len(xListBox.getChild('0').getChild('0').getChildren()))
self.assertEqual(155, len(xListBox.getChild('0').getChild('0').getChildren()))
xParDirFormatting = xListBox.getChild('1')
self.assertEqual(1, len(xParDirFormatting.getChildren()))
@@ -212,7 +214,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(153, len(xListBox.getChild('0').getChild('0').getChildren()))
self.assertEqual(155, 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()))
@@ -222,7 +224,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(153, len(xListBox.getChild('1').getChild('0').getChildren()))
self.assertEqual(155, len(xListBox.getChild('1').getChild('0').getChildren()))
# Outer bookmark
xBookmarkFormatting = xListBox.getChild('0')
@@ -269,7 +271,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(153, len(xListBox.getChild('0').getChild('0').getChildren()))
self.assertEqual(155, 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(153, len(xListBox.getChild('0').getChild('0').getChildren()))
self.assertEqual(155, len(xListBox.getChild('0').getChild('0').getChildren()))
xTableContent = xListBox.getChild('0').getChild('1')
self.assertEqual(5, len(xTableContent.getChildren()))
+11
View File
@@ -75,6 +75,7 @@ class SW_DLLPUBLIC SwDrawTextInfo
tools::Long m_nSpace = 0;
tools::Long m_nKern = 0;
tools::Long m_nLetterSpacing = 0;
sal_Int16 m_nScaleWidth = 100;
TextFrameIndex m_nNumberOfBlanks = TextFrameIndex{ 0 };
sal_uInt8 m_nCursorBidiLevel = 0;
bool m_bBullet : 1;
@@ -550,6 +551,16 @@ public:
return m_nLetterSpacing;
}
void SetScaleWidth( tools::Long nNew )
{
m_nScaleWidth = nNew;
}
tools::Long GetScaleWidth() const
{
return m_nScaleWidth;
}
void SetSpace( tools::Long nNew )
{
if( nNew < 0 )
+11
View File
@@ -499,6 +499,17 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
{
m_nCutPos = rInf.GetTextBreak( nLineWidth, nMaxLen, nMaxComp, rInf.GetCachedVclData().get() );
// tdf#168251 minimum glyph scaling allows more text in the line
// TODO don't be greedy, allow only an extra word or word part
const sal_Int16 nScaleWidthMinimum = aAdjustItem.GetPropScaleWidthMinimum();
if ( nScaleWidthMinimum < 100 )
{
SwTwips nExtraSpace = nLineWidth / (nScaleWidthMinimum / 100.0) - nLineWidth;
nLineWidth += nExtraSpace;
rInf.SetExtraSpace( rInf.GetExtraSpace() + nExtraSpace );
m_nCutPos = rInf.GetTextBreak( nLineWidth, nMaxLen, nMaxComp, rInf.GetCachedVclData().get() );
}
// tdf#167648 minimum letter spacing allows more text in the line
// TODO don't be greedy, allow only an extra word or word part
if ( const sal_Int16 nLetterSpacingMinimum = aAdjustItem.GetPropLetterSpacingMinimum() )
+4
View File
@@ -783,6 +783,10 @@ void SwTextPaintInfo::DrawText_( const OUString &rText, const SwLinePortion &rPo
if ( rPor.GetLetterSpacing() != 0 )
aDrawInf.SetLetterSpacing( rPor.GetLetterSpacing() / sal_Int32(nLength) );
// set custom glyph scaling (hyphenation hasn't been supported yet)
// Note: set 100 percent, too (to reset the setting of the previous line)
aDrawInf.SetScaleWidth( rPor.GetScaleWidth() );
m_pFnt->DrawText_( aDrawInf );
}
}
+8 -3
View File
@@ -370,11 +370,16 @@ void SwTextAdjuster::CalcNewBlock( SwLineLayout *pCurrent,
: 0;
SwLinePortion *pPortion = pCurrent->GetFirstPortion();
tools::Long nSpaceKern = pPortion->GetSpaceCount()
? tools::Long(pPortion->GetLetterSpacing()) / sal_Int32(pPortion->GetSpaceCount()) * 100
// word spacing filled by letter spacing and glyph scaling (at expansion) or
// word spacing shrunk by them (at shrinking)
tools::Long nSpaceKernAndScale = pPortion->GetSpaceCount()
? ( pPortion->GetLetterSpacing() + pPortion->GetScaleWidthSpacing() ) * 100.0 /
sal_Int32(pPortion->GetSpaceCount())
: 0;
// set expansion in 1/100 twips/space
pCurrent->SetLLSpaceAdd( nSpaceSub ? nSpaceSub + nSpaceKern : (nSpaceAdd > nSpaceKern ? nSpaceAdd - nSpaceKern : 0), nSpaceIdx );
pCurrent->SetLLSpaceAdd( nSpaceSub
? ( nSpaceSub + nSpaceKernAndScale <= LONG_MAX/2 ? 0 : nSpaceSub + nSpaceKernAndScale )
: ( nSpaceAdd > nSpaceKernAndScale ? nSpaceAdd - nSpaceKernAndScale : 0 ), nSpaceIdx );
pPos->Width( static_cast<SwGluePortion*>(pPos)->GetFixWidth() );
}
else if (IsOneBlock() && nCharCnt > TextFrameIndex(1))
+10
View File
@@ -54,6 +54,8 @@ private:
SwTwips m_nExtraSpaceSize = 0; // extra space over normal space width
SwTwips m_nLetterSpacing = 0; // letter spacing, TODO: add better resolution
TextFrameIndex m_nSpaceCount; // space count for letter spacing
SwTwips m_nScaleWidth = 100; // glyph scaling (good resolution to limit font generation)
float m_fScaleWidthSpacing = 0.0; // extra space filled by glyph scaling
std::optional<SwLinePortionLayoutContext> m_nLayoutContext;
@@ -85,6 +87,10 @@ public:
SwTwips GetLetterSpacing() const { return m_nLetterSpacing; }
void SetLetterSpacing(const SwTwips nNew) { m_nLetterSpacing = nNew; }
TextFrameIndex GetSpaceCount() const { return m_nSpaceCount; }
SwTwips GetScaleWidth() const { return m_nScaleWidth; }
void SetScaleWidth(const SwTwips nNew) { m_nScaleWidth = nNew; }
float GetScaleWidthSpacing() const { return m_fScaleWidthSpacing; }
void SetScaleWidthSpacing(const float fNew) { m_fScaleWidthSpacing = fNew; }
void SetSpaceCount(TextFrameIndex const nSpaceCount) { m_nSpaceCount = nSpaceCount; }
SwTwips GetHangingBaseline() const { return mnHangingBaseline; }
void SetHangingBaseline( const SwTwips nNewBaseline ) { mnHangingBaseline = nNewBaseline; }
@@ -205,6 +211,8 @@ inline SwLinePortion &SwLinePortion::operator=(const SwLinePortion &rPortion)
m_nExtraSpaceSize = rPortion.m_nExtraSpaceSize;
m_nLetterSpacing = rPortion.m_nLetterSpacing;
m_nSpaceCount = rPortion.m_nSpaceCount;
m_nScaleWidth = rPortion.m_nScaleWidth;
m_fScaleWidthSpacing = rPortion.m_fScaleWidthSpacing;
m_nLayoutContext = rPortion.m_nLayoutContext;
return *this;
}
@@ -224,6 +232,8 @@ inline SwLinePortion::SwLinePortion(const SwLinePortion &rPortion) :
m_nExtraSpaceSize(rPortion.m_nExtraSpaceSize),
m_nLetterSpacing(rPortion.m_nLetterSpacing),
m_nSpaceCount(rPortion.m_nSpaceCount),
m_nScaleWidth(rPortion.m_nScaleWidth),
m_fScaleWidthSpacing(rPortion.m_fScaleWidthSpacing),
m_nLayoutContext(rPortion.m_nLayoutContext)
{
}
+29
View File
@@ -306,6 +306,8 @@ void SwTextPortion::BreakCut( SwTextFormatInfo &rInf, const SwTextGuess &rGuess
ExtraShrunkWidth( 0 );
ExtraSpaceSize( 0 );
SetLetterSpacing( 0 );
SetScaleWidth( 100 );
SetScaleWidthSpacing( 0 );
}
}
@@ -317,6 +319,8 @@ void SwTextPortion::BreakUnderflow( SwTextFormatInfo &rInf )
ExtraShrunkWidth( 0 );
ExtraSpaceSize( 0 );
SetLetterSpacing( 0 );
SetScaleWidth( 100 );
SetScaleWidthSpacing( 0 );
SetLen( TextFrameIndex(0) );
SetAscent( 0 );
rInf.SetUnderflow( this );
@@ -359,9 +363,28 @@ void SwTextPortion::SetSpacing( SwTextFormatInfo &rInf, const TextFrameIndex nBr
// final letter spacing/character based on the desired word spacing and maximum letter spacing
// TODO fix resolution applying 1/100 twips instead of 1 twip
SwTwips nLetterSpacing = std::min( fLetterSpacingForDesiredWordSpacing, fMaximumLetterSpacing );
// at shrinking by letter spacing, avoid overshrinking
if ( nLetterSpacing < 0 )
{
float fMinimumLetterSpacing =
nWidthOf10Spaces / 10.0 * aAdjustItem.GetPropLetterSpacingMinimum() / 100.0;
nLetterSpacing = std::max( fLetterSpacingForDesiredWordSpacing, fMinimumLetterSpacing );
}
// full width of the extra (rounded) letter spacing within the line
// to adjust word spacing in SwTextAdjuster::CalcNewBlock()
SetLetterSpacing( SwTwips(nLetterSpacing * nLetterCount) );
SetSpaceCount( TextFrameIndex(nSpaces) );
// limit glyph scaling without optical sizing:
// apply it only after applying maximum letter spacing
// TODO: change this after variable font support
if ( aAdjustItem.GetPropScaleWidthMaximum() != 100 )
SetScaleWidth( 100.0 * rInf.GetLineWidth() / (rInf.GetBreakWidth() + GetLetterSpacing()) );
if ( GetScaleWidth() > aAdjustItem.GetPropScaleWidthMaximum() )
SetScaleWidth( aAdjustItem.GetPropScaleWidthMaximum() );
// space used by glyph scaling to adjust word spacing
if ( aAdjustItem.GetPropScaleWidthMaximum() != 100 )
SetScaleWidthSpacing( rInf.GetBreakWidth() * (GetScaleWidth() - 100.0) / 100.0 );
}
bool SwTextPortion::Format_( SwTextFormatInfo &rInf )
@@ -391,6 +414,8 @@ bool SwTextPortion::Format_( SwTextFormatInfo &rInf )
ExtraShrunkWidth( 0 );
ExtraSpaceSize( 0 );
SetLetterSpacing( 0 );
SetScaleWidth( 100 );
SetScaleWidthSpacing( 0 );
std::optional<SwTextGuess> pGuess(std::in_place);
bool bFull = !pGuess->Guess( *this, rInf, Height() );
@@ -406,6 +431,8 @@ bool SwTextPortion::Format_( SwTextFormatInfo &rInf )
bool bNoWordSpacing = aAdjustItem.GetPropWordSpacing() == 100 &&
aAdjustItem.GetPropWordSpacingMinimum() == 100 &&
aAdjustItem.GetPropWordSpacingMaximum() == 100 &&
aAdjustItem.GetPropScaleWidthMinimum() == 100 &&
aAdjustItem.GetPropScaleWidthMaximum() == 100 &&
aAdjustItem.GetPropLetterSpacingMinimum() == 0 &&
aAdjustItem.GetPropLetterSpacingMaximum() == 0;
// support old ODT documents, where only JustifyLinesWithShrinking was set
@@ -746,6 +773,8 @@ bool SwTextPortion::Format( SwTextFormatInfo &rInf )
ExtraShrunkWidth( 0 );
ExtraSpaceSize( 0 );
SetLetterSpacing( 0 );
SetScaleWidth( 100 );
SetScaleWidthSpacing( 0 );
SetLen( TextFrameIndex(0) );
SetAscent( 0 );
SetNextPortion( nullptr ); // ????
+7
View File
@@ -1140,6 +1140,13 @@ void SwSubFont::DrawText_( SwDrawTextInfo &rInf, const bool bGrey )
if( !pLastFont || pLastFont->GetOwner() != m_nFontCacheId )
ChgFnt( rInf.GetShell(), rInf.GetOut() );
// change glyph scaling, if needed
if ( GetPropWidth() != rInf.GetScaleWidth() )
{
SetPropWidth( rInf.GetScaleWidth() );
ChgFnt( rInf.GetShell(), rInf.GetOut() );
}
SwDigitModeModifier aDigitModeModifier(rInf.GetOut(), rInf.GetFont()->GetLanguage(),
SwModule::get()->GetCTLTextNumerals());
@@ -189,6 +189,8 @@
{ UNO_NAME_PARA_WORD_SPACING_MAX, RES_PARATR_ADJUST, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_WORD_SPACING_MAX }, \
{ UNO_NAME_PARA_LETTER_SPACING_MIN, RES_PARATR_ADJUST, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_LETTER_SPACING_MIN }, \
{ 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_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 }, \
@@ -458,6 +460,8 @@
{ UNO_NAME_PARA_WORD_SPACING_MAX, RES_PARATR_ADJUST, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_WORD_SPACING_MAX},\
{ UNO_NAME_PARA_LETTER_SPACING_MIN, RES_PARATR_ADJUST, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_LETTER_SPACING_MIN},\
{ 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_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},\
@@ -303,6 +303,8 @@ static OUString PropertyNametoRID(const OUString& rName)
{ "ParaRegisterModeActive", RID_PARA_REGISTER_MODE_ACTIVE },
{ "ParaRightMargin", RID_PARA_RIGHT_MARGIN },
{ "ParaRightMarginRelative", RID_PARA_RIGHT_MARGIN_RELATIVE },
{ "ParaScaleWidthMinimum", RID_PARA_SCALE_WIDTH_MIN },
{ "ParaScaleWidthMaximum", RID_PARA_SCALE_WIDTH_MAX },
{ "ParaShadowFormat", RID_PARA_SHADOW_FORMAT },
{ "ParaSplit", RID_PARA_SPLIT },
{ "ParaStyleName", RID_PARA_STYLE_NAME },
+2
View File
@@ -537,6 +537,8 @@ inline constexpr OUString PROP_ParaShadowFormat = u"ParaShadowFormat"_ustr;
inline constexpr OUString PROP_ParaSplit = u"ParaSplit"_ustr;
inline constexpr OUString PROP_ParaTabStops = u"ParaTabStops"_ustr;
inline constexpr OUString PROP_ParaTabStopDefaultDistance = u"ParaTabStopDefaultDistance"_ustr;
inline constexpr OUString PROP_ParaScaleWidthMinimum = u"ParaScaleWidthMinimum"_ustr;
inline constexpr OUString PROP_ParaScaleWidthMaximum = u"ParaScaleWidthMaximum"_ustr;
inline constexpr OUString PROP_ParaTopMargin = u"ParaTopMargin"_ustr;
inline constexpr OUString PROP_ParaTopMarginRelative = u"ParaTopMarginRelative"_ustr;
inline constexpr OUString PROP_ParaUserDefinedAttributes = u"ParaUserDefinedAttributes"_ustr;
+2
View File
@@ -2034,6 +2034,8 @@ namespace xmloff::token {
TOKEN( "text-rotation-angle", XML_TEXT_ROTATION_ANGLE ),
TOKEN( "text-rotation-scale", XML_TEXT_ROTATION_SCALE ),
TOKEN( "text-scale", XML_TEXT_SCALE ),
TOKEN( "text-scale-maximum", XML_TEXT_SCALE_MAXIMUM ),
TOKEN( "text-scale-minimum", XML_TEXT_SCALE_MINIMUM ),
TOKEN( "text-shadow", XML_TEXT_SHADOW ),
TOKEN( "text-style", XML_TEXT_STYLE ),
TOKEN( "text-transform", XML_TEXT_TRANSFORM ),
+4 -2
View File
@@ -947,10 +947,12 @@ sal_Int8 CheckExtendedNamespace(std::u16string_view sXMLAttributeName, std::u16s
|| IsXMLToken(sXMLAttributeName, XML_HYPHENATION_ZONE_PAGE)
|| IsXMLToken(sXMLAttributeName, XML_HYPHENATION_ZONE_SPREAD))
return IsXMLToken(sValue, XML_NO_LIMIT) ? -1 : 1;
// don't export word spacing when they have the default 100% value
// don't export word spacing and glyph scaling when they have the default 100% value
else if (IsXMLToken(sXMLAttributeName, XML_WORD_SPACING)
|| IsXMLToken(sXMLAttributeName, XML_WORD_SPACING_MINIMUM)
|| IsXMLToken(sXMLAttributeName, XML_WORD_SPACING_MAXIMUM))
|| IsXMLToken(sXMLAttributeName, XML_WORD_SPACING_MAXIMUM)
|| IsXMLToken(sXMLAttributeName, XML_TEXT_SCALE_MINIMUM)
|| IsXMLToken(sXMLAttributeName, XML_TEXT_SCALE_MAXIMUM))
{
static constexpr OUString s100PercentCompare( u"100%"_ustr );
size_t nBegin = sValue.find( s100PercentCompare );
+2
View File
@@ -487,6 +487,8 @@ XMLPropertyMapEntry constexpr aXMLParaPropMap[] =
MAP_EXT( PROP_ParaWordSpacingMaximum, XML_NAMESPACE_LO_EXT, XML_WORD_SPACING_MAXIMUM, XML_TYPE_PROP_PARAGRAPH|XML_TYPE_PERCENT16, 0 ),
MAP_EXT( PROP_ParaLetterSpacingMinimum, XML_NAMESPACE_LO_EXT, XML_LETTER_SPACING_MINIMUM, XML_TYPE_PROP_PARAGRAPH|XML_TYPE_PERCENT16, 0 ),
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 ),
MT_ED( PROP_CharScriptHint, XML_NAMESPACE_STYLE, XML_SCRIPT_TYPE, XML_TYPE_TEXT_SCRIPT_TYPE|MID_FLAG_MERGE_PROPERTY, 0 ),
+2
View File
@@ -1934,6 +1934,8 @@ text-position
text-rotation-angle
text-rotation-scale
text-scale
text-scale-maximum
text-scale-minimum
text-shadow
text-style
text-transform