editeng/source/items/frmitems.cxx                           |   50 +++
 include/editeng/lrspitem.hxx                                |   10 
 include/editeng/memberids.h                                 |    1 
 include/xmloff/xmltypes.hxx                                 |    1 
 schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng |   19 +
 sw/inc/unoprnms.hxx                                         |    1 
 sw/qa/extras/odfexport/data/tdf36709.fodt                   |  117 +++++++++
 sw/qa/extras/odfexport/odfexport2.cxx                       |   19 +
 sw/source/core/text/itrcrsr.cxx                             |   20 +
 sw/source/core/unocore/unomap1.cxx                          |    1 
 sw/source/core/unocore/unomapproperties.hxx                 |    3 
 vcl/qa/cppunit/pdfexport/data/tdf36709.fodt                 |  151 ++++++++++++
 vcl/qa/cppunit/pdfexport/pdfexport2.cxx                     |   51 ++++
 xmloff/inc/xmlbahdl.hxx                                     |   10 
 xmloff/inc/xmlprop.hxx                                      |    1 
 xmloff/source/style/prhdlfac.cxx                            |    3 
 xmloff/source/style/xmlbahdl.cxx                            |   53 ++++
 xmloff/source/text/txtprmap.cxx                             |    3 
 18 files changed, 510 insertions(+), 4 deletions(-)

New commits:
commit 43cd683230bc05d294b1bd64f1e7932feccdd3fb
Author:     Jonathan Clark <[email protected]>
AuthorDate: Mon Oct 21 11:31:46 2024 -0600
Commit:     Jonathan Clark <[email protected]>
CommitDate: Sat Nov 2 01:09:52 2024 +0100

    tdf#36709 Add loext:text-indent supporting font-relative units
    
    This change adds an ODF font-relative first-line indent paragraph style
    attribute as a LibreOffice extension. The corresponding ODF standard
    change is tracked by OFFICE-4165.
    
    This change only implements what is minimally necessary to serialize,
    deserialize, and check for ODF files containing this attribute. Further
    changes are necessary.
    
    * Added cssLength to schema, which is equivalent to length but also
      allows ic and em as units.
    * Added loext:text-indent to schema as a paragraph style attribute. This
      attribute is equivalent to fo:text-indent, but accepts cssLength
      instead of length.
    * Added XML_TYPE_UNIT_MEASURE to the ODF parser, which currently accepts
      only the font-relative measures and forces fallback in other cases.
    * Added loext:text-indent to the ODF parser. This attribute accepts
      font-relative metrics, and will behave as an import-only alias for
      fo:text-indent in other cases.
    * Updated SvxFirstLineIndentItem to handle unit-denominated measures.
    * Added proof-of-concept indentation handler to Writer. This
      implementation is incomplete and temporary, and will be revised in
      future changes.
    
    Change-Id: I7eb5c7382093cb18a9b0afbf93dacb34ba1d35ef
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/175941
    Tested-by: Jenkins
    Reviewed-by: Jonathan Clark <[email protected]>

diff --git a/editeng/source/items/frmitems.cxx 
b/editeng/source/items/frmitems.cxx
index 5dd0346ef47e..93d1c33fcc8f 100644
--- a/editeng/source/items/frmitems.cxx
+++ b/editeng/source/items/frmitems.cxx
@@ -28,6 +28,7 @@
 #include <com/sun/star/style/BreakType.hpp>
 #include <com/sun/star/style/GraphicLocation.hpp>
 #include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/beans/Pair.hpp>
 #include <com/sun/star/text/WritingMode2.hpp>
 #include <com/sun/star/frame/status/UpperLowerMarginScale.hpp>
 #include <com/sun/star/frame/status/LeftRightMarginScale.hpp>
@@ -500,6 +501,7 @@ void SvxFirstLineIndentItem::SetTextFirstLineOffset(
 {
     ASSERT_CHANGE_REFCOUNTED_ITEM;
     m_nFirstLineOffset = short((tools::Long(nF) * nProp ) / 100);
+    m_nUnit = css::util::MeasureUnit::TWIP;
     m_nPropFirstLineOffset = nProp;
 }
 
@@ -944,20 +946,45 @@ SvxFirstLineIndentItem::SvxFirstLineIndentItem(const 
short nFirst, const sal_uIn
 
 bool SvxFirstLineIndentItem::QueryValue(uno::Any& rVal, sal_uInt8 nMemberId) 
const
 {
-    bool bRet = true;
+    bool bRet = false;
     bool bConvert = 0 != (nMemberId & CONVERT_TWIPS);
     nMemberId &= ~CONVERT_TWIPS;
     switch (nMemberId)
     {
         case MID_FIRST_LINE_INDENT:
-            rVal <<= static_cast<sal_Int32>(bConvert ? 
convertTwipToMm100(m_nFirstLineOffset) : m_nFirstLineOffset);
-            break;
+            // MID_FIRST_LINE_INDENT only supports statically-convertible 
measures.
+            // 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);
+                bRet = true;
+            }
+        break;
+
         case MID_FIRST_LINE_REL_INDENT:
             rVal <<= static_cast<sal_Int16>(m_nPropFirstLineOffset);
+            bRet = true;
             break;
+
+        case MID_FIRST_LINE_UNIT_INDENT:
+            // MID_FIRST_LINE_UNIT_INDENT is used for any values that must be 
serialized
+            // as a unit-value pair. In practice, this will be limited to 
font-relative
+            // 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
+                };
+                bRet = true;
+            }
+        break;
+
         case MID_FIRST_AUTO:
             rVal <<= IsAutoFirst();
+            bRet = true;
             break;
+
         default:
             assert(false);
             bRet = false;
@@ -983,6 +1010,7 @@ bool SvxFirstLineIndentItem::PutValue(const uno::Any& 
rVal, sal_uInt8 nMemberId)
                 return false;
             }
             m_nFirstLineOffset = bConvert ? o3tl::toTwips(nVal, 
o3tl::Length::mm100) : nVal;
+            m_nUnit = css::util::MeasureUnit::TWIP;
             m_nPropFirstLineOffset = 100;
             break;
         }
@@ -999,6 +1027,19 @@ bool SvxFirstLineIndentItem::PutValue(const uno::Any& 
rVal, sal_uInt8 nMemberId)
             }
             break;
         }
+        case MID_FIRST_LINE_UNIT_INDENT:
+        {
+            css::beans::Pair<double, sal_Int16> stVal;
+            if (!(rVal >>= stVal))
+            {
+                return false;
+            }
+
+            m_nFirstLineOffset = stVal.First;
+            m_nUnit = stVal.Second;
+            m_nPropFirstLineOffset = 100;
+            break;
+        }
         case MID_FIRST_AUTO:
             SetAutoFirst(Any2Bool(rVal));
             break;
@@ -1017,6 +1058,7 @@ 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());
 }
@@ -1025,6 +1067,7 @@ size_t SvxFirstLineIndentItem::hashCode() const
 {
     std::size_t seed(0);
     o3tl::hash_combine(seed, m_nFirstLineOffset);
+    o3tl::hash_combine(seed, m_nUnit);
     o3tl::hash_combine(seed, m_nPropFirstLineOffset);
     o3tl::hash_combine(seed, m_bAutoFirst);
     return seed;
@@ -1096,6 +1139,7 @@ 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_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()));
     (void)xmlTextWriterEndElement(pWriter);
