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 // Implementation 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 neighbouring 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 referring 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 initial 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 // Bug #126768: vectors mvGlyph2Char and mvChar2BaseGlyph may have different sizes
1052 size_t nMaxCharIndex = mvGlyph2Char.size();
1053 if ( nMaxCharIndex > mvChar2BaseGlyph.size() )
1054 nMaxCharIndex = mvChar2BaseGlyph.size();
1055 while ( ++nCharIndex < nMaxCharIndex )
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
ApplyDXArray(ImplLayoutArgs & args,std::vector<int> & rDeltaWidth)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.maGlyphId != 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
kashidaJustify(std::vector<int> & rDeltaWidths,sal_GlyphId nKashidaIndex,int nKashidaWidth)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).maGlyphId ) )
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).maGlyphId);
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
GetCaretPositions(int nArraySize,sal_Int32 * pCaretXArray) const1283 void GraphiteLayout::GetCaretPositions( int nArraySize, sal_Int32* pCaretXArray ) const
1284 {
1285 // For each character except the last discover the caret positions
1286 // immediately 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.maGlyphId == 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.
GetNextGlyphs(int length,sal_GlyphId * glyph_out,::Point & aPosOut,int & glyph_slot,sal_Int32 * glyph_adv,int * char_index) const1407 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)->maGlyphId == 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->maGlyphId;
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->maGlyphId == 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)->maGlyphId == GF_DROPPED)
1491 ++glyph_slot;
1492 return numGlyphs;
1493 }
1494
1495
MoveGlyph(int nGlyphIndex,long nNewPos)1496 void GraphiteLayout::MoveGlyph( int nGlyphIndex, long nNewPos )
1497 {
1498 // TODO it might be better to actually 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].maGlyphId == 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
DropGlyph(int nGlyphIndex)1523 void GraphiteLayout::DropGlyph( int nGlyphIndex )
1524 {
1525 if(nGlyphIndex >= signed(mvGlyphs.size()))
1526 return;
1527
1528 GlyphItem & glyph = mvGlyphs[nGlyphIndex];
1529 glyph.maGlyphId = GF_DROPPED;
1530 #ifdef GRLAYOUT_DEBUG
1531 fprintf(grLog(),"Dropped %d\n", nGlyphIndex);
1532 #endif
1533 }
1534
Simplify(bool isBaseLayout)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->maGlyphId == 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