tdf#163105 Use HB data while selecting kashida insertion positions
Previously, Writer and Edit Engine would skip inserting kashida in words if the highest-priority candidate position is marked as invalid by HarfBuzz. This would happen even if the word contained multiple lower-ranked valid candidate positions. This change updates Writer and Edit Engine to pass HarfBuzz kashida position data to the selection algorithm. The algorithm has been updated to return the highest-priority valid position, if any. The algorithm has also been updated to use raw positions marked as valid by HarfBuzz as a fallback, if no better positions could be found. Change-Id: I40c6432c4607aee197e8767e5667db504469956a Reviewed-on: https://gerrit.libreoffice.org/c/core/+/173963 Tested-by: Jenkins Reviewed-by: Jonathan Clark <jonathan@libreoffice.org>
This commit is contained in:
@@ -60,6 +60,7 @@
|
||||
#include <editeng/forbiddencharacterstable.hxx>
|
||||
|
||||
#include <comphelper/configuration.hxx>
|
||||
#include <comphelper/scopeguard.hxx>
|
||||
|
||||
#include <math.h>
|
||||
#include <vcl/metric.hxx>
|
||||
@@ -2344,10 +2345,17 @@ void ImpEditEngine::ImpAdjustBlocks(ParaPortion& rParaPortion, EditLine& rLine,
|
||||
void ImpEditEngine::ImpFindKashidas(ContentNode* pNode, sal_Int32 nStart, sal_Int32 nEnd,
|
||||
std::vector<sal_Int32>& rArray, sal_Int32 nRemainingSpace)
|
||||
{
|
||||
auto nOldLayout = GetRefDevice()->GetLayoutMode();
|
||||
comphelper::ScopeGuard stGuard{ [this, nOldLayout]
|
||||
{ GetRefDevice()->SetLayoutMode(nOldLayout); } };
|
||||
|
||||
GetRefDevice()->SetLayoutMode(nOldLayout | vcl::text::ComplexTextLayoutFlags::BiDiRtl);
|
||||
|
||||
// Kashida glyph looks suspicious, skip Kashida justification
|
||||
if (GetRefDevice()->GetMinKashida() <= 0)
|
||||
return;
|
||||
|
||||
std::vector<bool> aValidPositions;
|
||||
std::vector<sal_Int32> aKashidaArray;
|
||||
std::vector<sal_Int32> aMinKashidaArray;
|
||||
sal_Int32 nTotalMinKashida = 0U;
|
||||
@@ -2367,11 +2375,12 @@ void ImpEditEngine::ImpFindKashidas(ContentNode* pNode, sal_Int32 nStart, sal_In
|
||||
aWordSel.Max().SetIndex( nEnd );
|
||||
|
||||
OUString aWord = GetSelected( aWordSel );
|
||||
GetRefDevice()->GetWordKashidaPositions(aWord, &aValidPositions);
|
||||
|
||||
// restore selection for proper iteration at the end of the function
|
||||
aWordSel.Max().SetIndex( nSavPos );
|
||||
|
||||
auto stKashidaPos = i18nutil::GetWordKashidaPosition(aWord);
|
||||
auto stKashidaPos = i18nutil::GetWordKashidaPosition(aWord, aValidPositions);
|
||||
|
||||
if (stKashidaPos.has_value())
|
||||
{
|
||||
@@ -2406,12 +2415,9 @@ void ImpEditEngine::ImpFindKashidas(ContentNode* pNode, sal_Int32 nStart, sal_In
|
||||
|
||||
// Validate
|
||||
std::vector<sal_Int32> aDropped;
|
||||
auto nOldLayout = GetRefDevice()->GetLayoutMode();
|
||||
GetRefDevice()->SetLayoutMode(nOldLayout | vcl::text::ComplexTextLayoutFlags::BiDiRtl);
|
||||
GetRefDevice()->ValidateKashidas(pNode->GetString(), nStart, nEnd - nStart,
|
||||
/*nPartIdx=*/nStart, /*nPartLen=*/nEnd - nStart, aKashidaArray,
|
||||
&aDropped);
|
||||
GetRefDevice()->SetLayoutMode(nOldLayout);
|
||||
|
||||
for (auto const& pos : aKashidaArray)
|
||||
if (std::find(aDropped.begin(), aDropped.end(), pos) == aDropped.end())
|
||||
|
||||
@@ -22,13 +22,17 @@ class KashidaTest : public CppUnit::TestFixture
|
||||
{
|
||||
public:
|
||||
void testCharacteristic();
|
||||
void testManualKashida();
|
||||
void testFinalYeh();
|
||||
void testNoZwnjExpansion();
|
||||
void testExcludeInvalid();
|
||||
|
||||
CPPUNIT_TEST_SUITE(KashidaTest);
|
||||
CPPUNIT_TEST(testCharacteristic);
|
||||
CPPUNIT_TEST(testManualKashida);
|
||||
CPPUNIT_TEST(testFinalYeh);
|
||||
CPPUNIT_TEST(testNoZwnjExpansion);
|
||||
CPPUNIT_TEST(testExcludeInvalid);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
};
|
||||
|
||||
@@ -54,6 +58,14 @@ void KashidaTest::testCharacteristic()
|
||||
CPPUNIT_ASSERT_EQUAL(sal_Int32(3), GetWordKashidaPosition(u"تمثیل"_ustr).value().nIndex);
|
||||
}
|
||||
|
||||
void KashidaTest::testManualKashida()
|
||||
{
|
||||
CPPUNIT_ASSERT_EQUAL(sal_Int32(2), GetWordKashidaPosition(u"برـای"_ustr).value().nIndex);
|
||||
|
||||
// Normally, a kashida would not be inserted after a final Yeh.
|
||||
CPPUNIT_ASSERT_EQUAL(sal_Int32(4), GetWordKashidaPosition(u"نیمِـي"_ustr).value().nIndex);
|
||||
}
|
||||
|
||||
// tdf#65344: Do not insert kashida before a final Yeh
|
||||
void KashidaTest::testFinalYeh()
|
||||
{
|
||||
@@ -73,6 +85,32 @@ void KashidaTest::testNoZwnjExpansion()
|
||||
CPPUNIT_ASSERT(!GetWordKashidaPosition(u"مت\u200Cن"_ustr).has_value());
|
||||
}
|
||||
|
||||
// tdf#163105: Do not insert kashida if the position is invalid
|
||||
void KashidaTest::testExcludeInvalid()
|
||||
{
|
||||
std::vector<bool> aValid;
|
||||
aValid.resize(5, true);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(sal_Int32(3),
|
||||
GetWordKashidaPosition(u"نویسه"_ustr, aValid).value().nIndex);
|
||||
|
||||
aValid[3] = false;
|
||||
CPPUNIT_ASSERT_EQUAL(sal_Int32(0),
|
||||
GetWordKashidaPosition(u"نویسه"_ustr, aValid).value().nIndex);
|
||||
|
||||
// Calls after this use the last resort (positions in aValid from end to start)
|
||||
aValid[0] = false;
|
||||
CPPUNIT_ASSERT_EQUAL(sal_Int32(2),
|
||||
GetWordKashidaPosition(u"نویسه"_ustr, aValid).value().nIndex);
|
||||
|
||||
aValid[2] = false;
|
||||
CPPUNIT_ASSERT_EQUAL(sal_Int32(1),
|
||||
GetWordKashidaPosition(u"نویسه"_ustr, aValid).value().nIndex);
|
||||
|
||||
aValid[1] = false;
|
||||
CPPUNIT_ASSERT(!GetWordKashidaPosition(u"نویسه"_ustr, aValid).has_value());
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(KashidaTest);
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ namespace
|
||||
|
||||
- tdf#65344: Kashida must not be inserted before the final form of Yeh, unless
|
||||
preceded by an initial or medial Seen.
|
||||
- tdf#163105: As a last resort, use the last valid insertion position from VCL.
|
||||
*/
|
||||
|
||||
#define IS_JOINING_GROUP(c, g) (u_getIntPropertyValue((c), UCHAR_JOINING_GROUP) == U_JG_##g)
|
||||
@@ -134,7 +135,8 @@ bool CanConnectToPrev(sal_Unicode cCh, sal_Unicode cPrevCh)
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<i18nutil::KashidaPosition> i18nutil::GetWordKashidaPosition(const OUString& rWord)
|
||||
std::optional<i18nutil::KashidaPosition>
|
||||
i18nutil::GetWordKashidaPosition(const OUString& rWord, const std::vector<bool>& pValidPositions)
|
||||
{
|
||||
sal_Int32 nIdx = 0;
|
||||
sal_Int32 nPrevIdx = 0;
|
||||
@@ -142,35 +144,45 @@ std::optional<i18nutil::KashidaPosition> i18nutil::GetWordKashidaPosition(const
|
||||
sal_Unicode cCh = 0;
|
||||
sal_Unicode cPrevCh = 0;
|
||||
|
||||
int nPriorityLevel = 7; // 0..6 = level found, 7 not found
|
||||
int nPriorityLevel = 8; // 0..7 = level found, 8 not found
|
||||
|
||||
sal_Int32 nWordLen = rWord.getLength();
|
||||
|
||||
SAL_WARN_IF(!pValidPositions.empty() && pValidPositions.size() != static_cast<size_t>(nWordLen),
|
||||
"i18n", "Kashida valid position array wrong size");
|
||||
|
||||
// ignore trailing vowel chars
|
||||
while (nWordLen && isTransparentChar(rWord[nWordLen - 1]))
|
||||
{
|
||||
--nWordLen;
|
||||
}
|
||||
|
||||
auto fnTryInsertBefore = [&rWord, &nIdx, &nPrevIdx, &nKashidaPos, &nPriorityLevel,
|
||||
&nWordLen](sal_Int32 nNewPriority, bool bIgnoreFinalYeh = false) {
|
||||
// Exclusions:
|
||||
auto fnTryInsertBefore
|
||||
= [&rWord, &nIdx, &nPrevIdx, &nKashidaPos, &nPriorityLevel, &nWordLen,
|
||||
&pValidPositions](sal_Int32 nNewPriority, bool bIgnoreFinalYeh = false) {
|
||||
// Exclusions:
|
||||
|
||||
// #i98410#: prevent ZWNJ expansion
|
||||
if (rWord[nPrevIdx] == 0x200C || rWord[nPrevIdx + 1] == 0x200C)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// tdf#163105: Do not insert kashida if the position is invalid
|
||||
if (!pValidPositions.empty() && !pValidPositions[nPrevIdx])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// tdf#65344: Do not insert kashida before a final Yeh
|
||||
if (!bIgnoreFinalYeh && nIdx == (nWordLen - 1) && isYehChar(rWord[nIdx]))
|
||||
{
|
||||
return;
|
||||
}
|
||||
// #i98410#: prevent ZWNJ expansion
|
||||
if (rWord[nPrevIdx] == 0x200C || rWord[nPrevIdx + 1] == 0x200C)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
nKashidaPos = nPrevIdx;
|
||||
nPriorityLevel = nNewPriority;
|
||||
};
|
||||
// tdf#65344: Do not insert kashida before a final Yeh
|
||||
if (!bIgnoreFinalYeh && nIdx == (nWordLen - 1) && isYehChar(rWord[nIdx]))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
nKashidaPos = nPrevIdx;
|
||||
nPriorityLevel = nNewPriority;
|
||||
};
|
||||
|
||||
while (nIdx < nWordLen)
|
||||
{
|
||||
@@ -270,7 +282,7 @@ std::optional<i18nutil::KashidaPosition> i18nutil::GetWordKashidaPosition(const
|
||||
}
|
||||
}
|
||||
|
||||
// other connecting possibilities
|
||||
// 7. Other connecting possibilities
|
||||
if (nPriorityLevel >= 6 && nIdx > 0)
|
||||
{
|
||||
// Reh, Zain (right joining) final form may appear in the middle of word
|
||||
@@ -286,6 +298,12 @@ std::optional<i18nutil::KashidaPosition> i18nutil::GetWordKashidaPosition(const
|
||||
}
|
||||
}
|
||||
|
||||
// 8. If valid position data exists, use the last legal position
|
||||
if (nPriorityLevel >= 7 && nIdx > 0 && !pValidPositions.empty())
|
||||
{
|
||||
fnTryInsertBefore(7);
|
||||
}
|
||||
|
||||
// Do not consider vowel marks when checking if a character
|
||||
// can be connected to previous character.
|
||||
if (!isTransparentChar(cCh))
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <i18nutil/i18nutildllapi.h>
|
||||
#include <rtl/ustring.hxx>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace i18nutil
|
||||
{
|
||||
@@ -18,7 +19,8 @@ struct KashidaPosition
|
||||
sal_Int32 nIndex;
|
||||
};
|
||||
|
||||
I18NUTIL_DLLPUBLIC std::optional<KashidaPosition> GetWordKashidaPosition(const OUString& rWord);
|
||||
I18NUTIL_DLLPUBLIC std::optional<KashidaPosition>
|
||||
GetWordKashidaPosition(const OUString& rWord, const std::vector<bool>& pValidPositions = {});
|
||||
}
|
||||
|
||||
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
|
||||
|
||||
@@ -1171,6 +1171,9 @@ public:
|
||||
std::vector<sal_Int32>* pKashidaPosDropped // invalid kashida positions (out)
|
||||
) const;
|
||||
|
||||
// tdf#163105: Get map of valid kashida positions for a single word
|
||||
void GetWordKashidaPositions(const OUString& rText, std::vector<bool>* pOutMap) const;
|
||||
|
||||
static void BeginFontSubstitution();
|
||||
static void EndFontSubstitution();
|
||||
static void AddFontSubstitute( const OUString& rFontName,
|
||||
|
||||
@@ -322,6 +322,14 @@ public:
|
||||
void GetKashidaPositions(TextFrameIndex nStt, TextFrameIndex nLen,
|
||||
std::vector<TextFrameIndex>& rKashidaPosition);
|
||||
|
||||
/** replaces kashida opportunities for a given text range.
|
||||
|
||||
rKashidaPositions: buffer containing char indices of the
|
||||
kashida opportunities relative to the paragraph
|
||||
*/
|
||||
void ReplaceKashidaPositions(TextFrameIndex nStt, TextFrameIndex nEnd,
|
||||
const std::vector<TextFrameIndex>& rKashidaPositions);
|
||||
|
||||
/** Use regular blank justification instead of kashdida justification for the given line of text.
|
||||
nStt Start char index of the line referring to the paragraph.
|
||||
nLen Number of characters in the line
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
#include <sal/config.h>
|
||||
|
||||
#include <o3tl/safeint.hxx>
|
||||
#include <com/sun/star/i18n/WordType.hpp>
|
||||
#include <swscanner.hxx>
|
||||
#include <i18nutil/kashida.hxx>
|
||||
|
||||
#include <IDocumentSettingAccess.hxx>
|
||||
#include <doc.hxx>
|
||||
@@ -135,13 +138,64 @@ void SwTextAdjuster::FormatBlock( )
|
||||
GetInfo().GetParaPortion()->GetRepaint().SetOffset(0);
|
||||
}
|
||||
|
||||
static bool lcl_CheckKashidaPositions( SwScriptInfo& rSI, SwTextSizeInfo& rInf, SwTextIter& rItr,
|
||||
sal_Int32& rKashidas, TextFrameIndex& nGluePortion)
|
||||
static bool lcl_CheckKashidaPositions(SwScriptInfo& rSI, SwTextSizeInfo& rInf, SwTextIter& rItr,
|
||||
sal_Int32& rKashidas, TextFrameIndex& nGluePortion,
|
||||
bool& rRemovedAllKashida)
|
||||
{
|
||||
rRemovedAllKashida = true;
|
||||
|
||||
// i60594 validate Kashida justification
|
||||
TextFrameIndex nIdx = rItr.GetStart();
|
||||
TextFrameIndex nEnd = rItr.GetEnd();
|
||||
|
||||
// Get the initial kashida position set, for invalidation
|
||||
std::vector<TextFrameIndex> aOldKashidaPositions;
|
||||
rSI.GetKashidaPositions(nIdx, rItr.GetLength(), aOldKashidaPositions);
|
||||
|
||||
std::vector<TextFrameIndex> aNewKashidaPositions;
|
||||
std::vector<bool> aValidPositions;
|
||||
|
||||
// Reparse the text, and reapply the kashida insertion rules
|
||||
std::function<LanguageType(sal_Int32, sal_Int32, bool)> const pGetLangOfChar(
|
||||
[&rInf](sal_Int32 const nBegin, sal_uInt16 const nScript, bool const bNoChar)
|
||||
{ return rInf.GetTextFrame()->GetLangOfChar(TextFrameIndex{ nBegin }, nScript, bNoChar); });
|
||||
SwScanner aScanner(pGetLangOfChar, rInf.GetText(), nullptr, ModelToViewHelper(),
|
||||
i18n::WordType::DICTIONARY_WORD, sal_Int32(nIdx), sal_Int32(nEnd));
|
||||
|
||||
while (aScanner.NextWord())
|
||||
{
|
||||
const OUString& rWord = aScanner.GetWord();
|
||||
|
||||
// Fetch the set of valid positions from VCL, where possible
|
||||
aValidPositions.clear();
|
||||
if ( SwScriptInfo::IsArabicText( rInf.GetText(), TextFrameIndex{aScanner.GetBegin()}, TextFrameIndex{aScanner.GetLen()} ) )
|
||||
{
|
||||
rItr.SeekAndChgAttrIter(TextFrameIndex{ aScanner.GetBegin() }, rInf.GetRefDev());
|
||||
|
||||
vcl::text::ComplexTextLayoutFlags nOldLayout = rInf.GetRefDev()->GetLayoutMode();
|
||||
rInf.GetRefDev()->SetLayoutMode(nOldLayout | vcl::text::ComplexTextLayoutFlags::BiDiRtl);
|
||||
|
||||
rInf.GetRefDev()->GetWordKashidaPositions(rWord, &aValidPositions);
|
||||
|
||||
rInf.GetRefDev()->SetLayoutMode(nOldLayout);
|
||||
}
|
||||
|
||||
auto stKashidaPos = i18nutil::GetWordKashidaPosition(rWord, aValidPositions);
|
||||
if (stKashidaPos.has_value())
|
||||
{
|
||||
TextFrameIndex nNewKashidaPos{ aScanner.GetBegin() + stKashidaPos->nIndex };
|
||||
aNewKashidaPositions.push_back(nNewKashidaPos);
|
||||
}
|
||||
}
|
||||
|
||||
if (aOldKashidaPositions != aNewKashidaPositions)
|
||||
{
|
||||
// Kashida positions have changed; restart CalcNewBlock
|
||||
rSI.ReplaceKashidaPositions(nIdx, nEnd, aNewKashidaPositions);
|
||||
rRemovedAllKashida = aNewKashidaPositions.empty();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Note on calling KashidaJustify():
|
||||
// Kashida positions may be marked as invalid. Therefore KashidaJustify may return the clean
|
||||
// total number of kashida positions, or the number of kashida positions after some positions
|
||||
@@ -154,12 +208,10 @@ static bool lcl_CheckKashidaPositions( SwScriptInfo& rSI, SwTextSizeInfo& rInf,
|
||||
|
||||
// kashida positions found in SwScriptInfo are not necessarily valid in every font
|
||||
// if two characters are replaced by a ligature glyph, there will be no place for a kashida
|
||||
std::vector<TextFrameIndex> aUncastKashidaPos;
|
||||
rSI.GetKashidaPositions(nIdx, rItr.GetLength(), aUncastKashidaPos);
|
||||
assert(aUncastKashidaPos.size() >= o3tl::make_unsigned(rKashidas));
|
||||
assert(aNewKashidaPositions.size() >= o3tl::make_unsigned(rKashidas));
|
||||
|
||||
std::vector<sal_Int32> aKashidaPos;
|
||||
std::transform(std::cbegin(aUncastKashidaPos), std::cend(aUncastKashidaPos),
|
||||
std::transform(std::cbegin(aNewKashidaPositions), std::cend(aNewKashidaPositions),
|
||||
std::back_inserter(aKashidaPos),
|
||||
[](TextFrameIndex nPos) { return static_cast<sal_Int32>(nPos); });
|
||||
|
||||
@@ -404,13 +456,15 @@ void SwTextAdjuster::CalcNewBlock( SwLineLayout *pCurrent,
|
||||
{
|
||||
// kashida positions found in SwScriptInfo are not necessarily valid in every font
|
||||
// if two characters are replaced by a ligature glyph, there will be no place for a kashida
|
||||
if ( !lcl_CheckKashidaPositions ( rSI, aInf, aItr, nKashidas, nGluePortion ))
|
||||
bool bRemovedAllKashida = false;
|
||||
if (!lcl_CheckKashidaPositions(rSI, aInf, aItr, nKashidas, nGluePortion,
|
||||
bRemovedAllKashida))
|
||||
{
|
||||
// all kashida positions are invalid
|
||||
// do regular blank justification
|
||||
pCurrent->FinishSpaceAdd();
|
||||
GetInfo().SetIdx( m_nStart );
|
||||
CalcNewBlock( pCurrent, pStopAt, nReal, true );
|
||||
CalcNewBlock(pCurrent, pStopAt, nReal, bRemovedAllKashida);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2314,6 +2314,27 @@ void SwScriptInfo::GetKashidaPositions(
|
||||
}
|
||||
}
|
||||
|
||||
void SwScriptInfo::ReplaceKashidaPositions(TextFrameIndex const nStt, TextFrameIndex const nEnd,
|
||||
const std::vector<TextFrameIndex>& rKashidaPositions)
|
||||
{
|
||||
auto it = m_Kashida.begin();
|
||||
while (it != m_Kashida.end() && *it < nStt)
|
||||
{
|
||||
++it;
|
||||
}
|
||||
|
||||
it = m_Kashida.insert(it, rKashidaPositions.begin(), rKashidaPositions.end());
|
||||
|
||||
it += rKashidaPositions.size();
|
||||
auto jt = it;
|
||||
while (jt != m_Kashida.end() && *jt < nEnd)
|
||||
{
|
||||
++jt;
|
||||
}
|
||||
|
||||
m_Kashida.erase(it, jt);
|
||||
}
|
||||
|
||||
void SwScriptInfo::SetNoKashidaLine(TextFrameIndex const nStt, TextFrameIndex const nLen)
|
||||
{
|
||||
m_NoKashidaLine.push_back( nStt );
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
<?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:creation-date>2024-09-25T22:51:39.180592512</meta:creation-date><dc:date>2024-09-25T22:52:41.682098885</dc:date><meta:editing-duration>PT1M3S</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="0" meta:word-count="0" meta:character-count="0" meta:non-whitespace-character-count="0"/><meta:generator>LibreOfficeDev/25.2.0.0.alpha0$Linux_X86_64 LibreOffice_project/8493389ffaaa809c9feb77622a5bd695dbb76e43</meta:generator></office:meta>
|
||||
<office:font-face-decls>
|
||||
<style:font-face style:name="Amiri" svg:font-family="Amiri" style:font-adornments="Regular" 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" svg:font-family="'Noto Sans'" style:font-family-generic="swiss"/>
|
||||
<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 Sans" 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 SC" style:font-size-asian="10.5pt" style:language-asian="zh" style:country-asian="CN" style:font-name-complex="Noto Sans" 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-complex="Amiri" style:font-family-complex="Amiri" style:font-style-name-complex="Regular" 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:paragraph-properties fo:text-align="justify" style:writing-mode="rl-tb"/>
|
||||
<style:text-properties style:font-family-complex="Amiri" style:font-pitch-complex="variable"/>
|
||||
</style:style>
|
||||
<style:style style:name="P2" style:family="paragraph">
|
||||
<loext:graphic-properties draw:fill="none" draw:fill-color="#ffffff"/>
|
||||
<style:paragraph-properties fo:text-align="end"/>
|
||||
<style:text-properties style:font-family-complex="Amiri" style:font-pitch-complex="variable"/>
|
||||
</style:style>
|
||||
<style:style style:name="T1" style:family="text">
|
||||
<style:text-properties style:font-family-complex="Amiri" style:font-pitch-complex="variable"/>
|
||||
</style:style>
|
||||
<style:style style:name="gr1" style:family="graphic">
|
||||
<style:graphic-properties draw:stroke="none" svg:stroke-color="#000000" draw:fill="none" draw:fill-color="#ffffff" fo:min-height="2.1965in" loext:decorative="false" style:run-through="foreground" style:wrap="run-through" style:number-wrapped-paragraphs="no-limit" style:vertical-pos="from-top" style:vertical-rel="paragraph" style:horizontal-pos="from-left" style:horizontal-rel="paragraph"/>
|
||||
<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="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="Standard"><draw:frame text:anchor-type="paragraph" draw:z-index="0" draw:name="Text Frame 1" draw:style-name="gr1" draw:text-style-name="P2" svg:width="6.4843in" svg:height="2.1969in" svg:x="0.0917in" svg:y="0.1091in">
|
||||
<draw:text-box>
|
||||
<text:p text:style-name="P1"><text:span text:style-name="T1">متن فارسی</text:span><text:span text:style-name="T1"><text:line-break/></text:span><text:span text:style-name="T1"/></text:p>
|
||||
</draw:text-box>
|
||||
</draw:frame></text:p>
|
||||
</office:text>
|
||||
</office:body>
|
||||
</office:document>
|
||||
@@ -0,0 +1,114 @@
|
||||
<?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:creation-date>2024-09-25T22:50:47.128978128</meta:creation-date><dc:date>2024-09-25T22:51:34.458596131</dc:date><meta:editing-duration>PT48S</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="10" meta:non-whitespace-character-count="8"/><meta:generator>LibreOfficeDev/25.2.0.0.alpha0$Linux_X86_64 LibreOffice_project/8493389ffaaa809c9feb77622a5bd695dbb76e43</meta:generator></office:meta>
|
||||
<office:font-face-decls>
|
||||
<style:font-face style:name="Amiri" svg:font-family="Amiri" style:font-adornments="Regular" 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" svg:font-family="'Noto Sans'" style:font-family-generic="swiss"/>
|
||||
<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 Sans" 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 SC" style:font-size-asian="10.5pt" style:language-asian="zh" style:country-asian="CN" style:font-name-complex="Noto Sans" 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-complex="Amiri" style:font-family-complex="Amiri" style:font-style-name-complex="Regular" 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>
|
||||
@@ -5627,7 +5627,7 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf151748KashidaSpace)
|
||||
// tdf#163105 - Writer kashida justification should expand spaces
|
||||
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf163105SwKashidaSpaceExpansion)
|
||||
{
|
||||
saveAsPDF(u"tdf163105.fodt");
|
||||
saveAsPDF(u"tdf163105-kashida-spaces.fodt");
|
||||
|
||||
auto pPdfDocument = parsePDFExport();
|
||||
CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
|
||||
@@ -5668,6 +5668,99 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf163105SwKashidaSpaceExpansion)
|
||||
CPPUNIT_ASSERT_GREATER(150.0, aRect.at(2).getWidth());
|
||||
}
|
||||
|
||||
// tdf#163105 - Writer should use font information when choosing kashida positions
|
||||
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf163105Writer)
|
||||
{
|
||||
saveAsPDF(u"tdf163105-writer.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();
|
||||
|
||||
// The fix allows kashida justification in this document.
|
||||
// Without the fix, this will be 1.
|
||||
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 greater than X
|
||||
CPPUNIT_ASSERT_LESS(170.0, aRect.at(2).getWidth());
|
||||
}
|
||||
|
||||
// tdf#163105 - Edit Engine should use font information when choosing kashida positions
|
||||
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf163105Editeng)
|
||||
{
|
||||
saveAsPDF(u"tdf163105-editeng.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();
|
||||
|
||||
// The fix allows kashida justification in this document.
|
||||
// Without the fix, this will be 1.
|
||||
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());
|
||||
|
||||
CPPUNIT_ASSERT_LESS(170.0, aRect.at(2).getWidth());
|
||||
}
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
CPPUNIT_PLUGIN_IMPLEMENT();
|
||||
|
||||
@@ -1214,6 +1214,35 @@ sal_Int32 OutputDevice::ValidateKashidas(const OUString& rTxt, sal_Int32 nIdx, s
|
||||
return nDropped;
|
||||
}
|
||||
|
||||
// tdf#163105: Get map of valid kashida positions for a single word
|
||||
void OutputDevice::GetWordKashidaPositions(const OUString& rText,
|
||||
std::vector<bool>* pOutMap) const
|
||||
{
|
||||
pOutMap->clear();
|
||||
|
||||
auto nEnd = rText.getLength();
|
||||
|
||||
// do layout
|
||||
std::unique_ptr<SalLayout> pSalLayout = ImplLayout(rText, 0, nEnd);
|
||||
if (!pSalLayout)
|
||||
return;
|
||||
|
||||
pOutMap->resize(nEnd, false);
|
||||
for (sal_Int32 i = 0; i < nEnd; ++i)
|
||||
{
|
||||
auto nNextPos = i + 1;
|
||||
|
||||
// Skip combining marks to find the next character after this position.
|
||||
while (nNextPos < nEnd
|
||||
&& u_getIntPropertyValue(rText[nNextPos], UCHAR_JOINING_TYPE) == U_JT_TRANSPARENT)
|
||||
{
|
||||
++nNextPos;
|
||||
}
|
||||
|
||||
pOutMap->at(i) = pSalLayout->IsKashidaPosValid(i, nNextPos);
|
||||
}
|
||||
}
|
||||
|
||||
bool OutputDevice::GetGlyphBoundRects( const Point& rOrigin, const OUString& rStr,
|
||||
int nIndex, int nLen, std::vector< tools::Rectangle >& rVector ) const
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user