diff --git a/include/editeng/lrspitem.hxx b/include/editeng/lrspitem.hxx
index 5fe01262e7c2..63bff2c974ca 100644
--- a/include/editeng/lrspitem.hxx
+++ b/include/editeng/lrspitem.hxx
@@ -21,6 +21,7 @@
 
 #include <svl/poolitem.hxx>
 #include <editeng/editengdllapi.h>
+#include <com/sun/star/util/MeasureUnit.hpp>
 
 
 // class SvxLRSpaceItem --------------------------------------------------
@@ -136,6 +137,7 @@ class EDITENG_DLLPUBLIC SvxFirstLineIndentItem final : 
public SfxPoolItem
 private:
     /// First-line indent always relative to GetTextLeft()
     short m_nFirstLineOffset = 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;
@@ -146,12 +148,18 @@ public:
 
     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; }
+    {
+        ASSERT_CHANGE_REFCOUNTED_ITEM;
+        m_nFirstLineOffset = nValue;
+        m_nUnit = css::util::MeasureUnit::TWIP;
+    }
 
     explicit SvxFirstLineIndentItem(const sal_uInt16 nId);
     SvxFirstLineIndentItem(const short nOffset, const sal_uInt16 nId);
diff --git a/include/editeng/memberids.h b/include/editeng/memberids.h
index 57250a9872e8..195eccce41c8 100644
--- a/include/editeng/memberids.h
+++ b/include/editeng/memberids.h
@@ -138,6 +138,7 @@
 #define MID_FIRST_AUTO              10
 #define MID_TXT_LMARGIN             11
 #define MID_GUTTER_MARGIN 12
+#define MID_FIRST_LINE_UNIT_INDENT 13
 
 //ProtectItem
 #define MID_PROTECT_CONTENT     0
diff --git a/include/xmloff/xmltypes.hxx b/include/xmloff/xmltypes.hxx
index 7c7cabb43df1..a3c6f057c610 100644
--- a/include/xmloff/xmltypes.hxx
+++ b/include/xmloff/xmltypes.hxx
@@ -154,6 +154,7 @@
 #define XML_TYPE_DOUBLE_PERCENT     0x00002024          //  50% (source is a 
double from 0.0 to 1.0)
 #define XML_TYPE_HEX                0x00002025          // 00544F1B
 #define XML_TYPE_PERCENT100 0x00002026 // 100th percent
+#define XML_TYPE_UNIT_MEASURE       0x00002027          // Source is a double 
paired with a unit
 
 // special basic types
 #define XML_TYPE_RECTANGLE_LEFT     0x00000100          // the Left member of 
an awt::Rectangle as a measure
diff --git a/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng 
b/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng
index a805123d7e24..5f53d50c8526 100644
--- a/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng
+++ b/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng
@@ -3098,6 +3098,25 @@ 
xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.
     </rng:optional>
   </rng:define>
 
+  <!-- https://issues.oasis-open.org/browse/OFFICE-4165 -->
+  <rng:define name="cssLength">
+    <rng:data type="string">
+      <rng:param 
name="pattern">-?([0-9]+(\.[0-9]*)?|\.[0-9]+)((cm)|(mm)|(in)|(pt)|(pc)|(px)|(ic)|(em))</rng:param>
+    </rng:data>
+  </rng:define>
+
+  <!-- https://issues.oasis-open.org/browse/OFFICE-4165 -->
+  <rng:define name="style-paragraph-properties-attlist" combine="interleave">
+    <rng:optional>
+      <rng:attribute name="loext:text-indent">
+        <rng:choice>
+          <rng:ref name="cssLength"/>
+          <rng:ref name="percent"/>
+        </rng:choice>
+      </rng:attribute>
+    </rng:optional>
+  </rng:define>
+
   <!-- TODO no proposal -->
   <rng:define name="chart-data-point-attlist" combine="interleave">
     <rng:optional>
diff --git a/sw/inc/unoprnms.hxx b/sw/inc/unoprnms.hxx
index e3d7e073c24d..13a64ef5484d 100644
--- a/sw/inc/unoprnms.hxx
+++ b/sw/inc/unoprnms.hxx
@@ -64,6 +64,7 @@ inline constexpr OUString 
UNO_NAME_PARA_IS_AUTO_FIRST_LINE_INDENT
 inline constexpr OUString UNO_NAME_PARA_FIRST_LINE_INDENT = 
u"ParaFirstLineIndent"_ustr;
 inline constexpr OUString UNO_NAME_PARA_FIRST_LINE_INDENT_RELATIVE
     = u"ParaFirstLineIndentRelative"_ustr;
+inline constexpr OUString UNO_NAME_PARA_FIRST_LINE_INDENT_UNIT = 
u"ParaFirstLineIndentUnit"_ustr;
 inline constexpr OUString UNO_NAME_PARA_IS_HYPHENATION = 
u"ParaIsHyphenation"_ustr;
 inline constexpr OUString UNO_NAME_PARA_HYPHENATION_MAX_LEADING_CHARS
     = u"ParaHyphenationMaxLeadingChars"_ustr;
diff --git a/sw/qa/extras/odfexport/data/tdf36709.fodt 
b/sw/qa/extras/odfexport/data/tdf36709.fodt
new file mode 100644
index 000000000000..f4dddd28ee85
--- /dev/null
+++ b/sw/qa/extras/odfexport/data/tdf36709.fodt
@@ -0,0 +1,117 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<office:document xmlns:css3t="http://www.w3.org/TR/css3-text/"; 
xmlns:grddl="http://www.w3.org/2003/g/data-view#"; 
xmlns:xhtml="http://www.w3.org/1999/xhtml"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xmlns:xsd="http://www.w3.org/2001/XMLSchema"; 
xmlns:xforms="http://www.w3.org/2002/xforms"; 
xmlns:dom="http://www.w3.org/2001/xml-events"; 
xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" 
xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" 
xmlns:math="http://www.w3.org/1998/Math/MathML"; 
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" 
xmlns:ooo="http://openoffice.org/2004/office"; 
xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" 
xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" 
xmlns:ooow="http://openoffice.org/2004/writer"; 
xmlns:xlink="http://www.w3.org/1999/xlink"; 
xmlns:drawooo="http://openoffice.org/2010/draw"; 
xmlns:oooc="http://openoffice.org/2004/calc"; 
xmlns:dc="http://purl.org/dc/elements/1.1/"; xmlns:c
 alcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" 
xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" 
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" 
xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" 
xmlns:tableooo="http://openoffice.org/2009/table"; 
xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" 
xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" 
xmlns:rpt="http://openoffice.org/2005/report"; 
xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0"
 xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" 
xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" 
xmlns:officeooo="http://openoffice.org/2009/office"; 
xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" 
xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" 
xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" 
xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:
 meta:1.0" 
xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0"
 office:version="1.4" office:mimetype="application/vnd.oasis.opendocument.text">
+ 
<office:meta><meta:creation-date>2024-10-31T15:19:06.580445570</meta:creation-date><dc:date>2024-10-31T15:22:40.161604509</dc:date><meta:editing-duration>PT3M34S</meta:editing-duration><meta:editing-cycles>1</meta:editing-cycles><meta:document-statistic
 meta:table-count="0" meta:image-count="0" meta:object-count="0" 
