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