Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : // Description: An implementation of the SalLayout interface that uses the
21 : // Graphite engine.
22 :
23 :
24 : // We need this to enable namespace support in libgrengine headers.
25 : #define GR_NAMESPACE
26 :
27 : // Enable lots of debug info
28 : #if OSL_DEBUG_LEVEL > 1
29 : #include <cstdio>
30 : #define GRLAYOUT_DEBUG 1
31 : #undef NDEBUG
32 : #endif
33 :
34 : // #define GRLAYOUT_DEBUG 1
35 :
36 : // Header files
37 : //
38 : // Standard Library
39 : #include <algorithm>
40 : #include <cassert>
41 : #include <functional>
42 : #include <limits>
43 : #include <numeric>
44 : #include <deque>
45 :
46 : // Platform
47 : #include <svsys.h>
48 :
49 : #include <salgdi.hxx>
50 :
51 : #include <unicode/uchar.h>
52 : #include <unicode/ubidi.h>
53 : #include <unicode/uscript.h>
54 :
55 : // Graphite Libraries (must be after vcl headers on windows)
56 : #include <graphite2/Segment.h>
57 :
58 : #include <graphite_layout.hxx>
59 : #include <graphite_features.hxx>
60 :
61 : // Module private type definitions and forward declarations.
62 : //
63 : // Module private names.
64 : //
65 :
66 : #ifdef GRLAYOUT_DEBUG
67 : static FILE * grLog()
68 : {
69 : #ifdef WNT
70 : static FILE * grLogFile = NULL;
71 : if (grLogFile == NULL)
72 : {
73 : std::string logFileName(getenv("TEMP"));
74 : logFileName.append("/graphitelayout.log");
75 : grLogFile = fopen(logFileName.c_str(),"w");
76 : }
77 : else
78 : fflush(grLogFile);
79 : return grLogFile;
80 : #else
81 : fflush(stdout);
82 : return stdout;
83 : #endif
84 : }
85 : #endif
86 :
87 : namespace
88 : {
89 0 : inline long round(const float n) {
90 0 : return long(n + (n < 0 ? -0.5 : 0.5));
91 : }
92 :
93 : template<typename T>
94 0 : inline bool in_range(const T i, const T b, const T e) {
95 0 : return !(b > i) && i < e;
96 : }
97 :
98 : template<typename T>
99 : inline bool is_subrange(const T sb, const T se, const T b, const T e) {
100 : return !(b > sb || se > e);
101 : }
102 :
103 : template<typename T>
104 : inline bool is_subrange(const std::pair<T, T> &s, const T b, const T e) {
105 : return is_subrange(s.first, s.second, b, e);
106 : }
107 :
108 0 : int findSameDirLimit(const sal_Unicode* buffer, int charCount, bool rtl)
109 : {
110 0 : UErrorCode status = U_ZERO_ERROR;
111 0 : UBiDi *ubidi = ubidi_openSized(charCount, 0, &status);
112 0 : int limit = 0;
113 : ubidi_setPara(ubidi, reinterpret_cast<const UChar *>(buffer), charCount,
114 0 : (rtl)?UBIDI_DEFAULT_RTL:UBIDI_DEFAULT_LTR, NULL, &status);
115 0 : UBiDiLevel level = 0;
116 0 : ubidi_getLogicalRun(ubidi, 0, &limit, &level);
117 0 : ubidi_close(ubidi);
118 0 : if ((rtl && !(level & 1)) || (!rtl && (level & 1)))
119 : {
120 0 : limit = 0;
121 : }
122 0 : return limit;
123 : }
124 :
125 : template <typename T>
126 0 : T maximum(T a, T b)
127 : {
128 0 : return (a > b)? a : b;
129 : }
130 : template <typename T>
131 0 : T minimum(T a, T b)
132 : {
133 0 : return (a < b)? a : b;
134 : }
135 :
136 : } // namespace
137 :
138 : // Impementation of the GraphiteLayout::Glyphs container class.
139 : // This is an extended vector class with methods added to enable
140 : // o Correctly filling with glyphs.
141 : // o Querying clustering relationships.
142 : // o manipulations that affect neighouring glyphs.
143 :
144 : const int GraphiteLayout::EXTRA_CONTEXT_LENGTH = 10;
145 :
146 : // find first slot of cluster and first slot of subsequent cluster
147 0 : static void findFirstClusterSlot(const gr_slot* base, gr_slot const** first, gr_slot const** after, int * firstChar, int * lastChar, bool bRtl)
148 : {
149 0 : if (gr_slot_attached_to(base) == NULL)
150 : {
151 0 : *first = base;
152 : *after = (bRtl)? gr_slot_prev_in_segment(base) :
153 0 : gr_slot_next_in_segment(base);
154 0 : *firstChar = gr_slot_before(base);
155 0 : *lastChar = gr_slot_after(base);
156 : }
157 0 : const gr_slot * attachment = gr_slot_first_attachment(base);
158 0 : while (attachment)
159 : {
160 0 : if (gr_slot_origin_X(*first) > gr_slot_origin_X(attachment))
161 0 : *first = attachment;
162 : const gr_slot* attachmentNext = (bRtl)?
163 0 : gr_slot_prev_in_segment(attachment) : gr_slot_next_in_segment(attachment);
164 0 : if (attachmentNext)
165 : {
166 0 : if (*after && (gr_slot_origin_X(*after) < gr_slot_origin_X(attachmentNext)))
167 0 : *after = attachmentNext;
168 : }
169 : else
170 : {
171 0 : *after = NULL;
172 : }
173 0 : if (gr_slot_before(attachment) < *firstChar)
174 0 : *firstChar = gr_slot_before(attachment);
175 0 : if (gr_slot_after(attachment) > *lastChar)
176 0 : *lastChar = gr_slot_after(attachment);
177 0 : if (gr_slot_first_attachment(attachment))
178 0 : findFirstClusterSlot(attachment, first, after, firstChar, lastChar, bRtl);
179 0 : attachment = gr_slot_next_sibling_attachment(attachment);
180 : }
181 0 : }
182 :
183 : // The Graphite glyph stream is really a sequence of glyph attachment trees
184 : // each rooted at a non-attached base glyph. fill_from walks the glyph stream,
185 : // finds each non-attached base glyph and calls append to record them as a
186 : // sequence of clusters.
187 : void
188 0 : GraphiteLayout::fillFrom(gr_segment * pSegment, ImplLayoutArgs &rArgs, float fScaling)
189 : {
190 0 : bool bRtl = (rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL);
191 0 : int nCharRequested = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
192 0 : int nChar = gr_seg_n_cinfo(pSegment);
193 0 : float fMinX = gr_seg_advance_X(pSegment);
194 0 : float fMaxX = 0.0f;
195 0 : long nDxOffset = 0; // from dropped glyphs
196 0 : int nFirstCharInCluster = 0;
197 0 : int nLastCharInCluster = 0;
198 0 : unsigned int nGlyphs = gr_seg_n_slots(pSegment);
199 0 : mvGlyph2Char.assign(nGlyphs, -1);
200 0 : mvGlyphs.reserve(nGlyphs);
201 :
202 0 : if (bRtl)
203 : {
204 0 : const gr_slot* baseSlot = gr_seg_last_slot(pSegment);
205 : // find first base
206 0 : while (baseSlot && (gr_slot_attached_to(baseSlot) != NULL))
207 0 : baseSlot = gr_slot_prev_in_segment(baseSlot);
208 0 : int iChar = nChar - 1;
209 0 : int iNextChar = nChar - 1;
210 0 : bool reordered = false;
211 0 : int nBaseGlyphIndex = 0;
212 : // now loop over bases
213 0 : while (baseSlot)
214 : {
215 0 : bool bCluster = !reordered;
216 0 : const gr_slot * clusterFirst = NULL;
217 0 : const gr_slot * clusterAfter = NULL;
218 0 : int firstChar = -1;
219 0 : int lastChar = -1;
220 0 : findFirstClusterSlot(baseSlot, &clusterFirst, &clusterAfter, &firstChar, &lastChar, bRtl);
221 0 : iNextChar = minimum<int>(firstChar, iNextChar);
222 0 : if (bCluster)
223 : {
224 0 : nBaseGlyphIndex = mvGlyphs.size();
225 0 : mvGlyph2Char[nBaseGlyphIndex] = iChar + mnSegCharOffset;
226 0 : nFirstCharInCluster = firstChar;
227 0 : nLastCharInCluster = lastChar;
228 : }
229 : else
230 : {
231 0 : mvGlyph2Char[mvGlyphs.size()] = firstChar + mnSegCharOffset;
232 0 : nFirstCharInCluster = minimum<int>(firstChar, nFirstCharInCluster);
233 0 : nLastCharInCluster = maximum<int>(firstChar, nLastCharInCluster);
234 : }
235 0 : float leftBoundary = gr_slot_origin_X(clusterFirst);
236 : float rightBoundary = (clusterAfter)?
237 0 : gr_slot_origin_X(clusterAfter) : gr_seg_advance_X(pSegment);
238 0 : if (
239 : lastChar < iChar &&
240 0 : (gr_cinfo_after(gr_seg_cinfo(pSegment, iChar)) >
241 0 : static_cast<int>(gr_slot_index(clusterAfter)))
242 : )
243 : {
244 0 : reordered = true;
245 : }
246 : else
247 : {
248 0 : reordered = false;
249 0 : iChar = iNextChar - 1;
250 : }
251 0 : if (mnSegCharOffset + nFirstCharInCluster >= mnMinCharPos &&
252 : mnSegCharOffset + nFirstCharInCluster < mnEndCharPos)
253 : {
254 0 : fMinX = minimum<float>(fMinX, leftBoundary);
255 0 : fMaxX = maximum<float>(fMaxX, rightBoundary);
256 0 : if (!reordered)
257 : {
258 0 : for (int i = nFirstCharInCluster; i <= nLastCharInCluster; i++)
259 : {
260 0 : if (mnSegCharOffset + i >= mnEndCharPos)
261 0 : break;
262 : // from the point of view of the dx array, the xpos is
263 : // the origin of the first glyph of the cluster rtl
264 0 : mvCharDxs[mnSegCharOffset + i - mnMinCharPos] =
265 0 : static_cast<int>(leftBoundary * fScaling) + nDxOffset;
266 0 : mvCharBreaks[mnSegCharOffset + i - mnMinCharPos] = gr_cinfo_break_weight(gr_seg_cinfo(pSegment, i));
267 : }
268 0 : mvChar2BaseGlyph[mnSegCharOffset + nFirstCharInCluster - mnMinCharPos] = nBaseGlyphIndex;
269 : }
270 : append(pSegment, rArgs, baseSlot, gr_slot_origin_X(baseSlot), rightBoundary, fScaling,
271 0 : nDxOffset, bCluster, mnSegCharOffset + firstChar);
272 : }
273 0 : if (mnSegCharOffset + nLastCharInCluster < mnMinCharPos)
274 : break;
275 0 : baseSlot = gr_slot_next_sibling_attachment(baseSlot);
276 : }
277 : }
278 : else
279 : {
280 0 : const gr_slot* baseSlot = gr_seg_first_slot(pSegment);
281 : // find first base
282 0 : while (baseSlot && (gr_slot_attached_to(baseSlot) != NULL))
283 0 : baseSlot = gr_slot_next_in_segment(baseSlot);
284 0 : int iChar = 0; // relative to segment
285 0 : int iNextChar = 0;
286 0 : bool reordered = false;
287 0 : int nBaseGlyphIndex = 0;
288 : // now loop over bases
289 0 : while (baseSlot)
290 : {
291 0 : bool bCluster = !reordered;
292 0 : const gr_slot * clusterFirst = NULL;
293 0 : const gr_slot * clusterAfter = NULL;
294 0 : int firstChar = -1;
295 0 : int lastChar = -1;
296 0 : findFirstClusterSlot(baseSlot, &clusterFirst, &clusterAfter, &firstChar, &lastChar, bRtl);
297 0 : iNextChar = maximum<int>(lastChar, iNextChar);
298 0 : if (bCluster)
299 : {
300 0 : nBaseGlyphIndex = mvGlyphs.size();
301 0 : mvGlyph2Char[nBaseGlyphIndex] = iChar + mnSegCharOffset;
302 0 : nFirstCharInCluster = firstChar;
303 0 : nLastCharInCluster = lastChar;
304 : }
305 : else
306 : {
307 0 : mvGlyph2Char[mvGlyphs.size()] = firstChar + mnSegCharOffset;
308 0 : nFirstCharInCluster = minimum<int>(firstChar, nFirstCharInCluster);
309 0 : nLastCharInCluster = maximum<int>(lastChar, nLastCharInCluster);
310 : }
311 0 : if (
312 : firstChar > iChar &&
313 0 : (gr_cinfo_before(gr_seg_cinfo(pSegment, iChar)) >
314 0 : static_cast<int>(gr_slot_index(clusterFirst)))
315 : )
316 : {
317 0 : reordered = true;
318 : }
319 : else
320 : {
321 0 : reordered = false;
322 0 : iChar = iNextChar + 1;
323 : }
324 0 : float leftBoundary = gr_slot_origin_X(clusterFirst);
325 : float rightBoundary = (clusterAfter)?
326 0 : gr_slot_origin_X(clusterAfter) : gr_seg_advance_X(pSegment);
327 0 : int bFirstChar = gr_cinfo_base(gr_seg_cinfo(pSegment, nFirstCharInCluster));
328 0 : if (mnSegCharOffset + bFirstChar >= mnMinCharPos &&
329 : mnSegCharOffset + bFirstChar < mnEndCharPos)
330 : {
331 0 : fMinX = minimum<float>(fMinX, leftBoundary);
332 0 : fMaxX = maximum<float>(fMaxX, rightBoundary);
333 0 : if (!reordered)
334 : {
335 0 : for (int i = nFirstCharInCluster; i <= nLastCharInCluster; i++)
336 : {
337 0 : int ibase = gr_cinfo_base(gr_seg_cinfo(pSegment, i));
338 0 : if (mnSegCharOffset + ibase >= mnEndCharPos)
339 0 : break;
340 : // from the point of view of the dx array, the xpos is
341 : // the origin of the first glyph of the next cluster ltr
342 0 : mvCharDxs[mnSegCharOffset + ibase - mnMinCharPos] =
343 0 : static_cast<int>(rightBoundary * fScaling) + nDxOffset;
344 0 : mvCharBreaks[mnSegCharOffset + ibase - mnMinCharPos] = gr_cinfo_break_weight(gr_seg_cinfo(pSegment, i));
345 : }
346 : // only set mvChar2BaseGlyph for first character of cluster
347 0 : mvChar2BaseGlyph[mnSegCharOffset + bFirstChar - mnMinCharPos] = nBaseGlyphIndex;
348 : }
349 : append(pSegment, rArgs, baseSlot, gr_slot_origin_X(baseSlot), rightBoundary, fScaling,
350 0 : nDxOffset, true, mnSegCharOffset + firstChar);
351 : }
352 0 : if (mnSegCharOffset + bFirstChar >= mnEndCharPos)
353 : break;
354 0 : baseSlot = gr_slot_next_sibling_attachment(baseSlot);
355 : }
356 : }
357 0 : long nXOffset = round(fMinX * fScaling);
358 0 : mnWidth = round(fMaxX * fScaling) - nXOffset + nDxOffset;
359 0 : if (mnWidth < 0)
360 : {
361 : // This can happen when there was no base inside the range
362 0 : mnWidth = 0;
363 : }
364 : // fill up non-base char dx with cluster widths from previous base glyph
365 0 : if (bRtl)
366 : {
367 0 : if (mvCharDxs[nCharRequested-1] == -1)
368 0 : mvCharDxs[nCharRequested-1] = 0;
369 : else
370 0 : mvCharDxs[nCharRequested-1] -= nXOffset;
371 0 : for (int i = nCharRequested - 2; i >= 0; i--)
372 : {
373 0 : if (mvCharDxs[i] == -1) mvCharDxs[i] = mvCharDxs[i+1];
374 0 : else mvCharDxs[i] -= nXOffset;
375 : }
376 : }
377 : else
378 : {
379 0 : if (mvCharDxs[0] == -1)
380 0 : mvCharDxs[0] = 0;
381 : else
382 0 : mvCharDxs[0] -= nXOffset;
383 0 : for (int i = 1; i < nCharRequested; i++)
384 : {
385 0 : if (mvCharDxs[i] == -1) mvCharDxs[i] = mvCharDxs[i-1];
386 0 : else mvCharDxs[i] -= nXOffset;
387 : #ifdef GRLAYOUT_DEBUG
388 : fprintf(grLog(),"%d,%d ", (int)i, (int)mvCharDxs[i]);
389 : #endif
390 : }
391 : }
392 : // remove offset due to context if there is one
393 0 : if (nXOffset != 0)
394 : {
395 0 : for (size_t i = 0; i < mvGlyphs.size(); i++)
396 0 : mvGlyphs[i].maLinearPos.X() -= nXOffset;
397 : }
398 : #ifdef GRLAYOUT_DEBUG
399 : fprintf(grLog(), "fillFrom %" SAL_PRI_SIZET "u glyphs offset %ld width %ld\n", mvGlyphs.size(), nXOffset, mnWidth);
400 : #endif
401 0 : }
402 :
403 : // append walks an attachment tree, flattening it, and converting it into a
404 : // sequence of GlyphItem objects which we can later manipulate.
405 : float
406 0 : GraphiteLayout::append(gr_segment *pSeg, ImplLayoutArgs &rArgs,
407 : const gr_slot * gi, float gOrigin, float nextGlyphOrigin, float scaling, long & rDXOffset,
408 : bool bIsBase, int baseChar)
409 : {
410 0 : bool bRtl = (rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL);
411 : float nextOrigin;
412 : assert(gi);
413 : assert(gr_slot_before(gi) <= gr_slot_after(gi));
414 0 : int firstChar = gr_slot_before(gi) + mnSegCharOffset;
415 : assert(mvGlyphs.size() < mvGlyph2Char.size());
416 0 : if (!bIsBase) mvGlyph2Char[mvGlyphs.size()] = baseChar;//firstChar;
417 : // is the next glyph attached or in the next cluster?
418 : //glyph_set_range_t iAttached = gi.attachedClusterGlyphs();
419 0 : const gr_slot * pFirstAttached = gr_slot_first_attachment(gi);
420 0 : const gr_slot * pNextSibling = gr_slot_next_sibling_attachment(gi);
421 0 : if (pFirstAttached)
422 0 : nextOrigin = gr_slot_origin_X(pFirstAttached);
423 0 : else if (!bIsBase && pNextSibling)
424 0 : nextOrigin = gr_slot_origin_X(pNextSibling);
425 : else
426 0 : nextOrigin = nextGlyphOrigin;
427 0 : long glyphId = gr_slot_gid(gi);
428 0 : long deltaOffset = 0;
429 0 : int scaledGlyphPos = round(gr_slot_origin_X(gi) * scaling);
430 0 : int glyphWidth = round((nextOrigin - gOrigin) * scaling);
431 : // if (glyphWidth < 0)
432 : // {
433 : // nextOrigin = gOrigin;
434 : // glyphWidth = 0;
435 : // }
436 : #ifdef GRLAYOUT_DEBUG
437 : fprintf(grLog(),"c%d g%ld,X%d W%d nX%f ", firstChar, glyphId,
438 : (int)(gr_slot_origin_X(gi) * scaling), glyphWidth, nextOrigin * scaling);
439 : #endif
440 0 : if (glyphId == 0)
441 : {
442 0 : rArgs.NeedFallback(firstChar, bRtl);
443 0 : if( (SAL_LAYOUT_FOR_FALLBACK & rArgs.mnFlags ))
444 : {
445 0 : glyphId = GF_DROPPED;
446 0 : deltaOffset -= glyphWidth;
447 0 : glyphWidth = 0;
448 : }
449 : }
450 0 : else if(rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK)
451 : {
452 : #ifdef GRLAYOUT_DEBUG
453 : fprintf(grLog(),"fallback c%d %x in run %d\n", firstChar, rArgs.mpStr[firstChar],
454 : rArgs.maRuns.PosIsInAnyRun(firstChar));
455 : #endif
456 : // glyphs that aren't requested for fallback will be taken from base
457 : // layout, so mark them as dropped (should this wait until Simplify(false) is called?)
458 0 : if (!rArgs.maRuns.PosIsInAnyRun(firstChar) &&
459 0 : in_range(firstChar, rArgs.mnMinCharPos, rArgs.mnEndCharPos))
460 : {
461 0 : glyphId = GF_DROPPED;
462 0 : deltaOffset -= glyphWidth;
463 0 : glyphWidth = 0;
464 : }
465 : }
466 : // append this glyph. Set the cluster flag if this glyph is attached to another
467 0 : long nGlyphFlags = bIsBase ? 0 : GlyphItem::IS_IN_CLUSTER;
468 0 : nGlyphFlags |= (bRtl)? GlyphItem::IS_RTL_GLYPH : 0;
469 0 : GlyphItem aGlyphItem(mvGlyphs.size(),
470 : glyphId,
471 : Point(scaledGlyphPos + rDXOffset,
472 0 : round((-gr_slot_origin_Y(gi) * scaling))),
473 : nGlyphFlags,
474 0 : glyphWidth);
475 0 : if (glyphId != static_cast<long>(GF_DROPPED))
476 0 : aGlyphItem.mnOrigWidth = round(gr_slot_advance_X(gi, mpFace, mpFont) * scaling);
477 0 : mvGlyphs.push_back(aGlyphItem);
478 :
479 : // update the offset if this glyph was dropped
480 0 : rDXOffset += deltaOffset;
481 :
482 : // Recursively append all the attached glyphs.
483 0 : float cOrigin = nextOrigin;
484 0 : for (const gr_slot * agi = gr_slot_first_attachment(gi); agi != NULL; agi = gr_slot_next_sibling_attachment(agi))
485 0 : cOrigin = append(pSeg, rArgs, agi, cOrigin, nextGlyphOrigin, scaling, rDXOffset, false, baseChar);
486 :
487 0 : return cOrigin;
488 : }
489 :
490 : //
491 : // An implementation of the SalLayout interface to enable Graphite enabled fonts to be used.
492 : //
493 0 : GraphiteLayout::GraphiteLayout(const gr_face * face, gr_font * font,
494 : const grutils::GrFeatureParser * pFeatures) throw()
495 : : mpFace(face),
496 : mpFont(font),
497 : mnWidth(0),
498 : mfScaling(1.0),
499 0 : mpFeatures(pFeatures)
500 : {
501 :
502 0 : }
503 :
504 0 : GraphiteLayout::~GraphiteLayout() throw()
505 : {
506 0 : clear();
507 : // the features and font are owned by the platform layers
508 0 : mpFeatures = NULL;
509 0 : mpFont = NULL;
510 0 : }
511 :
512 0 : void GraphiteLayout::clear()
513 : {
514 : // Destroy the segment and text source from any previous invocation of
515 : // LayoutText
516 0 : mvGlyphs.clear();
517 0 : mvCharDxs.clear();
518 0 : mvChar2BaseGlyph.clear();
519 0 : mvGlyph2Char.clear();
520 :
521 : // Reset the state to the empty state.
522 0 : mnWidth = 0;
523 : // Don't reset the scaling, because it is set before LayoutText
524 0 : }
525 :
526 : // This method shouldn't be called on windows, since it needs the dc reset
527 0 : bool GraphiteLayout::LayoutText(ImplLayoutArgs & rArgs)
528 : {
529 0 : gr_segment * pSegment = NULL;
530 0 : bool success = true;
531 0 : if (rArgs.mnMinCharPos < rArgs.mnEndCharPos)
532 : {
533 0 : pSegment = CreateSegment(rArgs);
534 0 : if (!pSegment)
535 0 : return false;
536 0 : success = LayoutGlyphs(rArgs, pSegment);
537 0 : if (pSegment)
538 : {
539 0 : gr_seg_destroy(pSegment);
540 0 : pSegment = NULL;
541 : }
542 : }
543 : else
544 : {
545 0 : clear();
546 : }
547 0 : return success;
548 : }
549 :
550 :
551 0 : gr_segment * GraphiteLayout::CreateSegment(ImplLayoutArgs& rArgs)
552 : {
553 : assert(rArgs.mnLength >= 0);
554 :
555 0 : gr_segment * pSegment = NULL;
556 :
557 : // Set the SalLayouts values to be the inital ones.
558 0 : SalLayout::AdjustLayout(rArgs);
559 : // TODO check if this is needed
560 0 : if (mnUnitsPerPixel > 1)
561 0 : mfScaling = 1.0f / mnUnitsPerPixel;
562 :
563 : // Clear out any previous buffers
564 0 : clear();
565 0 : bool bRtl = mnLayoutFlags & SAL_LAYOUT_BIDI_RTL;
566 : try
567 : {
568 : // Don't set RTL if font doesn't support it otherwise it forces rtl on
569 : // everything
570 : //if (bRtl && (mrFont.getSupportedScriptDirections() & gr::kfsdcHorizRtl))
571 : // maLayout.setRightToLeft(bRtl);
572 :
573 : // Context is often needed beyond the specified end, however, we don't
574 : // want it if there has been a direction change, since it is hard
575 : // to tell between reordering within one direction and multi-directional
576 : // text. Extra context, can also cause problems with ligatures stradling
577 : // a hyphenation point, so disable if CTL is disabled.
578 0 : mnSegCharOffset = rArgs.mnMinCharPos;
579 0 : int limit = rArgs.mnEndCharPos;
580 0 : if (!(SAL_LAYOUT_COMPLEX_DISABLED & rArgs.mnFlags))
581 : {
582 0 : const int nSegCharMin = maximum<int>(0, mnMinCharPos - EXTRA_CONTEXT_LENGTH);
583 0 : const int nSegCharLimit = minimum(rArgs.mnLength, mnEndCharPos + EXTRA_CONTEXT_LENGTH);
584 0 : if (nSegCharMin < mnSegCharOffset)
585 : {
586 : int sameDirEnd = findSameDirLimit(rArgs.mpStr + nSegCharMin,
587 0 : rArgs.mnEndCharPos - nSegCharMin, bRtl);
588 0 : if (sameDirEnd == rArgs.mnEndCharPos)
589 0 : mnSegCharOffset = nSegCharMin;
590 : }
591 0 : if (nSegCharLimit > limit)
592 : {
593 : limit += findSameDirLimit(rArgs.mpStr + rArgs.mnEndCharPos,
594 0 : nSegCharLimit - rArgs.mnEndCharPos, bRtl);
595 : }
596 : }
597 0 : size_t numchars = gr_count_unicode_characters(gr_utf16, rArgs.mpStr + mnSegCharOffset,
598 0 : rArgs.mpStr + (rArgs.mnLength > limit + 64 ? limit + 64 : rArgs.mnLength), NULL);
599 0 : if (mpFeatures)
600 0 : pSegment = gr_make_seg(mpFont, mpFace, 0, mpFeatures->values(), gr_utf16,
601 0 : rArgs.mpStr + mnSegCharOffset, numchars, bRtl);
602 : else
603 : pSegment = gr_make_seg(mpFont, mpFace, 0, NULL, gr_utf16,
604 0 : rArgs.mpStr + mnSegCharOffset, numchars, bRtl);
605 :
606 : //pSegment = new gr::RangeSegment((gr::Font *)&mrFont, mpTextSrc, &maLayout, mnMinCharPos, limit);
607 0 : if (pSegment != NULL)
608 : {
609 : #ifdef GRLAYOUT_DEBUG
610 : fprintf(grLog(),"Gr::LayoutText %d-%d, context %d, len %d, numchars %" SAL_PRI_SIZET "u, rtl %d scaling %f:", rArgs.mnMinCharPos,
611 : rArgs.mnEndCharPos, limit, rArgs.mnLength, numchars, bRtl, mfScaling);
612 : for (int i = mnSegCharOffset; i < limit; ++i)
613 : fprintf(grLog(), " %04X", rArgs.mpStr[i]);
614 : fprintf(grLog(), "\n");
615 : #endif
616 : }
617 : else
618 : {
619 : #ifdef GRLAYOUT_DEBUG
620 : fprintf(grLog(), "Gr::LayoutText failed: ");
621 : for (int i = mnMinCharPos; i < limit; i++)
622 : {
623 : fprintf(grLog(), "%04x ", rArgs.mpStr[i]);
624 : }
625 : fprintf(grLog(), "\n");
626 : #endif
627 0 : clear();
628 0 : return NULL;
629 : }
630 : }
631 0 : catch (...)
632 : {
633 0 : clear(); // destroy the text source and any partially built segments.
634 0 : return NULL;
635 : }
636 0 : return pSegment;
637 : }
638 :
639 0 : bool GraphiteLayout::LayoutGlyphs(ImplLayoutArgs& rArgs, gr_segment * pSegment)
640 : {
641 : // Calculate the initial character dxs.
642 0 : mvCharDxs.assign(mnEndCharPos - mnMinCharPos, -1);
643 0 : mvChar2BaseGlyph.assign(mnEndCharPos - mnMinCharPos, -1);
644 0 : mvCharBreaks.assign(mnEndCharPos - mnMinCharPos, 0);
645 0 : mnWidth = 0;
646 0 : if (mvCharDxs.size() > 0)
647 : {
648 : // Discover all the clusters.
649 : try
650 : {
651 0 : bool bRtl = mnLayoutFlags & SAL_LAYOUT_BIDI_RTL;
652 0 : fillFrom(pSegment, rArgs, mfScaling);
653 :
654 0 : if (bRtl)
655 : {
656 : // not needed for adjacent differences, but for mouse clicks to char
657 : std::transform(mvCharDxs.begin(), mvCharDxs.end(), mvCharDxs.begin(),
658 0 : std::bind1st(std::minus<long>(), mnWidth));
659 : // fixup last dx to ensure it always equals the width
660 0 : mvCharDxs[mvCharDxs.size() - 1] = mnWidth;
661 : }
662 : }
663 0 : catch (const std::exception &e)
664 : {
665 : #ifdef GRLAYOUT_DEBUG
666 : fprintf(grLog(),"LayoutGlyphs failed %s\n", e.what());
667 : #else
668 : (void)e;
669 : #endif
670 0 : return false;
671 : }
672 0 : catch (...)
673 : {
674 : #ifdef GRLAYOUT_DEBUG
675 : fprintf(grLog(),"LayoutGlyphs failed with exception");
676 : #endif
677 0 : return false;
678 : }
679 : }
680 : else
681 : {
682 0 : mnWidth = 0;
683 : }
684 0 : return true;
685 : }
686 :
687 0 : int GraphiteLayout::GetTextBreak(long maxmnWidth, long char_extra, int factor) const
688 : {
689 : #ifdef GRLAYOUT_DEBUG
690 : fprintf(grLog(),"Gr::GetTextBreak c[%d-%d) maxWidth %ld char extra %ld factor %d\n",
691 : mnMinCharPos, mnEndCharPos, maxmnWidth, char_extra, factor);
692 : #endif
693 :
694 : // return quickly if this segment is narrower than the target width
695 0 : if (maxmnWidth > mnWidth * factor + char_extra * (mnEndCharPos - mnMinCharPos - 1))
696 0 : return STRING_LEN;
697 :
698 0 : long nWidth = mvCharDxs[0] * factor;
699 0 : long wLastBreak = 0;
700 0 : int nLastBreak = -1;
701 0 : int nEmergency = -1;
702 0 : for (size_t i = 1; i < mvCharDxs.size(); i++)
703 : {
704 0 : nWidth += char_extra;
705 0 : if (nWidth > maxmnWidth) break;
706 0 : if (mvChar2BaseGlyph[i] != -1)
707 : {
708 0 : if (
709 0 : (mvCharBreaks[i] > -35 || (mvCharBreaks[i-1] > 0 && mvCharBreaks[i-1] < 35)) &&
710 0 : (mvCharBreaks[i-1] < 35 || (mvCharBreaks[i] < 0 && mvCharBreaks[i] > -35))
711 : )
712 : {
713 0 : nLastBreak = static_cast<int>(i);
714 0 : wLastBreak = nWidth;
715 : }
716 0 : nEmergency = static_cast<int>(i);
717 : }
718 0 : nWidth += (mvCharDxs[i] - mvCharDxs[i-1]) * factor;
719 : }
720 0 : int nBreak = mnMinCharPos;
721 0 : if (wLastBreak > 9 * maxmnWidth / 10)
722 0 : nBreak += nLastBreak;
723 : else
724 0 : if (nEmergency > -1)
725 0 : nBreak += nEmergency;
726 :
727 : #ifdef GRLAYOUT_DEBUG
728 : fprintf(grLog(), "Gr::GetTextBreak break after %d, weights(%d, %d)\n", nBreak - mnMinCharPos, mvCharBreaks[nBreak - mnMinCharPos], mvCharBreaks[nBreak - mnMinCharPos - 1]);
729 : #endif
730 :
731 0 : if (nBreak > mnEndCharPos) nBreak = STRING_LEN;
732 0 : else if (nBreak < mnMinCharPos) nBreak = mnMinCharPos;
733 0 : return nBreak;
734 : }
735 :
736 0 : long GraphiteLayout::FillDXArray( sal_Int32* pDXArray ) const
737 : {
738 0 : if (mnEndCharPos == mnMinCharPos)
739 : // Then we must be zero width!
740 0 : return 0;
741 :
742 0 : if (pDXArray)
743 : {
744 0 : for (size_t i = 0; i < mvCharDxs.size(); i++)
745 : {
746 : assert( (mvChar2BaseGlyph[i] == -1) ||
747 : ((signed)(mvChar2BaseGlyph[i]) < (signed)mvGlyphs.size()));
748 0 : if (mvChar2BaseGlyph[i] != -1 &&
749 0 : mvGlyphs[mvChar2BaseGlyph[i]].mnGlyphIndex == GF_DROPPED)
750 : {
751 : // when used in MultiSalLayout::GetTextBreak dropped glyphs
752 : // must have zero width
753 0 : pDXArray[i] = 0;
754 : }
755 : else
756 : {
757 0 : pDXArray[i] = mvCharDxs[i];
758 0 : if (i > 0) pDXArray[i] -= mvCharDxs[i-1];
759 : }
760 : #ifdef GRLAYOUT_DEBUG
761 : fprintf(grLog(),"%d,%d,%d ", (int)i, (int)mvCharDxs[i], pDXArray[i]);
762 : #endif
763 : }
764 : //std::adjacent_difference(mvCharDxs.begin(), mvCharDxs.end(), pDXArray);
765 : //for (size_t i = 0; i < mvCharDxs.size(); i++)
766 : // fprintf(grLog(),"%d,%d,%d ", (int)i, (int)mvCharDxs[i], pDXArray[i]);
767 : //fprintf(grLog(),"FillDX %ld,%d\n", mnWidth, std::accumulate(pDXArray, pDXArray + mvCharDxs.size(), 0));
768 : }
769 : #ifdef GRLAYOUT_DEBUG
770 : fprintf(grLog(),"FillDXArray %d-%d=%ld\n", mnMinCharPos, mnEndCharPos, mnWidth);
771 : #endif
772 0 : return mnWidth;
773 : }
774 :
775 0 : void GraphiteLayout::AdjustLayout(ImplLayoutArgs& rArgs)
776 : {
777 0 : SalLayout::AdjustLayout(rArgs);
778 0 : if(rArgs.mpDXArray)
779 : {
780 0 : std::vector<int> vDeltaWidths(mvGlyphs.size(), 0);
781 0 : ApplyDXArray(rArgs, vDeltaWidths);
782 :
783 0 : if( (mnLayoutFlags & SAL_LAYOUT_BIDI_RTL) &&
784 0 : !(rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK) )
785 : {
786 : // check if this is a kashida script
787 0 : bool bKashidaScript = false;
788 0 : for (int i = rArgs.mnMinCharPos; i < rArgs.mnEndCharPos; i++)
789 : {
790 0 : UErrorCode aStatus = U_ZERO_ERROR;
791 0 : UScriptCode scriptCode = uscript_getScript(rArgs.mpStr[i], &aStatus);
792 0 : if (scriptCode == USCRIPT_ARABIC || scriptCode == USCRIPT_SYRIAC)
793 : {
794 0 : bKashidaScript = true;
795 : break;
796 : }
797 : }
798 0 : int nKashidaWidth = 0;
799 0 : int nKashidaIndex = getKashidaGlyph(nKashidaWidth);
800 0 : if( nKashidaIndex != 0 && bKashidaScript)
801 : {
802 0 : kashidaJustify( vDeltaWidths, nKashidaIndex, nKashidaWidth );
803 : }
804 0 : }
805 : }
806 0 : else if (rArgs.mnLayoutWidth > 0)
807 : {
808 : #ifdef GRLAYOUT_DEBUG
809 : fprintf(grLog(), "AdjustLayout width %ld=>%ld\n", mnWidth, rArgs.mnLayoutWidth);
810 : #endif
811 0 : expandOrCondense(rArgs);
812 : }
813 0 : }
814 :
815 0 : void GraphiteLayout::expandOrCondense(ImplLayoutArgs &rArgs)
816 : {
817 0 : int nDeltaWidth = rArgs.mnLayoutWidth - mnWidth;
818 0 : if (nDeltaWidth > 0) // expand, just expand between clusters
819 : {
820 : // NOTE: for expansion we can use base glyphs (which have IsClusterStart set)
821 : // even though they may have been reordered in which case they will have
822 : // been placed in a bigger cluster for other purposes.
823 0 : int nClusterCount = 0;
824 0 : for (size_t j = 0; j < mvGlyphs.size(); j++)
825 : {
826 0 : if (mvGlyphs[j].IsClusterStart())
827 : {
828 0 : ++nClusterCount;
829 : }
830 : }
831 0 : if (nClusterCount > 1)
832 : {
833 0 : float fExtraPerCluster = static_cast<float>(nDeltaWidth) / static_cast<float>(nClusterCount - 1);
834 0 : int nCluster = 0;
835 0 : int nOffset = 0;
836 0 : for (size_t i = 0; i < mvGlyphs.size(); i++)
837 : {
838 0 : if (mvGlyphs[i].IsClusterStart())
839 : {
840 0 : nOffset = static_cast<int>(fExtraPerCluster * nCluster);
841 0 : int nCharIndex = mvGlyph2Char[i];
842 : assert(nCharIndex > -1);
843 0 : if (nCharIndex < mnMinCharPos ||
844 : static_cast<size_t>(nCharIndex-mnMinCharPos)
845 0 : >= mvCharDxs.size())
846 : {
847 0 : continue;
848 : }
849 0 : mvCharDxs[nCharIndex-mnMinCharPos] += nOffset;
850 : // adjust char dxs for rest of characters in cluster
851 0 : while (++nCharIndex - mnMinCharPos < static_cast<int>(mvChar2BaseGlyph.size()))
852 : {
853 0 : int nChar2Base = mvChar2BaseGlyph[nCharIndex-mnMinCharPos];
854 0 : if (nChar2Base == -1 || nChar2Base == static_cast<int>(i))
855 0 : mvCharDxs[nCharIndex-mnMinCharPos] += nOffset;
856 : else
857 0 : break;
858 : }
859 0 : ++nCluster;
860 : }
861 0 : mvGlyphs[i].maLinearPos.X() += nOffset;
862 : }
863 : }
864 : }
865 0 : else if (nDeltaWidth < 0)// condense - apply a factor to all glyph positions
866 : {
867 0 : if (mvGlyphs.empty()) return;
868 0 : Glyphs::iterator iLastGlyph = mvGlyphs.begin() + (mvGlyphs.size() - 1);
869 : // position last glyph using original width
870 0 : float fXFactor = static_cast<float>(rArgs.mnLayoutWidth - iLastGlyph->mnOrigWidth) / static_cast<float>(iLastGlyph->maLinearPos.X());
871 : #ifdef GRLAYOUT_DEBUG
872 : fprintf(grLog(), "Condense by factor %f last x%ld\n", fXFactor, iLastGlyph->maLinearPos.X());
873 : #endif
874 0 : if (fXFactor < 0)
875 : return; // probably a bad mnOrigWidth value
876 0 : iLastGlyph->maLinearPos.X() = rArgs.mnLayoutWidth - iLastGlyph->mnOrigWidth;
877 0 : Glyphs::iterator iGlyph = mvGlyphs.begin();
878 0 : while (iGlyph != iLastGlyph)
879 : {
880 0 : iGlyph->maLinearPos.X() = static_cast<int>(static_cast<float>(iGlyph->maLinearPos.X()) * fXFactor);
881 0 : ++iGlyph;
882 : }
883 0 : for (size_t i = 0; i < mvCharDxs.size(); i++)
884 : {
885 0 : mvCharDxs[i] = static_cast<int>(fXFactor * static_cast<float>(mvCharDxs[i]));
886 : }
887 : }
888 0 : mnWidth = rArgs.mnLayoutWidth;
889 : }
890 :
891 0 : void GraphiteLayout::ApplyDXArray(ImplLayoutArgs &args, std::vector<int> & rDeltaWidth)
892 : {
893 0 : const size_t nChars = args.mnEndCharPos - args.mnMinCharPos;
894 0 : if (nChars == 0) return;
895 :
896 : #ifdef GRLAYOUT_DEBUG
897 : for (size_t iDx = 0; iDx < mvCharDxs.size(); iDx++)
898 : fprintf(grLog(),"%d,%d,%d ", (int)iDx, (int)mvCharDxs[iDx], args.mpDXArray[iDx]);
899 : fprintf(grLog(),"ApplyDx\n");
900 : #endif
901 0 : bool bRtl = mnLayoutFlags & SAL_LAYOUT_BIDI_RTL;
902 0 : int nXOffset = 0;
903 0 : if (bRtl)
904 : {
905 0 : nXOffset = args.mpDXArray[nChars - 1] - mvCharDxs[nChars - 1];
906 : }
907 0 : int nPrevClusterGlyph = (bRtl)? (signed)mvGlyphs.size() : -1;
908 0 : int nPrevClusterLastChar = -1;
909 0 : for (size_t i = 0; i < nChars; i++)
910 : {
911 0 : int nChar2Base = mvChar2BaseGlyph[i];
912 0 : if ((nChar2Base > -1) && (nChar2Base != nPrevClusterGlyph))
913 : {
914 : assert((nChar2Base > -1) && (nChar2Base < (signed)mvGlyphs.size()));
915 0 : GlyphItem & gi = mvGlyphs[nChar2Base];
916 0 : if (!gi.IsClusterStart())
917 0 : continue;
918 :
919 : // find last glyph of this cluster
920 0 : size_t j = i + 1;
921 0 : int nLastChar = i;
922 0 : int nLastGlyph = nChar2Base;
923 0 : int nChar2BaseJ = -1;
924 0 : for (; j < nChars; j++)
925 : {
926 0 : nChar2BaseJ = mvChar2BaseGlyph[j];
927 : assert((nChar2BaseJ >= -1) && (nChar2BaseJ < (signed)mvGlyphs.size()));
928 0 : if (nChar2BaseJ != -1 )
929 : {
930 0 : nLastGlyph = nChar2BaseJ + ((bRtl)? +1 : -1);
931 0 : nLastChar = j - 1;
932 0 : break;
933 : }
934 : }
935 0 : if (nLastGlyph < 0)
936 : {
937 0 : nLastGlyph = nChar2Base;
938 : }
939 : // Its harder to find the last glyph rtl, since the first of
940 : // cluster is still on the left so we need to search towards
941 : // the previous cluster to the right
942 0 : if (bRtl)
943 : {
944 0 : nLastGlyph = nChar2Base;
945 0 : while (nLastGlyph + 1 < (signed)mvGlyphs.size() &&
946 0 : !mvGlyphs[nLastGlyph+1].IsClusterStart())
947 : {
948 0 : ++nLastGlyph;
949 : }
950 : }
951 0 : if (j == nChars)
952 : {
953 0 : nLastChar = nChars - 1;
954 0 : if (!bRtl) nLastGlyph = mvGlyphs.size() - 1;
955 : }
956 0 : int nBaseCount = 0;
957 : // count bases within cluster - may be more than 1 with reordering
958 0 : for (int k = nChar2Base; k <= nLastGlyph; k++)
959 : {
960 0 : if (mvGlyphs[k].IsClusterStart()) ++nBaseCount;
961 : }
962 : assert((nLastChar > -1) && (nLastChar < (signed)nChars));
963 0 : long nNewClusterWidth = args.mpDXArray[nLastChar];
964 0 : long nOrigClusterWidth = mvCharDxs[nLastChar];
965 0 : long nDGlyphOrigin = 0;
966 0 : if (nPrevClusterLastChar > - 1)
967 : {
968 : assert(nPrevClusterLastChar < (signed)nChars);
969 0 : nNewClusterWidth -= args.mpDXArray[nPrevClusterLastChar];
970 0 : nOrigClusterWidth -= mvCharDxs[nPrevClusterLastChar];
971 0 : nDGlyphOrigin = args.mpDXArray[nPrevClusterLastChar] - mvCharDxs[nPrevClusterLastChar];
972 : }
973 0 : long nDWidth = nNewClusterWidth - nOrigClusterWidth;
974 : #ifdef GRLAYOUT_DEBUG
975 : fprintf(grLog(), "c%lu last glyph %d/%lu\n", i, nLastGlyph, mvGlyphs.size());
976 : #endif
977 : assert((nLastGlyph > -1) && (nLastGlyph < (signed)mvGlyphs.size()));
978 0 : mvGlyphs[nLastGlyph].mnNewWidth += nDWidth;
979 0 : if (gi.mnGlyphIndex != GF_DROPPED)
980 0 : mvGlyphs[nLastGlyph].mnNewWidth += nDWidth;
981 : else
982 0 : nDGlyphOrigin += nDWidth;
983 0 : long nDOriginPerBase = (nBaseCount > 0)? nDWidth / nBaseCount : 0;
984 0 : nBaseCount = -1;
985 : // update glyph positions
986 0 : if (bRtl)
987 : {
988 0 : for (int n = nChar2Base; n <= nLastGlyph; n++)
989 : {
990 0 : if (mvGlyphs[n].IsClusterStart()) ++nBaseCount;
991 : assert((n > - 1) && (n < (signed)mvGlyphs.size()));
992 0 : mvGlyphs[n].maLinearPos.X() += -(nDGlyphOrigin + nDOriginPerBase * nBaseCount) + nXOffset;
993 : }
994 : }
995 : else
996 : {
997 0 : for (int n = nChar2Base; n <= nLastGlyph; n++)
998 : {
999 0 : if (mvGlyphs[n].IsClusterStart()) ++nBaseCount;
1000 : assert((n > - 1) && (n < (signed)mvGlyphs.size()));
1001 0 : mvGlyphs[n].maLinearPos.X() += nDGlyphOrigin + (nDOriginPerBase * nBaseCount) + nXOffset;
1002 : }
1003 : }
1004 0 : rDeltaWidth[nChar2Base] = nDWidth;
1005 : #ifdef GRLAYOUT_DEBUG
1006 : 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());
1007 : #endif
1008 0 : nPrevClusterGlyph = nChar2Base;
1009 0 : nPrevClusterLastChar = nLastChar;
1010 0 : i = nLastChar;
1011 : }
1012 : }
1013 : // Update the dx vector with the new values.
1014 : std::copy(args.mpDXArray, args.mpDXArray + nChars,
1015 0 : mvCharDxs.begin() + (args.mnMinCharPos - mnMinCharPos));
1016 : #ifdef GRLAYOUT_DEBUG
1017 : fprintf(grLog(),"ApplyDx %d(%ld)\n", args.mpDXArray[nChars - 1], mnWidth);
1018 : #endif
1019 0 : mnWidth = args.mpDXArray[nChars - 1];
1020 : }
1021 :
1022 0 : void GraphiteLayout::kashidaJustify(std::vector<int>& rDeltaWidths, sal_GlyphId nKashidaIndex, int nKashidaWidth)
1023 : {
1024 : // skip if the kashida glyph in the font looks suspicious
1025 0 : if( nKashidaWidth <= 0 )
1026 0 : return;
1027 :
1028 : // calculate max number of needed kashidas
1029 0 : Glyphs::iterator i = mvGlyphs.begin();
1030 0 : int nKashidaCount = 0;
1031 0 : int nOrigGlyphIndex = -1;
1032 0 : int nGlyphIndex = -1;
1033 0 : while (i != mvGlyphs.end())
1034 : {
1035 0 : nOrigGlyphIndex++;
1036 0 : nGlyphIndex++;
1037 : // only inject kashidas in RTL contexts
1038 0 : if( !(*i).IsRTLGlyph() )
1039 : {
1040 0 : ++i;
1041 0 : continue;
1042 : }
1043 : // no kashida-injection for blank justified expansion either
1044 0 : if( IsSpacingGlyph( (*i).mnGlyphIndex ) )
1045 : {
1046 0 : ++i;
1047 0 : continue;
1048 : }
1049 : // calculate gap, ignore if too small
1050 0 : int nGapWidth = rDeltaWidths[nOrigGlyphIndex];
1051 : // worst case is one kashida even for mini-gaps
1052 0 : if( 3 * nGapWidth < nKashidaWidth )
1053 : {
1054 0 : ++i;
1055 0 : continue;
1056 : }
1057 0 : nKashidaCount = 1 + (nGapWidth / nKashidaWidth);
1058 : #ifdef GRLAYOUT_DEBUG
1059 : printf("inserting %d kashidas at %u\n", nKashidaCount, (*i).mnGlyphIndex);
1060 : #endif
1061 0 : GlyphItem glyphItem = *i;
1062 0 : Point aPos(0, 0);
1063 0 : aPos.X() = (*i).maLinearPos.X();
1064 : GlyphItem newGi(glyphItem.mnCharPos, nKashidaIndex, aPos,
1065 0 : GlyphItem::IS_IN_CLUSTER|GlyphItem::IS_RTL_GLYPH, nKashidaWidth);
1066 0 : mvGlyphs.reserve(mvGlyphs.size() + nKashidaCount);
1067 0 : i = mvGlyphs.begin() + nGlyphIndex;
1068 0 : mvGlyphs.insert(i, nKashidaCount, newGi);
1069 0 : i = mvGlyphs.begin() + nGlyphIndex;
1070 0 : nGlyphIndex += nKashidaCount;
1071 : // now fix up the kashida positions
1072 0 : for (int j = 0; j < nKashidaCount; j++)
1073 : {
1074 0 : (*(i)).maLinearPos.X() -= nGapWidth;
1075 0 : nGapWidth -= nKashidaWidth;
1076 0 : ++i;
1077 : }
1078 :
1079 : // fixup rightmost kashida for gap remainder
1080 0 : if( nGapWidth < 0 )
1081 : {
1082 0 : if( nKashidaCount <= 1 )
1083 0 : nGapWidth /= 2; // for small gap move kashida to middle
1084 0 : (*(i-1)).mnNewWidth += nGapWidth; // adjust kashida width to gap width
1085 0 : (*(i-1)).maLinearPos.X() += nGapWidth;
1086 : }
1087 :
1088 0 : (*i).mnNewWidth = (*i).mnOrigWidth;
1089 0 : ++i;
1090 : }
1091 :
1092 : }
1093 :
1094 0 : void GraphiteLayout::GetCaretPositions( int nArraySize, sal_Int32* pCaretXArray ) const
1095 : {
1096 : // For each character except the last discover the caret positions
1097 : // immediatly before and after that character.
1098 : // This is used for underlines in the GUI amongst other things.
1099 : // It may be used from MultiSalLayout, in which case it must take into account
1100 : // glyphs that have been moved.
1101 0 : std::fill(pCaretXArray, pCaretXArray + nArraySize, -1);
1102 : // the layout method doesn't modify the layout even though it isn't
1103 : // const in the interface
1104 0 : bool bRtl = (mnLayoutFlags & SAL_LAYOUT_BIDI_RTL);//const_cast<GraphiteLayout*>(this)->maLayout.rightToLeft();
1105 0 : int prevBase = -1;
1106 0 : long prevClusterWidth = 0;
1107 0 : for (int i = 0, nCharSlot = 0; i < nArraySize && nCharSlot < static_cast<int>(mvCharDxs.size()); ++nCharSlot, i+=2)
1108 : {
1109 0 : if (mvChar2BaseGlyph[nCharSlot] != -1)
1110 : {
1111 0 : int nChar2Base = mvChar2BaseGlyph[nCharSlot];
1112 : assert((nChar2Base > -1) && (nChar2Base < (signed)mvGlyphs.size()));
1113 0 : GlyphItem gi = mvGlyphs[nChar2Base];
1114 0 : if (gi.mnGlyphIndex == GF_DROPPED)
1115 : {
1116 0 : continue;
1117 : }
1118 0 : int nCluster = nChar2Base;
1119 0 : long origClusterWidth = gi.mnNewWidth;
1120 0 : long nMin = gi.maLinearPos.X();
1121 0 : long nMax = gi.maLinearPos.X() + gi.mnNewWidth;
1122 : // attached glyphs are always stored after their base rtl or ltr
1123 0 : while (++nCluster < static_cast<int>(mvGlyphs.size()) &&
1124 0 : !mvGlyphs[nCluster].IsClusterStart())
1125 : {
1126 0 : origClusterWidth += mvGlyphs[nCluster].mnNewWidth;
1127 0 : if (mvGlyph2Char[nCluster] == nCharSlot)
1128 : {
1129 0 : nMin = minimum(nMin, mvGlyphs[nCluster].maLinearPos.X());
1130 0 : nMax = maximum(nMax, mvGlyphs[nCluster].maLinearPos.X() + mvGlyphs[nCluster].mnNewWidth);
1131 : }
1132 : }
1133 0 : if (bRtl)
1134 : {
1135 0 : pCaretXArray[i+1] = nMin;
1136 0 : pCaretXArray[i] = nMax;
1137 : }
1138 : else
1139 : {
1140 0 : pCaretXArray[i] = nMin;
1141 0 : pCaretXArray[i+1] = nMax;
1142 : }
1143 0 : prevBase = nChar2Base;
1144 0 : prevClusterWidth = origClusterWidth;
1145 : }
1146 0 : else if (prevBase > -1)
1147 : {
1148 : // this could probably be improved
1149 : assert((prevBase > -1) && (prevBase < (signed)mvGlyphs.size()));
1150 0 : GlyphItem gi = mvGlyphs[prevBase];
1151 0 : int nGlyph = prevBase + 1;
1152 : // try to find a better match, otherwise default to complete cluster
1153 0 : for (; nGlyph < static_cast<int>(mvGlyphs.size()) &&
1154 0 : !mvGlyphs[nGlyph].IsClusterStart(); nGlyph++)
1155 : {
1156 0 : if (mvGlyph2Char[nGlyph] == nCharSlot)
1157 : {
1158 0 : gi = mvGlyphs[nGlyph];
1159 0 : break;
1160 : }
1161 : }
1162 : // if no match position at end of cluster
1163 0 : if (nGlyph == static_cast<int>(mvGlyphs.size()) ||
1164 0 : mvGlyphs[nGlyph].IsClusterStart())
1165 : {
1166 0 : if (bRtl)
1167 : {
1168 0 : pCaretXArray[i+1] = gi.maLinearPos.X();
1169 0 : pCaretXArray[i] = gi.maLinearPos.X();
1170 : }
1171 : else
1172 : {
1173 0 : pCaretXArray[i] = gi.maLinearPos.X() + prevClusterWidth;
1174 0 : pCaretXArray[i+1] = gi.maLinearPos.X() + prevClusterWidth;
1175 : }
1176 : }
1177 : else
1178 : {
1179 0 : if (bRtl)
1180 : {
1181 0 : pCaretXArray[i+1] = gi.maLinearPos.X();
1182 0 : pCaretXArray[i] = gi.maLinearPos.X() + gi.mnNewWidth;
1183 : }
1184 : else
1185 : {
1186 0 : pCaretXArray[i] = gi.maLinearPos.X();
1187 0 : pCaretXArray[i+1] = gi.maLinearPos.X() + gi.mnNewWidth;
1188 : }
1189 : }
1190 : }
1191 : else
1192 : {
1193 0 : pCaretXArray[i] = pCaretXArray[i+1] = 0;
1194 : }
1195 : #ifdef GRLAYOUT_DEBUG
1196 : fprintf(grLog(),"%d,%d-%d\t", nCharSlot, pCaretXArray[i], pCaretXArray[i+1]);
1197 : #endif
1198 : }
1199 : #ifdef GRLAYOUT_DEBUG
1200 : fprintf(grLog(),"\n");
1201 : #endif
1202 0 : }
1203 :
1204 : // GetNextGlyphs returns a contiguous sequence of glyphs that can be
1205 : // rendered together. It should never return a dropped glyph.
1206 : // The glyph_slot returned should be the index of the next visible
1207 : // glyph after the last glyph returned by this call.
1208 : // The char_index array should be filled with the characters corresponding
1209 : // to each glyph returned.
1210 : // glyph_adv array should be a virtual width such that if successive
1211 : // glyphs returned by this method are added one after the other they
1212 : // have the correct spacing.
1213 : // The logic in this method must match that expected in MultiSalLayout which
1214 : // is used when glyph fallback is in operation.
1215 0 : int GraphiteLayout::GetNextGlyphs( int length, sal_GlyphId * glyph_out,
1216 : ::Point & aPosOut, int &glyph_slot, sal_Int32 * glyph_adv, int *char_index) const
1217 : {
1218 : // Sanity check on the slot index.
1219 0 : if (glyph_slot >= signed(mvGlyphs.size()))
1220 : {
1221 0 : glyph_slot = mvGlyphs.size();
1222 0 : return 0;
1223 : }
1224 : assert(glyph_slot >= 0);
1225 : // Find the first glyph in the substring.
1226 0 : for (; glyph_slot < signed(mvGlyphs.size()) &&
1227 0 : ((mvGlyphs.begin() + glyph_slot)->mnGlyphIndex == GF_DROPPED);
1228 : ++glyph_slot) {};
1229 :
1230 : // Update the length
1231 0 : const int nGlyphSlotEnd = minimum(size_t(glyph_slot + length), mvGlyphs.size());
1232 :
1233 : // We're all out of glyphs here.
1234 0 : if (glyph_slot == nGlyphSlotEnd)
1235 : {
1236 0 : return 0;
1237 : }
1238 :
1239 : // Find as many glyphs as we can which can be drawn in one go.
1240 0 : Glyphs::const_iterator glyph_itr = mvGlyphs.begin() + glyph_slot;
1241 0 : const int glyph_slot_begin = glyph_slot;
1242 0 : const int initial_y_pos = glyph_itr->maLinearPos.Y();
1243 :
1244 : // Set the position to the position of the start glyph.
1245 0 : ::Point aStartPos = glyph_itr->maLinearPos;
1246 : //aPosOut = glyph_itr->maLinearPos;
1247 0 : aPosOut = GetDrawPosition(aStartPos);
1248 :
1249 0 : for (;;) // Forever
1250 : {
1251 : // last index of the range from glyph_to_chars does not include this glyph
1252 0 : if (char_index)
1253 : {
1254 0 : if (glyph_slot >= (signed)mvGlyph2Char.size())
1255 : {
1256 0 : *char_index++ = mnMinCharPos + mvCharDxs.size();
1257 : }
1258 : else
1259 : {
1260 : assert(glyph_slot > -1);
1261 0 : if (mvGlyph2Char[glyph_slot] == -1)
1262 0 : *char_index++ = mnMinCharPos + mvCharDxs.size();
1263 : else
1264 0 : *char_index++ = mvGlyph2Char[glyph_slot];
1265 : }
1266 : }
1267 : // Copy out this glyphs data.
1268 0 : ++glyph_slot;
1269 0 : *glyph_out++ = glyph_itr->mnGlyphIndex;
1270 :
1271 : // Find the actual advance - this must be correct if called from
1272 : // MultiSalLayout::AdjustLayout which requests one glyph at a time.
1273 0 : const long nGlyphAdvance = (glyph_slot == static_cast<int>(mvGlyphs.size()))?
1274 0 : glyph_itr->mnNewWidth :
1275 0 : ((glyph_itr+1)->maLinearPos.X() - glyph_itr->maLinearPos.X());
1276 :
1277 : #ifdef GRLAYOUT_DEBUG
1278 : fprintf(grLog(),"GetNextGlyphs g%d gid%d c%d x%ld,%ld adv%ld, pos %ld,%ld\n",
1279 : glyph_slot - 1, glyph_itr->mnGlyphIndex,
1280 : mvGlyph2Char[glyph_slot-1], glyph_itr->maLinearPos.X(), glyph_itr->maLinearPos.Y(), nGlyphAdvance,
1281 : aPosOut.X(), aPosOut.Y());
1282 : #endif
1283 :
1284 0 : if (glyph_adv) // If we are returning advance store it.
1285 0 : *glyph_adv++ = nGlyphAdvance;
1286 : else // Stop when next advance is unexpected.
1287 0 : if (glyph_itr->mnOrigWidth != nGlyphAdvance) break;
1288 :
1289 : // Have fetched all the glyphs we need to
1290 0 : if (glyph_slot == nGlyphSlotEnd)
1291 0 : break;
1292 :
1293 0 : ++glyph_itr;
1294 : // Stop when next y position is unexpected.
1295 0 : if (initial_y_pos != glyph_itr->maLinearPos.Y())
1296 0 : break;
1297 :
1298 : // Stop if glyph dropped
1299 0 : if (glyph_itr->mnGlyphIndex == GF_DROPPED)
1300 0 : break;
1301 : }
1302 0 : int numGlyphs = glyph_slot - glyph_slot_begin;
1303 : // move the next glyph_slot to a glyph that hasn't been dropped
1304 0 : while (glyph_slot < static_cast<int>(mvGlyphs.size()) &&
1305 0 : (mvGlyphs.begin() + glyph_slot)->mnGlyphIndex == GF_DROPPED)
1306 0 : ++glyph_slot;
1307 0 : return numGlyphs;
1308 : }
1309 :
1310 0 : void GraphiteLayout::MoveGlyph( int nGlyphIndex, long nNewPos )
1311 : {
1312 : // TODO it might be better to actualy implement simplify properly, but this
1313 : // needs to be done carefully so the glyph/char maps are maintained
1314 : // If a glyph has been dropped then it wasn't returned by GetNextGlyphs, so
1315 : // the index here may be wrong
1316 0 : while ((mvGlyphs[nGlyphIndex].mnGlyphIndex == GF_DROPPED) &&
1317 0 : (nGlyphIndex < (signed)mvGlyphs.size()))
1318 : {
1319 0 : nGlyphIndex++;
1320 : }
1321 0 : const long dx = nNewPos - mvGlyphs[nGlyphIndex].maLinearPos.X();
1322 :
1323 0 : if (dx == 0) return;
1324 : // GenericSalLayout only changes maLinearPos, mvCharDxs doesn't change
1325 : #ifdef GRLAYOUT_DEBUG
1326 : fprintf(grLog(),"Move %d (%ld,%ld) c%d by %ld\n", nGlyphIndex, mvGlyphs[nGlyphIndex].maLinearPos.X(), nNewPos, mvGlyph2Char[nGlyphIndex], dx);
1327 : #endif
1328 0 : for (size_t gi = nGlyphIndex; gi < mvGlyphs.size(); gi++)
1329 : {
1330 0 : mvGlyphs[gi].maLinearPos.X() += dx;
1331 : }
1332 : // width does need to be updated for correct fallback
1333 0 : mnWidth += dx;
1334 : }
1335 :
1336 0 : void GraphiteLayout::DropGlyph( int nGlyphIndex )
1337 : {
1338 0 : if(nGlyphIndex >= signed(mvGlyphs.size()))
1339 0 : return;
1340 :
1341 0 : GlyphItem & glyph = mvGlyphs[nGlyphIndex];
1342 0 : glyph.mnGlyphIndex = GF_DROPPED;
1343 : #ifdef GRLAYOUT_DEBUG
1344 : fprintf(grLog(),"Dropped %d\n", nGlyphIndex);
1345 : #endif
1346 : }
1347 :
1348 0 : void GraphiteLayout::Simplify( bool isBaseLayout )
1349 : {
1350 0 : const sal_GlyphId dropMarker = isBaseLayout ? GF_DROPPED : 0;
1351 :
1352 0 : Glyphs::iterator gi = mvGlyphs.begin();
1353 : // TODO check whether we need to adjust positions here
1354 : // MultiSalLayout seems to move the glyphs itself, so it may not be needed.
1355 0 : long deltaX = 0;
1356 0 : while (gi != mvGlyphs.end())
1357 : {
1358 0 : if (gi->mnGlyphIndex == dropMarker)
1359 : {
1360 0 : deltaX += gi->mnNewWidth;
1361 0 : gi->mnNewWidth = 0;
1362 : }
1363 : else
1364 : {
1365 0 : deltaX = 0;
1366 : }
1367 0 : ++gi;
1368 : }
1369 : #ifdef GRLAYOUT_DEBUG
1370 : fprintf(grLog(),"Simplify base%d dx=%ld newW=%ld\n", isBaseLayout, deltaX, mnWidth - deltaX);
1371 : #endif
1372 : // discard width from trailing dropped glyphs, but not those in the middle
1373 0 : mnWidth -= deltaX;
1374 0 : }
1375 :
1376 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|