meta:page-count="1" meta:paragraph-count="2" meta:word-count="4" 
meta:character-count="42" 
meta:non-whitespace-character-count="40"/><meta:generator>LibreOfficeDev/25.2.0.0.alpha0$Linux_X86_64
 
LibreOffice_project/5f12ad737bbb930b76299df0433c8635ae27a7bd</meta:generator></office:meta>
+ <office:font-face-decls>
+  <style:font-face style:name="Liberation Serif" svg:font-family="'Liberation 
Serif'" style:font-family-generic="roman" style:font-pitch="variable"/>
+  <style:font-face style:name="Noto Sans1" svg:font-family="'Noto Sans'" 
style:font-family-generic="system" style:font-pitch="variable"/>
+  <style:font-face style:name="Noto Serif CJK SC" svg:font-family="'Noto Serif 
CJK SC'" style:font-family-generic="system" style:font-pitch="variable"/>
+ </office:font-face-decls>
+ <office:styles>
+  <style:default-style style:family="graphic">
+   <style:graphic-properties svg:stroke-color="#3465a4" 
draw:fill-color="#729fcf" fo:wrap-option="no-wrap" 
draw:shadow-offset-x="0.1181in" draw:shadow-offset-y="0.1181in" 
draw:start-line-spacing-horizontal="0.1114in" 
draw:start-line-spacing-vertical="0.1114in" 
draw:end-line-spacing-horizontal="0.1114in" 
draw:end-line-spacing-vertical="0.1114in" style:flow-with-text="false"/>
+   <style:paragraph-properties style:text-autospace="ideograph-alpha" 
style:line-break="strict" loext:tab-stop-distance="0in" 
style:writing-mode="lr-tb" style:font-independent-line-spacing="false">
+    <style:tab-stops/>
+   </style:paragraph-properties>
+   <style:text-properties style:use-window-font-color="true" 
loext:opacity="0%" style:font-name="Liberation Serif" fo:font-size="12pt" 
fo:language="en" fo:country="US" style:letter-kerning="true" 
style:font-name-asian="Noto Serif CJK SC" style:font-size-asian="10.5pt" 
style:language-asian="zh" style:country-asian="CN" 
style:font-name-complex="Noto Sans1" style:font-size-complex="12pt" 
style:language-complex="hi" style:country-complex="IN"/>
+  </style:default-style>
+  <style:default-style style:family="paragraph">
+   <style:paragraph-properties fo:orphans="2" fo:widows="2" 
fo:hyphenation-ladder-count="no-limit" fo:hyphenation-keep="auto" 
loext:hyphenation-keep-type="column" style:text-autospace="ideograph-alpha" 
style:punctuation-wrap="hanging" style:line-break="strict" 
style:tab-stop-distance="0.4925in" style:writing-mode="page"/>
+   <style:text-properties style:use-window-font-color="true" 
loext:opacity="0%" style:font-name="Liberation Serif" fo:font-size="12pt" 
fo:language="en" fo:country="US" style:letter-kerning="true" 
style:font-name-asian="Noto Serif CJK SC" style:font-size-asian="10.5pt" 
style:language-asian="zh" style:country-asian="CN" 
style:font-name-complex="Noto Sans1" style:font-size-complex="12pt" 
style:language-complex="hi" style:country-complex="IN" fo:hyphenate="false" 
fo:hyphenation-remain-char-count="2" fo:hyphenation-push-char-count="2" 
loext:hyphenation-no-caps="false" loext:hyphenation-no-last-word="false" 
loext:hyphenation-word-char-count="5" loext:hyphenation-zone="no-limit"/>
+  </style:default-style>
+  <style:default-style style:family="table">
+   <style:table-properties table:border-model="collapsing"/>
+  </style:default-style>
+  <style:default-style style:family="table-row">
+   <style:table-row-properties fo:keep-together="auto"/>
+  </style:default-style>
+  <style:style style:name="Standard" style:family="paragraph" 
style:class="text"/>
+  <text:outline-style style:name="Outline">
+   <text:outline-level-style text:level="1" style:num-format="">
+    <style:list-level-properties 
text:list-level-position-and-space-mode="label-alignment">
+     <style:list-level-label-alignment text:label-followed-by="listtab"/>
+    </style:list-level-properties>
+   </text:outline-level-style>
+   <text:outline-level-style text:level="2" style:num-format="">
+    <style:list-level-properties 
text:list-level-position-and-space-mode="label-alignment">
+     <style:list-level-label-alignment text:label-followed-by="listtab"/>
+    </style:list-level-properties>
+   </text:outline-level-style>
+   <text:outline-level-style text:level="3" style:num-format="">
+    <style:list-level-properties 
text:list-level-position-and-space-mode="label-alignment">
+     <style:list-level-label-alignment text:label-followed-by="listtab"/>
+    </style:list-level-properties>
+   </text:outline-level-style>
+   <text:outline-level-style text:level="4" style:num-format="">
+    <style:list-level-properties 
text:list-level-position-and-space-mode="label-alignment">
+     <style:list-level-label-alignment text:label-followed-by="listtab"/>
+    </style:list-level-properties>
+   </text:outline-level-style>
+   <text:outline-level-style text:level="5" style:num-format="">
+    <style:list-level-properties 
text:list-level-position-and-space-mode="label-alignment">
+     <style:list-level-label-alignment text:label-followed-by="listtab"/>
+    </style:list-level-properties>
+   </text:outline-level-style>
+   <text:outline-level-style text:level="6" style:num-format="">
+    <style:list-level-properties 
text:list-level-position-and-space-mode="label-alignment">
+     <style:list-level-label-alignment text:label-followed-by="listtab"/>
+    </style:list-level-properties>
+   </text:outline-level-style>
+   <text:outline-level-style text:level="7" style:num-format="">
+    <style:list-level-properties 
text:list-level-position-and-space-mode="label-alignment">
+     <style:list-level-label-alignment text:label-followed-by="listtab"/>
+    </style:list-level-properties>
+   </text:outline-level-style>
+   <text:outline-level-style text:level="8" style:num-format="">
+    <style:list-level-properties 
text:list-level-position-and-space-mode="label-alignment">
+     <style:list-level-label-alignment text:label-followed-by="listtab"/>
+    </style:list-level-properties>
+   </text:outline-level-style>
+   <text:outline-level-style text:level="9" style:num-format="">
+    <style:list-level-properties 
text:list-level-position-and-space-mode="label-alignment">
+     <style:list-level-label-alignment text:label-followed-by="listtab"/>
+    </style:list-level-properties>
+   </text:outline-level-style>
+   <text:outline-level-style text:level="10" style:num-format="">
+    <style:list-level-properties 
text:list-level-position-and-space-mode="label-alignment">
+     <style:list-level-label-alignment text:label-followed-by="listtab"/>
+    </style:list-level-properties>
+   </text:outline-level-style>
+  </text:outline-style>
+  <text:notes-configuration text:note-class="footnote" style:num-format="1" 
text:start-value="0" text:footnotes-position="page" 
text:start-numbering-at="document"/>
+  <text:notes-configuration text:note-class="endnote" style:num-format="i" 
text:start-value="0"/>
+  <text:linenumbering-configuration text:number-lines="false" 
text:offset="0.1965in" style:num-format="1" text:number-position="left" 
text:increment="5"/>
+  </office:styles>
+ <office:automatic-styles>
+  <style:style style:name="P1" style:family="paragraph" 
style:parent-style-name="Standard">
+   <style:paragraph-properties loext:text-indent="3in" 
style:auto-text-indent="false"/>
+   <style:text-properties/>
+  </style:style>
+  <style:style style:name="P2" style:family="paragraph" 
style:parent-style-name="Standard">
+   <style:paragraph-properties loext:text-indent="6em" 
style:auto-text-indent="false"/>
+   <style:text-properties/>
+  </style:style>
+  <style:page-layout style:name="pm1">
+   <style:page-layout-properties fo:page-width="8.2681in" 
fo:page-height="11.6929in" style:num-format="1" 
style:print-orientation="portrait" fo:margin-top="0.7874in" 
fo:margin-bottom="0.7874in" fo:margin-left="0.7874in" 
fo:margin-right="0.7874in" style:writing-mode="lr-tb" 
style:footnote-max-height="0in" loext:margin-gutter="0in">
+    <style:footnote-sep style:width="0.0071in" 
style:distance-before-sep="0.0398in" style:distance-after-sep="0.0398in" 
style:line-style="solid" style:adjustment="left" style:rel-width="25%" 
style:color="#000000"/>
+   </style:page-layout-properties>
+   <style:header-style/>
+   <style:footer-style/>
+  </style:page-layout>
+ </office:automatic-styles>
+ <office:master-styles>
+  <style:master-page style:name="Standard" style:page-layout-name="pm1"/>
+ </office:master-styles>
+ <office:body>
+  <office:text>
+   <text:sequence-decls>
+    <text:sequence-decl text:display-outline-level="0" 
text:name="Illustration"/>
+    <text:sequence-decl text:display-outline-level="0" text:name="Table"/>
+    <text:sequence-decl text:display-outline-level="0" text:name="Text"/>
+    <text:sequence-decl text:display-outline-level="0" text:name="Drawing"/>
+    <text:sequence-decl text:display-outline-level="0" text:name="Figure"/>
+   </text:sequence-decls>
+   <text:p text:style-name="P1">3in loext:text-indent</text:p>
+   <text:p text:style-name="P2">6em loext:text-indent</text:p>
+  </office:text>
+ </office:body>
+</office:document>
\ No newline at end of file
diff --git a/sw/qa/extras/odfexport/odfexport2.cxx 
b/sw/qa/extras/odfexport/odfexport2.cxx
index 38e4273915a4..d0353009a224 100644
--- a/sw/qa/extras/odfexport/odfexport2.cxx
+++ b/sw/qa/extras/odfexport/odfexport2.cxx
@@ -1800,6 +1800,25 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf163703)
                              + autostylename.toUtf8() + 
