update credits
[LibreOffice.git] / vcl / source / glyphs / graphite_layout.cxx
bloba9cb7cc4e21b837b9bc9a13956aa75998008e858
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 // Description: An implementation of the SalLayout interface that uses the
21 // Graphite engine.
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
30 // #define GRLAYOUT_DEBUG 1
32 // Header files
34 // Standard Library
35 #include <algorithm>
36 #include <cassert>
37 #include <functional>
38 #include <limits>
39 #include <numeric>
40 #include <deque>
42 // Platform
43 #include <svsys.h>
45 #include <salgdi.hxx>
47 #include <unicode/uchar.h>
48 #include <unicode/ubidi.h>
49 #include <unicode/uscript.h>
51 // Graphite Libraries (must be after vcl headers on windows)
52 #include <graphite2/Segment.h>
54 #include <graphite_layout.hxx>
55 #include <graphite_features.hxx>
57 // Module private type definitions and forward declarations.
59 // Module private names.
62 #ifdef GRLAYOUT_DEBUG
63 static FILE * grLog()
65 #ifdef WNT
66 static FILE * grLogFile = NULL;
67 if (grLogFile == NULL)
69 std::string logFileName(getenv("TEMP"));
70 logFileName.append("/graphitelayout.log");
71 grLogFile = fopen(logFileName.c_str(),"w");
73 else
74 fflush(grLogFile);
75 return grLogFile;
76 #else
77 fflush(stdout);
78 return stdout;
79 #endif
81 #endif
83 namespace
85 inline long round(const float n) {
86 return long(n + (n < 0 ? -0.5 : 0.5));
89 template<typename T>
90 inline bool in_range(const T i, const T b, const T e) {
91 return !(b > i) && i < e;
94 template<typename T>
95 inline bool is_subrange(const T sb, const T se, const T b, const T e) {
96 return !(b > sb || se > e);
99 template<typename T>
100 inline bool is_subrange(const std::pair<T, T> &s, const T b, const T e) {
101 return is_subrange(s.first, s.second, b, e);
104 int findSameDirLimit(const sal_Unicode* buffer, int charCount, bool rtl)
106 UErrorCode status = U_ZERO_ERROR;
107 UBiDi *ubidi = ubidi_openSized(charCount, 0, &status);
108 int limit = 0;
109 ubidi_setPara(ubidi, reinterpret_cast<const UChar *>(buffer), charCount,
110 (rtl)?UBIDI_DEFAULT_RTL:UBIDI_DEFAULT_LTR, NULL, &status);
111 UBiDiLevel level = 0;
112 ubidi_getLogicalRun(ubidi, 0, &limit, &level);
113 ubidi_close(ubidi);
114 if ((rtl && !(level & 1)) || (!rtl && (level & 1)))
116 limit = 0;
118 return limit;
121 template <typename T>
122 T maximum(T a, T b)
124 return (a > b)? a : b;
126 template <typename T>
127 T minimum(T a, T b)
129 return (a < b)? a : b;
132 } // namespace
134 // Impementation of the GraphiteLayout::Glyphs container class.
135 // This is an extended vector class with methods added to enable
136 // o Correctly filling with glyphs.
137 // o Querying clustering relationships.
138 // o manipulations that affect neighouring glyphs.
140 const int GraphiteLayout::EXTRA_CONTEXT_LENGTH = 10;
142 // find first slot of cluster and first slot of subsequent cluster
143 static void findFirstClusterSlot(const gr_slot* base, gr_slot const** first, gr_slot const** after, int * firstChar, int * lastChar, bool bRtl)
145 if (gr_slot_attached_to(base) == NULL)
147 *first = base;
148 *after = (bRtl)? gr_slot_prev_in_segment(base) :
149 gr_slot_next_in_segment(base);
150 *firstChar = gr_slot_before(base);
151 *lastChar = gr_slot_after(base);
153 const gr_slot * attachment = gr_slot_first_attachment(base);
154 while (attachment)
156 if (gr_slot_origin_X(*first) > gr_slot_origin_X(attachment))
157 *first = attachment;
158 const gr_slot* attachmentNext = (bRtl)?
159 gr_slot_prev_in_segment(attachment) : gr_slot_next_in_segment(attachment);
160 if (attachmentNext)
162 if (*after && (gr_slot_origin_X(*after) < gr_slot_origin_X(attachmentNext)))
163 *after = attachmentNext;
165 else
167 *after = NULL;
169 if (gr_slot_before(attachment) < *firstChar)
170 *firstChar = gr_slot_before(attachment);
171 if (gr_slot_after(attachment) > *lastChar)
172 *lastChar = gr_slot_after(attachment);
173 if (gr_slot_first_attachment(attachment))
174 findFirstClusterSlot(attachment, first, after, firstChar, lastChar, bRtl);
175 attachment = gr_slot_next_sibling_attachment(attachment);
179 // The Graphite glyph stream is really a sequence of glyph attachment trees
180 // each rooted at a non-attached base glyph. fill_from walks the glyph stream,
181 // finds each non-attached base glyph and calls append to record them as a
182 // sequence of clusters.
183 void
184 GraphiteLayout::fillFrom(gr_segment * pSegment, ImplLayoutArgs &rArgs, float fScaling)
186 bool bRtl = (rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL);
187 int nCharRequested = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
188 int nChar = gr_seg_n_cinfo(pSegment);
189 float fMinX = gr_seg_advance_X(pSegment);
190 float fMaxX = 0.0f;
191 long nDxOffset = 0; // from dropped glyphs
192 int nFirstCharInCluster = 0;
193 int nLastCharInCluster = 0;
194 unsigned int nGlyphs = gr_seg_n_slots(pSegment);
195 mvGlyph2Char.assign(nGlyphs, -1);
196 mvGlyphs.reserve(nGlyphs);
198 if (bRtl)
200 const gr_slot* baseSlot = gr_seg_last_slot(pSegment);
201 // find first base
202 while (baseSlot && (gr_slot_attached_to(baseSlot) != NULL))
203 baseSlot = gr_slot_prev_in_segment(baseSlot);
204 int iChar = nChar - 1;
205 int iNextChar = nChar - 1;
206 bool reordered = false;
207 int nBaseGlyphIndex = 0;
208 // now loop over bases
209 while (baseSlot)
211 bool bCluster = !reordered;
212 const gr_slot * clusterFirst = NULL;
213 const gr_slot * clusterAfter = NULL;
214 int firstChar = -1;
215 int lastChar = -1;
216 findFirstClusterSlot(baseSlot, &clusterFirst, &clusterAfter, &firstChar, &lastChar, bRtl);
217 iNextChar = minimum<int>(firstChar, iNextChar);
218 if (bCluster)
220 nBaseGlyphIndex = mvGlyphs.size();
221 mvGlyph2Char[nBaseGlyphIndex] = iChar + mnSegCharOffset;
222 nFirstCharInCluster = firstChar;
223 nLastCharInCluster = lastChar;
225 else
227 mvGlyph2Char[mvGlyphs.size()] = firstChar + mnSegCharOffset;
228 nFirstCharInCluster = minimum<int>(firstChar, nFirstCharInCluster);
229 nLastCharInCluster = maximum<int>(firstChar, nLastCharInCluster);
231 float leftBoundary = gr_slot_origin_X(clusterFirst);
232 float rightBoundary = (clusterAfter)?
233 gr_slot_origin_X(clusterAfter) : gr_seg_advance_X(pSegment);
234 if (
235 lastChar < iChar &&
236 (gr_cinfo_after(gr_seg_cinfo(pSegment, iChar)) >
237 static_cast<int>(gr_slot_index(clusterAfter)))
240 reordered = true;
242 else
244 reordered = false;
245 iChar = iNextChar - 1;
247 if (mnSegCharOffset + nFirstCharInCluster >= mnMinCharPos &&
248 mnSegCharOffset + nFirstCharInCluster < mnEndCharPos)
250 fMinX = minimum<float>(fMinX, leftBoundary);
251 fMaxX = maximum<float>(fMaxX, rightBoundary);
252 if (!reordered)
254 for (int i = nFirstCharInCluster; i <= nLastCharInCluster; i++)
256 if (mnSegCharOffset + i >= mnEndCharPos)
257 break;
258 // from the point of view of the dx array, the xpos is
259 // the origin of the first glyph of the cluster rtl
260 mvCharDxs[mnSegCharOffset + i - mnMinCharPos] =
261 static_cast<int>(leftBoundary * fScaling) + nDxOffset;
262 mvCharBreaks[mnSegCharOffset + i - mnMinCharPos] = gr_cinfo_break_weight(gr_seg_cinfo(pSegment, i));
264 mvChar2BaseGlyph[mnSegCharOffset + nFirstCharInCluster - mnMinCharPos] = nBaseGlyphIndex;
266 append(pSegment, rArgs, baseSlot, gr_slot_origin_X(baseSlot), rightBoundary, fScaling,
267 nDxOffset, bCluster, mnSegCharOffset + firstChar);
269 if (mnSegCharOffset + nLastCharInCluster < mnMinCharPos)
270 break;
271 baseSlot = gr_slot_next_sibling_attachment(baseSlot);
274 else
276 const gr_slot* baseSlot = gr_seg_first_slot(pSegment);
277 // find first base
278 while (baseSlot && (gr_slot_attached_to(baseSlot) != NULL))
279 baseSlot = gr_slot_next_in_segment(baseSlot);
280 int iChar = 0; // relative to segment
281 int iNextChar = 0;
282 bool reordered = false;
283 int nBaseGlyphIndex = 0;
284 // now loop over bases
285 while (baseSlot)
287 bool bCluster = !reordered;
288 const gr_slot * clusterFirst = NULL;
289 const gr_slot * clusterAfter = NULL;
290 int firstChar = -1;
291 int lastChar = -1;
292 findFirstClusterSlot(baseSlot, &clusterFirst, &clusterAfter, &firstChar, &lastChar, bRtl);
293 iNextChar = maximum<int>(lastChar, iNextChar);
294 if (bCluster)
296 nBaseGlyphIndex = mvGlyphs.size();
297 mvGlyph2Char[nBaseGlyphIndex] = iChar + mnSegCharOffset;
298 nFirstCharInCluster = firstChar;
299 nLastCharInCluster = lastChar;
301 else
303 mvGlyph2Char[mvGlyphs.size()] = firstChar + mnSegCharOffset;
304 nFirstCharInCluster = minimum<int>(firstChar, nFirstCharInCluster);
305 nLastCharInCluster = maximum<int>(lastChar, nLastCharInCluster);
307 if (
308 firstChar > iChar &&
309 (gr_cinfo_before(gr_seg_cinfo(pSegment, iChar)) >
310 static_cast<int>(gr_slot_index(clusterFirst)))
313 reordered = true;
315 else
317 reordered = false;
318 iChar = iNextChar + 1;
320 float leftBoundary = gr_slot_origin_X(clusterFirst);
321 float rightBoundary = (clusterAfter)?
322 gr_slot_origin_X(clusterAfter) : gr_seg_advance_X(pSegment);
323 int bFirstChar = gr_cinfo_base(gr_seg_cinfo(pSegment, nFirstCharInCluster));
324 if (mnSegCharOffset + bFirstChar >= mnMinCharPos &&
325 mnSegCharOffset + bFirstChar < mnEndCharPos)
327 fMinX = minimum<float>(fMinX, leftBoundary);
328 fMaxX = maximum<float>(fMaxX, rightBoundary);
329 if (!reordered)
331 for (int i = nFirstCharInCluster; i <= nLastCharInCluster; i++)
333 int ibase = gr_cinfo_base(gr_seg_cinfo(pSegment, i));
334 if (mnSegCharOffset + ibase >= mnEndCharPos)
335 break;
336 // from the point of view of the dx array, the xpos is
337 // the origin of the first glyph of the next cluster ltr
338 mvCharDxs[mnSegCharOffset + ibase - mnMinCharPos] =
339 static_cast<int>(rightBoundary * fScaling) + nDxOffset;
340 mvCharBreaks[mnSegCharOffset + ibase - mnMinCharPos] = gr_cinfo_break_weight(gr_seg_cinfo(pSegment, i));
342 // only set mvChar2BaseGlyph for first character of cluster
343 mvChar2BaseGlyph[mnSegCharOffset + bFirstChar - mnMinCharPos] = nBaseGlyphIndex;
345 append(pSegment, rArgs, baseSlot, gr_slot_origin_X(baseSlot), rightBoundary, fScaling,
346 nDxOffset, true, mnSegCharOffset + firstChar);
348 if (mnSegCharOffset + bFirstChar >= mnEndCharPos)
349 break;
350 baseSlot = gr_slot_next_sibling_attachment(baseSlot);
353 long nXOffset = round(fMinX * fScaling);
354 mnWidth = round(fMaxX * fScaling) - nXOffset + nDxOffset;
355 if (mnWidth < 0)
357 // This can happen when there was no base inside the range
358 mnWidth = 0;
360 // fill up non-base char dx with cluster widths from previous base glyph
361 if (bRtl)
363 if (mvCharDxs[nCharRequested-1] == -1)
364 mvCharDxs[nCharRequested-1] = 0;
365 else
366 mvCharDxs[nCharRequested-1] -= nXOffset;
367 for (int i = nCharRequested - 2; i >= 0; i--)
369 if (mvCharDxs[i] == -1) mvCharDxs[i] = mvCharDxs[i+1];
370 else mvCharDxs[i] -= nXOffset;
373 else
375 if (mvCharDxs[0] == -1)
376 mvCharDxs[0] = 0;
377 else
378 mvCharDxs[0] -= nXOffset;
379 for (int i = 1; i < nCharRequested; i++)
381 if (mvCharDxs[i] == -1) mvCharDxs[i] = mvCharDxs[i-1];
382 else mvCharDxs[i] -= nXOffset;
383 #ifdef GRLAYOUT_DEBUG
384 fprintf(grLog(),"%d,%d ", (int)i, (int)mvCharDxs[i]);
385 #endif
388 // remove offset due to context if there is one
389 if (nXOffset != 0)
391 for (size_t i = 0; i < mvGlyphs.size(); i++)
392 mvGlyphs[i].maLinearPos.X() -= nXOffset;
394 #ifdef GRLAYOUT_DEBUG
395 fprintf(grLog(), "fillFrom %" SAL_PRI_SIZET "u glyphs offset %ld width %ld\n", mvGlyphs.size(), nXOffset, mnWidth);
396 #endif
399 // append walks an attachment tree, flattening it, and converting it into a
400 // sequence of GlyphItem objects which we can later manipulate.
401 float
402 GraphiteLayout::append(gr_segment *pSeg, ImplLayoutArgs &rArgs,
403 const gr_slot * gi, float gOrigin, float nextGlyphOrigin, float scaling, long & rDXOffset,
404 bool bIsBase, int baseChar)
406 bool bRtl = (rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL);
407 float nextOrigin;
408 assert(gi);
409 assert(gr_slot_before(gi) <= gr_slot_after(gi));
410 int firstChar = gr_slot_before(gi) + mnSegCharOffset;
411 assert(mvGlyphs.size() < mvGlyph2Char.size());
412 if (!bIsBase) mvGlyph2Char[mvGlyphs.size()] = baseChar;//firstChar;
413 // is the next glyph attached or in the next cluster?
414 //glyph_set_range_t iAttached = gi.attachedClusterGlyphs();
415 const gr_slot * pFirstAttached = gr_slot_first_attachment(gi);
416 const gr_slot * pNextSibling = gr_slot_next_sibling_attachment(gi);
417 if (pFirstAttached)
418 nextOrigin = gr_slot_origin_X(pFirstAttached);
419 else if (!bIsBase && pNextSibling)
420 nextOrigin = gr_slot_origin_X(pNextSibling);
421 else
422 nextOrigin = nextGlyphOrigin;
423 long glyphId = gr_slot_gid(gi);
424 long deltaOffset = 0;
425 int scaledGlyphPos = round(gr_slot_origin_X(gi) * scaling);
426 int glyphWidth = round((nextOrigin - gOrigin) * scaling);
427 // if (glyphWidth < 0)
428 // {
429 // nextOrigin = gOrigin;
430 // glyphWidth = 0;
431 // }
432 #ifdef GRLAYOUT_DEBUG
433 fprintf(grLog(),"c%d g%ld,X%d W%d nX%f ", firstChar, glyphId,
434 (int)(gr_slot_origin_X(gi) * scaling), glyphWidth, nextOrigin * scaling);
435 #endif
436 if (glyphId == 0)
438 rArgs.NeedFallback(firstChar, bRtl);
439 if( (SAL_LAYOUT_FOR_FALLBACK & rArgs.mnFlags ))
441 glyphId = GF_DROPPED;
442 deltaOffset -= glyphWidth;
443 glyphWidth = 0;
446 else if(rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK)
448 #ifdef GRLAYOUT_DEBUG
449 fprintf(grLog(),"fallback c%d %x in run %d\n", firstChar, rArgs.mpStr[firstChar],
450 rArgs.maRuns.PosIsInAnyRun(firstChar));
451 #endif
452 // glyphs that aren't requested for fallback will be taken from base
453 // layout, so mark them as dropped (should this wait until Simplify(false) is called?)
454 if (!rArgs.maRuns.PosIsInAnyRun(firstChar) &&
455 in_range(firstChar, rArgs.mnMinCharPos, rArgs.mnEndCharPos))
457 glyphId = GF_DROPPED;
458 deltaOffset -= glyphWidth;
459 glyphWidth = 0;
462 // append this glyph. Set the cluster flag if this glyph is attached to another
463 long nGlyphFlags = bIsBase ? 0 : GlyphItem::IS_IN_CLUSTER;
464 nGlyphFlags |= (bRtl)? GlyphItem::IS_RTL_GLYPH : 0;
465 GlyphItem aGlyphItem(mvGlyphs.size(),
466 glyphId,
467 Point(scaledGlyphPos + rDXOffset,
468 round((-gr_slot_origin_Y(gi) * scaling))),
469 nGlyphFlags,
470 glyphWidth);
471 if (glyphId != static_cast<long>(GF_DROPPED))
472 aGlyphItem.mnOrigWidth = round(gr_slot_advance_X(gi, mpFace, mpFont) * scaling);
473 mvGlyphs.push_back(aGlyphItem);
475 // update the offset if this glyph was dropped
476 rDXOffset += deltaOffset;
478 // Recursively append all the attached glyphs.
479 float cOrigin = nextOrigin;
480 for (const gr_slot * agi = gr_slot_first_attachment(gi); agi != NULL; agi = gr_slot_next_sibling_attachment(agi))
481 cOrigin = append(pSeg, rArgs, agi, cOrigin, nextGlyphOrigin, scaling, rDXOffset, false, baseChar);
483 return cOrigin;
487 // An implementation of the SalLayout interface to enable Graphite enabled fonts to be used.
489 GraphiteLayout::GraphiteLayout(const gr_face * face, gr_font * font,
490 const grutils::GrFeatureParser * pFeatures) throw()
491 : mpFace(face),
492 mpFont(font),
493 mnWidth(0),
494 mfScaling(1.0),
495 mpFeatures(pFeatures)
500 GraphiteLayout::~GraphiteLayout() throw()
502 clear();
503 // the features and font are owned by the platform layers
504 mpFeatures = NULL;
505 mpFont = NULL;
508 void GraphiteLayout::clear()
510 // Destroy the segment and text source from any previous invocation of
511 // LayoutText
512 mvGlyphs.clear();
513 mvCharDxs.clear();
514 mvChar2BaseGlyph.clear();
515 mvGlyph2Char.clear();
517 // Reset the state to the empty state.
518 mnWidth = 0;
519 // Don't reset the scaling, because it is set before LayoutText
522 // This method shouldn't be called on windows, since it needs the dc reset
523 bool GraphiteLayout::LayoutText(ImplLayoutArgs & rArgs)
525 bool success = true;
526 if (rArgs.mnMinCharPos < rArgs.mnEndCharPos)
528 gr_segment * pSegment = CreateSegment(rArgs);
529 if (!pSegment)
530 return false;
531 success = LayoutGlyphs(rArgs, pSegment);
532 if (pSegment)
534 gr_seg_destroy(pSegment);
535 pSegment = NULL;
538 else
540 clear();
542 return success;
546 gr_segment * GraphiteLayout::CreateSegment(ImplLayoutArgs& rArgs)
548 assert(rArgs.mnLength >= 0);
550 gr_segment * pSegment = NULL;
552 // Set the SalLayouts values to be the initial ones.
553 SalLayout::AdjustLayout(rArgs);
554 // TODO check if this is needed
555 if (mnUnitsPerPixel > 1)
556 mfScaling = 1.0f / mnUnitsPerPixel;
558 // Clear out any previous buffers
559 clear();
560 bool bRtl = mnLayoutFlags & SAL_LAYOUT_BIDI_RTL;
563 // Don't set RTL if font doesn't support it otherwise it forces rtl on
564 // everything
565 //if (bRtl && (mrFont.getSupportedScriptDirections() & gr::kfsdcHorizRtl))
566 // maLayout.setRightToLeft(bRtl);
568 // Context is often needed beyond the specified end, however, we don't
569 // want it if there has been a direction change, since it is hard
570 // to tell between reordering within one direction and multi-directional
571 // text. Extra context, can also cause problems with ligatures stradling
572 // a hyphenation point, so disable if CTL is disabled.
573 mnSegCharOffset = rArgs.mnMinCharPos;
574 int limit = rArgs.mnEndCharPos;
575 if (!(SAL_LAYOUT_COMPLEX_DISABLED & rArgs.mnFlags))
577 const int nSegCharMin = maximum<int>(0, mnMinCharPos - EXTRA_CONTEXT_LENGTH);
578 const int nSegCharLimit = minimum(rArgs.mnLength, mnEndCharPos + EXTRA_CONTEXT_LENGTH);
579 if (nSegCharMin < mnSegCharOffset)
581 int sameDirEnd = findSameDirLimit(rArgs.mpStr + nSegCharMin,
582 rArgs.mnEndCharPos - nSegCharMin, bRtl);
583 if (sameDirEnd == rArgs.mnEndCharPos)
584 mnSegCharOffset = nSegCharMin;
586 if (nSegCharLimit > limit)
588 limit += findSameDirLimit(rArgs.mpStr + rArgs.mnEndCharPos,
589 nSegCharLimit - rArgs.mnEndCharPos, bRtl);
592 // int numchars = gr_count_unicode_characters(gr_utf16, rArgs.mpStr + mnSegCharOffset,
593 // rArgs.mpStr + (rArgs.mnLength > limit + 64 ? limit + 64 : rArgs.mnLength), NULL);
594 int numchars = rArgs.mnEndCharPos - mnSegCharOffset; // fdo#52540, fdo#68313, FIXME
595 if (mpFeatures)
596 pSegment = gr_make_seg(mpFont, mpFace, 0, mpFeatures->values(), gr_utf16,
597 rArgs.mpStr + mnSegCharOffset, numchars, bRtl);
598 else
599 pSegment = gr_make_seg(mpFont, mpFace, 0, NULL, gr_utf16,
600 rArgs.mpStr + mnSegCharOffset, numchars, bRtl);
602 //pSegment = new gr::RangeSegment((gr::Font *)&mrFont, mpTextSrc, &maLayout, mnMinCharPos, limit);
603 if (pSegment != NULL)
605 #ifdef GRLAYOUT_DEBUG
606 fprintf(grLog(),"Gr::LayoutText %d-%d, context %d, len %d, numchars %" SAL_PRI_SIZET "u, rtl %d scaling %f:", rArgs.mnMinCharPos,
607 rArgs.mnEndCharPos, limit, rArgs.mnLength, numchars, bRtl, mfScaling);
608 for (int i = mnSegCharOffset; i < limit; ++i)
609 fprintf(grLog(), " %04X", rArgs.mpStr[i]);
610 fprintf(grLog(), "\n");
611 #endif
613 else
615 #ifdef GRLAYOUT_DEBUG
616 fprintf(grLog(), "Gr::LayoutText failed: ");
617 for (int i = mnMinCharPos; i < limit; i++)
619 fprintf(grLog(), "%04x ", rArgs.mpStr[i]);
621 fprintf(grLog(), "\n");
622 #endif
623 clear();
624 return NULL;
627 catch (...)
629 clear(); // destroy the text source and any partially built segments.
630 return NULL;
632 return pSegment;
635 bool GraphiteLayout::LayoutGlyphs(ImplLayoutArgs& rArgs, gr_segment * pSegment)
637 // Calculate the initial character dxs.
638 mvCharDxs.assign(mnEndCharPos - mnMinCharPos, -1);
639 mvChar2BaseGlyph.assign(mnEndCharPos - mnMinCharPos, -1);
640 mvCharBreaks.assign(mnEndCharPos - mnMinCharPos, 0);
641 mnWidth = 0;
642 if (mvCharDxs.size() > 0)
644 // Discover all the clusters.
647 bool bRtl = mnLayoutFlags & SAL_LAYOUT_BIDI_RTL;
648 fillFrom(pSegment, rArgs, mfScaling);
650 if (bRtl)
652 // not needed for adjacent differences, but for mouse clicks to char
653 std::transform(mvCharDxs.begin(), mvCharDxs.end(), mvCharDxs.begin(),
654 std::bind1st(std::minus<long>(), mnWidth));
655 // fixup last dx to ensure it always equals the width
656 mvCharDxs[mvCharDxs.size() - 1] = mnWidth;
659 catch (const std::exception &e)
661 #ifdef GRLAYOUT_DEBUG
662 fprintf(grLog(),"LayoutGlyphs failed %s\n", e.what());
663 #else
664 (void)e;
665 #endif
666 return false;
668 catch (...)
670 #ifdef GRLAYOUT_DEBUG
671 fprintf(grLog(),"LayoutGlyphs failed with exception");
672 #endif
673 return false;
676 else
678 mnWidth = 0;
680 return true;
683 int GraphiteLayout::GetTextBreak(long maxmnWidth, long char_extra, int factor) const
685 #ifdef GRLAYOUT_DEBUG
686 fprintf(grLog(),"Gr::GetTextBreak c[%d-%d) maxWidth %ld char extra %ld factor %d\n",
687 mnMinCharPos, mnEndCharPos, maxmnWidth, char_extra, factor);
688 #endif
690 // return quickly if this segment is narrower than the target width
691 if (maxmnWidth > mnWidth * factor + char_extra * (mnEndCharPos - mnMinCharPos - 1))
692 return STRING_LEN;
694 long nWidth = mvCharDxs[0] * factor;
695 long wLastBreak = 0;
696 int nLastBreak = -1;
697 int nEmergency = -1;
698 for (size_t i = 1; i < mvCharDxs.size(); i++)
700 nWidth += char_extra;
701 if (nWidth > maxmnWidth) break;
702 if (mvChar2BaseGlyph[i] != -1)
704 if (
705 (mvCharBreaks[i] > -35 || (mvCharBreaks[i-1] > 0 && mvCharBreaks[i-1] < 35)) &&
706 (mvCharBreaks[i-1] < 35 || (mvCharBreaks[i] < 0 && mvCharBreaks[i] > -35))
709 nLastBreak = static_cast<int>(i);
710 wLastBreak = nWidth;
712 nEmergency = static_cast<int>(i);
714 nWidth += (mvCharDxs[i] - mvCharDxs[i-1]) * factor;
716 int nBreak = mnMinCharPos;
717 if (wLastBreak > 9 * maxmnWidth / 10)
718 nBreak += nLastBreak;
719 else
720 if (nEmergency > -1)
721 nBreak += nEmergency;
723 #ifdef GRLAYOUT_DEBUG
724 fprintf(grLog(), "Gr::GetTextBreak break after %d, weights(%d, %d)\n", nBreak - mnMinCharPos, mvCharBreaks[nBreak - mnMinCharPos], mvCharBreaks[nBreak - mnMinCharPos - 1]);
725 #endif
727 if (nBreak > mnEndCharPos) nBreak = STRING_LEN;
728 else if (nBreak < mnMinCharPos) nBreak = mnMinCharPos;
729 return nBreak;
732 long GraphiteLayout::FillDXArray( sal_Int32* pDXArray ) const
734 if (mnEndCharPos == mnMinCharPos)
735 // Then we must be zero width!
736 return 0;
738 if (pDXArray)
740 for (size_t i = 0; i < mvCharDxs.size(); i++)
742 assert( (mvChar2BaseGlyph[i] == -1) ||
743 ((signed)(mvChar2BaseGlyph[i]) < (signed)mvGlyphs.size()));
744 if (mvChar2BaseGlyph[i] != -1 &&
745 mvGlyphs[mvChar2BaseGlyph[i]].mnGlyphIndex == GF_DROPPED)
747 // when used in MultiSalLayout::GetTextBreak dropped glyphs
748 // must have zero width
749 pDXArray[i] = 0;
751 else
753 pDXArray[i] = mvCharDxs[i];
754 if (i > 0) pDXArray[i] -= mvCharDxs[i-1];
756 #ifdef GRLAYOUT_DEBUG
757 fprintf(grLog(),"%d,%d,%d ", (int)i, (int)mvCharDxs[i], pDXArray[i]);
758 #endif
760 //std::adjacent_difference(mvCharDxs.begin(), mvCharDxs.end(), pDXArray);
761 //for (size_t i = 0; i < mvCharDxs.size(); i++)
762 // fprintf(grLog(),"%d,%d,%d ", (int)i, (int)mvCharDxs[i], pDXArray[i]);
763 //fprintf(grLog(),"FillDX %ld,%d\n", mnWidth, std::accumulate(pDXArray, pDXArray + mvCharDxs.size(), 0));
765 #ifdef GRLAYOUT_DEBUG
766 fprintf(grLog(),"FillDXArray %d-%d=%ld\n", mnMinCharPos, mnEndCharPos, mnWidth);
767 #endif
768 return mnWidth;
771 void GraphiteLayout::AdjustLayout(ImplLayoutArgs& rArgs)
773 SalLayout::AdjustLayout(rArgs);
774 if(rArgs.mpDXArray)
776 std::vector<int> vDeltaWidths(mvGlyphs.size(), 0);
777 ApplyDXArray(rArgs, vDeltaWidths);
779 if( (mnLayoutFlags & SAL_LAYOUT_BIDI_RTL) &&
780 !(rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK) )
782 // check if this is a kashida script
783 bool bKashidaScript = false;
784 for (int i = rArgs.mnMinCharPos; i < rArgs.mnEndCharPos; i++)
786 UErrorCode aStatus = U_ZERO_ERROR;
787 UScriptCode scriptCode = uscript_getScript(rArgs.mpStr[i], &aStatus);
788 if (scriptCode == USCRIPT_ARABIC || scriptCode == USCRIPT_SYRIAC)
790 bKashidaScript = true;
791 break;
794 int nKashidaWidth = 0;
795 int nKashidaIndex = getKashidaGlyph(nKashidaWidth);
796 if( nKashidaIndex != 0 && bKashidaScript)
798 kashidaJustify( vDeltaWidths, nKashidaIndex, nKashidaWidth );
802 else if (rArgs.mnLayoutWidth > 0)
804 #ifdef GRLAYOUT_DEBUG
805 fprintf(grLog(), "AdjustLayout width %ld=>%ld\n", mnWidth, rArgs.mnLayoutWidth);
806 #endif
807 expandOrCondense(rArgs);
811 void GraphiteLayout::expandOrCondense(ImplLayoutArgs &rArgs)
813 int nDeltaWidth = rArgs.mnLayoutWidth - mnWidth;
814 if (nDeltaWidth > 0) // expand, just expand between clusters
816 // NOTE: for expansion we can use base glyphs (which have IsClusterStart set)
817 // even though they may have been reordered in which case they will have
818 // been placed in a bigger cluster for other purposes.
819 int nClusterCount = 0;
820 for (size_t j = 0; j < mvGlyphs.size(); j++)
822 if (mvGlyphs[j].IsClusterStart())
824 ++nClusterCount;
827 if (nClusterCount > 1)
829 float fExtraPerCluster = static_cast<float>(nDeltaWidth) / static_cast<float>(nClusterCount - 1);
830 int nCluster = 0;
831 int nOffset = 0;
832 for (size_t i = 0; i < mvGlyphs.size(); i++)
834 if (mvGlyphs[i].IsClusterStart())
836 nOffset = static_cast<int>(fExtraPerCluster * nCluster);
837 int nCharIndex = mvGlyph2Char[i];
838 assert(nCharIndex > -1);
839 if (nCharIndex < mnMinCharPos ||
840 static_cast<size_t>(nCharIndex-mnMinCharPos)
841 >= mvCharDxs.size())
843 continue;
845 mvCharDxs[nCharIndex-mnMinCharPos] += nOffset;
846 // adjust char dxs for rest of characters in cluster
847 while (++nCharIndex - mnMinCharPos < static_cast<int>(mvChar2BaseGlyph.size()))
849 int nChar2Base = mvChar2BaseGlyph[nCharIndex-mnMinCharPos];
850 if (nChar2Base == -1 || nChar2Base == static_cast<int>(i))
851 mvCharDxs[nCharIndex-mnMinCharPos] += nOffset;
852 else
853 break;
855 ++nCluster;
857 mvGlyphs[i].maLinearPos.X() += nOffset;
861 else if (nDeltaWidth < 0)// condense - apply a factor to all glyph positions
863 if (mvGlyphs.empty()) return;
864 Glyphs::iterator iLastGlyph = mvGlyphs.begin() + (mvGlyphs.size() - 1);
865 // position last glyph using original width
866 float fXFactor = static_cast<float>(rArgs.mnLayoutWidth - iLastGlyph->mnOrigWidth) / static_cast<float>(iLastGlyph->maLinearPos.X());
867 #ifdef GRLAYOUT_DEBUG
868 fprintf(grLog(), "Condense by factor %f last x%ld\n", fXFactor, iLastGlyph->maLinearPos.X());
869 #endif
870 if (fXFactor < 0)
871 return; // probably a bad mnOrigWidth value
872 iLastGlyph->maLinearPos.X() = rArgs.mnLayoutWidth - iLastGlyph->mnOrigWidth;
873 Glyphs::iterator iGlyph = mvGlyphs.begin();
874 while (iGlyph != iLastGlyph)
876 iGlyph->maLinearPos.X() = static_cast<int>(static_cast<float>(iGlyph->maLinearPos.X()) * fXFactor);
877 ++iGlyph;
879 for (size_t i = 0; i < mvCharDxs.size(); i++)
881 mvCharDxs[i] = static_cast<int>(fXFactor * static_cast<float>(mvCharDxs[i]));
884 mnWidth = rArgs.mnLayoutWidth;
887 void GraphiteLayout::ApplyDXArray(ImplLayoutArgs &args, std::vector<int> & rDeltaWidth)
889 const size_t nChars = args.mnEndCharPos - args.mnMinCharPos;
890 if (nChars == 0) return;
892 #ifdef GRLAYOUT_DEBUG
893 for (size_t iDx = 0; iDx < mvCharDxs.size(); iDx++)
894 fprintf(grLog(),"%d,%d,%d ", (int)iDx, (int)mvCharDxs[iDx], args.mpDXArray[iDx]);
895 fprintf(grLog(),"ApplyDx\n");
896 #endif
897 bool bRtl = mnLayoutFlags & SAL_LAYOUT_BIDI_RTL;
898 int nXOffset = 0;
899 if (bRtl)
901 nXOffset = args.mpDXArray[nChars - 1] - mvCharDxs[nChars - 1];
903 int nPrevClusterGlyph = (bRtl)? (signed)mvGlyphs.size() : -1;
904 int nPrevClusterLastChar = -1;
905 for (size_t i = 0; i < nChars; i++)
907 int nChar2Base = mvChar2BaseGlyph[i];
908 if ((nChar2Base > -1) && (nChar2Base != nPrevClusterGlyph))
910 assert((nChar2Base > -1) && (nChar2Base < (signed)mvGlyphs.size()));
911 GlyphItem & gi = mvGlyphs[nChar2Base];
912 if (!gi.IsClusterStart())
913 continue;
915 // find last glyph of this cluster
916 size_t j = i + 1;
917 int nLastChar = i;
918 int nLastGlyph = nChar2Base;
919 int nChar2BaseJ = -1;
920 for (; j < nChars; j++)
922 nChar2BaseJ = mvChar2BaseGlyph[j];
923 assert((nChar2BaseJ >= -1) && (nChar2BaseJ < (signed)mvGlyphs.size()));
924 if (nChar2BaseJ != -1 )
926 nLastGlyph = nChar2BaseJ + ((bRtl)? +1 : -1);
927 nLastChar = j - 1;
928 break;
931 if (nLastGlyph < 0)
933 nLastGlyph = nChar2Base;
935 // Its harder to find the last glyph rtl, since the first of
936 // cluster is still on the left so we need to search towards
937 // the previous cluster to the right
938 if (bRtl)
940 nLastGlyph = nChar2Base;
941 while (nLastGlyph + 1 < (signed)mvGlyphs.size() &&
942 !mvGlyphs[nLastGlyph+1].IsClusterStart())
944 ++nLastGlyph;
947 if (j == nChars)
949 nLastChar = nChars - 1;
950 if (!bRtl) nLastGlyph = mvGlyphs.size() - 1;
952 int nBaseCount = 0;
953 // count bases within cluster - may be more than 1 with reordering
954 for (int k = nChar2Base; k <= nLastGlyph; k++)
956 if (mvGlyphs[k].IsClusterStart()) ++nBaseCount;
958 assert((nLastChar > -1) && (nLastChar < (signed)nChars));
959 long nNewClusterWidth = args.mpDXArray[nLastChar];
960 long nOrigClusterWidth = mvCharDxs[nLastChar];
961 long nDGlyphOrigin = 0;
962 if (nPrevClusterLastChar > - 1)
964 assert(nPrevClusterLastChar < (signed)nChars);
965 nNewClusterWidth -= args.mpDXArray[nPrevClusterLastChar];
966 nOrigClusterWidth -= mvCharDxs[nPrevClusterLastChar];
967 nDGlyphOrigin = args.mpDXArray[nPrevClusterLastChar] - mvCharDxs[nPrevClusterLastChar];
969 long nDWidth = nNewClusterWidth - nOrigClusterWidth;
970 #ifdef GRLAYOUT_DEBUG
971 fprintf(grLog(), "c%lu last glyph %d/%lu\n", i, nLastGlyph, mvGlyphs.size());
972 #endif
973 assert((nLastGlyph > -1) && (nLastGlyph < (signed)mvGlyphs.size()));
974 mvGlyphs[nLastGlyph].mnNewWidth += nDWidth;
975 if (gi.mnGlyphIndex != GF_DROPPED)
976 mvGlyphs[nLastGlyph].mnNewWidth += nDWidth;
977 else
978 nDGlyphOrigin += nDWidth;
979 long nDOriginPerBase = (nBaseCount > 0)? nDWidth / nBaseCount : 0;
980 nBaseCount = -1;
981 // update glyph positions
982 if (bRtl)
984 for (int n = nChar2Base; n <= nLastGlyph; n++)
986 if (mvGlyphs[n].IsClusterStart()) ++nBaseCount;
987 assert((n > - 1) && (n < (signed)mvGlyphs.size()));
988 mvGlyphs[n].maLinearPos.X() += -(nDGlyphOrigin + nDOriginPerBase * nBaseCount) + nXOffset;
991 else
993 for (int n = nChar2Base; n <= nLastGlyph; n++)
995 if (mvGlyphs[n].IsClusterStart()) ++nBaseCount;
996 assert((n > - 1) && (n < (signed)mvGlyphs.size()));
997 mvGlyphs[n].maLinearPos.X() += nDGlyphOrigin + (nDOriginPerBase * nBaseCount) + nXOffset;
1000 rDeltaWidth[nChar2Base] = nDWidth;
1001 #ifdef GRLAYOUT_DEBUG
1002 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());
1003 #endif
1004 nPrevClusterGlyph = nChar2Base;
1005 nPrevClusterLastChar = nLastChar;
1006 i = nLastChar;
1009 // Update the dx vector with the new values.
1010 std::copy(args.mpDXArray, args.mpDXArray + nChars,
1011 mvCharDxs.begin() + (args.mnMinCharPos - mnMinCharPos));
1012 #ifdef GRLAYOUT_DEBUG
1013 fprintf(grLog(),"ApplyDx %d(%ld)\n", args.mpDXArray[nChars - 1], mnWidth);
1014 #endif
1015 mnWidth = args.mpDXArray[nChars - 1];
1018 void GraphiteLayout::kashidaJustify(std::vector<int>& rDeltaWidths, sal_GlyphId nKashidaIndex, int nKashidaWidth)
1020 // skip if the kashida glyph in the font looks suspicious
1021 if( nKashidaWidth <= 0 )
1022 return;
1024 // calculate max number of needed kashidas
1025 Glyphs::iterator i = mvGlyphs.begin();
1026 int nKashidaCount = 0;
1027 int nOrigGlyphIndex = -1;
1028 int nGlyphIndex = -1;
1029 while (i != mvGlyphs.end())
1031 nOrigGlyphIndex++;
1032 nGlyphIndex++;
1033 // only inject kashidas in RTL contexts
1034 if( !(*i).IsRTLGlyph() )
1036 ++i;
1037 continue;
1039 // no kashida-injection for blank justified expansion either
1040 if( IsSpacingGlyph( (*i).mnGlyphIndex ) )
1042 ++i;
1043 continue;
1045 // calculate gap, ignore if too small
1046 int nGapWidth = rDeltaWidths[nOrigGlyphIndex];
1047 // worst case is one kashida even for mini-gaps
1048 if( 3 * nGapWidth < nKashidaWidth )
1050 ++i;
1051 continue;
1053 nKashidaCount = 1 + (nGapWidth / nKashidaWidth);
1054 #ifdef GRLAYOUT_DEBUG
1055 printf("inserting %d kashidas at %u\n", nKashidaCount, (*i).mnGlyphIndex);
1056 #endif
1057 GlyphItem glyphItem = *i;
1058 Point aPos(0, 0);
1059 aPos.X() = (*i).maLinearPos.X();
1060 GlyphItem newGi(glyphItem.mnCharPos, nKashidaIndex, aPos,
1061 GlyphItem::IS_IN_CLUSTER|GlyphItem::IS_RTL_GLYPH, nKashidaWidth);
1062 mvGlyphs.reserve(mvGlyphs.size() + nKashidaCount);
1063 i = mvGlyphs.begin() + nGlyphIndex;
1064 mvGlyphs.insert(i, nKashidaCount, newGi);
1065 i = mvGlyphs.begin() + nGlyphIndex;
1066 nGlyphIndex += nKashidaCount;
1067 // now fix up the kashida positions
1068 for (int j = 0; j < nKashidaCount; j++)
1070 (*(i)).maLinearPos.X() -= nGapWidth;
1071 nGapWidth -= nKashidaWidth;
1072 ++i;
1075 // fixup rightmost kashida for gap remainder
1076 if( nGapWidth < 0 )
1078 if( nKashidaCount <= 1 )
1079 nGapWidth /= 2; // for small gap move kashida to middle
1080 (*(i-1)).mnNewWidth += nGapWidth; // adjust kashida width to gap width
1081 (*(i-1)).maLinearPos.X() += nGapWidth;
1084 (*i).mnNewWidth = (*i).mnOrigWidth;
1085 ++i;
1090 void GraphiteLayout::GetCaretPositions( int nArraySize, sal_Int32* pCaretXArray ) const
1092 // For each character except the last discover the caret positions
1093 // immediately before and after that character.
1094 // This is used for underlines in the GUI amongst other things.
1095 // It may be used from MultiSalLayout, in which case it must take into account
1096 // glyphs that have been moved.
1097 std::fill(pCaretXArray, pCaretXArray + nArraySize, -1);
1098 // the layout method doesn't modify the layout even though it isn't
1099 // const in the interface
1100 bool bRtl = (mnLayoutFlags & SAL_LAYOUT_BIDI_RTL);//const_cast<GraphiteLayout*>(this)->maLayout.rightToLeft();
1101 int prevBase = -1;
1102 long prevClusterWidth = 0;
1103 for (int i = 0, nCharSlot = 0; i < nArraySize && nCharSlot < static_cast<int>(mvCharDxs.size()); ++nCharSlot, i+=2)
1105 if (mvChar2BaseGlyph[nCharSlot] != -1)
1107 int nChar2Base = mvChar2BaseGlyph[nCharSlot];
1108 assert((nChar2Base > -1) && (nChar2Base < (signed)mvGlyphs.size()));
1109 GlyphItem gi = mvGlyphs[nChar2Base];
1110 if (gi.mnGlyphIndex == GF_DROPPED)
1112 continue;
1114 int nCluster = nChar2Base;
1115 long origClusterWidth = gi.mnNewWidth;
1116 long nMin = gi.maLinearPos.X();
1117 long nMax = gi.maLinearPos.X() + gi.mnNewWidth;
1118 // attached glyphs are always stored after their base rtl or ltr
1119 while (++nCluster < static_cast<int>(mvGlyphs.size()) &&
1120 !mvGlyphs[nCluster].IsClusterStart())
1122 origClusterWidth += mvGlyphs[nCluster].mnNewWidth;
1123 if (mvGlyph2Char[nCluster] == nCharSlot)
1125 nMin = minimum(nMin, mvGlyphs[nCluster].maLinearPos.X());
1126 nMax = maximum(nMax, mvGlyphs[nCluster].maLinearPos.X() + mvGlyphs[nCluster].mnNewWidth);
1129 if (bRtl)
1131 pCaretXArray[i+1] = nMin;
1132 pCaretXArray[i] = nMax;
1134 else
1136 pCaretXArray[i] = nMin;
1137 pCaretXArray[i+1] = nMax;
1139 prevBase = nChar2Base;
1140 prevClusterWidth = origClusterWidth;
1142 else if (prevBase > -1)
1144 // this could probably be improved
1145 assert((prevBase > -1) && (prevBase < (signed)mvGlyphs.size()));
1146 GlyphItem gi = mvGlyphs[prevBase];
1147 int nGlyph = prevBase + 1;
1148 // try to find a better match, otherwise default to complete cluster
1149 for (; nGlyph < static_cast<int>(mvGlyphs.size()) &&
1150 !mvGlyphs[nGlyph].IsClusterStart(); nGlyph++)
1152 if (mvGlyph2Char[nGlyph] == nCharSlot)
1154 gi = mvGlyphs[nGlyph];
1155 break;
1158 // if no match position at end of cluster
1159 if (nGlyph == static_cast<int>(mvGlyphs.size()) ||
1160 mvGlyphs[nGlyph].IsClusterStart())
1162 if (bRtl)
1164 pCaretXArray[i+1] = gi.maLinearPos.X();
1165 pCaretXArray[i] = gi.maLinearPos.X();
1167 else
1169 pCaretXArray[i] = gi.maLinearPos.X() + prevClusterWidth;
1170 pCaretXArray[i+1] = gi.maLinearPos.X() + prevClusterWidth;
1173 else
1175 if (bRtl)
1177 pCaretXArray[i+1] = gi.maLinearPos.X();
1178 pCaretXArray[i] = gi.maLinearPos.X() + gi.mnNewWidth;
1180 else
1182 pCaretXArray[i] = gi.maLinearPos.X();
1183 pCaretXArray[i+1] = gi.maLinearPos.X() + gi.mnNewWidth;
1187 else
1189 pCaretXArray[i] = pCaretXArray[i+1] = 0;
1191 #ifdef GRLAYOUT_DEBUG
1192 fprintf(grLog(),"%d,%d-%d\t", nCharSlot, pCaretXArray[i], pCaretXArray[i+1]);
1193 #endif
1195 #ifdef GRLAYOUT_DEBUG
1196 fprintf(grLog(),"\n");
1197 #endif
1200 // GetNextGlyphs returns a contiguous sequence of glyphs that can be
1201 // rendered together. It should never return a dropped glyph.
1202 // The glyph_slot returned should be the index of the next visible
1203 // glyph after the last glyph returned by this call.
1204 // The char_index array should be filled with the characters corresponding
1205 // to each glyph returned.
1206 // glyph_adv array should be a virtual width such that if successive
1207 // glyphs returned by this method are added one after the other they
1208 // have the correct spacing.
1209 // The logic in this method must match that expected in MultiSalLayout which
1210 // is used when glyph fallback is in operation.
1211 int GraphiteLayout::GetNextGlyphs( int length, sal_GlyphId * glyph_out,
1212 ::Point & aPosOut, int &glyph_slot, sal_Int32 * glyph_adv, int *char_index,
1213 const PhysicalFontFace** /*pFallbackFonts*/ ) const
1215 // Sanity check on the slot index.
1216 if (glyph_slot >= signed(mvGlyphs.size()))
1218 glyph_slot = mvGlyphs.size();
1219 return 0;
1221 assert(glyph_slot >= 0);
1222 // Find the first glyph in the substring.
1223 for (; glyph_slot < signed(mvGlyphs.size()) &&
1224 ((mvGlyphs.begin() + glyph_slot)->mnGlyphIndex == GF_DROPPED);
1225 ++glyph_slot) {};
1227 // Update the length
1228 const int nGlyphSlotEnd = minimum(size_t(glyph_slot + length), mvGlyphs.size());
1230 // We're all out of glyphs here.
1231 if (glyph_slot == nGlyphSlotEnd)
1233 return 0;
1236 // Find as many glyphs as we can which can be drawn in one go.
1237 Glyphs::const_iterator glyph_itr = mvGlyphs.begin() + glyph_slot;
1238 const int glyph_slot_begin = glyph_slot;
1239 const int initial_y_pos = glyph_itr->maLinearPos.Y();
1241 // Set the position to the position of the start glyph.
1242 ::Point aStartPos = glyph_itr->maLinearPos;
1243 //aPosOut = glyph_itr->maLinearPos;
1244 aPosOut = GetDrawPosition(aStartPos);
1246 for (;;) // Forever
1248 // last index of the range from glyph_to_chars does not include this glyph
1249 if (char_index)
1251 if (glyph_slot >= (signed)mvGlyph2Char.size())
1253 *char_index++ = mnMinCharPos + mvCharDxs.size();
1255 else
1257 assert(glyph_slot > -1);
1258 if (mvGlyph2Char[glyph_slot] == -1)
1259 *char_index++ = mnMinCharPos + mvCharDxs.size();
1260 else
1261 *char_index++ = mvGlyph2Char[glyph_slot];
1264 // Copy out this glyphs data.
1265 ++glyph_slot;
1266 *glyph_out++ = glyph_itr->mnGlyphIndex;
1268 // Find the actual advance - this must be correct if called from
1269 // MultiSalLayout::AdjustLayout which requests one glyph at a time.
1270 const long nGlyphAdvance = (glyph_slot == static_cast<int>(mvGlyphs.size()))?
1271 glyph_itr->mnNewWidth :
1272 ((glyph_itr+1)->maLinearPos.X() - glyph_itr->maLinearPos.X());
1274 #ifdef GRLAYOUT_DEBUG
1275 fprintf(grLog(),"GetNextGlyphs g%d gid%d c%d x%ld,%ld adv%ld, pos %ld,%ld\n",
1276 glyph_slot - 1, glyph_itr->mnGlyphIndex,
1277 mvGlyph2Char[glyph_slot-1], glyph_itr->maLinearPos.X(), glyph_itr->maLinearPos.Y(), nGlyphAdvance,
1278 aPosOut.X(), aPosOut.Y());
1279 #endif
1281 if (glyph_adv) // If we are returning advance store it.
1282 *glyph_adv++ = nGlyphAdvance;
1283 else // Stop when next advance is unexpected.
1284 if (glyph_itr->mnOrigWidth != nGlyphAdvance) break;
1286 // Have fetched all the glyphs we need to
1287 if (glyph_slot == nGlyphSlotEnd)
1288 break;
1290 ++glyph_itr;
1291 // Stop when next y position is unexpected.
1292 if (initial_y_pos != glyph_itr->maLinearPos.Y())
1293 break;
1295 // Stop if glyph dropped
1296 if (glyph_itr->mnGlyphIndex == GF_DROPPED)
1297 break;
1299 int numGlyphs = glyph_slot - glyph_slot_begin;
1300 // move the next glyph_slot to a glyph that hasn't been dropped
1301 while (glyph_slot < static_cast<int>(mvGlyphs.size()) &&
1302 (mvGlyphs.begin() + glyph_slot)->mnGlyphIndex == GF_DROPPED)
1303 ++glyph_slot;
1304 return numGlyphs;
1307 void GraphiteLayout::MoveGlyph( int nGlyphIndex, long nNewPos )
1309 // TODO it might be better to actualy implement simplify properly, but this
1310 // needs to be done carefully so the glyph/char maps are maintained
1311 // If a glyph has been dropped then it wasn't returned by GetNextGlyphs, so
1312 // the index here may be wrong
1313 while ((mvGlyphs[nGlyphIndex].mnGlyphIndex == GF_DROPPED) &&
1314 (nGlyphIndex < (signed)mvGlyphs.size()))
1316 nGlyphIndex++;
1318 const long dx = nNewPos - mvGlyphs[nGlyphIndex].maLinearPos.X();
1320 if (dx == 0) return;
1321 // GenericSalLayout only changes maLinearPos, mvCharDxs doesn't change
1322 #ifdef GRLAYOUT_DEBUG
1323 fprintf(grLog(),"Move %d (%ld,%ld) c%d by %ld\n", nGlyphIndex, mvGlyphs[nGlyphIndex].maLinearPos.X(), nNewPos, mvGlyph2Char[nGlyphIndex], dx);
1324 #endif
1325 for (size_t gi = nGlyphIndex; gi < mvGlyphs.size(); gi++)
1327 mvGlyphs[gi].maLinearPos.X() += dx;
1329 // width does need to be updated for correct fallback
1330 mnWidth += dx;
1333 void GraphiteLayout::DropGlyph( int nGlyphIndex )
1335 if(nGlyphIndex >= signed(mvGlyphs.size()))
1336 return;
1338 GlyphItem & glyph = mvGlyphs[nGlyphIndex];
1339 glyph.mnGlyphIndex = GF_DROPPED;
1340 #ifdef GRLAYOUT_DEBUG
1341 fprintf(grLog(),"Dropped %d\n", nGlyphIndex);
1342 #endif
1345 void GraphiteLayout::Simplify( bool isBaseLayout )
1347 const sal_GlyphId dropMarker = isBaseLayout ? GF_DROPPED : 0;
1349 Glyphs::iterator gi = mvGlyphs.begin();
1350 // TODO check whether we need to adjust positions here
1351 // MultiSalLayout seems to move the glyphs itself, so it may not be needed.
1352 long deltaX = 0;
1353 while (gi != mvGlyphs.end())
1355 if (gi->mnGlyphIndex == dropMarker)
1357 deltaX += gi->mnNewWidth;
1358 gi->mnNewWidth = 0;
1360 else
1362 deltaX = 0;
1364 ++gi;
1366 #ifdef GRLAYOUT_DEBUG
1367 fprintf(grLog(),"Simplify base%d dx=%ld newW=%ld\n", isBaseLayout, deltaX, mnWidth - deltaX);
1368 #endif
1369 // discard width from trailing dropped glyphs, but not those in the middle
1370 mnWidth -= deltaX;
1373 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */