sw/inc/charatr.hxx                                  |    3 +
 sw/inc/hintids.hxx                                  |    5 +
 sw/inc/swatrset.hxx                                 |    2 
 sw/inc/unoprnms.hxx                                 |    1 
 sw/qa/extras/htmlexport/htmlexport.cxx              |   27 +++++++++
 sw/qa/extras/odfexport/odfexport.cxx                |   56 ++++++++++++++++++++
 sw/qa/uitest/styleInspector/styleInspector.py       |   20 +++----
 sw/qa/uitest/styleInspector/tdf137513.py            |    2 
 sw/source/core/bastyp/init.cxx                      |    2 
 sw/source/core/doc/DocumentStylePoolManager.cxx     |    1 
 sw/source/core/doc/dbgoutsw.cxx                     |    1 
 sw/source/core/inc/swfntcch.hxx                     |    2 
 sw/source/core/inc/swfont.hxx                       |   16 +++++
 sw/source/core/layout/wsfrm.cxx                     |    1 
 sw/source/core/text/atrhndl.hxx                     |    2 
 sw/source/core/text/atrstck.cxx                     |   21 ++++---
 sw/source/core/txtnode/swfont.cxx                   |    3 +
 sw/source/core/unocore/unomap1.cxx                  |    2 
 sw/source/core/unocore/unomapproperties.hxx         |    2 
 sw/source/filter/html/css1atr.cxx                   |   12 ++++
 sw/source/filter/html/css1kywd.hxx                  |    1 
 sw/source/filter/html/htmlatr.cxx                   |    2 
 sw/source/filter/html/htmlcss1.cxx                  |    3 +
 sw/source/filter/html/svxcss1.cxx                   |   26 +++++++++
 sw/source/filter/html/swhtml.hxx                    |    1 
 sw/source/filter/ww8/attributeoutputbase.hxx        |    4 +
 sw/source/filter/ww8/docxattributeoutput.cxx        |    9 +++
 sw/source/filter/ww8/docxattributeoutput.hxx        |    3 +
 sw/source/filter/ww8/rtfattributeoutput.cxx         |   14 +++++
 sw/source/filter/ww8/rtfattributeoutput.hxx         |    5 +
 sw/source/filter/ww8/ww8atr.cxx                     |    9 +++
 sw/source/filter/ww8/ww8attributeoutput.hxx         |    3 +
 sw/source/uibase/app/docshini.cxx                   |    4 +
 vcl/qa/cppunit/pdfexport/data/testOpticalSizing.odt |binary
 vcl/qa/cppunit/pdfexport/pdfexport.cxx              |   38 +++++++++++++
 35 files changed, 281 insertions(+), 22 deletions(-)

New commits:
commit 475ec1aa3683cd4a7ff9e5f89d0b3fc858763fa3
Author:     Khaled Hosny <[email protected]>
AuthorDate: Mon Mar 2 02:02:25 2026 +0200
Commit:     Khaled Hosny <[email protected]>
CommitDate: Mon Mar 2 21:45:41 2026 +0100

    tdf#153368: Support optical size for variable fonts, part 5
    
    Support import/export of font-optical-sizing in HTML filter.
    
    Change-Id: Ib45b3152b57e674fc1e0b4b4b2359634909d15a0
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/200538
    Tested-by: Jenkins
    Reviewed-by: Khaled Hosny <[email protected]>

diff --git a/sw/qa/extras/htmlexport/htmlexport.cxx 
b/sw/qa/extras/htmlexport/htmlexport.cxx
index 20cfe024500f..85fa29ec4c5d 100644
--- a/sw/qa/extras/htmlexport/htmlexport.cxx
+++ b/sw/qa/extras/htmlexport/htmlexport.cxx
@@ -1651,6 +1651,33 @@ CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, 
testReqifNoTypeInUL)
     // Without the accompanying fix in place, this test would have failed
     assertXPathNoAttribute(pXmlDoc, 
"//reqif-xhtml:ul/reqif-xhtml:li/reqif-xhtml:ul", "type");
 }
+
+CPPUNIT_TEST_FIXTURE(HtmlExportTest, testOpticalSizing)
+{
+    createSwDoc();
+    uno::Reference<text::XTextRange> xRun = getRun(getParagraph(1), 1);
+    xRun->setString(u"text"_ustr);
+    uno::Reference<beans::XPropertySet> xCursor(xRun, uno::UNO_QUERY);
+
+    xCursor->setPropertyValue(u"CharOpticalSizing"_ustr, uno::Any(false));
+    saveAndReload(TestFilter::HTML_WRITER);
+
+    uno::Reference<beans::XPropertySet> xRetCursor(getRun(getParagraph(1), 1), 
uno::UNO_QUERY);
+    CPPUNIT_ASSERT_EQUAL(false, getProperty<bool>(xRetCursor, 
u"CharOpticalSizing"_ustr));
+
+    dispose();
+
+    createSwDoc();
+    uno::Reference<text::XTextRange> xRun2 = getRun(getParagraph(1), 1);
+    xRun2->setString(u"text"_ustr);
+    uno::Reference<beans::XPropertySet> xCursor2(xRun2, uno::UNO_QUERY);
+
+    xCursor2->setPropertyValue(u"CharOpticalSizing"_ustr, uno::Any(true));
+    saveAndReload(TestFilter::HTML_WRITER);
+
+    uno::Reference<beans::XPropertySet> xRetCursor2(getRun(getParagraph(1), 
1), uno::UNO_QUERY);
+    CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(xRetCursor2, 
u"CharOpticalSizing"_ustr));
+}
 } // end of anonymous namespace
 CPPUNIT_PLUGIN_IMPLEMENT();
 
diff --git a/sw/source/filter/html/css1atr.cxx 
b/sw/source/filter/html/css1atr.cxx
index 6f80bb53ff47..12334aaac59d 100644
--- a/sw/source/filter/html/css1atr.cxx
+++ b/sw/source/filter/html/css1atr.cxx
@@ -48,6 +48,7 @@
 #include <editeng/spltitem.hxx>
 #include <editeng/orphitem.hxx>
 #include <editeng/charhiddenitem.hxx>
+#include <editeng/opticalsizingitem.hxx>
 #include <svx/xoutbmp.hxx>
 #include <svx/svdobj.hxx>
 #include <editeng/langitem.hxx>
@@ -2549,6 +2550,16 @@ static SwHTMLWriter& OutCSS1_SvxHidden( SwHTMLWriter& 
rWrt, const SfxPoolItem& r
     return rWrt;
 }
 
