1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/textandloc/textrendering/textformatting/test/src/TGraphemeIterator.cpp Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,1339 @@
1.4 +/*
1.5 +* Copyright (c) 2003-2010 Nokia Corporation and/or its subsidiary(-ies).
1.6 +* All rights reserved.
1.7 +* This component and the accompanying materials are made available
1.8 +* under the terms of "Eclipse Public License v1.0"
1.9 +* which accompanies this distribution, and is available
1.10 +* at the URL "http://www.eclipse.org/legal/epl-v10.html".
1.11 +*
1.12 +* Initial Contributors:
1.13 +* Nokia Corporation - initial contribution.
1.14 +*
1.15 +* Contributors:
1.16 +*
1.17 +* Description:
1.18 +* TGraphemeIterator.cpp unit tests for RTmGraphemeEdgeIterator
1.19 +*
1.20 +*/
1.21 +
1.22 +
1.23 +#include "TestLayout.h"
1.24 +#include "TGraphicsContext.h"
1.25 +#include "TMINTERP.H"
1.26 +
1.27 +#include "tgraphemeiterator.h"
1.28 +
1.29 +namespace LocalToTGraphemeIterator
1.30 +{
1.31 +CTGraphemeIteratorStep* TestStep;
1.32 +#define TESTPOINT(p) TestStep->testpoint(p,(TText8*)__FILE__,__LINE__)
1.33 +#define TESTPRINT(p) TestStep->print(p,(TText8*)__FILE__,__LINE__)
1.34 +
1.35 +struct TTransliteration
1.36 + {
1.37 + const TText* iString;
1.38 + TInt iChar;
1.39 + };
1.40 +static const TTransliteration KArabicTransliteration[] =
1.41 + {
1.42 + { reinterpret_cast<const TText*>(L"?"), 0x61F },
1.43 + { reinterpret_cast<const TText*>(L"`"), 0x621 }, // in-line hamza
1.44 + { reinterpret_cast<const TText*>(L"a"), 0x627 }, // alif
1.45 + { reinterpret_cast<const TText*>(L"b"), 0x628 },
1.46 + { reinterpret_cast<const TText*>(L"A"), 0x629 }, // teh marbuta
1.47 + { reinterpret_cast<const TText*>(L"t"), 0x62A },
1.48 + { reinterpret_cast<const TText*>(L"th"), 0x62B },
1.49 + { reinterpret_cast<const TText*>(L"j"), 0x62C },
1.50 + { reinterpret_cast<const TText*>(L"H"), 0x62D }, // hah
1.51 + { reinterpret_cast<const TText*>(L"kh"), 0x62E },
1.52 + { reinterpret_cast<const TText*>(L"d"), 0x62F },
1.53 + { reinterpret_cast<const TText*>(L"dh"), 0x630 },
1.54 + { reinterpret_cast<const TText*>(L"r"), 0x631 },
1.55 + { reinterpret_cast<const TText*>(L"z"), 0x632 },
1.56 + { reinterpret_cast<const TText*>(L"s"), 0x633 },
1.57 + { reinterpret_cast<const TText*>(L"sh"), 0x634 },
1.58 + { reinterpret_cast<const TText*>(L"S"), 0x635 },
1.59 + { reinterpret_cast<const TText*>(L"D"), 0x636 },
1.60 + { reinterpret_cast<const TText*>(L"T"), 0x637 },
1.61 + { reinterpret_cast<const TText*>(L"Z"), 0x638 }, // zah
1.62 + { reinterpret_cast<const TText*>(L"'"), 0x639 }, // ain
1.63 + { reinterpret_cast<const TText*>(L"g"), 0x63A },
1.64 + { reinterpret_cast<const TText*>(L"_"), 0x640 }, // kashida
1.65 + { reinterpret_cast<const TText*>(L"f"), 0x641 },
1.66 + { reinterpret_cast<const TText*>(L"q"), 0x642 },
1.67 + { reinterpret_cast<const TText*>(L"k"), 0x643 },
1.68 + { reinterpret_cast<const TText*>(L"l"), 0x644 }, // lam
1.69 + { reinterpret_cast<const TText*>(L"m"), 0x645 },
1.70 + { reinterpret_cast<const TText*>(L"n"), 0x646 },
1.71 + { reinterpret_cast<const TText*>(L"h"), 0x647 }, // heh
1.72 + { reinterpret_cast<const TText*>(L"w"), 0x648 },
1.73 + { reinterpret_cast<const TText*>(L"y"), 0x64A },
1.74 + { reinterpret_cast<const TText*>(L"^F"), 0x64B }, // fathatan
1.75 + { reinterpret_cast<const TText*>(L"^D"), 0x64C }, // dammatan
1.76 + { reinterpret_cast<const TText*>(L"^K"), 0x64D }, // kasratan
1.77 + { reinterpret_cast<const TText*>(L"^f"), 0x64E }, // fatha
1.78 + { reinterpret_cast<const TText*>(L"^d"), 0x64F }, // damma
1.79 + { reinterpret_cast<const TText*>(L"^k"), 0x650 }, // kasra
1.80 + { reinterpret_cast<const TText*>(L"^s"), 0x651 }, // shadda
1.81 + { reinterpret_cast<const TText*>(L"^h"), 0x652 }, // sukun
1.82 + { reinterpret_cast<const TText*>(L"^~"), 0x653 }, // maddah
1.83 + { reinterpret_cast<const TText*>(L"^`"), 0x654 }, // hamza above
1.84 + { reinterpret_cast<const TText*>(L"_`"), 0x653 }, // hamza below
1.85 + { reinterpret_cast<const TText*>(L"0"), 0x660 },
1.86 + { reinterpret_cast<const TText*>(L"1"), 0x661 },
1.87 + { reinterpret_cast<const TText*>(L"2"), 0x662 },
1.88 + { reinterpret_cast<const TText*>(L"3"), 0x663 },
1.89 + { reinterpret_cast<const TText*>(L"4"), 0x664 },
1.90 + { reinterpret_cast<const TText*>(L"5"), 0x665 },
1.91 + { reinterpret_cast<const TText*>(L"6"), 0x666 },
1.92 + { reinterpret_cast<const TText*>(L"7"), 0x667 },
1.93 + { reinterpret_cast<const TText*>(L"8"), 0x668 },
1.94 + { reinterpret_cast<const TText*>(L"9"), 0x669 }
1.95 + };
1.96 +}
1.97 +using namespace LocalToTGraphemeIterator;
1.98 +
1.99 +TText TransliterateSingle(const TText*& aInput, const TText* aEnd)
1.100 + {
1.101 + const TInt tableSize =
1.102 + sizeof(KArabicTransliteration)/sizeof(KArabicTransliteration[0]);
1.103 + for (TInt i = 0; i != tableSize; ++i)
1.104 + {
1.105 + const TText* p = KArabicTransliteration[i].iString;
1.106 + const TText* q = aInput;
1.107 + while (q != aEnd && *q == *p)
1.108 + {
1.109 + ++q;
1.110 + ++p;
1.111 + if (*p == '\0')
1.112 + {
1.113 + aInput = q;
1.114 + return static_cast<TText>(KArabicTransliteration[i].iChar);
1.115 + }
1.116 + }
1.117 + }
1.118 + TText result = *aInput;
1.119 + ++aInput;
1.120 + return result;
1.121 + }
1.122 +
1.123 +// transliteration is turned on with { and off with }.
1.124 +// use }{ to split digraphs.
1.125 +void Transliterate(const TDesC& aIn, TDes& aOut)
1.126 + {
1.127 + const TText KTransliterationOn = '{';
1.128 + const TText KTransliterationOff = '}';
1.129 + TBool transliterating = EFalse;
1.130 + const TText* p = &aIn[0];
1.131 + const TText* pEnd = p + aIn.Length();
1.132 + while (p != pEnd)
1.133 + {
1.134 + if (!transliterating)
1.135 + {
1.136 + if (*p == KTransliterationOn)
1.137 + {
1.138 + transliterating = ETrue;
1.139 + ++p;
1.140 + }
1.141 + else
1.142 + aOut.Append(*p++);
1.143 + }
1.144 + else
1.145 + {
1.146 + if (*p == KTransliterationOff)
1.147 + {
1.148 + transliterating = EFalse;
1.149 + ++p;
1.150 + }
1.151 + else
1.152 + aOut.Append(TransliterateSingle(p, pEnd));
1.153 + }
1.154 + }
1.155 + }
1.156 +
1.157 +/**
1.158 +Tests RTmGraphemeEdgeIterator::DocPosMatches for this document position and
1.159 +edge.
1.160 +*/
1.161 +void TestDocPosMatchesCase(const TTmGraphemeEdgeInfo& aEdgeInfo,
1.162 + TTmDocPosSpec& aPosSpec,
1.163 + RTmGraphemeEdgeIterator::TGraphemeMatch aExpectedMatchType)
1.164 + {
1.165 + TInt start = aEdgeInfo.iPos.iDocPos.iPos;
1.166 + TInt end = start;
1.167 + if (aEdgeInfo.iPos.iDocPos.iLeadingEdge)
1.168 + end += aEdgeInfo.iCodePoints;
1.169 + else
1.170 + {
1.171 + start -= aEdgeInfo.iCodePoints - 1;
1.172 + ++end;
1.173 + }
1.174 + aPosSpec.iPos = start - 1;
1.175 + TESTPOINT(RTmGraphemeEdgeIterator::DocPosMatches(aPosSpec, aEdgeInfo)
1.176 + == RTmGraphemeEdgeIterator::ENoMatch);
1.177 + for (TInt i = start; i != end; ++i)
1.178 + {
1.179 + aPosSpec.iPos = i;
1.180 + TESTPOINT(RTmGraphemeEdgeIterator::DocPosMatches(aPosSpec, aEdgeInfo)
1.181 + == aExpectedMatchType);
1.182 + }
1.183 + aPosSpec.iPos = end;
1.184 + TESTPOINT(RTmGraphemeEdgeIterator::DocPosMatches(aPosSpec, aEdgeInfo)
1.185 + == RTmGraphemeEdgeIterator::ENoMatch);
1.186 + }
1.187 +
1.188 +/**
1.189 +Tests RTmGraphemeEdgeIterator::DocPosMatches for this edge and all relevant
1.190 +document position specifications.
1.191 +*/
1.192 +void TestDocPosMatchesAllSpecs(const TTmGraphemeEdgeInfo& aEdgeInfo)
1.193 + {
1.194 + TTmDocPosSpec posSpec;
1.195 + RTmGraphemeEdgeIterator::TGraphemeMatch expected;
1.196 + posSpec.iType = TTmDocPosSpec::ELeftToRight;
1.197 + expected = aEdgeInfo.iPos.iRightToLeft?
1.198 + RTmGraphemeEdgeIterator::EPositionOnly
1.199 + : RTmGraphemeEdgeIterator::ETotalMatch;
1.200 + TestDocPosMatchesCase(aEdgeInfo, posSpec, expected);
1.201 + posSpec.iType = TTmDocPosSpec::ERightToLeft;
1.202 + expected = aEdgeInfo.iPos.iRightToLeft?
1.203 + RTmGraphemeEdgeIterator::ETotalMatch
1.204 + : RTmGraphemeEdgeIterator::EPositionOnly;
1.205 + TestDocPosMatchesCase(aEdgeInfo, posSpec, expected);
1.206 + posSpec.iType = TTmDocPosSpec::ETrailing;
1.207 + expected = aEdgeInfo.iPos.iDocPos.iLeadingEdge?
1.208 + RTmGraphemeEdgeIterator::ENoMatch
1.209 + : RTmGraphemeEdgeIterator::ETotalMatch;
1.210 + TestDocPosMatchesCase(aEdgeInfo, posSpec, expected);
1.211 + posSpec.iType = TTmDocPosSpec::ELeading;
1.212 + expected = aEdgeInfo.iPos.iDocPos.iLeadingEdge?
1.213 + RTmGraphemeEdgeIterator::ETotalMatch
1.214 + : RTmGraphemeEdgeIterator::ENoMatch;
1.215 + TestDocPosMatchesCase(aEdgeInfo, posSpec, expected);
1.216 + }
1.217 +
1.218 +/**
1.219 +Tests RTmGraphemeEdgeIterator::DocPosMatches for a variety of edges and
1.220 +positions.
1.221 +*/
1.222 +void TestDocPosMatches()
1.223 + {
1.224 + TTmGraphemeEdgeInfo edgeInfo;
1.225 + edgeInfo.iPos.iDocPos.iPos = 5;
1.226 + for (edgeInfo.iCodePoints = 1; edgeInfo.iCodePoints <= 3;
1.227 + ++edgeInfo.iCodePoints)
1.228 + {
1.229 + edgeInfo.iPos.iDocPos.iLeadingEdge = ETrue;
1.230 + edgeInfo.iPos.iRightToLeft = EFalse;
1.231 + TestDocPosMatchesAllSpecs(edgeInfo);
1.232 + edgeInfo.iPos.iDocPos.iLeadingEdge = EFalse;
1.233 + TestDocPosMatchesAllSpecs(edgeInfo);
1.234 + edgeInfo.iPos.iRightToLeft = ETrue;
1.235 + TestDocPosMatchesAllSpecs(edgeInfo);
1.236 + edgeInfo.iPos.iDocPos.iLeadingEdge = ETrue;
1.237 + TestDocPosMatchesAllSpecs(edgeInfo);
1.238 + }
1.239 + }
1.240 +
1.241 +enum TEdgeType { ETrail, ELead };
1.242 +enum TEdgeRelationship { EEdgeDifferent, EEdgeSame, EEdgeNewline };
1.243 +enum TAmbiguity { EUnamb = 0, EAmb = 1 };
1.244 +enum TDirectionality { EL2R = 0, ER2L = 1 };
1.245 +struct TEdge
1.246 + {
1.247 + TInt iPos;
1.248 + TEdgeType iLeading;
1.249 + TEdgeRelationship iNext;
1.250 + TAmbiguity iAmbiguity;
1.251 + TDirectionality iRightToLeft;
1.252 + };
1.253 +
1.254 +_LIT(KLatin1, "Latin text\x2029Latin text over three lines.");
1.255 +static const TEdge KLatin1Edges[] =
1.256 + {
1.257 + {0, ETrail, EEdgeSame, EUnamb, EL2R}, {0, ELead, EEdgeDifferent, EUnamb, EL2R},
1.258 + {1, ETrail, EEdgeSame, EUnamb, EL2R}, {1, ELead, EEdgeDifferent, EUnamb, EL2R},
1.259 + {2, ETrail, EEdgeSame, EUnamb, EL2R}, {2, ELead, EEdgeDifferent, EUnamb, EL2R},
1.260 + {3, ETrail, EEdgeSame, EUnamb, EL2R}, {3, ELead, EEdgeDifferent, EUnamb, EL2R},
1.261 + {4, ETrail, EEdgeSame, EUnamb, EL2R}, {4, ELead, EEdgeDifferent, EUnamb, EL2R},
1.262 + {5, ETrail, EEdgeSame, EUnamb, EL2R}, {5, ELead, EEdgeDifferent, EUnamb, EL2R},
1.263 + {6, ETrail, EEdgeSame, EUnamb, EL2R}, {6, ELead, EEdgeDifferent, EUnamb, EL2R},
1.264 + {7, ETrail, EEdgeSame, EUnamb, EL2R}, {7, ELead, EEdgeDifferent, EUnamb, EL2R},
1.265 + {8, ETrail, EEdgeSame, EUnamb, EL2R}, {8, ELead, EEdgeDifferent, EUnamb, EL2R},
1.266 + {9, ETrail, EEdgeSame, EUnamb, EL2R}, {9, ELead, EEdgeDifferent, EUnamb, EL2R},
1.267 + {10, ETrail, EEdgeSame, EUnamb, EL2R}, {10, ELead, EEdgeNewline, EUnamb, EL2R},
1.268 + {11, ETrail, EEdgeSame, EUnamb, EL2R}, {11, ELead, EEdgeDifferent, EUnamb, EL2R},
1.269 + {12, ETrail, EEdgeSame, EUnamb, EL2R}, {12, ELead, EEdgeDifferent, EUnamb, EL2R},
1.270 + {13, ETrail, EEdgeSame, EUnamb, EL2R}, {13, ELead, EEdgeDifferent, EUnamb, EL2R},
1.271 + {14, ETrail, EEdgeSame, EUnamb, EL2R}, {14, ELead, EEdgeDifferent, EUnamb, EL2R},
1.272 + {15, ETrail, EEdgeSame, EUnamb, EL2R}, {15, ELead, EEdgeDifferent, EUnamb, EL2R},
1.273 + {16, ETrail, EEdgeSame, EUnamb, EL2R}, {16, ELead, EEdgeDifferent, EUnamb, EL2R},
1.274 + {17, ETrail, EEdgeSame, EUnamb, EL2R}, {17, ELead, EEdgeDifferent, EUnamb, EL2R},
1.275 + {18, ETrail, EEdgeSame, EUnamb, EL2R}, {18, ELead, EEdgeDifferent, EUnamb, EL2R},
1.276 + {19, ETrail, EEdgeSame, EUnamb, EL2R}, {19, ELead, EEdgeDifferent, EUnamb, EL2R},
1.277 + {20, ETrail, EEdgeSame, EUnamb, EL2R}, {20, ELead, EEdgeDifferent, EUnamb, EL2R},
1.278 +// This change tests the change made for DEF059214 which makes
1.279 +// the trailing edges of line breaks over unambiguous text move to
1.280 +// the start of the next line rather than hanging onto the end of
1.281 +// the breaking line.
1.282 +// {21, ETrail, EEdgeSame, EUnamb, EL2R}, {21, ELead, EEdgeDifferent, EUnamb, EL2R},
1.283 +// {22, ETrail, EEdgeNewline, EUnamb, EL2R}, {22, ELead, EEdgeDifferent, EUnamb, EL2R},
1.284 + {21, ETrail, EEdgeSame, EUnamb, EL2R}, {21, ELead, EEdgeNewline, EUnamb, EL2R},
1.285 + {22, ETrail, EEdgeSame, EUnamb, EL2R}, {22, ELead, EEdgeDifferent, EUnamb, EL2R},
1.286 + {23, ETrail, EEdgeSame, EUnamb, EL2R}, {23, ELead, EEdgeDifferent, EUnamb, EL2R},
1.287 + {24, ETrail, EEdgeSame, EUnamb, EL2R}, {24, ELead, EEdgeDifferent, EUnamb, EL2R},
1.288 + {25, ETrail, EEdgeSame, EUnamb, EL2R}, {25, ELead, EEdgeDifferent, EUnamb, EL2R},
1.289 + {26, ETrail, EEdgeSame, EUnamb, EL2R}, {26, ELead, EEdgeDifferent, EUnamb, EL2R},
1.290 + {27, ETrail, EEdgeSame, EUnamb, EL2R}, {27, ELead, EEdgeDifferent, EUnamb, EL2R},
1.291 + {28, ETrail, EEdgeSame, EUnamb, EL2R}, {28, ELead, EEdgeDifferent, EUnamb, EL2R},
1.292 + {29, ETrail, EEdgeSame, EUnamb, EL2R}, {29, ELead, EEdgeDifferent, EUnamb, EL2R},
1.293 + {30, ETrail, EEdgeSame, EUnamb, EL2R}, {30, ELead, EEdgeDifferent, EUnamb, EL2R},
1.294 + {31, ETrail, EEdgeSame, EUnamb, EL2R}, {31, ELead, EEdgeDifferent, EUnamb, EL2R},
1.295 + {32, ETrail, EEdgeSame, EUnamb, EL2R}, {32, ELead, EEdgeDifferent, EUnamb, EL2R},
1.296 + {33, ETrail, EEdgeSame, EUnamb, EL2R}, {33, ELead, EEdgeDifferent, EUnamb, EL2R},
1.297 + {34, ETrail, EEdgeSame, EUnamb, EL2R}, {34, ELead, EEdgeDifferent, EUnamb, EL2R},
1.298 + {35, ETrail, EEdgeSame, EUnamb, EL2R}, {35, ELead, EEdgeDifferent, EUnamb, EL2R},
1.299 + {36, ETrail, EEdgeSame, EUnamb, EL2R}, {36, ELead, EEdgeDifferent, EUnamb, EL2R},
1.300 + {37, ETrail, EEdgeSame, EUnamb, EL2R}, {37, ELead, EEdgeDifferent, EUnamb, EL2R},
1.301 + {38, ETrail, EEdgeSame, EUnamb, EL2R}, {38, ELead, EEdgeDifferent, EUnamb, EL2R},
1.302 + {39, ETrail, EEdgeSame, EUnamb, EL2R}, {39, ELead, EEdgeDifferent, EUnamb, EL2R},
1.303 + {40, ETrail, EEdgeSame, EUnamb, EL2R}, {40, ELead, EEdgeDifferent, EUnamb, EL2R},
1.304 +// This change tests the change made for DEF059214
1.305 +// {41, ETrail, EEdgeSame, EUnamb, EL2R}, {41, ELead, EEdgeDifferent, EUnamb, EL2R},
1.306 +// {42, ETrail, EEdgeNewline, EUnamb, EL2R}, {42, ELead, EEdgeDifferent, EUnamb, EL2R},
1.307 +// similar changes have been made to other tests.
1.308 + {41, ETrail, EEdgeSame, EUnamb, EL2R}, {41, ELead, EEdgeNewline, EUnamb, EL2R},
1.309 + {42, ETrail, EEdgeSame, EUnamb, EL2R}, {42, ELead, EEdgeDifferent, EUnamb, EL2R},
1.310 + {43, ETrail, EEdgeSame, EUnamb, EL2R}, {43, ELead, EEdgeDifferent, EUnamb, EL2R},
1.311 + {44, ETrail, EEdgeSame, EUnamb, EL2R}, {44, ELead, EEdgeDifferent, EUnamb, EL2R},
1.312 + {45, ETrail, EEdgeSame, EUnamb, EL2R}, {45, ELead, EEdgeDifferent, EUnamb, EL2R},
1.313 + {46, ETrail, EEdgeSame, EUnamb, EL2R}, {46, ELead, EEdgeDifferent, EUnamb, EL2R},
1.314 + {47, ETrail, EEdgeSame, EUnamb, EL2R}, {47, ELead, EEdgeDifferent, EUnamb, EL2R},
1.315 + {48, ETrail, EEdgeSame, EUnamb, EL2R}, {48, ELead, EEdgeNewline, EUnamb, EL2R},
1.316 + };
1.317 +
1.318 +_LIT(KArabic1, "{al'rbyA}\x2029{al'rbyA kf Sayd almwstfa}\x2029{lala lala}.");
1.319 +static const TEdge KArabic1Edges[] =
1.320 + {
1.321 + {7, ELead, EEdgeSame, EUnamb, ER2L}, {7, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.322 + {6, ELead, EEdgeSame, EUnamb, ER2L}, {6, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.323 + {5, ELead, EEdgeSame, EUnamb, ER2L}, {5, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.324 + {4, ELead, EEdgeSame, EUnamb, ER2L}, {4, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.325 + {3, ELead, EEdgeSame, EUnamb, ER2L}, {3, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.326 + {2, ELead, EEdgeSame, EUnamb, ER2L}, {2, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.327 + {1, ELead, EEdgeSame, EUnamb, ER2L}, {1, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.328 + {0, ELead, EEdgeSame, EUnamb, ER2L}, {0, ETrail, EEdgeNewline, EUnamb, ER2L},
1.329 + {18, ELead, EEdgeSame, EUnamb, ER2L}, {18, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.330 + {17, ELead, EEdgeSame, EUnamb, ER2L}, {17, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.331 + {16, ELead, EEdgeSame, EUnamb, ER2L}, {16, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.332 + {15, ELead, EEdgeSame, EUnamb, ER2L}, {15, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.333 + {14, ELead, EEdgeSame, EUnamb, ER2L}, {14, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.334 + {13, ELead, EEdgeSame, EUnamb, ER2L}, {13, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.335 + {12, ELead, EEdgeSame, EUnamb, ER2L}, {12, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.336 + {11, ELead, EEdgeSame, EUnamb, ER2L}, {11, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.337 + {10, ELead, EEdgeSame, EUnamb, ER2L}, {10, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.338 + {9, ELead, EEdgeSame, EUnamb, ER2L}, {9, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.339 + {8, ELead, EEdgeSame, EUnamb, ER2L}, {8, ETrail, EEdgeNewline, EUnamb, ER2L},
1.340 + {23, ELead, EEdgeSame, EUnamb, ER2L}, {23, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.341 + {22, ELead, EEdgeSame, EUnamb, ER2L}, {22, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.342 + {21, ELead, EEdgeSame, EUnamb, ER2L}, {21, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.343 + {20, ELead, EEdgeSame, EUnamb, ER2L}, {20, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.344 + {19, ELead, EEdgeSame, EUnamb, ER2L}, {19, ETrail, EEdgeNewline, EUnamb, ER2L},
1.345 + {32, ELead, EEdgeSame, EUnamb, ER2L}, {32, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.346 + {31, ELead, EEdgeSame, EUnamb, ER2L}, {31, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.347 + {30, ELead, EEdgeSame, EUnamb, ER2L}, {30, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.348 + {29, ELead, EEdgeSame, EUnamb, ER2L}, {29, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.349 + {28, ELead, EEdgeSame, EUnamb, ER2L}, {28, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.350 + {27, ELead, EEdgeSame, EUnamb, ER2L}, {27, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.351 + {26, ELead, EEdgeSame, EUnamb, ER2L}, {26, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.352 + {25, ELead, EEdgeSame, EUnamb, ER2L}, {25, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.353 + {24, ELead, EEdgeSame, EUnamb, ER2L}, {24, ETrail, EEdgeNewline, EUnamb, ER2L},
1.354 + {43, ELead, EEdgeSame, EUnamb, ER2L}, {43, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.355 + {42, ELead, EEdgeSame, EUnamb, ER2L}, {42, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.356 + {40, ELead, EEdgeSame, EUnamb, ER2L}, {40, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.357 + {38, ELead, EEdgeSame, EUnamb, ER2L}, {38, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.358 + {37, ELead, EEdgeSame, EUnamb, ER2L}, {37, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.359 + {35, ELead, EEdgeSame, EUnamb, ER2L}, {35, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.360 + {33, ELead, EEdgeSame, EUnamb, ER2L}, {33, ETrail, EEdgeNewline, EUnamb, ER2L}
1.361 + };
1.362 +
1.363 +// Add another example including combining marks and zero-width characters
1.364 +_LIT(KCombiners1, "z\x300\x301\x302y\x300\x301\x302\x2029z\x300\x301\x302(\xFEFF)");
1.365 +static const TEdge KCombiners1Edges[] =
1.366 + {
1.367 + {0, ETrail, EEdgeSame, EUnamb, EL2R}, {0, ELead, EEdgeDifferent, EUnamb, EL2R},
1.368 + {4, ETrail, EEdgeSame, EUnamb, EL2R}, {4, ELead, EEdgeDifferent, EUnamb, EL2R},
1.369 + {8, ETrail, EEdgeSame, EUnamb, EL2R}, {8, ELead, EEdgeNewline, EUnamb, EL2R},
1.370 + {9, ETrail, EEdgeSame, EUnamb, EL2R}, {9, ELead, EEdgeNewline, EUnamb, EL2R},
1.371 + {13, ETrail, EEdgeSame, EUnamb, EL2R}, {13, ELead, EEdgeDifferent, EUnamb, EL2R},
1.372 + {14, ETrail, EEdgeSame, EUnamb, EL2R}, {14, ELead, EEdgeSame, EUnamb, EL2R},
1.373 + {15, ETrail, EEdgeSame, EUnamb, EL2R}, {15, ELead, EEdgeDifferent, EUnamb, EL2R},
1.374 + {16, ETrail, EEdgeSame, EUnamb, EL2R}, {16, ELead, EEdgeNewline, EUnamb, EL2R}
1.375 + };
1.376 +
1.377 +// Add another example including bidirectional text
1.378 +_LIT(KBidi1, "A\x301{b^ft^k}12\x200FZ\x301");
1.379 +static const TEdge KBidi1Edges[] =
1.380 + {
1.381 + {0, ETrail, EEdgeSame, EUnamb, EL2R}, {0, ELead, EEdgeDifferent, EUnamb, EL2R},
1.382 + {2, ETrail, EEdgeSame, EUnamb, EL2R}, {9, ETrail, EEdgeSame, EAmb, ER2L},
1.383 + {8, ELead, EEdgeSame, EUnamb, ER2L}, {6, ELead, EEdgeDifferent, EAmb, EL2R},
1.384 + {7, ETrail, EEdgeSame, EUnamb, EL2R}, {7, ELead, EEdgeDifferent, EUnamb, EL2R},
1.385 + {8, ETrail, EEdgeSame, EUnamb, EL2R}, {6, ETrail, EEdgeDifferent, EAmb, ER2L},
1.386 + {4, ELead, EEdgeSame, EUnamb, ER2L}, {4, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.387 + {2, ELead, EEdgeSame, EUnamb, ER2L}, {9, ELead, EEdgeDifferent, EAmb, EL2R},
1.388 + {11, ETrail, EEdgeSame, EUnamb, EL2R}, {11, ELead, EEdgeNewline, EUnamb, EL2R}
1.389 + };
1.390 +
1.391 +// This example contains the "amtriguous" case where numbers
1.392 +// are embedded within Arabic but are next to Latin.
1.393 +_LIT(KBidi2, "A\x301{b^ft^k}12Z\x301");
1.394 +static const TEdge KBidi2Edges[] =
1.395 + {
1.396 + {0, ETrail, EEdgeSame, EUnamb, EL2R}, {0, ELead, EEdgeDifferent, EUnamb, EL2R},
1.397 + {2, ETrail, EEdgeSame, EUnamb, EL2R}, {6, ELead, EEdgeDifferent, EAmb, EL2R},
1.398 + {7, ETrail, EEdgeSame, EUnamb, EL2R}, {7, ELead, EEdgeDifferent, EUnamb, EL2R},
1.399 + {8, ETrail, EEdgeSame, EUnamb, EL2R}, {6, ETrail, EEdgeDifferent, EAmb, ER2L},
1.400 + {4, ELead, EEdgeSame, EUnamb, ER2L}, {4, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.401 + {2, ELead, EEdgeSame, EUnamb, ER2L}, {8, ELead, EEdgeDifferent, EAmb, EL2R},
1.402 + {10, ETrail, EEdgeSame, EUnamb, EL2R}, {10, ELead, EEdgeNewline, EUnamb, EL2R}
1.403 + };
1.404 +
1.405 +_LIT(KParagraphs1, "z\x2029{b}\x2029z");
1.406 +static const TEdge KParagraphs1Edges[] =
1.407 + {
1.408 + // First line:
1.409 + // 0T 0L (Z) 1T 1L (ParagraphDelimiter)
1.410 + {0, ETrail, EEdgeSame, EUnamb, EL2R}, {0, ELead, EEdgeDifferent, EUnamb, EL2R},
1.411 + {1, ETrail, EEdgeSame, EUnamb, EL2R}, {1, ELead, EEdgeNewline, EUnamb, EL2R},
1.412 + // Second line:
1.413 + // (PD) 3L< 3T< (Beh) 2L< 2T<
1.414 + {3, ELead, EEdgeSame, EUnamb, ER2L}, {3, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.415 + {2, ELead, EEdgeSame, EUnamb, ER2L}, {2, ETrail, EEdgeNewline, EUnamb, ER2L},
1.416 + // Third line:
1.417 + // 4T 4L (Z) 5T 5L (NominalPD)
1.418 + {4, ETrail, EEdgeSame, EUnamb, EL2R}, {4, ELead, EEdgeDifferent, EUnamb, EL2R},
1.419 + {5, ETrail, EEdgeSame, EUnamb, EL2R}, {5, ELead, EEdgeNewline, EUnamb, EL2R}
1.420 + };
1.421 +
1.422 +// 2 characters per line
1.423 +_LIT(KEmbedded1, "z{bb}z\x2029{b}zz{b}\x2029z{b}zz{b}z\x2029{b}z{bb}z{b}");
1.424 +// T=trailing, L=leading, !=Ambiguous, <=Right-to-left
1.425 +static const TEdge KEmbedded1Edges[] =
1.426 + {
1.427 + // First line:
1.428 + // 0T 0L (0Z) 1T! 2T!< (1Beh) 1L<
1.429 + {0, ETrail, EEdgeSame, EUnamb, EL2R}, {0, ELead, EEdgeDifferent, EUnamb, EL2R},
1.430 + {1, ETrail, EEdgeSame, EAmb, EL2R}, {2, ETrail, EEdgeDifferent, EAmb, ER2L},
1.431 + {1, ELead, EEdgeNewline, EUnamb, ER2L},
1.432 + // Second line:
1.433 + // 3T< (2Beh) 2L!< 3L! (3Z) 4T 4L (4ParagraphDelimiter)
1.434 + {3, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.435 + {2, ELead, EEdgeSame, EAmb, ER2L}, {3, ELead, EEdgeDifferent, EAmb, EL2R},
1.436 + {4, ETrail, EEdgeSame, EUnamb, EL2R}, {4, ELead, EEdgeNewline, EUnamb, EL2R},
1.437 + // Third line:
1.438 + // 6L (6Z) 7T! 6T!< (5Beh) 5L< 5T<
1.439 + {6, ELead, EEdgeDifferent, EUnamb, EL2R},
1.440 + {7, ETrail, EEdgeSame, EAmb, EL2R}, {6, ETrail, EEdgeDifferent, EAmb, ER2L},
1.441 + {5, ELead, EEdgeSame, EUnamb, ER2L}, {5, ETrail, EEdgeNewline, EUnamb, ER2L},
1.442 + // Fourth line:
1.443 + // (9PD) 9L< 9T< (8Beh) 8L!< 7L! (7Z) 8T
1.444 + {9, ELead, EEdgeSame, EUnamb, ER2L}, {9, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.445 + {8, ELead, EEdgeSame, EAmb, ER2L}, {7, ELead, EEdgeDifferent, EAmb, EL2R},
1.446 + {8, ETrail, EEdgeNewline, EUnamb, EL2R},
1.447 + // Fifth line:
1.448 + // 10T 10L (10Z) 11T! 12T!< (11Beh) 11L<
1.449 + {10, ETrail, EEdgeSame, EUnamb, EL2R}, {10, ELead, EEdgeDifferent, EUnamb, EL2R},
1.450 + {11, ETrail, EEdgeSame, EAmb, EL2R}, {12, ETrail, EEdgeDifferent, EAmb, ER2L},
1.451 + {11, ELead, EEdgeNewline, EUnamb, ER2L},
1.452 + // Sixth line:
1.453 + // 12L (12Z) 13T 13L (13Z) 14T
1.454 + {12, ELead, EEdgeDifferent, EUnamb, EL2R},
1.455 + {13, ETrail, EEdgeSame, EUnamb, EL2R}, {13, ELead, EEdgeDifferent, EUnamb, EL2R},
1.456 + {14, ETrail, EEdgeNewline, EUnamb, EL2R},
1.457 + // Seventh line:
1.458 + // 15T< (14Beh) 14L!< 15L! (15Z) 16T 16L (16PD)
1.459 + {15, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.460 + {14, ELead, EEdgeSame, EAmb, ER2L}, {15, ELead, EEdgeDifferent, EAmb, EL2R},
1.461 + {16, ETrail, EEdgeSame, EUnamb, EL2R}, {16, ELead, EEdgeNewline, EUnamb, EL2R},
1.462 + // Eighth line:
1.463 + // 18L (18Z) 19T! 18T!< (17Beh) 17L< 17T<
1.464 + {18, ELead, EEdgeDifferent, EUnamb, EL2R},
1.465 + {19, ETrail, EEdgeSame, EAmb, EL2R}, {18, ETrail, EEdgeDifferent, EAmb, ER2L},
1.466 + {17, ELead, EEdgeSame, EUnamb, ER2L}, {17, ETrail, EEdgeNewline, EUnamb, ER2L},
1.467 + // Ninth line:
1.468 + // 21T< (20Beh) 20L< 20T< (19Beh) 19L<
1.469 + {21, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.470 + {20, ELead, EEdgeSame, EUnamb, ER2L}, {20, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.471 + {19, ELead, EEdgeNewline, EUnamb, ER2L},
1.472 + // Tenth line:
1.473 + // (23NominalPD) 23L< 23T< (22Beh) 22L!< 21L! (21Z) 22T
1.474 + {23, ELead, EEdgeSame, EUnamb, ER2L}, {23, ETrail, EEdgeDifferent, EUnamb, ER2L},
1.475 + {22, ELead, EEdgeSame, EAmb, ER2L}, {21, ELead, EEdgeDifferent, EAmb, EL2R},
1.476 + {22, ETrail, EEdgeNewline, EUnamb, EL2R},
1.477 + };
1.478 +
1.479 +/**
1.480 +Returns which portion of the text is in the specified line.
1.481 +*/
1.482 +void LineExtent(TInt aLine, CTestTmTextLayout& aLayout,
1.483 + TInt& aLineStart, TInt& aLineEnd)
1.484 + {
1.485 + CTmTextLayout& layout = aLayout.Layout();
1.486 + TTmInterpreterParam interpParam(layout);
1.487 + RTmGeneralInterpreter interp(aLayout.Source(), interpParam);
1.488 + interp.LineNumberToLine(aLine);
1.489 + aLineStart = interp.LineInfo().iStart;
1.490 + aLineEnd = interp.LineInfo().iEnd;
1.491 + }
1.492 +
1.493 +/**
1.494 +Finds an edge in the expected edges list that matches a document position
1.495 +specification.
1.496 +*/
1.497 +const TEdge* FindExpectedEdge(const TTmDocPosSpec& aPos,
1.498 + const TEdge* aExpected, TInt aNumExpected)
1.499 + {
1.500 + const TEdge* nearestTrailing = 0;
1.501 + TInt distanceTrailing = KMaxTInt;
1.502 + const TEdge* nearestLeading = 0;
1.503 + TInt distanceLeading = KMinTInt;
1.504 + for (const TEdge* e = aExpected; e != aExpected + aNumExpected; ++e)
1.505 + {
1.506 + TInt distance = e->iPos - aPos.iPos;
1.507 + if (!e->iLeading && 0 <= distance && distance < distanceTrailing)
1.508 + {
1.509 + distanceTrailing = distance;
1.510 + nearestTrailing = e;
1.511 + }
1.512 + if (e->iLeading && distanceLeading < distance && distance <= 0)
1.513 + {
1.514 + distanceLeading = distance;
1.515 + nearestLeading = e;
1.516 + }
1.517 + }
1.518 + if (aPos.iType == TTmDocPosSpec::ELeading || !nearestTrailing)
1.519 + {
1.520 + return nearestLeading;
1.521 + }
1.522 + if (aPos.iType == TTmDocPosSpec::ETrailing || !nearestLeading)
1.523 + {
1.524 + return nearestTrailing;
1.525 + }
1.526 + // Differences in iPos might be because pos is within a grapheme cluster,
1.527 + // or might be that the leading or trailing edge is not on that line.
1.528 + // Grapheme cluster differences are OK, not on the line differences will mean
1.529 + // that the one that does not match the input position is wrong.
1.530 + if (nearestLeading->iPos == aPos.iPos && nearestTrailing->iPos != aPos.iPos)
1.531 + return nearestLeading;
1.532 + if (nearestTrailing->iPos == aPos.iPos && nearestLeading->iPos != aPos.iPos)
1.533 + return nearestTrailing;
1.534 + TBool directionalitiesMatch = nearestTrailing->iRightToLeft?
1.535 + nearestLeading->iRightToLeft : !nearestLeading->iRightToLeft;
1.536 + if (directionalitiesMatch)
1.537 + return nearestLeading;
1.538 + TBool leadingIsCorrect = aPos.iType == TTmDocPosSpec::ERightToLeft?
1.539 + nearestLeading->iRightToLeft : !nearestLeading->iRightToLeft;
1.540 + return leadingIsCorrect? nearestLeading : nearestTrailing;
1.541 + }
1.542 +
1.543 +/**
1.544 +Returns ETrue if and only if the two edges specified are expected to be
1.545 +coincident.
1.546 +*/
1.547 +TBool ExpectedEdgesCoincide(const TEdge* aA, const TEdge* aB)
1.548 + {
1.549 + const TEdge* a = aA < aB? aA : aB;
1.550 + const TEdge* b = aA < aB? aB : aA;
1.551 + while (a != b)
1.552 + {
1.553 + if (a->iNext != EEdgeSame)
1.554 + return EFalse;
1.555 + ++a;
1.556 + }
1.557 + return ETrue;
1.558 + }
1.559 +
1.560 +/**
1.561 +Tests that the edge information matches the expected edge.
1.562 +*/
1.563 +void TestExpectedEdge(const TTmPosInfo2& aEdgeInfo,
1.564 + const TEdge* aExpected)
1.565 + {
1.566 + TESTPOINT(aEdgeInfo.iRightToLeft?
1.567 + aExpected->iRightToLeft : !aExpected->iRightToLeft);
1.568 + TESTPOINT(aEdgeInfo.iDocPos.iPos == aExpected->iPos);
1.569 + TESTPOINT(aEdgeInfo.iDocPos.iLeadingEdge?
1.570 + aExpected->iLeading : !aExpected->iLeading);
1.571 + }
1.572 +
1.573 +/**
1.574 +Tests that the edge information matches one of the expected edges.
1.575 +*/
1.576 +void TestEdgeExists(const TTmPosInfo2& aEdgeInfo,
1.577 + const TEdge* aExpected, TInt aNumExpected)
1.578 + {
1.579 + TTmDocPos pos(aEdgeInfo.iDocPos);
1.580 + const TEdge* edge = FindExpectedEdge(pos, aExpected, aNumExpected);
1.581 + TESTPOINT(edge != 0);
1.582 + TestExpectedEdge(aEdgeInfo, edge);
1.583 + }
1.584 +
1.585 +/**
1.586 +Tests that the visual position matches one of the expected edges.
1.587 +*/
1.588 +void TestVisualPositionExists(const TTmVisualDocPos& aPos,
1.589 + const TEdge* aExpected, TInt aNumExpected)
1.590 + {
1.591 + TESTPOINT(aPos.Ambiguity() != TTmVisualDocPos::ENotFound);
1.592 + TTmDocPos posLeft(aPos.LeftEdge().iDocPos);
1.593 + const TEdge* left = FindExpectedEdge(posLeft, aExpected, aNumExpected);
1.594 + TestExpectedEdge(aPos.LeftEdge(), left);
1.595 + TTmDocPos posRight(aPos.RightEdge().iDocPos);
1.596 + const TEdge* right = FindExpectedEdge(posRight, aExpected, aNumExpected);
1.597 + TestExpectedEdge(aPos.RightEdge(), right);
1.598 + TESTPOINT( (aPos.Ambiguity() == TTmVisualDocPos::EAmbiguous
1.599 + && left->iAmbiguity && right->iAmbiguity)
1.600 + || (aPos.Ambiguity() != TTmVisualDocPos::EAmbiguous
1.601 + && !left->iAmbiguity && !right->iAmbiguity) );
1.602 + TESTPOINT(ExpectedEdgesCoincide(left, right));
1.603 + }
1.604 +
1.605 +/**
1.606 +Tests that a RTmGraphemeEdgeIterator iterates through all the positions in a
1.607 +line from left to right.
1.608 +*/
1.609 +void TestLayoutSimplePass(CTestTmTextLayout& aLayout,
1.610 + const TEdge* aExpected, TInt aNumExpected)
1.611 + {
1.612 + CTmTextLayout& layout = aLayout.Layout();
1.613 + TTmInterpreterParam interpParam(layout);
1.614 + RTmGeneralInterpreter interp(aLayout.Source(), interpParam);
1.615 + interp.LineNumberToLine(0);
1.616 + RTmGraphemeEdgeIterator it;
1.617 + it.Begin(interp);
1.618 + TTmPosInfo2 last = it.GetInfo();
1.619 + for (TInt i = 0; i != aNumExpected; ++i)
1.620 + {
1.621 + const TEdge& expected = aExpected[i];
1.622 + TESTPOINT(expected.iPos == last.iDocPos.iPos);
1.623 + TESTPOINT(expected.iLeading == last.iDocPos.iLeadingEdge);
1.624 + it.Next();
1.625 + if (it.AtEnd())
1.626 + {
1.627 + TESTPOINT(expected.iNext == EEdgeNewline);
1.628 + while (interp.Op() != TTmInterpreter::EOpLine && interp.Next())
1.629 + {}
1.630 + if (i + 1 != aNumExpected)
1.631 + {
1.632 + it.Begin(interp);
1.633 + last = it.GetInfo();
1.634 + }
1.635 + }
1.636 + else
1.637 + {
1.638 + TTmPosInfo2 thisOne = it.GetInfo();
1.639 + TestEdgeExists(thisOne, aExpected, aNumExpected);
1.640 + TESTPOINT(expected.iNext != EEdgeNewline);
1.641 + if (expected.iNext == EEdgeSame)
1.642 + TESTPOINT(last.iEdge.iX == thisOne.iEdge.iX);
1.643 + else if (expected.iNext == EEdgeDifferent)
1.644 + TESTPOINT(last.iEdge.iX != thisOne.iEdge.iX);
1.645 + last = thisOne;
1.646 + }
1.647 + }
1.648 + it.Close();
1.649 + interp.Close();
1.650 + }
1.651 +
1.652 +/**
1.653 +Tests that FindXPos returns the edge 'closest' to the input co-ordinate
1.654 +where there is no ambiguity.
1.655 +*/
1.656 +void TestLayoutFindXPosEdges(TInt aLine,
1.657 + CTestTmTextLayout& aLayout,
1.658 + const TEdge* aExpected, TInt aNumExpected)
1.659 + {
1.660 + CTmTextLayout& layout = aLayout.Layout();
1.661 + TTmInterpreterParam interpParam(layout);
1.662 + TTmPosInfo2 posInfo;
1.663 + TTmDocPosSpec posSpec;
1.664 + TTmLineInfo lineInfo;
1.665 + TTmVisualDocPos visPos;
1.666 + for (TInt i = 0; i != aNumExpected - 1; ++i)
1.667 + {
1.668 + const TEdge& expectedL = aExpected[i];
1.669 + const TEdge& expectedR = aExpected[i + 1];
1.670 + if (expectedL.iNext == EEdgeDifferent)
1.671 + {
1.672 + // This code assumes that no character has a width of exactly 1 pixel.
1.673 + if (!expectedL.iAmbiguity)
1.674 + {
1.675 + posSpec.iPos = expectedL.iPos;
1.676 + posSpec.iType = expectedL.iLeading?
1.677 + TTmDocPosSpec::ELeading : TTmDocPosSpec::ETrailing;
1.678 + layout.FindDocPos(posSpec, posInfo, lineInfo);
1.679 + RTmGeneralInterpreter interp(aLayout.Source(), interpParam);
1.680 + interp.LineNumberToLine(aLine);
1.681 + RTmGraphemeEdgeIterator it;
1.682 + it.Begin(interp);
1.683 + it.FindXPos(posInfo.iEdge.iX, visPos);
1.684 + TESTPOINT(visPos.Ambiguity() != TTmVisualDocPos::EAmbiguous);
1.685 + TESTPOINT(visPos.Ambiguity() != TTmVisualDocPos::ENotFound);
1.686 + TESTPOINT(visPos.LeftEdge().iDocPos.iPos == expectedL.iPos);
1.687 + TESTPOINT(visPos.LeftEdge().iDocPos.iLeadingEdge?
1.688 + expectedL.iLeading : !expectedL.iLeading);
1.689 + it.Close();
1.690 + interp.Close();
1.691 + }
1.692 + if (!expectedR.iAmbiguity)
1.693 + {
1.694 + posSpec.iPos = expectedR.iPos;
1.695 + posSpec.iType = expectedR.iLeading?
1.696 + TTmDocPosSpec::ELeading : TTmDocPosSpec::ETrailing;
1.697 + layout.FindDocPos(posSpec, posInfo, lineInfo);
1.698 + RTmGeneralInterpreter interp(aLayout.Source(), interpParam);
1.699 + interp.LineNumberToLine(aLine);
1.700 + RTmGraphemeEdgeIterator it;
1.701 + it.Begin(interp);
1.702 + it.FindXPos(posInfo.iEdge.iX - 1, visPos);
1.703 + TESTPOINT(visPos.Ambiguity() != TTmVisualDocPos::EAmbiguous);
1.704 + TESTPOINT(visPos.Ambiguity() != TTmVisualDocPos::ENotFound);
1.705 + TESTPOINT(visPos.LeftEdge().iDocPos.iPos == expectedR.iPos);
1.706 + TESTPOINT(visPos.LeftEdge().iDocPos.iLeadingEdge?
1.707 + expectedR.iLeading : !expectedR.iLeading);
1.708 + it.Close();
1.709 + interp.Close();
1.710 + }
1.711 + }
1.712 + }
1.713 + }
1.714 +
1.715 +/**
1.716 +Tests that RTmGraphemeEdgeIterator::FindXPos finds document positions that
1.717 +match the positions they are supposed to be in.
1.718 +*/
1.719 +void TestLayoutFindXPos(TInt aLine,
1.720 + CTestTmTextLayout& aLayout,
1.721 + const TEdge* aExpected, TInt aNumExpected)
1.722 + {
1.723 + TInt lastLeftX = KMinTInt;
1.724 + TInt lastRightX = KMinTInt;
1.725 + TTmVisualDocPos visPos;
1.726 + TBool finished = EFalse;
1.727 + for (TInt x = -10; !finished; ++x)
1.728 + {
1.729 + CTmTextLayout& layout = aLayout.Layout();
1.730 + TTmInterpreterParam interpParam(layout);
1.731 + RTmGeneralInterpreter interp(aLayout.Source(), interpParam);
1.732 + interp.LineNumberToLine(aLine);
1.733 + RTmGraphemeEdgeIterator it;
1.734 + it.Begin(interp);
1.735 + it.FindXPos(x, visPos);
1.736 + TestVisualPositionExists(visPos, aExpected, aNumExpected);
1.737 + TESTPOINT(visPos.Ambiguity() != TTmVisualDocPos::ENotFound);
1.738 + TESTPOINT(visPos.LeftEdge().iEdge.iX <= visPos.RightEdge().iEdge.iX);
1.739 + TESTPOINT(visPos.Ambiguity() == TTmVisualDocPos::EAmbiguous
1.740 + || visPos.LeftEdge().iEdge.iX == visPos.RightEdge().iEdge.iX);
1.741 + TESTPOINT(lastLeftX <= visPos.LeftEdge().iEdge.iX);
1.742 + if (lastLeftX == visPos.LeftEdge().iEdge.iX)
1.743 + {
1.744 + TESTPOINT(lastRightX == visPos.RightEdge().iEdge.iX);
1.745 + while (aExpected->iPos != visPos.LeftEdge().iDocPos.iPos
1.746 + || aExpected->iLeading != visPos.LeftEdge().iDocPos.iLeadingEdge)
1.747 + {
1.748 + TESTPOINT(aExpected->iNext == EEdgeSame);
1.749 + TESTPOINT(0 < aNumExpected);
1.750 + ++aExpected;
1.751 + --aNumExpected;
1.752 + }
1.753 + }
1.754 + else
1.755 + {
1.756 + TESTPOINT(lastRightX <= visPos.LeftEdge().iEdge.iX);
1.757 + while (aExpected->iPos != visPos.LeftEdge().iDocPos.iPos
1.758 + || aExpected->iLeading != visPos.LeftEdge().iDocPos.iLeadingEdge)
1.759 + {
1.760 + TESTPOINT(0 < aNumExpected);
1.761 + ++aExpected;
1.762 + --aNumExpected;
1.763 + }
1.764 + }
1.765 + if (interp.LineInfo().iInnerRect.iBr.iX + 120 < x)
1.766 + finished = ETrue;
1.767 + it.Close();
1.768 + interp.Close();
1.769 + }
1.770 + while (aExpected->iNext != EEdgeNewline)
1.771 + {
1.772 + TESTPOINT(aExpected->iNext == EEdgeSame);
1.773 + TESTPOINT(0 < aNumExpected);
1.774 + ++aExpected;
1.775 + --aNumExpected;
1.776 + }
1.777 + }
1.778 +
1.779 +/**
1.780 +Uses RTmGraphemeEdgeIterator::FindEdge to find a document position in a
1.781 +CTestTmTextLayout.
1.782 +*/
1.783 +TBool FindEdgeFromLayout(CTestTmTextLayout& aLayout, TInt aLine,
1.784 + const TTmDocPosSpec& aDocPos, TTmPosInfo2& aInfo)
1.785 + {
1.786 + CTmTextLayout& layout = aLayout.Layout();
1.787 + TTmInterpreterParam interpParam(layout);
1.788 + RTmGeneralInterpreter interp(aLayout.Source(), interpParam);
1.789 + interp.LineNumberToLine(aLine);
1.790 + RTmGraphemeEdgeIterator it;
1.791 + it.Begin(interp);
1.792 + TBool result = it.FindEdge(aDocPos, aInfo);
1.793 + it.Close();
1.794 + interp.Close();
1.795 + return result;
1.796 + }
1.797 +
1.798 +/**
1.799 +Tests that RTmGraphemeEdgeIterator::FindEdge finds the edges in the layout with
1.800 +specifications of leading or trailing edges.
1.801 +*/
1.802 +void TestLayoutFindEdgesInVisualOrder(TInt aLine,
1.803 + CTestTmTextLayout& aLayout,
1.804 + const TEdge* aExpected, TInt aNumExpected)
1.805 + {
1.806 + TInt lastX = KMinTInt;
1.807 + TBool sameExpected = EFalse;
1.808 + TTmPosInfo2 posInfo;
1.809 + while (aNumExpected != 0)
1.810 + {
1.811 + TTmDocPosSpec posSpec(aExpected->iPos, aExpected->iLeading?
1.812 + TTmDocPosSpec::ELeading : TTmDocPosSpec::ETrailing);
1.813 + FindEdgeFromLayout(aLayout, aLine, posSpec, posInfo);
1.814 + TestEdgeExists(posInfo, aExpected, aNumExpected);
1.815 + TESTPOINT(aExpected->iLeading?
1.816 + posInfo.iDocPos.iLeadingEdge : !posInfo.iDocPos.iLeadingEdge);
1.817 + TESTPOINT(aExpected->iPos == posInfo.iDocPos.iPos);
1.818 + TESTPOINT(sameExpected || posInfo.iEdge.iX != lastX);
1.819 + TESTPOINT(!sameExpected || posInfo.iEdge.iX == lastX);
1.820 + lastX = posInfo.iEdge.iX;
1.821 + sameExpected = aExpected->iNext == EEdgeSame? ETrue : EFalse;
1.822 + ++aExpected;
1.823 + --aNumExpected;
1.824 + }
1.825 + }
1.826 +
1.827 +/**
1.828 +Tests that RTmGraphemeEdgeIterator::FindEdge finds the edges in the layout with
1.829 +specifications of directionality.
1.830 +*/
1.831 +void TestLayoutFindEdgesByDirectionality(TInt aLine,
1.832 + CTestTmTextLayout& aLayout,
1.833 + const TEdge* aExpected, TInt aNumExpected)
1.834 + {
1.835 + TInt lineStart;
1.836 + TInt lineEnd;
1.837 + LineExtent(aLine, aLayout, lineStart, lineEnd);
1.838 + TTmPosInfo2 lToRPosInfo;
1.839 + TTmPosInfo2 rToLPosInfo;
1.840 + for (TInt pos = lineStart - 1; pos != lineEnd + 1; ++pos)
1.841 + {
1.842 + TTmDocPosSpec rToLPosSpec(pos, TTmDocPosSpec::ERightToLeft);
1.843 + TBool rToLFound = FindEdgeFromLayout(aLayout, aLine, rToLPosSpec, rToLPosInfo);
1.844 + TTmDocPosSpec lToRPosSpec(pos, TTmDocPosSpec::ELeftToRight);
1.845 + TBool lToRFound = FindEdgeFromLayout(aLayout, aLine, lToRPosSpec, lToRPosInfo);
1.846 + if (!lToRFound)
1.847 + {
1.848 + TESTPOINT(!rToLFound);
1.849 + TESTPOINT(pos < lineStart || lineEnd <= pos);
1.850 + }
1.851 + else
1.852 + {
1.853 + TESTPOINT(rToLFound);
1.854 + TestEdgeExists(rToLPosInfo, aExpected, aNumExpected);
1.855 + TestEdgeExists(lToRPosInfo, aExpected, aNumExpected);
1.856 + // Now find the nearest edges in the expected range
1.857 + TTmDocPosSpec trailingPosSpec(pos, TTmDocPosSpec::ETrailing);
1.858 + const TEdge* trailingExpected
1.859 + = FindExpectedEdge(trailingPosSpec, aExpected, aNumExpected);
1.860 + TTmDocPosSpec leadingPosSpec(pos, TTmDocPosSpec::ELeading);
1.861 + const TEdge* leadingExpected
1.862 + = FindExpectedEdge(leadingPosSpec, aExpected, aNumExpected);
1.863 + if (!trailingExpected)
1.864 + trailingExpected = leadingExpected;
1.865 + if (!leadingExpected)
1.866 + leadingExpected = trailingExpected;
1.867 + const TEdge* rToLPosEdge
1.868 + = FindExpectedEdge(rToLPosInfo.iDocPos, aExpected, aNumExpected);
1.869 + const TEdge* lToRPosEdge
1.870 + = FindExpectedEdge(lToRPosInfo.iDocPos, aExpected, aNumExpected);
1.871 + TESTPOINT(leadingExpected != 0);
1.872 + TESTPOINT(trailingExpected != 0);
1.873 + TESTPOINT(ExpectedEdgesCoincide(leadingExpected, rToLPosEdge)
1.874 + || ExpectedEdgesCoincide(trailingExpected, rToLPosEdge));
1.875 + TESTPOINT(ExpectedEdgesCoincide(leadingExpected, lToRPosEdge)
1.876 + || ExpectedEdgesCoincide(trailingExpected, lToRPosEdge));
1.877 + // Also check that the "found" ones are at least as good as the
1.878 + // "expected" ones.
1.879 + TESTPOINT(rToLPosInfo.iRightToLeft
1.880 + || (!leadingExpected->iRightToLeft && !trailingExpected->iRightToLeft));
1.881 + TESTPOINT(!lToRPosInfo.iRightToLeft
1.882 + || (leadingExpected->iRightToLeft && trailingExpected->iRightToLeft));
1.883 + }
1.884 + }
1.885 + }
1.886 +
1.887 +/**
1.888 +Tests RTmGraphemeEdgeIterator::FindEdgeRightwards or
1.889 +RTmGraphemeEdgeIterator::FindEdgeLeftwards.
1.890 +*/
1.891 +void TestLayoutFindEdgesLeftRight(TInt aLine,
1.892 + CTestTmTextLayout& aLayout, TBool aRightwards,
1.893 + const TEdge* aExpected, TInt aNumExpected)
1.894 + {
1.895 + TInt lineStart;
1.896 + TInt lineEnd;
1.897 + LineExtent(aLine, aLayout, lineStart, lineEnd);
1.898 + TTmPosInfo2 nearest;
1.899 + TTmVisualDocPos next;
1.900 + const TTmDocPosSpec::TType types[4]
1.901 + = {TTmDocPosSpec::ETrailing,
1.902 + TTmDocPosSpec::ELeading,
1.903 + TTmDocPosSpec::ELeftToRight,
1.904 + TTmDocPosSpec::ERightToLeft};
1.905 +
1.906 + for (TInt pos = lineStart - 1; pos != lineEnd + 1; ++pos)
1.907 + {
1.908 + for (TInt type = 0; type != 4; ++type)
1.909 + {
1.910 + TTmDocPosSpec posSpec(pos, types[type]);
1.911 +
1.912 + // What do we expect the nearest to be?
1.913 + TTmDocPosSpec leadingPosSpec(pos, TTmDocPosSpec::ELeading);
1.914 + TTmDocPosSpec trailingPosSpec(pos, TTmDocPosSpec::ETrailing);
1.915 +
1.916 + const TEdge* leadingExpected
1.917 + = FindExpectedEdge(leadingPosSpec, aExpected, aNumExpected);
1.918 + const TEdge* trailingExpected
1.919 + = FindExpectedEdge(trailingPosSpec, aExpected, aNumExpected);
1.920 +
1.921 + // but should we expect anything at all?
1.922 + if (pos < lineStart || lineEnd < pos)
1.923 + leadingExpected = trailingExpected = 0;
1.924 + if (posSpec.iType == TTmDocPosSpec::ELeading
1.925 + && (!leadingExpected || !leadingExpected->iLeading))
1.926 + leadingExpected = trailingExpected = 0;
1.927 + if (posSpec.iType == TTmDocPosSpec::ETrailing
1.928 + && (!trailingExpected || trailingExpected->iLeading))
1.929 + leadingExpected = trailingExpected = 0;
1.930 +
1.931 + // <lineEnd, trailing> is the only element that may be present
1.932 + // at position lineEnd.
1.933 + if (pos == lineEnd)
1.934 + {
1.935 + // If we are looking for a leading edge, we won't find
1.936 + // the trailing even if it is there.
1.937 + if (posSpec.iType == TTmDocPosSpec::ELeading
1.938 + || !trailingExpected
1.939 + || trailingExpected->iPos != pos)
1.940 + leadingExpected = trailingExpected = 0;
1.941 + }
1.942 + // <lineStart, trailing> may not be in the line.
1.943 + // We must check explicitly.
1.944 + if (pos == lineStart && posSpec.iType == TTmDocPosSpec::ETrailing)
1.945 + {
1.946 + // If there is no trailing edge at the start of the line
1.947 + // and we are looking for one, we
1.948 + // do not expect to have a nearest match there.
1.949 + if (!trailingExpected || trailingExpected->iPos != pos)
1.950 + leadingExpected = trailingExpected = 0;
1.951 + }
1.952 +
1.953 + if (!leadingExpected)
1.954 + leadingExpected = trailingExpected;
1.955 + if (!trailingExpected)
1.956 + trailingExpected = leadingExpected;
1.957 +
1.958 + const TEdge* nextExpected = 0;
1.959 +
1.960 + CTmTextLayout& layout = aLayout.Layout();
1.961 + TTmInterpreterParam interpParam(layout);
1.962 + RTmGeneralInterpreter interp(aLayout.Source(), interpParam);
1.963 + interp.LineNumberToLine(aLine);
1.964 + RTmGraphemeEdgeIterator it;
1.965 + it.Begin(interp);
1.966 + RTmGraphemeEdgeIterator::TEdgesFound result = aRightwards?
1.967 + it.FindEdgeRightwards(posSpec, nearest, next)
1.968 + : it.FindEdgeLeftwards(posSpec, nearest, next);
1.969 + interp.Close();
1.970 +
1.971 + // Does what we got match what we expect?
1.972 + if (!leadingExpected)
1.973 + {
1.974 + TESTPOINT(result == RTmGraphemeEdgeIterator::ENone);
1.975 + }
1.976 + else
1.977 + {
1.978 + TESTPOINT(result == RTmGraphemeEdgeIterator::ENearestOnly
1.979 + || result == RTmGraphemeEdgeIterator::ENearestAndNext);
1.980 + TTmDocPosSpec nearestPos(nearest.iDocPos);
1.981 + const TEdge* nearestEdge
1.982 + = FindExpectedEdge(nearestPos, aExpected, aNumExpected);
1.983 + TestExpectedEdge(nearest, nearestEdge);
1.984 + const TEdge* matchingEdge = leadingExpected;
1.985 + if (posSpec.iType == TTmDocPosSpec::ELeading)
1.986 + TESTPOINT(ExpectedEdgesCoincide(leadingExpected, nearestEdge));
1.987 + else if (posSpec.iType == TTmDocPosSpec::ETrailing)
1.988 + {
1.989 + TESTPOINT(ExpectedEdgesCoincide(trailingExpected, nearestEdge));
1.990 + matchingEdge = trailingExpected;
1.991 + }
1.992 + else
1.993 + {
1.994 + TESTPOINT(ExpectedEdgesCoincide(leadingExpected, nearestEdge)
1.995 + || ExpectedEdgesCoincide(trailingExpected, nearestEdge));
1.996 + if (ExpectedEdgesCoincide(trailingExpected, nearestEdge))
1.997 + matchingEdge = trailingExpected;
1.998 + TBool directionalitiesMatch = leadingExpected->iRightToLeft?
1.999 + trailingExpected->iRightToLeft : !trailingExpected->iRightToLeft;
1.1000 + TBool foundCorrectDirectionality
1.1001 + = posSpec.iType == TTmDocPosSpec::ERightToLeft?
1.1002 + nearest.iRightToLeft : !nearest.iRightToLeft;
1.1003 + TESTPOINT(foundCorrectDirectionality || directionalitiesMatch);
1.1004 + }
1.1005 +
1.1006 + // Find next edge in expected list
1.1007 + const TEdge* e = matchingEdge;
1.1008 + const TEdge* end = aRightwards?
1.1009 + aExpected + aNumExpected - 1
1.1010 + : aExpected;
1.1011 + TInt direction = aRightwards? 1 : -1;
1.1012 + while (nextExpected == 0 && e != end)
1.1013 + {
1.1014 + e += direction;
1.1015 + if (!ExpectedEdgesCoincide(e, matchingEdge))
1.1016 + nextExpected = e;
1.1017 + }
1.1018 + }
1.1019 + if (!nextExpected)
1.1020 + TESTPOINT(result == RTmGraphemeEdgeIterator::ENone
1.1021 + || result == RTmGraphemeEdgeIterator::ENearestOnly);
1.1022 + else
1.1023 + {
1.1024 + TESTPOINT(result == RTmGraphemeEdgeIterator::ENearestAndNext);
1.1025 + TestVisualPositionExists(next, aExpected, aNumExpected);
1.1026 + TESTPOINT(next.Ambiguity() != TTmVisualDocPos::ENotFound);
1.1027 + TTmDocPosSpec nextPosLeft(next.LeftEdge().iDocPos);
1.1028 + TESTPOINT(ExpectedEdgesCoincide(nextExpected,
1.1029 + FindExpectedEdge(nextPosLeft, aExpected, aNumExpected)));
1.1030 + TTmDocPosSpec nextPosRight(next.RightEdge().iDocPos);
1.1031 + TESTPOINT(ExpectedEdgesCoincide(nextExpected,
1.1032 + FindExpectedEdge(nextPosRight, aExpected, aNumExpected)));
1.1033 + }
1.1034 + it.Close();
1.1035 + }
1.1036 + }
1.1037 + }
1.1038 +
1.1039 +/**
1.1040 +Tests RTmGraphemeEdgeIterator::FindEdgeRightwards.
1.1041 +*/
1.1042 +void TestLayoutFindEdgesRightwards(TInt aLine,
1.1043 + CTestTmTextLayout& aLayout,
1.1044 + const TEdge* aExpected, TInt aNumExpected)
1.1045 + {
1.1046 + TestLayoutFindEdgesLeftRight(aLine, aLayout, ETrue,
1.1047 + aExpected, aNumExpected);
1.1048 + }
1.1049 +
1.1050 +/**
1.1051 +Tests RTmGraphemeEdgeIterator::FindEdgeLeftwards.
1.1052 +*/
1.1053 +void TestLayoutFindEdgesLeftwards(TInt aLine,
1.1054 + CTestTmTextLayout& aLayout,
1.1055 + const TEdge* aExpected, TInt aNumExpected)
1.1056 + {
1.1057 + TestLayoutFindEdgesLeftRight(aLine, aLayout, EFalse,
1.1058 + aExpected, aNumExpected);
1.1059 + }
1.1060 +
1.1061 +/**
1.1062 +Tests RTmGraphemeEdgeIterator::NextPosition. Expected behaviour is to find the
1.1063 +smallest number 'n' that is a position in the same line greater than the input 'i',
1.1064 +where the positions <i, leading> and <n, trailing> are not coincident.
1.1065 +*/
1.1066 +void TestLayoutFindEdgesForwards(TInt aLine,
1.1067 + CTestTmTextLayout& aLayout,
1.1068 + const TEdge* aExpected, TInt aNumExpected)
1.1069 + {
1.1070 + TInt lineStart;
1.1071 + TInt lineEnd;
1.1072 + LineExtent(aLine, aLayout, lineStart, lineEnd);
1.1073 + for (TInt i = lineStart - 1; i != lineEnd + 1; ++i)
1.1074 + {
1.1075 + CTmTextLayout& layout = aLayout.Layout();
1.1076 + TTmInterpreterParam interpParam(layout);
1.1077 + RTmGeneralInterpreter interp(aLayout.Source(), interpParam);
1.1078 + interp.LineNumberToLine(aLine);
1.1079 + RTmGraphemeEdgeIterator it;
1.1080 + it.Begin(interp);
1.1081 + TInt result = it.NextPosition(i);
1.1082 + interp.Close();
1.1083 +
1.1084 + if (result == KErrNotFound)
1.1085 + {
1.1086 + // Must be at or after the line's end.
1.1087 + // Try to find a later edge in the line...
1.1088 + TTmDocPosSpec in(i, TTmDocPosSpec::ELeading);
1.1089 + const TEdge* inEdge = FindExpectedEdge(in,
1.1090 + aExpected, aNumExpected);
1.1091 + TTmDocPosSpec out(result, TTmDocPosSpec::ETrailing);
1.1092 + const TEdge* outEdge = FindExpectedEdge(out,
1.1093 + aExpected, aNumExpected);
1.1094 + // ...and test that we failed.
1.1095 + TESTPOINT(!inEdge || inEdge->iPos <= i);
1.1096 + TESTPOINT(!outEdge || outEdge->iPos <= i);
1.1097 + }
1.1098 + else
1.1099 + {
1.1100 + TESTPOINT(i < result);
1.1101 + TTmDocPosSpec in(i, TTmDocPosSpec::ELeading);
1.1102 + const TEdge* inEdge = FindExpectedEdge(in,
1.1103 + aExpected, aNumExpected);
1.1104 + TTmDocPosSpec out(result, TTmDocPosSpec::ETrailing);
1.1105 + const TEdge* outEdge = FindExpectedEdge(out,
1.1106 + aExpected, aNumExpected);
1.1107 + TESTPOINT(outEdge != 0);
1.1108 + if (inEdge)
1.1109 + {
1.1110 + TESTPOINT(lineStart <= i);
1.1111 + TESTPOINT(!ExpectedEdgesCoincide(inEdge, outEdge));
1.1112 + for (TInt j = i + 1; j != result; ++j)
1.1113 + {
1.1114 + TTmDocPosSpec between(j, TTmDocPosSpec::ETrailing);
1.1115 + const TEdge* betweenEdge = FindExpectedEdge(between,
1.1116 + aExpected, aNumExpected);
1.1117 + TESTPOINT(betweenEdge != 0);
1.1118 + // Test that, if there actually is a <j, trailing> edge, it is
1.1119 + // coincident with <i, leading>. If the edge does not exist
1.1120 + // it does not matter. We can find out if it exists by checking
1.1121 + // whether the returned expected edge has the same position
1.1122 + // we asked for.
1.1123 + TESTPOINT(ExpectedEdgesCoincide(inEdge, betweenEdge)
1.1124 + || j != betweenEdge->iPos);
1.1125 + }
1.1126 + }
1.1127 + else
1.1128 + {
1.1129 + // before the start means finding the first trailing edge
1.1130 + TESTPOINT (i < lineStart);
1.1131 + TInt leastTrailingEdge = KMaxTInt;
1.1132 + for (const TEdge* e = aExpected; e != aExpected + aNumExpected;
1.1133 + ++e)
1.1134 + {
1.1135 + if (!e->iLeading && e->iPos < leastTrailingEdge)
1.1136 + leastTrailingEdge = e->iPos;
1.1137 + }
1.1138 + TESTPOINT(leastTrailingEdge == result);
1.1139 + }
1.1140 + }
1.1141 + it.Close();
1.1142 + }
1.1143 + }
1.1144 +
1.1145 +/**
1.1146 +Tests RTmGraphemeEdgeIterator::PreviousPosition. Expected behaviour is to find the
1.1147 +largest number 'n' that is a position in the same line smaller than the input 'i',
1.1148 +where the positions <i, trailing> and <n, leading> are not coincident.
1.1149 +*/
1.1150 +void TestLayoutFindEdgesBackwards(TInt aLine,
1.1151 + CTestTmTextLayout& aLayout,
1.1152 + const TEdge* aExpected, TInt aNumExpected)
1.1153 + {
1.1154 + TInt lineStart;
1.1155 + TInt lineEnd;
1.1156 + LineExtent(aLine, aLayout, lineStart, lineEnd);
1.1157 + for (TInt i = lineStart - 1; i != lineEnd + 1; ++i)
1.1158 + {
1.1159 + CTmTextLayout& layout = aLayout.Layout();
1.1160 + TTmInterpreterParam interpParam(layout);
1.1161 + RTmGeneralInterpreter interp(aLayout.Source(), interpParam);
1.1162 + interp.LineNumberToLine(aLine);
1.1163 + RTmGraphemeEdgeIterator it;
1.1164 + it.Begin(interp);
1.1165 + TInt result = it.PreviousPosition(i);
1.1166 + interp.Close();
1.1167 +
1.1168 + if (result == KErrNotFound)
1.1169 + {
1.1170 + // Must be at or before the line's beginning.
1.1171 + // Could possibly be that there are no leading edges in the line, but
1.1172 + // we'll ignore that possibility.
1.1173 + TESTPOINT(i <= lineStart);
1.1174 + }
1.1175 + else
1.1176 + {
1.1177 + TESTPOINT(result < i);
1.1178 + TTmDocPosSpec out(result, TTmDocPosSpec::ELeading);
1.1179 + const TEdge* outEdge = FindExpectedEdge(out,
1.1180 + aExpected, aNumExpected);
1.1181 + TESTPOINT(outEdge != 0);
1.1182 + TTmDocPosSpec in(i, TTmDocPosSpec::ETrailing);
1.1183 + const TEdge* inEdge = FindExpectedEdge(in,
1.1184 + aExpected, aNumExpected);
1.1185 + // if we could not find a trailing edge at this number, then we
1.1186 + // were beyond the end of the line.
1.1187 + if (inEdge && !inEdge->iLeading)
1.1188 + {
1.1189 + TESTPOINT(inEdge != 0);
1.1190 + TESTPOINT(!ExpectedEdgesCoincide(inEdge, outEdge));
1.1191 + for (TInt j = result + 1; j != i; ++j)
1.1192 + {
1.1193 + TTmDocPosSpec between(j, TTmDocPosSpec::ELeading);
1.1194 + const TEdge* betweenEdge = FindExpectedEdge(between,
1.1195 + aExpected, aNumExpected);
1.1196 + TESTPOINT(betweenEdge != 0);
1.1197 + // Test that, if there actually is a <j, trailing> edge, it is
1.1198 + // coincident with <i, leading>. If the edge does not exist
1.1199 + // it does not matter. We can find out if it exists by checking
1.1200 + // whether the returned expected edge has the same position
1.1201 + // we asked for.
1.1202 + TESTPOINT(ExpectedEdgesCoincide(inEdge, betweenEdge)
1.1203 + || j != betweenEdge->iPos);
1.1204 + }
1.1205 + }
1.1206 + else
1.1207 + {
1.1208 + // after the end means finding the last leading edge
1.1209 + TInt greatestLeadingEdge = KMinTInt;
1.1210 + for (const TEdge* e = aExpected; e != aExpected + aNumExpected;
1.1211 + ++e)
1.1212 + {
1.1213 + if (e->iLeading && greatestLeadingEdge < e->iPos)
1.1214 + greatestLeadingEdge = e->iPos;
1.1215 + }
1.1216 + TESTPOINT(greatestLeadingEdge == result);
1.1217 + }
1.1218 + }
1.1219 + it.Close();
1.1220 + }
1.1221 + }
1.1222 +
1.1223 +typedef void FTestLine(TInt aLine,
1.1224 + CTestTmTextLayout& aLayout,
1.1225 + const TEdge* aExpected, TInt aNumExpected);
1.1226 +
1.1227 +/**
1.1228 +Runs a particular test for each line in the input data.
1.1229 +*/
1.1230 +void TestEachLine(FTestLine* aFn,
1.1231 + CTestTmTextLayout& aLayout, const TEdge* aExpected, TInt aNumExpected)
1.1232 + {
1.1233 + TInt line = 0;
1.1234 + TInt start = 0;
1.1235 + for (TInt end = 1; end != aNumExpected; ++end)
1.1236 + {
1.1237 + if (aExpected[end - 1].iNext == EEdgeNewline)
1.1238 + {
1.1239 + aFn(line, aLayout, aExpected + start, end - start);
1.1240 + start = end;
1.1241 + ++line;
1.1242 + }
1.1243 + }
1.1244 + }
1.1245 +
1.1246 +/**
1.1247 +Tests TTmGraphemeIterator and supporting functionality for the specified
1.1248 +layout.
1.1249 +*/
1.1250 +void TestLayoutL(CTestTmTextLayout& aLayout,
1.1251 + const TEdge* aExpected, TInt aNumExpected)
1.1252 + {
1.1253 + TESTPRINT(_L("Simple iteration"));
1.1254 + TestLayoutSimplePass(aLayout, aExpected, aNumExpected);
1.1255 + TESTPRINT(_L("FindXPos"));
1.1256 + TestEachLine(TestLayoutFindXPos,
1.1257 + aLayout, aExpected, aNumExpected);
1.1258 + TESTPRINT(_L("FindXPos (unambiguous edges)"));
1.1259 + TestEachLine(TestLayoutFindXPosEdges,
1.1260 + aLayout, aExpected, aNumExpected);
1.1261 + TESTPRINT(_L("FindEdge"));
1.1262 + TestEachLine(TestLayoutFindEdgesInVisualOrder,
1.1263 + aLayout, aExpected, aNumExpected);
1.1264 + TestEachLine(TestLayoutFindEdgesByDirectionality,
1.1265 + aLayout, aExpected, aNumExpected);
1.1266 + TESTPRINT(_L("FindEdgeRightwards"));
1.1267 + TestEachLine(TestLayoutFindEdgesRightwards,
1.1268 + aLayout, aExpected, aNumExpected);
1.1269 + TESTPRINT(_L("FindEdgeLeftwards"));
1.1270 + TestEachLine(TestLayoutFindEdgesLeftwards,
1.1271 + aLayout, aExpected, aNumExpected);
1.1272 + TESTPRINT(_L("NextPosition"));
1.1273 + TestEachLine(TestLayoutFindEdgesForwards,
1.1274 + aLayout, aExpected, aNumExpected);
1.1275 + TESTPRINT(_L("PreviousPosition"));
1.1276 + TestEachLine(TestLayoutFindEdgesBackwards,
1.1277 + aLayout, aExpected, aNumExpected);
1.1278 + }
1.1279 +
1.1280 +/**
1.1281 +Tests TTmGraphemeIterator and supporting functionality for each piece of text.
1.1282 +*/
1.1283 +TVerdict CTGraphemeIteratorStep::doTestStepL()
1.1284 + {
1.1285 + SetTestStepResult(EPass);
1.1286 + TestStep = this;
1.1287 + TESTPRINT(_L("RTmGraphemeEdgeIterator unit"));
1.1288 +
1.1289 + TESTPRINT(_L(" @SYMTestCaseID:SYSLIB-FORM-LEGACY-GRAPHEMEITERATOR-0001 DocPosMatches "));
1.1290 + TestDocPosMatches();
1.1291 + TESTPRINT(_L("Simple Latin"));
1.1292 + CTestTmTextLayout* latin1 = CTestTmTextLayout::NewLC(
1.1293 + KLatin1, 100, Transliterate);
1.1294 + TestLayoutL( *latin1, KLatin1Edges,
1.1295 + sizeof(KLatin1Edges)/sizeof(KLatin1Edges[0]));
1.1296 + CleanupStack::PopAndDestroy(latin1);
1.1297 +
1.1298 + TESTPRINT(_L("Simple Arabic"));
1.1299 + CTestTmTextLayout* arabic1 = CTestTmTextLayout::NewLC(
1.1300 + KArabic1, 100, Transliterate);
1.1301 + TestLayoutL(*arabic1, KArabic1Edges,
1.1302 + sizeof(KArabic1Edges)/sizeof(KArabic1Edges[0]));
1.1303 + CleanupStack::PopAndDestroy(arabic1);
1.1304 +
1.1305 + TESTPRINT(_L("Latin with combining marks and zero width characters"));
1.1306 + CTestTmTextLayout* combiners1 = CTestTmTextLayout::NewLC(
1.1307 + KCombiners1, 20, Transliterate);
1.1308 + TestLayoutL(*combiners1, KCombiners1Edges,
1.1309 + sizeof(KCombiners1Edges)/sizeof(KCombiners1Edges[0]));
1.1310 + CleanupStack::PopAndDestroy(combiners1);
1.1311 +
1.1312 + TESTPRINT(_L("Bidirectional text with combining marks"));
1.1313 + CTestTmTextLayout* bidi1 = CTestTmTextLayout::NewLC(
1.1314 + KBidi1, 60, Transliterate);
1.1315 + TestLayoutL( *bidi1, KBidi1Edges,
1.1316 + sizeof(KBidi1Edges)/sizeof(KBidi1Edges[0]));
1.1317 + CleanupStack::PopAndDestroy(bidi1);
1.1318 +
1.1319 + TESTPRINT(_L("Bidirectional text with combining marks and 'amtriguity'"));
1.1320 + CTestTmTextLayout* bidi2 = CTestTmTextLayout::NewLC(
1.1321 + KBidi2, 60, Transliterate);
1.1322 + TestLayoutL(*bidi2, KBidi2Edges,
1.1323 + sizeof(KBidi2Edges)/sizeof(KBidi2Edges[0]));
1.1324 + CleanupStack::PopAndDestroy(bidi2);
1.1325 +
1.1326 + TESTPRINT(_L("Small paragraphs of alternating directionality"));
1.1327 + CTestTmTextLayout* paragraphs1 = CTestTmTextLayout::NewLC(
1.1328 + KParagraphs1, 20, Transliterate);
1.1329 + TestLayoutL(*paragraphs1, KParagraphs1Edges,
1.1330 + sizeof(KParagraphs1Edges)/sizeof(KParagraphs1Edges[0]));
1.1331 + CleanupStack::PopAndDestroy(paragraphs1);
1.1332 +
1.1333 + TESTPRINT(_L("Lines ending over or next to embedded runs"));
1.1334 + CTestTmTextLayout* embedded1 = CTestTmTextLayout::NewLC(
1.1335 + KEmbedded1, 20, Transliterate);
1.1336 + TestLayoutL( *embedded1, KEmbedded1Edges,
1.1337 + sizeof(KEmbedded1Edges)/sizeof(KEmbedded1Edges[0]));
1.1338 + CleanupStack::PopAndDestroy(embedded1);
1.1339 +
1.1340 + return TestStepResult();
1.1341 + }
1.1342 +