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