summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Clark <jonathan@libreoffice.org>2024-08-30 00:21:12 -0600
committerXisco Fauli <xiscofauli@libreoffice.org>2024-09-03 08:58:22 +0200
commit9d45184354c9f4b6475fa845bea8cc425babed49 (patch)
tree650d41dc3b698f986124533a81d73e78798a5ed6
parent149ca1e0a8123b8722ecb74f6f5e8e5080ce6052 (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.hxx3
-rw-r--r--editeng/source/editeng/impedit3.cxx40
-rw-r--r--vcl/qa/cppunit/pdfexport/data/tdf151748.fodt160
-rw-r--r--vcl/qa/cppunit/pdfexport/pdfexport2.cxx61
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();