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