+static SwHTMLWriter& OutCSS1_SvxOpticalSizing( SwHTMLWriter& rWrt, const 
SfxPoolItem& rHt )
+{
+    if( static_cast<const SvxOpticalSizingItem&>(rHt).GetValue() )
+        rWrt.OutCSS1_PropertyAscii( sCSS1_P_font_optical_sizing, sCSS1_PV_auto 
);
+    else
+        rWrt.OutCSS1_PropertyAscii( sCSS1_P_font_optical_sizing, sCSS1_PV_none 
);
+
+    return rWrt;
+}
+
 static SwHTMLWriter& OutCSS1_SvxFontWeight( SwHTMLWriter& rWrt, const 
SfxPoolItem& rHt )
 {
     sal_uInt16 nScript = CSS1_OUTMODE_WESTERN;
@@ -3465,7 +3476,7 @@ SwAttrFnTab const aCSS1AttrFnTab = {
 /* RES_CHRATR_BIDIRTL */            nullptr,
 /* RES_CHRATR_UNUSED3 */            nullptr,
 /* RES_CHRATR_SCRIPT_HINT */        nullptr,
-/* RES_CHRATR_OPTICAL_SIZING */     nullptr,
+/* RES_CHRATR_OPTICAL_SIZING */     OutCSS1_SvxOpticalSizing,
 
 /* RES_TXTATR_REFMARK */            nullptr,
 /* RES_TXTATR_TOXMARK */            nullptr,
diff --git a/sw/source/filter/html/css1kywd.hxx 
b/sw/source/filter/html/css1kywd.hxx
index 2548cd3a9537..8332083ad0f1 100644
--- a/sw/source/filter/html/css1kywd.hxx
+++ b/sw/source/filter/html/css1kywd.hxx
@@ -65,6 +65,7 @@ constexpr inline std::string_view sCSS1_PV_italic = "italic";
 constexpr inline std::string_view sCSS1_PV_oblique = "oblique";
 
 constexpr inline std::string_view sCSS1_P_font_variant = "font-variant";
+constexpr inline std::string_view sCSS1_P_font_optical_sizing = 
"font-optical-sizing";
 
 //constexpr inline std::string_view sCSS1_PV_normal = "normal";
 constexpr inline std::string_view sCSS1_PV_small_caps = "small-caps";
diff --git a/sw/source/filter/html/htmlatr.cxx 
b/sw/source/filter/html/htmlatr.cxx
index d917fea69094..cc33bf540ff4 100644
--- a/sw/source/filter/html/htmlatr.cxx
+++ b/sw/source/filter/html/htmlatr.cxx
@@ -45,6 +45,7 @@
 #include <editeng/lrspitem.hxx>
 #include <editeng/langitem.hxx>
 #include <editeng/frmdiritem.hxx>
+#include <editeng/opticalsizingitem.hxx>
 #include <fchrfmt.hxx>
 #include <fmtautofmt.hxx>
 #include <fmtfsize.hxx>
@@ -3316,7 +3317,7 @@ const SwAttrFnTab aHTMLAttrFnTab = {
 /* RES_CHRATR_BIDIRTL */            nullptr,
 /* RES_CHRATR_UNUSED3 */            nullptr,
 /* RES_CHRATR_SCRIPT_HINT */        nullptr,
-/* RES_CHRATR_OPTICAL_SIZING */     nullptr,
+/* RES_CHRATR_OPTICAL_SIZING */     OutHTML_CSS1Attr,
 
 /* RES_TXTATR_REFMARK */            nullptr,
 /* RES_TXTATR_TOXMARK */            nullptr,
diff --git a/sw/source/filter/html/htmlcss1.cxx 
b/sw/source/filter/html/htmlcss1.cxx
index 5229d3c69eb5..cc0bb8d90cd8 100644
--- a/sw/source/filter/html/htmlcss1.cxx
+++ b/sw/source/filter/html/htmlcss1.cxx
@@ -1598,6 +1598,9 @@ HTMLAttr **SwHTMLParser::GetAttrTabEntry( sal_uInt16 
nWhich )
     case RES_CHRATR_BACKGROUND:
         ppAttr = &m_xAttrTab->pCharBrush;
         break;
+    case RES_CHRATR_OPTICAL_SIZING:
+        ppAttr = &m_xAttrTab->pOpticalSizing;
+        break;
     case RES_CHRATR_BOX:
         ppAttr = &m_xAttrTab->pCharBox;
         break;
diff --git a/sw/source/filter/html/svxcss1.cxx 
b/sw/source/filter/html/svxcss1.cxx
index 69cc2f381cd4..ffd8dd0a7982 100644
--- a/sw/source/filter/html/svxcss1.cxx
+++ b/sw/source/filter/html/svxcss1.cxx
@@ -52,6 +52,7 @@
 #include <editeng/widwitem.hxx>
 #include <editeng/frmdiritem.hxx>
 #include <editeng/orphitem.hxx>
+#include <editeng/opticalsizingitem.hxx>
 #include <utility>
 #include <vcl/metric.hxx>
 #include <vcl/svapp.hxx>
@@ -283,6 +284,7 @@ struct SvxCSS1ItemIds
     sal_uInt16 nKerning;
     sal_uInt16 nCaseMap;
     sal_uInt16 nBlink;
+    sal_uInt16 nOpticalSizing;
 
     sal_uInt16 nLineSpacing;
     sal_uInt16 nAdjust;
@@ -738,6 +740,7 @@ SvxCSS1Parser::SvxCSS1Parser( SfxItemPool& rPool, OUString 
aBaseURL,
     aItemIds.nKerning = initTrueWhich( SID_ATTR_CHAR_KERNING );
     aItemIds.nCaseMap = initTrueWhich( SID_ATTR_CHAR_CASEMAP );
     aItemIds.nBlink = initTrueWhich( SID_ATTR_FLASH );
+    aItemIds.nOpticalSizing = initTrueWhich( SID_ATTR_CHAR_OPTICAL_SIZING );
 
     aItemIds.nLineSpacing = initTrueWhich( SID_ATTR_PARA_LINESPACE );
     aItemIds.nAdjust = initTrueWhich( SID_ATTR_PARA_ADJUST );
@@ -1247,6 +1250,28 @@ static void ParseCSS1_font_variant( const CSS1Expression 
*pExpr,
     }
 }
 
+static void ParseCSS1_font_optical_sizing( const CSS1Expression *pExpr,
+                                    SfxItemSet &rItemSet,
+                                    SvxCSS1PropertyInfo& /*rPropInfo*/,
+                                    const SvxCSS1Parser& /*rParser*/ )
+{
+    assert(pExpr && "no expression");
+
+    switch( pExpr->GetType() )
+    {
+    case CSS1_IDENT:
+        {
+            if( o3tl::equalsIgnoreAsciiCase( pExpr->GetString(), sCSS1_PV_auto 
) )
+                rItemSet.Put( SvxOpticalSizingItem( true, 
aItemIds.nOpticalSizing ) );
+            else if( o3tl::equalsIgnoreAsciiCase( pExpr->GetString(), 
sCSS1_PV_none ) )
+                rItemSet.Put( SvxOpticalSizingItem( false, 
aItemIds.nOpticalSizing ) );
+            break;
+        }
+    default:
+        break;
+    }
+}
+
 static void ParseCSS1_text_transform( const CSS1Expression *pExpr,
                                     SfxItemSet &rItemSet,
                                     SvxCSS1PropertyInfo& /*rPropInfo*/,
@@ -3099,6 +3124,7 @@ CSS1PropEntry constexpr aCSS1PropFnTab[] =
     { sCSS1_P_float, ParseCSS1_float },
     { sCSS1_P_font, ParseCSS1_font },
     { sCSS1_P_font_family, ParseCSS1_font_family },
+    { sCSS1_P_font_optical_sizing, ParseCSS1_font_optical_sizing },
     { sCSS1_P_font_size, ParseCSS1_font_size },
     { sCSS1_P_font_style, ParseCSS1_font_style },
     { sCSS1_P_font_variant, ParseCSS1_font_variant },
diff --git a/sw/source/filter/html/swhtml.hxx b/sw/source/filter/html/swhtml.hxx
index 151189aa7acf..5147d8e8c6fa 100644
--- a/sw/source/filter/html/swhtml.hxx
+++ b/sw/source/filter/html/swhtml.hxx
@@ -128,6 +128,7 @@ struct HTMLAttrTable
     HTMLAttr* pLanguageCJK;
     HTMLAttr* pLanguageCTL;
     HTMLAttr* pCharBox;
+    HTMLAttr* pOpticalSizing;
 };
 
 class HTMLAttr
commit 1802e84d03c3d458edc3e124d2a7839660bc53d0
Author:     Khaled Hosny <[email protected]>
AuthorDate: Mon Mar 2 02:02:23 2026 +0200
Commit:     Khaled Hosny <[email protected]>
CommitDate: Mon Mar 2 21:45:29 2026 +0100

    tdf#153368: Support optical size for variable fonts, part 4
    
    Support saving/loading font-optical-sizing in Writer. The
    font-optical-sizing property is enabled on the standard style for new
    documents, and for old documents it remains unset for backward
    compatibility.
    
    Add no-op stubs to DOCX, RTF, WW8, and HTML filters.
    
    Change-Id: I87e3de603785dbd588099bfbd72e13e4538fc420
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/200536
    Reviewed-by: Khaled Hosny <[email protected]>
    Tested-by: Jenkins

diff --git a/sw/inc/charatr.hxx b/sw/inc/charatr.hxx
index 3bea7a6d38e7..677088b44d11 100644
--- a/sw/inc/charatr.hxx
+++ b/sw/inc/charatr.hxx
@@ -43,6 +43,7 @@
 #include <editeng/langitem.hxx>
 #include <editeng/colritem.hxx>
 #include <editeng/scripthintitem.hxx>
+#include <editeng/opticalsizingitem.hxx>
 
 // implementation of the character attribute methods of SwAttrSet
 
@@ -112,6 +113,8 @@ inline const SvxCharReliefItem   &SwAttrSet::GetCharRelief( 
bool bInP ) const
     {   return Get( RES_CHRATR_RELIEF, bInP ); }
 inline const SvxCharHiddenItem   &SwAttrSet::GetCharHidden( bool bInP ) const
     {   return Get( RES_CHRATR_HIDDEN, bInP ); }
+inline const SvxOpticalSizingItem&SwAttrSet::GetOpticalSizing(bool bInP) const
+    {   return Get( RES_CHRATR_OPTICAL_SIZING,bInP); }
 
 // implementation of the character attribute methods of SwFormat
 
diff --git a/sw/inc/hintids.hxx b/sw/inc/hintids.hxx
index 423dafdcea08..59f0c2d51672 100644
--- a/sw/inc/hintids.hxx
+++ b/sw/inc/hintids.hxx
@@ -76,6 +76,7 @@ class SvxLanguageItem;
 class SvxLineSpacingItem;
 class SvxNoHyphenItem;
 class SvxOpaqueItem;
+class SvxOpticalSizingItem;
 class SvxOrphansItem;
 class SvxOverlineItem;
 class SvxPaperBinItem;
@@ -244,7 +245,9 @@ inline constexpr TypedWhichId<SfxGrabBagItem> 
RES_CHRATR_GRABBAG(RES_CHRATR_BEGI
 inline constexpr TypedWhichId<SfxInt16Item> 
RES_CHRATR_BIDIRTL(RES_CHRATR_BEGIN + 43);
 inline constexpr TypedWhichId<SfxInt16Item> 
RES_CHRATR_UNUSED3(RES_CHRATR_BEGIN + 44);
 inline constexpr TypedWhichId<SvxScriptHintItem> 
RES_CHRATR_SCRIPT_HINT(RES_CHRATR_BEGIN + 45);
-inline constexpr sal_uInt16 RES_CHRATR_END(RES_CHRATR_BEGIN + 46);
+inline constexpr TypedWhichId<SvxOpticalSizingItem> 
RES_CHRATR_OPTICAL_SIZING(RES_CHRATR_BEGIN
+                                                                              
+ 46);
+inline constexpr sal_uInt16 RES_CHRATR_END(RES_CHRATR_BEGIN + 47);
 
 // this Attribute used only in a TextNodes SwpAttr-Array
 inline constexpr sal_uInt16 RES_TXTATR_BEGIN(RES_CHRATR_END);
diff --git a/sw/inc/swatrset.hxx b/sw/inc/swatrset.hxx
index 67709a849665..af13c71880ca 100644
--- a/sw/inc/swatrset.hxx
+++ b/sw/inc/swatrset.hxx
@@ -52,6 +52,7 @@ class SvxCharRotateItem;
 class SvxCharReliefItem;
 class SvxCharHiddenItem;
 class SvxScriptHintItem;
+class SvxOpticalSizingItem;
 
 // Frame attributes
 class SwFormatFillOrder;
@@ -244,6 +245,7 @@ public:
     inline const SvxCharRotateItem        &GetCharRotate( bool = true ) const;
     inline const SvxCharReliefItem        &GetCharRelief( bool = true ) const;
     inline const SvxCharHiddenItem      &GetCharHidden( bool = true ) const;
+    inline const SvxOpticalSizingItem   &GetOpticalSizing( bool = true ) const;
 
     // Frame attributes. Implementation in frmatr.hxx.
     inline const SwFormatFillOrder       &GetFillOrder( bool = true ) const;
diff --git a/sw/inc/unoprnms.hxx b/sw/inc/unoprnms.hxx
index 3d9270fdb429..e30a28c26581 100644
--- a/sw/inc/unoprnms.hxx
+++ b/sw/inc/unoprnms.hxx
@@ -151,6 +151,7 @@ inline constexpr OUString UNO_NAME_CHAR_PROP_HEIGHT_COMPLEX 
= u"CharPropHeightCo
 inline constexpr OUString UNO_NAME_CHAR_DIFF_HEIGHT_COMPLEX = 
u"CharDiffHeightComplex"_ustr;
 inline constexpr OUString UNO_NAME_CHAR_ESCAPEMENT_HEIGHT = 
u"CharEscapementHeight"_ustr;
 inline constexpr OUString UNO_NAME_CHAR_TRANSPARENCE = 
u"CharTransparence"_ustr;
+inline constexpr OUString UNO_NAME_CHAR_OPTICAL_SIZING = 
u"CharOpticalSizing"_ustr;
 inline constexpr OUString UNO_NAME_HIDE_TAB_LEADER_AND_PAGE_NUMBERS
     = u"HideTabLeaderAndPageNumber"_ustr;
 inline constexpr OUString UNO_NAME_TAB_IN_TOC = u"TabInTOC"_ustr;
diff --git a/sw/qa/extras/odfexport/odfexport.cxx 
b/sw/qa/extras/odfexport/odfexport.cxx
index 153b1a56197e..a97c68702030 100644
--- a/sw/qa/extras/odfexport/odfexport.cxx
+++ b/sw/qa/extras/odfexport/odfexport.cxx
@@ -1558,7 +1558,63 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf60700_directories)
     CPPUNIT_ASSERT_EQUAL(0, nMatches);
 }
 
+// CharOpticalSizing should be enabled by default for new documents
+CPPUNIT_TEST_FIXTURE(Test, testOpticalSizing1)
+{
+    createSwDoc();
+
+    {
+        // It should be true by default for new documents
+        uno::Reference<beans::XPropertySet> xCursor(getRun(getParagraph(1), 
1), uno::UNO_QUERY);
+        CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(xCursor, 
u"CharOpticalSizing"_ustr));
+
+        // and it should survive save-and-reload
+        saveAndReload(TestFilter::ODT);
+        uno::Reference<beans::XPropertySet> xRun(getRun(getParagraph(1), 1), 
uno::UNO_QUERY);
+        CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(xRun, 
u"CharOpticalSizing"_ustr));
+    }
+
+    {
+        // Setting it manually should set it in contents
+        uno::Reference<beans::XPropertySet> xCursor(getRun(getParagraph(1), 
1), uno::UNO_QUERY);
+        xCursor->setPropertyValue(u"CharOpticalSizing"_ustr, uno::Any(true));
+        save(TestFilter::ODT);
+        xmlDocUniquePtr pXmlDoc = parseExport(u"content.xml"_ustr);
+        assertXPath(pXmlDoc, 
"//style:style/style:text-properties[@loext:font-optical-sizing='auto']", 1);
+
+        // and it should survive save-and-reload
+        saveAndReload(TestFilter::ODT);
+        uno::Reference<beans::XPropertySet> xRun(getRun(getParagraph(1), 1), 
uno::UNO_QUERY);
+        CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(xRun, 
u"CharOpticalSizing"_ustr));
+        pXmlDoc = parseExport(u"content.xml"_ustr);
+        assertXPath(pXmlDoc, 
"//style:style/style:text-properties[@loext:font-optical-sizing='auto']", 1);
+    }
 