"']/style:text-properties";
     assertXPath(pXml, autoStyleXPath, "font-style", u"italic");
 }
+
+CPPUNIT_TEST_FIXTURE(Test, testTdf36709)
+{
+    // Verifies that loext:text-indent correctly round-trips
+    loadAndReload("tdf36709.fodt");
+    CPPUNIT_ASSERT_EQUAL(1, getPages());
+    xmlDocUniquePtr pXmlDoc = parseExport(u"content.xml"_ustr);
+
+    // Style P1 should have been rewritten as fo:text-indent
+    assertXPath(pXmlDoc, 
"//style:style[@style:name='P1']/style:paragraph-properties[@fo:text-indent]", 
1);
+    assertXPath(pXmlDoc, 
"//style:style[@style:name='P1']/style:paragraph-properties[@loext:text-indent]",
 0);
+    assertXPath(pXmlDoc, 
"//style:style[@style:name='P1']/style:paragraph-properties", "text-indent", 
u"3in");
+
+    // Style P2 should have round-tripped as loext:text-indent
+    assertXPath(pXmlDoc, 
"//style:style[@style:name='P2']/style:paragraph-properties[@fo:text-indent]", 
0);
+    assertXPath(pXmlDoc, 
"//style:style[@style:name='P2']/style:paragraph-properties[@loext:text-indent]",
 1);
+    assertXPath(pXmlDoc, 
"//style:style[@style:name='P2']/style:paragraph-properties", "text-indent", 
u"6em");
+}
+
 } // end of anonymous namespace
 CPPUNIT_PLUGIN_IMPLEMENT();
 
diff --git a/sw/source/core/text/itrcrsr.cxx b/sw/source/core/text/itrcrsr.cxx
index f1db92073cba..c393154777a0 100644
--- a/sw/source/core/text/itrcrsr.cxx
+++ b/sw/source/core/text/itrcrsr.cxx
@@ -299,6 +299,26 @@ 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/unocore/unomap1.cxx 
b/sw/source/core/unocore/unomap1.cxx
index f9e16b1f4260..d53bdac79dfb 100644
--- a/sw/source/core/unocore/unomap1.cxx
+++ b/sw/source/core/unocore/unomap1.cxx
@@ -25,6 +25,7 @@
 #include <com/sun/star/awt/Size.hpp>
 #include <com/sun/star/awt/XBitmap.hpp>
 #include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/Pair.hpp>
 #include <com/sun/star/beans/PropertyAttribute.hpp>
 #include <com/sun/star/beans/PropertyValue.hpp>
 #include <com/sun/star/container/XIndexContainer.hpp>
diff --git a/sw/source/core/unocore/unomapproperties.hxx 
b/sw/source/core/unocore/unomapproperties.hxx
index 01cae68a6de9..2401fbc42db7 100644
--- a/sw/source/core/unocore/unomapproperties.hxx
+++ b/sw/source/core/unocore/unomapproperties.hxx
@@ -158,6 +158,7 @@
         { UNO_NAME_PARA_RIGHT_MARGIN,                   RES_MARGIN_RIGHT,      
        cppu::UnoType<sal_Int32>::get(),     PropertyAttribute::MAYBEVOID, 
MID_R_MARGIN           | CONVERT_TWIPS }, \
         { UNO_NAME_PARA_IS_AUTO_FIRST_LINE_INDENT,      RES_MARGIN_FIRSTLINE,  
        cppu::UnoType<bool>::get(),          PropertyAttribute::MAYBEVOID, 
MID_FIRST_AUTO                         }, \
         { UNO_NAME_PARA_FIRST_LINE_INDENT,              RES_MARGIN_FIRSTLINE,  
        cppu::UnoType<sal_Int32>::get(),     PropertyAttribute::MAYBEVOID, 
MID_FIRST_LINE_INDENT  | CONVERT_TWIPS }, \
+        { UNO_NAME_PARA_FIRST_LINE_INDENT_UNIT,         RES_MARGIN_FIRSTLINE,  
        cppu::UnoType<css::beans::Pair<double, sal_Int16>>::get(), 
PropertyAttribute::MAYBEVOID, MID_FIRST_LINE_UNIT_INDENT }, \
         STANDARD_FONT_PROPERTIES \
         CJK_FONT_PROPERTIES \
         CTL_FONT_PROPERTIES \
