1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // Description: An implementation of the SalLayout interface that uses the
29 //              Graphite engine.
30 
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_vcl.hxx"
33 
34 // We need this to enable namespace support in libgrengine headers.
35 #define GR_NAMESPACE
36 
37 // Enable lots of debug info
38 #ifdef DEBUG
39 //#define GRLAYOUT_DEBUG 1
40 //#undef NDEBUG
41 #endif
42 
43 // Header files
44 //
45 // Standard Library
46 #include <algorithm>
47 #include <cassert>
48 #include <functional>
49 #include <limits>
50 #include <numeric>
51 #include <deque>
52 
53 // Platform
54 #ifdef WNT
55 #include <tools/svwin.h>
56 #include <svsys.h>
57 #endif
58 
59 #ifdef UNX
60 #include <graphite_adaptors.hxx>
61 #endif
62 
63 #include <salgdi.hxx>
64 
65 #include <unicode/uchar.h>
66 #include <unicode/ubidi.h>
67 #include <unicode/uscript.h>
68 
69 // Graphite Libraries (must be after vcl headers on windows)
70 #include <preextstl.h>
71 #include <graphite/GrClient.h>
72 #include <graphite/Font.h>
73 #include <graphite/ITextSource.h>
74 #include <graphite/Segment.h>
75 #include <graphite/SegmentPainter.h>
76 #include <postextstl.h>
77 
78 #include <graphite_layout.hxx>
79 #include <graphite_features.hxx>
80 #include "graphite_textsrc.hxx"
81 
82 
83 // Module private type definitions and forward declarations.
84 //
85 // Module private names.
86 //
87 
88 #ifdef GRLAYOUT_DEBUG
89 FILE * grLogFile = NULL;
90 FILE * grLog()
91 {
92 #ifdef WNT
93     std::string logFileName(getenv("TEMP"));
94     logFileName.append("\\graphitelayout.log");
95     if (grLogFile == NULL) grLogFile = fopen(logFileName.c_str(),"w");
96     else fflush(grLogFile);
97     return grLogFile;
98 #else
99     return stdout;
100 #endif
101 }
102 #endif
103 
104 #ifdef GRCACHE
105 #include <graphite_cache.hxx>
106 #endif
107 
108 
109 namespace
110 {
111     typedef ext_std::pair<gr::GlyphIterator, gr::GlyphIterator>       glyph_range_t;
112     typedef ext_std::pair<gr::GlyphSetIterator, gr::GlyphSetIterator> glyph_set_range_t;
113 
114     inline long round(const float n) {
115         return long(n + (n < 0 ? -0.5 : 0.5));
116     }
117 
118 
119     template<typename T>
120     inline bool in_range(const T i, const T b, const T e) {
121         return !(b > i) && i < e;
122     }
123 
124 
125     template<typename T>
126     inline bool is_subrange(const T sb, const T se, const T b, const T e) {
127         return !(b > sb || se > e);
128     }
129 
130 
131     template<typename T>
132     inline bool is_subrange(const std::pair<T, T> &s, const T b, const T e) {
133         return is_subrange(s.first, s.second, b, e);
134     }
135 
136     int findSameDirLimit(const xub_Unicode* buffer, int charCount, bool rtl)
137     {
138         UErrorCode status = U_ZERO_ERROR;
139         UBiDi *ubidi = ubidi_openSized(charCount, 0, &status);
140         int limit = 0;
141         ubidi_setPara(ubidi, reinterpret_cast<const UChar *>(buffer), charCount,
142             (rtl)?UBIDI_DEFAULT_RTL:UBIDI_DEFAULT_LTR, NULL, &status);
143         UBiDiLevel level = 0;
144         ubidi_getLogicalRun(ubidi, 0, &limit, &level);
145         ubidi_close(ubidi);
146         if ((rtl && !(level & 1)) || (!rtl && (level & 1)))
147         {
148             limit = 0;
149         }
150         return limit;
151     }
152 
153 } // namespace
154 
155 
156 
157 // Impementation of the GraphiteLayout::Glyphs container class.
158 //    This is an extended vector class with methods added to enable
159 //        o Correctly filling with glyphs.
160 //        o Querying clustering relationships.
161 //        o manipulations that affect neighouring glyphs.
162 
163 const int GraphiteLayout::EXTRA_CONTEXT_LENGTH = 10;
164 #ifdef GRCACHE
165 GraphiteCacheHandler GraphiteCacheHandler::instance;
166 #endif
167 
168 // The Graphite glyph stream is really a sequence of glyph attachment trees
169 //  each rooted at a non-attached base glyph.  fill_from walks the glyph stream
170 //  find each non-attached base glyph and calls append to record them as a
171 //  sequence of clusters.
172 void
173 GraphiteLayout::Glyphs::fill_from(gr::Segment & rSegment, ImplLayoutArgs &rArgs,
174     bool bRtl, long &rWidth, float fScaling, std::vector<int> & rChar2Base, std::vector<int> & rGlyph2Char, std::vector<int> & rCharDxs)
175 {
176     // Create a glyph item for each of the glyph and append it to the base class glyph list.
177     typedef ext_std::pair< gr::GlyphSetIterator, gr::GlyphSetIterator > GrGlyphSet;
178     int nChar = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
179     glyph_range_t iGlyphs = rSegment.glyphs();
180     int nGlyphs = iGlyphs.second - iGlyphs.first;
181     gr::GlyphIterator prevBase = iGlyphs.second;
182     float fSegmentAdvance = rSegment.advanceWidth();
183     float fMinX = fSegmentAdvance;
184     float fMaxX = 0.0f;
185     rGlyph2Char.assign(nGlyphs, -1);
186     long nDxOffset = 0;
187     int nGlyphIndex = (bRtl)? (nGlyphs - 1) : 0;
188     // OOo always expects the glyphs in ltr order
189     int nDelta = (bRtl)? -1 : 1;
190 
191     int nLastGlyph = (bRtl)? nGlyphs - 1: 0;
192     int nNextChar = (bRtl)? (rSegment.stopCharacter() - 1) : rSegment.startCharacter();//rArgs.mnMinCharPos;
193     // current glyph number (Graphite glyphs)
194     //int currGlyph = 0;
195     int nFirstCharInCluster = nNextChar;
196     int nFirstGlyphInCluster = nLastGlyph;
197 
198     // ltr first char in cluster is lowest, same is true for rtl
199     // ltr first glyph in cluster is lowest, rtl first glyph is highest
200 
201     // loop over the glyphs determining which characters are linked to them
202     gr::GlyphIterator gi;
203     for (gi = iGlyphs.first + nGlyphIndex;
204          nGlyphIndex >= 0 && nGlyphIndex < nGlyphs;
205          nGlyphIndex+= nDelta, gi = iGlyphs.first + nGlyphIndex)
206     {
207         gr::GlyphInfo info = (*gi);
208 #ifdef GRLAYOUT_DEBUG
209         fprintf(grLog(),"Glyph %d %f,%f\n", (int)info.logicalIndex(), info.origin(), info.yOffset());
210 #endif
211         // the last character associated with this glyph is after
212         // our current cluster buffer position
213         if ((bRtl && ((signed)info.firstChar() <= nNextChar)) ||
214             (!bRtl && ((signed)info.lastChar() >= nNextChar)))
215         {
216             if ((bRtl && nGlyphIndex < nLastGlyph) ||
217                 (!bRtl && nGlyphIndex > nLastGlyph))
218             {
219                 // this glyph is after the previous one left->right
220                 // if insertion is allowed before it then we are in a
221                 // new cluster
222                 int nAttachedBase = (*(info.attachedClusterBase())).logicalIndex();
223                 if (!info.isAttached() ||
224                     !in_range(nAttachedBase, nFirstGlyphInCluster, nGlyphIndex))
225                 {
226                     if (in_range(nFirstCharInCluster, rArgs.mnMinCharPos, rArgs.mnEndCharPos) &&
227                         nFirstGlyphInCluster != nGlyphIndex)
228                     {
229                         std::pair <float,float> aBounds =
230                             appendCluster(rSegment, rArgs, bRtl,
231                             fSegmentAdvance, nFirstCharInCluster,
232                             nNextChar, nFirstGlyphInCluster, nGlyphIndex, fScaling,
233                             rChar2Base, rGlyph2Char, rCharDxs, nDxOffset);
234                         fMinX = std::min(aBounds.first, fMinX);
235                         fMaxX = std::max(aBounds.second, fMaxX);
236                     }
237                     nFirstCharInCluster = (bRtl)? info.lastChar() : info.firstChar();
238                     nFirstGlyphInCluster = nGlyphIndex;
239                 }
240                 nLastGlyph = (bRtl)? std::min(nGlyphIndex, nAttachedBase) :
241                     std::max(nGlyphIndex, nAttachedBase);
242             }
243             // loop over chacters associated with this glyph and characters
244             // between nextChar and the last character associated with this glyph
245             // giving them the current cluster id.  This allows for character /glyph
246             // order reversal.
247             // For each character we do a reverse glyph id look up
248             // and store the glyph id with the highest logical index in nLastGlyph
249             while ((bRtl && ((signed)info.firstChar() <= nNextChar)) ||
250                    (!bRtl && (signed)info.lastChar() >= nNextChar))
251             {
252                 GrGlyphSet charGlyphs = rSegment.charToGlyphs(nNextChar);
253                 nNextChar += nDelta;
254                 gr::GlyphSetIterator gj = charGlyphs.first;
255                 while (gj != charGlyphs.second)
256                 {
257                     nLastGlyph = (bRtl)? min(nLastGlyph, (signed)(*gj).logicalIndex()) : max(nLastGlyph, (signed)(*gj).logicalIndex());
258                     ++gj;
259                 }
260             }
261             // Loop over attached glyphs and make sure they are all in the cluster since you
262             // can have glyphs attached with another base glyph in between
263             glyph_set_range_t iAttached = info.attachedClusterGlyphs();
264             for (gr::GlyphSetIterator agi = iAttached.first; agi != iAttached.second; ++agi)
265             {
266                 nLastGlyph = (bRtl)? min(nLastGlyph, (signed)(*agi).logicalIndex()) : max(nLastGlyph, (signed)(*agi).logicalIndex());
267             }
268 
269             // if this is a rtl attached glyph, then we need to include its
270             // base in the cluster, which will have a lower graphite index
271             if (bRtl)
272             {
273                 if ((signed)info.attachedClusterBase()->logicalIndex() < nLastGlyph)
274                 {
275                     nLastGlyph = info.attachedClusterBase()->logicalIndex();
276                 }
277             }
278         }
279 
280         // it is possible for the lastChar to be after nextChar and
281         // firstChar to be before the nFirstCharInCluster in rare
282         // circumstances e.g. Myanmar word for cemetery
283         if ((bRtl && ((signed)info.lastChar() > nFirstCharInCluster)) ||
284             (!bRtl && ((signed)info.firstChar() < nFirstCharInCluster)))
285         {
286             nFirstCharInCluster = info.firstChar();
287         }
288     }
289     // process last cluster
290     if (in_range(nFirstCharInCluster, rArgs.mnMinCharPos, rArgs.mnEndCharPos) &&
291         nFirstGlyphInCluster != nGlyphIndex)
292     {
293         std::pair <float,float> aBounds =
294             appendCluster(rSegment, rArgs, bRtl, fSegmentAdvance,
295                           nFirstCharInCluster, nNextChar,
296                           nFirstGlyphInCluster, nGlyphIndex, fScaling,
297                           rChar2Base, rGlyph2Char, rCharDxs, nDxOffset);
298         fMinX = std::min(aBounds.first, fMinX);
299         fMaxX = std::max(aBounds.second, fMaxX);
300     }
301     long nXOffset = round(fMinX * fScaling);
302     rWidth = round(fMaxX * fScaling) - nXOffset + nDxOffset;
303     if (rWidth < 0)
304     {
305         // This can happen when there was no base inside the range
306         rWidth = 0;
307     }
308     // fill up non-base char dx with cluster widths from previous base glyph
309     if (bRtl)
310     {
311         if (rCharDxs[nChar-1] == -1)
312             rCharDxs[nChar-1] = 0;
313         else
314             rCharDxs[nChar-1] -= nXOffset;
315         for (int i = nChar - 2; i >= 0; i--)
316         {
317             if (rCharDxs[i] == -1) rCharDxs[i] = rCharDxs[i+1];
318             else rCharDxs[i] -= nXOffset;
319         }
320     }
321     else
322     {
323         if (rCharDxs[0] == -1)
324             rCharDxs[0] = 0;
325         else
326             rCharDxs[0] -= nXOffset;
327         for (int i = 1; i < nChar; i++)
328         {
329             if (rCharDxs[i] == -1) rCharDxs[i] = rCharDxs[i-1];
330             else rCharDxs[i] -= nXOffset;
331         }
332     }
333 #ifdef GRLAYOUT_DEBUG
334     fprintf(grLog(),"Glyphs xOff%ld dropDx%ld w%ld\n", nXOffset, nDxOffset, rWidth);
335 #endif
336     // remove offset due to context if there is one
337     if (nXOffset != 0)
338     {
339         for (size_t i = 0; i < size(); i++)
340             (*this)[i].maLinearPos.X() -= nXOffset;
341     }
342 }
343 
344 std::pair<float,float> GraphiteLayout::Glyphs::appendCluster(gr::Segment& rSeg,
345     ImplLayoutArgs & rArgs, bool bRtl,float fSegmentAdvance,
346     int nFirstCharInCluster, int nNextChar, int nFirstGlyphInCluster,
347     int nNextGlyph, float fScaling, std::vector<int> & rChar2Base,
348     std::vector<int> & rGlyph2Char, std::vector<int> & rCharDxs, long & rDXOffset)
349 {
350     glyph_range_t iGlyphs = rSeg.glyphs();
351     int nGlyphs = iGlyphs.second - iGlyphs.first;
352     int nDelta = (bRtl)? -1 : 1;
353     gr::GlyphInfo aFirstGlyph = *(iGlyphs.first + nFirstGlyphInCluster);
354     std::pair <float, float> aBounds;
355     aBounds.first = aFirstGlyph.origin();
356     aBounds.second = aFirstGlyph.origin();
357     // before we add the glyphs to this vector, we record the
358     // glyph's index in the vector (which is not the same as
359     // the Segment's glyph index!)
360     assert(size() < rGlyph2Char.size());
361     rChar2Base[nFirstCharInCluster-rArgs.mnMinCharPos] = size();
362     rGlyph2Char[size()] = nFirstCharInCluster;
363 
364     // can we break before this cluster?
365     // Glyphs may have either a positive or negative breakWeight refering to
366     // the position after or before the glyph respectively
367     int nPrevBreakWeight = 0;
368     if (nFirstGlyphInCluster > 0)
369     {
370         nPrevBreakWeight = (iGlyphs.first + (nFirstGlyphInCluster - 1))->breakweight();
371     }
372     int nBreakWeight = aFirstGlyph.breakweight();
373     if (nBreakWeight < 0)
374     {
375         // negative means it applies to the position before the glyph's character
376         nBreakWeight *= -1;
377         if (nPrevBreakWeight > 0 && nPrevBreakWeight < nBreakWeight)
378         {
379             // prevBreakWeight wins
380             nBreakWeight = nPrevBreakWeight;
381         }
382     }
383     else
384     {
385         nBreakWeight = 0;
386         // positive means break after
387         if (nPrevBreakWeight > 0)
388             nBreakWeight = nPrevBreakWeight;
389     }
390     if (nBreakWeight > gr::klbNoBreak/*0*/ &&
391         // nBreakWeight <= gr::klbHyphenBreak) // uses Graphite hyphenation
392         nBreakWeight <= gr::klbLetterBreak) // Needed for issue 111272
393     {
394         if (nBreakWeight < gr::klbHyphenBreak)
395             rChar2Base[nFirstCharInCluster-rArgs.mnMinCharPos] |= WORD_BREAK_BEFORE;
396         else
397             rChar2Base[nFirstCharInCluster-rArgs.mnMinCharPos] |= HYPHEN_BREAK_BEFORE;
398     }
399     // always allow a break before a space even if graphite doesn't
400     if (rArgs.mpStr[nFirstCharInCluster] == 0x20)
401         rChar2Base[nFirstCharInCluster-rArgs.mnMinCharPos] |= WORD_BREAK_BEFORE;
402 
403     bool bBaseGlyph = true;
404     for (int j = nFirstGlyphInCluster;
405         j != nNextGlyph; j += nDelta)
406     {
407         long nNextOrigin;
408         float fNextOrigin;
409         gr::GlyphInfo aGlyph = *(iGlyphs.first + j);
410         if (j + nDelta >= nGlyphs || j + nDelta < 0) // at rhs ltr,rtl
411         {
412             fNextOrigin = fSegmentAdvance;
413             nNextOrigin = round(fSegmentAdvance * fScaling + rDXOffset);
414             aBounds.second = std::max(fSegmentAdvance, aBounds.second);
415         }
416         else
417         {
418             gr::GlyphInfo aNextGlyph = *(iGlyphs.first + j + nDelta);
419             fNextOrigin = std::max(aNextGlyph.attachedClusterBase()->origin(), aNextGlyph.origin());
420             aBounds.second = std::max(fNextOrigin, aBounds.second);
421             nNextOrigin = round(fNextOrigin * fScaling + rDXOffset);
422         }
423         aBounds.first = std::min(aGlyph.origin(), aBounds.first);
424         if ((signed)aGlyph.firstChar() < rArgs.mnEndCharPos &&
425             (signed)aGlyph.firstChar() >= rArgs.mnMinCharPos)
426         {
427             rCharDxs[aGlyph.firstChar()-rArgs.mnMinCharPos] = nNextOrigin;
428         }
429         if ((signed)aGlyph.attachedClusterBase()->logicalIndex() == j)
430         {
431             append(rSeg, rArgs, aGlyph, fNextOrigin, fScaling, rChar2Base, rGlyph2Char, rCharDxs, rDXOffset, bBaseGlyph);
432             bBaseGlyph = false;
433         }
434     }
435     // from the point of view of the dx array, the xpos is
436     // the origin of the first glyph of the next cluster ltr
437     // rtl it is the origin of the 1st glyph of the cluster
438     long nXPos = (bRtl)?
439         round(aFirstGlyph.attachedClusterBase()->origin() * fScaling) + rDXOffset :
440         round(aBounds.second * fScaling) + rDXOffset;
441     // force the last char in range to have the width of the cluster
442     if (bRtl)
443     {
444         for (int n = nNextChar + 1; n <= nFirstCharInCluster; n++)
445         {
446             if ((n < rArgs.mnEndCharPos) && (n >= rArgs.mnMinCharPos))
447                 rCharDxs[n-rArgs.mnMinCharPos] = nXPos;
448         }
449     }
450     else
451     {
452         for (int n = nNextChar - 1; n >= nFirstCharInCluster; n--)
453         {
454             if (n < rArgs.mnEndCharPos && n >= rArgs.mnMinCharPos)
455                 rCharDxs[n-rArgs.mnMinCharPos] = nXPos;
456         }
457     }
458 #ifdef GRLAYOUT_DEBUG
459     fprintf(grLog(),"Cluster g[%d-%d) c[%d-%d)%x x%ld y%f bw%d\n", nFirstGlyphInCluster, nNextGlyph, nFirstCharInCluster, nNextChar, rArgs.mpStr[nFirstCharInCluster], nXPos, aFirstGlyph.yOffset(), nBreakWeight);
460 #endif
461     return aBounds;
462 }
463 
464 // append walks an attachment tree, flattening it, and converting it into a
465 // sequence of GlyphItem objects which we can later manipulate.
466 void
467 GraphiteLayout::Glyphs::append(gr::Segment &segment, ImplLayoutArgs &args, gr::GlyphInfo & gi, float nextGlyphOrigin, float scaling, std::vector<int> & rChar2Base, std::vector<int> & rGlyph2Char, std::vector<int> & rCharDxs, long & rDXOffset, bool bIsBase)
468 {
469     float nextOrigin = nextGlyphOrigin;
470     int firstChar = std::min(gi.firstChar(), gi.lastChar());
471     assert(size() < rGlyph2Char.size());
472     if (!bIsBase) rGlyph2Char[size()] = firstChar;
473     // is the next glyph attached or in the next cluster?
474     glyph_set_range_t iAttached = gi.attachedClusterGlyphs();
475     if (iAttached.first != iAttached.second)
476     {
477         nextOrigin = iAttached.first->origin();
478     }
479     long glyphId = gi.glyphID();
480     long deltaOffset = 0;
481     int glyphWidth = round(nextOrigin * scaling) - round(gi.origin() * scaling);
482 #ifdef GRLAYOUT_DEBUG
483     fprintf(grLog(),"c%d g%d gWidth%d x%f ", firstChar, (int)gi.logicalIndex(), glyphWidth, nextOrigin);
484 #endif
485     if (glyphId == 0)
486     {
487         args.NeedFallback(
488             firstChar,
489             gr::RightToLeftDir(gr::DirCode(gi.directionality())));
490         if( (SAL_LAYOUT_FOR_FALLBACK & args.mnFlags ))
491         {
492             glyphId = GF_DROPPED;
493             deltaOffset -= glyphWidth;
494             glyphWidth = 0;
495         }
496     }
497     else if(args.mnFlags & SAL_LAYOUT_FOR_FALLBACK)
498     {
499 #ifdef GRLAYOUT_DEBUG
500         fprintf(grLog(),"fallback c%d %x in run %d\n", firstChar, args.mpStr[firstChar],
501             args.maRuns.PosIsInAnyRun(firstChar));
502 #endif
503         // glyphs that aren't requested for fallback will be taken from base
504         // layout, so mark them as dropped (should this wait until Simplify(false) is called?)
505         if (!args.maRuns.PosIsInAnyRun(firstChar) &&
506             in_range(firstChar, args.mnMinCharPos, args.mnEndCharPos))
507         {
508             glyphId = GF_DROPPED;
509             deltaOffset -= glyphWidth;
510             glyphWidth = 0;
511         }
512     }
513     // append this glyph.
514     long nGlyphFlags = bIsBase ? 0 : GlyphItem::IS_IN_CLUSTER;
515     // directionality seems to be unreliable
516     //nGlyphFlags |= gr::RightToLeftDir(gr::DirCode(gi.attachedClusterBase()->directionality())) ? GlyphItem::IS_RTL_GLYPH : 0;
517     nGlyphFlags |= (gi.directionLevel() & 0x1)? GlyphItem::IS_RTL_GLYPH : 0;
518     GlyphItem aGlyphItem(size(),//gi.logicalIndex(),
519         glyphId,
520         Point(round(gi.origin() * scaling + rDXOffset),
521             round((-gi.yOffset() * scaling) - segment.AscentOffset()* scaling)),
522         nGlyphFlags,
523         glyphWidth);
524     aGlyphItem.mnOrigWidth = round(gi.advanceWidth() * scaling);
525     push_back(aGlyphItem);
526 
527     // update the offset if this glyph was dropped
528     rDXOffset += deltaOffset;
529 
530     // Recursively apply append all the attached glyphs.
531     for (gr::GlyphSetIterator agi = iAttached.first; agi != iAttached.second; ++agi)
532     {
533         if (agi + 1 == iAttached.second)
534             append(segment, args, *agi, nextGlyphOrigin, scaling, rChar2Base, rGlyph2Char,rCharDxs, rDXOffset, false);
535         else
536             append(segment, args, *agi, (agi + 1)->origin(), scaling, rChar2Base, rGlyph2Char, rCharDxs, rDXOffset, false);
537     }
538 }
539 
540 //
541 // An implementation of the SalLayout interface to enable Graphite enabled fonts to be used.
542 //
543 GraphiteLayout::GraphiteLayout(const gr::Font & font, const grutils::GrFeatureParser * pFeatures) throw()
544   : mpTextSrc(0),
545     mrFont(font),
546     mnWidth(0),
547     mfScaling(1.0),
548     mpFeatures(pFeatures)
549 {
550     // Line settings can have subtle affects on space handling
551     // since we don't really know whether it is the end of a line or just a run
552     // in the middle, it is hard to know what to set them to.
553     // If true, it can cause end of line spaces to be hidden e.g. Doulos SIL
554     maLayout.setStartOfLine(false);
555     maLayout.setEndOfLine(false);
556     maLayout.setDumbFallback(true);
557     // trailing ws doesn't seem to always take affect if end of line is true
558     maLayout.setTrailingWs(gr::ktwshAll);
559 #ifdef GRLAYOUT_DEBUG
560     gr::ScriptDirCode aDirCode = font.getSupportedScriptDirections();
561     fprintf(grLog(),"GraphiteLayout scripts %x %lx\n", aDirCode, long(this));
562 #endif
563 }
564 
565 
566 GraphiteLayout::~GraphiteLayout() throw()
567 {
568     clear();
569     // the features are owned by the platform layers
570     mpFeatures = NULL;
571 }
572 
573 void GraphiteLayout::clear()
574 {
575     // Destroy the segment and text source from any previous invocation of
576     // LayoutText
577     mvGlyphs.clear();
578     mvCharDxs.clear();
579     mvChar2BaseGlyph.clear();
580     mvGlyph2Char.clear();
581 
582 #ifndef GRCACHE
583     delete mpTextSrc;
584 #endif
585 
586     // Reset the state to the empty state.
587     mpTextSrc=0;
588     mnWidth = 0;
589     // Don't reset the scaling, because it is set before LayoutText
590 }
591 
592 // This method shouldn't be called on windows, since it needs the dc reset
593 bool GraphiteLayout::LayoutText(ImplLayoutArgs & rArgs)
594 {
595 #ifdef GRCACHE
596     GrSegRecord * pSegRecord = NULL;
597     gr::Segment * pSegment = NULL;
598     // Graphite can in rare cases crash with a zero length
599     if (rArgs.mnMinCharPos < rArgs.mnEndCharPos)
600     {
601         pSegment = CreateSegment(rArgs, &pSegRecord);
602         if (!pSegment)
603             return false;
604     }
605     else
606     {
607         clear();
608         return true;
609     }
610     // layout the glyphs as required by OpenOffice
611     bool success = LayoutGlyphs(rArgs, pSegment, pSegRecord);
612 
613     if (pSegRecord) pSegRecord->unlock();
614     else delete pSegment;
615 #else
616     gr::Segment * pSegment = NULL;
617     bool success = true;
618     if (rArgs.mnMinCharPos < rArgs.mnEndCharPos)
619     {
620         pSegment = CreateSegment(rArgs);
621         if (!pSegment)
622             return false;
623         success = LayoutGlyphs(rArgs, pSegment);
624         if (pSegment) delete pSegment;
625     }
626     else
627     {
628         clear();
629     }
630 #endif
631     return success;
632 }
633 
634 #ifdef GRCACHE
635 class GrFontHasher : public gr::Font
636 {
637 public:
638     GrFontHasher(const gr::Font & aFont) : gr::Font(aFont), mrRealFont(const_cast<gr::Font&>(aFont)) {};
639     ~GrFontHasher(){};
640     virtual bool bold() { return mrRealFont.bold(); };
641     virtual bool italic() { return mrRealFont.italic(); };
642     virtual float ascent()  { return mrRealFont.ascent(); };
643     virtual float descent()  { return mrRealFont.descent(); };
644     virtual float height()  { return mrRealFont.height(); };
645     virtual gr::Font* copyThis() { return mrRealFont.copyThis(); };
646     virtual unsigned int getDPIx() { return mrRealFont.getDPIx(); };
647     virtual unsigned int getDPIy() { return mrRealFont.getDPIy(); };
648     virtual const void* getTable(gr::fontTableId32 nId, size_t* nSize)
649     { return mrRealFont.getTable(nId,nSize); }
650     virtual void getFontMetrics(float*pA, float*pB, float*pC) { mrRealFont.getFontMetrics(pA,pB,pC); };
651 
652     sal_Int32 hashCode(const grutils::GrFeatureParser * mpFeatures)
653     {
654         // is this sufficient?
655         ext_std::wstring aFace;
656         bool bBold;
657         bool bItalic;
658         UniqueCacheInfo(aFace, bBold, bItalic);
659         sal_Unicode uName[32]; // max length used in gr::Font
660         // Note: graphite stores font names as UTF-16 even if wchar_t is 32bit
661         // this conversion should be OK.
662         for (size_t i = 0; i < aFace.size() && i < 32; i++)
663         {
664             uName[i] = aFace[i];
665         }
666         size_t iSize = aFace.size();
667         if (0 == iSize) return 0;
668         sal_Int32 hash = rtl_ustr_hashCode_WithLength(uName, iSize);
669         hash ^= static_cast<sal_Int32>(height());
670         hash |= (bBold)? 0x1000000 : 0;
671         hash |= (bItalic)? 0x2000000 : 0;
672         if (mpFeatures)
673             hash ^= mpFeatures->hashCode();
674 #ifdef GRLAYOUT_DEBUG
675         fprintf(grLog(), "font hash %x size %f\n", (int)hash, height());
676 #endif
677         return hash;
678     };
679 protected:
680     virtual void UniqueCacheInfo( ext_std::wstring& stuFace, bool& fBold, bool& fItalic )
681     {
682 #ifdef WIN32
683         dynamic_cast<GraphiteWinFont&>(mrRealFont).UniqueCacheInfo(stuFace, fBold, fItalic);
684 #else
685 #ifdef UNX
686         dynamic_cast<GraphiteFontAdaptor&>(mrRealFont).UniqueCacheInfo(stuFace, fBold, fItalic);
687 #else
688 #error Unknown base type for gr::Font::UniqueCacheInfo
689 #endif
690 #endif
691     }
692 private:
693     gr::Font & mrRealFont;
694 };
695 #endif
696 
697 #ifdef GRCACHE
698 gr::Segment * GraphiteLayout::CreateSegment(ImplLayoutArgs& rArgs, GrSegRecord ** pSegRecord)
699 #else
700 gr::Segment * GraphiteLayout::CreateSegment(ImplLayoutArgs& rArgs)
701 #endif
702 {
703     assert(rArgs.mnLength >= 0);
704 
705     gr::Segment * pSegment = NULL;
706 
707     // Set the SalLayouts values to be the inital ones.
708     SalLayout::AdjustLayout(rArgs);
709     // TODO check if this is needed
710     if (mnUnitsPerPixel > 1)
711         mfScaling = 1.0f / mnUnitsPerPixel;
712 
713     // Clear out any previous buffers
714     clear();
715     bool bRtl = mnLayoutFlags & SAL_LAYOUT_BIDI_RTL;
716     try
717     {
718         // Don't set RTL if font doesn't support it otherwise it forces rtl on
719         // everything
720         if (bRtl && (mrFont.getSupportedScriptDirections() & gr::kfsdcHorizRtl))
721             maLayout.setRightToLeft(bRtl);
722 
723         // Context is often needed beyond the specified end, however, we don't
724         // want it if there has been a direction change, since it is hard
725         // to tell between reordering within one direction and multi-directional
726         // text. Extra context, can also cause problems with ligatures stradling
727         // a hyphenation point, so disable if CTL is disabled.
728         const int  nSegCharLimit = min(rArgs.mnLength, mnEndCharPos + EXTRA_CONTEXT_LENGTH);
729         int limit = rArgs.mnEndCharPos;
730         if ((nSegCharLimit > limit) && !(SAL_LAYOUT_COMPLEX_DISABLED & rArgs.mnFlags))
731         {
732             limit += findSameDirLimit(rArgs.mpStr + rArgs.mnEndCharPos,
733                 nSegCharLimit - rArgs.mnEndCharPos, bRtl);
734         }
735 
736 #ifdef GRCACHE
737         GrFontHasher hasher(mrFont);
738         sal_Int32 aFontHash = hasher.hashCode(mpFeatures);
739         GraphiteSegmentCache * pCache =
740             (GraphiteCacheHandler::instance).getCache(aFontHash);
741         if (pCache)
742         {
743             *pSegRecord = pCache->getSegment(rArgs, bRtl, limit);
744             if (*pSegRecord)
745             {
746                 pSegment = (*pSegRecord)->getSegment();
747                 mpTextSrc = (*pSegRecord)->getTextSrc();
748                 maLayout.setRightToLeft((*pSegRecord)->isRtl());
749                 if (rArgs.mpStr != mpTextSrc->getLayoutArgs().mpStr ||
750                     rArgs.mnMinCharPos != mpTextSrc->getLayoutArgs().mnMinCharPos ||
751                     rArgs.mnEndCharPos != mpTextSrc->getLayoutArgs().mnEndCharPos ||
752                     (SAL_LAYOUT_FOR_FALLBACK & rArgs.mnFlags) )
753                 {
754                     (*pSegRecord)->clearVectors();
755                 }
756                 mpTextSrc->switchLayoutArgs(rArgs);
757                 if (limit > rArgs.mnMinCharPos && limit == rArgs.mnEndCharPos
758                     && pSegment->stopCharacter() != limit)
759                 {
760                     // check that the last character is not part of a ligature
761                     glyph_set_range_t aGlyphSet = pSegment->charToGlyphs(limit - 1);
762                     if (aGlyphSet.first == aGlyphSet.second)
763                     {
764                         // no glyphs associated with this glyph - occurs mid ligature
765                         pSegment = NULL;
766                         *pSegRecord = NULL;
767                     }
768                     else
769                     {
770                         while (aGlyphSet.first != aGlyphSet.second)
771                         {
772                             int lastChar = static_cast<int>((*aGlyphSet.first).lastChar());
773                             if (lastChar >= limit)
774                             {
775                                 pSegment = NULL;
776                                 *pSegRecord = NULL;
777                                 break;
778                             }
779                             aGlyphSet.first++;
780                         }
781                     }
782                 }
783                 if (pSegment)
784                     return pSegment;
785             }
786         }
787 #endif
788 
789         // Create a new TextSource object for the engine.
790         mpTextSrc = new TextSourceAdaptor(rArgs, limit);
791         if (mpFeatures) mpTextSrc->setFeatures(mpFeatures);
792 
793         pSegment = new gr::RangeSegment((gr::Font *)&mrFont, mpTextSrc, &maLayout, mnMinCharPos, limit);
794         if (pSegment != NULL)
795         {
796 #ifdef GRLAYOUT_DEBUG
797             fprintf(grLog(),"Gr::LayoutText %d-%d, context %d,len%d rtl%d/%d scaling %f\n", rArgs.mnMinCharPos,
798                rArgs.mnEndCharPos, limit, rArgs.mnLength, maLayout.rightToLeft(), pSegment->rightToLeft(), mfScaling);
799 #endif
800 #ifdef GRCACHE
801             // on a new segment rightToLeft should be correct
802             *pSegRecord = pCache->cacheSegment(mpTextSrc, pSegment, pSegment->rightToLeft());
803 #endif
804         }
805         else
806         {
807 #ifdef GRLAYOUT_DEBUG
808             fprintf(grLog(), "Gr::LayoutText failed: ");
809             for (int i = mnMinCharPos; i < limit; i++)
810             {
811                 fprintf(grLog(), "%04x ", rArgs.mpStr[i]);
812             }
813             fprintf(grLog(), "\n");
814 #endif
815             clear();
816             return NULL;
817         }
818     }
819     catch (...)
820     {
821         clear();  // destroy the text source and any partially built segments.
822         return NULL;
823     }
824     return pSegment;
825 }
826 
827 #ifdef GRCACHE
828 bool GraphiteLayout::LayoutGlyphs(ImplLayoutArgs& rArgs, gr::Segment * pSegment, GrSegRecord * pSegRecord)
829 #else
830 bool GraphiteLayout::LayoutGlyphs(ImplLayoutArgs& rArgs, gr::Segment * pSegment)
831 #endif
832 {
833 #ifdef GRCACHE
834 #ifdef GRCACHE_REUSE_VECTORS
835     // if we have an exact match, then we can reuse the glyph vectors from before
836     if (pSegRecord && (pSegRecord->glyphs().size() > 0) &&
837         (pSegRecord->fontScale() == mfScaling) &&
838         !(SAL_LAYOUT_FOR_FALLBACK & rArgs.mnFlags) )
839     {
840         mnWidth = pSegRecord->width();
841         mvGlyphs = pSegRecord->glyphs();
842         mvCharDxs = pSegRecord->charDxs();
843         mvChar2BaseGlyph = pSegRecord->char2BaseGlyph();
844         mvGlyph2Char = pSegRecord->glyph2Char();
845         return true;
846     }
847 #endif
848 #endif
849     // Calculate the initial character dxs.
850     mvCharDxs.assign(mnEndCharPos - mnMinCharPos, -1);
851     mvChar2BaseGlyph.assign(mnEndCharPos - mnMinCharPos, -1);
852     mnWidth = 0;
853     if (mvCharDxs.size() > 0)
854     {
855         // Discover all the clusters.
856         try
857         {
858             // Note: we use the layout rightToLeft() because in cached segments
859             // rightToLeft() may no longer be valid if the engine has been run
860             // ltr since the segment was created.
861 #ifdef GRCACHE
862             bool bRtl = pSegRecord? pSegRecord->isRtl() : pSegment->rightToLeft();
863 #else
864             bool bRtl = pSegment->rightToLeft();
865 #endif
866             mvGlyphs.fill_from(*pSegment, rArgs, bRtl,
867                 mnWidth, mfScaling, mvChar2BaseGlyph, mvGlyph2Char, mvCharDxs);
868 
869             if (bRtl)
870             {
871                 // not needed for adjacent differences, but for mouse clicks to char
872                 std::transform(mvCharDxs.begin(), mvCharDxs.end(), mvCharDxs.begin(),
873                     std::bind1st(std::minus<long>(), mnWidth));
874                 // fixup last dx to ensure it always equals the width
875                 mvCharDxs[mvCharDxs.size() - 1] = mnWidth;
876             }
877 #ifdef GRCACHE
878 #ifdef GRCACHE_REUSE_VECTORS
879             if (pSegRecord && rArgs.maReruns.IsEmpty() &&
880                 !(SAL_LAYOUT_FOR_FALLBACK & rArgs.mnFlags))
881             {
882                 pSegRecord->setGlyphVectors(mnWidth, mvGlyphs, mvCharDxs,
883                                             mvChar2BaseGlyph, mvGlyph2Char,
884                                             mfScaling);
885             }
886 #endif
887 #endif
888         }
889         catch (std::exception e)
890         {
891 #ifdef GRLAYOUT_DEBUG
892             fprintf(grLog(),"LayoutGlyphs failed %s\n", e.what());
893 #endif
894             return false;
895         }
896         catch (...)
897         {
898 #ifdef GRLAYOUT_DEBUG
899             fprintf(grLog(),"LayoutGlyphs failed with exception");
900 #endif
901             return false;
902         }
903     }
904     else
905     {
906         mnWidth = 0;
907     }
908     return true;
909 }
910 
911 int GraphiteLayout::GetTextBreak(long maxmnWidth, long char_extra, int factor) const
912 {
913 #ifdef GRLAYOUT_DEBUG
914     fprintf(grLog(),"Gr::GetTextBreak c[%d-%d) maxWidth %ld char extra %ld factor %d\n",
915         mnMinCharPos, mnEndCharPos, maxmnWidth, char_extra, factor);
916 #endif
917 
918     // return quickly if this segment is narrower than the target width
919     if (maxmnWidth > mnWidth * factor + char_extra * (mnEndCharPos - mnMinCharPos - 1))
920         return STRING_LEN;
921 
922     long nWidth = mvCharDxs[0] * factor;
923     int nLastBreak = -1;
924     for (size_t i = 1; i < mvCharDxs.size(); i++)
925     {
926         nWidth += char_extra;
927         if (nWidth > maxmnWidth) break;
928         if (mvChar2BaseGlyph[i] != -1)
929         {
930             if (mvChar2BaseGlyph[i] & (WORD_BREAK_BEFORE | HYPHEN_BREAK_BEFORE))
931                 nLastBreak = static_cast<int>(i);
932         }
933         nWidth += (mvCharDxs[i] - mvCharDxs[i-1]) * factor;
934     }
935     int nBreak = mnMinCharPos;
936     if (nLastBreak > -1)
937         nBreak += nLastBreak;
938 
939 #ifdef GRLAYOUT_DEBUG
940     fprintf(grLog(), "Gr::GetTextBreak break after %d\n", nBreak - mnMinCharPos);
941 #endif
942 
943     if (nBreak > mnEndCharPos) nBreak = STRING_LEN;
944     else if (nBreak < mnMinCharPos) nBreak = mnMinCharPos;
945     return nBreak;
946 }
947 
948 
949 long GraphiteLayout::FillDXArray( sal_Int32* pDXArray ) const
950 {
951     if (mnEndCharPos == mnMinCharPos)
952         // Then we must be zero width!
953         return 0;
954 
955     if (pDXArray)
956     {
957         for (size_t i = 0; i < mvCharDxs.size(); i++)
958         {
959             assert( (mvChar2BaseGlyph[i] == -1) ||
960                 ((signed)(mvChar2BaseGlyph[i] & GLYPH_INDEX_MASK) < (signed)mvGlyphs.size()));
961             if (mvChar2BaseGlyph[i] != -1 &&
962                 mvGlyphs[mvChar2BaseGlyph[i] & GLYPH_INDEX_MASK].mnGlyphIndex == GF_DROPPED)
963             {
964                 // when used in MultiSalLayout::GetTextBreak dropped glyphs
965                 // must have zero width
966                 pDXArray[i] = 0;
967             }
968             else
969             {
970                 pDXArray[i] = mvCharDxs[i];
971                 if (i > 0) pDXArray[i] -= mvCharDxs[i-1];
972             }
973 #ifdef GRLAYOUT_DEBUG
974             fprintf(grLog(),"%d,%d,%d ", (int)i, (int)mvCharDxs[i], pDXArray[i]);
975 #endif
976         }
977         //std::adjacent_difference(mvCharDxs.begin(), mvCharDxs.end(), pDXArray);
978         //for (size_t i = 0; i < mvCharDxs.size(); i++)
979         //    fprintf(grLog(),"%d,%d,%d ", (int)i, (int)mvCharDxs[i], pDXArray[i]);
980         //fprintf(grLog(),"FillDX %ld,%d\n", mnWidth, std::accumulate(pDXArray, pDXArray + mvCharDxs.size(), 0));
981     }
982 #ifdef GRLAYOUT_DEBUG
983     fprintf(grLog(),"FillDXArray %d-%d,%d=%ld\n", mnMinCharPos, mnEndCharPos, (int)mpTextSrc->getLength(), mnWidth);
984 #endif
985     return mnWidth;
986 }
987 
988 
989 void  GraphiteLayout::AdjustLayout(ImplLayoutArgs& rArgs)
990 {
991     SalLayout::AdjustLayout(rArgs);
992     if(rArgs.mpDXArray)
993     {
994         std::vector<int> vDeltaWidths(mvGlyphs.size(), 0);
995         ApplyDXArray(rArgs, vDeltaWidths);
996 
997         if( (mnLayoutFlags & SAL_LAYOUT_BIDI_RTL) &&
998            !(rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK) )
999         {
1000             // check if this is a kashida script
1001             bool bKashidaScript = false;
1002             for (int i = rArgs.mnMinCharPos; i < rArgs.mnEndCharPos; i++)
1003             {
1004                 UErrorCode aStatus = U_ZERO_ERROR;
1005                 UScriptCode scriptCode = uscript_getScript(rArgs.mpStr[i], &aStatus);
1006                 if (scriptCode == USCRIPT_ARABIC || scriptCode == USCRIPT_SYRIAC)
1007                 {
1008                     bKashidaScript = true;
1009                     break;
1010                 }
1011             }
1012             int nKashidaWidth = 0;
1013             int nKashidaIndex = getKashidaGlyph(nKashidaWidth);
1014             if( nKashidaIndex != 0 && bKashidaScript)
1015             {
1016                 kashidaJustify( vDeltaWidths, nKashidaIndex, nKashidaWidth );
1017             }
1018         }
1019     }
1020     else if (rArgs.mnLayoutWidth > 0)
1021     {
1022 #ifdef GRLAYOUT_DEBUG
1023         fprintf(grLog(), "AdjustLayout width %ld=>%ld\n", mnWidth, rArgs.mnLayoutWidth);
1024 #endif
1025         expandOrCondense(rArgs);
1026     }
1027 }
1028 
1029 void GraphiteLayout::expandOrCondense(ImplLayoutArgs &rArgs)
1030 {
1031     int nDeltaWidth = rArgs.mnLayoutWidth - mnWidth;
1032     if (nDeltaWidth > 0) // expand, just expand between clusters
1033     {
1034         int nClusterCount = 0;
1035         for (size_t j = 0; j < mvGlyphs.size(); j++)
1036         {
1037             if (mvGlyphs[j].IsClusterStart())
1038             {
1039                 ++nClusterCount;
1040             }
1041         }
1042         if (nClusterCount > 1)
1043         {
1044             float fExtraPerCluster = static_cast<float>(nDeltaWidth) / static_cast<float>(nClusterCount - 1);
1045             int nCluster = 0;
1046             int nOffset = 0;
1047             for (size_t i = 0; i < mvGlyphs.size(); i++)
1048             {
1049                 if (mvGlyphs[i].IsClusterStart())
1050                 {
1051                     nOffset = FRound( fExtraPerCluster * nCluster );
1052                     size_t nCharIndex = mvGlyph2Char[i];
1053                     mvCharDxs[nCharIndex] += nOffset;
1054                     // adjust char dxs for rest of characters in cluster
1055                     while (++nCharIndex < mvGlyph2Char.size())
1056                     {
1057                         int nChar2Base = (mvChar2BaseGlyph[nCharIndex] == -1)? -1 : (int)(mvChar2BaseGlyph[nCharIndex] & GLYPH_INDEX_MASK);
1058                         if (nChar2Base == -1 || nChar2Base == static_cast<int>(i))
1059                             mvCharDxs[nCharIndex] += nOffset;
1060                     }
1061                     ++nCluster;
1062                 }
1063                 mvGlyphs[i].maLinearPos.X() += nOffset;
1064             }
1065         }
1066     }
1067     else // condense - apply a factor to all glyph positions
1068     {
1069         if (mvGlyphs.size() == 0) return;
1070         Glyphs::iterator iLastGlyph = mvGlyphs.begin() + (mvGlyphs.size() - 1);
1071         // position last glyph using original width
1072         float fXFactor = static_cast<float>(rArgs.mnLayoutWidth - iLastGlyph->mnOrigWidth) / static_cast<float>(iLastGlyph->maLinearPos.X());
1073 #ifdef GRLAYOUT_DEBUG
1074         fprintf(grLog(), "Condense by factor %f\n", fXFactor);
1075 #endif
1076         iLastGlyph->maLinearPos.X() = rArgs.mnLayoutWidth - iLastGlyph->mnOrigWidth;
1077         Glyphs::iterator iGlyph = mvGlyphs.begin();
1078         while (iGlyph != iLastGlyph)
1079         {
1080             iGlyph->maLinearPos.X() = FRound( fXFactor * iGlyph->maLinearPos.X() );
1081             ++iGlyph;
1082         }
1083         for (size_t i = 0; i < mvCharDxs.size(); i++)
1084         {
1085             mvCharDxs[i] = FRound( fXFactor * mvCharDxs[i] );
1086         }
1087     }
1088     mnWidth = rArgs.mnLayoutWidth;
1089 }
1090 
1091 void GraphiteLayout::ApplyDXArray(ImplLayoutArgs &args, std::vector<int> & rDeltaWidth)
1092 {
1093     const size_t nChars = args.mnEndCharPos - args.mnMinCharPos;
1094     if (nChars == 0) return;
1095 
1096 #ifdef GRLAYOUT_DEBUG
1097     for (size_t iDx = 0; iDx < mvCharDxs.size(); iDx++)
1098          fprintf(grLog(),"%d,%d,%d ", (int)iDx, (int)mvCharDxs[iDx], args.mpDXArray[iDx]);
1099     fprintf(grLog(),"ApplyDx\n");
1100 #endif
1101     bool bRtl = mnLayoutFlags & SAL_LAYOUT_BIDI_RTL;
1102     int nXOffset = 0;
1103     if (bRtl)
1104     {
1105         nXOffset = args.mpDXArray[nChars - 1] - mvCharDxs[nChars - 1];
1106     }
1107     int nPrevClusterGlyph = (bRtl)? (signed)mvGlyphs.size() : -1;
1108     int nPrevClusterLastChar = -1;
1109     for (size_t i = 0; i < nChars; i++)
1110     {
1111         int nChar2Base = (mvChar2BaseGlyph[i] == -1)? -1 : (int)(mvChar2BaseGlyph[i] & GLYPH_INDEX_MASK);
1112         if ((nChar2Base > -1) && (nChar2Base != nPrevClusterGlyph))
1113         {
1114             assert((nChar2Base > -1) && (nChar2Base < (signed)mvGlyphs.size()));
1115             GlyphItem & gi = mvGlyphs[nChar2Base];
1116             if (!gi.IsClusterStart())
1117                 continue;
1118 
1119             // find last glyph of this cluster
1120             size_t j = i + 1;
1121             int nLastChar = i;
1122             int nLastGlyph = nChar2Base;
1123             for (; j < nChars; j++)
1124             {
1125                 int nChar2BaseJ = (mvChar2BaseGlyph[j] == -1)? -1 : (int)(mvChar2BaseGlyph[j] & GLYPH_INDEX_MASK);
1126                 assert((nChar2BaseJ >= -1) && (nChar2BaseJ < (signed)mvGlyphs.size()));
1127                 if (nChar2BaseJ != -1 && mvGlyphs[nChar2BaseJ].IsClusterStart())
1128                 {
1129                     nLastGlyph = nChar2BaseJ + ((bRtl)? +1 : -1);
1130                     nLastChar = j - 1;
1131                     break;
1132                 }
1133             }
1134             if (nLastGlyph < 0)
1135             {
1136                 nLastGlyph = nChar2Base;
1137             }
1138             // Its harder to find the last glyph rtl, since the first of
1139             // cluster is still on the left so we need to search towards
1140             // the previous cluster to the right
1141             if (bRtl)
1142             {
1143                 nLastGlyph = nChar2Base;
1144                 while (nLastGlyph + 1 < (signed)mvGlyphs.size() &&
1145                        !mvGlyphs[nLastGlyph+1].IsClusterStart())
1146                 {
1147                     ++nLastGlyph;
1148                 }
1149             }
1150             if (j == nChars)
1151             {
1152                 nLastChar = nChars - 1;
1153                 if (!bRtl) nLastGlyph = mvGlyphs.size() - 1;
1154             }
1155             assert((nLastChar > -1) && (nLastChar < (signed)nChars));
1156             long nNewClusterWidth = args.mpDXArray[nLastChar];
1157             long nOrigClusterWidth = mvCharDxs[nLastChar];
1158             long nDGlyphOrigin = 0;
1159             if (nPrevClusterLastChar > - 1)
1160             {
1161                 assert(nPrevClusterLastChar < (signed)nChars);
1162                 nNewClusterWidth -= args.mpDXArray[nPrevClusterLastChar];
1163                 nOrigClusterWidth -= mvCharDxs[nPrevClusterLastChar];
1164                 nDGlyphOrigin = args.mpDXArray[nPrevClusterLastChar] - mvCharDxs[nPrevClusterLastChar];
1165             }
1166             long nDWidth = nNewClusterWidth - nOrigClusterWidth;
1167 #ifdef GRLAYOUT_DEBUG
1168             fprintf(grLog(), "c%lu last glyph %d/%lu\n", i, nLastGlyph, mvGlyphs.size());
1169 #endif
1170             assert((nLastGlyph > -1) && (nLastGlyph < (signed)mvGlyphs.size()));
1171             mvGlyphs[nLastGlyph].mnNewWidth += nDWidth;
1172             if (gi.mnGlyphIndex != GF_DROPPED)
1173                 mvGlyphs[nLastGlyph].mnNewWidth += nDWidth;
1174             else
1175                 nDGlyphOrigin += nDWidth;
1176             // update glyph positions
1177             if (bRtl)
1178             {
1179                 for (int n = nChar2Base; n <= nLastGlyph; n++)
1180                 {
1181                     assert((n > - 1) && (n < (signed)mvGlyphs.size()));
1182                     mvGlyphs[n].maLinearPos.X() += -nDGlyphOrigin + nXOffset;
1183                 }
1184             }
1185             else
1186             {
1187                 for (int n = nChar2Base; n <= nLastGlyph; n++)
1188                 {
1189                     assert((n > - 1) && (n < (signed)mvGlyphs.size()));
1190                     mvGlyphs[n].maLinearPos.X() += nDGlyphOrigin + nXOffset;
1191                 }
1192             }
1193             rDeltaWidth[nChar2Base] = nDWidth;
1194 #ifdef GRLAYOUT_DEBUG
1195             fprintf(grLog(),"c%d g%d-%d dW%ld-%ld=%ld dX%ld x%ld\t", (int)i, nChar2Base, nLastGlyph, nNewClusterWidth, nOrigClusterWidth, nDWidth, nDGlyphOrigin, mvGlyphs[nChar2Base].maLinearPos.X());
1196 #endif
1197             nPrevClusterGlyph = nChar2Base;
1198             nPrevClusterLastChar = nLastChar;
1199             i = nLastChar;
1200         }
1201     }
1202     // Update the dx vector with the new values.
1203     std::copy(args.mpDXArray, args.mpDXArray + nChars,
1204       mvCharDxs.begin() + (args.mnMinCharPos - mnMinCharPos));
1205 #ifdef GRLAYOUT_DEBUG
1206     fprintf(grLog(),"ApplyDx %d(%ld)\n", args.mpDXArray[nChars - 1], mnWidth);
1207 #endif
1208     mnWidth = args.mpDXArray[nChars - 1];
1209 }
1210 
1211 void GraphiteLayout::kashidaJustify(std::vector<int>& rDeltaWidths, sal_GlyphId nKashidaIndex, int nKashidaWidth)
1212 {
1213     // skip if the kashida glyph in the font looks suspicious
1214     if( nKashidaWidth <= 0 )
1215         return;
1216 
1217     // calculate max number of needed kashidas
1218     Glyphs::iterator i = mvGlyphs.begin();
1219     int nKashidaCount = 0;
1220     int nOrigGlyphIndex = -1;
1221     int nGlyphIndex = -1;
1222     while (i != mvGlyphs.end())
1223     {
1224         nOrigGlyphIndex++;
1225         nGlyphIndex++;
1226         // only inject kashidas in RTL contexts
1227         if( !(*i).IsRTLGlyph() )
1228         {
1229             ++i;
1230             continue;
1231         }
1232         // no kashida-injection for blank justified expansion either
1233         if( IsSpacingGlyph( (*i).mnGlyphIndex ) )
1234         {
1235             ++i;
1236             continue;
1237         }
1238         // calculate gap, ignore if too small
1239         int nGapWidth = rDeltaWidths[nOrigGlyphIndex];
1240         // worst case is one kashida even for mini-gaps
1241         if( 3 * nGapWidth < nKashidaWidth )
1242         {
1243             ++i;
1244             continue;
1245         }
1246         nKashidaCount = 1 + (nGapWidth / nKashidaWidth);
1247 #ifdef GRLAYOUT_DEBUG
1248         printf("inserting %d kashidas at %u\n", nKashidaCount, (*i).mnGlyphIndex);
1249 #endif
1250         GlyphItem glyphItem = *i;
1251         Point aPos(0, 0);
1252         aPos.X() = (*i).maLinearPos.X();
1253         GlyphItem newGi(glyphItem.mnCharPos, nKashidaIndex, aPos,
1254                 GlyphItem::IS_IN_CLUSTER|GlyphItem::IS_RTL_GLYPH, nKashidaWidth);
1255         mvGlyphs.reserve(mvGlyphs.size() + nKashidaCount);
1256         i = mvGlyphs.begin() + nGlyphIndex;
1257         mvGlyphs.insert(i, nKashidaCount, newGi);
1258         i = mvGlyphs.begin() + nGlyphIndex;
1259         nGlyphIndex += nKashidaCount;
1260         // now fix up the kashida positions
1261         for (int j = 0; j < nKashidaCount; j++)
1262         {
1263             (*(i)).maLinearPos.X() -= nGapWidth;
1264             nGapWidth -= nKashidaWidth;
1265             i++;
1266         }
1267 
1268         // fixup rightmost kashida for gap remainder
1269         if( nGapWidth < 0 )
1270         {
1271             if( nKashidaCount <= 1 )
1272                 nGapWidth /= 2;               // for small gap move kashida to middle
1273             (*(i-1)).mnNewWidth += nGapWidth;  // adjust kashida width to gap width
1274             (*(i-1)).maLinearPos.X() += nGapWidth;
1275         }
1276 
1277         (*i).mnNewWidth = (*i).mnOrigWidth;
1278         ++i;
1279     }
1280 
1281 }
1282 
1283 void GraphiteLayout::GetCaretPositions( int nArraySize, sal_Int32* pCaretXArray ) const
1284 {
1285     // For each character except the last discover the caret positions
1286     // immediatly before and after that character.
1287     // This is used for underlines in the GUI amongst other things.
1288     // It may be used from MultiSalLayout, in which case it must take into account
1289     // glyphs that have been moved.
1290     std::fill(pCaretXArray, pCaretXArray + nArraySize, -1);
1291     // the layout method doesn't modify the layout even though it isn't
1292     // const in the interface
1293     bool bRtl = const_cast<GraphiteLayout*>(this)->maLayout.rightToLeft();
1294     int prevBase = -1;
1295     long prevClusterWidth = 0;
1296     for (int i = 0, nCharSlot = 0; i < nArraySize && nCharSlot < static_cast<int>(mvCharDxs.size()); ++nCharSlot, i+=2)
1297     {
1298         if (mvChar2BaseGlyph[nCharSlot] != -1)
1299         {
1300             int nChar2Base = mvChar2BaseGlyph[nCharSlot] & GLYPH_INDEX_MASK;
1301             assert((mvChar2BaseGlyph[nCharSlot] > -1) && (nChar2Base < (signed)mvGlyphs.size()));
1302             GlyphItem gi = mvGlyphs[nChar2Base];
1303             if (gi.mnGlyphIndex == GF_DROPPED)
1304             {
1305                 continue;
1306             }
1307             int nCluster = nChar2Base;
1308             long origClusterWidth = gi.mnNewWidth;
1309             long nMin = gi.maLinearPos.X();
1310             long nMax = gi.maLinearPos.X() + gi.mnNewWidth;
1311             // attached glyphs are always stored after their base rtl or ltr
1312             while (++nCluster < static_cast<int>(mvGlyphs.size()) &&
1313                 !mvGlyphs[nCluster].IsClusterStart())
1314             {
1315                 origClusterWidth += mvGlyphs[nCluster].mnNewWidth;
1316                 if (mvGlyph2Char[nCluster] == nCharSlot)
1317                 {
1318                     nMin = std::min(nMin, mvGlyphs[nCluster].maLinearPos.X());
1319                     nMax = std::min(nMax, mvGlyphs[nCluster].maLinearPos.X() + mvGlyphs[nCluster].mnNewWidth);
1320                 }
1321             }
1322             if (bRtl)
1323             {
1324                 pCaretXArray[i+1] = nMin;
1325                 pCaretXArray[i] = nMax;
1326             }
1327             else
1328             {
1329                 pCaretXArray[i] = nMin;
1330                 pCaretXArray[i+1] = nMax;
1331             }
1332             prevBase = nChar2Base;
1333             prevClusterWidth = origClusterWidth;
1334         }
1335         else if (prevBase > -1)
1336         {
1337             // this could probably be improved
1338             assert((prevBase > -1) && (prevBase < (signed)mvGlyphs.size()));
1339             GlyphItem gi = mvGlyphs[prevBase];
1340             int nGlyph = prevBase + 1;
1341             // try to find a better match, otherwise default to complete cluster
1342             for (; nGlyph < static_cast<int>(mvGlyphs.size()) &&
1343                  !mvGlyphs[nGlyph].IsClusterStart(); nGlyph++)
1344             {
1345                 if (mvGlyph2Char[nGlyph] == nCharSlot)
1346                 {
1347                     gi = mvGlyphs[nGlyph];
1348                     break;
1349                 }
1350             }
1351             long nGWidth = gi.mnNewWidth;
1352             // if no match position at end of cluster
1353             if (nGlyph == static_cast<int>(mvGlyphs.size()) ||
1354                 mvGlyphs[nGlyph].IsClusterStart())
1355             {
1356                 nGWidth = prevClusterWidth;
1357                 if (bRtl)
1358                 {
1359                     pCaretXArray[i+1] = gi.maLinearPos.X();
1360                     pCaretXArray[i] = gi.maLinearPos.X();
1361                 }
1362                 else
1363                 {
1364                     pCaretXArray[i] = gi.maLinearPos.X() + prevClusterWidth;
1365                     pCaretXArray[i+1] = gi.maLinearPos.X() + prevClusterWidth;
1366                 }
1367             }
1368             else
1369             {
1370                 if (bRtl)
1371                 {
1372                     pCaretXArray[i+1] = gi.maLinearPos.X();
1373                     pCaretXArray[i] = gi.maLinearPos.X() + gi.mnNewWidth;
1374                 }
1375                 else
1376                 {
1377                     pCaretXArray[i] = gi.maLinearPos.X();
1378                     pCaretXArray[i+1] = gi.maLinearPos.X() + gi.mnNewWidth;
1379                 }
1380             }
1381         }
1382         else
1383         {
1384             pCaretXArray[i] = pCaretXArray[i+1] = 0;
1385         }
1386 #ifdef GRLAYOUT_DEBUG
1387         fprintf(grLog(),"%d,%d-%d\t", nCharSlot, pCaretXArray[i], pCaretXArray[i+1]);
1388 #endif
1389     }
1390 #ifdef GRLAYOUT_DEBUG
1391     fprintf(grLog(),"\n");
1392 #endif
1393 }
1394 
1395 
1396 // GetNextGlyphs returns a contiguous sequence of glyphs that can be
1397 // rendered together. It should never return a dropped glyph.
1398 // The glyph_slot returned should be the index of the next visible
1399 // glyph after the last glyph returned by this call.
1400 // The char_index array should be filled with the characters corresponding
1401 // to each glyph returned.
1402 // glyph_adv array should be a virtual width such that if successive
1403 // glyphs returned by this method are added one after the other they
1404 // have the correct spacing.
1405 // The logic in this method must match that expected in MultiSalLayout which
1406 // is used when glyph fallback is in operation.
1407 int GraphiteLayout::GetNextGlyphs( int length, sal_GlyphId * glyph_out,
1408         ::Point & aPosOut, int &glyph_slot, sal_Int32 * glyph_adv, int *char_index) const
1409 {
1410   // Sanity check on the slot index.
1411   if (glyph_slot >= signed(mvGlyphs.size()))
1412   {
1413     glyph_slot = mvGlyphs.size();
1414     return 0;
1415   }
1416   assert(glyph_slot >= 0);
1417   // Find the first glyph in the substring.
1418   for (; glyph_slot < signed(mvGlyphs.size()) &&
1419           ((mvGlyphs.begin() + glyph_slot)->mnGlyphIndex == GF_DROPPED);
1420           ++glyph_slot) {};
1421 
1422   // Update the length
1423   const int nGlyphSlotEnd = std::min(size_t(glyph_slot + length), mvGlyphs.size());
1424 
1425   // We're all out of glyphs here.
1426   if (glyph_slot == nGlyphSlotEnd)
1427   {
1428     return 0;
1429   }
1430 
1431   // Find as many glyphs as we can which can be drawn in one go.
1432   Glyphs::const_iterator glyph_itr = mvGlyphs.begin() + glyph_slot;
1433   const int         glyph_slot_begin = glyph_slot;
1434   const int            initial_y_pos = glyph_itr->maLinearPos.Y();
1435 
1436   // Set the position to the position of the start glyph.
1437   ::Point aStartPos = glyph_itr->maLinearPos;
1438   //aPosOut = glyph_itr->maLinearPos;
1439   aPosOut = GetDrawPosition(aStartPos);
1440 
1441 
1442   for (;;)  // Forever
1443   {
1444      // last index of the range from glyph_to_chars does not include this glyph
1445      if (char_index)
1446      {
1447         assert((glyph_slot >= -1) && (glyph_slot < (signed)mvGlyph2Char.size()));
1448         if (mvGlyph2Char[glyph_slot] == -1)
1449             *char_index++ = mvCharDxs.size();
1450         else
1451             *char_index++ = mvGlyph2Char[glyph_slot];
1452      }
1453      // Copy out this glyphs data.
1454      ++glyph_slot;
1455      *glyph_out++ = glyph_itr->mnGlyphIndex;
1456 
1457      // Find the actual advance - this must be correct if called from
1458      // MultiSalLayout::AdjustLayout which requests one glyph at a time.
1459      const long nGlyphAdvance = (glyph_slot == static_cast<int>(mvGlyphs.size()))?
1460           glyph_itr->mnNewWidth :
1461           ((glyph_itr+1)->maLinearPos.X() - glyph_itr->maLinearPos.X());
1462 
1463 #ifdef GRLAYOUT_DEBUG
1464     fprintf(grLog(),"GetNextGlyphs g%d c%d x%ld,%ld adv%ld, pos %ld,%ld\n", glyph_slot - 1,
1465             GLYPH_INDEX_MASK&mvGlyph2Char[glyph_slot-1], glyph_itr->maLinearPos.X(), glyph_itr->maLinearPos.Y(), nGlyphAdvance,
1466             aPosOut.X(), aPosOut.Y());
1467 #endif
1468 
1469      if (glyph_adv)  // If we are returning advance store it.
1470        *glyph_adv++ = nGlyphAdvance;
1471      else // Stop when next advance is unexpected.
1472        if (glyph_itr->mnOrigWidth != nGlyphAdvance)  break;
1473 
1474      // Have fetched all the glyphs we need to
1475      if (glyph_slot == nGlyphSlotEnd)
1476          break;
1477 
1478      ++glyph_itr;
1479      // Stop when next y position is unexpected.
1480      if (initial_y_pos != glyph_itr->maLinearPos.Y())
1481        break;
1482 
1483      // Stop if glyph dropped
1484      if (glyph_itr->mnGlyphIndex == GF_DROPPED)
1485        break;
1486   }
1487   int numGlyphs = glyph_slot - glyph_slot_begin;
1488   // move the next glyph_slot to a glyph that hasn't been dropped
1489   while (glyph_slot < static_cast<int>(mvGlyphs.size()) &&
1490          (mvGlyphs.begin() + glyph_slot)->mnGlyphIndex == GF_DROPPED)
1491          ++glyph_slot;
1492   return numGlyphs;
1493 }
1494 
1495 
1496 void GraphiteLayout::MoveGlyph( int nGlyphIndex, long nNewPos )
1497 {
1498     // TODO it might be better to actualy implement simplify properly, but this
1499     // needs to be done carefully so the glyph/char maps are maintained
1500     // If a glyph has been dropped then it wasn't returned by GetNextGlyphs, so
1501     // the index here may be wrong
1502     while ((mvGlyphs[nGlyphIndex].mnGlyphIndex == GF_DROPPED) &&
1503            (nGlyphIndex < (signed)mvGlyphs.size()))
1504     {
1505         nGlyphIndex++;
1506     }
1507     const long dx = nNewPos - mvGlyphs[nGlyphIndex].maLinearPos.X();
1508 
1509     if (dx == 0)  return;
1510     // GenericSalLayout only changes maLinearPos, mvCharDxs doesn't change
1511 #ifdef GRLAYOUT_DEBUG
1512     fprintf(grLog(),"Move %d (%ld,%ld) c%d by %ld\n", nGlyphIndex, mvGlyphs[nGlyphIndex].maLinearPos.X(), nNewPos, mvGlyph2Char[nGlyphIndex], dx);
1513 #endif
1514     for (size_t gi = nGlyphIndex; gi < mvGlyphs.size(); gi++)
1515     {
1516         mvGlyphs[gi].maLinearPos.X() += dx;
1517     }
1518     // width does need to be updated for correct fallback
1519     mnWidth += dx;
1520 }
1521 
1522 
1523 void GraphiteLayout::DropGlyph( int nGlyphIndex )
1524 {
1525     if(nGlyphIndex >= signed(mvGlyphs.size()))
1526         return;
1527 
1528     GlyphItem & glyph = mvGlyphs[nGlyphIndex];
1529     glyph.mnGlyphIndex = GF_DROPPED;
1530 #ifdef GRLAYOUT_DEBUG
1531     fprintf(grLog(),"Dropped %d\n", nGlyphIndex);
1532 #endif
1533 }
1534 
1535 void GraphiteLayout::Simplify( bool isBaseLayout )
1536 {
1537   const sal_GlyphId dropMarker = isBaseLayout ? GF_DROPPED : 0;
1538 
1539   Glyphs::iterator gi = mvGlyphs.begin();
1540   // TODO check whether we need to adjust positions here
1541   // MultiSalLayout seems to move the glyphs itself, so it may not be needed.
1542   long deltaX = 0;
1543   while (gi != mvGlyphs.end())
1544   {
1545       if (gi->mnGlyphIndex == dropMarker)
1546       {
1547         deltaX += gi->mnNewWidth;
1548         gi->mnNewWidth = 0;
1549       }
1550       else
1551       {
1552         deltaX = 0;
1553       }
1554       //mvCharDxs[mvGlyph2Char[gi->mnCharPos]] -= deltaX;
1555       ++gi;
1556   }
1557 #ifdef GRLAYOUT_DEBUG
1558   fprintf(grLog(),"Simplify base%d dx=%ld newW=%ld\n", isBaseLayout, deltaX, mnWidth - deltaX);
1559 #endif
1560   // discard width from trailing dropped glyphs, but not those in the middle
1561   mnWidth -= deltaX;
1562 }
1563