cui/source/tabpages/paragrph.cxx                |   20 +-
 editeng/source/items/frmitems.cxx               |  175 ++++++++++++++++++------
 include/editeng/lrspitem.hxx                    |   49 ++++--
 include/editeng/numitem.hxx                     |    5 
 include/vcl/metric.hxx                          |   11 +
 offapi/com/sun/star/util/MeasureUnit.idl        |   12 +
 sax/qa/cppunit/test_converter.cxx               |    2 
 sax/source/tools/converter.cxx                  |    4 
 sw/inc/ndtxt.hxx                                |   12 +
 sw/qa/extras/uiwriter/uiwriter8.cxx             |   21 ++
 sw/source/core/crsr/crstrvl.cxx                 |    2 
 sw/source/core/doc/DocumentStylePoolManager.cxx |   29 ++-
 sw/source/core/doc/docfmt.cxx                   |    3 
 sw/source/core/doc/fmtcol.cxx                   |    7 
 sw/source/core/edit/autofmt.cxx                 |   41 +++--
 sw/source/core/inc/swfont.hxx                   |    4 
 sw/source/core/layout/fly.cxx                   |    3 
 sw/source/core/layout/frmtool.cxx               |    6 
 sw/source/core/text/EnhancedPDFExportHelper.cxx |    2 
 sw/source/core/text/frmcrsr.cxx                 |    9 -
 sw/source/core/text/frmpaint.cxx                |    5 
 sw/source/core/text/itratr.cxx                  |    3 
 sw/source/core/text/itrcrsr.cxx                 |   29 ---
 sw/source/core/text/porfld.cxx                  |   11 -
 sw/source/core/tox/ToxTabStopTokenHandler.cxx   |    6 
 sw/source/core/txtnode/ndtxt.cxx                |   49 ++++--
 sw/source/core/txtnode/swfont.cxx               |   12 +
 sw/source/core/txtnode/thints.cxx               |    4 
 sw/source/filter/html/css1atr.cxx               |    5 
 sw/source/filter/html/htmlatr.cxx               |    6 
 sw/source/filter/html/htmlctxt.cxx              |    5 
 sw/source/filter/html/htmlnumreader.cxx         |    4 
 sw/source/filter/html/svxcss1.cxx               |    3 
 sw/source/filter/html/swhtml.cxx                |   15 +-
 sw/source/filter/ww8/docxattributeoutput.cxx    |    3 
 sw/source/filter/ww8/rtfattributeoutput.cxx     |    6 
 sw/source/filter/ww8/wrtw8nds.cxx               |   22 ++-
 sw/source/filter/ww8/ww8atr.cxx                 |    6 
 sw/source/filter/ww8/ww8graf.cxx                |    4 
 sw/source/filter/ww8/ww8par.cxx                 |   20 +-
 sw/source/filter/ww8/ww8par2.cxx                |    2 
 sw/source/filter/ww8/ww8par3.cxx                |    7 
 sw/source/filter/ww8/ww8par6.cxx                |    9 -
 sw/source/uibase/app/docstyle.cxx               |    8 -
 sw/source/uibase/shells/txtattr.cxx             |    5 
 sw/source/uibase/uiview/viewtab.cxx             |   19 +-
 sw/source/uibase/wrtsh/delete.cxx               |    6 
 vcl/inc/font/FontMetricData.hxx                 |    6 
 vcl/source/font/fontmetric.cxx                  |   42 ++++-
 vcl/source/outdev/font.cxx                      |    4 
 xmloff/source/style/xmlbahdl.cxx                |    4 
 51 files changed, 511 insertions(+), 236 deletions(-)

New commits:
commit d3a59f7a915f9cc0864f2c76ccf90b6cd6fea339
Author:     Jonathan Clark <[email protected]>
AuthorDate: Thu Nov 7 02:23:02 2024 -0700
Commit:     Jonathan Clark <[email protected]>
CommitDate: Wed Nov 13 09:35:20 2024 +0100

    tdf#36709 sw: Writer layout for font-relative first-line indent
    
    This change implements layout for font-relative paragraph first-line
    indentation in Writer.
    
    Change-Id: Ie8f386bcc13a43ab92d5c15654c24bfdfc62bd69
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/176216
    Tested-by: Jenkins
    Reviewed-by: Jonathan Clark <[email protected]>

diff --git a/cui/source/tabpages/paragrph.cxx b/cui/source/tabpages/paragrph.cxx
index 33c3005eebaa..2b262378a361 100644
--- a/cui/source/tabpages/paragrph.cxx
+++ b/cui/source/tabpages/paragrph.cxx
@@ -411,20 +411,26 @@ bool SvxStdParagraphTabPage::FillItemSet( SfxItemSet* 
rOutSet )
 
             if (m_aFLineIndent.IsRelative())
             {
-                item.SetTextFirstLineOffset(rOldItem.GetTextFirstLineOffset(),
+                item.SetTextFirstLineOffset(
+                    rOldItem.GetTextFirstLineOffsetValue(), 
rOldItem.GetTextFirstLineOffsetUnit(),
                     
static_cast<sal_uInt16>(m_aFLineIndent.get_value(FieldUnit::NONE)));
             }
             else
             {
-                
item.SetTextFirstLineOffset(static_cast<sal_uInt16>(m_aFLineIndent.GetCoreValue(eUnit)));
+                // tdf#36709: TODO: Handle font-relative units from GUI
+                item.SetTextFirstLineOffset(
+                    
static_cast<sal_uInt16>(m_aFLineIndent.GetCoreValue(eUnit)),
+                    css::util::MeasureUnit::TWIP);
             }
         }
         else
         {
-            
item.SetTextFirstLineOffset(static_cast<sal_uInt16>(m_aFLineIndent.GetCoreValue(eUnit)));
+            // tdf#36709: TODO: Handle font-relative units from GUI
+            
item.SetTextFirstLineOffset(static_cast<sal_uInt16>(m_aFLineIndent.GetCoreValue(eUnit)),
+                                        css::util::MeasureUnit::TWIP);
         }
         item.SetAutoFirst(m_xAutoCB->get_active());
-        if (item.GetTextFirstLineOffset() < 0)
+        if (item.GetTextFirstLineOffsetValue() < 0)
         {
             bNullTab = true;
         }
@@ -660,16 +666,18 @@ void SvxStdParagraphTabPage::Reset( const SfxItemSet* 
rSet )
             }
             else
             {
+                // tdf#36709: TODO: Handle font-relative units from GUI
                 m_aFLineIndent.SetRelative(false);
                 m_aFLineIndent.set_min(-9999, FieldUnit::NONE);
                 m_aFLineIndent.SetFieldUnit(eFUnit);
-                
m_aFLineIndent.SetMetricValue(rOldFirstLine.GetTextFirstLineOffset(), eUnit);
+                
m_aFLineIndent.SetMetricValue(rOldFirstLine.GetTextFirstLineOffsetValue(), 
eUnit);
             }
             m_xAutoCB->set_active(rOldFirstLine.IsAutoFirst());
         }
         else
         {
-            
m_aFLineIndent.SetMetricValue(rOldFirstLine.GetTextFirstLineOffset(), eUnit);
+            // tdf#36709: TODO: Handle font-relative units from GUI
+            
m_aFLineIndent.SetMetricValue(rOldFirstLine.GetTextFirstLineOffsetValue(), 
eUnit);
             m_xAutoCB->set_active(rOldFirstLine.IsAutoFirst());
         }
         AutoHdl_Impl(*m_xAutoCB);
diff --git a/editeng/source/items/frmitems.cxx 
b/editeng/source/items/frmitems.cxx
index 073ad972e9ac..3f11d48057ac 100644
--- a/editeng/source/items/frmitems.cxx
+++ b/editeng/source/items/frmitems.cxx
@@ -75,6 +75,7 @@
 #include <o3tl/enumrange.hxx>
 #include <o3tl/safeint.hxx>
 #include <sal/log.hxx>
+#include <sax/tools/converter.hxx>
 #include <vcl/GraphicLoader.hxx>
 #include <unotools/securityoptions.hxx>
 #include <docmodel/uno/UnoComplexColor.hxx>
@@ -496,15 +497,6 @@ void SvxLRSpaceItem::SetRight(const tools::Long nR, const 
sal_uInt16 nProp)
     nPropRightMargin = nProp;
 }
 
-void SvxFirstLineIndentItem::SetTextFirstLineOffset(
-    const short nF, const sal_uInt16 nProp)
-{
-    ASSERT_CHANGE_REFCOUNTED_ITEM;
-    m_nFirstLineOffset = short((tools::Long(nF) * nProp ) / 100);
-    m_nUnit = css::util::MeasureUnit::TWIP;
-    m_nPropFirstLineOffset = nProp;
-}
-
 void SvxLRSpaceItem::SetTextFirstLineOffset(const short nF, const sal_uInt16 
nProp)
 {
     // note: left margin contains any negative first line offset - preserve it!
@@ -561,12 +553,17 @@ tools::Long SvxTextLeftMarginItem::GetTextLeft() const
     return m_nTextLeftMargin;
 }
 
-tools::Long SvxTextLeftMarginItem::GetLeft(SvxFirstLineIndentItem const& 
rFirstLine) const
+tools::Long SvxTextLeftMarginItem::GetLeft(const SvxFirstLineIndentItem& 
rFirstLine,
+                                           const SvxFontUnitMetrics& rMetrics) 
const
 {
     // add any negative first line offset to text left margin to get left
-    return (rFirstLine.GetTextFirstLineOffset() < 0)
-        ? m_nTextLeftMargin + rFirstLine.GetTextFirstLineOffset()
-        : m_nTextLeftMargin;
+    if (rFirstLine.GetTextFirstLineOffsetValue() < 0.0)
+    {
+        auto nFirstLineOffset = 
rFirstLine.ResolveTextFirstLineOffset(rMetrics);
+        return m_nTextLeftMargin + nFirstLineOffset;
+    }
+
+    return m_nTextLeftMargin;
 }
 
 tools::Long SvxLRSpaceItem::GetTextLeft() const
@@ -938,10 +935,76 @@ SvxFirstLineIndentItem::SvxFirstLineIndentItem(const 
sal_uInt16 nId)
 {
 }
 
-SvxFirstLineIndentItem::SvxFirstLineIndentItem(const short nFirst, const 
sal_uInt16 nId)
-    : SfxPoolItem(nId, SfxItemType::SvxFirstLineIndentItemType)
-    , m_nFirstLineOffset(nFirst)
+SvxFirstLineIndentItem::SvxFirstLineIndentItem(double dValue, sal_uInt16 nUnit,
+                                               const sal_uInt16 nId)
+    : SvxFirstLineIndentItem(nId)
+{
+    SetTextFirstLineOffset(dValue, nUnit);
+}
+
+bool SvxFirstLineIndentItem::IsAutoFirst() const { return m_bAutoFirst; }
+
+void SvxFirstLineIndentItem::SetAutoFirst(bool bNew)
+{
+    ASSERT_CHANGE_REFCOUNTED_ITEM;
+    m_bAutoFirst = bNew;
+}
+
+void SvxFirstLineIndentItem::SetPropTextFirstLineOffset(sal_uInt16 nProp)
 {
+    ASSERT_CHANGE_REFCOUNTED_ITEM;
+    m_nPropFirstLineOffset = nProp;
+}
+
+sal_uInt16 SvxFirstLineIndentItem::GetPropTextFirstLineOffset() const
+{
+    return m_nPropFirstLineOffset;
+}
+
+void SvxFirstLineIndentItem::SetTextFirstLineOffset(double dValue, sal_Int16 
nUnit,
+                                                    sal_uInt16 nProp)
+{
+    ASSERT_CHANGE_REFCOUNTED_ITEM;
+    m_dFirstLineOffset = dValue;
+    m_nUnit = nUnit;
+    m_nPropFirstLineOffset = nProp;
+
+    if (nProp != 100)
+    {
+        m_dFirstLineOffset = (dValue * static_cast<double>(nProp)) / 100.0;
+    }
+}
+
+double SvxFirstLineIndentItem::GetTextFirstLineOffsetValue() const { return 
m_dFirstLineOffset; }
+
+sal_Int16 SvxFirstLineIndentItem::GetTextFirstLineOffsetUnit() const { return 
m_nUnit; }
+
+double
+SvxFirstLineIndentItem::ResolveTextFirstLineOffsetDouble(const 
SvxFontUnitMetrics& rMetrics) const
+{
+    if(m_nUnit == css::util::MeasureUnit::TWIP)
+        return m_dFirstLineOffset;
+
+    SAL_WARN_IF(!rMetrics.m_bInitialized, "editeng", "font-relative 
indentation lost");
+
+    switch (m_nUnit)
+    {
+        case css::util::MeasureUnit::FONT_EM:
+            return m_dFirstLineOffset * rMetrics.m_dEmTwips;
+
+        case css::util::MeasureUnit::FONT_CJK_ADVANCE:
+            return m_dFirstLineOffset * rMetrics.m_dIcTwips;
+
+        default:
+            SAL_WARN("editeng", "unhandled type conversion");
+            return 0.0;
+    }
+}
+
+sal_Int32
+SvxFirstLineIndentItem::ResolveTextFirstLineOffset(const SvxFontUnitMetrics& 
rMetrics) const
+{
+    return 
static_cast<sal_Int32>(std::llround(ResolveTextFirstLineOffsetDouble(rMetrics)));
 }
 
 bool SvxFirstLineIndentItem::QueryValue(uno::Any& rVal, sal_uInt8 nMemberId) 
const
@@ -956,8 +1019,9 @@ bool SvxFirstLineIndentItem::QueryValue(uno::Any& rVal, 
sal_uInt8 nMemberId) con
             // In practice, these are always stored here in twips.
             if (m_nUnit == css::util::MeasureUnit::TWIP)
             {
-                rVal <<= static_cast<sal_Int32>(bConvert ? 
convertTwipToMm100(m_nFirstLineOffset)
-                                                         : m_nFirstLineOffset);
+                auto nConvOffset
+                    = (bConvert ? convertTwipToMm100(m_dFirstLineOffset) : 
m_dFirstLineOffset);
+                rVal <<= static_cast<sal_Int32>(std::llround(nConvOffset));
                 bRet = true;
             }
         break;
@@ -973,9 +1037,7 @@ bool SvxFirstLineIndentItem::QueryValue(uno::Any& rVal, 
sal_uInt8 nMemberId) con
             // units (e.g. em, ic), and all other units will be pre-converted 
to twips.
             if (m_nUnit != css::util::MeasureUnit::TWIP)
             {
-                rVal <<= css::beans::Pair<double, sal_Int16>{
-                    static_cast<double>(m_nFirstLineOffset), m_nUnit
-                };
+                rVal <<= css::beans::Pair<double, sal_Int16>{ 
m_dFirstLineOffset, m_nUnit };
                 bRet = true;
             }
         break;
@@ -1009,7 +1071,8 @@ bool SvxFirstLineIndentItem::PutValue(const uno::Any& 
rVal, sal_uInt8 nMemberId)
             {
                 return false;
             }
-            m_nFirstLineOffset = bConvert ? o3tl::toTwips(nVal, 
o3tl::Length::mm100) : nVal;
+
+            m_dFirstLineOffset = bConvert ? o3tl::toTwips(nVal, 
o3tl::Length::mm100) : nVal;
             m_nUnit = css::util::MeasureUnit::TWIP;
             m_nPropFirstLineOffset = 100;
             break;
@@ -1035,7 +1098,7 @@ bool SvxFirstLineIndentItem::PutValue(const uno::Any& 
rVal, sal_uInt8 nMemberId)
                 return false;
             }
 