@@ -422,6 +423,7 @@
                     { UNO_NAME_PARA_RIGHT_MARGIN_RELATIVE, RES_MARGIN_RIGHT, 
cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE,        MID_R_REL_MARGIN},\
                     { UNO_NAME_PARA_IS_AUTO_FIRST_LINE_INDENT, 
RES_MARGIN_FIRSTLINE, cppu::UnoType<bool>::get(),      PROPERTY_NONE, 
MID_FIRST_AUTO},\
                     { UNO_NAME_PARA_FIRST_LINE_INDENT, RES_MARGIN_FIRSTLINE, 
cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 
MID_FIRST_LINE_INDENT|CONVERT_TWIPS},\
+                    { UNO_NAME_PARA_FIRST_LINE_INDENT_UNIT, 
RES_MARGIN_FIRSTLINE, cppu::UnoType<css::beans::Pair<double, 
sal_Int16>>::get(), PROPERTY_NONE, MID_FIRST_LINE_UNIT_INDENT},\
                     { UNO_NAME_PARA_FIRST_LINE_INDENT_RELATIVE, 
RES_MARGIN_FIRSTLINE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 
MID_FIRST_LINE_REL_INDENT|CONVERT_TWIPS},\
                     { UNO_NAME_CHAR_KERNING, RES_CHRATR_KERNING    ,  
cppu::UnoType<sal_Int16>::get()  ,         PROPERTY_NONE,  CONVERT_TWIPS},\
                     { UNO_NAME_CHAR_NO_HYPHENATION, RES_CHRATR_NOHYPHEN   ,   
cppu::UnoType<bool>::get()  ,       PROPERTY_NONE,     0},\
@@ -545,6 +547,7 @@
                     { UNO_NAME_PARA_ADJUST, RES_PARATR_ADJUST,      
cppu::UnoType<sal_Int16>::get(),         PropertyAttribute::MAYBEVOID, 
MID_PARA_ADJUST}, \
                     { UNO_NAME_PARA_BOTTOM_MARGIN, RES_UL_SPACE,          
cppu::UnoType<sal_Int32>::get(),           PropertyAttribute::MAYBEVOID, 
MID_LO_MARGIN|CONVERT_TWIPS}, \
                     { UNO_NAME_PARA_FIRST_LINE_INDENT, RES_MARGIN_FIRSTLINE, 
cppu::UnoType<sal_Int32>::get(),      PropertyAttribute::MAYBEVOID, 
MID_FIRST_LINE_INDENT|CONVERT_TWIPS}, \
+                    { UNO_NAME_PARA_FIRST_LINE_INDENT_UNIT, 
RES_MARGIN_FIRSTLINE, cppu::UnoType<css::beans::Pair<double, 
sal_Int16>>::get(), PropertyAttribute::MAYBEVOID, MID_FIRST_LINE_UNIT_INDENT}, \
                     { UNO_NAME_PARA_LEFT_MARGIN, RES_MARGIN_TEXTLEFT, 
cppu::UnoType<sal_Int32>::get(),           PropertyAttribute::MAYBEVOID, 
MID_TXT_LMARGIN|CONVERT_TWIPS},   \
                     { UNO_NAME_PARA_LINE_SPACING, RES_PARATR_LINESPACING, 
cppu::UnoType<css::style::LineSpacing>::get(),       
PropertyAttribute::MAYBEVOID,     CONVERT_TWIPS},   \
                     { UNO_NAME_PARA_RIGHT_MARGIN, RES_MARGIN_RIGHT, 
cppu::UnoType<sal_Int32>::get(),           PropertyAttribute::MAYBEVOID, 
MID_R_MARGIN|CONVERT_TWIPS},  \
diff --git a/vcl/qa/cppunit/pdfexport/data/tdf36709.fodt 
b/vcl/qa/cppunit/pdfexport/data/tdf36709.fodt
new file mode 100644
index 000000000000..9aa29be8086b
--- /dev/null
+++ b/vcl/qa/cppunit/pdfexport/data/tdf36709.fodt
@@ -0,0 +1,151 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<office:document xmlns:css3t="http://www.w3.org/TR/css3-text/"; 
xmlns:grddl="http://www.w3.org/2003/g/data-view#"; 
xmlns:xhtml="http://www.w3.org/1999/xhtml"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xmlns:xsd="http://www.w3.org/2001/XMLSchema"; 
xmlns:xforms="http://www.w3.org/2002/xforms"; 
xmlns:dom="http://www.w3.org/2001/xml-events"; 
xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" 
xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" 
xmlns:math="http://www.w3.org/1998/Math/MathML"; 
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" 
xmlns:ooo="http://openoffice.org/2004/office"; 
xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" 
xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" 
xmlns:ooow="http://openoffice.org/2004/writer"; 
xmlns:xlink="http://www.w3.org/1999/xlink"; 
xmlns:drawooo="http://openoffice.org/2010/draw"; 
xmlns:oooc="http://openoffice.org/2004/calc"; 
xmlns:dc="http://purl.org/dc/elements/1.1/"; xmlns:c
 alcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" 
xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" 
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" 
xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" 
xmlns:tableooo="http://openoffice.org/2009/table"; 
xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" 
xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" 
xmlns:rpt="http://openoffice.org/2005/report"; 
xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0"
 xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" 
xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" 
xmlns:officeooo="http://openoffice.org/2009/office"; 
xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" 
xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" 
xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" 
xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:
 meta:1.0" 
xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0"
 office:version="1.4" office:mimetype="application/vnd.oasis.opendocument.text">
+ 
<office:meta><meta:creation-date>2024-10-31T13:46:50.189497642</meta:creation-date><meta:generator>LibreOfficeDev/25.2.0.0.alpha0$Linux_X86_64
 
LibreOffice_project/5f12ad737bbb930b76299df0433c8635ae27a7bd</meta:generator><dc:date>2024-10-31T13:49:19.200988598</dc:date><meta:editing-duration>PT2M30S</meta:editing-duration><meta:editing-cycles>1</meta:editing-cycles><meta:document-statistic
 meta:table-count="0" meta:image-count="0" meta:object-count="0" 
