sl@0: /* sl@0: * Copyright (c) 2003-2010 Nokia Corporation and/or its subsidiary(-ies). sl@0: * All rights reserved. sl@0: * This component and the accompanying materials are made available sl@0: * under the terms of "Eclipse Public License v1.0" sl@0: * which accompanies this distribution, and is available sl@0: * at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: * sl@0: * Initial Contributors: sl@0: * Nokia Corporation - initial contribution. sl@0: * sl@0: * Contributors: sl@0: * sl@0: * Description: sl@0: * TGraphemeIterator.cpp unit tests for RTmGraphemeEdgeIterator sl@0: * sl@0: */ sl@0: sl@0: sl@0: #include "TestLayout.h" sl@0: #include "TGraphicsContext.h" sl@0: #include "TMINTERP.H" sl@0: sl@0: #include "tgraphemeiterator.h" sl@0: sl@0: namespace LocalToTGraphemeIterator sl@0: { sl@0: CTGraphemeIteratorStep* TestStep; sl@0: #define TESTPOINT(p) TestStep->testpoint(p,(TText8*)__FILE__,__LINE__) sl@0: #define TESTPRINT(p) TestStep->print(p,(TText8*)__FILE__,__LINE__) sl@0: sl@0: struct TTransliteration sl@0: { sl@0: const TText* iString; sl@0: TInt iChar; sl@0: }; sl@0: static const TTransliteration KArabicTransliteration[] = sl@0: { sl@0: { reinterpret_cast(L"?"), 0x61F }, sl@0: { reinterpret_cast(L"`"), 0x621 }, // in-line hamza sl@0: { reinterpret_cast(L"a"), 0x627 }, // alif sl@0: { reinterpret_cast(L"b"), 0x628 }, sl@0: { reinterpret_cast(L"A"), 0x629 }, // teh marbuta sl@0: { reinterpret_cast(L"t"), 0x62A }, sl@0: { reinterpret_cast(L"th"), 0x62B }, sl@0: { reinterpret_cast(L"j"), 0x62C }, sl@0: { reinterpret_cast(L"H"), 0x62D }, // hah sl@0: { reinterpret_cast(L"kh"), 0x62E }, sl@0: { reinterpret_cast(L"d"), 0x62F }, sl@0: { reinterpret_cast(L"dh"), 0x630 }, sl@0: { reinterpret_cast(L"r"), 0x631 }, sl@0: { reinterpret_cast(L"z"), 0x632 }, sl@0: { reinterpret_cast(L"s"), 0x633 }, sl@0: { reinterpret_cast(L"sh"), 0x634 }, sl@0: { reinterpret_cast(L"S"), 0x635 }, sl@0: { reinterpret_cast(L"D"), 0x636 }, sl@0: { reinterpret_cast(L"T"), 0x637 }, sl@0: { reinterpret_cast(L"Z"), 0x638 }, // zah sl@0: { reinterpret_cast(L"'"), 0x639 }, // ain sl@0: { reinterpret_cast(L"g"), 0x63A }, sl@0: { reinterpret_cast(L"_"), 0x640 }, // kashida sl@0: { reinterpret_cast(L"f"), 0x641 }, sl@0: { reinterpret_cast(L"q"), 0x642 }, sl@0: { reinterpret_cast(L"k"), 0x643 }, sl@0: { reinterpret_cast(L"l"), 0x644 }, // lam sl@0: { reinterpret_cast(L"m"), 0x645 }, sl@0: { reinterpret_cast(L"n"), 0x646 }, sl@0: { reinterpret_cast(L"h"), 0x647 }, // heh sl@0: { reinterpret_cast(L"w"), 0x648 }, sl@0: { reinterpret_cast(L"y"), 0x64A }, sl@0: { reinterpret_cast(L"^F"), 0x64B }, // fathatan sl@0: { reinterpret_cast(L"^D"), 0x64C }, // dammatan sl@0: { reinterpret_cast(L"^K"), 0x64D }, // kasratan sl@0: { reinterpret_cast(L"^f"), 0x64E }, // fatha sl@0: { reinterpret_cast(L"^d"), 0x64F }, // damma sl@0: { reinterpret_cast(L"^k"), 0x650 }, // kasra sl@0: { reinterpret_cast(L"^s"), 0x651 }, // shadda sl@0: { reinterpret_cast(L"^h"), 0x652 }, // sukun sl@0: { reinterpret_cast(L"^~"), 0x653 }, // maddah sl@0: { reinterpret_cast(L"^`"), 0x654 }, // hamza above sl@0: { reinterpret_cast(L"_`"), 0x653 }, // hamza below sl@0: { reinterpret_cast(L"0"), 0x660 }, sl@0: { reinterpret_cast(L"1"), 0x661 }, sl@0: { reinterpret_cast(L"2"), 0x662 }, sl@0: { reinterpret_cast(L"3"), 0x663 }, sl@0: { reinterpret_cast(L"4"), 0x664 }, sl@0: { reinterpret_cast(L"5"), 0x665 }, sl@0: { reinterpret_cast(L"6"), 0x666 }, sl@0: { reinterpret_cast(L"7"), 0x667 }, sl@0: { reinterpret_cast(L"8"), 0x668 }, sl@0: { reinterpret_cast(L"9"), 0x669 } sl@0: }; sl@0: } sl@0: using namespace LocalToTGraphemeIterator; sl@0: sl@0: TText TransliterateSingle(const TText*& aInput, const TText* aEnd) sl@0: { sl@0: const TInt tableSize = sl@0: sizeof(KArabicTransliteration)/sizeof(KArabicTransliteration[0]); sl@0: for (TInt i = 0; i != tableSize; ++i) sl@0: { sl@0: const TText* p = KArabicTransliteration[i].iString; sl@0: const TText* q = aInput; sl@0: while (q != aEnd && *q == *p) sl@0: { sl@0: ++q; sl@0: ++p; sl@0: if (*p == '\0') sl@0: { sl@0: aInput = q; sl@0: return static_cast(KArabicTransliteration[i].iChar); sl@0: } sl@0: } sl@0: } sl@0: TText result = *aInput; sl@0: ++aInput; sl@0: return result; sl@0: } sl@0: sl@0: // transliteration is turned on with { and off with }. sl@0: // use }{ to split digraphs. sl@0: void Transliterate(const TDesC& aIn, TDes& aOut) sl@0: { sl@0: const TText KTransliterationOn = '{'; sl@0: const TText KTransliterationOff = '}'; sl@0: TBool transliterating = EFalse; sl@0: const TText* p = &aIn[0]; sl@0: const TText* pEnd = p + aIn.Length(); sl@0: while (p != pEnd) sl@0: { sl@0: if (!transliterating) sl@0: { sl@0: if (*p == KTransliterationOn) sl@0: { sl@0: transliterating = ETrue; sl@0: ++p; sl@0: } sl@0: else sl@0: aOut.Append(*p++); sl@0: } sl@0: else sl@0: { sl@0: if (*p == KTransliterationOff) sl@0: { sl@0: transliterating = EFalse; sl@0: ++p; sl@0: } sl@0: else sl@0: aOut.Append(TransliterateSingle(p, pEnd)); sl@0: } sl@0: } sl@0: } sl@0: sl@0: /** sl@0: Tests RTmGraphemeEdgeIterator::DocPosMatches for this document position and sl@0: edge. sl@0: */ sl@0: void TestDocPosMatchesCase(const TTmGraphemeEdgeInfo& aEdgeInfo, sl@0: TTmDocPosSpec& aPosSpec, sl@0: RTmGraphemeEdgeIterator::TGraphemeMatch aExpectedMatchType) sl@0: { sl@0: TInt start = aEdgeInfo.iPos.iDocPos.iPos; sl@0: TInt end = start; sl@0: if (aEdgeInfo.iPos.iDocPos.iLeadingEdge) sl@0: end += aEdgeInfo.iCodePoints; sl@0: else sl@0: { sl@0: start -= aEdgeInfo.iCodePoints - 1; sl@0: ++end; sl@0: } sl@0: aPosSpec.iPos = start - 1; sl@0: TESTPOINT(RTmGraphemeEdgeIterator::DocPosMatches(aPosSpec, aEdgeInfo) sl@0: == RTmGraphemeEdgeIterator::ENoMatch); sl@0: for (TInt i = start; i != end; ++i) sl@0: { sl@0: aPosSpec.iPos = i; sl@0: TESTPOINT(RTmGraphemeEdgeIterator::DocPosMatches(aPosSpec, aEdgeInfo) sl@0: == aExpectedMatchType); sl@0: } sl@0: aPosSpec.iPos = end; sl@0: TESTPOINT(RTmGraphemeEdgeIterator::DocPosMatches(aPosSpec, aEdgeInfo) sl@0: == RTmGraphemeEdgeIterator::ENoMatch); sl@0: } sl@0: sl@0: /** sl@0: Tests RTmGraphemeEdgeIterator::DocPosMatches for this edge and all relevant sl@0: document position specifications. sl@0: */ sl@0: void TestDocPosMatchesAllSpecs(const TTmGraphemeEdgeInfo& aEdgeInfo) sl@0: { sl@0: TTmDocPosSpec posSpec; sl@0: RTmGraphemeEdgeIterator::TGraphemeMatch expected; sl@0: posSpec.iType = TTmDocPosSpec::ELeftToRight; sl@0: expected = aEdgeInfo.iPos.iRightToLeft? sl@0: RTmGraphemeEdgeIterator::EPositionOnly sl@0: : RTmGraphemeEdgeIterator::ETotalMatch; sl@0: TestDocPosMatchesCase(aEdgeInfo, posSpec, expected); sl@0: posSpec.iType = TTmDocPosSpec::ERightToLeft; sl@0: expected = aEdgeInfo.iPos.iRightToLeft? sl@0: RTmGraphemeEdgeIterator::ETotalMatch sl@0: : RTmGraphemeEdgeIterator::EPositionOnly; sl@0: TestDocPosMatchesCase(aEdgeInfo, posSpec, expected); sl@0: posSpec.iType = TTmDocPosSpec::ETrailing; sl@0: expected = aEdgeInfo.iPos.iDocPos.iLeadingEdge? sl@0: RTmGraphemeEdgeIterator::ENoMatch sl@0: : RTmGraphemeEdgeIterator::ETotalMatch; sl@0: TestDocPosMatchesCase(aEdgeInfo, posSpec, expected); sl@0: posSpec.iType = TTmDocPosSpec::ELeading; sl@0: expected = aEdgeInfo.iPos.iDocPos.iLeadingEdge? sl@0: RTmGraphemeEdgeIterator::ETotalMatch sl@0: : RTmGraphemeEdgeIterator::ENoMatch; sl@0: TestDocPosMatchesCase(aEdgeInfo, posSpec, expected); sl@0: } sl@0: sl@0: /** sl@0: Tests RTmGraphemeEdgeIterator::DocPosMatches for a variety of edges and sl@0: positions. sl@0: */ sl@0: void TestDocPosMatches() sl@0: { sl@0: TTmGraphemeEdgeInfo edgeInfo; sl@0: edgeInfo.iPos.iDocPos.iPos = 5; sl@0: for (edgeInfo.iCodePoints = 1; edgeInfo.iCodePoints <= 3; sl@0: ++edgeInfo.iCodePoints) sl@0: { sl@0: edgeInfo.iPos.iDocPos.iLeadingEdge = ETrue; sl@0: edgeInfo.iPos.iRightToLeft = EFalse; sl@0: TestDocPosMatchesAllSpecs(edgeInfo); sl@0: edgeInfo.iPos.iDocPos.iLeadingEdge = EFalse; sl@0: TestDocPosMatchesAllSpecs(edgeInfo); sl@0: edgeInfo.iPos.iRightToLeft = ETrue; sl@0: TestDocPosMatchesAllSpecs(edgeInfo); sl@0: edgeInfo.iPos.iDocPos.iLeadingEdge = ETrue; sl@0: TestDocPosMatchesAllSpecs(edgeInfo); sl@0: } sl@0: } sl@0: sl@0: enum TEdgeType { ETrail, ELead }; sl@0: enum TEdgeRelationship { EEdgeDifferent, EEdgeSame, EEdgeNewline }; sl@0: enum TAmbiguity { EUnamb = 0, EAmb = 1 }; sl@0: enum TDirectionality { EL2R = 0, ER2L = 1 }; sl@0: struct TEdge sl@0: { sl@0: TInt iPos; sl@0: TEdgeType iLeading; sl@0: TEdgeRelationship iNext; sl@0: TAmbiguity iAmbiguity; sl@0: TDirectionality iRightToLeft; sl@0: }; sl@0: sl@0: _LIT(KLatin1, "Latin text\x2029Latin text over three lines."); sl@0: static const TEdge KLatin1Edges[] = sl@0: { sl@0: {0, ETrail, EEdgeSame, EUnamb, EL2R}, {0, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {1, ETrail, EEdgeSame, EUnamb, EL2R}, {1, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {2, ETrail, EEdgeSame, EUnamb, EL2R}, {2, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {3, ETrail, EEdgeSame, EUnamb, EL2R}, {3, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {4, ETrail, EEdgeSame, EUnamb, EL2R}, {4, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {5, ETrail, EEdgeSame, EUnamb, EL2R}, {5, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {6, ETrail, EEdgeSame, EUnamb, EL2R}, {6, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {7, ETrail, EEdgeSame, EUnamb, EL2R}, {7, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {8, ETrail, EEdgeSame, EUnamb, EL2R}, {8, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {9, ETrail, EEdgeSame, EUnamb, EL2R}, {9, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {10, ETrail, EEdgeSame, EUnamb, EL2R}, {10, ELead, EEdgeNewline, EUnamb, EL2R}, sl@0: {11, ETrail, EEdgeSame, EUnamb, EL2R}, {11, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {12, ETrail, EEdgeSame, EUnamb, EL2R}, {12, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {13, ETrail, EEdgeSame, EUnamb, EL2R}, {13, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {14, ETrail, EEdgeSame, EUnamb, EL2R}, {14, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {15, ETrail, EEdgeSame, EUnamb, EL2R}, {15, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {16, ETrail, EEdgeSame, EUnamb, EL2R}, {16, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {17, ETrail, EEdgeSame, EUnamb, EL2R}, {17, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {18, ETrail, EEdgeSame, EUnamb, EL2R}, {18, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {19, ETrail, EEdgeSame, EUnamb, EL2R}, {19, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {20, ETrail, EEdgeSame, EUnamb, EL2R}, {20, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: // This change tests the change made for DEF059214 which makes sl@0: // the trailing edges of line breaks over unambiguous text move to sl@0: // the start of the next line rather than hanging onto the end of sl@0: // the breaking line. sl@0: // {21, ETrail, EEdgeSame, EUnamb, EL2R}, {21, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: // {22, ETrail, EEdgeNewline, EUnamb, EL2R}, {22, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {21, ETrail, EEdgeSame, EUnamb, EL2R}, {21, ELead, EEdgeNewline, EUnamb, EL2R}, sl@0: {22, ETrail, EEdgeSame, EUnamb, EL2R}, {22, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {23, ETrail, EEdgeSame, EUnamb, EL2R}, {23, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {24, ETrail, EEdgeSame, EUnamb, EL2R}, {24, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {25, ETrail, EEdgeSame, EUnamb, EL2R}, {25, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {26, ETrail, EEdgeSame, EUnamb, EL2R}, {26, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {27, ETrail, EEdgeSame, EUnamb, EL2R}, {27, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {28, ETrail, EEdgeSame, EUnamb, EL2R}, {28, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {29, ETrail, EEdgeSame, EUnamb, EL2R}, {29, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {30, ETrail, EEdgeSame, EUnamb, EL2R}, {30, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {31, ETrail, EEdgeSame, EUnamb, EL2R}, {31, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {32, ETrail, EEdgeSame, EUnamb, EL2R}, {32, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {33, ETrail, EEdgeSame, EUnamb, EL2R}, {33, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {34, ETrail, EEdgeSame, EUnamb, EL2R}, {34, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {35, ETrail, EEdgeSame, EUnamb, EL2R}, {35, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {36, ETrail, EEdgeSame, EUnamb, EL2R}, {36, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {37, ETrail, EEdgeSame, EUnamb, EL2R}, {37, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {38, ETrail, EEdgeSame, EUnamb, EL2R}, {38, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {39, ETrail, EEdgeSame, EUnamb, EL2R}, {39, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {40, ETrail, EEdgeSame, EUnamb, EL2R}, {40, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: // This change tests the change made for DEF059214 sl@0: // {41, ETrail, EEdgeSame, EUnamb, EL2R}, {41, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: // {42, ETrail, EEdgeNewline, EUnamb, EL2R}, {42, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: // similar changes have been made to other tests. sl@0: {41, ETrail, EEdgeSame, EUnamb, EL2R}, {41, ELead, EEdgeNewline, EUnamb, EL2R}, sl@0: {42, ETrail, EEdgeSame, EUnamb, EL2R}, {42, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {43, ETrail, EEdgeSame, EUnamb, EL2R}, {43, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {44, ETrail, EEdgeSame, EUnamb, EL2R}, {44, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {45, ETrail, EEdgeSame, EUnamb, EL2R}, {45, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {46, ETrail, EEdgeSame, EUnamb, EL2R}, {46, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {47, ETrail, EEdgeSame, EUnamb, EL2R}, {47, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {48, ETrail, EEdgeSame, EUnamb, EL2R}, {48, ELead, EEdgeNewline, EUnamb, EL2R}, sl@0: }; sl@0: sl@0: _LIT(KArabic1, "{al'rbyA}\x2029{al'rbyA kf Sayd almwstfa}\x2029{lala lala}."); sl@0: static const TEdge KArabic1Edges[] = sl@0: { sl@0: {7, ELead, EEdgeSame, EUnamb, ER2L}, {7, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {6, ELead, EEdgeSame, EUnamb, ER2L}, {6, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {5, ELead, EEdgeSame, EUnamb, ER2L}, {5, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {4, ELead, EEdgeSame, EUnamb, ER2L}, {4, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {3, ELead, EEdgeSame, EUnamb, ER2L}, {3, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {2, ELead, EEdgeSame, EUnamb, ER2L}, {2, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {1, ELead, EEdgeSame, EUnamb, ER2L}, {1, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {0, ELead, EEdgeSame, EUnamb, ER2L}, {0, ETrail, EEdgeNewline, EUnamb, ER2L}, sl@0: {18, ELead, EEdgeSame, EUnamb, ER2L}, {18, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {17, ELead, EEdgeSame, EUnamb, ER2L}, {17, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {16, ELead, EEdgeSame, EUnamb, ER2L}, {16, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {15, ELead, EEdgeSame, EUnamb, ER2L}, {15, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {14, ELead, EEdgeSame, EUnamb, ER2L}, {14, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {13, ELead, EEdgeSame, EUnamb, ER2L}, {13, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {12, ELead, EEdgeSame, EUnamb, ER2L}, {12, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {11, ELead, EEdgeSame, EUnamb, ER2L}, {11, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {10, ELead, EEdgeSame, EUnamb, ER2L}, {10, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {9, ELead, EEdgeSame, EUnamb, ER2L}, {9, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {8, ELead, EEdgeSame, EUnamb, ER2L}, {8, ETrail, EEdgeNewline, EUnamb, ER2L}, sl@0: {23, ELead, EEdgeSame, EUnamb, ER2L}, {23, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {22, ELead, EEdgeSame, EUnamb, ER2L}, {22, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {21, ELead, EEdgeSame, EUnamb, ER2L}, {21, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {20, ELead, EEdgeSame, EUnamb, ER2L}, {20, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {19, ELead, EEdgeSame, EUnamb, ER2L}, {19, ETrail, EEdgeNewline, EUnamb, ER2L}, sl@0: {32, ELead, EEdgeSame, EUnamb, ER2L}, {32, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {31, ELead, EEdgeSame, EUnamb, ER2L}, {31, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {30, ELead, EEdgeSame, EUnamb, ER2L}, {30, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {29, ELead, EEdgeSame, EUnamb, ER2L}, {29, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {28, ELead, EEdgeSame, EUnamb, ER2L}, {28, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {27, ELead, EEdgeSame, EUnamb, ER2L}, {27, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {26, ELead, EEdgeSame, EUnamb, ER2L}, {26, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {25, ELead, EEdgeSame, EUnamb, ER2L}, {25, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {24, ELead, EEdgeSame, EUnamb, ER2L}, {24, ETrail, EEdgeNewline, EUnamb, ER2L}, sl@0: {43, ELead, EEdgeSame, EUnamb, ER2L}, {43, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {42, ELead, EEdgeSame, EUnamb, ER2L}, {42, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {40, ELead, EEdgeSame, EUnamb, ER2L}, {40, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {38, ELead, EEdgeSame, EUnamb, ER2L}, {38, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {37, ELead, EEdgeSame, EUnamb, ER2L}, {37, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {35, ELead, EEdgeSame, EUnamb, ER2L}, {35, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {33, ELead, EEdgeSame, EUnamb, ER2L}, {33, ETrail, EEdgeNewline, EUnamb, ER2L} sl@0: }; sl@0: sl@0: // Add another example including combining marks and zero-width characters sl@0: _LIT(KCombiners1, "z\x300\x301\x302y\x300\x301\x302\x2029z\x300\x301\x302(\xFEFF)"); sl@0: static const TEdge KCombiners1Edges[] = sl@0: { sl@0: {0, ETrail, EEdgeSame, EUnamb, EL2R}, {0, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {4, ETrail, EEdgeSame, EUnamb, EL2R}, {4, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {8, ETrail, EEdgeSame, EUnamb, EL2R}, {8, ELead, EEdgeNewline, EUnamb, EL2R}, sl@0: {9, ETrail, EEdgeSame, EUnamb, EL2R}, {9, ELead, EEdgeNewline, EUnamb, EL2R}, sl@0: {13, ETrail, EEdgeSame, EUnamb, EL2R}, {13, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {14, ETrail, EEdgeSame, EUnamb, EL2R}, {14, ELead, EEdgeSame, EUnamb, EL2R}, sl@0: {15, ETrail, EEdgeSame, EUnamb, EL2R}, {15, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {16, ETrail, EEdgeSame, EUnamb, EL2R}, {16, ELead, EEdgeNewline, EUnamb, EL2R} sl@0: }; sl@0: sl@0: // Add another example including bidirectional text sl@0: _LIT(KBidi1, "A\x301{b^ft^k}12\x200FZ\x301"); sl@0: static const TEdge KBidi1Edges[] = sl@0: { sl@0: {0, ETrail, EEdgeSame, EUnamb, EL2R}, {0, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {2, ETrail, EEdgeSame, EUnamb, EL2R}, {9, ETrail, EEdgeSame, EAmb, ER2L}, sl@0: {8, ELead, EEdgeSame, EUnamb, ER2L}, {6, ELead, EEdgeDifferent, EAmb, EL2R}, sl@0: {7, ETrail, EEdgeSame, EUnamb, EL2R}, {7, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {8, ETrail, EEdgeSame, EUnamb, EL2R}, {6, ETrail, EEdgeDifferent, EAmb, ER2L}, sl@0: {4, ELead, EEdgeSame, EUnamb, ER2L}, {4, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {2, ELead, EEdgeSame, EUnamb, ER2L}, {9, ELead, EEdgeDifferent, EAmb, EL2R}, sl@0: {11, ETrail, EEdgeSame, EUnamb, EL2R}, {11, ELead, EEdgeNewline, EUnamb, EL2R} sl@0: }; sl@0: sl@0: // This example contains the "amtriguous" case where numbers sl@0: // are embedded within Arabic but are next to Latin. sl@0: _LIT(KBidi2, "A\x301{b^ft^k}12Z\x301"); sl@0: static const TEdge KBidi2Edges[] = sl@0: { sl@0: {0, ETrail, EEdgeSame, EUnamb, EL2R}, {0, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {2, ETrail, EEdgeSame, EUnamb, EL2R}, {6, ELead, EEdgeDifferent, EAmb, EL2R}, sl@0: {7, ETrail, EEdgeSame, EUnamb, EL2R}, {7, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {8, ETrail, EEdgeSame, EUnamb, EL2R}, {6, ETrail, EEdgeDifferent, EAmb, ER2L}, sl@0: {4, ELead, EEdgeSame, EUnamb, ER2L}, {4, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {2, ELead, EEdgeSame, EUnamb, ER2L}, {8, ELead, EEdgeDifferent, EAmb, EL2R}, sl@0: {10, ETrail, EEdgeSame, EUnamb, EL2R}, {10, ELead, EEdgeNewline, EUnamb, EL2R} sl@0: }; sl@0: sl@0: _LIT(KParagraphs1, "z\x2029{b}\x2029z"); sl@0: static const TEdge KParagraphs1Edges[] = sl@0: { sl@0: // First line: sl@0: // 0T 0L (Z) 1T 1L (ParagraphDelimiter) sl@0: {0, ETrail, EEdgeSame, EUnamb, EL2R}, {0, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {1, ETrail, EEdgeSame, EUnamb, EL2R}, {1, ELead, EEdgeNewline, EUnamb, EL2R}, sl@0: // Second line: sl@0: // (PD) 3L< 3T< (Beh) 2L< 2T< sl@0: {3, ELead, EEdgeSame, EUnamb, ER2L}, {3, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {2, ELead, EEdgeSame, EUnamb, ER2L}, {2, ETrail, EEdgeNewline, EUnamb, ER2L}, sl@0: // Third line: sl@0: // 4T 4L (Z) 5T 5L (NominalPD) sl@0: {4, ETrail, EEdgeSame, EUnamb, EL2R}, {4, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {5, ETrail, EEdgeSame, EUnamb, EL2R}, {5, ELead, EEdgeNewline, EUnamb, EL2R} sl@0: }; sl@0: sl@0: // 2 characters per line sl@0: _LIT(KEmbedded1, "z{bb}z\x2029{b}zz{b}\x2029z{b}zz{b}z\x2029{b}z{bb}z{b}"); sl@0: // T=trailing, L=leading, !=Ambiguous, <=Right-to-left sl@0: static const TEdge KEmbedded1Edges[] = sl@0: { sl@0: // First line: sl@0: // 0T 0L (0Z) 1T! 2T!< (1Beh) 1L< sl@0: {0, ETrail, EEdgeSame, EUnamb, EL2R}, {0, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {1, ETrail, EEdgeSame, EAmb, EL2R}, {2, ETrail, EEdgeDifferent, EAmb, ER2L}, sl@0: {1, ELead, EEdgeNewline, EUnamb, ER2L}, sl@0: // Second line: sl@0: // 3T< (2Beh) 2L!< 3L! (3Z) 4T 4L (4ParagraphDelimiter) sl@0: {3, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {2, ELead, EEdgeSame, EAmb, ER2L}, {3, ELead, EEdgeDifferent, EAmb, EL2R}, sl@0: {4, ETrail, EEdgeSame, EUnamb, EL2R}, {4, ELead, EEdgeNewline, EUnamb, EL2R}, sl@0: // Third line: sl@0: // 6L (6Z) 7T! 6T!< (5Beh) 5L< 5T< sl@0: {6, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {7, ETrail, EEdgeSame, EAmb, EL2R}, {6, ETrail, EEdgeDifferent, EAmb, ER2L}, sl@0: {5, ELead, EEdgeSame, EUnamb, ER2L}, {5, ETrail, EEdgeNewline, EUnamb, ER2L}, sl@0: // Fourth line: sl@0: // (9PD) 9L< 9T< (8Beh) 8L!< 7L! (7Z) 8T sl@0: {9, ELead, EEdgeSame, EUnamb, ER2L}, {9, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {8, ELead, EEdgeSame, EAmb, ER2L}, {7, ELead, EEdgeDifferent, EAmb, EL2R}, sl@0: {8, ETrail, EEdgeNewline, EUnamb, EL2R}, sl@0: // Fifth line: sl@0: // 10T 10L (10Z) 11T! 12T!< (11Beh) 11L< sl@0: {10, ETrail, EEdgeSame, EUnamb, EL2R}, {10, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {11, ETrail, EEdgeSame, EAmb, EL2R}, {12, ETrail, EEdgeDifferent, EAmb, ER2L}, sl@0: {11, ELead, EEdgeNewline, EUnamb, ER2L}, sl@0: // Sixth line: sl@0: // 12L (12Z) 13T 13L (13Z) 14T sl@0: {12, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {13, ETrail, EEdgeSame, EUnamb, EL2R}, {13, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {14, ETrail, EEdgeNewline, EUnamb, EL2R}, sl@0: // Seventh line: sl@0: // 15T< (14Beh) 14L!< 15L! (15Z) 16T 16L (16PD) sl@0: {15, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {14, ELead, EEdgeSame, EAmb, ER2L}, {15, ELead, EEdgeDifferent, EAmb, EL2R}, sl@0: {16, ETrail, EEdgeSame, EUnamb, EL2R}, {16, ELead, EEdgeNewline, EUnamb, EL2R}, sl@0: // Eighth line: sl@0: // 18L (18Z) 19T! 18T!< (17Beh) 17L< 17T< sl@0: {18, ELead, EEdgeDifferent, EUnamb, EL2R}, sl@0: {19, ETrail, EEdgeSame, EAmb, EL2R}, {18, ETrail, EEdgeDifferent, EAmb, ER2L}, sl@0: {17, ELead, EEdgeSame, EUnamb, ER2L}, {17, ETrail, EEdgeNewline, EUnamb, ER2L}, sl@0: // Ninth line: sl@0: // 21T< (20Beh) 20L< 20T< (19Beh) 19L< sl@0: {21, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {20, ELead, EEdgeSame, EUnamb, ER2L}, {20, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {19, ELead, EEdgeNewline, EUnamb, ER2L}, sl@0: // Tenth line: sl@0: // (23NominalPD) 23L< 23T< (22Beh) 22L!< 21L! (21Z) 22T sl@0: {23, ELead, EEdgeSame, EUnamb, ER2L}, {23, ETrail, EEdgeDifferent, EUnamb, ER2L}, sl@0: {22, ELead, EEdgeSame, EAmb, ER2L}, {21, ELead, EEdgeDifferent, EAmb, EL2R}, sl@0: {22, ETrail, EEdgeNewline, EUnamb, EL2R}, sl@0: }; sl@0: sl@0: /** sl@0: Returns which portion of the text is in the specified line. sl@0: */ sl@0: void LineExtent(TInt aLine, CTestTmTextLayout& aLayout, sl@0: TInt& aLineStart, TInt& aLineEnd) sl@0: { sl@0: CTmTextLayout& layout = aLayout.Layout(); sl@0: TTmInterpreterParam interpParam(layout); sl@0: RTmGeneralInterpreter interp(aLayout.Source(), interpParam); sl@0: interp.LineNumberToLine(aLine); sl@0: aLineStart = interp.LineInfo().iStart; sl@0: aLineEnd = interp.LineInfo().iEnd; sl@0: } sl@0: sl@0: /** sl@0: Finds an edge in the expected edges list that matches a document position sl@0: specification. sl@0: */ sl@0: const TEdge* FindExpectedEdge(const TTmDocPosSpec& aPos, sl@0: const TEdge* aExpected, TInt aNumExpected) sl@0: { sl@0: const TEdge* nearestTrailing = 0; sl@0: TInt distanceTrailing = KMaxTInt; sl@0: const TEdge* nearestLeading = 0; sl@0: TInt distanceLeading = KMinTInt; sl@0: for (const TEdge* e = aExpected; e != aExpected + aNumExpected; ++e) sl@0: { sl@0: TInt distance = e->iPos - aPos.iPos; sl@0: if (!e->iLeading && 0 <= distance && distance < distanceTrailing) sl@0: { sl@0: distanceTrailing = distance; sl@0: nearestTrailing = e; sl@0: } sl@0: if (e->iLeading && distanceLeading < distance && distance <= 0) sl@0: { sl@0: distanceLeading = distance; sl@0: nearestLeading = e; sl@0: } sl@0: } sl@0: if (aPos.iType == TTmDocPosSpec::ELeading || !nearestTrailing) sl@0: { sl@0: return nearestLeading; sl@0: } sl@0: if (aPos.iType == TTmDocPosSpec::ETrailing || !nearestLeading) sl@0: { sl@0: return nearestTrailing; sl@0: } sl@0: // Differences in iPos might be because pos is within a grapheme cluster, sl@0: // or might be that the leading or trailing edge is not on that line. sl@0: // Grapheme cluster differences are OK, not on the line differences will mean sl@0: // that the one that does not match the input position is wrong. sl@0: if (nearestLeading->iPos == aPos.iPos && nearestTrailing->iPos != aPos.iPos) sl@0: return nearestLeading; sl@0: if (nearestTrailing->iPos == aPos.iPos && nearestLeading->iPos != aPos.iPos) sl@0: return nearestTrailing; sl@0: TBool directionalitiesMatch = nearestTrailing->iRightToLeft? sl@0: nearestLeading->iRightToLeft : !nearestLeading->iRightToLeft; sl@0: if (directionalitiesMatch) sl@0: return nearestLeading; sl@0: TBool leadingIsCorrect = aPos.iType == TTmDocPosSpec::ERightToLeft? sl@0: nearestLeading->iRightToLeft : !nearestLeading->iRightToLeft; sl@0: return leadingIsCorrect? nearestLeading : nearestTrailing; sl@0: } sl@0: sl@0: /** sl@0: Returns ETrue if and only if the two edges specified are expected to be sl@0: coincident. sl@0: */ sl@0: TBool ExpectedEdgesCoincide(const TEdge* aA, const TEdge* aB) sl@0: { sl@0: const TEdge* a = aA < aB? aA : aB; sl@0: const TEdge* b = aA < aB? aB : aA; sl@0: while (a != b) sl@0: { sl@0: if (a->iNext != EEdgeSame) sl@0: return EFalse; sl@0: ++a; sl@0: } sl@0: return ETrue; sl@0: } sl@0: sl@0: /** sl@0: Tests that the edge information matches the expected edge. sl@0: */ sl@0: void TestExpectedEdge(const TTmPosInfo2& aEdgeInfo, sl@0: const TEdge* aExpected) sl@0: { sl@0: TESTPOINT(aEdgeInfo.iRightToLeft? sl@0: aExpected->iRightToLeft : !aExpected->iRightToLeft); sl@0: TESTPOINT(aEdgeInfo.iDocPos.iPos == aExpected->iPos); sl@0: TESTPOINT(aEdgeInfo.iDocPos.iLeadingEdge? sl@0: aExpected->iLeading : !aExpected->iLeading); sl@0: } sl@0: sl@0: /** sl@0: Tests that the edge information matches one of the expected edges. sl@0: */ sl@0: void TestEdgeExists(const TTmPosInfo2& aEdgeInfo, sl@0: const TEdge* aExpected, TInt aNumExpected) sl@0: { sl@0: TTmDocPos pos(aEdgeInfo.iDocPos); sl@0: const TEdge* edge = FindExpectedEdge(pos, aExpected, aNumExpected); sl@0: TESTPOINT(edge != 0); sl@0: TestExpectedEdge(aEdgeInfo, edge); sl@0: } sl@0: sl@0: /** sl@0: Tests that the visual position matches one of the expected edges. sl@0: */ sl@0: void TestVisualPositionExists(const TTmVisualDocPos& aPos, sl@0: const TEdge* aExpected, TInt aNumExpected) sl@0: { sl@0: TESTPOINT(aPos.Ambiguity() != TTmVisualDocPos::ENotFound); sl@0: TTmDocPos posLeft(aPos.LeftEdge().iDocPos); sl@0: const TEdge* left = FindExpectedEdge(posLeft, aExpected, aNumExpected); sl@0: TestExpectedEdge(aPos.LeftEdge(), left); sl@0: TTmDocPos posRight(aPos.RightEdge().iDocPos); sl@0: const TEdge* right = FindExpectedEdge(posRight, aExpected, aNumExpected); sl@0: TestExpectedEdge(aPos.RightEdge(), right); sl@0: TESTPOINT( (aPos.Ambiguity() == TTmVisualDocPos::EAmbiguous sl@0: && left->iAmbiguity && right->iAmbiguity) sl@0: || (aPos.Ambiguity() != TTmVisualDocPos::EAmbiguous sl@0: && !left->iAmbiguity && !right->iAmbiguity) ); sl@0: TESTPOINT(ExpectedEdgesCoincide(left, right)); sl@0: } sl@0: sl@0: /** sl@0: Tests that a RTmGraphemeEdgeIterator iterates through all the positions in a sl@0: line from left to right. sl@0: */ sl@0: void TestLayoutSimplePass(CTestTmTextLayout& aLayout, sl@0: const TEdge* aExpected, TInt aNumExpected) sl@0: { sl@0: CTmTextLayout& layout = aLayout.Layout(); sl@0: TTmInterpreterParam interpParam(layout); sl@0: RTmGeneralInterpreter interp(aLayout.Source(), interpParam); sl@0: interp.LineNumberToLine(0); sl@0: RTmGraphemeEdgeIterator it; sl@0: it.Begin(interp); sl@0: TTmPosInfo2 last = it.GetInfo(); sl@0: for (TInt i = 0; i != aNumExpected; ++i) sl@0: { sl@0: const TEdge& expected = aExpected[i]; sl@0: TESTPOINT(expected.iPos == last.iDocPos.iPos); sl@0: TESTPOINT(expected.iLeading == last.iDocPos.iLeadingEdge); sl@0: it.Next(); sl@0: if (it.AtEnd()) sl@0: { sl@0: TESTPOINT(expected.iNext == EEdgeNewline); sl@0: while (interp.Op() != TTmInterpreter::EOpLine && interp.Next()) sl@0: {} sl@0: if (i + 1 != aNumExpected) sl@0: { sl@0: it.Begin(interp); sl@0: last = it.GetInfo(); sl@0: } sl@0: } sl@0: else sl@0: { sl@0: TTmPosInfo2 thisOne = it.GetInfo(); sl@0: TestEdgeExists(thisOne, aExpected, aNumExpected); sl@0: TESTPOINT(expected.iNext != EEdgeNewline); sl@0: if (expected.iNext == EEdgeSame) sl@0: TESTPOINT(last.iEdge.iX == thisOne.iEdge.iX); sl@0: else if (expected.iNext == EEdgeDifferent) sl@0: TESTPOINT(last.iEdge.iX != thisOne.iEdge.iX); sl@0: last = thisOne; sl@0: } sl@0: } sl@0: it.Close(); sl@0: interp.Close(); sl@0: } sl@0: sl@0: /** sl@0: Tests that FindXPos returns the edge 'closest' to the input co-ordinate sl@0: where there is no ambiguity. sl@0: */ sl@0: void TestLayoutFindXPosEdges(TInt aLine, sl@0: CTestTmTextLayout& aLayout, sl@0: const TEdge* aExpected, TInt aNumExpected) sl@0: { sl@0: CTmTextLayout& layout = aLayout.Layout(); sl@0: TTmInterpreterParam interpParam(layout); sl@0: TTmPosInfo2 posInfo; sl@0: TTmDocPosSpec posSpec; sl@0: TTmLineInfo lineInfo; sl@0: TTmVisualDocPos visPos; sl@0: for (TInt i = 0; i != aNumExpected - 1; ++i) sl@0: { sl@0: const TEdge& expectedL = aExpected[i]; sl@0: const TEdge& expectedR = aExpected[i + 1]; sl@0: if (expectedL.iNext == EEdgeDifferent) sl@0: { sl@0: // This code assumes that no character has a width of exactly 1 pixel. sl@0: if (!expectedL.iAmbiguity) sl@0: { sl@0: posSpec.iPos = expectedL.iPos; sl@0: posSpec.iType = expectedL.iLeading? sl@0: TTmDocPosSpec::ELeading : TTmDocPosSpec::ETrailing; sl@0: layout.FindDocPos(posSpec, posInfo, lineInfo); sl@0: RTmGeneralInterpreter interp(aLayout.Source(), interpParam); sl@0: interp.LineNumberToLine(aLine); sl@0: RTmGraphemeEdgeIterator it; sl@0: it.Begin(interp); sl@0: it.FindXPos(posInfo.iEdge.iX, visPos); sl@0: TESTPOINT(visPos.Ambiguity() != TTmVisualDocPos::EAmbiguous); sl@0: TESTPOINT(visPos.Ambiguity() != TTmVisualDocPos::ENotFound); sl@0: TESTPOINT(visPos.LeftEdge().iDocPos.iPos == expectedL.iPos); sl@0: TESTPOINT(visPos.LeftEdge().iDocPos.iLeadingEdge? sl@0: expectedL.iLeading : !expectedL.iLeading); sl@0: it.Close(); sl@0: interp.Close(); sl@0: } sl@0: if (!expectedR.iAmbiguity) sl@0: { sl@0: posSpec.iPos = expectedR.iPos; sl@0: posSpec.iType = expectedR.iLeading? sl@0: TTmDocPosSpec::ELeading : TTmDocPosSpec::ETrailing; sl@0: layout.FindDocPos(posSpec, posInfo, lineInfo); sl@0: RTmGeneralInterpreter interp(aLayout.Source(), interpParam); sl@0: interp.LineNumberToLine(aLine); sl@0: RTmGraphemeEdgeIterator it; sl@0: it.Begin(interp); sl@0: it.FindXPos(posInfo.iEdge.iX - 1, visPos); sl@0: TESTPOINT(visPos.Ambiguity() != TTmVisualDocPos::EAmbiguous); sl@0: TESTPOINT(visPos.Ambiguity() != TTmVisualDocPos::ENotFound); sl@0: TESTPOINT(visPos.LeftEdge().iDocPos.iPos == expectedR.iPos); sl@0: TESTPOINT(visPos.LeftEdge().iDocPos.iLeadingEdge? sl@0: expectedR.iLeading : !expectedR.iLeading); sl@0: it.Close(); sl@0: interp.Close(); sl@0: } sl@0: } sl@0: } sl@0: } sl@0: sl@0: /** sl@0: Tests that RTmGraphemeEdgeIterator::FindXPos finds document positions that sl@0: match the positions they are supposed to be in. sl@0: */ sl@0: void TestLayoutFindXPos(TInt aLine, sl@0: CTestTmTextLayout& aLayout, sl@0: const TEdge* aExpected, TInt aNumExpected) sl@0: { sl@0: TInt lastLeftX = KMinTInt; sl@0: TInt lastRightX = KMinTInt; sl@0: TTmVisualDocPos visPos; sl@0: TBool finished = EFalse; sl@0: for (TInt x = -10; !finished; ++x) sl@0: { sl@0: CTmTextLayout& layout = aLayout.Layout(); sl@0: TTmInterpreterParam interpParam(layout); sl@0: RTmGeneralInterpreter interp(aLayout.Source(), interpParam); sl@0: interp.LineNumberToLine(aLine); sl@0: RTmGraphemeEdgeIterator it; sl@0: it.Begin(interp); sl@0: it.FindXPos(x, visPos); sl@0: TestVisualPositionExists(visPos, aExpected, aNumExpected); sl@0: TESTPOINT(visPos.Ambiguity() != TTmVisualDocPos::ENotFound); sl@0: TESTPOINT(visPos.LeftEdge().iEdge.iX <= visPos.RightEdge().iEdge.iX); sl@0: TESTPOINT(visPos.Ambiguity() == TTmVisualDocPos::EAmbiguous sl@0: || visPos.LeftEdge().iEdge.iX == visPos.RightEdge().iEdge.iX); sl@0: TESTPOINT(lastLeftX <= visPos.LeftEdge().iEdge.iX); sl@0: if (lastLeftX == visPos.LeftEdge().iEdge.iX) sl@0: { sl@0: TESTPOINT(lastRightX == visPos.RightEdge().iEdge.iX); sl@0: while (aExpected->iPos != visPos.LeftEdge().iDocPos.iPos sl@0: || aExpected->iLeading != visPos.LeftEdge().iDocPos.iLeadingEdge) sl@0: { sl@0: TESTPOINT(aExpected->iNext == EEdgeSame); sl@0: TESTPOINT(0 < aNumExpected); sl@0: ++aExpected; sl@0: --aNumExpected; sl@0: } sl@0: } sl@0: else sl@0: { sl@0: TESTPOINT(lastRightX <= visPos.LeftEdge().iEdge.iX); sl@0: while (aExpected->iPos != visPos.LeftEdge().iDocPos.iPos sl@0: || aExpected->iLeading != visPos.LeftEdge().iDocPos.iLeadingEdge) sl@0: { sl@0: TESTPOINT(0 < aNumExpected); sl@0: ++aExpected; sl@0: --aNumExpected; sl@0: } sl@0: } sl@0: if (interp.LineInfo().iInnerRect.iBr.iX + 120 < x) sl@0: finished = ETrue; sl@0: it.Close(); sl@0: interp.Close(); sl@0: } sl@0: while (aExpected->iNext != EEdgeNewline) sl@0: { sl@0: TESTPOINT(aExpected->iNext == EEdgeSame); sl@0: TESTPOINT(0 < aNumExpected); sl@0: ++aExpected; sl@0: --aNumExpected; sl@0: } sl@0: } sl@0: sl@0: /** sl@0: Uses RTmGraphemeEdgeIterator::FindEdge to find a document position in a sl@0: CTestTmTextLayout. sl@0: */ sl@0: TBool FindEdgeFromLayout(CTestTmTextLayout& aLayout, TInt aLine, sl@0: const TTmDocPosSpec& aDocPos, TTmPosInfo2& aInfo) sl@0: { sl@0: CTmTextLayout& layout = aLayout.Layout(); sl@0: TTmInterpreterParam interpParam(layout); sl@0: RTmGeneralInterpreter interp(aLayout.Source(), interpParam); sl@0: interp.LineNumberToLine(aLine); sl@0: RTmGraphemeEdgeIterator it; sl@0: it.Begin(interp); sl@0: TBool result = it.FindEdge(aDocPos, aInfo); sl@0: it.Close(); sl@0: interp.Close(); sl@0: return result; sl@0: } sl@0: sl@0: /** sl@0: Tests that RTmGraphemeEdgeIterator::FindEdge finds the edges in the layout with sl@0: specifications of leading or trailing edges. sl@0: */ sl@0: void TestLayoutFindEdgesInVisualOrder(TInt aLine, sl@0: CTestTmTextLayout& aLayout, sl@0: const TEdge* aExpected, TInt aNumExpected) sl@0: { sl@0: TInt lastX = KMinTInt; sl@0: TBool sameExpected = EFalse; sl@0: TTmPosInfo2 posInfo; sl@0: while (aNumExpected != 0) sl@0: { sl@0: TTmDocPosSpec posSpec(aExpected->iPos, aExpected->iLeading? sl@0: TTmDocPosSpec::ELeading : TTmDocPosSpec::ETrailing); sl@0: FindEdgeFromLayout(aLayout, aLine, posSpec, posInfo); sl@0: TestEdgeExists(posInfo, aExpected, aNumExpected); sl@0: TESTPOINT(aExpected->iLeading? sl@0: posInfo.iDocPos.iLeadingEdge : !posInfo.iDocPos.iLeadingEdge); sl@0: TESTPOINT(aExpected->iPos == posInfo.iDocPos.iPos); sl@0: TESTPOINT(sameExpected || posInfo.iEdge.iX != lastX); sl@0: TESTPOINT(!sameExpected || posInfo.iEdge.iX == lastX); sl@0: lastX = posInfo.iEdge.iX; sl@0: sameExpected = aExpected->iNext == EEdgeSame? ETrue : EFalse; sl@0: ++aExpected; sl@0: --aNumExpected; sl@0: } sl@0: } sl@0: sl@0: /** sl@0: Tests that RTmGraphemeEdgeIterator::FindEdge finds the edges in the layout with sl@0: specifications of directionality. sl@0: */ sl@0: void TestLayoutFindEdgesByDirectionality(TInt aLine, sl@0: CTestTmTextLayout& aLayout, sl@0: const TEdge* aExpected, TInt aNumExpected) sl@0: { sl@0: TInt lineStart; sl@0: TInt lineEnd; sl@0: LineExtent(aLine, aLayout, lineStart, lineEnd); sl@0: TTmPosInfo2 lToRPosInfo; sl@0: TTmPosInfo2 rToLPosInfo; sl@0: for (TInt pos = lineStart - 1; pos != lineEnd + 1; ++pos) sl@0: { sl@0: TTmDocPosSpec rToLPosSpec(pos, TTmDocPosSpec::ERightToLeft); sl@0: TBool rToLFound = FindEdgeFromLayout(aLayout, aLine, rToLPosSpec, rToLPosInfo); sl@0: TTmDocPosSpec lToRPosSpec(pos, TTmDocPosSpec::ELeftToRight); sl@0: TBool lToRFound = FindEdgeFromLayout(aLayout, aLine, lToRPosSpec, lToRPosInfo); sl@0: if (!lToRFound) sl@0: { sl@0: TESTPOINT(!rToLFound); sl@0: TESTPOINT(pos < lineStart || lineEnd <= pos); sl@0: } sl@0: else sl@0: { sl@0: TESTPOINT(rToLFound); sl@0: TestEdgeExists(rToLPosInfo, aExpected, aNumExpected); sl@0: TestEdgeExists(lToRPosInfo, aExpected, aNumExpected); sl@0: // Now find the nearest edges in the expected range sl@0: TTmDocPosSpec trailingPosSpec(pos, TTmDocPosSpec::ETrailing); sl@0: const TEdge* trailingExpected sl@0: = FindExpectedEdge(trailingPosSpec, aExpected, aNumExpected); sl@0: TTmDocPosSpec leadingPosSpec(pos, TTmDocPosSpec::ELeading); sl@0: const TEdge* leadingExpected sl@0: = FindExpectedEdge(leadingPosSpec, aExpected, aNumExpected); sl@0: if (!trailingExpected) sl@0: trailingExpected = leadingExpected; sl@0: if (!leadingExpected) sl@0: leadingExpected = trailingExpected; sl@0: const TEdge* rToLPosEdge sl@0: = FindExpectedEdge(rToLPosInfo.iDocPos, aExpected, aNumExpected); sl@0: const TEdge* lToRPosEdge sl@0: = FindExpectedEdge(lToRPosInfo.iDocPos, aExpected, aNumExpected); sl@0: TESTPOINT(leadingExpected != 0); sl@0: TESTPOINT(trailingExpected != 0); sl@0: TESTPOINT(ExpectedEdgesCoincide(leadingExpected, rToLPosEdge) sl@0: || ExpectedEdgesCoincide(trailingExpected, rToLPosEdge)); sl@0: TESTPOINT(ExpectedEdgesCoincide(leadingExpected, lToRPosEdge) sl@0: || ExpectedEdgesCoincide(trailingExpected, lToRPosEdge)); sl@0: // Also check that the "found" ones are at least as good as the sl@0: // "expected" ones. sl@0: TESTPOINT(rToLPosInfo.iRightToLeft sl@0: || (!leadingExpected->iRightToLeft && !trailingExpected->iRightToLeft)); sl@0: TESTPOINT(!lToRPosInfo.iRightToLeft sl@0: || (leadingExpected->iRightToLeft && trailingExpected->iRightToLeft)); sl@0: } sl@0: } sl@0: } sl@0: sl@0: /** sl@0: Tests RTmGraphemeEdgeIterator::FindEdgeRightwards or sl@0: RTmGraphemeEdgeIterator::FindEdgeLeftwards. sl@0: */ sl@0: void TestLayoutFindEdgesLeftRight(TInt aLine, sl@0: CTestTmTextLayout& aLayout, TBool aRightwards, sl@0: const TEdge* aExpected, TInt aNumExpected) sl@0: { sl@0: TInt lineStart; sl@0: TInt lineEnd; sl@0: LineExtent(aLine, aLayout, lineStart, lineEnd); sl@0: TTmPosInfo2 nearest; sl@0: TTmVisualDocPos next; sl@0: const TTmDocPosSpec::TType types[4] sl@0: = {TTmDocPosSpec::ETrailing, sl@0: TTmDocPosSpec::ELeading, sl@0: TTmDocPosSpec::ELeftToRight, sl@0: TTmDocPosSpec::ERightToLeft}; sl@0: sl@0: for (TInt pos = lineStart - 1; pos != lineEnd + 1; ++pos) sl@0: { sl@0: for (TInt type = 0; type != 4; ++type) sl@0: { sl@0: TTmDocPosSpec posSpec(pos, types[type]); sl@0: sl@0: // What do we expect the nearest to be? sl@0: TTmDocPosSpec leadingPosSpec(pos, TTmDocPosSpec::ELeading); sl@0: TTmDocPosSpec trailingPosSpec(pos, TTmDocPosSpec::ETrailing); sl@0: sl@0: const TEdge* leadingExpected sl@0: = FindExpectedEdge(leadingPosSpec, aExpected, aNumExpected); sl@0: const TEdge* trailingExpected sl@0: = FindExpectedEdge(trailingPosSpec, aExpected, aNumExpected); sl@0: sl@0: // but should we expect anything at all? sl@0: if (pos < lineStart || lineEnd < pos) sl@0: leadingExpected = trailingExpected = 0; sl@0: if (posSpec.iType == TTmDocPosSpec::ELeading sl@0: && (!leadingExpected || !leadingExpected->iLeading)) sl@0: leadingExpected = trailingExpected = 0; sl@0: if (posSpec.iType == TTmDocPosSpec::ETrailing sl@0: && (!trailingExpected || trailingExpected->iLeading)) sl@0: leadingExpected = trailingExpected = 0; sl@0: sl@0: // is the only element that may be present sl@0: // at position lineEnd. sl@0: if (pos == lineEnd) sl@0: { sl@0: // If we are looking for a leading edge, we won't find sl@0: // the trailing even if it is there. sl@0: if (posSpec.iType == TTmDocPosSpec::ELeading sl@0: || !trailingExpected sl@0: || trailingExpected->iPos != pos) sl@0: leadingExpected = trailingExpected = 0; sl@0: } sl@0: // may not be in the line. sl@0: // We must check explicitly. sl@0: if (pos == lineStart && posSpec.iType == TTmDocPosSpec::ETrailing) sl@0: { sl@0: // If there is no trailing edge at the start of the line sl@0: // and we are looking for one, we sl@0: // do not expect to have a nearest match there. sl@0: if (!trailingExpected || trailingExpected->iPos != pos) sl@0: leadingExpected = trailingExpected = 0; sl@0: } sl@0: sl@0: if (!leadingExpected) sl@0: leadingExpected = trailingExpected; sl@0: if (!trailingExpected) sl@0: trailingExpected = leadingExpected; sl@0: sl@0: const TEdge* nextExpected = 0; sl@0: sl@0: CTmTextLayout& layout = aLayout.Layout(); sl@0: TTmInterpreterParam interpParam(layout); sl@0: RTmGeneralInterpreter interp(aLayout.Source(), interpParam); sl@0: interp.LineNumberToLine(aLine); sl@0: RTmGraphemeEdgeIterator it; sl@0: it.Begin(interp); sl@0: RTmGraphemeEdgeIterator::TEdgesFound result = aRightwards? sl@0: it.FindEdgeRightwards(posSpec, nearest, next) sl@0: : it.FindEdgeLeftwards(posSpec, nearest, next); sl@0: interp.Close(); sl@0: sl@0: // Does what we got match what we expect? sl@0: if (!leadingExpected) sl@0: { sl@0: TESTPOINT(result == RTmGraphemeEdgeIterator::ENone); sl@0: } sl@0: else sl@0: { sl@0: TESTPOINT(result == RTmGraphemeEdgeIterator::ENearestOnly sl@0: || result == RTmGraphemeEdgeIterator::ENearestAndNext); sl@0: TTmDocPosSpec nearestPos(nearest.iDocPos); sl@0: const TEdge* nearestEdge sl@0: = FindExpectedEdge(nearestPos, aExpected, aNumExpected); sl@0: TestExpectedEdge(nearest, nearestEdge); sl@0: const TEdge* matchingEdge = leadingExpected; sl@0: if (posSpec.iType == TTmDocPosSpec::ELeading) sl@0: TESTPOINT(ExpectedEdgesCoincide(leadingExpected, nearestEdge)); sl@0: else if (posSpec.iType == TTmDocPosSpec::ETrailing) sl@0: { sl@0: TESTPOINT(ExpectedEdgesCoincide(trailingExpected, nearestEdge)); sl@0: matchingEdge = trailingExpected; sl@0: } sl@0: else sl@0: { sl@0: TESTPOINT(ExpectedEdgesCoincide(leadingExpected, nearestEdge) sl@0: || ExpectedEdgesCoincide(trailingExpected, nearestEdge)); sl@0: if (ExpectedEdgesCoincide(trailingExpected, nearestEdge)) sl@0: matchingEdge = trailingExpected; sl@0: TBool directionalitiesMatch = leadingExpected->iRightToLeft? sl@0: trailingExpected->iRightToLeft : !trailingExpected->iRightToLeft; sl@0: TBool foundCorrectDirectionality sl@0: = posSpec.iType == TTmDocPosSpec::ERightToLeft? sl@0: nearest.iRightToLeft : !nearest.iRightToLeft; sl@0: TESTPOINT(foundCorrectDirectionality || directionalitiesMatch); sl@0: } sl@0: sl@0: // Find next edge in expected list sl@0: const TEdge* e = matchingEdge; sl@0: const TEdge* end = aRightwards? sl@0: aExpected + aNumExpected - 1 sl@0: : aExpected; sl@0: TInt direction = aRightwards? 1 : -1; sl@0: while (nextExpected == 0 && e != end) sl@0: { sl@0: e += direction; sl@0: if (!ExpectedEdgesCoincide(e, matchingEdge)) sl@0: nextExpected = e; sl@0: } sl@0: } sl@0: if (!nextExpected) sl@0: TESTPOINT(result == RTmGraphemeEdgeIterator::ENone sl@0: || result == RTmGraphemeEdgeIterator::ENearestOnly); sl@0: else sl@0: { sl@0: TESTPOINT(result == RTmGraphemeEdgeIterator::ENearestAndNext); sl@0: TestVisualPositionExists(next, aExpected, aNumExpected); sl@0: TESTPOINT(next.Ambiguity() != TTmVisualDocPos::ENotFound); sl@0: TTmDocPosSpec nextPosLeft(next.LeftEdge().iDocPos); sl@0: TESTPOINT(ExpectedEdgesCoincide(nextExpected, sl@0: FindExpectedEdge(nextPosLeft, aExpected, aNumExpected))); sl@0: TTmDocPosSpec nextPosRight(next.RightEdge().iDocPos); sl@0: TESTPOINT(ExpectedEdgesCoincide(nextExpected, sl@0: FindExpectedEdge(nextPosRight, aExpected, aNumExpected))); sl@0: } sl@0: it.Close(); sl@0: } sl@0: } sl@0: } sl@0: sl@0: /** sl@0: Tests RTmGraphemeEdgeIterator::FindEdgeRightwards. sl@0: */ sl@0: void TestLayoutFindEdgesRightwards(TInt aLine, sl@0: CTestTmTextLayout& aLayout, sl@0: const TEdge* aExpected, TInt aNumExpected) sl@0: { sl@0: TestLayoutFindEdgesLeftRight(aLine, aLayout, ETrue, sl@0: aExpected, aNumExpected); sl@0: } sl@0: sl@0: /** sl@0: Tests RTmGraphemeEdgeIterator::FindEdgeLeftwards. sl@0: */ sl@0: void TestLayoutFindEdgesLeftwards(TInt aLine, sl@0: CTestTmTextLayout& aLayout, sl@0: const TEdge* aExpected, TInt aNumExpected) sl@0: { sl@0: TestLayoutFindEdgesLeftRight(aLine, aLayout, EFalse, sl@0: aExpected, aNumExpected); sl@0: } sl@0: sl@0: /** sl@0: Tests RTmGraphemeEdgeIterator::NextPosition. Expected behaviour is to find the sl@0: smallest number 'n' that is a position in the same line greater than the input 'i', sl@0: where the positions and are not coincident. sl@0: */ sl@0: void TestLayoutFindEdgesForwards(TInt aLine, sl@0: CTestTmTextLayout& aLayout, sl@0: const TEdge* aExpected, TInt aNumExpected) sl@0: { sl@0: TInt lineStart; sl@0: TInt lineEnd; sl@0: LineExtent(aLine, aLayout, lineStart, lineEnd); sl@0: for (TInt i = lineStart - 1; i != lineEnd + 1; ++i) sl@0: { sl@0: CTmTextLayout& layout = aLayout.Layout(); sl@0: TTmInterpreterParam interpParam(layout); sl@0: RTmGeneralInterpreter interp(aLayout.Source(), interpParam); sl@0: interp.LineNumberToLine(aLine); sl@0: RTmGraphemeEdgeIterator it; sl@0: it.Begin(interp); sl@0: TInt result = it.NextPosition(i); sl@0: interp.Close(); sl@0: sl@0: if (result == KErrNotFound) sl@0: { sl@0: // Must be at or after the line's end. sl@0: // Try to find a later edge in the line... sl@0: TTmDocPosSpec in(i, TTmDocPosSpec::ELeading); sl@0: const TEdge* inEdge = FindExpectedEdge(in, sl@0: aExpected, aNumExpected); sl@0: TTmDocPosSpec out(result, TTmDocPosSpec::ETrailing); sl@0: const TEdge* outEdge = FindExpectedEdge(out, sl@0: aExpected, aNumExpected); sl@0: // ...and test that we failed. sl@0: TESTPOINT(!inEdge || inEdge->iPos <= i); sl@0: TESTPOINT(!outEdge || outEdge->iPos <= i); sl@0: } sl@0: else sl@0: { sl@0: TESTPOINT(i < result); sl@0: TTmDocPosSpec in(i, TTmDocPosSpec::ELeading); sl@0: const TEdge* inEdge = FindExpectedEdge(in, sl@0: aExpected, aNumExpected); sl@0: TTmDocPosSpec out(result, TTmDocPosSpec::ETrailing); sl@0: const TEdge* outEdge = FindExpectedEdge(out, sl@0: aExpected, aNumExpected); sl@0: TESTPOINT(outEdge != 0); sl@0: if (inEdge) sl@0: { sl@0: TESTPOINT(lineStart <= i); sl@0: TESTPOINT(!ExpectedEdgesCoincide(inEdge, outEdge)); sl@0: for (TInt j = i + 1; j != result; ++j) sl@0: { sl@0: TTmDocPosSpec between(j, TTmDocPosSpec::ETrailing); sl@0: const TEdge* betweenEdge = FindExpectedEdge(between, sl@0: aExpected, aNumExpected); sl@0: TESTPOINT(betweenEdge != 0); sl@0: // Test that, if there actually is a edge, it is sl@0: // coincident with . If the edge does not exist sl@0: // it does not matter. We can find out if it exists by checking sl@0: // whether the returned expected edge has the same position sl@0: // we asked for. sl@0: TESTPOINT(ExpectedEdgesCoincide(inEdge, betweenEdge) sl@0: || j != betweenEdge->iPos); sl@0: } sl@0: } sl@0: else sl@0: { sl@0: // before the start means finding the first trailing edge sl@0: TESTPOINT (i < lineStart); sl@0: TInt leastTrailingEdge = KMaxTInt; sl@0: for (const TEdge* e = aExpected; e != aExpected + aNumExpected; sl@0: ++e) sl@0: { sl@0: if (!e->iLeading && e->iPos < leastTrailingEdge) sl@0: leastTrailingEdge = e->iPos; sl@0: } sl@0: TESTPOINT(leastTrailingEdge == result); sl@0: } sl@0: } sl@0: it.Close(); sl@0: } sl@0: } sl@0: sl@0: /** sl@0: Tests RTmGraphemeEdgeIterator::PreviousPosition. Expected behaviour is to find the sl@0: largest number 'n' that is a position in the same line smaller than the input 'i', sl@0: where the positions and are not coincident. sl@0: */ sl@0: void TestLayoutFindEdgesBackwards(TInt aLine, sl@0: CTestTmTextLayout& aLayout, sl@0: const TEdge* aExpected, TInt aNumExpected) sl@0: { sl@0: TInt lineStart; sl@0: TInt lineEnd; sl@0: LineExtent(aLine, aLayout, lineStart, lineEnd); sl@0: for (TInt i = lineStart - 1; i != lineEnd + 1; ++i) sl@0: { sl@0: CTmTextLayout& layout = aLayout.Layout(); sl@0: TTmInterpreterParam interpParam(layout); sl@0: RTmGeneralInterpreter interp(aLayout.Source(), interpParam); sl@0: interp.LineNumberToLine(aLine); sl@0: RTmGraphemeEdgeIterator it; sl@0: it.Begin(interp); sl@0: TInt result = it.PreviousPosition(i); sl@0: interp.Close(); sl@0: sl@0: if (result == KErrNotFound) sl@0: { sl@0: // Must be at or before the line's beginning. sl@0: // Could possibly be that there are no leading edges in the line, but sl@0: // we'll ignore that possibility. sl@0: TESTPOINT(i <= lineStart); sl@0: } sl@0: else sl@0: { sl@0: TESTPOINT(result < i); sl@0: TTmDocPosSpec out(result, TTmDocPosSpec::ELeading); sl@0: const TEdge* outEdge = FindExpectedEdge(out, sl@0: aExpected, aNumExpected); sl@0: TESTPOINT(outEdge != 0); sl@0: TTmDocPosSpec in(i, TTmDocPosSpec::ETrailing); sl@0: const TEdge* inEdge = FindExpectedEdge(in, sl@0: aExpected, aNumExpected); sl@0: // if we could not find a trailing edge at this number, then we sl@0: // were beyond the end of the line. sl@0: if (inEdge && !inEdge->iLeading) sl@0: { sl@0: TESTPOINT(inEdge != 0); sl@0: TESTPOINT(!ExpectedEdgesCoincide(inEdge, outEdge)); sl@0: for (TInt j = result + 1; j != i; ++j) sl@0: { sl@0: TTmDocPosSpec between(j, TTmDocPosSpec::ELeading); sl@0: const TEdge* betweenEdge = FindExpectedEdge(between, sl@0: aExpected, aNumExpected); sl@0: TESTPOINT(betweenEdge != 0); sl@0: // Test that, if there actually is a edge, it is sl@0: // coincident with . If the edge does not exist sl@0: // it does not matter. We can find out if it exists by checking sl@0: // whether the returned expected edge has the same position sl@0: // we asked for. sl@0: TESTPOINT(ExpectedEdgesCoincide(inEdge, betweenEdge) sl@0: || j != betweenEdge->iPos); sl@0: } sl@0: } sl@0: else sl@0: { sl@0: // after the end means finding the last leading edge sl@0: TInt greatestLeadingEdge = KMinTInt; sl@0: for (const TEdge* e = aExpected; e != aExpected + aNumExpected; sl@0: ++e) sl@0: { sl@0: if (e->iLeading && greatestLeadingEdge < e->iPos) sl@0: greatestLeadingEdge = e->iPos; sl@0: } sl@0: TESTPOINT(greatestLeadingEdge == result); sl@0: } sl@0: } sl@0: it.Close(); sl@0: } sl@0: } sl@0: sl@0: typedef void FTestLine(TInt aLine, sl@0: CTestTmTextLayout& aLayout, sl@0: const TEdge* aExpected, TInt aNumExpected); sl@0: sl@0: /** sl@0: Runs a particular test for each line in the input data. sl@0: */ sl@0: void TestEachLine(FTestLine* aFn, sl@0: CTestTmTextLayout& aLayout, const TEdge* aExpected, TInt aNumExpected) sl@0: { sl@0: TInt line = 0; sl@0: TInt start = 0; sl@0: for (TInt end = 1; end != aNumExpected; ++end) sl@0: { sl@0: if (aExpected[end - 1].iNext == EEdgeNewline) sl@0: { sl@0: aFn(line, aLayout, aExpected + start, end - start); sl@0: start = end; sl@0: ++line; sl@0: } sl@0: } sl@0: } sl@0: sl@0: /** sl@0: Tests TTmGraphemeIterator and supporting functionality for the specified sl@0: layout. sl@0: */ sl@0: void TestLayoutL(CTestTmTextLayout& aLayout, sl@0: const TEdge* aExpected, TInt aNumExpected) sl@0: { sl@0: TESTPRINT(_L("Simple iteration")); sl@0: TestLayoutSimplePass(aLayout, aExpected, aNumExpected); sl@0: TESTPRINT(_L("FindXPos")); sl@0: TestEachLine(TestLayoutFindXPos, sl@0: aLayout, aExpected, aNumExpected); sl@0: TESTPRINT(_L("FindXPos (unambiguous edges)")); sl@0: TestEachLine(TestLayoutFindXPosEdges, sl@0: aLayout, aExpected, aNumExpected); sl@0: TESTPRINT(_L("FindEdge")); sl@0: TestEachLine(TestLayoutFindEdgesInVisualOrder, sl@0: aLayout, aExpected, aNumExpected); sl@0: TestEachLine(TestLayoutFindEdgesByDirectionality, sl@0: aLayout, aExpected, aNumExpected); sl@0: TESTPRINT(_L("FindEdgeRightwards")); sl@0: TestEachLine(TestLayoutFindEdgesRightwards, sl@0: aLayout, aExpected, aNumExpected); sl@0: TESTPRINT(_L("FindEdgeLeftwards")); sl@0: TestEachLine(TestLayoutFindEdgesLeftwards, sl@0: aLayout, aExpected, aNumExpected); sl@0: TESTPRINT(_L("NextPosition")); sl@0: TestEachLine(TestLayoutFindEdgesForwards, sl@0: aLayout, aExpected, aNumExpected); sl@0: TESTPRINT(_L("PreviousPosition")); sl@0: TestEachLine(TestLayoutFindEdgesBackwards, sl@0: aLayout, aExpected, aNumExpected); sl@0: } sl@0: sl@0: /** sl@0: Tests TTmGraphemeIterator and supporting functionality for each piece of text. sl@0: */ sl@0: TVerdict CTGraphemeIteratorStep::doTestStepL() sl@0: { sl@0: SetTestStepResult(EPass); sl@0: TestStep = this; sl@0: TESTPRINT(_L("RTmGraphemeEdgeIterator unit")); sl@0: sl@0: TESTPRINT(_L(" @SYMTestCaseID:SYSLIB-FORM-LEGACY-GRAPHEMEITERATOR-0001 DocPosMatches ")); sl@0: TestDocPosMatches(); sl@0: TESTPRINT(_L("Simple Latin")); sl@0: CTestTmTextLayout* latin1 = CTestTmTextLayout::NewLC( sl@0: KLatin1, 100, Transliterate); sl@0: TestLayoutL( *latin1, KLatin1Edges, sl@0: sizeof(KLatin1Edges)/sizeof(KLatin1Edges[0])); sl@0: CleanupStack::PopAndDestroy(latin1); sl@0: sl@0: TESTPRINT(_L("Simple Arabic")); sl@0: CTestTmTextLayout* arabic1 = CTestTmTextLayout::NewLC( sl@0: KArabic1, 100, Transliterate); sl@0: TestLayoutL(*arabic1, KArabic1Edges, sl@0: sizeof(KArabic1Edges)/sizeof(KArabic1Edges[0])); sl@0: CleanupStack::PopAndDestroy(arabic1); sl@0: sl@0: TESTPRINT(_L("Latin with combining marks and zero width characters")); sl@0: CTestTmTextLayout* combiners1 = CTestTmTextLayout::NewLC( sl@0: KCombiners1, 20, Transliterate); sl@0: TestLayoutL(*combiners1, KCombiners1Edges, sl@0: sizeof(KCombiners1Edges)/sizeof(KCombiners1Edges[0])); sl@0: CleanupStack::PopAndDestroy(combiners1); sl@0: sl@0: TESTPRINT(_L("Bidirectional text with combining marks")); sl@0: CTestTmTextLayout* bidi1 = CTestTmTextLayout::NewLC( sl@0: KBidi1, 60, Transliterate); sl@0: TestLayoutL( *bidi1, KBidi1Edges, sl@0: sizeof(KBidi1Edges)/sizeof(KBidi1Edges[0])); sl@0: CleanupStack::PopAndDestroy(bidi1); sl@0: sl@0: TESTPRINT(_L("Bidirectional text with combining marks and 'amtriguity'")); sl@0: CTestTmTextLayout* bidi2 = CTestTmTextLayout::NewLC( sl@0: KBidi2, 60, Transliterate); sl@0: TestLayoutL(*bidi2, KBidi2Edges, sl@0: sizeof(KBidi2Edges)/sizeof(KBidi2Edges[0])); sl@0: CleanupStack::PopAndDestroy(bidi2); sl@0: sl@0: TESTPRINT(_L("Small paragraphs of alternating directionality")); sl@0: CTestTmTextLayout* paragraphs1 = CTestTmTextLayout::NewLC( sl@0: KParagraphs1, 20, Transliterate); sl@0: TestLayoutL(*paragraphs1, KParagraphs1Edges, sl@0: sizeof(KParagraphs1Edges)/sizeof(KParagraphs1Edges[0])); sl@0: CleanupStack::PopAndDestroy(paragraphs1); sl@0: sl@0: TESTPRINT(_L("Lines ending over or next to embedded runs")); sl@0: CTestTmTextLayout* embedded1 = CTestTmTextLayout::NewLC( sl@0: KEmbedded1, 20, Transliterate); sl@0: TestLayoutL( *embedded1, KEmbedded1Edges, sl@0: sizeof(KEmbedded1Edges)/sizeof(KEmbedded1Edges[0])); sl@0: CleanupStack::PopAndDestroy(embedded1); sl@0: sl@0: return TestStepResult(); sl@0: } sl@0: