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 : // Enable lots of debug info
24 : #if OSL_DEBUG_LEVEL > 1
25 : #include <cstdio>
26 : #define GRLAYOUT_DEBUG 1
27 : #undef NDEBUG
28 : #endif
29 :
30 : // #define GRLAYOUT_DEBUG 1
31 :
32 : // Header files
33 :
34 : // Standard Library
35 : #include <algorithm>
36 : #include <cassert>
37 : #include <functional>
38 : #include <limits>
39 : #include <numeric>
40 : #include <deque>
41 :
42 : // Platform
43 : #include <svsys.h>
44 :
45 : #include <salgdi.hxx>
46 :
47 : #include <unicode/uchar.h>
48 : #include <unicode/ubidi.h>
49 : #include <unicode/uscript.h>
50 :
51 : #include <vcl/unohelp.hxx>
52 : #include <com/sun/star/i18n/XCharacterClassification.hpp>
53 : #include <com/sun/star/i18n/UnicodeType.hpp>
54 :
55 : // Graphite Libraries (must be after vcl headers on windows)
56 : #include <graphite_static.hxx>
57 : #include <graphite2/Segment.h>
58 :
59 : #include <graphite_layout.hxx>
60 : #include <graphite_features.hxx>
61 :
62 : // Module private type definitions and forward declarations.
63 :
64 : // Module private names.
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 0 : lastChar < iChar && clusterAfter &&
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 0 : 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 0 : 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 0 : 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 0 : 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 0 : 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 : // An implementation of the SalLayout interface to enable Graphite enabled fonts to be used.
491 :
492 0 : GraphiteLayout::GraphiteLayout(const gr_face * face, gr_font * font,
493 : const grutils::GrFeatureParser * pFeatures) throw()
494 : : mpFace(face)
495 : , mpFont(font)
496 : , mnSegCharOffset(0)
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 : bool success = true;
530 0 : if (rArgs.mnMinCharPos < rArgs.mnEndCharPos)
531 : {
532 0 : gr_segment * pSegment = CreateSegment(rArgs);
533 0 : if (!pSegment)
534 0 : return false;
535 0 : success = LayoutGlyphs(rArgs, pSegment);
536 0 : if (pSegment)
537 : {
538 0 : gr_seg_destroy(pSegment);
539 0 : pSegment = NULL;
540 : }
541 : }
542 : else
543 : {
544 0 : clear();
545 : }
546 0 : return success;
547 : }
548 :
549 0 : gr_segment * GraphiteLayout::CreateSegment(ImplLayoutArgs& rArgs)
550 : {
551 : assert(rArgs.mnLength >= 0);
552 :
553 0 : gr_segment * pSegment = NULL;
554 :
555 : // Set the SalLayouts values to be the initial ones.
556 0 : SalLayout::AdjustLayout(rArgs);
557 : // TODO check if this is needed
558 0 : if (mnUnitsPerPixel > 1)
559 0 : mfScaling = 1.0f / mnUnitsPerPixel;
560 :
561 : // Clear out any previous buffers
562 0 : clear();
563 0 : bool bRtl = mnLayoutFlags & SAL_LAYOUT_BIDI_RTL;
564 : try
565 : {
566 : // Don't set RTL if font doesn't support it otherwise it forces rtl on
567 : // everything
568 : //if (bRtl && (mrFont.getSupportedScriptDirections() & gr::kfsdcHorizRtl))
569 : // maLayout.setRightToLeft(bRtl);
570 :
571 : // Context is often needed beyond the specified end, however, we don't
572 : // want it if there has been a direction change, since it is hard
573 : // to tell between reordering within one direction and multi-directional
574 : // text. Extra context, can also cause problems with ligatures stradling
575 : // a hyphenation point, so disable if CTL is disabled.
576 0 : mnSegCharOffset = rArgs.mnMinCharPos;
577 0 : int limit = rArgs.mnEndCharPos;
578 0 : if (!(SAL_LAYOUT_COMPLEX_DISABLED & rArgs.mnFlags))
579 : {
580 0 : const int nSegCharMin = maximum<int>(0, mnMinCharPos - EXTRA_CONTEXT_LENGTH);
581 0 : const int nSegCharLimit = minimum(rArgs.mnLength, mnEndCharPos + EXTRA_CONTEXT_LENGTH);
582 0 : if (nSegCharMin < mnSegCharOffset)
583 : {
584 : int sameDirEnd = findSameDirLimit(rArgs.mpStr + nSegCharMin,
585 0 : rArgs.mnEndCharPos - nSegCharMin, bRtl);
586 0 : if (sameDirEnd == rArgs.mnEndCharPos)
587 0 : mnSegCharOffset = nSegCharMin;
588 : }
589 0 : if (nSegCharLimit > limit)
590 : {
591 : limit += findSameDirLimit(rArgs.mpStr + rArgs.mnEndCharPos,
592 0 : nSegCharLimit - rArgs.mnEndCharPos, bRtl);
593 : }
594 : }
595 :
596 0 : size_t numchars = gr_count_unicode_characters(gr_utf16, rArgs.mpStr + mnSegCharOffset,
597 0 : rArgs.mpStr + (rArgs.mnLength > limit + 64 ? limit + 64 : rArgs.mnLength), NULL);
598 0 : static com::sun::star::uno::Reference< com::sun::star::i18n::XCharacterClassification > xCharClass;
599 0 : if ( !xCharClass.is() )
600 0 : xCharClass = vcl::unohelper::CreateCharacterClassification();
601 0 : size_t numchars2 = rArgs.mnEndCharPos - mnSegCharOffset; // fdo#52540, fdo#68313, fdo#70666 avoid bad ligature replacement
602 0 : if (numchars > numchars2 && xCharClass->getType(rArgs.mpStr, numchars2 + 1) == ::com::sun::star::i18n::UnicodeType::LOWERCASE_LETTER)
603 0 : numchars = numchars2;
604 0 : if (mpFeatures)
605 0 : pSegment = gr_make_seg(mpFont, mpFace, 0, mpFeatures->values(), gr_utf16,
606 0 : rArgs.mpStr + mnSegCharOffset, numchars, bRtl);
607 : else
608 : pSegment = gr_make_seg(mpFont, mpFace, 0, NULL, gr_utf16,
609 0 : rArgs.mpStr + mnSegCharOffset, numchars, bRtl);
610 :
611 : //pSegment = new gr::RangeSegment((gr::Font *)&mrFont, mpTextSrc, &maLayout, mnMinCharPos, limit);
612 0 : if (pSegment != NULL)
613 : {
614 : #ifdef GRLAYOUT_DEBUG
615 : fprintf(grLog(),"Gr::LayoutText %d-%d, context %d, len %d, numchars %d, rtl %d scaling %f:", rArgs.mnMinCharPos,
616 : rArgs.mnEndCharPos, limit, rArgs.mnLength, numchars, bRtl, mfScaling);
617 : for (int i = mnSegCharOffset; i < limit; ++i)
618 : fprintf(grLog(), " %04X", rArgs.mpStr[i]);
619 : fprintf(grLog(), "\n");
620 : #endif
621 : }
622 : else
623 : {
624 : #ifdef GRLAYOUT_DEBUG
625 : fprintf(grLog(), "Gr::LayoutText failed: ");
626 : for (int i = mnMinCharPos; i < limit; i++)
627 : {
628 : fprintf(grLog(), "%04x ", rArgs.mpStr[i]);
629 : }
630 : fprintf(grLog(), "\n");
631 : #endif
632 0 : clear();
633 0 : return NULL;
634 : }
635 : }
636 0 : catch (...)
637 : {
638 0 : clear(); // destroy the text source and any partially built segments.
639 0 : return NULL;
640 : }
641 0 : return pSegment;
642 : }
643 :
644 0 : bool GraphiteLayout::LayoutGlyphs(ImplLayoutArgs& rArgs, gr_segment * pSegment)
645 : {
646 : // Calculate the initial character dxs.
647 0 : mvCharDxs.assign(mnEndCharPos - mnMinCharPos, -1);
648 0 : mvChar2BaseGlyph.assign(mnEndCharPos - mnMinCharPos, -1);
649 0 : mvCharBreaks.assign(mnEndCharPos - mnMinCharPos, 0);
650 0 : mnWidth = 0;
651 0 : if (mvCharDxs.size() > 0)
652 : {
653 : // Discover all the clusters.
654 : try
655 : {
656 0 : bool bRtl = mnLayoutFlags & SAL_LAYOUT_BIDI_RTL;
657 0 : fillFrom(pSegment, rArgs, mfScaling);
658 :
659 0 : if (bRtl)
660 : {
661 : // not needed for adjacent differences, but for mouse clicks to char
662 : std::transform(mvCharDxs.begin(), mvCharDxs.end(), mvCharDxs.begin(),
663 0 : std::bind1st(std::minus<long>(), mnWidth));
664 : // fixup last dx to ensure it always equals the width
665 0 : mvCharDxs[mvCharDxs.size() - 1] = mnWidth;
666 : }
667 : }
668 0 : catch (const std::exception &e)
669 : {
670 : #ifdef GRLAYOUT_DEBUG
671 : fprintf(grLog(),"LayoutGlyphs failed %s\n", e.what());
672 : #else
673 : (void)e;
674 : #endif
675 0 : return false;
676 : }
677 0 : catch (...)
678 : {
679 : #ifdef GRLAYOUT_DEBUG
680 : fprintf(grLog(),"LayoutGlyphs failed with exception");
681 : #endif
682 0 : return false;
683 : }
684 : }
685 : else
686 : {
687 0 : mnWidth = 0;
688 : }
689 0 : return true;
690 : }
691 :
692 0 : sal_Int32 GraphiteLayout::GetTextBreak(long maxmnWidth, long char_extra, int factor) const
693 : {
694 : #ifdef GRLAYOUT_DEBUG
695 : fprintf(grLog(),"Gr::GetTextBreak c[%d-%d) maxWidth %ld char extra %ld factor %d\n",
696 : mnMinCharPos, mnEndCharPos, maxmnWidth, char_extra, factor);
697 : #endif
698 :
699 : // return quickly if this segment is narrower than the target width
700 0 : if (maxmnWidth > mnWidth * factor + char_extra * (mnEndCharPos - mnMinCharPos - 1))
701 0 : return -1;
702 :
703 0 : long nWidth = mvCharDxs[0] * factor;
704 0 : long wLastBreak = 0;
705 0 : int nLastBreak = -1;
706 0 : int nEmergency = -1;
707 0 : for (size_t i = 1; i < mvCharDxs.size(); i++)
708 : {
709 0 : nWidth += char_extra;
710 0 : if (nWidth > maxmnWidth) break;
711 0 : if (mvChar2BaseGlyph[i] != -1)
712 : {
713 0 : if (
714 0 : (mvCharBreaks[i] > -35 || (mvCharBreaks[i-1] > 0 && mvCharBreaks[i-1] < 35)) &&
715 0 : (mvCharBreaks[i-1] < 35 || (mvCharBreaks[i] < 0 && mvCharBreaks[i] > -35))
716 : )
717 : {
718 0 : nLastBreak = static_cast<int>(i);
719 0 : wLastBreak = nWidth;
720 : }
721 0 : nEmergency = static_cast<int>(i);
722 : }
723 0 : nWidth += (mvCharDxs[i] - mvCharDxs[i-1]) * factor;
724 : }
725 0 : int nBreak = mnMinCharPos;
726 0 : if (wLastBreak > 9 * maxmnWidth / 10)
727 0 : nBreak += nLastBreak;
728 : else
729 0 : if (nEmergency > -1)
730 0 : nBreak += nEmergency;
731 :
732 : #ifdef GRLAYOUT_DEBUG
733 : fprintf(grLog(), "Gr::GetTextBreak break after %d, weights(%d, %d)\n", nBreak - mnMinCharPos, mvCharBreaks[nBreak - mnMinCharPos], mvCharBreaks[nBreak - mnMinCharPos - 1]);
734 : #endif
735 :
736 0 : if (nBreak > mnEndCharPos)
737 0 : nBreak = -1;
738 0 : else if (nBreak < mnMinCharPos)
739 0 : nBreak = mnMinCharPos;
740 0 : return nBreak;
741 : }
742 :
743 0 : long GraphiteLayout::FillDXArray( sal_Int32* pDXArray ) const
744 : {
745 0 : if (mnEndCharPos == mnMinCharPos)
746 : // Then we must be zero width!
747 0 : return 0;
748 :
749 0 : if (pDXArray)
750 : {
751 0 : for (size_t i = 0; i < mvCharDxs.size(); i++)
752 : {
753 : assert( (mvChar2BaseGlyph[i] == -1) ||
754 : ((signed)(mvChar2BaseGlyph[i]) < (signed)mvGlyphs.size()));
755 0 : if (mvChar2BaseGlyph[i] != -1 &&
756 0 : mvGlyphs[mvChar2BaseGlyph[i]].maGlyphId == GF_DROPPED)
757 : {
758 : // when used in MultiSalLayout::GetTextBreak dropped glyphs
759 : // must have zero width
760 0 : pDXArray[i] = 0;
761 : }
762 : else
763 : {
764 0 : pDXArray[i] = mvCharDxs[i];
765 0 : if (i > 0) pDXArray[i] -= mvCharDxs[i-1];
766 : }
767 : #ifdef GRLAYOUT_DEBUG
768 : fprintf(grLog(),"%d,%d,%d ", (int)i, (int)mvCharDxs[i], pDXArray[i]);
769 : #endif
770 : }
771 : //std::adjacent_difference(mvCharDxs.begin(), mvCharDxs.end(), pDXArray);
772 : //for (size_t i = 0; i < mvCharDxs.size(); i++)
773 : // fprintf(grLog(),"%d,%d,%d ", (int)i, (int)mvCharDxs[i], pDXArray[i]);
774 : //fprintf(grLog(),"FillDX %ld,%d\n", mnWidth, std::accumulate(pDXArray, pDXArray + mvCharDxs.size(), 0));
775 : }
776 : #ifdef GRLAYOUT_DEBUG
777 : fprintf(grLog(),"FillDXArray %d-%d=%ld\n", mnMinCharPos, mnEndCharPos, mnWidth);
778 : #endif
779 0 : return mnWidth;
780 : }
781 :
782 0 : void GraphiteLayout::AdjustLayout(ImplLayoutArgs& rArgs)
783 : {
784 0 : SalLayout::AdjustLayout(rArgs);
785 0 : if(rArgs.mpDXArray)
786 : {
787 0 : std::vector<int> vDeltaWidths(mvGlyphs.size(), 0);
788 0 : ApplyDXArray(rArgs, vDeltaWidths);
789 :
790 0 : if( (mnLayoutFlags & SAL_LAYOUT_BIDI_RTL) &&
791 0 : !(rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK) )
792 : {
793 : // check if this is a kashida script
794 0 : bool bKashidaScript = false;
795 0 : for (int i = rArgs.mnMinCharPos; i < rArgs.mnEndCharPos; i++)
796 : {
797 0 : UErrorCode aStatus = U_ZERO_ERROR;
798 0 : UScriptCode scriptCode = uscript_getScript(rArgs.mpStr[i], &aStatus);
799 0 : if (scriptCode == USCRIPT_ARABIC || scriptCode == USCRIPT_SYRIAC)
800 : {
801 0 : bKashidaScript = true;
802 0 : break;
803 : }
804 : }
805 0 : int nKashidaWidth = 0;
806 0 : int nKashidaIndex = getKashidaGlyph(nKashidaWidth);
807 0 : if( nKashidaIndex != 0 && bKashidaScript)
808 : {
809 0 : kashidaJustify( vDeltaWidths, nKashidaIndex, nKashidaWidth );
810 : }
811 0 : }
812 : }
813 0 : else if (rArgs.mnLayoutWidth > 0)
814 : {
815 : #ifdef GRLAYOUT_DEBUG
816 : fprintf(grLog(), "AdjustLayout width %ld=>%ld\n", mnWidth, rArgs.mnLayoutWidth);
817 : #endif
818 0 : expandOrCondense(rArgs);
819 : }
820 0 : }
821 :
822 0 : void GraphiteLayout::expandOrCondense(ImplLayoutArgs &rArgs)
823 : {
824 0 : int nDeltaWidth = rArgs.mnLayoutWidth - mnWidth;
825 0 : if (nDeltaWidth > 0) // expand, just expand between clusters
826 : {
827 : // NOTE: for expansion we can use base glyphs (which have IsClusterStart set)
828 : // even though they may have been reordered in which case they will have
829 : // been placed in a bigger cluster for other purposes.
830 0 : int nClusterCount = 0;
831 0 : for (size_t j = 0; j < mvGlyphs.size(); j++)
832 : {
833 0 : if (mvGlyphs[j].IsClusterStart())
834 : {
835 0 : ++nClusterCount;
836 : }
837 : }
838 0 : if (nClusterCount > 1)
839 : {
840 0 : float fExtraPerCluster = static_cast<float>(nDeltaWidth) / static_cast<float>(nClusterCount - 1);
841 0 : int nCluster = 0;
842 0 : int nOffset = 0;
843 0 : for (size_t i = 0; i < mvGlyphs.size(); i++)
844 : {
845 0 : if (mvGlyphs[i].IsClusterStart())
846 : {
847 0 : nOffset = static_cast<int>(fExtraPerCluster * nCluster);
848 0 : int nCharIndex = mvGlyph2Char[i];
849 : assert(nCharIndex > -1);
850 0 : if (nCharIndex < mnMinCharPos ||
851 0 : static_cast<size_t>(nCharIndex-mnMinCharPos)
852 0 : >= mvCharDxs.size())
853 : {
854 0 : continue;
855 : }
856 0 : mvCharDxs[nCharIndex-mnMinCharPos] += nOffset;
857 : // adjust char dxs for rest of characters in cluster
858 0 : while (++nCharIndex - mnMinCharPos < static_cast<int>(mvChar2BaseGlyph.size()))
859 : {
860 0 : int nChar2Base = mvChar2BaseGlyph[nCharIndex-mnMinCharPos];
861 0 : if (nChar2Base == -1 || nChar2Base == static_cast<int>(i))
862 0 : mvCharDxs[nCharIndex-mnMinCharPos] += nOffset;
863 : else
864 : break;
865 : }
866 0 : ++nCluster;
867 : }
868 0 : mvGlyphs[i].maLinearPos.X() += nOffset;
869 : }
870 : }
871 : }
872 0 : else if (nDeltaWidth < 0)// condense - apply a factor to all glyph positions
873 : {
874 0 : if (mvGlyphs.empty()) return;
875 0 : Glyphs::iterator iLastGlyph = mvGlyphs.begin() + (mvGlyphs.size() - 1);
876 : // position last glyph using original width
877 0 : float fXFactor = static_cast<float>(rArgs.mnLayoutWidth - iLastGlyph->mnOrigWidth) / static_cast<float>(iLastGlyph->maLinearPos.X());
878 : #ifdef GRLAYOUT_DEBUG
879 : fprintf(grLog(), "Condense by factor %f last x%ld\n", fXFactor, iLastGlyph->maLinearPos.X());
880 : #endif
881 0 : if (fXFactor < 0)
882 0 : return; // probably a bad mnOrigWidth value
883 0 : iLastGlyph->maLinearPos.X() = rArgs.mnLayoutWidth - iLastGlyph->mnOrigWidth;
884 0 : Glyphs::iterator iGlyph = mvGlyphs.begin();
885 0 : while (iGlyph != iLastGlyph)
886 : {
887 0 : iGlyph->maLinearPos.X() = static_cast<int>(static_cast<float>(iGlyph->maLinearPos.X()) * fXFactor);
888 0 : ++iGlyph;
889 : }
890 0 : for (size_t i = 0; i < mvCharDxs.size(); i++)
891 : {
892 0 : mvCharDxs[i] = static_cast<int>(fXFactor * static_cast<float>(mvCharDxs[i]));
893 : }
894 : }
895 0 : mnWidth = rArgs.mnLayoutWidth;
896 : }
897 :
898 0 : void GraphiteLayout::ApplyDXArray(ImplLayoutArgs &args, std::vector<int> & rDeltaWidth)
899 : {
900 0 : const size_t nChars = args.mnEndCharPos - args.mnMinCharPos;
901 0 : if (nChars == 0) return;
902 :
903 : #ifdef GRLAYOUT_DEBUG
904 : for (size_t iDx = 0; iDx < mvCharDxs.size(); iDx++)
905 : fprintf(grLog(),"%d,%d,%d ", (int)iDx, (int)mvCharDxs[iDx], args.mpDXArray[iDx]);
906 : fprintf(grLog(),"ApplyDx\n");
907 : #endif
908 0 : bool bRtl = mnLayoutFlags & SAL_LAYOUT_BIDI_RTL;
909 0 : int nXOffset = 0;
910 0 : if (bRtl)
911 : {
912 0 : nXOffset = args.mpDXArray[nChars - 1] - mvCharDxs[nChars - 1];
913 : }
914 0 : int nPrevClusterGlyph = (bRtl)? (signed)mvGlyphs.size() : -1;
915 0 : int nPrevClusterLastChar = -1;
916 0 : for (size_t i = 0; i < nChars; i++)
917 : {
918 0 : int nChar2Base = mvChar2BaseGlyph[i];
919 0 : if ((nChar2Base > -1) && (nChar2Base != nPrevClusterGlyph))
920 : {
921 : assert((nChar2Base > -1) && (nChar2Base < (signed)mvGlyphs.size()));
922 0 : GlyphItem & gi = mvGlyphs[nChar2Base];
923 0 : if (!gi.IsClusterStart())
924 0 : continue;
925 :
926 : // find last glyph of this cluster
927 0 : size_t j = i + 1;
928 0 : int nLastChar = i;
929 0 : int nLastGlyph = nChar2Base;
930 0 : int nChar2BaseJ = -1;
931 0 : for (; j < nChars; j++)
932 : {
933 0 : nChar2BaseJ = mvChar2BaseGlyph[j];
934 : assert((nChar2BaseJ >= -1) && (nChar2BaseJ < (signed)mvGlyphs.size()));
935 0 : if (nChar2BaseJ != -1 )
936 : {
937 0 : nLastGlyph = nChar2BaseJ + ((bRtl)? +1 : -1);
938 0 : nLastChar = j - 1;
939 0 : break;
940 : }
941 : }
942 0 : if (nLastGlyph < 0)
943 : {
944 0 : nLastGlyph = nChar2Base;
945 : }
946 : // Its harder to find the last glyph rtl, since the first of
947 : // cluster is still on the left so we need to search towards
948 : // the previous cluster to the right
949 0 : if (bRtl)
950 : {
951 0 : nLastGlyph = nChar2Base;
952 0 : while (nLastGlyph + 1 < (signed)mvGlyphs.size() &&
953 0 : !mvGlyphs[nLastGlyph+1].IsClusterStart())
954 : {
955 0 : ++nLastGlyph;
956 : }
957 : }
958 0 : if (j == nChars)
959 : {
960 0 : nLastChar = nChars - 1;
961 0 : if (!bRtl) nLastGlyph = mvGlyphs.size() - 1;
962 : }
963 0 : int nBaseCount = 0;
964 : // count bases within cluster - may be more than 1 with reordering
965 0 : for (int k = nChar2Base; k <= nLastGlyph; k++)
966 : {
967 0 : if (mvGlyphs[k].IsClusterStart()) ++nBaseCount;
968 : }
969 : assert((nLastChar > -1) && (nLastChar < (signed)nChars));
970 0 : long nNewClusterWidth = args.mpDXArray[nLastChar];
971 0 : long nOrigClusterWidth = mvCharDxs[nLastChar];
972 0 : long nDGlyphOrigin = 0;
973 0 : if (nPrevClusterLastChar > - 1)
974 : {
975 : assert(nPrevClusterLastChar < (signed)nChars);
976 0 : nNewClusterWidth -= args.mpDXArray[nPrevClusterLastChar];
977 0 : nOrigClusterWidth -= mvCharDxs[nPrevClusterLastChar];
978 0 : nDGlyphOrigin = args.mpDXArray[nPrevClusterLastChar] - mvCharDxs[nPrevClusterLastChar];
979 : }
980 0 : long nDWidth = nNewClusterWidth - nOrigClusterWidth;
981 : #ifdef GRLAYOUT_DEBUG
982 : fprintf(grLog(), "c%lu last glyph %d/%lu\n", i, nLastGlyph, mvGlyphs.size());
983 : #endif
984 : assert((nLastGlyph > -1) && (nLastGlyph < (signed)mvGlyphs.size()));
985 0 : mvGlyphs[nLastGlyph].mnNewWidth += nDWidth;
986 0 : if (gi.maGlyphId != GF_DROPPED)
987 0 : mvGlyphs[nLastGlyph].mnNewWidth += nDWidth;
988 : else
989 0 : nDGlyphOrigin += nDWidth;
990 0 : long nDOriginPerBase = (nBaseCount > 0)? nDWidth / nBaseCount : 0;
991 0 : nBaseCount = -1;
992 : // update glyph positions
993 0 : if (bRtl)
994 : {
995 0 : for (int n = nChar2Base; n <= nLastGlyph; n++)
996 : {
997 0 : if (mvGlyphs[n].IsClusterStart()) ++nBaseCount;
998 : assert((n > - 1) && (n < (signed)mvGlyphs.size()));
999 0 : mvGlyphs[n].maLinearPos.X() += -(nDGlyphOrigin + nDOriginPerBase * nBaseCount) + nXOffset;
1000 : }
1001 : }
1002 : else
1003 : {
1004 0 : for (int n = nChar2Base; n <= nLastGlyph; n++)
1005 : {
1006 0 : if (mvGlyphs[n].IsClusterStart()) ++nBaseCount;
1007 : assert((n > - 1) && (n < (signed)mvGlyphs.size()));
1008 0 : mvGlyphs[n].maLinearPos.X() += nDGlyphOrigin + (nDOriginPerBase * nBaseCount) + nXOffset;
1009 : }
1010 : }
1011 0 : rDeltaWidth[nChar2Base] = nDWidth;
1012 : #ifdef GRLAYOUT_DEBUG
1013 : 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());
1014 : #endif
1015 0 : nPrevClusterGlyph = nChar2Base;
1016 0 : nPrevClusterLastChar = nLastChar;
1017 0 : i = nLastChar;
1018 : }
1019 : }
1020 : // Update the dx vector with the new values.
1021 : std::copy(args.mpDXArray, args.mpDXArray + nChars,
1022 0 : mvCharDxs.begin() + (args.mnMinCharPos - mnMinCharPos));
1023 : #ifdef GRLAYOUT_DEBUG
1024 : fprintf(grLog(),"ApplyDx %d(%ld)\n", args.mpDXArray[nChars - 1], mnWidth);
1025 : #endif
1026 0 : mnWidth = args.mpDXArray[nChars - 1];
1027 : }
1028 :
1029 0 : void GraphiteLayout::kashidaJustify(std::vector<int>& rDeltaWidths, sal_GlyphId nKashidaIndex, int nKashidaWidth)
1030 : {
1031 : // skip if the kashida glyph in the font looks suspicious
1032 0 : if( nKashidaWidth <= 0 )
1033 0 : return;
1034 :
1035 : // calculate max number of needed kashidas
1036 0 : Glyphs::iterator i = mvGlyphs.begin();
1037 0 : int nKashidaCount = 0;
1038 0 : int nOrigGlyphIndex = -1;
1039 0 : int nGlyphIndex = -1;
1040 0 : while (i != mvGlyphs.end())
1041 : {
1042 0 : nOrigGlyphIndex++;
1043 0 : nGlyphIndex++;
1044 : // only inject kashidas in RTL contexts
1045 0 : if( !(*i).IsRTLGlyph() )
1046 : {
1047 0 : ++i;
1048 0 : continue;
1049 : }
1050 : // no kashida-injection for blank justified expansion either
1051 0 : if( IsSpacingGlyph( (*i).maGlyphId ) )
1052 : {
1053 0 : ++i;
1054 0 : continue;
1055 : }
1056 : // calculate gap, ignore if too small
1057 0 : int nGapWidth = rDeltaWidths[nOrigGlyphIndex];
1058 : // worst case is one kashida even for mini-gaps
1059 0 : if( 3 * nGapWidth < nKashidaWidth )
1060 : {
1061 0 : ++i;
1062 0 : continue;
1063 : }
1064 0 : nKashidaCount = 1 + (nGapWidth / nKashidaWidth);
1065 : #ifdef GRLAYOUT_DEBUG
1066 : printf("inserting %d kashidas at %u\n", nKashidaCount, (*i).maGlyphId);
1067 : #endif
1068 0 : GlyphItem glyphItem = *i;
1069 0 : Point aPos(0, 0);
1070 0 : aPos.X() = (*i).maLinearPos.X();
1071 : GlyphItem newGi(glyphItem.mnCharPos, nKashidaIndex, aPos,
1072 0 : GlyphItem::IS_IN_CLUSTER|GlyphItem::IS_RTL_GLYPH, nKashidaWidth);
1073 0 : mvGlyphs.reserve(mvGlyphs.size() + nKashidaCount);
1074 0 : i = mvGlyphs.begin() + nGlyphIndex;
1075 0 : mvGlyphs.insert(i, nKashidaCount, newGi);
1076 0 : i = mvGlyphs.begin() + nGlyphIndex;
1077 0 : nGlyphIndex += nKashidaCount;
1078 : // now fix up the kashida positions
1079 0 : for (int j = 0; j < nKashidaCount; j++)
1080 : {
1081 0 : (*(i)).maLinearPos.X() -= nGapWidth;
1082 0 : nGapWidth -= nKashidaWidth;
1083 0 : ++i;
1084 : }
1085 :
1086 : // fixup rightmost kashida for gap remainder
1087 0 : if( nGapWidth < 0 )
1088 : {
1089 0 : if( nKashidaCount <= 1 )
1090 0 : nGapWidth /= 2; // for small gap move kashida to middle
1091 0 : (*(i-1)).mnNewWidth += nGapWidth; // adjust kashida width to gap width
1092 0 : (*(i-1)).maLinearPos.X() += nGapWidth;
1093 : }
1094 :
1095 0 : (*i).mnNewWidth = (*i).mnOrigWidth;
1096 0 : ++i;
1097 : }
1098 :
1099 : }
1100 :
1101 0 : void GraphiteLayout::GetCaretPositions( int nArraySize, sal_Int32* pCaretXArray ) const
1102 : {
1103 : // For each character except the last discover the caret positions
1104 : // immediately before and after that character.
1105 : // This is used for underlines in the GUI amongst other things.
1106 : // It may be used from MultiSalLayout, in which case it must take into account
1107 : // glyphs that have been moved.
1108 0 : std::fill(pCaretXArray, pCaretXArray + nArraySize, -1);
1109 : // the layout method doesn't modify the layout even though it isn't
1110 : // const in the interface
1111 0 : bool bRtl = (mnLayoutFlags & SAL_LAYOUT_BIDI_RTL);//const_cast<GraphiteLayout*>(this)->maLayout.rightToLeft();
1112 0 : int prevBase = -1;
1113 0 : long prevClusterWidth = 0;
1114 0 : for (int i = 0, nCharSlot = 0; i < nArraySize && nCharSlot < static_cast<int>(mvCharDxs.size()); ++nCharSlot, i+=2)
1115 : {
1116 0 : if (mvChar2BaseGlyph[nCharSlot] != -1)
1117 : {
1118 0 : int nChar2Base = mvChar2BaseGlyph[nCharSlot];
1119 : assert((nChar2Base > -1) && (nChar2Base < (signed)mvGlyphs.size()));
1120 0 : GlyphItem gi = mvGlyphs[nChar2Base];
1121 0 : if (gi.maGlyphId == GF_DROPPED)
1122 : {
1123 0 : continue;
1124 : }
1125 0 : int nCluster = nChar2Base;
1126 0 : long origClusterWidth = gi.mnNewWidth;
1127 0 : long nMin = gi.maLinearPos.X();
1128 0 : long nMax = gi.maLinearPos.X() + gi.mnNewWidth;
1129 : // attached glyphs are always stored after their base rtl or ltr
1130 0 : while (++nCluster < static_cast<int>(mvGlyphs.size()) &&
1131 0 : !mvGlyphs[nCluster].IsClusterStart())
1132 : {
1133 0 : origClusterWidth += mvGlyphs[nCluster].mnNewWidth;
1134 0 : if (mvGlyph2Char[nCluster] == nCharSlot)
1135 : {
1136 0 : nMin = minimum(nMin, mvGlyphs[nCluster].maLinearPos.X());
1137 0 : nMax = maximum(nMax, mvGlyphs[nCluster].maLinearPos.X() + mvGlyphs[nCluster].mnNewWidth);
1138 : }
1139 : }
1140 0 : if (bRtl)
1141 : {
1142 0 : pCaretXArray[i+1] = nMin;
1143 0 : pCaretXArray[i] = nMax;
1144 : }
1145 : else
1146 : {
1147 0 : pCaretXArray[i] = nMin;
1148 0 : pCaretXArray[i+1] = nMax;
1149 : }
1150 0 : prevBase = nChar2Base;
1151 0 : prevClusterWidth = origClusterWidth;
1152 : }
1153 0 : else if (prevBase > -1)
1154 : {
1155 : // this could probably be improved
1156 : assert((prevBase > -1) && (prevBase < (signed)mvGlyphs.size()));
1157 0 : GlyphItem gi = mvGlyphs[prevBase];
1158 0 : int nGlyph = prevBase + 1;
1159 : // try to find a better match, otherwise default to complete cluster
1160 0 : for (; nGlyph < static_cast<int>(mvGlyphs.size()) &&
1161 0 : !mvGlyphs[nGlyph].IsClusterStart(); nGlyph++)
1162 : {
1163 0 : if (mvGlyph2Char[nGlyph] == nCharSlot)
1164 : {
1165 0 : gi = mvGlyphs[nGlyph];
1166 0 : break;
1167 : }
1168 : }
1169 : // if no match position at end of cluster
1170 0 : if (nGlyph == static_cast<int>(mvGlyphs.size()) ||
1171 0 : mvGlyphs[nGlyph].IsClusterStart())
1172 : {
1173 0 : if (bRtl)
1174 : {
1175 0 : pCaretXArray[i+1] = gi.maLinearPos.X();
1176 0 : pCaretXArray[i] = gi.maLinearPos.X();
1177 : }
1178 : else
1179 : {
1180 0 : pCaretXArray[i] = gi.maLinearPos.X() + prevClusterWidth;
1181 0 : pCaretXArray[i+1] = gi.maLinearPos.X() + prevClusterWidth;
1182 : }
1183 : }
1184 : else
1185 : {
1186 0 : if (bRtl)
1187 : {
1188 0 : pCaretXArray[i+1] = gi.maLinearPos.X();
1189 0 : pCaretXArray[i] = gi.maLinearPos.X() + gi.mnNewWidth;
1190 : }
1191 : else
1192 : {
1193 0 : pCaretXArray[i] = gi.maLinearPos.X();
1194 0 : pCaretXArray[i+1] = gi.maLinearPos.X() + gi.mnNewWidth;
1195 : }
1196 : }
1197 : }
1198 : else
1199 : {
1200 0 : pCaretXArray[i] = pCaretXArray[i+1] = 0;
1201 : }
1202 : #ifdef GRLAYOUT_DEBUG
1203 : fprintf(grLog(),"%d,%d-%d\t", nCharSlot, pCaretXArray[i], pCaretXArray[i+1]);
1204 : #endif
1205 : }
1206 : #ifdef GRLAYOUT_DEBUG
1207 : fprintf(grLog(),"\n");
1208 : #endif
1209 0 : }
1210 :
1211 : // GetNextGlyphs returns a contiguous sequence of glyphs that can be
1212 : // rendered together. It should never return a dropped glyph.
1213 : // The glyph_slot returned should be the index of the next visible
1214 : // glyph after the last glyph returned by this call.
1215 : // The char_index array should be filled with the characters corresponding
1216 : // to each glyph returned.
1217 : // glyph_adv array should be a virtual width such that if successive
1218 : // glyphs returned by this method are added one after the other they
1219 : // have the correct spacing.
1220 : // The logic in this method must match that expected in MultiSalLayout which
1221 : // is used when glyph fallback is in operation.
1222 0 : int GraphiteLayout::GetNextGlyphs( int length, sal_GlyphId * glyph_out,
1223 : ::Point & aPosOut, int &glyph_slot, sal_Int32 * glyph_adv, int *char_index,
1224 : const PhysicalFontFace** /*pFallbackFonts*/ ) const
1225 : {
1226 : // Sanity check on the slot index.
1227 0 : if (glyph_slot >= signed(mvGlyphs.size()))
1228 : {
1229 0 : glyph_slot = mvGlyphs.size();
1230 0 : return 0;
1231 : }
1232 : assert(glyph_slot >= 0);
1233 : // Find the first glyph in the substring.
1234 0 : for (; glyph_slot < signed(mvGlyphs.size()) &&
1235 0 : ((mvGlyphs.begin() + glyph_slot)->maGlyphId == GF_DROPPED);
1236 : ++glyph_slot) {};
1237 :
1238 : // Update the length
1239 0 : const int nGlyphSlotEnd = minimum(size_t(glyph_slot + length), mvGlyphs.size());
1240 :
1241 : // We're all out of glyphs here.
1242 0 : if (glyph_slot == nGlyphSlotEnd)
1243 : {
1244 0 : return 0;
1245 : }
1246 :
1247 : // Find as many glyphs as we can which can be drawn in one go.
1248 0 : Glyphs::const_iterator glyph_itr = mvGlyphs.begin() + glyph_slot;
1249 0 : const int glyph_slot_begin = glyph_slot;
1250 0 : const int initial_y_pos = glyph_itr->maLinearPos.Y();
1251 :
1252 : // Set the position to the position of the start glyph.
1253 0 : ::Point aStartPos = glyph_itr->maLinearPos;
1254 : //aPosOut = glyph_itr->maLinearPos;
1255 0 : aPosOut = GetDrawPosition(aStartPos);
1256 :
1257 : for (;;) // Forever
1258 : {
1259 : // last index of the range from glyph_to_chars does not include this glyph
1260 0 : if (char_index)
1261 : {
1262 0 : if (glyph_slot >= (signed)mvGlyph2Char.size())
1263 : {
1264 0 : *char_index++ = mnMinCharPos + mvCharDxs.size();
1265 : }
1266 : else
1267 : {
1268 : assert(glyph_slot > -1);
1269 0 : if (mvGlyph2Char[glyph_slot] == -1)
1270 0 : *char_index++ = mnMinCharPos + mvCharDxs.size();
1271 : else
1272 0 : *char_index++ = mvGlyph2Char[glyph_slot];
1273 : }
1274 : }
1275 : // Copy out this glyphs data.
1276 0 : ++glyph_slot;
1277 0 : *glyph_out++ = glyph_itr->maGlyphId;
1278 :
1279 : // Find the actual advance - this must be correct if called from
1280 : // MultiSalLayout::AdjustLayout which requests one glyph at a time.
1281 0 : const long nGlyphAdvance = (glyph_slot == static_cast<int>(mvGlyphs.size()))?
1282 0 : glyph_itr->mnNewWidth :
1283 0 : ((glyph_itr+1)->maLinearPos.X() - glyph_itr->maLinearPos.X());
1284 :
1285 : #ifdef GRLAYOUT_DEBUG
1286 : fprintf(grLog(),"GetNextGlyphs g%d gid%d c%d x%ld,%ld adv%ld, pos %ld,%ld\n",
1287 : glyph_slot - 1, glyph_itr->maGlyphId,
1288 : mvGlyph2Char[glyph_slot-1], glyph_itr->maLinearPos.X(), glyph_itr->maLinearPos.Y(), nGlyphAdvance,
1289 : aPosOut.X(), aPosOut.Y());
1290 : #endif
1291 :
1292 0 : if (glyph_adv) // If we are returning advance store it.
1293 0 : *glyph_adv++ = nGlyphAdvance;
1294 : else // Stop when next advance is unexpected.
1295 0 : if (glyph_itr->mnOrigWidth != nGlyphAdvance) break;
1296 :
1297 : // Have fetched all the glyphs we need to
1298 0 : if (glyph_slot == nGlyphSlotEnd)
1299 0 : break;
1300 :
1301 0 : ++glyph_itr;
1302 : // Stop when next y position is unexpected.
1303 0 : if (initial_y_pos != glyph_itr->maLinearPos.Y())
1304 0 : break;
1305 :
1306 : // Stop if glyph dropped
1307 0 : if (glyph_itr->maGlyphId == GF_DROPPED)
1308 0 : break;
1309 0 : }
1310 0 : int numGlyphs = glyph_slot - glyph_slot_begin;
1311 : // move the next glyph_slot to a glyph that hasn't been dropped
1312 0 : while (glyph_slot < static_cast<int>(mvGlyphs.size()) &&
1313 0 : (mvGlyphs.begin() + glyph_slot)->maGlyphId == GF_DROPPED)
1314 0 : ++glyph_slot;
1315 0 : return numGlyphs;
1316 : }
1317 :
1318 0 : void GraphiteLayout::MoveGlyph( int nGlyphIndex, long nNewPos )
1319 : {
1320 : // TODO it might be better to actualy implement simplify properly, but this
1321 : // needs to be done carefully so the glyph/char maps are maintained
1322 : // If a glyph has been dropped then it wasn't returned by GetNextGlyphs, so
1323 : // the index here may be wrong
1324 0 : while ((mvGlyphs[nGlyphIndex].maGlyphId == GF_DROPPED) &&
1325 0 : (nGlyphIndex < (signed)mvGlyphs.size()))
1326 : {
1327 0 : nGlyphIndex++;
1328 : }
1329 0 : const long dx = nNewPos - mvGlyphs[nGlyphIndex].maLinearPos.X();
1330 :
1331 0 : if (dx == 0) return;
1332 : // GenericSalLayout only changes maLinearPos, mvCharDxs doesn't change
1333 : #ifdef GRLAYOUT_DEBUG
1334 : fprintf(grLog(),"Move %d (%ld,%ld) c%d by %ld\n", nGlyphIndex, mvGlyphs[nGlyphIndex].maLinearPos.X(), nNewPos, mvGlyph2Char[nGlyphIndex], dx);
1335 : #endif
1336 0 : for (size_t gi = nGlyphIndex; gi < mvGlyphs.size(); gi++)
1337 : {
1338 0 : mvGlyphs[gi].maLinearPos.X() += dx;
1339 : }
1340 : // width does need to be updated for correct fallback
1341 0 : mnWidth += dx;
1342 : }
1343 :
1344 0 : void GraphiteLayout::DropGlyph( int nGlyphIndex )
1345 : {
1346 0 : if(nGlyphIndex >= signed(mvGlyphs.size()))
1347 0 : return;
1348 :
1349 0 : GlyphItem & glyph = mvGlyphs[nGlyphIndex];
1350 0 : glyph.maGlyphId = GF_DROPPED;
1351 : #ifdef GRLAYOUT_DEBUG
1352 : fprintf(grLog(),"Dropped %d\n", nGlyphIndex);
1353 : #endif
1354 : }
1355 :
1356 0 : void GraphiteLayout::Simplify( bool isBaseLayout )
1357 : {
1358 0 : const sal_GlyphId dropMarker = isBaseLayout ? GF_DROPPED : 0;
1359 :
1360 0 : Glyphs::iterator gi = mvGlyphs.begin();
1361 : // TODO check whether we need to adjust positions here
1362 : // MultiSalLayout seems to move the glyphs itself, so it may not be needed.
1363 0 : long deltaX = 0;
1364 0 : while (gi != mvGlyphs.end())
1365 : {
1366 0 : if (gi->maGlyphId == dropMarker)
1367 : {
1368 0 : deltaX += gi->mnNewWidth;
1369 0 : gi->mnNewWidth = 0;
1370 : }
1371 : else
1372 : {
1373 0 : deltaX = 0;
1374 : }
1375 0 : ++gi;
1376 : }
1377 : #ifdef GRLAYOUT_DEBUG
1378 : fprintf(grLog(),"Simplify base%d dx=%ld newW=%ld\n", isBaseLayout, deltaX, mnWidth - deltaX);
1379 : #endif
1380 : // discard width from trailing dropped glyphs, but not those in the middle
1381 0 : mnWidth -= deltaX;
1382 3 : }
1383 :
1384 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|