meta:page-count="1" meta:paragraph-count="8" meta:word-count="32" 
meta:character-count="144" 
meta:non-whitespace-character-count="120"/></office:meta>
+ <office:font-face-decls>
+  <style:font-face style:name="Liberation Serif" svg:font-family="'Liberation 
Serif'" style:font-family-generic="roman" style:font-pitch="variable"/>
+  <style:font-face style:name="Noto Sans1" svg:font-family="'Noto Sans'" 
style:font-family-generic="system" style:font-pitch="variable"/>
+  <style:font-face style:name="Noto Serif CJK SC" svg:font-family="'Noto Serif 
CJK SC'" style:font-family-generic="system" style:font-pitch="variable"/>
+ </office:font-face-decls>
+ <office:styles>
+  <style:default-style style:family="graphic">
+   <style:graphic-properties svg:stroke-color="#3465a4" 
draw:fill-color="#729fcf" fo:wrap-option="no-wrap" 
draw:shadow-offset-x="0.1181in" draw:shadow-offset-y="0.1181in" 
draw:start-line-spacing-horizontal="0.1114in" 
draw:start-line-spacing-vertical="0.1114in" 
draw:end-line-spacing-horizontal="0.1114in" 
draw:end-line-spacing-vertical="0.1114in" style:flow-with-text="false"/>
+   <style:paragraph-properties style:text-autospace="ideograph-alpha" 
style:line-break="strict" loext:tab-stop-distance="0in" 
style:writing-mode="lr-tb" style:font-independent-line-spacing="false">
+    <style:tab-stops/>
+   </style:paragraph-properties>
+   <style:text-properties style:use-window-font-color="true" 
loext:opacity="0%" style:font-name="Liberation Serif" fo:font-size="12pt" 
fo:language="en" fo:country="US" style:letter-kerning="true" 
style:font-name-asian="Noto Serif CJK SC" style:font-size-asian="10.5pt" 
style:language-asian="zh" style:country-asian="CN" 
style:font-name-complex="Noto Sans1" style:font-size-complex="12pt" 
style:language-complex="hi" style:country-complex="IN"/>
+  </style:default-style>
+  <style:default-style style:family="paragraph">
+   <style:paragraph-properties fo:orphans="2" fo:widows="2" 
fo:hyphenation-ladder-count="no-limit" fo:hyphenation-keep="auto" 
loext:hyphenation-keep-type="column" style:text-autospace="ideograph-alpha" 
style:punctuation-wrap="hanging" style:line-break="strict" 
style:tab-stop-distance="0.4925in" style:writing-mode="page"/>
+   <style:text-properties style:use-window-font-color="true" 
loext:opacity="0%" style:font-name="Liberation Serif" fo:font-size="12pt" 
fo:language="en" fo:country="US" style:letter-kerning="true" 
style:font-name-asian="Noto Serif CJK SC" style:font-size-asian="10.5pt" 
style:language-asian="zh" style:country-asian="CN" 
style:font-name-complex="Noto Sans1" style:font-size-complex="12pt" 
style:language-complex="hi" style:country-complex="IN" fo:hyphenate="false" 
fo:hyphenation-remain-char-count="2" fo:hyphenation-push-char-count="2" 
loext:hyphenation-no-caps="false" loext:hyphenation-no-last-word="false" 
loext:hyphenation-word-char-count="5" loext:hyphenation-zone="no-limit"/>
+  </style:default-style>
+  <style:default-style style:family="table">
+   <style:table-properties table:border-model="collapsing"/>
+  </style:default-style>
+  <style:default-style style:family="table-row">
+   <style:table-row-properties fo:keep-together="auto"/>
+  </style:default-style>
+  <style:style style:name="Standard" style:family="paragraph" 
style:class="text"/>
+  <text:outline-style style:name="Outline">
+   <text:outline-level-style text:level="1" style:num-format="">
+    <style:list-level-properties 
text:list-level-position-and-space-mode="label-alignment">
+     <style:list-level-label-alignment text:label-followed-by="listtab"/>
+    </style:list-level-properties>
+   </text:outline-level-style>
+   <text:outline-level-style text:level="2" style:num-format="">
+    <style:list-level-properties 
text:list-level-position-and-space-mode="label-alignment">
+     <style:list-level-label-alignment text:label-followed-by="listtab"/>
+    </style:list-level-properties>
+   </text:outline-level-style>
+   <text:outline-level-style text:level="3" style:num-format="">
+    <style:list-level-properties 
text:list-level-position-and-space-mode="label-alignment">
+     <style:list-level-label-alignment text:label-followed-by="listtab"/>
+    </style:list-level-properties>
+   </text:outline-level-style>
+   <text:outline-level-style text:level="4" style:num-format="">
+    <style:list-level-properties 
text:list-level-position-and-space-mode="label-alignment">
+     <style:list-level-label-alignment text:label-followed-by="listtab"/>
+    </style:list-level-properties>
+   </text:outline-level-style>
+   <text:outline-level-style text:level="5" style:num-format="">
+    <style:list-level-properties 
text:list-level-position-and-space-mode="label-alignment">
+     <style:list-level-label-alignment text:label-followed-by="listtab"/>
+    </style:list-level-properties>
+   </text:outline-level-style>
+   <text:outline-level-style text:level="6" style:num-format="">
+    <style:list-level-properties 
text:list-level-position-and-space-mode="label-alignment">
+     <style:list-level-label-alignment text:label-followed-by="listtab"/>
+    </style:list-level-properties>
+   </text:outline-level-style>
+   <text:outline-level-style text:level="7" style:num-format="">
+    <style:list-level-properties 
text:list-level-position-and-space-mode="label-alignment">
+     <style:list-level-label-alignment text:label-followed-by="listtab"/>
+    </style:list-level-properties>
+   </text:outline-level-style>
+   <text:outline-level-style text:level="8" style:num-format="">
+    <style:list-level-properties 
text:list-level-position-and-space-mode="label-alignment">
+     <style:list-level-label-alignment text:label-followed-by="listtab"/>
+    </style:list-level-properties>
+   </text:outline-level-style>
+   <text:outline-level-style text:level="9" style:num-format="">
+    <style:list-level-properties 
text:list-level-position-and-space-mode="label-alignment">
+     <style:list-level-label-alignment text:label-followed-by="listtab"/>
+    </style:list-level-properties>
+   </text:outline-level-style>
+   <text:outline-level-style text:level="10" style:num-format="">
+    <style:list-level-properties 
text:list-level-position-and-space-mode="label-alignment">
+     <style:list-level-label-alignment text:label-followed-by="listtab"/>
+    </style:list-level-properties>
+   </text:outline-level-style>
+  </text:outline-style>
+  <text:notes-configuration text:note-class="footnote" style:num-format="1" 
text:start-value="0" text:footnotes-position="page" 
text:start-numbering-at="document"/>
+  <text:notes-configuration text:note-class="endnote" style:num-format="i" 
text:start-value="0"/>
+  <text:linenumbering-configuration text:number-lines="false" 
text:offset="0.1965in" style:num-format="1" text:number-position="left" 
text:increment="5"/>
+  </office:styles>
+ <office:automatic-styles>
+  <style:style style:name="P1" style:family="paragraph" 
style:parent-style-name="Standard">
+   <style:paragraph-properties loext:text-indent="0em" 
style:auto-text-indent="false"/>
+   <style:text-properties/>
+  </style:style>
+  <style:style style:name="P2" style:family="paragraph" 
style:parent-style-name="Standard">
+   <style:paragraph-properties loext:text-indent="1em" 
style:auto-text-indent="false"/>
+   <style:text-properties/>
+  </style:style>
+  <style:style style:name="P3" style:family="paragraph" 
style:parent-style-name="Standard">
+   <style:paragraph-properties loext:text-indent="2em" 
style:auto-text-indent="false"/>
+   <style:text-properties/>
+  </style:style>
+  <style:style style:name="P4" style:family="paragraph" 
style:parent-style-name="Standard">
+   <style:paragraph-properties loext:text-indent="3em" 
style:auto-text-indent="false"/>
+   <style:text-properties/>
+  </style:style>
+  <style:style style:name="P5" style:family="paragraph" 
style:parent-style-name="Standard">
+   <style:text-properties/>
+  </style:style>
+  <style:style style:name="P6" style:family="paragraph" 
style:parent-style-name="Standard">
+   <style:paragraph-properties loext:text-indent="2em" 
style:auto-text-indent="false"/>
+   <style:text-properties/>
+  </style:style>
+  <style:style style:name="P7" style:family="paragraph" 
style:parent-style-name="Standard">
+   <style:paragraph-properties loext:text-indent="2em" 
style:auto-text-indent="false"/>
+   <style:text-properties fo:font-size="16pt" style:font-size-asian="16pt" 
style:font-size-complex="16pt"/>
+  </style:style>
+  <style:style style:name="P8" style:family="paragraph" 
style:parent-style-name="Standard">
+   <style:paragraph-properties loext:text-indent="2em" 
style:auto-text-indent="false"/>
+   <style:text-properties fo:font-size="20pt" style:font-size-asian="20pt" 
style:font-size-complex="20pt"/>
+  </style:style>
+  <style:style style:name="P9" style:family="paragraph" 
style:parent-style-name="Standard">
+   <style:paragraph-properties loext:text-indent="2em" 
style:auto-text-indent="false"/>
+   <style:text-properties fo:font-size="24pt" style:font-size-asian="24pt" 
style:font-size-complex="24pt"/>
+  </style:style>
+  <style:page-layout style:name="pm1">
+   <style:page-layout-properties fo:page-width="8.2681in" 
fo:page-height="11.6929in" style:num-format="1" 
style:print-orientation="portrait" fo:margin-top="0.7874in" 
fo:margin-bottom="0.7874in" fo:margin-left="0.7874in" 
fo:margin-right="0.7874in" style:writing-mode="lr-tb" 
style:footnote-max-height="0in" loext:margin-gutter="0in">
+    <style:footnote-sep style:width="0.0071in" 
style:distance-before-sep="0.0398in" style:distance-after-sep="0.0398in" 
style:line-style="solid" style:adjustment="left" style:rel-width="25%" 
style:color="#000000"/>
+   </style:page-layout-properties>
+   <style:header-style/>
+   <style:footer-style/>
+  </style:page-layout>
+ </office:automatic-styles>
+ <office:master-styles>
+  <style:master-page style:name="Standard" style:page-layout-name="pm1"/>
+ </office:master-styles>
+ <office:body>
+  <office:text>
+   <text:sequence-decls>
+    <text:sequence-decl text:display-outline-level="0" 
text:name="Illustration"/>
+    <text:sequence-decl text:display-outline-level="0" text:name="Table"/>
+    <text:sequence-decl text:display-outline-level="0" text:name="Text"/>
+    <text:sequence-decl text:display-outline-level="0" text:name="Drawing"/>
+    <text:sequence-decl text:display-outline-level="0" text:name="Figure"/>
+   </text:sequence-decls>
+   <text:p text:style-name="P1">0 em constant size</text:p>
+   <text:p text:style-name="P2">1 em constant size</text:p>
+   <text:p text:style-name="P3">2 em constant size</text:p>
+   <text:p text:style-name="P4">3 em constant size</text:p>
+   <text:p text:style-name="P5"/>
+   <text:p text:style-name="P6">2 em variable size</text:p>
+   <text:p text:style-name="P7">2 em variable size</text:p>
+   <text:p text:style-name="P8">2 em variable size</text:p>
+   <text:p text:style-name="P9">2 em variable size</text:p>
+  </office:text>
+ </office:body>
+</office:document>
\ No newline at end of file
diff --git a/vcl/qa/cppunit/pdfexport/pdfexport2.cxx 
b/vcl/qa/cppunit/pdfexport/pdfexport2.cxx
index 9a1a314ad3e7..72b2038b7d73 100644
--- a/vcl/qa/cppunit/pdfexport/pdfexport2.cxx
+++ b/vcl/qa/cppunit/pdfexport/pdfexport2.cxx
@@ -5822,6 +5822,57 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest2, 
testTdf140767SyriacJustification)
     CPPUNIT_ASSERT_LESS(90.0, aRect.at(5).getWidth());
 }
 
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf36709FirstLineIndentEm)
+{
+    saveAsPDF(u"tdf36709.fodt");
+
+    auto pPdfDocument = parsePDFExport();
+    CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
+
+    auto pPdfPage = pPdfDocument->openPage(/*nIndex*/ 0);
+    CPPUNIT_ASSERT(pPdfPage);
+    auto pTextPage = pPdfPage->getTextPage();
+    CPPUNIT_ASSERT(pTextPage);
+
+    int nPageObjectCount = pPdfPage->getObjectCount();
+
+    CPPUNIT_ASSERT_EQUAL(8, nPageObjectCount);
+
+    std::vector<OUString> aText;
+    std::vector<basegfx::B2DRectangle> aRect;
+
+    for (int i = 0; i < nPageObjectCount; ++i)
+    {
+        auto pPageObject = pPdfPage->getObject(i);
+        CPPUNIT_ASSERT_MESSAGE("no object", pPageObject != nullptr);
+        if (pPageObject->getType() == vcl::pdf::PDFPageObjectType::Text)
+        {
+            aText.push_back(pPageObject->getText(pTextPage));
+            aRect.push_back(pPageObject->getBounds());
+        }
+    }
+
+    CPPUNIT_ASSERT_EQUAL(size_t(8), aText.size());
+
+    CPPUNIT_ASSERT_EQUAL(u"0 em constant size"_ustr, aText.at(0).trim());
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(57.256, aRect.at(0).getMinX(), /*delta*/ 2.0);
+    CPPUNIT_ASSERT_EQUAL(u"1 em constant size"_ustr, aText.at(1).trim());
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(69.856, aRect.at(1).getMinX(), /*delta*/ 2.0);
+    CPPUNIT_ASSERT_EQUAL(u"2 em constant size"_ustr, aText.at(2).trim());
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(81.328, aRect.at(2).getMinX(), /*delta*/ 2.0);
+    CPPUNIT_ASSERT_EQUAL(u"3 em constant size"_ustr, aText.at(3).trim());
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(93.376, aRect.at(3).getMinX(), /*delta*/ 2.0);
+
+    CPPUNIT_ASSERT_EQUAL(u"2 em variable size"_ustr, aText.at(4).trim());
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(81.328, aRect.at(4).getMinX(), /*delta*/ 2.0);
+    CPPUNIT_ASSERT_EQUAL(u"2 em variable size"_ustr, aText.at(5).trim());
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(89.504, aRect.at(5).getMinX(), /*delta*/ 2.0);
+    CPPUNIT_ASSERT_EQUAL(u"2 em variable size"_ustr, aText.at(6).trim());
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(97.680, aRect.at(6).getMinX(), /*delta*/ 2.0);
+    CPPUNIT_ASSERT_EQUAL(u"2 em variable size"_ustr, aText.at(7).trim());
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(105.856, aRect.at(7).getMinX(), /*delta*/ 
2.0);
+}
+
 } // end anonymous namespace
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/xmloff/inc/xmlbahdl.hxx b/xmloff/inc/xmlbahdl.hxx
index 09e392d6cc6b..7736d6d2f432 100644
--- a/xmloff/inc/xmlbahdl.hxx
+++ b/xmloff/inc/xmlbahdl.hxx
@@ -68,6 +68,16 @@ public:
     virtual bool exportXML( OUString& rStrExpValue, const css::uno::Any& 
rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
 };
 
