sw/source/core/text/portxt.cxx | 21 ++++ sw/source/core/txtnode/fntcache.cxx | 14 ++- vcl/qa/cppunit/pdfexport/data/tdf163105.fodt | 115 +++++++++++++++++++++++++++ vcl/qa/cppunit/pdfexport/pdfexport2.cxx | 44 ++++++++++ 4 files changed, 190 insertions(+), 4 deletions(-)
New commits: commit 41671d732ad933175779b61159b56824ff77b2fe Author: Jonathan Clark <[email protected]> AuthorDate: Tue Sep 24 14:51:56 2024 -0600 Commit: Jonathan Clark <[email protected]> CommitDate: Wed Sep 25 03:22:38 2024 +0200 tdf#163105 sw: Add some whitespace expansion to kashida justification Tihs change updates Writer kashida justification to include some whitespace expansion, mirroring the behavior of Edit Engine and other word processor programs. Each kashida and space character are given 1 unit each of extra space. Change-Id: I8c9031a0d51844e532b9d1f7e3619c2c9ba23f6d Reviewed-on: https://gerrit.libreoffice.org/c/core/+/173884 Tested-by: Jenkins Reviewed-by: Jonathan Clark <[email protected]> diff --git a/sw/source/core/text/portxt.cxx b/sw/source/core/text/portxt.cxx index bd64b6336a15..4c71e5da2c29 100644 --- a/sw/source/core/text/portxt.cxx +++ b/sw/source/core/text/portxt.cxx @@ -43,6 +43,11 @@ using namespace ::sw::mark; using namespace ::com::sun::star; using namespace ::com::sun::star::i18n::ScriptType; +static TextFrameIndex lcl_AddSpace_Latin(const SwTextSizeInfo& rInf, const OUString* pStr, + const SwLinePortion& rPor, TextFrameIndex nPos, + TextFrameIndex nEnd, const SwScriptInfo* pSI, + sal_uInt8 nScript); + // Returns for how many characters an extra space has to be added // (for justified alignment). static TextFrameIndex lcl_AddSpace(const SwTextSizeInfo &rInf, @@ -117,7 +122,11 @@ static TextFrameIndex lcl_AddSpace(const SwTextSizeInfo &rInf, // i60591: need to check result of KashidaJustify // determine if kashida justification is applicable if (nKashRes != -1) - return TextFrameIndex(nKashRes); + { + // tdf#163105: For kashida justification, also expand whitespace. + auto nCntLatin = lcl_AddSpace_Latin(rInf, pStr, rPor, nPos, nEnd, pSI, nScript); + return nCntLatin + TextFrameIndex{ nKashRes }; + } } } @@ -144,6 +153,16 @@ static TextFrameIndex lcl_AddSpace(const SwTextSizeInfo &rInf, } } + return lcl_AddSpace_Latin(rInf, pStr, rPor, nPos, nEnd, pSI, nScript); +} + +static TextFrameIndex lcl_AddSpace_Latin(const SwTextSizeInfo& rInf, const OUString* pStr, + const SwLinePortion& rPor, TextFrameIndex nPos, + TextFrameIndex nEnd, const SwScriptInfo* pSI, + sal_uInt8 nScript) +{ + TextFrameIndex nCnt(0); + // Here starts the good old "Look for blanks and add space to them" part. // Note: We do not want to add space to an isolated latin blank in front // of some complex characters in RTL environment diff --git a/sw/source/core/txtnode/fntcache.cxx b/sw/source/core/txtnode/fntcache.cxx index 1e1fc24e0a5f..8b68e29b9ec3 100644 --- a/sw/source/core/txtnode/fntcache.cxx +++ b/sw/source/core/txtnode/fntcache.cxx @@ -1135,7 +1135,9 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf ) rInf.GetLen(), nSpaceAdd ) != -1 ) { bSpecialJust = true; - nSpaceAdd = 0; + + // Intentionally do not clear nSpaceAdd for kashida justification. + // The rest of the space will be handled below. } else aKashidaArray.clear(); @@ -1348,7 +1350,10 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf ) if ( pSI && pSI->CountKashida() && pSI->KashidaJustify( &aKernArray, aKashidaArray.data(), rInf.GetIdx(), rInf.GetLen(), nSpaceAdd ) != -1 ) - nSpaceAdd = 0; + { + // Intentionally do not clear nSpaceAdd for kashida justification. + // The rest of the space will be handled below. + } else { aKashidaArray.clear(); @@ -1838,7 +1843,10 @@ TextFrameIndex SwFntObj::GetModelPositionForViewPoint(SwDrawTextInfo &rInf) if ( pSI && pSI->CountKashida() && pSI->KashidaJustify( &aKernArray, nullptr, rInf.GetIdx(), rInf.GetLen(), nSpaceAdd ) != -1 ) - nSpaceAdd = 0; + { + // Intentionally do not clear nSpaceAdd for kashida justification. + // The rest of the space will be handled below. + } } } diff --git a/vcl/qa/cppunit/pdfexport/data/tdf163105.fodt b/vcl/qa/cppunit/pdfexport/data/tdf163105.fodt new file mode 100644 index 000000000000..eae83caedc1d --- /dev/null +++ b/vcl/qa/cppunit/pdfexport/data/tdf163105.fodt @@ -0,0 +1,115 @@ +<?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.3" office:mimetype="application/vnd.oasis.opendocument.text"> + <office:meta><meta:creation-date>2024-09-24T15:20:14.511295513</meta:creation-date><dc:date>2024-09-24T15:21:44.061753584</dc:date><meta:editing-duration>PT1M30S</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="1" meta:word-count="2" meta:character-count="12" meta:non-whitespace-character-count="10"/><meta:generator>LibreOffice/24.2.6.2$Linux_X86_64 LibreOffice_project/8e9a753d9daaea75c34b417ba1bdf556bf2fc5b3</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="Mangal" svg:font-family="Mangal"/> + <style:font-face style:name="Noto Sans" svg:font-family="'Noto Sans'" style:font-adornments="Regular" style:font-family-generic="swiss" style:font-pitch="variable"/> + <style:font-face style:name="Noto Sans Arabic" svg:font-family="'Noto Sans Arabic'" style:font-adornments="Regular" style:font-family-generic="swiss" style:font-pitch="variable"/> + <style:font-face style:name="Noto Sans CJK SC" svg:font-family="'Noto Sans CJK SC'" style:font-family-generic="swiss"/> + </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 Sans CJK SC" style:font-size-asian="10.5pt" style:language-asian="ja" style:country-asian="JP" style:font-name-complex="Mangal" 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" 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 Sans CJK SC" style:font-size-asian="10.5pt" style:language-asian="ja" style:country-asian="JP" style:font-name-complex="Mangal" 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 Sans" fo:font-family="'Noto Sans'" style:font-style-name="Regular" style:font-family-generic="swiss" style:font-pitch="variable" style:font-name-complex="Noto Sans Arabic" style:font-family-complex="'Noto Sans Arabic'" style:font-style-name-complex="Regular" style:font-family-generic-complex="swiss" style:font-pitch-complex="variable"/> + </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:paragraph-properties fo:text-align="justify" style:justify-single-word="false" style:writing-mode="rl-tb"/> + </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">نویسه کشیده<text:line-break/></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 974e26dda193..390dbc203438 100644 --- a/vcl/qa/cppunit/pdfexport/pdfexport2.cxx +++ b/vcl/qa/cppunit/pdfexport/pdfexport2.cxx @@ -5624,6 +5624,50 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf151748KashidaSpace) CPPUNIT_ASSERT_EQUAL(u"توسط"_ustr, aText.at(16).trim()); } +// tdf#163105 - Writer kashida justification should expand spaces +CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf163105SwKashidaSpaceExpansion) +{ + saveAsPDF(u"tdf163105.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(5, nPageObjectCount); + + std::vector<OUString> aText; + std::vector<basegfx::B2DRectangle> aRect; + + int nTextObjectCount = 0; + 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()); + ++nTextObjectCount; + } + } + + CPPUNIT_ASSERT_EQUAL(5, nTextObjectCount); + + CPPUNIT_ASSERT_EQUAL(u"یده"_ustr, aText.at(0).trim()); + CPPUNIT_ASSERT_EQUAL(u""_ustr, aText.at(1).trim()); + CPPUNIT_ASSERT_EQUAL(u"ه کش"_ustr, aText.at(2).trim()); // This span is whitespace justified + CPPUNIT_ASSERT_EQUAL(u""_ustr, aText.at(3).trim()); + CPPUNIT_ASSERT_EQUAL(u"نویس"_ustr, aText.at(4).trim()); + + // Without the fix, this will be less than 25 + CPPUNIT_ASSERT_GREATER(150.0, aRect.at(2).getWidth()); +} + } // end anonymous namespace CPPUNIT_PLUGIN_IMPLEMENT();
