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