-            m_nFirstLineOffset = stVal.First;
+            m_dFirstLineOffset = stVal.First;
             m_nUnit = stVal.Second;
             m_nPropFirstLineOffset = 100;
             break;
@@ -1057,16 +1120,15 @@ bool SvxFirstLineIndentItem::operator==(const 
SfxPoolItem& rAttr) const
 
     const SvxFirstLineIndentItem& rOther = static_cast<const 
SvxFirstLineIndentItem&>(rAttr);
 
-    return (m_nFirstLineOffset == rOther.GetTextFirstLineOffset()
-        && m_nUnit == rOther.GetTextFirstLineOffsetUnit()
-        && m_nPropFirstLineOffset == rOther.GetPropTextFirstLineOffset()
-        && m_bAutoFirst == rOther.IsAutoFirst());
+    return std::tie(m_dFirstLineOffset, m_nUnit, m_nPropFirstLineOffset, 
m_bAutoFirst)
+           == std::tie(rOther.m_dFirstLineOffset, rOther.m_nUnit, 
rOther.m_nPropFirstLineOffset,
+                       rOther.m_bAutoFirst);
 }
 
 size_t SvxFirstLineIndentItem::hashCode() const
 {
     std::size_t seed(0);
-    o3tl::hash_combine(seed, m_nFirstLineOffset);
+    o3tl::hash_combine(seed, m_dFirstLineOffset);
     o3tl::hash_combine(seed, m_nUnit);
     o3tl::hash_combine(seed, m_nPropFirstLineOffset);
     o3tl::hash_combine(seed, m_bAutoFirst);
@@ -1095,10 +1157,15 @@ bool SvxFirstLineIndentItem::GetPresentation
                 rText += unicode::formatPercent(m_nPropFirstLineOffset,
                     Application::GetSettings().GetUILanguageTag());
             }
+            else if (m_nUnit != css::util::MeasureUnit::TWIP)
+            {
+                OUStringBuffer stBuf;
+                sax::Converter::convertMeasureUnit(stBuf, m_dFirstLineOffset, 
m_nUnit);
+                rText += stBuf.makeStringAndClear();
+            }
             else
             {
-                rText += 
GetMetricText(static_cast<tools::Long>(m_nFirstLineOffset),
-                                       eCoreUnit, ePresUnit, &rIntl);
+                rText += GetMetricText(m_dFirstLineOffset, eCoreUnit, 
ePresUnit, &rIntl);
             }
             return true;
         }
@@ -1110,11 +1177,16 @@ bool SvxFirstLineIndentItem::GetPresentation
                 rText += unicode::formatPercent(m_nPropFirstLineOffset,
                             Application::GetSettings().GetUILanguageTag());
             }
+            else if (m_nUnit != css::util::MeasureUnit::TWIP)
+            {
+                OUStringBuffer stBuf;
+                sax::Converter::convertMeasureUnit(stBuf, m_dFirstLineOffset, 
m_nUnit);
+                rText += stBuf.makeStringAndClear();
+            }
             else
             {
-                rText += 
GetMetricText(static_cast<tools::Long>(m_nFirstLineOffset),
-                                       eCoreUnit, ePresUnit, &rIntl)
-                    + " " + EditResId(GetMetricId(ePresUnit));
+                rText += GetMetricText(m_dFirstLineOffset, eCoreUnit, 
ePresUnit, &rIntl) + " "
+                         + EditResId(GetMetricId(ePresUnit));
             }
             return true;
         }
@@ -1126,7 +1198,8 @@ bool SvxFirstLineIndentItem::GetPresentation
 void SvxFirstLineIndentItem::ScaleMetrics(tools::Long const nMult, tools::Long 
const nDiv)
 {
     ASSERT_CHANGE_REFCOUNTED_ITEM;
-    m_nFirstLineOffset = static_cast<short>(BigInt::Scale(m_nFirstLineOffset, 
nMult, nDiv));
+    m_dFirstLineOffset
+        = (m_dFirstLineOffset * static_cast<double>(nMult)) / 
static_cast<double>(nDiv);
 }
 
 bool SvxFirstLineIndentItem::HasMetrics() const
@@ -1138,7 +1211,8 @@ void SvxFirstLineIndentItem::dumpAsXml(xmlTextWriterPtr 
pWriter) const
 {
     (void)xmlTextWriterStartElement(pWriter, 
BAD_CAST("SvxFirstLineIndentItem"));
     (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), 
BAD_CAST(OString::number(Which()).getStr()));
-    (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nFirstLineOffset"), 
BAD_CAST(OString::number(m_nFirstLineOffset).getStr()));
+    (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_dFirstLineOffset"),
+                                      
BAD_CAST(OString::number(m_dFirstLineOffset).getStr()));
     (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nUnit"), 
BAD_CAST(OString::number(m_nUnit).getStr()));
     (void)xmlTextWriterWriteAttribute(pWriter, 
BAD_CAST("m_nPropFirstLineOffset"), 
BAD_CAST(OString::number(m_nPropFirstLineOffset).getStr()));
     (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_bAutoFirst"), 
BAD_CAST(OString::number(int(m_bAutoFirst)).getStr()));
@@ -1151,13 +1225,34 @@ boost::property_tree::ptree 
SvxFirstLineIndentItem::dumpAsJSON() const
 
     boost::property_tree::ptree aState;
 
-    MapUnit eTargetUnit = MapUnit::MapInch;
+    switch (m_nUnit)
+    {
+        case css::util::MeasureUnit::TWIP:
+        {
+            MapUnit eTargetUnit = MapUnit::MapInch;
 
-    OUString sFirstline = GetMetricText(GetTextFirstLineOffset(),
-                        MapUnit::MapTwip, eTargetUnit, nullptr);
+            OUString sFirstline
+                = GetMetricText(m_dFirstLineOffset, MapUnit::MapTwip, 
eTargetUnit, nullptr);
 
-    aState.put("firstline", sFirstline);
-    aState.put("unit", "inch");
+            aState.put("firstline", sFirstline);
+            aState.put("unit", "inch");
+        }
+        break;
+
+        case css::util::MeasureUnit::FONT_EM:
+            aState.put("firstline", m_dFirstLineOffset);
+            aState.put("unit", "em");
+            break;
+
+        case css::util::MeasureUnit::FONT_CJK_ADVANCE:
+            aState.put("firstline", m_dFirstLineOffset);
+            aState.put("unit", "ic");
+            break;
+
+        default:
+            SAL_WARN("editeng", "unhandled type conversion");
+            break;
+    }
 
     aTree.push_back(std::make_pair("state", aState));
 
diff --git a/include/editeng/lrspitem.hxx b/include/editeng/lrspitem.hxx
index 63bff2c974ca..e4f0e8a3dbf7 100644
--- a/include/editeng/lrspitem.hxx
+++ b/include/editeng/lrspitem.hxx
@@ -47,6 +47,22 @@ SetTextLeft SetTextFirst GetLeft GetTextLeft  GetTextFirst  
(What?)
 
 class SvxFirstLineIndentItem;
 
+/// helper struct used for resolving font-relative indentation
+struct SvxFontUnitMetrics
+{
+    double m_dEmTwips = 0.0;
+    double m_dIcTwips = 0.0;
+    bool m_bInitialized = false;
+
+    SvxFontUnitMetrics() = default;
+    SvxFontUnitMetrics(double dEmTwips, double dIcTwips)
+        : m_dEmTwips(dEmTwips)
+        , m_dIcTwips(dIcTwips)
+        , m_bInitialized(true)
+    {
+    }
+};
+
 /// GetLeft() - for everything that's not applied to a paragraph
 class EDITENG_DLLPUBLIC SvxLeftMarginItem final : public SfxPoolItem
 {
@@ -100,7 +116,8 @@ public:
     //TODO: need this?
     //void SetLeft(SvxFirstLineIndentItem const& rFirstLine, const tools::Long 
nL, const sal_uInt16 nProp = 100);
     /// get left margin without negative first-line indent
-    tools::Long GetLeft(SvxFirstLineIndentItem const& rFirstLine) const;
+    tools::Long GetLeft(const SvxFirstLineIndentItem& rFirstLine,
+                        const SvxFontUnitMetrics& rMetrics) const;
     sal_uInt16 GetPropLeft() const { return m_nPropLeftMargin; }
 
     void SetTextLeft(const tools::Long nL, const sal_uInt16 nProp = 100);
@@ -136,33 +153,27 @@ class EDITENG_DLLPUBLIC SvxFirstLineIndentItem final : 
public SfxPoolItem
 {
 private:
     /// First-line indent always relative to GetTextLeft()
-    short m_nFirstLineOffset = 0;
+    double m_dFirstLineOffset = 0.0;
     sal_Int16 m_nUnit = css::util::MeasureUnit::TWIP;
     sal_uInt16 m_nPropFirstLineOffset = 100;
     /// Automatic calculation of the first line indent
     bool m_bAutoFirst = false;
 
 public:
-    bool IsAutoFirst()  const { return m_bAutoFirst; }
-    void SetAutoFirst(const bool bNew) { ASSERT_CHANGE_REFCOUNTED_ITEM; 
m_bAutoFirst = bNew; }
+    bool IsAutoFirst() const;
+    void SetAutoFirst(const bool bNew);
 
-    void SetTextFirstLineOffset(const short nF, const sal_uInt16 nProp = 100);
-    short GetTextFirstLineOffset() const { return m_nFirstLineOffset; }
-    double GetTextFirstLineOffsetDouble() const { return m_nFirstLineOffset; }
-    sal_Int16 GetTextFirstLineOffsetUnit() const { return m_nUnit; }
-    void SetPropTextFirstLineOffset(const sal_uInt16 nProp)
-                    { ASSERT_CHANGE_REFCOUNTED_ITEM; m_nPropFirstLineOffset = 
nProp; }
-    sal_uInt16 GetPropTextFirstLineOffset() const
-                    { return m_nPropFirstLineOffset; }
-    void SetTextFirstLineOffsetValue(const short nValue)
-    {
-        ASSERT_CHANGE_REFCOUNTED_ITEM;
-        m_nFirstLineOffset = nValue;
-        m_nUnit = css::util::MeasureUnit::TWIP;
-    }
+    void SetPropTextFirstLineOffset(sal_uInt16 nProp);
+    sal_uInt16 GetPropTextFirstLineOffset() const;
+
+    void SetTextFirstLineOffset(double dValue, sal_Int16 nUnit, sal_uInt16 
nProp = 100);
+    double GetTextFirstLineOffsetValue() const;
+    sal_Int16 GetTextFirstLineOffsetUnit() const;
+    double ResolveTextFirstLineOffsetDouble(const SvxFontUnitMetrics& 
rMetrics) const;
+    sal_Int32 ResolveTextFirstLineOffset(const SvxFontUnitMetrics& rMetrics) 
const;
 
     explicit SvxFirstLineIndentItem(const sal_uInt16 nId);
-    SvxFirstLineIndentItem(const short nOffset, const sal_uInt16 nId);
+    SvxFirstLineIndentItem(double dValue, sal_uInt16 nUnit, const sal_uInt16 
nId);
     SvxFirstLineIndentItem(SvxFirstLineIndentItem const &) = default; // 
SfxPoolItem copy function dichotomy
 
     // "pure virtual Methods" from SfxPoolItem
diff --git a/include/editeng/numitem.hxx b/include/editeng/numitem.hxx
index 423a0e2aeda3..c6067c53cb40 100644
--- a/include/editeng/numitem.hxx
+++ b/include/editeng/numitem.hxx
@@ -26,6 +26,7 @@
 #include <editeng/numdef.hxx>
 #include <tools/color.hxx>
 #include <com/sun/star/style/NumberingType.hpp>
+#include <com/sun/star/util/MeasureUnit.hpp>
 #include <unotools/fontcvt.hxx>
 #include <editeng/editengdllapi.h>
 #include <o3tl/typed_flags_set.hxx>
@@ -129,6 +130,7 @@ private:
     SvxNumPositionAndSpaceMode mePositionAndSpaceMode;
 
     sal_Int32           nFirstLineOffset;   // First line indent
+    sal_Int16 nFirstLineOffsetUnit = css::util::MeasureUnit::TWIP;
     sal_Int32           nAbsLSpace;         // Distance Border<->Number
     short               nCharTextDistance;  // Distance Number<->Text
 
@@ -139,6 +141,7 @@ private:
     tools::Long                mnListtabPos;
     // specifies the first line indent
     tools::Long                mnFirstLineIndent;
+    sal_Int16 mnFirstLineIndentUnit = css::util::MeasureUnit::TWIP;
     // specifies the indent before the text, e.g. in L2R-layout the left margin
     tools::Long                mnIndentAt;
 
@@ -210,6 +213,7 @@ public:
     sal_Int32       GetAbsLSpace() const;
     void            SetFirstLineOffset(sal_Int32 nSet) { nFirstLineOffset = 
nSet;}
     sal_Int32       GetFirstLineOffset() const;
+    sal_Int16 GetFirstLineOffsetUnit() const { return nFirstLineOffsetUnit; }
     void            SetCharTextDistance(short nSet) { nCharTextDistance = 
nSet; }
     short           GetCharTextDistance() const;
 
@@ -220,6 +224,7 @@ public:
     tools::Long GetListtabPos() const { return mnListtabPos;}
     void SetFirstLineIndent( const tools::Long nFirstLineIndent );
     tools::Long GetFirstLineIndent() const { return mnFirstLineIndent;}
+    sal_Int16 GetFirstLineIndentUnit() const { return mnFirstLineIndentUnit; }
     void SetIndentAt( const tools::Long nIndentAt );
     tools::Long GetIndentAt() const { return mnIndentAt;}
 
diff --git a/include/vcl/metric.hxx b/include/vcl/metric.hxx
index bc38cb227256..0f9942e3992f 100644
--- a/include/vcl/metric.hxx
+++ b/include/vcl/metric.hxx
@@ -49,6 +49,10 @@ public:
     sal_Int32           GetBulletOffset() const                     { return 
mnBulletOffset; }
     sal_Int32           GetHangingBaseline() const                  { return 
mnHangingBaseline; }
 
+    double GetUnitEm() const { return mdEmSize; }
+    double GetHorCJKAdvance() const { return mdHorCJKAdvanceSize; }
+    double GetVertCJKAdvance() const { return mdVertCJKAdvanceSize; }
+
     void                SetAscent( sal_Int32 nAscent )                   { 
mnAscent = nAscent; }
     void                SetDescent( sal_Int32 nDescent )                 { 
mnDescent = nDescent; }
     void                SetExternalLeading( sal_Int32 nExtLeading )      { 
mnExtLeading = nExtLeading; }
@@ -58,6 +62,10 @@ public:
     void                SetBulletOffset( sal_Int32 nOffset )             { 
mnBulletOffset = nOffset; }
     void                SetHangingBaseline( sal_Int32 nBaseline )        { 
mnHangingBaseline = nBaseline; }
 
+    void SetUnitEm(double dValue) { mdEmSize = dValue; }
+    void SetHorCJKAdvance(double dValue) { mdHorCJKAdvanceSize = dValue; }
+    void SetVertCJKAdvance(double dValue) { mdVertCJKAdvanceSize = dValue; }
+
     bool                IsFullstopCentered() const                  { return 
mbFullstopCentered; }
 
     void                SetFullstopCenteredFlag( bool bCentered )   { 
mbFullstopCentered = bCentered; }
@@ -85,6 +93,9 @@ private:
     sal_Int32           mnSlant;                       // Slant
     sal_Int32           mnBulletOffset;                // Offset for 
non-printing character
     sal_Int32           mnHangingBaseline;             // Offset from Romn 
baseline to hanging baseline.
+    double mdEmSize; // Size of an 'em' unit
+    double mdHorCJKAdvanceSize; // Size of an 'ic' unit in horizontal layout
+    double mdVertCJKAdvanceSize; // Size of an 'ic' unit in vertical layout
 
     bool                mbFullstopCentered;
 };