+/**
+    PropertyHandler for the XML-data-type: XML_TYPE_UNIT_MEASURE
+*/
+class XMLUnitMeasurePropHdl : public XMLPropertyHandler
+{
+public:
+    bool importXML( const OUString& rStrImpValue, css::uno::Any& rValue, const 
SvXMLUnitConverter& rUnitConverter ) const override;
+    bool exportXML( OUString& rStrExpValue, const css::uno::Any& rValue, const 
SvXMLUnitConverter& rUnitConverter ) const override;
+};
+
 /**
     PropertyHandler for the XML-data-type: XML_TYPE_PERCENT
 */
diff --git a/xmloff/inc/xmlprop.hxx b/xmloff/inc/xmlprop.hxx
index b7acca175a1d..8f47e328ff44 100644
--- a/xmloff/inc/xmlprop.hxx
+++ b/xmloff/inc/xmlprop.hxx
@@ -489,6 +489,7 @@ inline constexpr OUString PROP_ParaContextMargin = 
u"ParaContextMargin"_ustr;
 inline constexpr OUString PROP_ParaExpandSingleWord = 
u"ParaExpandSingleWord"_ustr;
 inline constexpr OUString PROP_ParaFirstLineIndent = 
u"ParaFirstLineIndent"_ustr;
 inline constexpr OUString PROP_ParaFirstLineIndentRelative = 
