diff options
author | Jonathan Clark <jonathan@libreoffice.org> | 2024-08-30 00:21:12 -0600 |
---|---|---|
committer | Xisco Fauli <xiscofauli@libreoffice.org> | 2024-09-03 08:58:22 +0200 |
commit | 9d45184354c9f4b6475fa845bea8cc425babed49 (patch) | |
tree | 650d41dc3b698f986124533a81d73e78798a5ed6 | |
parent | 149ca1e0a8123b8722ecb74f6f5e8e5080ce6052 (diff) |
tdf#151748 editeng: Improve kashida position validation
Previously, editeng did not validate whether kashida insertion positions
had enough room for at least a single kashida glyph. This caused kashida
glyphs to overlap other characters in some situations.
Editeng will now drop candidate kashida insertion positions from the
beginning of the line, until there is enough room to safely justify the
remaining text. This approximates Writer's behavior.
Change-Id: I804cae72503332bea8dc9e60cdfe08bd3429dc52
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/172641
Reviewed-by: Jonathan Clark <jonathan@libreoffice.org>
Tested-by: Jenkins
(cherry picked from commit 937023bca427f803a9e7085d5090d5d2b17623ed)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/172670
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
-rw-r--r-- | editeng/source/editeng/impedit.hxx | 3 | ||||
-rw-r--r-- | editeng/source/editeng/impedit3.cxx | 40 | ||||
-rw-r--r-- | vcl/qa/cppunit/pdfexport/data/tdf151748.fodt | 160 | ||||
-rw-r--r-- | vcl/qa/cppunit/pdfexport/pdfexport2.cxx | 61 |
4 files changed, 255 insertions, 9 deletions
diff --git a/editeng/source/editeng/impedit.hxx b/editeng/source/editeng/impedit.hxx index 4a42a012d7ac..4b6679a640b5 100644 --- a/editeng/source/editeng/impedit.hxx +++ b/editeng/source/editeng/impedit.hxx @@ -719,7 +719,8 @@ private: bool ImplHasText() const; - void ImpFindKashidas( ContentNode* pNode, sal_Int32 nStart, sal_Int32 nEnd, std::vector<sal_Int32>& rArray ); + void ImpFindKashidas(ContentNode* pNode, sal_Int32 nStart, sal_Int32 nEnd, + std::vector<sal_Int32>& rArray, sal_Int32 nRemainingSpace); void InsertContent(std::unique_ptr<ContentNode> pNode, sal_Int32 nPos); EditPaM SplitContent( sal_Int32 nNode, sal_Int32 nSepPos ); diff --git a/editeng/source/editeng/impedit3.cxx b/editeng/source/editeng/impedit3.cxx index f1d9a706c9e0..8d6abb99920b 100644 --- a/editeng/source/editeng/impedit3.cxx +++ b/editeng/source/editeng/impedit3.cxx @@ -2295,17 +2295,16 @@ void ImpEditEngine::ImpAdjustBlocks(ParaPortion& rParaPortion, EditLine& rLine, std::vector<sal_Int32> aPositions; // Kashidas ? - ImpFindKashidas( pNode, nFirstChar, nLastChar, aPositions ); + ImpFindKashidas(pNode, nFirstChar, nLastChar, aPositions, nRemainingSpace); auto nKashidas = aPositions.size(); sal_uInt16 nLastScript = i18n::ScriptType::LATIN; for ( sal_Int32 nChar = nFirstChar; nChar <= nLastChar; nChar++ ) { EditPaM aPaM( pNode, nChar+1 ); - LanguageType eLang = GetLanguage(aPaM).nLang; sal_uInt16 nScript = GetI18NScriptType(aPaM); // Arabic script is handled above, but if no Kashida positions are found, use blanks. - if (MsLangId::getPrimaryLanguage(eLang) == LANGUAGE_ARABIC_PRIMARY_ONLY && nKashidas) + if (nKashidas) continue; if ( pNode->GetChar(nChar) == ' ' ) @@ -2337,8 +2336,7 @@ void ImpEditEngine::ImpAdjustBlocks(ParaPortion& rParaPortion, EditLine& rLine, // If the last character is a blank, it is rejected! // The width must be distributed to the blockers in front... // But not if it is the only one. - if ( ( pNode->GetChar( nLastChar ) == ' ' ) && ( aPositions.size() > 1 ) && - ( MsLangId::getPrimaryLanguage( GetLanguage( EditPaM( pNode, nLastChar ) ).nLang ) != LANGUAGE_ARABIC_PRIMARY_ONLY ) ) + if ((pNode->GetChar(nLastChar) == ' ') && (aPositions.size() > 1) && (!nKashidas)) { aPositions.pop_back(); sal_Int32 nPortionStart, nPortion; @@ -2416,13 +2414,16 @@ void ImpEditEngine::ImpAdjustBlocks(ParaPortion& rParaPortion, EditLine& rLine, } // For Kashidas from sw/source/core/text/porlay.cxx -void ImpEditEngine::ImpFindKashidas( ContentNode* pNode, sal_Int32 nStart, sal_Int32 nEnd, std::vector<sal_Int32>& rArray ) +void ImpEditEngine::ImpFindKashidas(ContentNode* pNode, sal_Int32 nStart, sal_Int32 nEnd, + std::vector<sal_Int32>& rArray, sal_Int32 nRemainingSpace) { // Kashida glyph looks suspicious, skip Kashida justification if (GetRefDevice()->GetMinKashida() <= 0) return; std::vector<sal_Int32> aKashidaArray; + std::vector<sal_Int32> aMinKashidaArray; + sal_Int32 nTotalMinKashida = 0U; // the search has to be performed on a per word base @@ -2431,6 +2432,7 @@ void ImpEditEngine::ImpFindKashidas( ContentNode* pNode, sal_Int32 nStart, sal_I if ( aWordSel.Min().GetIndex() < nStart ) aWordSel.Min().SetIndex( nStart ); + SvxFont aTmpFont(pNode->GetCharAttribs().GetDefFont()); while ( ( aWordSel.Min().GetNode() == pNode ) && ( aWordSel.Min().GetIndex() < nEnd ) ) { const sal_Int32 nSavPos = aWordSel.Max().GetIndex(); @@ -2588,13 +2590,35 @@ void ImpEditEngine::ImpFindKashidas( ContentNode* pNode, sal_Int32 nStart, sal_I ++nIdx; } // end of current word - if ( nKashidaPos>=0 ) - aKashidaArray.push_back( nKashidaPos ); + if (nKashidaPos >= 0) + { + SeekCursor(pNode, nKashidaPos + 1, aTmpFont); + aTmpFont.SetPhysFont(*GetRefDevice()); + + auto nMinKashidaWidth = GetRefDevice()->GetMinKashida(); + nTotalMinKashida += nMinKashidaWidth; + aMinKashidaArray.push_back(nMinKashidaWidth); + + aKashidaArray.push_back(nKashidaPos); + } aWordSel = WordRight( aWordSel.Max(), css::i18n::WordType::DICTIONARY_WORD ); aWordSel = SelectWord( aWordSel, css::i18n::WordType::DICTIONARY_WORD ); } + // Greedily reject kashida positions from start-to-end until there is enough room. + // This will push kashida justification away from the start of the line. + std::reverse(aKashidaArray.begin(), aKashidaArray.end()); + std::reverse(aMinKashidaArray.begin(), aMinKashidaArray.end()); + while (!aKashidaArray.empty() && nTotalMinKashida > nRemainingSpace) + { + nTotalMinKashida -= aMinKashidaArray.back(); + aMinKashidaArray.pop_back(); + aKashidaArray.pop_back(); + } + + std::reverse(aKashidaArray.begin(), aKashidaArray.end()); + // Validate std::vector<sal_Int32> aDropped; auto nOldLayout = GetRefDevice()->GetLayoutMode(); diff --git a/vcl/qa/cppunit/pdfexport/data/tdf151748.fodt b/vcl/qa/cppunit/pdfexport/data/tdf151748.fodt new file mode 100644 index 000000000000..93c12a655fff --- /dev/null +++ b/vcl/qa/cppunit/pdfexport/data/tdf151748.fodt @@ -0,0 +1,160 @@ +<?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.3" office:mimetype="application/vnd.oasis.opendocument.text"> + <office:meta><meta:initial-creator>Eyal Rozenberg</meta:initial-creator><meta:creation-date>2024-08-16T21:34:46.833527525</meta:creation-date><dc:date>2024-08-30T01:36:34.697764958</dc:date><meta:editing-duration>PT9M57S</meta:editing-duration><meta:editing-cycles>6</meta:editing-cycles><meta:generator>LibreOfficeDev/25.2.0.0.alpha0$Linux_X86_64 LibreOffice_project/87ae339de7d1f83235a180dc50293f2857ef37a8</meta:generator><meta:document-statistic meta:table-count="0" meta:image-count="0" meta:object-count="0" meta:page-count="1" meta:paragraph-count="4" meta:word-count="0" meta:character-count="0" meta:non-whitespace-character-count="0"/></office:meta> + <office:font-face-decls> + <style:font-face style:name="David CLM1" svg:font-family="'David CLM'" style:font-family-generic="system" style:font-pitch="variable"/> + <style:font-face style:name="DejaVu Sans" svg:font-family="'DejaVu Sans'" style:font-adornments="Book" style:font-family-generic="swiss" style:font-pitch="variable"/> + <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 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 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:writing-mode="lr-tb" style:flow-with-text="false"/> + <style:paragraph-properties fo:text-align="end" 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="IL" 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="David CLM1" style:font-size-complex="12pt" style:language-complex="he" style:country-complex="IL"/> + </style:default-style> + <style:default-style style:family="paragraph"> + <style:paragraph-properties fo:text-align="end" style:justify-single-word="false" 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="IL" 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="David CLM1" style:font-size-complex="12pt" style:language-complex="he" style:country-complex="IL" 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:paragraph-properties fo:text-align="end" style:justify-single-word="false"/> + <style:text-properties style:font-name-complex="DejaVu Sans" style:font-family-complex="'DejaVu Sans'" style:font-style-name-complex="Book" style:font-family-generic-complex="swiss" style:font-pitch-complex="variable" style:font-style-complex="normal" style:font-weight-complex="normal"/> + </style:style> + <text:outline-style style:name="Outline"> + <text:outline-level-style text:level="1" loext:num-list-format="%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" loext:num-list-format="%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" loext:num-list-format="%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" loext:num-list-format="%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" loext:num-list-format="%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" loext:num-list-format="%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" loext:num-list-format="%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" loext:num-list-format="%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" loext:num-list-format="%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" loext:num-list-format="%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="P2" style:family="paragraph"> + <style:paragraph-properties fo:text-align="justify" style:writing-mode="rl-tb"/> + </style:style> + <style:style style:name="P3" style:family="paragraph"> + <loext:graphic-properties draw:fill="none" draw:fill-color="#ffffff"/> + <style:paragraph-properties fo:text-align="justify"/> + <style:text-properties style:font-name-complex="Noto Sans Arabic" style:language-complex="ar" style:country-complex="SA" style:font-style-complex="normal" style:font-weight-complex="normal"/> + </style:style> + <style:style style:name="P4" style:family="paragraph" style:parent-style-name="Standard"> + <style:text-properties/> + </style:style> + <style:style style:name="T1" style:family="text"> + <style:text-properties style:font-name-complex="Noto Sans Arabic" style:language-complex="ar" style:country-complex="SA" style:font-style-complex="normal" style:font-weight-complex="normal"/> + </style:style> + <style:style style:name="T2" style:family="text"> + <style:text-properties fo:font-size="26pt" style:font-size-asian="26pt" style:font-name-complex="Noto Sans Arabic" style:font-size-complex="26pt" style:language-complex="ar" style:country-complex="SA" style:font-style-complex="normal" style:font-weight-complex="normal"/> + </style:style> + <style:style style:name="gr1" style:family="graphic"> + <style:graphic-properties draw:stroke="solid" svg:stroke-color="#000000" draw:fill="none" draw:fill-color="#ffffff" fo:min-height="0.7047in" loext:decorative="false" style:run-through="foreground" style:wrap="run-through" style:number-wrapped-paragraphs="no-limit" style:vertical-pos="middle" style:vertical-rel="baseline" style:horizontal-pos="from-left" style:horizontal-rel="paragraph" draw:wrap-influence-on-position="once-concurrent" loext:allow-overlap="true" style:flow-with-text="false"/> + <style:paragraph-properties style:writing-mode="lr-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="rl-tb" style:layout-grid-color="#c0c0c0" style:layout-grid-lines="20" style:layout-grid-base-height="0.278in" style:layout-grid-ruby-height="0.139in" style:layout-grid-mode="none" style:layout-grid-ruby-below="false" style:layout-grid-print="false" style:layout-grid-display="false" 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="right" style:rel-width="25%" style:color="#000000"/> + </style:page-layout-properties> + <style:header-style/> + <style:footer-style/> + </style:page-layout> + <style:style style:name="dp1" style:family="drawing-page"> + <style:drawing-page-properties draw:background-size="full"/> + </style:style> + </office:automatic-styles> + <office:master-styles> + <style:master-page style:name="Standard" style:page-layout-name="pm1" draw:style-name="dp1"/> + </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"><draw:frame text:anchor-type="as-char" draw:z-index="0" draw:name="Text Frame 1" draw:style-name="gr1" draw:text-style-name="P3" svg:width="0.9606in" svg:height="0.7051in"> + <draw:text-box> + <text:p text:style-name="P2"><text:span text:style-name="T1">خط تخوردگی و توسط</text:span></text:p> + </draw:text-box> + </draw:frame></text:p> + <text:p text:style-name="P4"><draw:frame text:anchor-type="as-char" draw:z-index="1" draw:name="Text Frame 2" draw:style-name="gr1" draw:text-style-name="P3" svg:width="1.0394in" svg:height="0.7051in"> + <draw:text-box> + <text:p text:style-name="P2"><text:span text:style-name="T1">خط تخوردگی و توسط</text:span></text:p> + </draw:text-box> + </draw:frame></text:p> + <text:p text:style-name="P4"><draw:frame text:anchor-type="as-char" draw:z-index="2" draw:name="Text Frame 3" draw:style-name="gr1" draw:text-style-name="P3" svg:width="1.2949in" svg:height="0.7051in"> + <draw:text-box> + <text:p text:style-name="P2"><text:span text:style-name="T1">خط تخوردگی و توسط</text:span></text:p> + </draw:text-box> + </draw:frame></text:p> + <text:p text:style-name="P4"><draw:frame text:anchor-type="as-char" draw:z-index="3" draw:name="Text Frame 4" draw:style-name="gr1" draw:text-style-name="P3" svg:width="1.2949in" svg:height="1.1161in"> + <draw:text-box> + <text:p text:style-name="P2"><text:span text:style-name="T2">خط</text:span><text:span text:style-name="T1"> تخوردگی و توسط</text:span></text:p> + </draw:text-box> + </draw:frame></text:p> + <text:p text:style-name="P4"/> + </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 2ff82e742e61..c8527d71dd9a 100644 --- a/vcl/qa/cppunit/pdfexport/pdfexport2.cxx +++ b/vcl/qa/cppunit/pdfexport/pdfexport2.cxx @@ -5621,6 +5621,67 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf160786) CPPUNIT_ASSERT_LESS(aRect.at(3).getMaxX() + aRect.at(3).getWidth(), aRect.at(4).getMinX()); } +// tdf#151748 - Textboxes should validate kashida positions +CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf151748KashidaSpace) +{ + aMediaDescriptor[u"FilterName"_ustr] <<= u"writer_pdf_Export"_ustr; + saveAsPDF(u"tdf151748.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(21, 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(17, nTextObjectCount); + + // Box 1: Not enough room for kashida + CPPUNIT_ASSERT_EQUAL(u"خط تخوردگی و"_ustr, aText.at(0).trim()); + CPPUNIT_ASSERT_EQUAL(u"توسط"_ustr, aText.at(1).trim()); + + // Box 2: One kashida toward end + CPPUNIT_ASSERT_EQUAL(u"وردگی و"_ustr, aText.at(2).trim()); + CPPUNIT_ASSERT_EQUAL(u""_ustr, aText.at(3).trim()); // Kashida + CPPUNIT_ASSERT_EQUAL(u"خط تخ"_ustr, aText.at(4).trim()); + CPPUNIT_ASSERT_EQUAL(u"توسط"_ustr, aText.at(5).trim()); + + // Box 3: Two kashida + CPPUNIT_ASSERT_EQUAL(u"وردگی و"_ustr, aText.at(6).trim()); + CPPUNIT_ASSERT_EQUAL(u""_ustr, aText.at(7).trim()); // Kashida + CPPUNIT_ASSERT_EQUAL(u"ط تخ"_ustr, aText.at(8).trim()); + CPPUNIT_ASSERT_EQUAL(u""_ustr, aText.at(9).trim()); // Kashida + CPPUNIT_ASSERT_EQUAL(u"خ"_ustr, aText.at(10).trim()); + CPPUNIT_ASSERT_EQUAL(u"توسط"_ustr, aText.at(11).trim()); + + // Box 4: One kashida (text size change) + CPPUNIT_ASSERT_EQUAL(u"خط"_ustr, aText.at(12).trim()); + CPPUNIT_ASSERT_EQUAL(u"وردگی و"_ustr, aText.at(13).trim()); + CPPUNIT_ASSERT_EQUAL(u""_ustr, aText.at(14).trim()); // Kashida + CPPUNIT_ASSERT_EQUAL(u"تخ"_ustr, aText.at(15).trim()); + CPPUNIT_ASSERT_EQUAL(u"توسط"_ustr, aText.at(16).trim()); +} + } // end anonymous namespace CPPUNIT_PLUGIN_IMPLEMENT(); |