diff --git a/offapi/com/sun/star/util/MeasureUnit.idl 
b/offapi/com/sun/star/util/MeasureUnit.idl
index 2b9a93a6e12a..ef3f52de9a64 100644
--- a/offapi/com/sun/star/util/MeasureUnit.idl
+++ b/offapi/com/sun/star/util/MeasureUnit.idl
@@ -84,11 +84,17 @@ published constants MeasureUnit
     /** all measures for this component are in SYSFONT */
     const short SYSFONT = 18;
 
-    /** all measures for this component are in em relative to the font */
+    /** all measures for this component are in em relative to the font
+
+        @since LibreOffice 25.2
+     */
     const short FONT_EM = 19;
 
-    /** all measures for this component are in ideographic advances relative 
to the font */
-    const short FONT_IC = 20;
+    /** all measures for this component are in ic, per the CSS definition
+
+        @since LibreOffice 25.2
+     */
+    const short FONT_CJK_ADVANCE = 20;
 
 };
 
diff --git a/sax/qa/cppunit/test_converter.cxx 
b/sax/qa/cppunit/test_converter.cxx
index 47bd5b9d412d..65eb7f0acfb3 100644
--- a/sax/qa/cppunit/test_converter.cxx
+++ b/sax/qa/cppunit/test_converter.cxx
@@ -660,7 +660,7 @@ void ConverterTest::testConvertMeasureUnit()
     fnFromStr("5000%", 5000.0, MeasureUnit::PERCENT, true);
     fnFromStr("5000cm", 5000.0, MeasureUnit::CM, true);
     fnFromStr("5000em", 5000.0, MeasureUnit::FONT_EM, true);
-    fnFromStr("5000ic", 5000.0, MeasureUnit::FONT_IC, true);
+    fnFromStr("5000ic", 5000.0, MeasureUnit::FONT_CJK_ADVANCE, true);
     fnFromStr("5000in", 5000.0, MeasureUnit::INCH, true);
     fnFromStr("5000mm", 5000.0, MeasureUnit::MM, true);
     fnFromStr("5000pt", 5000.0, MeasureUnit::POINT, true);
diff --git a/sax/source/tools/converter.cxx b/sax/source/tools/converter.cxx
index 0c1613a32a89..61557575350b 100644
--- a/sax/source/tools/converter.cxx
+++ b/sax/source/tools/converter.cxx
@@ -76,7 +76,7 @@ const std::map<sal_Int16, std::string_view> 
stConvertMeasureUnitStrMap{
     { MeasureUnit::INCH, gpsINCH },      { MeasureUnit::POINT, gpsPT },
     { MeasureUnit::PICA, gpsPC },        { MeasureUnit::PERCENT, gpsPERCENT },
     { MeasureUnit::PIXEL, gpsPX },       { MeasureUnit::FONT_EM, gpsFONT_EM },
-    { MeasureUnit::FONT_IC, gpsFONT_IC }
+    { MeasureUnit::FONT_CJK_ADVANCE, gpsFONT_IC }
 };
 
 o3tl::Length Measure2O3tlUnit(sal_Int16 nUnit)
@@ -159,7 +159,7 @@ template <class V> static std::optional<sal_Int16> 
lcl_parseMeasureUnit(const V&
 
         case u'i':
             if (wordEndsWith(rString.substr(1), "c"))
-                return MeasureUnit::FONT_IC;
+                return MeasureUnit::FONT_CJK_ADVANCE;
             if (wordEndsWith(rString.substr(1), "n"))
                 return MeasureUnit::INCH;
             break;
diff --git a/sw/inc/ndtxt.hxx b/sw/inc/ndtxt.hxx
index c77e91a89371..48d9d287df50 100644
--- a/sw/inc/ndtxt.hxx
+++ b/sw/inc/ndtxt.hxx
@@ -43,6 +43,7 @@ class SwNumRule;
 class SwNodeNum;
 class SvxFirstLineIndentItem;
 class SvxTextLeftMarginItem;
+struct SvxFontUnitMetrics;
 class SwXParagraph;
 
 namespace utl {
@@ -530,13 +531,18 @@ public:
        Returns the combined first line indent of this text node and
        its numbering.
 
-       @param the first line indent of this text node taking the
-               numbering into account (return parameter)
+       @param rFirstOffset
+       the first line indent of this text node taking the numbering into
+       account (return parameter)
+
+       @param rMetrics
+       helper structure containing font metrics, used for resolving font-
+       relative indentation
 
        @retval true   this node has SwNodeNum and has numbering rule
        @retval false  else
      */
-    bool GetFirstLineOfsWithNum( short& rFirstOffset ) const;
+    bool GetFirstLineOfsWithNum( short& rFirstOffset, const 
SvxFontUnitMetrics& rMetrics ) const;
 
     SwTwips GetAdditionalIndentForStartingNewList() const;
 
diff --git a/sw/qa/extras/uiwriter/uiwriter8.cxx 
b/sw/qa/extras/uiwriter/uiwriter8.cxx
index 109970c1cf6e..df3be99f9037 100644
--- a/sw/qa/extras/uiwriter/uiwriter8.cxx
+++ b/sw/qa/extras/uiwriter/uiwriter8.cxx
@@ -991,8 +991,11 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest8, 
testInsertAutoTextIntoListFromParaStyle)
         CPPUNIT_ASSERT_EQUAL(pNumRule, rNode.GetNumRule());
         CPPUNIT_ASSERT_EQUAL(pTextLeftMargin->GetTextLeft(),
                              rNode.GetAttr(RES_MARGIN_TEXTLEFT).GetTextLeft());
-        CPPUNIT_ASSERT_EQUAL(pFirstLineIndent->GetTextFirstLineOffset(),
-                             
rNode.GetAttr(RES_MARGIN_FIRSTLINE).GetTextFirstLineOffset());
+        CPPUNIT_ASSERT_DOUBLES_EQUAL(
+            pFirstLineIndent->GetTextFirstLineOffsetValue(),
+            rNode.GetAttr(RES_MARGIN_FIRSTLINE).GetTextFirstLineOffsetValue(), 
0.01);
+        CPPUNIT_ASSERT_EQUAL(pFirstLineIndent->GetTextFirstLineOffsetUnit(),
+                             
rNode.GetAttr(RES_MARGIN_FIRSTLINE).GetTextFirstLineOffsetUnit());
     }
 
     pWrtShell->FwdPara();
@@ -1017,8 +1020,11 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest8, 
testInsertAutoTextIntoListFromParaStyle)
         CPPUNIT_ASSERT_EQUAL(pNumRule, rNode.GetNumRule());
         CPPUNIT_ASSERT_EQUAL(pTextLeftMargin->GetTextLeft(),
                              rNode.GetAttr(RES_MARGIN_TEXTLEFT).GetTextLeft());
-        CPPUNIT_ASSERT_EQUAL(pFirstLineIndent->GetTextFirstLineOffset(),
-                             
rNode.GetAttr(RES_MARGIN_FIRSTLINE).GetTextFirstLineOffset());
+        CPPUNIT_ASSERT_DOUBLES_EQUAL(
+            pFirstLineIndent->GetTextFirstLineOffsetValue(),
+            rNode.GetAttr(RES_MARGIN_FIRSTLINE).GetTextFirstLineOffsetValue(), 
0.01);
+        CPPUNIT_ASSERT_EQUAL(pFirstLineIndent->GetTextFirstLineOffsetUnit(),
+                             
rNode.GetAttr(RES_MARGIN_FIRSTLINE).GetTextFirstLineOffsetUnit());
     }
 
     pWrtShell->FwdPara();
@@ -1043,8 +1049,11 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest8, 
testInsertAutoTextIntoListFromParaStyle)
         CPPUNIT_ASSERT_EQUAL(pNumRule, rNode.GetNumRule());
         CPPUNIT_ASSERT_EQUAL(pTextLeftMargin->GetTextLeft(),
                              rNode.GetAttr(RES_MARGIN_TEXTLEFT).GetTextLeft());
-        CPPUNIT_ASSERT_EQUAL(pFirstLineIndent->GetTextFirstLineOffset(),
-                             
rNode.GetAttr(RES_MARGIN_FIRSTLINE).GetTextFirstLineOffset());
+        CPPUNIT_ASSERT_DOUBLES_EQUAL(
+            pFirstLineIndent->GetTextFirstLineOffsetValue(),
+            rNode.GetAttr(RES_MARGIN_FIRSTLINE).GetTextFirstLineOffsetValue(), 
0.01);
+        CPPUNIT_ASSERT_EQUAL(pFirstLineIndent->GetTextFirstLineOffsetUnit(),
+                             
rNode.GetAttr(RES_MARGIN_FIRSTLINE).GetTextFirstLineOffsetUnit());
     }
 }
 
diff --git a/sw/source/core/crsr/crstrvl.cxx b/sw/source/core/crsr/crstrvl.cxx
index dcdfc4118851..e187159bc7d3 100644
--- a/sw/source/core/crsr/crstrvl.cxx
+++ b/sw/source/core/crsr/crstrvl.cxx
@@ -2438,7 +2438,7 @@ bool SwCursorShell::SetShadowCursorPos( const Point& rPt, 
SwFillMode eFillMode )
                     RES_MARGIN_FIRSTLINE, RES_MARGIN_TEXTLEFT> 
aSet(GetDoc()->GetAttrPool());
             SvxFirstLineIndentItem 
firstLine(pCNd->GetAttr(RES_MARGIN_FIRSTLINE));
             SvxTextLeftMarginItem 
leftMargin(pCNd->GetAttr(RES_MARGIN_TEXTLEFT));
-            firstLine.SetTextFirstLineOffset(0);
+            firstLine.SetTextFirstLineOffset(0.0, 
css::util::MeasureUnit::TWIP);
             leftMargin.SetTextLeft(aFPos.nTabCnt);
             aSet.Put(firstLine);
             aSet.Put(leftMargin);
