cui/source/inc/paragrph.hxx | 5 cui/source/tabpages/paragrph.cxx | 28 ++++ cui/uiconfig/ui/paragalignpage.ui | 64 +++++++++++ editeng/source/items/paraitem.cxx | 26 ++++ include/editeng/adjustitem.hxx | 25 ++++ include/editeng/memberids.h | 2 include/editeng/unotext.hxx | 2 include/xmloff/xmltoken.hxx | 2 offapi/com/sun/star/style/ParagraphProperties.idl | 28 ++++ schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng | 24 ++++ svx/sdi/svxitems.sdi | 2 sw/inc/inspectorproperties.hrc | 2 sw/inc/unoprnms.hxx | 2 sw/qa/extras/layout/data/tdf168251.fodt | 56 +++++++++ sw/qa/extras/layout/layout3.cxx | 68 ++++++++++++ sw/qa/uitest/styleInspector/styleInspector.py | 30 ++--- sw/qa/uitest/styleInspector/tdf137513.py | 2 sw/source/core/inc/drawfont.hxx | 11 + sw/source/core/text/guess.cxx | 11 + sw/source/core/text/inftxt.cxx | 4 sw/source/core/text/itradj.cxx | 11 + sw/source/core/text/porlin.hxx | 10 + sw/source/core/text/portxt.cxx | 29 +++++ sw/source/core/txtnode/swfont.cxx | 7 + sw/source/core/unocore/unomapproperties.hxx | 4 sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx | 2 xmloff/inc/xmlprop.hxx | 2 xmloff/source/core/xmltoken.cxx | 2 xmloff/source/style/xmlexppr.cxx | 6 - xmloff/source/text/txtprmap.cxx | 2 xmloff/source/token/tokens.txt | 2 31 files changed, 448 insertions(+), 23 deletions(-)
New commits: commit 45ec7bd76196dcc60b4c2db2f6f00623ecbaf5a4 Author: László Németh <[email protected]> AuthorDate: Tue Sep 2 16:56:13 2025 +0200 Commit: László Németh <[email protected]> CommitDate: Wed Sep 3 15:22:37 2025 +0200 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 <[email protected]> diff --git a/cui/source/inc/paragrph.hxx b/cui/source/inc/paragrph.hxx index 4218f3091e3b..856daffcf776 100644 --- a/cui/source/inc/paragrph.hxx +++ b/cui/source/inc/paragrph.hxx @@ -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); diff --git a/cui/source/tabpages/paragrph.cxx b/cui/source/tabpages/paragrph.cxx index 5feaf5420286..652231e16347 100644 --- a/cui/source/tabpages/paragrph.cxx +++ b/cui/source/tabpages/paragrph.cxx @@ -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); diff --git a/cui/uiconfig/ui/paragalignpage.ui b/cui/uiconfig/ui/paragalignpage.ui index 451f6b6765cc..92824780cf41 100644 --- a/cui/uiconfig/ui/paragalignpage.ui +++ b/cui/uiconfig/ui/paragalignpage.ui @@ -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> diff --git a/editeng/source/items/paraitem.cxx b/editeng/source/items/paraitem.cxx index b3f24417c82e..30adcd39a35e 100644 --- a/editeng/source/items/paraitem.cxx +++ b/editeng/source/items/paraitem.cxx @@ -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); diff --git a/include/editeng/adjustitem.hxx b/include/editeng/adjustitem.hxx index 534bacd78cec..4f9697f5b20e 100644 --- a/include/editeng/adjustitem.hxx +++ b/include/editeng/adjustitem.hxx @@ -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 diff --git a/include/editeng/memberids.h b/include/editeng/memberids.h index 26b2e3c7693a..944664bc0ecf 100644 --- a/include/editeng/memberids.h +++ b/include/editeng/memberids.h @@ -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 diff --git a/include/editeng/unotext.hxx b/include/editeng/unotext.hxx index 035f8287b9dc..3a144ef5ed9c 100644 --- a/include/editeng/unotext.hxx +++ b/include/editeng/unotext.hxx @@ -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; diff --git a/include/xmloff/xmltoken.hxx b/include/xmloff/xmltoken.hxx index 760f44427496..828de31eb2aa 100644 --- a/include/xmloff/xmltoken.hxx +++ b/include/xmloff/xmltoken.hxx @@ -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, diff --git a/offapi/com/sun/star/style/ParagraphProperties.idl b/offapi/com/sun/star/style/ParagraphProperties.idl index 45bc14c6508e..c18217a1bf79 100644 --- a/offapi/com/sun/star/style/ParagraphProperties.idl +++ b/offapi/com/sun/star/style/ParagraphProperties.idl @@ -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; }; diff --git a/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng b/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng index 7b0ce565ab5e..05bc58c52dc7 100644 --- a/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng +++ b/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng @@ -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> diff --git a/svx/sdi/svxitems.sdi b/svx/sdi/svxitems.sdi index 1af2e4a1e2ca..fca1b739b832 100644 --- a/svx/sdi/svxitems.sdi +++ b/svx/sdi/svxitems.sdi @@ -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; diff --git a/sw/inc/inspectorproperties.hrc b/sw/inc/inspectorproperties.hrc index bd0d3b166f79..5c7d5f2c62cd 100644 --- a/sw/inc/inspectorproperties.hrc +++ b/sw/inc/inspectorproperties.hrc @@ -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") diff --git a/sw/inc/unoprnms.hxx b/sw/inc/unoprnms.hxx index 8ffe778f437a..341777e80b23 100644 --- a/sw/inc/unoprnms.hxx +++ b/sw/inc/unoprnms.hxx @@ -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; diff --git a/sw/qa/extras/layout/data/tdf168251.fodt b/sw/qa/extras/layout/data/tdf168251.fodt new file mode 100644 index 000000000000..0f6c405114da --- /dev/null +++ b/sw/qa/extras/layout/data/tdf168251.fodt @@ -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:c alcext="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="'Liberation Serif'" 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> diff --git a/sw/qa/extras/layout/layout3.cxx b/sw/qa/extras/layout/layout3.cxx index 6b259aa69bc4..1cd8d590f546 100644 --- a/sw/qa/extras/layout/layout3.cxx +++ b/sw/qa/extras/layout/layout3.cxx @@ -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"); diff --git a/sw/qa/uitest/styleInspector/styleInspector.py b/sw/qa/uitest/styleInspector/styleInspector.py index d133461c7a82..a9ab1fc940ee 100644 --- a/sw/qa/uitest/styleInspector/styleInspector.py +++ b/sw/qa/uitest/styleInspector/styleInspector.py @@ -26,7 +26,7 @@ class styleNavigator(UITestCase): # The cursor is on text without formatting and default style self.assertEqual(1, len(xListBox.getChild('0').getChildren())) self.assertEqual("Default Paragraph Style ", 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 ", 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 ", 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 ", 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 ", get_state_as_dict(xParStyle.getChild('1'))['Text']) self.assertEqual(28, len(xParStyle.getChild('1').getChildren())) xTitleStyle = xParStyle.getChild('2') self.assertEqual("Title ", get_state_as_dict(xTitleStyle)['Text']) - self.assertEqual(21, len(xTitleStyle.getChildren())) + self.assertEqual(23, len(xTitleStyle.getChildren())) self.assertEqual("Char Difference Height 0", get_state_as_dict(xTitleStyle.getChild('0'))['Text']) self.assertEqual("Char Difference Height Asian 0", get_state_as_dict(xTitleStyle.getChild('1'))['Text']) self.assertEqual("Char Difference Height Complex 0", get_state_as_dict(xTitleStyle.getChild('2'))['Text']) @@ -100,9 +100,11 @@ class styleNavigator(UITestCase): self.assertEqual("Para Last Line Adjust 0", get_state_as_dict(xTitleStyle.getChild('15'))['Text']) self.assertEqual("Para Letter Spacing Maximum 0", get_state_as_dict(xTitleStyle.getChild('16'))['Text']) self.assertEqual("Para Letter Spacing Minimum 0", get_state_as_dict(xTitleStyle.getChild('17'))['Text']) - self.assertEqual("Para Word Spacing 100", get_state_as_dict(xTitleStyle.getChild('18'))['Text']) - self.assertEqual("Para Word Spacing Maximum 100", get_state_as_dict(xTitleStyle.getChild('19'))['Text']) - self.assertEqual("Para Word Spacing Minimum 100", get_state_as_dict(xTitleStyle.getChild('20'))['Text']) + self.assertEqual("Para Scale Width Maximum 100", get_state_as_dict(xTitleStyle.getChild('18'))['Text']) + self.assertEqual("Para Scale Width Minimum 100", get_state_as_dict(xTitleStyle.getChild('19'))['Text']) + self.assertEqual("Para Word Spacing 100", get_state_as_dict(xTitleStyle.getChild('20'))['Text']) + self.assertEqual("Para Word Spacing Maximum 100", get_state_as_dict(xTitleStyle.getChild('21'))['Text']) + self.assertEqual("Para Word Spacing Minimum 100", 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 ", 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 ", 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 ", 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 ", 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 ", 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 ", 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 ", 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())) diff --git a/sw/qa/uitest/styleInspector/tdf137513.py b/sw/qa/uitest/styleInspector/tdf137513.py index af0e24d993d3..8352c2a352e9 100644 --- a/sw/qa/uitest/styleInspector/tdf137513.py +++ b/sw/qa/uitest/styleInspector/tdf137513.py @@ -35,7 +35,7 @@ class tdf137513(UITestCase): self.assertEqual(2, len(xListBox.getChild('0').getChildren())) self.assertEqual("Default Paragraph Style ", get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text']) self.assertEqual("Table Contents ", 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())) diff --git a/sw/source/core/inc/drawfont.hxx b/sw/source/core/inc/drawfont.hxx index 3438c366c03a..443f47635353 100644 --- a/sw/source/core/inc/drawfont.hxx +++ b/sw/source/core/inc/drawfont.hxx @@ -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 ) diff --git a/sw/source/core/text/guess.cxx b/sw/source/core/text/guess.cxx index 384c98baba8d..f75ffa5510cf 100644 --- a/sw/source/core/text/guess.cxx +++ b/sw/source/core/text/guess.cxx @@ -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() ) diff --git a/sw/source/core/text/inftxt.cxx b/sw/source/core/text/inftxt.cxx index 8c297d4e300d..a9333e66b9b4 100644 --- a/sw/source/core/text/inftxt.cxx +++ b/sw/source/core/text/inftxt.cxx @@ -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 ); } } diff --git a/sw/source/core/text/itradj.cxx b/sw/source/core/text/itradj.cxx index c6230b18f5db..64bd6e7dd582 100644 --- a/sw/source/core/text/itradj.cxx +++ b/sw/source/core/text/itradj.cxx @@ -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)) diff --git a/sw/source/core/text/porlin.hxx b/sw/source/core/text/porlin.hxx index 4405a1aff144..6a3a58f1aec6 100644 --- a/sw/source/core/text/porlin.hxx +++ b/sw/source/core/text/porlin.hxx @@ -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) { } diff --git a/sw/source/core/text/portxt.cxx b/sw/source/core/text/portxt.cxx index a8c4fac62a03..8e836ce1fc39 100644 --- a/sw/source/core/text/portxt.cxx +++ b/sw/source/core/text/portxt.cxx @@ -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 ); // ???? diff --git a/sw/source/core/txtnode/swfont.cxx b/sw/source/core/txtnode/swfont.cxx index 0dd010370e2d..07dfef5d43c7 100644 --- a/sw/source/core/txtnode/swfont.cxx +++ b/sw/source/core/txtnode/swfont.cxx @@ -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()); diff --git a/sw/source/core/unocore/unomapproperties.hxx b/sw/source/core/unocore/unomapproperties.hxx index 595c4aa40749..0e6ee62e52af 100644 --- a/sw/source/core/unocore/unomapproperties.hxx +++ b/sw/source/core/unocore/unomapproperties.hxx @@ -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},\ diff --git a/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx b/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx index 4e6b905ea2c1..33a27b191cac 100644 --- a/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx +++ b/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx @@ -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 }, diff --git a/xmloff/inc/xmlprop.hxx b/xmloff/inc/xmlprop.hxx index b82ed64e0a6d..657ddf1eeae8 100644 --- a/xmloff/inc/xmlprop.hxx +++ b/xmloff/inc/xmlprop.hxx @@ -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; diff --git a/xmloff/source/core/xmltoken.cxx b/xmloff/source/core/xmltoken.cxx index 8f678d44fbfb..4b8c63d5936c 100644 --- a/xmloff/source/core/xmltoken.cxx +++ b/xmloff/source/core/xmltoken.cxx @@ -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 ), diff --git a/xmloff/source/style/xmlexppr.cxx b/xmloff/source/style/xmlexppr.cxx index 552f0dde8f51..803c441b7b3e 100644 --- a/xmloff/source/style/xmlexppr.cxx +++ b/xmloff/source/style/xmlexppr.cxx @@ -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 ); diff --git a/xmloff/source/text/txtprmap.cxx b/xmloff/source/text/txtprmap.cxx index db2c0ad64fdf..593006cf0ec3 100644 --- a/xmloff/source/text/txtprmap.cxx +++ b/xmloff/source/text/txtprmap.cxx @@ -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 ), diff --git a/xmloff/source/token/tokens.txt b/xmloff/source/token/tokens.txt index 979779e54eb1..9576187b7522 100644 --- a/xmloff/source/token/tokens.txt +++ b/xmloff/source/token/tokens.txt @@ -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
