summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Clark <jonathan@libreoffice.org>2024-12-04 01:43:37 -0700
committerAdolfo Jayme Barrientos <fitojb@ubuntu.com>2024-12-07 20:37:16 +0100
commit1221b01653240a515fc8a88c70ae2e06fc5ef57b (patch)
tree38f15659508bdb8ef836f1197b6ac2c76e53729a
parent1b4b1e0659d7dff79d031ff5c8fbb34fad2c71a1 (diff)
tdf#162750 sw: Fix layout with small caps inside ligatures
Previously, Writer was not correctly terminating layout contexts at the starts of small caps spans. This could cause incorrect character placement in certain cases. Regression since: Commit 30d376fb7ded4c96c85ad1112a0e44b5929657c9 "tdf#61444 Correct Writer text layout across formatting changes" and Commit ab0a4543cab77ae0c7c0a79feb8aebab71163dd7 "tdf#124116 Correct Writer text shaping across formatting changes" Change-Id: I863b9b66356eb0a9efb5bbdc75e80b43d56aaaf0 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/177839 Reviewed-by: Jonathan Clark <jonathan@libreoffice.org> Tested-by: Jenkins (cherry picked from commit dfa81bdb3a7956d631c8ccb1e00166289d37993e) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/177865 Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org> Signed-off-by: Xisco Fauli <xiscofauli@libreoffice.org> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/178042 Reviewed-by: Adolfo Jayme Barrientos <fitojb@ubuntu.com>
-rw-r--r--sw/source/core/text/itratr.cxx88
-rw-r--r--sw/source/core/text/itratr.hxx1
-rw-r--r--sw/source/core/text/itrform2.cxx4
-rw-r--r--vcl/qa/cppunit/pdfexport/data/tdf162750.fodt119
-rw-r--r--vcl/qa/cppunit/pdfexport/pdfexport2.cxx37
5 files changed, 248 insertions, 1 deletions
diff --git a/sw/source/core/text/itratr.cxx b/sw/source/core/text/itratr.cxx
index ae38bda0bab3..800d6f5781ac 100644
--- a/sw/source/core/text/itratr.cxx
+++ b/sw/source/core/text/itratr.cxx
@@ -23,6 +23,7 @@
#include <hintids.hxx>
#include <editeng/charscaleitem.hxx>
+#include <editeng/cmapitem.hxx>
#include <svl/itemiter.hxx>
#include <svx/svdobj.hxx>
#include <vcl/svapp.hxx>
@@ -838,6 +839,93 @@ TextFrameIndex SwAttrIter::GetNextAttr() const
}
}
+namespace
+{
+class FormatBreakTracker
+{
+private:
+ std::optional<SvxCaseMap> m_nCaseMap;
+
+ bool m_bNeedsBreak = false;
+
+ void SetCaseMap(SvxCaseMap nValue)
+ {
+ if (m_nCaseMap != nValue)
+ m_bNeedsBreak = true;
+
+ m_nCaseMap = nValue;
+ }
+
+public:
+ void HandleItemSet(const SfxItemSet& rSet)
+ {
+ if (const SvxCaseMapItem* pItem = rSet.GetItem(RES_CHRATR_CASEMAP))
+ SetCaseMap(pItem->GetCaseMap());
+ }
+
+ void Reset() { m_bNeedsBreak = false; }
+
+ bool NeedsBreak() const { return m_bNeedsBreak; }
+};
+
+bool HasFormatBreakAttribute(FormatBreakTracker* pTracker, const SwTextAttr* pAttr)
+{
+ pTracker->Reset();
+
+ switch (pAttr->Which())
+ {
+ case RES_TXTATR_AUTOFMT:
+ case RES_TXTATR_CHARFMT:
+ {
+ const SfxItemSet& rSet((pAttr->Which() == RES_TXTATR_CHARFMT)
+ ? static_cast<SfxItemSet const&>(
+ pAttr->GetCharFormat().GetCharFormat()->GetAttrSet())
+ : *pAttr->GetAutoFormat().GetStyleHandle());
+
+ pTracker->HandleItemSet(rSet);
+ }
+ break;
+ }
+
+ if (pAttr->IsFormatIgnoreStart() || pAttr->IsFormatIgnoreEnd())
+ pTracker->Reset();
+
+ return pTracker->NeedsBreak();
+}
+}
+
+TextFrameIndex SwAttrIter::GetNextLayoutBreakAttr() const
+{
+ size_t nStartIndex(m_nStartIndex);
+ SwTextNode const* pTextNode(m_pTextNode);
+
+ sal_Int32 nNext = std::numeric_limits<sal_Int32>::max();
+
+ auto* pHints = pTextNode->GetpSwpHints();
+ if (!pHints)
+ {
+ return TextFrameIndex{ nNext };
+ }
+
+ FormatBreakTracker stTracker;
+ stTracker.HandleItemSet(pTextNode->GetSwAttrSet());
+
+ for (size_t i = 0; i < pHints->Count(); ++i)
+ {
+ SwTextAttr* const pAttr(pHints->Get(i));
+ if (HasFormatBreakAttribute(&stTracker, pAttr))
+ {
+ if (i >= nStartIndex)
+ {
+ nNext = pAttr->GetStart();
+ break;
+ }
+ }
+ }
+
+ return TextFrameIndex{ nNext };
+}
+
namespace {
class SwMinMaxArgs
diff --git a/sw/source/core/text/itratr.hxx b/sw/source/core/text/itratr.hxx
index 91b3d4f9200c..d61d112404eb 100644
--- a/sw/source/core/text/itratr.hxx
+++ b/sw/source/core/text/itratr.hxx
@@ -87,6 +87,7 @@ public:
// The parameter returns the position of the next change before or at the
// char position.
TextFrameIndex GetNextAttr() const;
+ TextFrameIndex GetNextLayoutBreakAttr() const;
/// Enables the attributes used at char pos nPos in the logical font
bool Seek(TextFrameIndex nPos);
// Creates the font at the specified position via Seek() and checks
diff --git a/sw/source/core/text/itrform2.cxx b/sw/source/core/text/itrform2.cxx
index 06b7361550cf..16fe77e5f12b 100644
--- a/sw/source/core/text/itrform2.cxx
+++ b/sw/source/core/text/itrform2.cxx
@@ -1385,6 +1385,8 @@ SwTextPortion *SwTextFormatter::NewTextPortion( SwTextFormatInfo &rInf )
// until next attribute change:
const TextFrameIndex nNextAttr = GetNextAttr();
+ // until next layout-breaking attribute change:
+ const TextFrameIndex nNextLayoutBreakAttr = GetNextLayoutBreakAttr();
// end of script type:
const TextFrameIndex nNextScript = m_pScriptInfo->NextScriptChg(rInf.GetIdx());
// end of direction:
@@ -1394,7 +1396,7 @@ SwTextPortion *SwTextFormatter::NewTextPortion( SwTextFormatInfo &rInf )
// bookmarks
const TextFrameIndex nNextBookmark = m_pScriptInfo->NextBookmark(rInf.GetIdx());
- auto nNextContext = std::min({ nNextChg, nNextScript, nNextDir });
+ auto nNextContext = std::min({ nNextChg, nNextLayoutBreakAttr, nNextScript, nNextDir });
nNextChg = std::min({ nNextChg, nNextAttr, nNextScript, nNextDir, nNextHidden, nNextBookmark });
// Turbo boost:
diff --git a/vcl/qa/cppunit/pdfexport/data/tdf162750.fodt b/vcl/qa/cppunit/pdfexport/data/tdf162750.fodt
new file mode 100644
index 000000000000..be345551e91c
--- /dev/null
+++ b/vcl/qa/cppunit/pdfexport/data/tdf162750.fodt
@@ -0,0 +1,119 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<office:document xmlns:css3t="http://www.w3.org/TR/css3-text/" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:rpt="http://openoffice.org/2005/report" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:officeooo="http://openoffice.org/2009/office" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" office:version="1.4" office:mimetype="application/vnd.oasis.opendocument.text">
+ <office:meta><meta:creation-date>2024-12-04T21:34:47.433434835</meta:creation-date><dc:date>2024-12-04T21:37:36.661647530</dc:date><meta:editing-duration>PT2M49S</meta:editing-duration><meta:editing-cycles>2</meta:editing-cycles><meta:generator>LibreOfficeDev/25.2.0.0.alpha1$Linux_X86_64 LibreOffice_project/277d26808adc6812e17b910a6300006fe03f4614</meta:generator><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="2" meta:character-count="6" meta:non-whitespace-character-count="6"/></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 Serif" svg:font-family="'Noto Serif'" style:font-adornments="Regular" style:font-family-generic="roman" style:font-pitch="variable"/>
+ <style:font-face style:name="Noto Serif CJK JP" svg:font-family="'Noto Serif CJK JP'" style:font-family-generic="system" style:font-pitch="variable"/>
+ <style:font-face style:name="Tahoma1" svg:font-family="Tahoma" 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 JP" style:font-size-asian="10.5pt" style:language-asian="ja" style:country-asian="JP" style:font-name-complex="Tahoma1" style:font-size-complex="12pt" style:language-complex="ar" style:country-complex="SA"/>
+ </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 JP" style:font-size-asian="10.5pt" style:language-asian="ja" style:country-asian="JP" style:font-name-complex="Tahoma1" style:font-size-complex="12pt" style:language-complex="ar" style:country-complex="SA" 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">
+ <style:text-properties style:font-name="Noto Serif" fo:font-family="'Noto Serif'" style:font-style-name="Regular" style:font-family-generic="roman" style:font-pitch="variable" fo:font-size="60pt"/>
+ </style:style>
+ <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:text-properties/>
+ </style:style>
+ <style:style style:name="T1" style:family="text">
+ <style:text-properties fo:font-variant="small-caps"/>
+ </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">ffi</text:p>
+ <text:p text:style-name="P1">f<text:span text:style-name="T1">fi</text:span></text:p>
+ <text:p text:style-name="P1"/>
+ </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 c8527d71dd9a..541bb5f8009e 100644
--- a/vcl/qa/cppunit/pdfexport/pdfexport2.cxx
+++ b/vcl/qa/cppunit/pdfexport/pdfexport2.cxx
@@ -5682,6 +5682,43 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf151748KashidaSpace)
CPPUNIT_ASSERT_EQUAL(u"توسط"_ustr, aText.at(16).trim());
}
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf162750SmallCapsLigature)
+{
+ aMediaDescriptor[u"FilterName"_ustr] <<= u"writer_pdf_Export"_ustr;
+ saveAsPDF(u"tdf162750.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(3, nPageObjectCount);
+
+ std::vector<OUString> aText;
+ 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));
+ }
+ }
+
+ CPPUNIT_ASSERT_EQUAL(size_t(3), aText.size());
+ CPPUNIT_ASSERT_EQUAL(u"ffi"_ustr, aText.at(0).trim());
+
+ // Without the fix, this will be "ffi"
+ CPPUNIT_ASSERT_EQUAL(u"f"_ustr, aText.at(1).trim());
+
+ CPPUNIT_ASSERT_EQUAL(u"FI"_ustr, aText.at(2).trim());
+}
+
} // end anonymous namespace
CPPUNIT_PLUGIN_IMPLEMENT();