diff --git a/sw/source/core/doc/DocumentStylePoolManager.cxx 
b/sw/source/core/doc/DocumentStylePoolManager.cxx
index 09e7efe6f09c..7df2dc58ebfd 100644
--- a/sw/source/core/doc/DocumentStylePoolManager.cxx
+++ b/sw/source/core/doc/DocumentStylePoolManager.cxx
@@ -212,8 +212,8 @@ namespace
                 {
                     SvxFirstLineIndentItem 
firstLine(pColl->GetFormatAttr(RES_MARGIN_FIRSTLINE));
                     SvxTextLeftMarginItem 
leftMargin(pColl->GetFormatAttr(RES_MARGIN_TEXTLEFT));
-                    
firstLine.SetTextFirstLineOffsetValue(rNFormat.GetFirstLineOffset());
-                        //TODO: overflow
+                    
firstLine.SetTextFirstLineOffset(rNFormat.GetFirstLineOffset(),
+                                                     
rNFormat.GetFirstLineOffsetUnit());
                     leftMargin.SetTextLeft(rNFormat.GetAbsLSpace());
                     pColl->SetFormatAttr(firstLine);
                     pColl->SetFormatAttr(leftMargin);
@@ -232,7 +232,7 @@ namespace
                             bool bHeader, bool bTab )
     {
         sal_uInt16 nLeft = o3tl::convert(5 * nFact, o3tl::Length::mm, 
o3tl::Length::twip);
-        SvxFirstLineIndentItem const firstLine(0, RES_MARGIN_FIRSTLINE);
+        SvxFirstLineIndentItem const firstLine(RES_MARGIN_FIRSTLINE);
         SvxTextLeftMarginItem const leftMargin(nLeft, RES_MARGIN_TEXTLEFT);
         rSet.Put(firstLine);
         rSet.Put(leftMargin);
@@ -257,7 +257,8 @@ namespace
                             sal_uInt16 nNxt, SwTwips nEZ, SwTwips nLeft,
                             SwTwips nUpper, SwTwips nLower )
     {
-        SvxFirstLineIndentItem const firstLine(sal_uInt16(nEZ), 
RES_MARGIN_FIRSTLINE);
+        SvxFirstLineIndentItem firstLine(nEZ, css::util::MeasureUnit::TWIP, 
RES_MARGIN_FIRSTLINE);
+
         SvxTextLeftMarginItem const leftMargin(sal_uInt16(nLeft), 
RES_MARGIN_TEXTLEFT);
         rSet.Put(firstLine);
         rSet.Put(leftMargin);
@@ -710,7 +711,8 @@ SwTextFormatColl* 
DocumentStylePoolManager::GetTextCollFromPool( sal_uInt16 nId,
         case RES_POOLCOLL_TEXT_IDENT:           // Text body indentation
             {
                 auto const first(o3tl::convert(5, o3tl::Length::mm, 
o3tl::Length::twip));
-                SvxFirstLineIndentItem const firstLine(first, 
RES_MARGIN_FIRSTLINE);
+                SvxFirstLineIndentItem const firstLine(first, 
css::util::MeasureUnit::TWIP,
+                                                       RES_MARGIN_FIRSTLINE);
                 SvxTextLeftMarginItem const leftMargin(0, RES_MARGIN_TEXTLEFT);
                 aSet.Put(firstLine);
                 aSet.Put(leftMargin);
@@ -720,7 +722,8 @@ SwTextFormatColl* 
DocumentStylePoolManager::GetTextCollFromPool( sal_uInt16 nId,
             {
                 auto const first(-o3tl::convert(5, o3tl::Length::mm, 
o3tl::Length::twip));
                 auto const left(o3tl::convert(1, o3tl::Length::cm, 
o3tl::Length::twip));
-                SvxFirstLineIndentItem const firstLine(first, 
RES_MARGIN_FIRSTLINE);
+                SvxFirstLineIndentItem const firstLine(first, 
css::util::MeasureUnit::TWIP,
+                                                       RES_MARGIN_FIRSTLINE);
                 SvxTextLeftMarginItem const leftMargin(left, 
RES_MARGIN_TEXTLEFT);
                 aSet.Put(firstLine);
                 aSet.Put(leftMargin);
@@ -733,7 +736,7 @@ SwTextFormatColl* 
DocumentStylePoolManager::GetTextCollFromPool( sal_uInt16 nId,
         case RES_POOLCOLL_TEXT_MOVE:            // Text body move
             {
                 auto const left(o3tl::convert(5, o3tl::Length::mm, 
o3tl::Length::twip));
-                SvxFirstLineIndentItem const firstLine(0, 
RES_MARGIN_FIRSTLINE);
+                SvxFirstLineIndentItem const firstLine(RES_MARGIN_FIRSTLINE);
                 SvxTextLeftMarginItem const leftMargin(left, 
RES_MARGIN_TEXTLEFT);
                 aSet.Put(firstLine);
                 aSet.Put(leftMargin);
@@ -744,7 +747,8 @@ SwTextFormatColl* 
DocumentStylePoolManager::GetTextCollFromPool( sal_uInt16 nId,
             {
                 auto const first(-o3tl::convert(45, o3tl::Length::mm, 
o3tl::Length::twip));
                 auto const left(o3tl::convert(5, o3tl::Length::cm, 
o3tl::Length::twip));
-                SvxFirstLineIndentItem const firstLine(first, 
RES_MARGIN_FIRSTLINE);
+                SvxFirstLineIndentItem const firstLine(first, 
css::util::MeasureUnit::TWIP,
+                                                       RES_MARGIN_FIRSTLINE);
                 SvxTextLeftMarginItem const leftMargin(left, 
RES_MARGIN_TEXTLEFT);
                 aSet.Put(firstLine);
                 aSet.Put(leftMargin);
@@ -757,7 +761,7 @@ SwTextFormatColl* 
DocumentStylePoolManager::GetTextCollFromPool( sal_uInt16 nId,
         case RES_POOLCOLL_MARGINAL:         // Text body marginal
             {
                 auto const left(o3tl::convert(4, o3tl::Length::cm, 
o3tl::Length::twip));
-                SvxFirstLineIndentItem const firstLine(0, 
RES_MARGIN_FIRSTLINE);
+                SvxFirstLineIndentItem const firstLine(RES_MARGIN_FIRSTLINE);
                 SvxTextLeftMarginItem const leftMargin(left, 
RES_MARGIN_TEXTLEFT);
                 aSet.Put(firstLine);
                 aSet.Put(leftMargin);
@@ -952,7 +956,8 @@ SwTextFormatColl* 
DocumentStylePoolManager::GetTextCollFromPool( sal_uInt16 nId,
             {
                 auto const first(-o3tl::convert(6, o3tl::Length::mm, 
o3tl::Length::twip));
                 auto const left(o3tl::convert(6, o3tl::Length::mm, 
o3tl::Length::twip));
-                SvxFirstLineIndentItem const firstLine(first, 
RES_MARGIN_FIRSTLINE);
+                SvxFirstLineIndentItem const firstLine(first, 
css::util::MeasureUnit::TWIP,
+                                                       RES_MARGIN_FIRSTLINE);
                 SvxTextLeftMarginItem const leftMargin(left, 
RES_MARGIN_TEXTLEFT);
                 aSet.Put(firstLine);
                 aSet.Put(leftMargin);
@@ -1025,7 +1030,7 @@ SwTextFormatColl* 
DocumentStylePoolManager::GetTextCollFromPool( sal_uInt16 nId,
                 aSet.Put(rightMargin);
 
                 // First line indent
-                aSet.Put(SvxFirstLineIndentItem(0, RES_MARGIN_FIRSTLINE));
+                aSet.Put(SvxFirstLineIndentItem(RES_MARGIN_FIRSTLINE));
 
                 // Added as part of tdf#159531
                 // Top/bottom spacing (i.e. Above/Below paragraph spacing)
@@ -1394,7 +1399,7 @@ SwTextFormatColl* 
DocumentStylePoolManager::GetTextCollFromPool( sal_uInt16 nId,
             {
                 auto const left(o3tl::convert(1, o3tl::Length::cm, 
o3tl::Length::twip));
                 auto const right(o3tl::convert(1, o3tl::Length::cm, 
o3tl::Length::twip));
-                SvxFirstLineIndentItem const firstLine(0, 
RES_MARGIN_FIRSTLINE);
+                SvxFirstLineIndentItem const firstLine(RES_MARGIN_FIRSTLINE);
                 SvxTextLeftMarginItem const leftMargin(left, 
RES_MARGIN_TEXTLEFT);
                 SvxRightMarginItem const rightMargin(right, RES_MARGIN_RIGHT);
                 aSet.Put(firstLine);
diff --git a/sw/source/core/doc/docfmt.cxx b/sw/source/core/doc/docfmt.cxx
index 0c48355c6417..adec26e403c4 100644
--- a/sw/source/core/doc/docfmt.cxx
+++ b/sw/source/core/doc/docfmt.cxx
@@ -1706,7 +1706,8 @@ void SwDoc::MoveLeftMargin(const SwPaM& rPam, bool 
bRight, bool bModulus,
                             }
                             if (indents & ::sw::ListLevelIndents::FirstLine)
                             {
-                                
firstLine.SetTextFirstLineOffset(static_cast<short>(rFormat.GetFirstLineIndent()));
+                                
firstLine.SetTextFirstLineOffset(rFormat.GetFirstLineIndent(),
+                                                                 
rFormat.GetFirstLineIndentUnit());
                             }
                         }
                     }
diff --git a/sw/source/core/doc/fmtcol.cxx b/sw/source/core/doc/fmtcol.cxx
index e6c4c16310a4..a7d47a8681fe 100644
--- a/sw/source/core/doc/fmtcol.cxx
+++ b/sw/source/core/doc/fmtcol.cxx
@@ -251,10 +251,11 @@ void SwTextFormatColl::SwClientNotify(const SwModify& 
rModify, const SfxHint& rH
             // We had a relative value -> recalculate
             if (100 != pOldFirstLineIndent->GetPropTextFirstLineOffset())
             {
-                const short nOld = 
pOldFirstLineIndent->GetTextFirstLineOffset();
-                
aNew.SetTextFirstLineOffset(pNewFirstLineIndent->GetTextFirstLineOffset(),
+                const double dOld = 
pOldFirstLineIndent->GetTextFirstLineOffsetValue();
+                
aNew.SetTextFirstLineOffset(pNewFirstLineIndent->GetTextFirstLineOffsetValue(),
+                                            
pNewFirstLineIndent->GetTextFirstLineOffsetUnit(),
                                             
pOldFirstLineIndent->GetPropTextFirstLineOffset());
-                bChg = nOld != aNew.GetTextFirstLineOffset();
+                bChg = dOld != aNew.GetTextFirstLineOffsetValue();
             }
             if( bChg )
             {
diff --git a/sw/source/core/edit/autofmt.cxx b/sw/source/core/edit/autofmt.cxx
index 092fa86fa4f3..d875b161fa67 100644
--- a/sw/source/core/edit/autofmt.cxx
+++ b/sw/source/core/edit/autofmt.cxx
@@ -1482,7 +1482,14 @@ void SwAutoFormat::BuildEnum( sal_uInt16 nLvl, 
sal_uInt16 nDigitLevel )
 
         SwTextFrameInfo aInfo( m_pCurTextFrame );
         nLeftTextPos = aInfo.GetCharPos(nPos);
-        nLeftTextPos -= 
m_pCurTextFrame->GetTextNodeForParaProps()->GetSwAttrSet().GetTextLeftMargin().GetLeft(m_pCurTextFrame->GetTextNodeForParaProps()->GetSwAttrSet().GetFirstLineIndent());
+
+        nLeftTextPos -= m_pCurTextFrame->GetTextNodeForParaProps()
+                            ->GetSwAttrSet()
+                            .GetTextLeftMargin()
+                            .GetLeft(m_pCurTextFrame->GetTextNodeForParaProps()
+                                         ->GetSwAttrSet()
+                                         .GetFirstLineIndent(),
+                                     /*metrics*/ {});
     }
 
     if( m_bMoreLines )
@@ -2505,9 +2512,14 @@ SwAutoFormat::SwAutoFormat( SwEditShell* pEdShell, 
SvxSwAutoFormatFlags aFlags,
                     SvxTextLeftMarginItem const*const pTextLeftMargin(
                         m_pCurTextFrame->GetTextNodeForParaProps()
                             
->GetSwAttrSet().GetItemIfSet(RES_MARGIN_TEXTLEFT));
-                    short nSz(pFirstLineIndent ? 
pFirstLineIndent->GetTextFirstLineOffset() : 0);
-                    if (0 != nSz ||
-                        (pTextLeftMargin && 0 != 
pTextLeftMargin->GetTextLeft()))
+
+                    // Unit conversion is not needed here: check the sign only
+                    double dIndentValue = 0.0;
+                    if (pFirstLineIndent)
+                        dIndentValue = 
pFirstLineIndent->GetTextFirstLineOffsetValue();
+
+                    if (0.0 != dIndentValue
+                        || (pTextLeftMargin && 0 != 
pTextLeftMargin->GetTextLeft()))
                     {
                         // exception: numbering/enumeration can have an 
indentation
                         if (IsEnumericChar(*m_pCurTextFrame))
@@ -2526,9 +2538,9 @@ SwAutoFormat::SwAutoFormat( SwEditShell* pEdShell, 
SvxSwAutoFormatFlags aFlags,
                         if( bReplaceStyles )
                         {
                             // then use one of our templates
-                            if( 0 < nSz )           // positive 1st line 
indentation
+                            if (0.0 < dIndentValue) // positive 1st line 
indentation
                                 BuildIndent();
-                            else if( 0 > nSz )      // negative 1st line 
indentation
+                            else if (0.0 > dIndentValue) // negative 1st line 
indentation
                                 BuildNegIndent( aFInfo.GetLineStart() );
                             else if (pTextLeftMargin && 
pTextLeftMargin->GetTextLeft() != 0)   // is indentation
                                 BuildTextIndent();
@@ -2744,15 +2756,20 @@ SwAutoFormat::SwAutoFormat( SwEditShell* pEdShell, 
SvxSwAutoFormatFlags aFlags,
                     SvxTextLeftMarginItem const*const pTextLeftMargin(
                         m_pCurTextFrame->GetTextNodeForParaProps()
                             ->GetSwAttrSet().GetItemIfSet(RES_MARGIN_TEXTLEFT, 
false));
-                    short nSz(pFirstLineIndent ? 
pFirstLineIndent->GetTextFirstLineOffset() : 0);
-                    if( bReplaceStyles &&
-                        (0 != nSz ||
-                            (pTextLeftMargin && 0 != 
pTextLeftMargin->GetTextLeft())))
+
+                    // Unit conversino is not needed here: check the sign only
+                    double dIndentValue = 0.0;
+                    if (pFirstLineIndent)
+                        dIndentValue = 
pFirstLineIndent->GetTextFirstLineOffsetValue();
+
+                    if (bReplaceStyles
+                        && (0.0 != dIndentValue
+                            || (pTextLeftMargin && 0 != 
pTextLeftMargin->GetTextLeft())))
                     {
                         // then use one of our templates
-                        if( 0 < nSz )           // positive 1st line 
indentation
+                        if (0.0 < dIndentValue) // positive 1st line 
indentation
                             BuildIndent();
-                        else if( 0 > nSz )      // negative 1st line 
indentation
+                        else if (0.0 > dIndentValue) // negative 1st line 
indentation
                         {
                             BuildNegIndent( aFInfo.GetLineStart() );
                         }
diff --git a/sw/source/core/inc/swfont.hxx b/sw/source/core/inc/swfont.hxx
index 6e4aafef7cc9..87f6492754ac 100644
--- a/sw/source/core/inc/swfont.hxx
+++ b/sw/source/core/inc/swfont.hxx
@@ -28,6 +28,7 @@
 #include <swtypes.hxx>
 #include "drawfont.hxx"
 #include <editeng/borderline.hxx>
+#include <editeng/lrspitem.hxx>
 #include <optional>
 #include <o3tl/enumarray.hxx>
 
@@ -427,6 +428,9 @@ public:
         const SvxShadowItemSide nShadow, const bool bVertLayout, const bool 
bVertLayoutLRBT,
         const bool bSkipLeft, const bool bSkipRight ) const;
 
+    // Extract metrics for font-relative unit conversion
+    SvxFontUnitMetrics GetFontUnitMetrics() const;
+
     void dumpAsXml( xmlTextWriterPtr writer ) const;
 };
 
diff --git a/sw/source/core/layout/fly.cxx b/sw/source/core/layout/fly.cxx
index e33617ef8f31..9a91f98d75a1 100644
--- a/sw/source/core/layout/fly.cxx
+++ b/sw/source/core/layout/fly.cxx
@@ -3021,8 +3021,9 @@ static SwTwips lcl_CalcAutoWidth( const SwLayoutFrame& 
rFrame )
             SvxRightMarginItem const& rRightMargin(rParaSet.GetRightMargin());
             if (!static_cast<const SwTextFrame*>(pFrame)->IsLocked())
             {
+                // tdf#36709: TODO: Handle font-relative first-line indent
                 nMin += rRightMargin.GetRight() + rLeftMargin.GetTextLeft()
-                        + rFirstLine.GetTextFirstLineOffset();
+                        + rFirstLine.ResolveTextFirstLineOffset({});
             }
         }
         else if ( pFrame->IsTabFrame() )
diff --git a/sw/source/core/layout/frmtool.cxx 
b/sw/source/core/layout/frmtool.cxx
index 3f6afd46e5ef..1cf9fac80564 100644
--- a/sw/source/core/layout/frmtool.cxx
+++ b/sw/source/core/layout/frmtool.cxx
@@ -2353,7 +2353,8 @@ tools::Long SwBorderAttrs::CalcRight( const SwFrame* 
pCaller ) const
     {
         if (pCaller->IsRightToLeft())
         {
-            nRight += m_pTextLeftMargin->GetLeft(*m_pFirstLineIndent);
+            // tdf#36709: TODO: Handle font-relative units
+            nRight += m_pTextLeftMargin->GetLeft(*m_pFirstLineIndent, 
/*metrics*/ {});
         }
         else
         {
@@ -2408,7 +2409,8 @@ tools::Long SwBorderAttrs::CalcLeft( const SwFrame 
*pCaller ) const
     {
         if (pCaller->IsTextFrame())
         {
-            nLeft += m_pTextLeftMargin->GetLeft(*m_pFirstLineIndent);
+            // tdf#36709: TODO: Handle font-relative units
+            nLeft += m_pTextLeftMargin->GetLeft(*m_pFirstLineIndent, 
/*metrics*/ {});
         }
         else
             nLeft += m_xLR->GetLeft();
diff --git a/sw/source/core/text/EnhancedPDFExportHelper.cxx 
b/sw/source/core/text/EnhancedPDFExportHelper.cxx
index 81173d209c3e..ee221ccb66bf 100644
--- a/sw/source/core/text/EnhancedPDFExportHelper.cxx
+++ b/sw/source/core/text/EnhancedPDFExportHelper.cxx
@@ -832,7 +832,7 @@ void SwTaggedPDFHelper::SetAttributes( 
vcl::PDFWriter::StructElement eType )
             OSL_ENSURE( pFrame->IsTextFrame(), "Frame type <-> tag attribute 
mismatch" );
             const SvxFirstLineIndentItem& rFirstLine(
                 static_cast<const 
SwTextFrame*>(pFrame)->GetTextNodeForParaProps()->GetSwAttrSet().GetFirstLineIndent());
-            nVal = rFirstLine.GetTextFirstLineOffset();
+            nVal = rFirstLine.ResolveTextFirstLineOffset({});
             if ( 0 != nVal )
                 mpPDFExtOutDevData->SetStructureAttributeNumerical( 
vcl::PDFWriter::TextIndent, nVal );
         }
diff --git a/sw/source/core/text/frmcrsr.cxx b/sw/source/core/text/frmcrsr.cxx
index 882573f7cb96..0c845b0150a7 100644
--- a/sw/source/core/text/frmcrsr.cxx
+++ b/sw/source/core/text/frmcrsr.cxx
@@ -216,7 +216,8 @@ bool SwTextFrame::GetCharRect( SwRect& rOrig, const 
SwPosition &rPos,
         Point aPnt1 = pFrame->getFrameArea().Pos() + 
pFrame->getFramePrintArea().Pos();
         SwTextNode const*const pTextNd(GetTextNodeForParaProps());
         short nFirstOffset;
-        pTextNd->GetFirstLineOfsWithNum( nFirstOffset );
+        // tdf#36709: TODO: Handle font-relative units
+        pTextNd->GetFirstLineOfsWithNum(nFirstOffset, {});
 
         Point aPnt2;
         if ( aRectFnSet.IsVert() )
@@ -1482,8 +1483,10 @@ void SwTextFrame::FillCursorPos( SwFillData& rFill ) 
const
             if( nFirst && nDiff > -1 )
                 rRect.Top( rRect.Top() + nFirst );
             rRect.Height( nLineHeight );
-            SwTwips nLeft = rFill.Left() + rTextLeftMargin.GetLeft(rFirstLine) 
+
-                            GetTextNodeForParaProps()->GetLeftMarginWithNum();
+
+            // tdf#36709: TODO: Handle font-relative units
+            SwTwips nLeft = rFill.Left() + rTextLeftMargin.GetLeft(rFirstLine, 
/*metrics*/ {})
+                            + 
GetTextNodeForParaProps()->GetLeftMarginWithNum();
             SwTwips nRight = rFill.Right() - rRightMargin.GetRight();
             SwTwips nCenter = ( nLeft + nRight ) / 2;
             rRect.Left( nLeft );
diff --git a/sw/source/core/text/frmpaint.cxx b/sw/source/core/text/frmpaint.cxx
index 85d4bb03828e..c39ea7b7395f 100644
--- a/sw/source/core/text/frmpaint.cxx
+++ b/sw/source/core/text/frmpaint.cxx
@@ -575,9 +575,10 @@ bool SwTextFrame::PaintEmpty( const SwRect &rRect, bool 
bCheck ) const
                 const SvxFirstLineIndentItem& rFirstLine(
                     
GetTextNodeForParaProps()->GetSwAttrSet().GetFirstLineIndent());
 
-                if (0 < rFirstLine.GetTextFirstLineOffset())
+                // tdf#36709: TODO: Handle font-relative first-line indentation
+                if (0.0 < rFirstLine.GetTextFirstLineOffsetValue())
                 {
-                    aPos.AdjustX(rFirstLine.GetTextFirstLineOffset());
+                    aPos.AdjustX(rFirstLine.ResolveTextFirstLineOffset({}));
                 }
 
                 std::unique_ptr<SwSaveClip, o3tl::default_delete<SwSaveClip>> 
xClip;
diff --git a/sw/source/core/text/itratr.cxx b/sw/source/core/text/itratr.cxx
index ae38bda0bab3..c7d03b80b9f9 100644
--- a/sw/source/core/text/itratr.cxx
+++ b/sw/source/core/text/itratr.cxx
@@ -1085,7 +1085,8 @@ void SwTextNode::GetMinMaxSize( SwNodeOffset nIndex, 
sal_uLong& rMin, sal_uLong
     tools::Long nLROffset = rTextLeftMargin.GetTextLeft() + 
GetLeftMarginWithNum( true );
     short nFLOffs;
     // For enumerations a negative first line indentation is probably filled 
already
-    if( !GetFirstLineOfsWithNum( nFLOffs ) || nFLOffs > nLROffset )
+    // tdf#36709: TODO: Handle font-relative units
+    if (!GetFirstLineOfsWithNum(nFLOffs, {}) || nFLOffs > nLROffset)
         nLROffset = nFLOffs;
 
     SwMinMaxNodeArgs aNodeArgs;
diff --git a/sw/source/core/text/itrcrsr.cxx b/sw/source/core/text/itrcrsr.cxx
index c393154777a0..6da4d23ce54d 100644
--- a/sw/source/core/text/itrcrsr.cxx
+++ b/sw/source/core/text/itrcrsr.cxx
@@ -161,6 +161,8 @@ void SwTextMargin::CtorInitTextMargin( SwTextFrame 
*pNewFrame, SwTextSizeInfo *p
     GetInfo().SetFont( GetFnt() );
     const SwTextNode *const pNode = m_pFrame->GetTextNodeForParaProps();
 
+    auto stMetrics = GetFnt()->GetFontUnitMetrics();
+
     SvxFirstLineIndentItem const& 
rFirstLine(pNode->GetSwAttrSet().GetFirstLineIndent());
     SvxTextLeftMarginItem const& 
rTextLeftMargin(pNode->GetSwAttrSet().GetTextLeftMargin());
     // #i95907#
@@ -192,7 +194,7 @@ void SwTextMargin::CtorInitTextMargin( SwTextFrame 
*pNewFrame, SwTextSizeInfo *p
                 // #i95907#
                 // #i111284#
                 // rSpace.GetLeft() + rSpace.GetTextLeft();
-                (rTextLeftMargin.GetLeft(rFirstLine) - 
rTextLeftMargin.GetTextLeft());
+                (rTextLeftMargin.GetLeft(rFirstLine, stMetrics) - 
rTextLeftMargin.GetTextLeft());
     }
     else
     {
@@ -208,7 +210,7 @@ void SwTextMargin::CtorInitTextMargin( SwTextFrame 
*pNewFrame, SwTextSizeInfo *p
                     pNode->GetLeftMarginWithNum() -
                     // #i95907#
                     // #i111284#
-                    (rTextLeftMargin.GetLeft(rFirstLine) - 
rTextLeftMargin.GetTextLeft());
+                    (rTextLeftMargin.GetLeft(rFirstLine, stMetrics) - 
rTextLeftMargin.GetTextLeft());
         }
         else
         {
@@ -239,8 +241,7 @@ void SwTextMargin::CtorInitTextMargin( SwTextFrame 
*pNewFrame, SwTextSizeInfo *p
     {
         short nFLOfst = 0;
         tools::Long nFirstLineOfs = 0;
-        if( !pNode->GetFirstLineOfsWithNum( nFLOfst ) &&
-            rFirstLine.IsAutoFirst())
+        if (!pNode->GetFirstLineOfsWithNum(nFLOfst, stMetrics) && 
rFirstLine.IsAutoFirst())
         {
             nFirstLineOfs = GetFnt()->GetSize( GetFnt()->GetActual() 
).Height();
             LanguageType const aLang = m_pFrame->GetLangOfChar(
@@ -299,26 +300,6 @@ void SwTextMargin::CtorInitTextMargin( SwTextFrame 
*pNewFrame, SwTextSizeInfo *p
                 }
             }
         }
-        else if (!pNode->GetFirstLineOfsWithNum(nFLOfst)
-                 && rFirstLine.GetTextFirstLineOffsetUnit() != 
css::util::MeasureUnit::TWIP)
-        {
-            auto nFntHeight = 
GetFnt()->GetSize(GetFnt()->GetActual()).Height();
-
-            // tdf#36709: TODO: Complete and consolidate unit conversion code
-            switch (rFirstLine.GetTextFirstLineOffsetUnit())
-            {
-                case css::util::MeasureUnit::FONT_IC:
-                case css::util::MeasureUnit::FONT_EM:
-                    nFirstLineOfs
-                        = 
static_cast<tools::Long>(static_cast<double>(nFntHeight)
-                                                   * 
rFirstLine.GetTextFirstLineOffsetDouble());
-                    break;
-
-                default:
-                    nFirstLineOfs = 0;
-                    break;
-            }
-        }
         else
             nFirstLineOfs = nFLOfst;
 
diff --git a/sw/source/core/text/porfld.cxx b/sw/source/core/text/porfld.cxx
index a955d3d72b65..36cbdbce162d 100644
--- a/sw/source/core/text/porfld.cxx
+++ b/sw/source/core/text/porfld.cxx
@@ -604,11 +604,14 @@ bool SwNumberPortion::Format( SwTextFormatInfo &rInf )
                 (IsFootnoteNumPortion() &&
                  
rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::NO_GAP_AFTER_NOTE_NUMBER)))
             {
+                // tdf#36709: TODO: Handle font-relative first line indentation
                 nDiff = rInf.Left()
-                    + rInf.GetTextFrame()->GetTextNodeForParaProps()->
-                        
GetSwAttrSet().GetFirstLineIndent().GetTextFirstLineOffset()
-                    - rInf.First()
-                    + rInf.ForcedLeftMargin();
+                        + rInf.GetTextFrame()
+                              ->GetTextNodeForParaProps()
+                              ->GetSwAttrSet()
+                              .GetFirstLineIndent()
+                              .ResolveTextFirstLineOffset({})
+                        - rInf.First() + rInf.ForcedLeftMargin();
             }
             else
             {
diff --git a/sw/source/core/tox/ToxTabStopTokenHandler.cxx 
b/sw/source/core/tox/ToxTabStopTokenHandler.cxx
index 2cdcc358c971..3ebedfec05a3 100644
--- a/sw/source/core/tox/ToxTabStopTokenHandler.cxx
+++ b/sw/source/core/tox/ToxTabStopTokenHandler.cxx
@@ -114,8 +114,10 @@ auto DefaultToxTabStopTokenHandler::CalcEndStop(SwTextNode 
const& rNode,
             rNode.GetTextColl()->GetFirstLineIndent());
         SvxTextLeftMarginItem const& rTextLeftMargin(
             rNode.GetTextColl()->GetTextLeftMargin());
-        nRightMargin -= rTextLeftMargin.GetLeft(rFirstLine);
-        nRightMargin -= rFirstLine.GetTextFirstLineOffset();
+
+        // tdf#36709: TODO: Handle font-relative units
+        nRightMargin -= rTextLeftMargin.GetLeft(rFirstLine, /*metrics*/ {});
+        nRightMargin -= rFirstLine.ResolveTextFirstLineOffset(/*metrics*/ {});
     }
     return nRightMargin - 1; // subtract 1 twip to avoid equal for 
TabOverMargin
 }
diff --git a/sw/source/core/txtnode/ndtxt.cxx b/sw/source/core/txtnode/ndtxt.cxx
index bc965b3ad7fc..edb760a33ba9 100644
--- a/sw/source/core/txtnode/ndtxt.cxx
+++ b/sw/source/core/txtnode/ndtxt.cxx
@@ -3311,7 +3311,8 @@ tools::Long SwTextNode::GetLeftMarginWithNum( bool 
bTextLeft ) const
             if( pRule->IsAbsSpaces() )
             {
                 SvxFirstLineIndentItem const& 
rFirst(GetSwAttrSet().GetFirstLineIndent());
-                nRet = nRet - 
GetSwAttrSet().GetTextLeftMargin().GetLeft(rFirst);
+                // tdf#36709: TODO: Handle font-relative units
+                nRet = nRet - 
GetSwAttrSet().GetTextLeftMargin().GetLeft(rFirst, /*metrics*/ {});
             }
         }
         else if ( rFormat.GetPositionAndSpaceMode() == 
SvxNumberFormat::LABEL_ALIGNMENT )
@@ -3324,27 +3325,29 @@ tools::Long SwTextNode::GetLeftMarginWithNum( bool 
bTextLeft ) const
             // list/paragraph items. (this is rather inelegant)
             SvxFirstLineIndentItem 
firstLine(GetSwAttrSet().GetFirstLineIndent());
             SvxTextLeftMarginItem 
leftMargin(GetSwAttrSet().GetTextLeftMargin());
-            nRet = bTextLeft
-                ? - leftMargin.GetTextLeft()
-                : - leftMargin.GetLeft(firstLine);
+            // tdf#36709: TODO: Handle font-relative units
+            nRet = bTextLeft ? -leftMargin.GetTextLeft()
+                             : -leftMargin.GetLeft(firstLine, /*metrics*/ {});
             if (indents & ::sw::ListLevelIndents::LeftMargin)
             {
                 leftMargin.SetTextLeft(rFormat.GetIndentAt());
             }
             if (indents & ::sw::ListLevelIndents::FirstLine)
             {
-                firstLine.SetTextFirstLineOffset(rFormat.GetFirstLineIndent());
+                firstLine.SetTextFirstLineOffset(rFormat.GetFirstLineIndent(),
+                                                 
rFormat.GetFirstLineIndentUnit());
             }
-            nRet += bTextLeft
-                ? leftMargin.GetTextLeft()
-                : leftMargin.GetLeft(firstLine);
+            // tdf#36709: TODO: Handle font-relative units
+            nRet += bTextLeft ? leftMargin.GetTextLeft()
+                              : leftMargin.GetLeft(firstLine, /*metrics*/ {});
         }
     }
 
     return nRet;
 }
 
-bool SwTextNode::GetFirstLineOfsWithNum( short& rFLOffset ) const
+bool SwTextNode::GetFirstLineOfsWithNum(short& rFLOffset,
+                                        const SvxFontUnitMetrics& rMetrics) 
const
 {
     // #i95907#
     rFLOffset = 0;
@@ -3363,7 +3366,7 @@ bool SwTextNode::GetFirstLineOfsWithNum( short& rFLOffset 
) const
                 if 
(!getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING))
                 {
                     SvxFirstLineIndentItem const 
aItem(GetSwAttrSet().GetFirstLineIndent());
-                    rFLOffset = rFLOffset + aItem.GetTextFirstLineOffset();
+                    rFLOffset = rFLOffset + 
aItem.ResolveTextFirstLineOffset(rMetrics);
                 }
             }
             else if ( rFormat.GetPositionAndSpaceMode() == 
SvxNumberFormat::LABEL_ALIGNMENT )
@@ -3375,7 +3378,7 @@ bool SwTextNode::GetFirstLineOfsWithNum( short& rFLOffset 
) const
                 else if 
(!getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING))
                 {
                     SvxFirstLineIndentItem const 
aItem(GetSwAttrSet().GetFirstLineIndent());
-                    rFLOffset = aItem.GetTextFirstLineOffset();
+                    rFLOffset = aItem.ResolveTextFirstLineOffset(rMetrics);
                 }
             }
         }
@@ -3383,7 +3386,7 @@ bool SwTextNode::GetFirstLineOfsWithNum( short& rFLOffset 
) const
         return true;
     }
 
-    rFLOffset = GetSwAttrSet().GetFirstLineIndent().GetTextFirstLineOffset();
+    rFLOffset = 
GetSwAttrSet().GetFirstLineIndent().ResolveTextFirstLineOffset(rMetrics);
     return false;
 }
 
@@ -3398,12 +3401,16 @@ SwTwips 
SwTextNode::GetAdditionalIndentForStartingNewList() const
         if ( rFormat.GetPositionAndSpaceMode() == 
SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
         {
             SvxFirstLineIndentItem const& 
rFirst(GetSwAttrSet().GetFirstLineIndent());
-            nAdditionalIndent = 
GetSwAttrSet().GetTextLeftMargin().GetLeft(rFirst);
+
+            // tdf#36709: TODO: Handle font-relative indentation
+            nAdditionalIndent = 
GetSwAttrSet().GetTextLeftMargin().GetLeft(rFirst, /*metrics*/ {});
 
             if 
(getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING))
             {
-                nAdditionalIndent = nAdditionalIndent -
-                    
GetSwAttrSet().GetFirstLineIndent().GetTextFirstLineOffset();
+                // tdf#36709: TODO: Handle font-relative indentation
+                nAdditionalIndent
+                    = nAdditionalIndent
+                      - 
GetSwAttrSet().GetFirstLineIndent().ResolveTextFirstLineOffset({});
             }
         }
         else if ( rFormat.GetPositionAndSpaceMode() == 
SvxNumberFormat::LABEL_ALIGNMENT )
@@ -3414,26 +3421,30 @@ SwTwips 
SwTextNode::GetAdditionalIndentForStartingNewList() const
             ::sw::ListLevelIndents const 
indents(AreListLevelIndentsApplicable());
             SvxFirstLineIndentItem const aFirst(
                     indents & ::sw::ListLevelIndents::FirstLine
-                    ? SvxFirstLineIndentItem(rFormat.GetFirstLineIndent(), 
RES_MARGIN_FIRSTLINE)
+                    ? SvxFirstLineIndentItem(rFormat.GetFirstLineIndent(),
+                                             rFormat.GetFirstLineIndentUnit(), 
RES_MARGIN_FIRSTLINE)
                     : GetSwAttrSet().GetFirstLineIndent());
             SvxTextLeftMarginItem const aLeft(
                     indents & ::sw::ListLevelIndents::LeftMargin
                     ? SvxTextLeftMarginItem(rFormat.GetIndentAt(), 
RES_MARGIN_TEXTLEFT)
                     : GetSwAttrSet().GetTextLeftMargin());
-            nAdditionalIndent = aLeft.GetLeft(aFirst);
+            // tdf#36709: TODO: Handle font-relative indentation
+            nAdditionalIndent = aLeft.GetLeft(aFirst, /*metrics*/ {});
             if (!(indents & ::sw::ListLevelIndents::FirstLine))
             {
                 if 
(getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING))
                 {
-                    nAdditionalIndent = nAdditionalIndent - 
aFirst.GetTextFirstLineOffset();
+                    // tdf#36709: TODO: Handle font-relative first line 
indentation
+                    nAdditionalIndent = nAdditionalIndent - 
aFirst.ResolveTextFirstLineOffset({});
                 }
             }
         }
     }
     else
     {
+        // tdf#36709: TODO: Handle font-relative first line indentation
         SvxFirstLineIndentItem const& 
rFirst(GetSwAttrSet().GetFirstLineIndent());
-        nAdditionalIndent = GetSwAttrSet().GetTextLeftMargin().GetLeft(rFirst);
+        nAdditionalIndent = GetSwAttrSet().GetTextLeftMargin().GetLeft(rFirst, 
/*metrics*/ {});
     }
 
     return nAdditionalIndent;
diff --git a/sw/source/core/txtnode/swfont.cxx 
b/sw/source/core/txtnode/swfont.cxx
index 5499c8b84421..864d3c99ecfb 100644
--- a/sw/source/core/txtnode/swfont.cxx
+++ b/sw/source/core/txtnode/swfont.cxx
@@ -45,6 +45,7 @@
 #include <editeng/charhiddenitem.hxx>
 #include <editeng/boxitem.hxx>
 #include <editeng/shaditem.hxx>
+#include <vcl/metric.hxx>
 #include <IDocumentSettingAccess.hxx>
 #include <charatr.hxx>
 #include <viewsh.hxx>
@@ -340,6 +341,17 @@ sal_uInt16 SwFont::CalcShadowSpace(const SvxShadowItemSide 
nShadow, const bool b
     return nSpace;
 }
 
+SvxFontUnitMetrics SwFont::GetFontUnitMetrics() const
+{
+    // tdf#36709: Metrics conversion should use em and ic values from the 
bound fonts.
+    // Unfortunately, this currently poses a problem due to font substitution: 
tests
+    // abort when a missing font is set on a device.
+    // In the interim, use height for all metrics. This is technically not 
correct, but
+    // should be close enough for common fonts.
+    return { /*em*/ static_cast<double>(GetHeight(GetActual())),
+             /*ic*/ static_cast<double>(GetHeight(SwFontScript::CJK)) };
+}
+
 void SwFont::dumpAsXml(xmlTextWriterPtr writer) const
 {
     (void)xmlTextWriterStartElement(writer, BAD_CAST("SwFont"));
diff --git a/sw/source/core/txtnode/thints.cxx 
b/sw/source/core/txtnode/thints.cxx
index a8001e2cdf94..4dab2b5ee23d 100644
--- a/sw/source/core/txtnode/thints.cxx
+++ b/sw/source/core/txtnode/thints.cxx
@@ -2135,7 +2135,9 @@ static void lcl_MergeListLevelIndentAsLRSpaceItem( const 
SwTextNode& rTextNode,
         {
             if (indents & ::sw::ListLevelIndents::FirstLine)
             {
-                SvxFirstLineIndentItem const 
firstLine(static_cast<short>(rFormat.GetFirstLineIndent()), 
RES_MARGIN_FIRSTLINE);
+                SvxFirstLineIndentItem const 
firstLine(rFormat.GetFirstLineIndent(),
+                                                       
rFormat.GetFirstLineIndentUnit(),
+                                                       RES_MARGIN_FIRSTLINE);
                 rSet.Put(firstLine);
             }
             if (indents & ::sw::ListLevelIndents::LeftMargin)
diff --git a/sw/source/filter/html/css1atr.cxx 
b/sw/source/filter/html/css1atr.cxx
index 20a47b5a842c..de2c160f3330 100644
--- a/sw/source/filter/html/css1atr.cxx
+++ b/sw/source/filter/html/css1atr.cxx
@@ -2794,8 +2794,9 @@ static SwHTMLWriter& 
OutCSS1_SvxFirstLineIndent(SwHTMLWriter & rWrt, SfxPoolItem
     // match that of the current template
 
     // The LineIndent of the first line might contain the room for numbering
-    tools::Long nFirstLineIndent = 
static_cast<tools::Long>(rFirstLine.GetTextFirstLineOffset())
-            - rWrt.m_nFirstLineIndent;
+    tools::Long nFirstLineIndent
+        = static_cast<tools::Long>(rFirstLine.ResolveTextFirstLineOffset({}))
+          - rWrt.m_nFirstLineIndent;
     if (rWrt.m_nDfltFirstLineIndent != nFirstLineIndent)
     {
         rWrt.OutCSS1_UnitProperty(sCSS1_P_text_indent, nFirstLineIndent);
diff --git a/sw/source/filter/html/htmlatr.cxx 
b/sw/source/filter/html/htmlatr.cxx
index 38bee88aaa93..a3e1c8817728 100644
--- a/sw/source/filter/html/htmlatr.cxx
+++ b/sw/source/filter/html/htmlatr.cxx
@@ -377,7 +377,7 @@ SwHTMLFormatInfo::SwHTMLFormatInfo( const SwFormat *pF, 
SwDoc *pDoc, SwDoc *pTem
         (pReferenceFormat ? pReferenceFormat : pFormat)->GetRightMargin());
     nLeftMargin = rTextLeftMargin.GetTextLeft();
     nRightMargin = rRightMargin.GetRight();
-    nFirstLineIndent = rFirstLine.GetTextFirstLineOffset();
+    nFirstLineIndent = rFirstLine.ResolveTextFirstLineOffset({});
 
     const SvxULSpaceItem &rULSpace =
         (pReferenceFormat ? pReferenceFormat : pFormat)->GetULSpace();
@@ -717,7 +717,7 @@ static void OutHTML_SwFormat( SwHTMLWriter& rWrt, const 
SwFormat& rFormat,
             rWrt.m_nDfltLeftMargin = rTextLeftMargin.GetTextLeft();
 
         // In numbered lists, don't output a first line indent.
-        rWrt.m_nFirstLineIndent = rFirstLine.GetTextFirstLineOffset();
+        rWrt.m_nFirstLineIndent = rFirstLine.ResolveTextFirstLineOffset({});
     }
 
     if( rInfo.bInNumberBulletList && bNumbered && bPara && 
!rWrt.m_bCfgOutStyles )
@@ -2086,7 +2086,7 @@ SwHTMLWriter& OutHTML_SwTextNode( SwHTMLWriter& rWrt, 
const SwContentNode& rNode
             SvxFirstLineIndentItem const& 
rFirstLine(pItemSet->Get(RES_MARGIN_FIRSTLINE));
             SvxTextLeftMarginItem const& 
rTextLeftMargin(pItemSet->Get(RES_MARGIN_TEXTLEFT));
             SvxRightMarginItem const& 
rRightMargin(pItemSet->Get(RES_MARGIN_RIGHT));
-            sal_Int32 const nLeft(rTextLeftMargin.GetLeft(rFirstLine));
+            sal_Int32 const nLeft(rTextLeftMargin.GetLeft(rFirstLine, 
/*metrics*/ {}));
             sal_Int32 const nRight(rRightMargin.GetRight());
             if( nLeft || nRight )
             {
diff --git a/sw/source/filter/html/htmlctxt.cxx 
b/sw/source/filter/html/htmlctxt.cxx
index 7dad6a6065d4..bb0a1d58c734 100644
--- a/sw/source/filter/html/htmlctxt.cxx
+++ b/sw/source/filter/html/htmlctxt.cxx
@@ -638,13 +638,14 @@ void SwHTMLParser::InsertAttrs( SfxItemSet &rItemSet,
                         nRight = nOldRight + static_cast< sal_uInt16 
>(rPropInfo.m_nRightMargin);
                 }
                 if (rPropInfo.m_bTextIndent && pFirstLineItem)
-                    nIndent = pFirstLineItem->GetTextFirstLineOffset();
+                    nIndent = pFirstLineItem->ResolveTextFirstLineOffset({});
 
                 // Remember the value for the following paragraphs
                 pContext->SetMargins( nLeft, nRight, nIndent );
 
                 // Set the attribute on the current paragraph
-                SvxFirstLineIndentItem const firstLine(nIndent, 
RES_MARGIN_FIRSTLINE);
+                SvxFirstLineIndentItem firstLine(nIndent, 
css::util::MeasureUnit::TWIP,
+                                                 RES_MARGIN_FIRSTLINE);
                 NewAttr(m_xAttrTab, &m_xAttrTab->pFirstLineIndent, firstLine);
                 EndAttr(m_xAttrTab->pFirstLineIndent, false);
                 SvxTextLeftMarginItem const leftMargin(nLeft, 
RES_MARGIN_TEXTLEFT);
diff --git a/sw/source/filter/html/htmlnumreader.cxx 
b/sw/source/filter/html/htmlnumreader.cxx
index d9ecc21f3d3b..64d4d89fa259 100644
--- a/sw/source/filter/html/htmlnumreader.cxx
+++ b/sw/source/filter/html/htmlnumreader.cxx
@@ -277,8 +277,8 @@ void SwHTMLParser::NewNumberBulletList( HtmlTokenId nToken )
                 }
                 if( aPropInfo.m_bTextIndent )
                 {
-                    short nTextIndent =
-                        
aItemSet.Get(RES_MARGIN_FIRSTLINE).GetTextFirstLineOffset();
+                    short nTextIndent
+                        = 
aItemSet.Get(RES_MARGIN_FIRSTLINE).ResolveTextFirstLineOffset({});
                     aNumFormat.SetFirstLineOffset( nTextIndent );
                     bChangeNumFormat = true;
                 }
diff --git a/sw/source/filter/html/svxcss1.cxx 
b/sw/source/filter/html/svxcss1.cxx
index f93ff5387c97..8350dff92c7d 100644
--- a/sw/source/filter/html/svxcss1.cxx
+++ b/sw/source/filter/html/svxcss1.cxx
@@ -1987,7 +1987,8 @@ static void ParseCSS1_text_indent( const CSS1Expression 
*pExpr,
     if( !bSet )
         return;
 
-    SvxFirstLineIndentItem const firstLine(nIndent, RES_MARGIN_FIRSTLINE);
+    SvxFirstLineIndentItem const firstLine(nIndent, 
css::util::MeasureUnit::TWIP,
+                                           RES_MARGIN_FIRSTLINE);
     rItemSet.Put(firstLine);
     rPropInfo.m_bTextIndent = true;
 }
diff --git a/sw/source/filter/html/swhtml.cxx b/sw/source/filter/html/swhtml.cxx
index e35749be234e..5f8236f97217 100644
--- a/sw/source/filter/html/swhtml.cxx
+++ b/sw/source/filter/html/swhtml.cxx
@@ -4772,7 +4772,7 @@ void SwHTMLParser::SetTextCollAttrs( HTMLAttrContext 
*pContext )
         {
             sal_Int32 nLeft = rItemSet.Get(RES_MARGIN_TEXTLEFT).GetTextLeft();
             sal_Int32 nRight = rItemSet.Get(RES_MARGIN_RIGHT).GetRight();
-            nFirstLineIndent = 
rItemSet.Get(RES_MARGIN_FIRSTLINE).GetTextFirstLineOffset();
+            nFirstLineIndent = 
rItemSet.Get(RES_MARGIN_FIRSTLINE).ResolveTextFirstLineOffset({});
 
             // In Definition lists the margins also contain the margins from 
the previous levels
             if( RES_POOLCOLL_HTML_DD == nTopColl )
@@ -4814,7 +4814,7 @@ void SwHTMLParser::SetTextCollAttrs( HTMLAttrContext 
*pContext )
         }
         if( !nFirstLineIndent )
         {
-            nFirstLineIndent = 
pCollToSet->GetFirstLineIndent().GetTextFirstLineOffset();
+            nFirstLineIndent = 
pCollToSet->GetFirstLineIndent().ResolveTextFirstLineOffset({});
         }
     }
 
@@ -4837,16 +4837,16 @@ void SwHTMLParser::SetTextCollAttrs( HTMLAttrContext 
*pContext )
     const SvxFirstLineIndentItem & rFirstLine = 
pCollToSet->GetFirstLineIndent();
     const SvxTextLeftMarginItem & rTextLeftMargin = 
pCollToSet->GetTextLeftMargin();
     const SvxRightMarginItem & rRightMargin = pCollToSet->GetRightMargin();
-    bool bSetLRSpace = nLeftMargin != rTextLeftMargin.GetTextLeft() ||
-                      nFirstLineIndent != rFirstLine.GetTextFirstLineOffset() 
||
-                      nRightMargin != rRightMargin.GetRight();
+    bool bSetLRSpace = nLeftMargin != rTextLeftMargin.GetTextLeft()
+                       || nFirstLineIndent != 
rFirstLine.ResolveTextFirstLineOffset({})
+                       || nRightMargin != rRightMargin.GetRight();
 
     if( bSetLRSpace )
     {
         SvxFirstLineIndentItem firstLine(rFirstLine);
         SvxTextLeftMarginItem leftMargin(rTextLeftMargin);
         SvxRightMarginItem rightMargin(rRightMargin);
-        firstLine.SetTextFirstLineOffset(nFirstLineIndent);
+        firstLine.SetTextFirstLineOffset(nFirstLineIndent, 
css::util::MeasureUnit::TWIP);
         leftMargin.SetTextLeft(nLeftMargin);
         rightMargin.SetRight(nRightMargin);
         if( pItemSet )
@@ -5081,7 +5081,8 @@ void SwHTMLParser::InsertSpacer()
                 GetMarginsFromContextWithNumberBullet( nLeft, nRight, nIndent 
);
                 nIndent = nIndent + static_cast<short>(nSize);
 
-                SvxFirstLineIndentItem const firstLine(nIndent, 
RES_MARGIN_FIRSTLINE);
+                SvxFirstLineIndentItem const firstLine(nIndent, 
css::util::MeasureUnit::TWIP,
+                                                       RES_MARGIN_FIRSTLINE);
                 SvxTextLeftMarginItem const leftMargin(nLeft, 
RES_MARGIN_TEXTLEFT);
                 SvxRightMarginItem const rightMargin(nRight, RES_MARGIN_RIGHT);
 
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx 
b/sw/source/filter/ww8/docxattributeoutput.cxx
index e02477836c9c..c36fc02f633a 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -9276,7 +9276,8 @@ void DocxAttributeOutput::FormatPaperBin(const 
SvxPaperBinItem& rPaperBin)
 
 void DocxAttributeOutput::FormatFirstLineIndent(SvxFirstLineIndentItem const& 
rFirstLine)
 {
-    sal_Int32 const nFirstLineAdjustment(rFirstLine.GetTextFirstLineOffset());
+    // tdf#83844: TODO: export FONT_CJK_ADVANCE first line indent as 
HangingChars/FirstLineChars
+    sal_Int32 const 
nFirstLineAdjustment(rFirstLine.ResolveTextFirstLineOffset({}));
     if (nFirstLineAdjustment > 0)
     {
         AddToAttrList(m_pLRSpaceAttrList, FSNS(XML_w, XML_firstLine),
diff --git a/sw/source/filter/ww8/rtfattributeoutput.cxx 
b/sw/source/filter/ww8/rtfattributeoutput.cxx
index dd0d581257fa..bea6f6940c6e 100644
--- a/sw/source/filter/ww8/rtfattributeoutput.cxx
+++ b/sw/source/filter/ww8/rtfattributeoutput.cxx
@@ -3253,7 +3253,9 @@ void RtfAttributeOutput::ParaNumRule_Impl(const 
SwTextNode* pTextNd, sal_Int32 n
     SvxFirstLineIndentItem firstLine(rNdSet.Get(RES_MARGIN_FIRSTLINE));
     SvxTextLeftMarginItem leftMargin(rNdSet.Get(RES_MARGIN_TEXTLEFT));
     leftMargin.SetTextLeft(leftMargin.GetTextLeft() + pFormat->GetIndentAt());
-    firstLine.SetTextFirstLineOffset(pFormat->GetFirstLineOffset()); //TODO: 
overflow
+
+    firstLine.SetTextFirstLineOffset(pFormat->GetFirstLineOffset(),
+                                     pFormat->GetFirstLineOffsetUnit());
 
     sal_uInt16 nStyle = m_rExport.GetId(pFormat->GetCharFormat());
     OString* pString = m_rExport.GetStyle(nStyle);
@@ -3380,7 +3382,7 @@ void RtfAttributeOutput::FormatPaperBin(const 
SvxPaperBinItem& rItem)
 void RtfAttributeOutput::FormatFirstLineIndent(SvxFirstLineIndentItem const& 
rFirstLine)
 {
     m_aStyles.append(OOO_STRING_SVTOOLS_RTF_FI);
-    
m_aStyles.append(static_cast<sal_Int32>(rFirstLine.GetTextFirstLineOffset()));
+    m_aStyles.append(rFirstLine.ResolveTextFirstLineOffset({}));
 }
 
 void RtfAttributeOutput::FormatTextLeftMargin(SvxTextLeftMarginItem const& 
rTextLeftMargin)
diff --git a/sw/source/filter/ww8/wrtw8nds.cxx 
b/sw/source/filter/ww8/wrtw8nds.cxx
index 260bd38fc6ad..5f113a231ea0 100644
--- a/sw/source/filter/ww8/wrtw8nds.cxx
+++ b/sw/source/filter/ww8/wrtw8nds.cxx
@@ -2757,7 +2757,7 @@ void MSWordExportBase::OutputTextNode( SwTextNode& rNode )
                     {
                         const SvxFirstLineIndentItem *const 
pFirstLine(aSet.GetItem<SvxFirstLineIndentItem>(RES_MARGIN_FIRSTLINE));
                         if (pFirstLine)
-                            nFirstLineIndent = 
pFirstLine->GetTextFirstLineOffset();
+                            nFirstLineIndent = 
pFirstLine->ResolveTextFirstLineOffset({});
                     }
 
                     // Insert tab for aesthetic purposes #i24762#
@@ -3062,11 +3062,17 @@ void MSWordExportBase::OutputTextNode( SwTextNode& 
rNode )
                     {
                         if (bParaRTL)
                         {
-                            
firstLine.SetTextFirstLineOffsetValue(firstLine.GetTextFirstLineOffset() + 
pFormat->GetAbsLSpace() - pFormat->GetFirstLineOffset()); //TODO: overflow
+                            firstLine.SetTextFirstLineOffset(
+                                firstLine.ResolveTextFirstLineOffset({}) + 
pFormat->GetAbsLSpace()
+                                    - pFormat->GetFirstLineOffset(),
+                                css::util::MeasureUnit::TWIP);
                         }
                         else
                         {
-                            
firstLine.SetTextFirstLineOffset(firstLine.GetTextFirstLineOffset() + 
GetWordFirstLineOffset(*pFormat));
+                            firstLine.SetTextFirstLineOffset(
+                                firstLine.ResolveTextFirstLineOffset({})
+                                    + GetWordFirstLineOffset(*pFormat),
+                                css::util::MeasureUnit::TWIP);
                         }
                     }
 
@@ -3165,7 +3171,7 @@ void MSWordExportBase::OutputTextNode( SwTextNode& rNode )
                 const SvxTextLeftMarginItem *const 
pTextLeftMargin(oTmpSet->GetItemIfSet(RES_MARGIN_TEXTLEFT));
                 SvxFirstLineIndentItem firstLine(pFirstLineIndent
                         ? *pFirstLineIndent
-                        : SvxFirstLineIndentItem(0, RES_MARGIN_FIRSTLINE));
+                        : SvxFirstLineIndentItem(RES_MARGIN_FIRSTLINE));
                 SvxTextLeftMarginItem leftMargin(pTextLeftMargin
                         ? *pTextLeftMargin
                         : SvxTextLeftMarginItem(0, RES_MARGIN_TEXTLEFT));
@@ -3186,18 +3192,20 @@ void MSWordExportBase::OutputTextNode( SwTextNode& 
rNode )
                 if ( rNumFormat.GetPositionAndSpaceMode() ==
                                         
SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
                 {
-                    leftMargin.SetTextLeft(leftMargin.GetLeft(firstLine) + 
rNumFormat.GetAbsLSpace());
+                    leftMargin.SetTextLeft(leftMargin.GetLeft(firstLine, 
/*metrics*/ {})
+                                           + rNumFormat.GetAbsLSpace());
                 }
                 else
                 {
-                    leftMargin.SetTextLeft(leftMargin.GetLeft(firstLine) + 
rNumFormat.GetIndentAt());
+                    leftMargin.SetTextLeft(leftMargin.GetLeft(firstLine, 
/*metrics*/ {})
+                                           + rNumFormat.GetIndentAt());
                 }
 
                 // new first line indent = 0
                 // (first line indent is ignored)
                 if (!bParaRTL)
                 {
-                    firstLine.SetTextFirstLineOffset(0);
+                    firstLine.SetTextFirstLineOffset(0.0, 
css::util::MeasureUnit::TWIP);
                 }
 
                 // put back the new item
diff --git a/sw/source/filter/ww8/ww8atr.cxx b/sw/source/filter/ww8/ww8atr.cxx
index 9a2eee82747c..50b35698c620 100644
--- a/sw/source/filter/ww8/ww8atr.cxx
+++ b/sw/source/filter/ww8/ww8atr.cxx
@@ -927,7 +927,8 @@ void MSWordExportBase::OutputFormat( const SwFormat& 
rFormat, bool bPapFormat, b
                     SvxTextLeftMarginItem 
leftMargin(aSet.Get(RES_MARGIN_TEXTLEFT));
 
                     leftMargin.SetTextLeft(leftMargin.GetTextLeft() + 
rNFormat.GetAbsLSpace());
-                    
firstLine.SetTextFirstLineOffset(GetWordFirstLineOffset(rNFormat));
+                    
firstLine.SetTextFirstLineOffset(GetWordFirstLineOffset(rNFormat),
+                                                     
css::util::MeasureUnit::TWIP);
 
                     aSet.Put(firstLine);
                     aSet.Put(leftMargin);
@@ -4348,9 +4349,10 @@ void WW8AttributeOutput::FormatPaperBin( const 
SvxPaperBinItem& rPaperBin )
 
 void WW8AttributeOutput::FormatFirstLineIndent(SvxFirstLineIndentItem const& 
rFirstLine)
 {
+    // tdf#80596: TODO export sprmPDxcLeft1 for first line indents in ICs
     // sprmPDxaLeft1
     m_rWW8Export.InsUInt16( 0x8460 );        //asian version ?
-    m_rWW8Export.InsUInt16( rFirstLine.GetTextFirstLineOffset() );
+    m_rWW8Export.InsUInt16(rFirstLine.ResolveTextFirstLineOffset({}));
 }
 
 void WW8AttributeOutput::FormatTextLeftMargin(SvxTextLeftMarginItem const& 
rTextLeftMargin)
diff --git a/sw/source/filter/ww8/ww8graf.cxx b/sw/source/filter/ww8/ww8graf.cxx
index d0ad7521ee6b..d3e8cd0bc20e 100644
--- a/sw/source/filter/ww8/ww8graf.cxx
+++ b/sw/source/filter/ww8/ww8graf.cxx
@@ -526,7 +526,7 @@ void SwWW8ImplReader::InsertTxbxStyAttrs(SfxItemSet& rS, 
sal_uInt16 nColl, ManTy
 
                 SvxLRSpaceItem aLR(rS.Get(EE_PARA_LRSPACE));
                 aLR.SetTextFirstLineOffset(
-                    
pStyInf->m_pFormat->GetFirstLineIndent().GetTextFirstLineOffset());
+                    
pStyInf->m_pFormat->GetFirstLineIndent().ResolveTextFirstLineOffset({}));
                 
aLR.SetTextLeft(pStyInf->m_pFormat->GetTextLeftMargin().GetTextLeft());
                 aLR.SetRight(pStyInf->m_pFormat->GetRightMargin().GetRight());
                 rS.Put(aLR);
@@ -773,7 +773,7 @@ void SwWW8ImplReader::InsertAttrsAsDrawingAttrs(WW8_CP 
nStartCp, WW8_CP nEndCp,
                             {
                                 aLR.SetTextFirstLineOffset(
                                     static_cast<const 
SvxFirstLineIndentItem*>(pItem)
-                                        ->GetTextFirstLineOffset());
+                                        ->ResolveTextFirstLineOffset({}));
                             }
                             else if (nWhich == RES_MARGIN_TEXTLEFT)
                             {
diff --git a/sw/source/filter/ww8/ww8par.cxx b/sw/source/filter/ww8/ww8par.cxx
index 31f8f8f0f340..397d1b9c7ae9 100644
--- a/sw/source/filter/ww8/ww8par.cxx
+++ b/sw/source/filter/ww8/ww8par.cxx
@@ -1226,7 +1226,7 @@ static tools::Long 
lcl_GetTrueMargin(SvxFirstLineIndentItem const& rFirstLine,
             "<lcl_GetTrueMargin> - misusage: position-and-space-mode does not 
equal LABEL_WIDTH_AND_POSITION" );
 
     const tools::Long nBodyIndent = rLeftMargin.GetTextLeft();
-    const tools::Long nFirstLineDiff = rFirstLine.GetTextFirstLineOffset();
+    const tools::Long nFirstLineDiff = 
rFirstLine.ResolveTextFirstLineOffset({});
     rFirstLinePos = nBodyIndent + nFirstLineDiff;
 
     const auto nPseudoListBodyIndent = rFormat.GetAbsLSpace();
@@ -1249,14 +1249,15 @@ void SyncIndentWithList( SvxFirstLineIndentItem & 
rFirstLine,
         tools::Long nWantedFirstLinePos;
         tools::Long nExtraListIndent = lcl_GetTrueMargin(rFirstLine, 
rLeftMargin, rFormat, nWantedFirstLinePos);
         rLeftMargin.SetTextLeft(nWantedFirstLinePos - nExtraListIndent);
-        rFirstLine.SetTextFirstLineOffset(0);
+        rFirstLine.SetTextFirstLineOffset(0.0, css::util::MeasureUnit::TWIP);
     }
     else if ( rFormat.GetPositionAndSpaceMode() == 
SvxNumberFormat::LABEL_ALIGNMENT )
     {
         if ( !bFirstLineOfstSet && bLeftIndentSet &&
              rFormat.GetFirstLineIndent() != 0 )
         {
-            rFirstLine.SetTextFirstLineOffset(rFormat.GetFirstLineIndent());
+            rFirstLine.SetTextFirstLineOffset(rFormat.GetFirstLineIndent(),
+                                              
rFormat.GetFirstLineIndentUnit());
         }
         else if ( bFirstLineOfstSet && !bLeftIndentSet &&
                   rFormat.GetIndentAt() != 0 )
@@ -1267,7 +1268,8 @@ void SyncIndentWithList( SvxFirstLineIndentItem & 
rFirstLine,
         {
             if ( rFormat.GetFirstLineIndent() != 0 )
             {
-                
rFirstLine.SetTextFirstLineOffset(rFormat.GetFirstLineIndent());
+                rFirstLine.SetTextFirstLineOffset(rFormat.GetFirstLineIndent(),
+                                                  
rFormat.GetFirstLineIndentUnit());
             }
             if ( rFormat.GetIndentAt() != 0 )
             {
@@ -1355,7 +1357,10 @@ void SwWW8FltControlStack::SetAttrInDoc(const 
SwPosition& rTmpPos,
                     if (rEntry.m_pAttr->Which() == RES_MARGIN_FIRSTLINE)
                     {
                         SvxFirstLineIndentItem const 
firstLineEntry(*static_cast<SvxFirstLineIndentItem*>(rEntry.m_pAttr.get()));
-                        
firstLineNew.SetTextFirstLineOffset(firstLineEntry.GetTextFirstLineOffset(), 
firstLineEntry.GetPropTextFirstLineOffset());
+                        firstLineNew.SetTextFirstLineOffset(
+                            firstLineEntry.GetTextFirstLineOffsetValue(),
+                            firstLineEntry.GetTextFirstLineOffsetUnit(),
+                            firstLineEntry.GetPropTextFirstLineOffset());
                         
firstLineNew.SetAutoFirst(firstLineEntry.IsAutoFirst());
                     }
                     else
@@ -1380,7 +1385,10 @@ void SwWW8FltControlStack::SetAttrInDoc(const 
SwPosition& rTmpPos,
                         }
                         else
                         {
-                            
firstLineNew.SetTextFirstLineOffset(firstLineOld.GetTextFirstLineOffset(), 
firstLineOld.GetPropTextFirstLineOffset());
+                            firstLineNew.SetTextFirstLineOffset(
+                                firstLineOld.GetTextFirstLineOffsetValue(),
+                                firstLineOld.GetTextFirstLineOffsetUnit(),
+                                firstLineOld.GetPropTextFirstLineOffset());
                             
firstLineNew.SetAutoFirst(firstLineOld.IsAutoFirst());
                         }
 
diff --git a/sw/source/filter/ww8/ww8par2.cxx b/sw/source/filter/ww8/ww8par2.cxx
index 78f6ee19ac85..0f5e7129b5f0 100644
--- a/sw/source/filter/ww8/ww8par2.cxx
+++ b/sw/source/filter/ww8/ww8par2.cxx
@@ -292,7 +292,7 @@ sal_uInt16 SwWW8ImplReader::End_Footnote()
                         const SvxFirstLineIndentItem *const 
pFirstLine(aSet.GetItem<SvxFirstLineIndentItem>(RES_MARGIN_FIRSTLINE));
                         if (pFirstLine)
                         {
-                            nFirstLineIndent = 
pFirstLine->GetTextFirstLineOffset();
+                            nFirstLineIndent = 
pFirstLine->ResolveTextFirstLineOffset({});
                         }
                     }
 
diff --git a/sw/source/filter/ww8/ww8par3.cxx b/sw/source/filter/ww8/ww8par3.cxx
index f5ee3a1dc5fa..adf1a5e31f31 100644
--- a/sw/source/filter/ww8/ww8par3.cxx
+++ b/sw/source/filter/ww8/ww8par3.cxx
@@ -1680,7 +1680,8 @@ void UseListIndent(SwWW8StyInf &rStyle, const SwNumFormat 
&rFormat)
         SvxFirstLineIndentItem 
firstLine(rStyle.m_pFormat->GetFormatAttr(RES_MARGIN_FIRSTLINE));
         SvxTextLeftMarginItem 
leftMargin(rStyle.m_pFormat->GetFormatAttr(RES_MARGIN_TEXTLEFT));
         leftMargin.SetTextLeft(nAbsLSpace);
-        
firstLine.SetTextFirstLineOffset(writer_cast<short>(nListFirstLineIndent));
+        
firstLine.SetTextFirstLineOffset(writer_cast<short>(nListFirstLineIndent),
+                                         css::util::MeasureUnit::TWIP);
         rStyle.m_pFormat->SetFormatAttr(firstLine);
         rStyle.m_pFormat->SetFormatAttr(leftMargin);
         rStyle.m_bListRelevantIndentSet = true;
@@ -1702,7 +1703,7 @@ void SetStyleIndent(SwWW8StyInf &rStyle, const 
SwNumFormat &rFormat)
     else
     {
         leftMargin.SetTextLeft(0);
-        firstLine.SetTextFirstLineOffset(0);
+        firstLine.SetTextFirstLineOffset(0.0, css::util::MeasureUnit::TWIP);
     }
     rStyle.m_pFormat->SetFormatAttr(firstLine);
     rStyle.m_pFormat->SetFormatAttr(leftMargin);
@@ -2020,7 +2021,7 @@ void SwWW8ImplReader::Read_LFOPosition(sal_uInt16, const 
sal_uInt8* pData,
                     pFirstLine.reset(pItem->Clone());
 
                 // reset/blank the left indent (and only the left)
-                pFirstLine->SetTextFirstLineOffset(0);
+                pFirstLine->SetTextFirstLineOffset(0.0, 
css::util::MeasureUnit::TWIP);
                 SvxTextLeftMarginItem leftMargin(0, RES_MARGIN_TEXTLEFT);
 
                 // apply the modified SvxLRSpaceItem to the current paragraph
diff --git a/sw/source/filter/ww8/ww8par6.cxx b/sw/source/filter/ww8/ww8par6.cxx
index 08d02b772a4c..a81ab545e38a 100644
--- a/sw/source/filter/ww8/ww8par6.cxx
+++ b/sw/source/filter/ww8/ww8par6.cxx
@@ -4306,7 +4306,8 @@ void SwWW8ImplReader::Read_LR( sal_uInt16 nId, const 
sal_uInt8* pData, short nLe
             if ( pFormat && pFormat->GetPositionAndSpaceMode() == 
SvxNumberFormat::LABEL_ALIGNMENT )
             {
                 pLeftMargin->SetTextLeft(pFormat->GetIndentAt());
-                
pFirstLine->SetTextFirstLineOffset(static_cast<short>(pFormat->GetFirstLineIndent()));
+                
pFirstLine->SetTextFirstLineOffset(pFormat->GetFirstLineIndent(),
+                                                   
pFormat->GetFirstLineIndentUnit());
                 // make paragraph have hard-set indent attributes
                 pTextNode->SetAttr(*pLeftMargin);
                 pTextNode->SetAttr(*pFirstLine);
@@ -4378,11 +4379,13 @@ void SwWW8ImplReader::Read_LR( sal_uInt16 nId, const 
sal_uInt8* pData, short nLe
                 {
                     const SvxFirstLineIndentItem & rFirstLine =
                         
m_vColl[m_nCurrentColl].m_pFormat->GetFormatAttr(RES_MARGIN_FIRSTLINE);
-                    nPara = nPara - rFirstLine.GetTextFirstLineOffset();
+                    // tdf#80596: TODO handle sprmPDxcLeft1
+                    nPara = nPara - rFirstLine.ResolveTextFirstLineOffset({});
                 }
             }
 
-            pFirstLine->SetTextFirstLineOffset(nPara);
+            // tdf#80596: TODO handle sprmPDxcLeft1
+            pFirstLine->SetTextFirstLineOffset(nPara, 
css::util::MeasureUnit::TWIP);
 
             if (!m_pCurrentColl)
             {
diff --git a/sw/source/uibase/app/docstyle.cxx 
b/sw/source/uibase/app/docstyle.cxx
index 5644990236f3..02236edde471 100644
--- a/sw/source/uibase/app/docstyle.cxx
+++ b/sw/source/uibase/app/docstyle.cxx
@@ -215,7 +215,9 @@ public:
                 if (!oLRSpaceItem)
                     oLRSpaceItem.emplace(EE_PARA_LRSPACE);
                 auto pFirstLineItem = static_cast<const 
SvxFirstLineIndentItem*>(pItem);
-                
(*oLRSpaceItem).SetTextFirstLineOffsetValue(pFirstLineItem->GetTextFirstLineOffset());
+                // tdf#36709: TODO: Handle font-relative first-line indentation
+                (*oLRSpaceItem)
+                    
.SetTextFirstLineOffsetValue(pFirstLineItem->ResolveTextFirstLineOffset({}));
                 (*oLRSpaceItem).SetAutoFirst(pFirstLineItem->IsAutoFirst());
             }
             else if (nWhich == RES_MARGIN_TEXTLEFT)
@@ -1603,7 +1605,9 @@ void SwDocStyleSheet::MergeIndentAttrsOfListStyle( 
SfxItemSet& rSet )
         {
             if (indents & ::sw::ListLevelIndents::FirstLine)
             {
-                SvxFirstLineIndentItem const 
firstLine(static_cast<short>(rFormat.GetFirstLineIndent()), 
RES_MARGIN_FIRSTLINE);
+                SvxFirstLineIndentItem const 
firstLine(rFormat.GetFirstLineIndent(),
+                                                       
rFormat.GetFirstLineIndentUnit(),
+                                                       RES_MARGIN_FIRSTLINE);
                 rSet.Put(firstLine);
             }
             if (indents & ::sw::ListLevelIndents::LeftMargin)
diff --git a/sw/source/uibase/shells/txtattr.cxx 
b/sw/source/uibase/shells/txtattr.cxx
index d1749af5f859..c8ac640ad940 100644
--- a/sw/source/uibase/shells/txtattr.cxx
+++ b/sw/source/uibase/shells/txtattr.cxx
@@ -771,7 +771,10 @@ void SwTextShell::GetAttrState(SfxItemSet &rSet)
                     SvxFirstLineIndentItem const& 
rFirstLine(aCoreSet.Get(RES_MARGIN_FIRSTLINE));
                     SvxTextLeftMarginItem const& 
rLeftMargin(aCoreSet.Get(RES_MARGIN_TEXTLEFT));
                     SvxRightMarginItem const& 
rRightMargin(aCoreSet.Get(RES_MARGIN_RIGHT));
-                    
aLR.SetTextFirstLineOffset(rFirstLine.GetTextFirstLineOffset(), 
rFirstLine.GetPropTextFirstLineOffset());
+
+                    // tdf#36709: TODO: Handle font-relative units
+                    
aLR.SetTextFirstLineOffset(rFirstLine.ResolveTextFirstLineOffset({}),
+                                               
rFirstLine.GetPropTextFirstLineOffset());
                     aLR.SetAutoFirst(rFirstLine.IsAutoFirst());
                     aLR.SetTextLeft(rLeftMargin.GetTextLeft(), 
rLeftMargin.GetPropLeft());
                     aLR.SetRight(rRightMargin.GetRight(), 
rRightMargin.GetPropRight());
diff --git a/sw/source/uibase/uiview/viewtab.cxx 
b/sw/source/uibase/uiview/viewtab.cxx
index 66927459d019..9386cccb6638 100644
--- a/sw/source/uibase/uiview/viewtab.cxx
+++ b/sw/source/uibase/uiview/viewtab.cxx
@@ -630,7 +630,7 @@ void SwView::ExecTabWin( SfxRequest const & rReq )
             rSh.GetCurAttr( aSet );
             const SvxFirstLineIndentItem & 
rFirstLine(aSet.Get(RES_MARGIN_FIRSTLINE));
 
-            if (rFirstLine.GetTextFirstLineOffset() < 0)
+            if (rFirstLine.GetTextFirstLineOffsetValue() < 0.0)
             {
                 SvxTabStop aSwTabStop( 0, SvxTabAdjust::Default );
                 aTabStops.Insert( aSwTabStop );
@@ -705,7 +705,8 @@ void SwView::ExecTabWin( SfxRequest const & rReq )
             {
                 SvxFirstLineIndentItem 
firstLine(aLRSpaceSet.Get(RES_MARGIN_FIRSTLINE));
                 const OUString ratio = fLineIndent->GetValue();
-                firstLine.SetTextFirstLineOffset(nPageWidth * ratio.toFloat());
+                firstLine.SetTextFirstLineOffset(nPageWidth * ratio.toFloat(),
+                                                 css::util::MeasureUnit::TWIP);
                 rSh.SetAttrItem(firstLine);
             }
             else if (const SfxStringItem *pLeftIndent = 
pReqArgs->GetItemIfSet(SID_PARAGRAPH_LEFT_INDENT))
@@ -733,7 +734,8 @@ void SwView::ExecTabWin( SfxRequest const & rReq )
         SvxFirstLineIndentItem 
firstLine(aLRSpaceSet.Get(RES_MARGIN_FIRSTLINE));
         SvxTextLeftMarginItem leftMargin(aLRSpaceSet.Get(RES_MARGIN_TEXTLEFT));
 
-        tools::Long nIndentDist = firstLine.GetTextFirstLineOffset();
+        // tdf#36709: TODO: Handle font-relative hanging indent
+        tools::Long nIndentDist = firstLine.ResolveTextFirstLineOffset({});
 
         if (nIndentDist == 0)
         {
@@ -742,7 +744,7 @@ void SwView::ExecTabWin( SfxRequest const & rReq )
         }
 
         leftMargin.SetTextLeft(leftMargin.GetTextLeft() + nIndentDist);
-        firstLine.SetTextFirstLineOffset(nIndentDist * -1);
+        firstLine.SetTextFirstLineOffset(nIndentDist * -1, 
css::util::MeasureUnit::TWIP);
 
         firstLine.SetAutoFirst(false); // old code would do this, is it wanted?
         rSh.SetAttrItem(firstLine);
@@ -1670,7 +1672,10 @@ void SwView::StateTabWin(SfxItemSet& rSet)
                     SvxFirstLineIndentItem const& 
rFirstLine(aCoreSet.Get(RES_MARGIN_FIRSTLINE));
                     SvxTextLeftMarginItem const& 
rLeftMargin(aCoreSet.Get(RES_MARGIN_TEXTLEFT));
                     SvxRightMarginItem const& 
rRightMargin(aCoreSet.Get(RES_MARGIN_RIGHT));
-                    
aLR->SetTextFirstLineOffset(rFirstLine.GetTextFirstLineOffset(), 
rFirstLine.GetPropTextFirstLineOffset());
+
+                    // tdf#36709: TODO: Handle font-relative first-line 
indentation
+                    
aLR->SetTextFirstLineOffset(rFirstLine.ResolveTextFirstLineOffset({}),
+                                                
rFirstLine.GetPropTextFirstLineOffset());
                     aLR->SetAutoFirst(rFirstLine.IsAutoFirst());
                     aLR->SetTextLeft(rLeftMargin.GetTextLeft(), 
rLeftMargin.GetPropLeft());
                     aLR->SetRight(rRightMargin.GetRight(), 
rRightMargin.GetPropRight());
@@ -1684,7 +1689,9 @@ void SwView::StateTabWin(SfxItemSet& rSet)
                                         
m_pNumRuleNodeFromDoc->GetLeftMarginWithNum( true ) );
 
                         short nFLOffset;
-                        m_pNumRuleNodeFromDoc->GetFirstLineOfsWithNum( 
nFLOffset );
+
+                        // tdf#36709: TODO: Handle font-relative units
+                        
m_pNumRuleNodeFromDoc->GetFirstLineOfsWithNum(nFLOffset, {});
 
                         aLR->SetLeft( nOffset + nFLOffset );
                     }
diff --git a/sw/source/uibase/wrtsh/delete.cxx 
b/sw/source/uibase/wrtsh/delete.cxx
index 2cb0e2ed6d72..79172e8a6819 100644
--- a/sw/source/uibase/wrtsh/delete.cxx
+++ b/sw/source/uibase/wrtsh/delete.cxx
@@ -70,17 +70,17 @@ bool SwWrtShell::TryRemoveIndent()
 
     SvxFirstLineIndentItem firstLine(aAttrSet.Get(RES_MARGIN_FIRSTLINE));
     SvxTextLeftMarginItem leftMargin(aAttrSet.Get(RES_MARGIN_TEXTLEFT));
-    short aOldFirstLineOfst = firstLine.GetTextFirstLineOffset();
+    short aOldFirstLineOfst = firstLine.ResolveTextFirstLineOffset({});
 
     if (aOldFirstLineOfst > 0)
     {
-        firstLine.SetTextFirstLineOffset(0);
+        firstLine.SetTextFirstLineOffset(0.0, css::util::MeasureUnit::TWIP);
         bResult = true;
     }
     else if (aOldFirstLineOfst < 0)
     {
         // this used to call SetLeft() but this should be the same result
-        firstLine.SetTextFirstLineOffset(0);
+        firstLine.SetTextFirstLineOffset(0.0, css::util::MeasureUnit::TWIP);
         leftMargin.SetTextLeft(leftMargin.GetTextLeft() + aOldFirstLineOfst);
         bResult = true;
     }
diff --git a/vcl/inc/font/FontMetricData.hxx b/vcl/inc/font/FontMetricData.hxx
index 636d2f3f718d..b82b9947ceb9 100644
--- a/vcl/inc/font/FontMetricData.hxx
+++ b/vcl/inc/font/FontMetricData.hxx
@@ -57,6 +57,9 @@ public:
     int             GetSlant() const                                           
     { return mnSlant; }
     double                 GetMinKashida() const                               
            { return mnMinKashida; }
     tools::Long            GetHangingBaseline() const                          
            { return mnHangingBaseline; }
+    double GetUnitEm() const { return mdEmSize; }
+    double GetHorCJKAdvance() const { return mdHorCJKAdvanceSize; }
+    double GetVertCJKAdvance() const { return mdVertCJKAdvanceSize; }
 
     void            SetSlant(int nSlant)                                       
     { mnSlant=nSlant; }
     void            SetMinKashida(double nMinKashida )                         
            { mnMinKashida=nMinKashida; }
@@ -118,6 +121,9 @@ private:
     int             mnSlant;                    // Slant (Italic/Oblique)
     double                 mnMinKashida;               // Minimal width of 
kashida (Arabic)
-e 
... etc. - the rest is truncated

Reply via email to