u"ParaFirstLineIndentRelative"_ustr;
+inline constexpr OUString PROP_ParaFirstLineIndentUnit = 
u"ParaFirstLineIndentUnit"_ustr;
 inline constexpr OUString PROP_ParaHyphenationMaxHyphens = 
u"ParaHyphenationMaxHyphens"_ustr;
 inline constexpr OUString PROP_ParaHyphenationMaxLeadingChars
     = u"ParaHyphenationMaxLeadingChars"_ustr;
diff --git a/xmloff/source/style/prhdlfac.cxx b/xmloff/source/style/prhdlfac.cxx
index a14d61ea31c2..fd6dae604bf8 100644
--- a/xmloff/source/style/prhdlfac.cxx
+++ b/xmloff/source/style/prhdlfac.cxx
@@ -195,6 +195,9 @@ std::unique_ptr<XMLPropertyHandler> 
XMLPropertyHandlerFactory::CreatePropertyHan
         case XML_TYPE_MEASURE16:
             pPropHdl.reset(new XMLMeasurePropHdl( 2 ));
             break;
+        case XML_TYPE_UNIT_MEASURE:
+            pPropHdl = std::make_unique<XMLUnitMeasurePropHdl>();
+            break;
         case XML_TYPE_PERCENT :
             pPropHdl.reset(new XMLPercentPropHdl( 4 ));
             break;
diff --git a/xmloff/source/style/xmlbahdl.cxx b/xmloff/source/style/xmlbahdl.cxx
index a0aea8ccb1a4..02c271ad6a2e 100644
--- a/xmloff/source/style/xmlbahdl.cxx
+++ b/xmloff/source/style/xmlbahdl.cxx
@@ -27,6 +27,7 @@
 #include <sax/tools/converter.hxx>
 #include <xmloff/xmluconv.hxx>
 #include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/beans/Pair.hpp>
 #include <xmloff/xmltoken.hxx>
 
 #include <limits.h>
@@ -207,6 +208,58 @@ bool XMLMeasurePropHdl::exportXML( OUString& rStrExpValue, 
const Any& rValue, co
 }
 
 
+bool XMLUnitMeasurePropHdl::importXML( const OUString& rStrImpValue, Any& 
rValue, const SvXMLUnitConverter& ) const
+{
+    double fValue = 0.0;
+    std::optional<sal_Int16> nValueUnit;
+
+    auto bRet = ::sax::Converter::convertMeasureUnit( fValue, nValueUnit, 
rStrImpValue );
+
+    if(bRet)
+    {
+        // This importer may only accept font-relative units.
+        // Discard all other units to allow fall-through to other attributes.
+        if (css::util::MeasureUnit::FONT_EM != nValueUnit
+            && css::util::MeasureUnit::FONT_IC != nValueUnit)
+        {
+            return false;
+        }
+
+        css::beans::Pair<double, sal_Int16> stValue{fValue, 
nValueUnit.value()};
+        rValue <<= stValue;
+    }
+
+    return bRet;
+}
+
+bool XMLUnitMeasurePropHdl::exportXML( OUString& rStrExpValue, const Any& 
rValue, const SvXMLUnitConverter& ) const
+{
+    bool bRet = false;
+    css::beans::Pair<double, sal_Int16> stValue{0.0, 
css::util::MeasureUnit::MM_100TH};
+
+    if( rValue >>= stValue )
+    {
+        auto [fValue, nValueUnit] = stValue;
+
+        // This exporter may only produce font-relative units.
+        // Discard all other units to allow fall-through to other attributes.
+        if (css::util::MeasureUnit::FONT_EM != nValueUnit
+            && css::util::MeasureUnit::FONT_IC != nValueUnit)
+        {
+            return false;
+        }
+
+        OUStringBuffer aOut;
+        ::sax::Converter::convertMeasureUnit( aOut, fValue, nValueUnit );
+        rStrExpValue = aOut.makeStringAndClear();
+
+        bRet = true;
+    }
+
+    return bRet;
+}
+
+
 XMLBoolFalsePropHdl::~XMLBoolFalsePropHdl()
 {
     // nothing to do
diff --git a/xmloff/source/text/txtprmap.cxx b/xmloff/source/text/txtprmap.cxx
index 25cb36ef1b0d..8553965c67d0 100644
--- a/xmloff/source/text/txtprmap.cxx
+++ b/xmloff/source/text/txtprmap.cxx
@@ -366,6 +366,9 @@ XMLPropertyMapEntry constexpr aXMLParaPropMap[] =
 
     MP_E( PROP_ParaFirstLineIndent,         XML_NAMESPACE_FO,    
XML_TEXT_INDENT,      XML_TYPE_MEASURE|MID_FLAG_MULTI_PROPERTY, 
CTF_PARAFIRSTLINE ),
     MP_E( PROP_ParaFirstLineIndentRelative, XML_NAMESPACE_FO,    
XML_TEXT_INDENT,      XML_TYPE_PERCENT,                         
CTF_PARAFIRSTLINE_REL ),
+    MAP_EXT( PROP_ParaFirstLineIndentUnit, XML_NAMESPACE_LO_EXT, 
XML_TEXT_INDENT, 
XML_TYPE_PROP_PARAGRAPH|XML_TYPE_UNIT_MEASURE|MID_FLAG_MULTI_PROPERTY, 0 ),
+    MAP_EXT_I( PROP_ParaFirstLineIndent, XML_NAMESPACE_LO_EXT, 
XML_TEXT_INDENT, 
XML_TYPE_PROP_PARAGRAPH|XML_TYPE_MEASURE|MID_FLAG_MULTI_PROPERTY, 
CTF_PARAFIRSTLINE ),
+    MAP_EXT_I( PROP_ParaFirstLineIndentRelative, XML_NAMESPACE_LO_EXT, 
XML_TEXT_INDENT, XML_TYPE_PROP_PARAGRAPH|XML_TYPE_PERCENT, 
CTF_PARAFIRSTLINE_REL ),
     MP_E( PROP_ParaIsAutoFirstLineIndent,   XML_NAMESPACE_STYLE, 
XML_AUTO_TEXT_INDENT, XML_TYPE_BOOL,                            0 ),
     // RES_PAGEDESC
     MP_E( PROP_PageDescName,     XML_NAMESPACE_STYLE, XML_MASTER_PAGE_NAME, 
MID_FLAG_SPECIAL_ITEM|XML_TYPE_STYLENAME,            CTF_PAGEDESCNAME ),

Reply via email to