+    {
+        // It can also be disabled
+        uno::Reference<beans::XPropertySet> xCursor(getRun(getParagraph(1), 
1), uno::UNO_QUERY);
+        xCursor->setPropertyValue(u"CharOpticalSizing"_ustr, uno::Any(false));
+        save(TestFilter::ODT);
+        xmlDocUniquePtr pXmlDoc = parseExport(u"content.xml"_ustr);
+        assertXPath(pXmlDoc, 
"//style:style/style:text-properties[@loext:font-optical-sizing='none']", 1);
+
+        // and it should survive save-and-reload
+        saveAndReload(TestFilter::ODT);
+        uno::Reference<beans::XPropertySet> xRun3(getRun(getParagraph(1), 1), 
uno::UNO_QUERY);
+        CPPUNIT_ASSERT_EQUAL(false, getProperty<bool>(xRun3, 
u"CharOpticalSizing"_ustr));
+        pXmlDoc = parseExport(u"content.xml"_ustr);
+        assertXPath(pXmlDoc, 
"//style:style/style:text-properties[@loext:font-optical-sizing='none']", 1);
+    }
+
+}
+
+// CharOpticalSizing should be disabled by default for old documents
+CPPUNIT_TEST_FIXTURE(Test, testOpticalSizing2)
+{
+    createSwDoc("allow-overlap.odt");
+    uno::Reference<beans::XPropertySet> xCursor(getRun(getParagraph(1), 1), 
uno::UNO_QUERY);
+    CPPUNIT_ASSERT_EQUAL(false, getProperty<bool>(xCursor, 
u"CharOpticalSizing"_ustr));
+}
 } // end of anonymous namespace
 CPPUNIT_PLUGIN_IMPLEMENT();
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/uitest/styleInspector/styleInspector.py 
b/sw/qa/uitest/styleInspector/styleInspector.py
index 70554e5307c1..fec8f514abc0 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(157, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(158, 
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(157, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(158, 
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(157, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(158, 
len(xListBox.getChild('0').getChild('0').getChildren()))
 
             xParDirFormatting = xListBox.getChild('1')
             self.assertEqual(7, len(xParDirFormatting.getChildren()))
@@ -75,7 +75,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(157, len(xParStyle.getChild('0').getChildren()))
+            self.assertEqual(158, len(xParStyle.getChild('0').getChildren()))
             self.assertEqual("Heading  ", 
get_state_as_dict(xParStyle.getChild('1'))['Text'])
             self.assertEqual(28, len(xParStyle.getChild('1').getChildren()))
 
@@ -116,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(157, len(xParStyle.getChild('0').getChildren()))
+            self.assertEqual(158, 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()))
 
@@ -151,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(157, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(158, 
len(xListBox.getChild('0').getChild('0').getChildren()))
             self.assertEqual(0, len(xListBox.getChild('1').getChildren()))
             self.assertEqual(0, len(xListBox.getChild('2').getChildren()))
             self.assertEqual(0, len(xListBox.getChild('3').getChildren()))
@@ -161,7 +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(157, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(158, 
len(xListBox.getChild('0').getChild('0').getChildren()))
 
             xParDirFormatting = xListBox.getChild('1')
             self.assertEqual(1, len(xParDirFormatting.getChildren()))
@@ -214,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(157, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(158, 
len(xListBox.getChild('0').getChild('0').getChildren()))
             self.assertEqual(0, len(xListBox.getChild('1').getChildren()))
             self.assertEqual(0, len(xListBox.getChild('2').getChildren()))
             self.assertEqual(0, len(xListBox.getChild('3').getChildren()))
@@ -224,7 +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(157, 
len(xListBox.getChild('1').getChild('0').getChildren()))
+            self.assertEqual(158, 
len(xListBox.getChild('1').getChild('0').getChildren()))
 
             # Outer bookmark
             xBookmarkFormatting = xListBox.getChild('0')
@@ -271,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(157, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(158, 
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 de29a3be0588..b499dd5bc5e1 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(157, 
len(xListBox.getChild('0').getChild('0').getChildren()))
+            self.assertEqual(158, 
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/bastyp/init.cxx b/sw/source/core/bastyp/init.cxx
index 9ceb9195a425..c7fd89b47244 100644
--- a/sw/source/core/bastyp/init.cxx
+++ b/sw/source/core/bastyp/init.cxx
@@ -55,6 +55,7 @@
 #include <editeng/lspcitem.hxx>
 #include <editeng/nhypitem.hxx>
 #include <editeng/opaqitem.hxx>
+#include <editeng/opticalsizingitem.hxx>
 #include <editeng/orphitem.hxx>
 #include <editeng/paravertalignitem.hxx>
 #include <editeng/pbinitem.hxx>
@@ -332,6 +333,7 @@ std::unique_ptr<ItemInfoPackage> 
createItemInfoPackageSwAttributes()
             { RES_CHRATR_BIDIRTL, new SfxInt16Item( RES_CHRATR_BIDIRTL, 
sal_Int16(-1) ), 0, SFX_ITEMINFOFLAG_NONE },
             { RES_CHRATR_UNUSED3, new SfxVoidItem( RES_CHRATR_UNUSED3 ), 0, 
SFX_ITEMINFOFLAG_NONE },
             { RES_CHRATR_SCRIPT_HINT, new SvxScriptHintItem( 
RES_CHRATR_SCRIPT_HINT ), SID_ATTR_CHAR_SCRIPT_HINT, SFX_ITEMINFOFLAG_NONE },
+            { RES_CHRATR_OPTICAL_SIZING, new SvxOpticalSizingItem( false, 
RES_CHRATR_OPTICAL_SIZING ), SID_ATTR_CHAR_OPTICAL_SIZING, 
SFX_ITEMINFOFLAG_NONE },
 
             { RES_TXTATR_REFMARK, new SwFormatRefMark( SwMarkName() ),  0, 
SFX_ITEMINFOFLAG_NONE },
             { RES_TXTATR_TOXMARK, createSwTOXMarkForItemInfoPackage(),  0, 
SFX_ITEMINFOFLAG_NONE },
diff --git a/sw/source/core/doc/DocumentStylePoolManager.cxx 
b/sw/source/core/doc/DocumentStylePoolManager.cxx
index 4b1667a37f05..17ddfbeb49d3 100644
--- a/sw/source/core/doc/DocumentStylePoolManager.cxx
+++ b/sw/source/core/doc/DocumentStylePoolManager.cxx
@@ -57,6 +57,7 @@
 #include <editeng/charrotateitem.hxx>
 #include <editeng/emphasismarkitem.hxx>
 #include <editeng/scriptspaceitem.hxx>
+#include <editeng/opticalsizingitem.hxx>
 #include <o3tl/unit_conversion.hxx>
 #include <svx/strings.hrc>
 #include <svx/dialmgr.hxx>
diff --git a/sw/source/core/doc/dbgoutsw.cxx b/sw/source/core/doc/dbgoutsw.cxx
index 9b739d2575aa..08049eec18b0 100644
--- a/sw/source/core/doc/dbgoutsw.cxx
+++ b/sw/source/core/doc/dbgoutsw.cxx
@@ -126,6 +126,7 @@ static std::map<sal_uInt16,OUString> & GetItemWhichMap()
         { RES_CHRATR_HIDDEN , "CHRATR_HIDDEN" },
         { RES_CHRATR_BOX , "CHRATR_BOX" },
         { RES_CHRATR_SHADOW , "CHRATR_SHADOW" },
+        { RES_CHRATR_OPTICAL_SIZING , "CHRATR_OPTICAL_SIZING" },
         { RES_TXTATR_AUTOFMT , "TXTATR_AUTOFMT" },
         { RES_TXTATR_INETFMT , "TXTATR_INETFMT" },
         { RES_TXTATR_REFMARK , "TXTATR_REFMARK" },
diff --git a/sw/source/core/inc/swfntcch.hxx b/sw/source/core/inc/swfntcch.hxx
index 43c4c0e43e80..6bb893ca0edb 100644
--- a/sw/source/core/inc/swfntcch.hxx
+++ b/sw/source/core/inc/swfntcch.hxx
@@ -18,7 +18,7 @@
  */
 #pragma once
 
-#define NUM_DEFAULT_VALUES 40
+#define NUM_DEFAULT_VALUES 41
 
 #include "swfont.hxx"
 
diff --git a/sw/source/core/inc/swfont.hxx b/sw/source/core/inc/swfont.hxx
index a99fdb6fc3a6..afe346863acc 100644
--- a/sw/source/core/inc/swfont.hxx
+++ b/sw/source/core/inc/swfont.hxx
@@ -103,6 +103,7 @@ class SwSubFont final : public SvxFont
     inline void SetWordLineMode( const bool bWordLineMode );
     inline void SetEmphasisMark( const FontEmphasisMark eValue );
     inline void SetRelief( const FontRelief eNew );
+    inline void SetOpticalSizing( bool bOpticalSizing );
 
     // methods for sub-/superscript
     inline void SetEscapement( const short nNewEsc );
@@ -235,6 +236,7 @@ public:
     inline void SetFixKerning( const short nNewKern );
     inline void SetCaseMap( const SvxCaseMap eNew );
     inline void SetEmphasisMark( const FontEmphasisMark eValue );
+    inline void SetOpticalSizing( bool bOpticalSizing );
 
     // methods for sub-/superscript
     inline void SetEscapement( const short nNewEsc );
@@ -707,6 +709,20 @@ inline void SwFont::SetEmphasisMark( const 
FontEmphasisMark eValue )
     m_aSub[SwFontScript::CTL].SetEmphasisMark( eValue );
 }
 
+inline void SwSubFont::SetOpticalSizing( bool bOpticalSizing )
+{
+    m_nFontCacheId = nullptr;
+    Font::SetOpticalSizing( bOpticalSizing );
+}
+
+inline void SwFont::SetOpticalSizing( bool bOpticalSizing )
+{
+    m_bFontChg = true;
+    m_aSub[SwFontScript::Latin].SetOpticalSizing( bOpticalSizing );
+    m_aSub[SwFontScript::CJK].SetOpticalSizing( bOpticalSizing );
+    m_aSub[SwFontScript::CTL].SetOpticalSizing( bOpticalSizing );
+}
+
 inline void SwFont::SetPropWidth( const sal_uInt16 nNew )
 {
     if( nNew != m_aSub[SwFontScript::Latin].GetPropWidth() )
diff --git a/sw/source/core/layout/wsfrm.cxx b/sw/source/core/layout/wsfrm.cxx
index f28276de6c93..b3afcb4999cd 100644
--- a/sw/source/core/layout/wsfrm.cxx
+++ b/sw/source/core/layout/wsfrm.cxx
@@ -2622,6 +2622,7 @@ void SwContentFrame::UpdateAttr_( const SfxPoolItem* 
pOld, const SfxPoolItem* pN
         case RES_CHRATR_ESCAPEMENT:
         case RES_CHRATR_CONTOUR:
         case RES_CHRATR_NOHYPHEN:
+        case RES_CHRATR_OPTICAL_SIZING:
         case RES_PARATR_NUMRULE:
             rInvFlags |= SwContentFrameInvFlags::SetCompletePaint;
             break;
diff --git a/sw/source/core/text/atrhndl.hxx b/sw/source/core/text/atrhndl.hxx
index 979b99800124..f4ebc728ce7d 100644
--- a/sw/source/core/text/atrhndl.hxx
+++ b/sw/source/core/text/atrhndl.hxx
@@ -18,7 +18,7 @@
  */
 
 #pragma once
-#define NUM_ATTRIBUTE_STACKS 46
+#define NUM_ATTRIBUTE_STACKS 47
 
 #include <vector>
 #include <swfntcch.hxx>
diff --git a/sw/source/core/text/atrstck.cxx b/sw/source/core/text/atrstck.cxx
index 1d8c0ba479f5..caf108486f09 100644
--- a/sw/source/core/text/atrstck.cxx
+++ b/sw/source/core/text/atrstck.cxx
@@ -44,6 +44,7 @@
 #include <editeng/boxitem.hxx>
 #include <editeng/nhypitem.hxx>
 #include <editeng/shaditem.hxx>
+#include <editeng/opticalsizingitem.hxx>
 #include <viewopt.hxx>
 #include <charfmt.hxx>
 #include <fchrfmt.hxx>
@@ -115,18 +116,19 @@ const sal_uInt8 StackPos[ RES_TXTATR_WITHEND_END - 
RES_CHRATR_BEGIN + 1 ] =
      0, // RES_CHRATR_GRABBAG,                   // 43
      0, // RES_CHRATR_BIDIRTL,                   // 44
      0, // RES_CHRATR_UNUSED3,                   // 45
-    39, // RES_CHRATR_SCRIPT_HINT,               // 46
-    40, // RES_TXTATR_REFMARK,                   // 47
-    41, // RES_TXTATR_TOXMARK,                   // 48
-    42, // RES_TXTATR_META,                      // 49
-    42, // RES_TXTATR_METAFIELD,                 // 50
+    39, // RES_CHRATR_SCRIPT_HINT,               // 45
+    40, // RES_CHRATR_OPTICAL_SIZING             // 46
+    41, // RES_TXTATR_REFMARK,                   // 47
+    42, // RES_TXTATR_TOXMARK,                   // 48
+    43, // RES_TXTATR_META,                      // 49
+    43, // RES_TXTATR_METAFIELD,                 // 50
      0, // RES_TXTATR_AUTOFMT,                   // 51
      0, // RES_TXTATR_INETFMT                    // 52
      0, // RES_TXTATR_CHARFMT,                   // 53
-    43, // RES_TXTATR_CJK_RUBY,                  // 54
+    44, // RES_TXTATR_CJK_RUBY,                  // 54
      0, // RES_TXTATR_UNKNOWN_CONTAINER,         // 55
-    44, // RES_TXTATR_INPUTFIELD                 // 56
-    45, // RES_TXTATR_CONTENTCONTROL             // 57
+    45, // RES_TXTATR_INPUTFIELD                 // 56
+    46, // RES_TXTATR_CONTENTCONTROL             // 57
 };
 
 namespace CharFormat
@@ -831,6 +833,9 @@ void SwAttrHandler::FontChg(const SfxPoolItem& rItem, 
SwFont& rFnt, bool bPush )
                 rFnt.SetVertical(m_pDefaultArray[ nRotateStack 
]->StaticWhichCast(RES_CHRATR_ROTATE).GetValue(), m_bVertLayout);
             break;
         }
+        case RES_CHRATR_OPTICAL_SIZING :
+            rFnt.SetOpticalSizing( 
rItem.StaticWhichCast(RES_CHRATR_OPTICAL_SIZING).GetValue() );
+            break;
         case RES_TXTATR_CJK_RUBY :
             rFnt.SetVertical( 0_deg10, m_bVertLayout );
             break;
diff --git a/sw/source/core/txtnode/swfont.cxx 
b/sw/source/core/txtnode/swfont.cxx
index ef33a4e6bae8..e7cc710827af 100644
--- a/sw/source/core/txtnode/swfont.cxx
+++ b/sw/source/core/txtnode/swfont.cxx
@@ -665,6 +665,8 @@ void SwFont::SetDiffFnt( const SfxItemSet *pAttrSet,
         const SvxTwoLinesItem* pTwoLinesItem = pAttrSet->GetItemIfSet( 
RES_CHRATR_TWO_LINES );
         if( pTwoLinesItem && pTwoLinesItem->GetValue() )
             SetVertical( 0_deg10 );
+        if( const SvxOpticalSizingItem* pItem = pAttrSet->GetItemIfSet( 
RES_CHRATR_OPTICAL_SIZING ) )
+            SetOpticalSizing( pItem->GetValue() );
     }
     else
     {
@@ -854,6 +856,7 @@ SwFont::SwFont( const SwAttrSet* pAttrSet,
         m_aSub[ SwFontScript::CJK ].m_bSmallCapsPercentage66 = true;
         m_aSub[ SwFontScript::CTL ].m_bSmallCapsPercentage66 = true;
     }
+    SetOpticalSizing( pAttrSet->GetOpticalSizing().GetValue() );
 }
 
 SwFont::~SwFont()
diff --git a/sw/source/core/unocore/unomap1.cxx 
b/sw/source/core/unocore/unomap1.cxx
index 184c46aafb98..d73181f1157a 100644
--- a/sw/source/core/unocore/unomap1.cxx
+++ b/sw/source/core/unocore/unomap1.cxx
@@ -206,6 +206,7 @@ std::span<const SfxItemPropertyMapEntry> 
SwUnoPropertyMapProvider::GetCharStyleP
         { UNO_NAME_CHAR_OVERLINE_HAS_COLOR, RES_CHRATR_OVERLINE ,  
cppu::UnoType<bool>::get(),            PROPERTY_NONE, MID_TL_HASCOLOR},
         { UNO_NAME_CHAR_KERNING, RES_CHRATR_KERNING    ,  
cppu::UnoType<sal_Int16>::get()  ,         PROPERTY_NONE,  CONVERT_TWIPS},
         { UNO_NAME_CHAR_NO_HYPHENATION, RES_CHRATR_NOHYPHEN   ,   
cppu::UnoType<bool>::get()  ,       PROPERTY_NONE,     0},
+        { UNO_NAME_CHAR_OPTICAL_SIZING, RES_CHRATR_OPTICAL_SIZING  ,  
cppu::UnoType<bool>::get()  ,       PROPERTY_NONE,     0},
         { UNO_NAME_CHAR_SHADOWED, RES_CHRATR_SHADOWED  ,  
cppu::UnoType<bool>::get()  ,       PROPERTY_NONE, 0},
         { UNO_NAME_CHAR_CONTOURED, RES_CHRATR_CONTOUR,    
cppu::UnoType<bool>::get()  ,       PROPERTY_NONE, 0},
         { UNO_NAME_CHAR_WORD_MODE, 
RES_CHRATR_WORDLINEMODE,cppu::UnoType<bool>::get()  ,    PROPERTY_NONE,     0},
@@ -278,6 +279,7 @@ std::span<const SfxItemPropertyMapEntry>  
SwUnoPropertyMapProvider::GetAutoCharS
         { UNO_NAME_CHAR_OVERLINE_HAS_COLOR, RES_CHRATR_OVERLINE ,  
cppu::UnoType<bool>::get(),              PROPERTY_NONE, MID_TL_HASCOLOR},
         { UNO_NAME_CHAR_KERNING, RES_CHRATR_KERNING    ,  
cppu::UnoType<sal_Int16>::get()  ,         PROPERTY_NONE,  CONVERT_TWIPS},
         { UNO_NAME_CHAR_NO_HYPHENATION, RES_CHRATR_NOHYPHEN   ,   
cppu::UnoType<bool>::get()  ,       PROPERTY_NONE,     0},
+        { UNO_NAME_CHAR_OPTICAL_SIZING, RES_CHRATR_OPTICAL_SIZING  ,  
cppu::UnoType<bool>::get()  ,       PROPERTY_NONE,     0},
         { UNO_NAME_CHAR_SHADOWED, RES_CHRATR_SHADOWED  ,  
cppu::UnoType<bool>::get()  ,       PROPERTY_NONE, 0},
         { UNO_NAME_CHAR_CONTOURED, RES_CHRATR_CONTOUR,    
cppu::UnoType<bool>::get()  ,       PROPERTY_NONE, 0},
         { UNO_NAME_CHAR_WORD_MODE, 
RES_CHRATR_WORDLINEMODE,cppu::UnoType<bool>::get()  ,    PROPERTY_NONE,     0},
diff --git a/sw/source/core/unocore/unomapproperties.hxx 
b/sw/source/core/unocore/unomapproperties.hxx
index 62fa63945d35..015c08af042b 100644
--- a/sw/source/core/unocore/unomapproperties.hxx
+++ b/sw/source/core/unocore/unomapproperties.hxx
@@ -172,6 +172,7 @@
         CTL_FONT_PROPERTIES \
         { UNO_NAME_CHAR_KERNING,                        RES_CHRATR_KERNING,    
        cppu::UnoType<sal_Int16>::get(),         PropertyAttribute::MAYBEVOID, 
CONVERT_TWIPS                          }, \
         { UNO_NAME_CHAR_NO_HYPHENATION,                 RES_CHRATR_NOHYPHEN,   
        cppu::UnoType<bool>::get(),       PropertyAttribute::MAYBEVOID, 0       
                               }, \
+        { UNO_NAME_CHAR_OPTICAL_SIZING,                 
RES_CHRATR_OPTICAL_SIZING,     cppu::UnoType<bool>::get(),       
PropertyAttribute::MAYBEVOID, 0                                      }, \
         { UNO_NAME_CHAR_SHADOWED,                       RES_CHRATR_SHADOWED,   
        cppu::UnoType<bool>::get(),       PropertyAttribute::MAYBEVOID, 0       
                               }, \
         { UNO_NAME_CHAR_CONTOURED,                      RES_CHRATR_CONTOUR,    
        cppu::UnoType<bool>::get(),       PropertyAttribute::MAYBEVOID, 0       
                               }, \
         { UNO_NAME_DROP_CAP_FORMAT,                     RES_PARATR_DROP,       
        cppu::UnoType<css::style::DropCapFormat>::get(),    
PropertyAttribute::MAYBEVOID, MID_DROPCAP_FORMAT     | CONVERT_TWIPS }, \
@@ -446,6 +447,7 @@
                     { UNO_NAME_PARA_FIRST_LINE_INDENT_RELATIVE, 
RES_MARGIN_FIRSTLINE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 
MID_FIRST_LINE_REL_INDENT|CONVERT_TWIPS},\
                     { UNO_NAME_CHAR_KERNING, RES_CHRATR_KERNING    ,  
cppu::UnoType<sal_Int16>::get()  ,         PROPERTY_NONE,  CONVERT_TWIPS},\
                     { UNO_NAME_CHAR_NO_HYPHENATION, RES_CHRATR_NOHYPHEN   ,   
cppu::UnoType<bool>::get()  ,       PROPERTY_NONE,     0},\
+                    { UNO_NAME_CHAR_OPTICAL_SIZING, RES_CHRATR_OPTICAL_SIZING 
,  cppu::UnoType<bool>::get()  ,       PROPERTY_NONE,     0},\
                     { UNO_NAME_CHAR_SHADOWED, RES_CHRATR_SHADOWED  ,  
cppu::UnoType<bool>::get()  ,       PROPERTY_NONE, 0},\
                     { UNO_NAME_CHAR_CONTOURED, RES_CHRATR_CONTOUR,    
cppu::UnoType<bool>::get()  ,       PROPERTY_NONE, 0},\
                     { UNO_NAME_DROP_CAP_FORMAT, RES_PARATR_DROP,        
cppu::UnoType<css::style::DropCapFormat>::get()  , PROPERTY_NONE, 
MID_DROPCAP_FORMAT|CONVERT_TWIPS     },\
diff --git a/sw/source/filter/html/css1atr.cxx 
b/sw/source/filter/html/css1atr.cxx
index 42e884c1b2eb..6f80bb53ff47 100644
--- a/sw/source/filter/html/css1atr.cxx
+++ b/sw/source/filter/html/css1atr.cxx
@@ -3465,6 +3465,7 @@ SwAttrFnTab const aCSS1AttrFnTab = {
 /* RES_CHRATR_BIDIRTL */            nullptr,
 /* RES_CHRATR_UNUSED3 */            nullptr,
 /* RES_CHRATR_SCRIPT_HINT */        nullptr,
+/* RES_CHRATR_OPTICAL_SIZING */     nullptr,
 
 /* RES_TXTATR_REFMARK */            nullptr,
 /* RES_TXTATR_TOXMARK */            nullptr,
diff --git a/sw/source/filter/html/htmlatr.cxx 
b/sw/source/filter/html/htmlatr.cxx
index 1a5076dc1fa3..d917fea69094 100644
--- a/sw/source/filter/html/htmlatr.cxx
+++ b/sw/source/filter/html/htmlatr.cxx
@@ -3316,6 +3316,7 @@ const SwAttrFnTab aHTMLAttrFnTab = {
 /* RES_CHRATR_BIDIRTL */            nullptr,
 /* RES_CHRATR_UNUSED3 */            nullptr,
 /* RES_CHRATR_SCRIPT_HINT */        nullptr,
+/* RES_CHRATR_OPTICAL_SIZING */     nullptr,
 
 /* RES_TXTATR_REFMARK */            nullptr,
 /* RES_TXTATR_TOXMARK */            nullptr,
diff --git a/sw/source/filter/ww8/attributeoutputbase.hxx 
b/sw/source/filter/ww8/attributeoutputbase.hxx
index 2d46bef3842b..2f4000819959 100644
--- a/sw/source/filter/ww8/attributeoutputbase.hxx
+++ b/sw/source/filter/ww8/attributeoutputbase.hxx
@@ -48,6 +48,7 @@ class SvxShadowedItem;
 class SvxUnderlineItem;
 class SvxWeightItem;
 class SvxAutoKernItem;
+class SvxOpticalSizingItem;
 class SvxBlinkItem;
 class SvxBrushItem;
 class XFillStyleItem;
@@ -399,6 +400,9 @@ protected:
     /// Sfx item RES_CHRATR_AUTOKERN
     virtual void CharAutoKern( const SvxAutoKernItem& ) = 0;
 
+    /// Sfx item RES_CHRATR_OPTICAL_SIZING
+    virtual void CharOpticalSizing( const SvxOpticalSizingItem& ) = 0;
+
     /// Sfx item RES_CHRATR_BLINK
     virtual void CharAnimatedText( const SvxBlinkItem& ) = 0;
 
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx 
b/sw/source/filter/ww8/docxattributeoutput.cxx
index 8b6554c83128..2c29bffb408f 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -56,6 +56,7 @@
 #include <oox/export/drawingml.hxx>
 
 #include <editeng/autokernitem.hxx>
+#include <editeng/opticalsizingitem.hxx>
 #include <editeng/unoprnms.hxx>
 #include <editeng/fontitem.hxx>
 #include <editeng/tstpitem.hxx>
@@ -5263,6 +5264,9 @@ void DocxAttributeOutput::OutputDefaultItem(const 
SfxPoolItem& rHt)
         case RES_CHRATR_AUTOKERN:
             bMustWrite = rHt.StaticWhichCast(RES_CHRATR_AUTOKERN).GetValue();
             break;
+        case RES_CHRATR_OPTICAL_SIZING:
+            bMustWrite = 
rHt.StaticWhichCast(RES_CHRATR_OPTICAL_SIZING).GetValue();
+            break;
         case RES_CHRATR_BLINK:
             bMustWrite = rHt.StaticWhichCast(RES_CHRATR_BLINK).GetValue();
             break;
@@ -8400,6 +8404,11 @@ void DocxAttributeOutput::CharAutoKern( const 
SvxAutoKernItem& rAutoKern )
     m_pSerializer->singleElementNS(XML_w, XML_kern, FSNS(XML_w, XML_val), 
sFontSize);
 }
 
+void DocxAttributeOutput::CharOpticalSizing( const SvxOpticalSizingItem& )
+{
+    // MSOffice has no equivalent for optical sizing, so nothing is exported.
+}
+
 void DocxAttributeOutput::CharAnimatedText( const SvxBlinkItem& rBlink )
 {
     if ( rBlink.GetValue() )
diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx 
b/sw/source/filter/ww8/docxattributeoutput.hxx
index d23d191b92b0..6156821d47cf 100644
--- a/sw/source/filter/ww8/docxattributeoutput.hxx
+++ b/sw/source/filter/ww8/docxattributeoutput.hxx
@@ -586,6 +586,9 @@ protected:
     /// Sfx item RES_CHRATR_AUTOKERN
     virtual void CharAutoKern( const SvxAutoKernItem& ) override;
 
+    /// Sfx item RES_CHRATR_OPTICAL_SIZING
+    virtual void CharOpticalSizing( const SvxOpticalSizingItem& ) override;
+
     /// Sfx item RES_CHRATR_BLINK
     virtual void CharAnimatedText( const SvxBlinkItem& rBlink ) override;
 
diff --git a/sw/source/filter/ww8/rtfattributeoutput.cxx 
b/sw/source/filter/ww8/rtfattributeoutput.cxx
index e4051aa159f9..9e1c91879c31 100644
--- a/sw/source/filter/ww8/rtfattributeoutput.cxx
+++ b/sw/source/filter/ww8/rtfattributeoutput.cxx
@@ -49,6 +49,7 @@
 #include <editeng/contouritem.hxx>
 #include <editeng/shdditem.hxx>
 #include <editeng/autokernitem.hxx>
+#include <editeng/opticalsizingitem.hxx>
 #include <editeng/emphasismarkitem.hxx>
 #include <editeng/twolinesitem.hxx>
 #include <editeng/charscaleitem.hxx>
@@ -516,6 +517,9 @@ void RtfAttributeOutput::OutputFormattingItem(const 
SfxPoolItem& item, OStringBu
         case RES_CHRATR_AUTOKERN:
             OutputCharAutoKern(item.StaticWhichCast(RES_CHRATR_AUTOKERN), buf);
             break;
+        case RES_CHRATR_OPTICAL_SIZING:
+            
OutputCharOpticalSizing(item.StaticWhichCast(RES_CHRATR_OPTICAL_SIZING), buf);
+            break;
         case RES_CHRATR_BLINK:
             OutputCharAnimatedText(item.StaticWhichCast(RES_CHRATR_BLINK), 
buf);
             break;
@@ -3205,12 +3209,22 @@ void RtfAttributeOutput::CharAutoKern(const 
SvxAutoKernItem& rAutoKern)
     m_aCharFormatting.Put(rAutoKern);
 }
 
+void RtfAttributeOutput::CharOpticalSizing(const SvxOpticalSizingItem& 
rOpticalSizing)
+{
+    m_aCharFormatting.Put(rOpticalSizing);
+}
+
 void RtfAttributeOutput::OutputCharAutoKern(const SvxAutoKernItem& rAutoKern, 
OStringBuffer& buf)
 {
     buf.append(OOO_STRING_SVTOOLS_RTF_KERNING);
     buf.append(static_cast<sal_Int32>(rAutoKern.GetValue() ? 1 : 0));
 }
 
+void RtfAttributeOutput::OutputCharOpticalSizing(const SvxOpticalSizingItem&, 
OStringBuffer&)
+{
+    // MSOffice has no equivalent for optical sizing, so nothing is exported.
+}
+
 void RtfAttributeOutput::CharAnimatedText(const SvxBlinkItem& rBlink)
 {
     m_aCharFormatting.Put(rBlink);
diff --git a/sw/source/filter/ww8/rtfattributeoutput.hxx 
b/sw/source/filter/ww8/rtfattributeoutput.hxx
index 289773a1477d..8d6aeabe2c5c 100644
--- a/sw/source/filter/ww8/rtfattributeoutput.hxx
+++ b/sw/source/filter/ww8/rtfattributeoutput.hxx
@@ -301,6 +301,9 @@ protected:
     /// Sfx item RES_CHRATR_AUTOKERN
     void CharAutoKern(const SvxAutoKernItem& rAutoKern) override;
 
+    /// Sfx item RES_CHRATR_OPTICAL_SIZING
+    void CharOpticalSizing(const SvxOpticalSizingItem& rOpticalSizing) 
override;
+
     /// Sfx item RES_CHRATR_BLINK
     void CharAnimatedText(const SvxBlinkItem& rBlink) override;
 
@@ -554,6 +557,8 @@ private:
                                   bool assoc) const;
     static void OutputCharWeightAssoc(const SvxWeightItem& rWeight, 
OStringBuffer& buf, bool assoc);
     static void OutputCharAutoKern(const SvxAutoKernItem& rAutoKern, 
OStringBuffer& buf);
+    static void OutputCharOpticalSizing(const SvxOpticalSizingItem& 
rOpticalSizing,
+                                        OStringBuffer& buf);
     static void OutputCharAnimatedText(const SvxBlinkItem& rBlink, 
OStringBuffer& buf);
     void OutputCharBackground(const SvxBrushItem& rBrush, OStringBuffer& buf) 
const;
     static void OutputCharRotate(const SvxCharRotateItem& rRotate, 
OStringBuffer& buf);
diff --git a/sw/source/filter/ww8/ww8atr.cxx b/sw/source/filter/ww8/ww8atr.cxx
index 8a5878dc49e4..6202a36d15e6 100644
--- a/sw/source/filter/ww8/ww8atr.cxx
+++ b/sw/source/filter/ww8/ww8atr.cxx
@@ -64,6 +64,7 @@
 #include <editeng/contouritem.hxx>
 #include <editeng/shdditem.hxx>
 #include <editeng/autokernitem.hxx>
+#include <editeng/opticalsizingitem.hxx>
 #include <editeng/pbinitem.hxx>
 #include <editeng/emphasismarkitem.hxx>
 #include <editeng/twolinesitem.hxx>
@@ -1316,6 +1317,11 @@ void WW8AttributeOutput::CharAutoKern( const 
SvxAutoKernItem& rAutoKern )
     m_rWW8Export.InsUInt16( rAutoKern.GetValue() ? 2 : 0 );
 }
 
+void WW8AttributeOutput::CharOpticalSizing( const SvxOpticalSizingItem& )
+{
+    // MSOffice has no equivalent for optical sizing, so nothing is exported.
+}
+
 void WW8AttributeOutput::CharAnimatedText( const SvxBlinkItem& rBlink )
 {
     m_rWW8Export.InsUInt16( NS_sprm::CSfxText::val );
@@ -5772,6 +5778,9 @@ void AttributeOutputBase::OutputItem( const SfxPoolItem& 
rHt )
         case RES_CHRATR_AUTOKERN:
             CharAutoKern(rHt.StaticWhichCast(RES_CHRATR_AUTOKERN));
             break;
+        case RES_CHRATR_OPTICAL_SIZING:
+            CharOpticalSizing(rHt.StaticWhichCast(RES_CHRATR_OPTICAL_SIZING));
+            break;
         case RES_CHRATR_BLINK:
             CharAnimatedText(rHt.StaticWhichCast(RES_CHRATR_BLINK));
             break;
diff --git a/sw/source/filter/ww8/ww8attributeoutput.hxx 
b/sw/source/filter/ww8/ww8attributeoutput.hxx
index 501959b546d7..7c1ef75cdd06 100644
--- a/sw/source/filter/ww8/ww8attributeoutput.hxx
+++ b/sw/source/filter/ww8/ww8attributeoutput.hxx
@@ -252,6 +252,9 @@ protected:
     /// Sfx item RES_CHRATR_AUTOKERN
     virtual void CharAutoKern( const SvxAutoKernItem& ) override;
 
+    /// Sfx item RES_CHRATR_OPTICAL_SIZING
+    virtual void CharOpticalSizing( const SvxOpticalSizingItem& ) override;
+
     /// Sfx item RES_CHRATR_BLINK
     virtual void CharAnimatedText( const SvxBlinkItem& ) override;
 
diff --git a/sw/source/uibase/app/docshini.cxx 
b/sw/source/uibase/app/docshini.cxx
index 4c24c8706c9c..f7a19b3e72e5 100644
--- a/sw/source/uibase/app/docshini.cxx
+++ b/sw/source/uibase/app/docshini.cxx
@@ -47,6 +47,7 @@
 #include <editeng/orphitem.hxx>
 #include <editeng/widwitem.hxx>
 #include <editeng/hyphenzoneitem.hxx>
+#include <editeng/opticalsizingitem.hxx>
 #include <vcl/metric.hxx>
 #include <vcl/rendercontext/GetDefaultFontFlags.hxx>
 #include <vcl/svapp.hxx>
@@ -302,6 +303,9 @@ bool SwDocShell::InitNew( const uno::Reference < 
embed::XStorage >& xStor )
     //#i16874# AutoKerning as default for new documents
     m_xDoc->SetDefault( SvxAutoKernItem( true, RES_CHRATR_AUTOKERN ) );
 
+    // tdf#153368 Optical sizing as default for new documents
+    m_xDoc->SetDefault( SvxOpticalSizingItem( true, RES_CHRATR_OPTICAL_SIZING 
) );
+
     // #i42080# - Due to the several calls of method <SetDefault(..)>
     // at the document instance, the document is modified. Thus, reset this
     // status here. Note: In method <SubInitNew()> this is also done.
diff --git a/vcl/qa/cppunit/pdfexport/data/testOpticalSizing.odt 
b/vcl/qa/cppunit/pdfexport/data/testOpticalSizing.odt
new file mode 100644
index 000000000000..cb9130423caa
Binary files /dev/null and 
b/vcl/qa/cppunit/pdfexport/data/testOpticalSizing.odt differ
diff --git a/vcl/qa/cppunit/pdfexport/pdfexport.cxx 
b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
index cc42c417c5b5..3789f713dc4e 100644
--- a/vcl/qa/cppunit/pdfexport/pdfexport.cxx
+++ b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
@@ -2167,6 +2167,44 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest, 
testVariableFontPSName2)
 #endif
 }
 
+// This test docuemnt embeds a variable font with opsz axis, and sets the text 
in the same font but
+// different point sizes. The font should be embedded multiple times as 
different instances
+// corresponding to the different opsz values.
+CPPUNIT_TEST_FIXTURE(PdfExportTest, testOpticalSizing)
+{
+// Embedding variable fonts does not work on Linux, only the default instance 
is enumerated
+// https://bugs.documentfoundation.org/show_bug.cgi?id=155853
+#if defined MACOSX || defined _WIN32
+    loadFromFile(u"testOpticalSizing.odt");
+    save(TestFilter::PDF_WRITER);
+
+    vcl::filter::PDFDocument aDocument;
+    SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
+    CPPUNIT_ASSERT(aDocument.Read(aStream));
+
+    std::set<OString> aFontNames;
+    for (const auto& aElement : aDocument.GetElements())
+    {
+        auto pObject = 
dynamic_cast<vcl::filter::PDFObjectElement*>(aElement.get());
+        if (!pObject)
+            continue;
+        auto pType = 
dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Type"_ostr));
+        if (pType && pType->GetValue() == "Font")
+        {
+            auto pName
+                = 
dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("BaseFont"_ostr));
+            aFontNames.insert(pName->GetValue().copy(7)); // skip the subset id
+        }
+    }
+
+    std::set<OString> aExpected{ "Fraunces_144opsz_400wght"_ostr, 
"Fraunces_80opsz_400wght"_ostr,
+                                 "Fraunces_60opsz_400wght"_ostr,  
"Fraunces_40opsz_400wght"_ostr,
+                                 "Fraunces_20opsz_400wght"_ostr,  
"Fraunces-Regular"_ostr };
+
+    CPPUNIT_ASSERT_EQUAL(aExpected, aFontNames);
+#endif
+}
+
 CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf157679)
 {
     // Import the bugdoc and export as PDF.

Reply via email to