merge the formfield patch from ooo-build
[ooovba.git] / vcl / source / glyphs / graphite_layout.cxx
blob5e5f18cb039a298766ea21d6da4b6786064c72f0
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: $
10 * $Revision: $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // Description: An implementation of the SalLayout interface that uses the
32 // Graphite engine.
34 // MARKER(update_precomp.py): autogen include statement, do not remove
35 #include "precompiled_vcl.hxx"
37 // We need this to enable namespace support in libgrengine headers.
38 #define GR_NAMESPACE
40 // Enable lots of debug info
41 #ifdef DEBUG
42 //#define GRLAYOUT_DEBUG 1
43 //#undef NDEBUG
44 #endif
46 // Header files
48 // Standard Library
49 #include <algorithm>
50 #include <cassert>
51 #include <functional>
52 #include <limits>
53 #include <numeric>
54 #include <deque>
56 // Platform
57 #ifdef WNT
58 #include <tools/svwin.h>
59 #include <svsys.h>
60 #endif
62 #include <vcl/salgdi.hxx>
64 #include <unicode/uchar.h>
65 #include <unicode/ubidi.h>
66 #include <unicode/uscript.h>
68 // Graphite Libraries (must be after vcl headers on windows)
69 #include <graphite/GrClient.h>
70 #include <graphite/Font.h>
71 #include <graphite/ITextSource.h>
72 #include <graphite/Segment.h>
73 #include <graphite/SegmentPainter.h>
75 #include <vcl/graphite_layout.hxx>
76 #include <vcl/graphite_features.hxx>
77 #include "graphite_textsrc.hxx"
80 // Module private type definitions and forward declarations.
82 // Module private names.
85 #ifdef GRLAYOUT_DEBUG
86 FILE * grLogFile = NULL;
87 FILE * grLog()
89 #ifdef WNT
90 std::string logFileName(getenv("TEMP"));
91 logFileName.append("\\graphitelayout.log");
92 if (grLogFile == NULL) grLogFile = fopen(logFileName.c_str(),"w");
93 else fflush(grLogFile);
94 return grLogFile;
95 #else
96 return stdout;
97 #endif
99 #endif
101 #ifdef GRCACHE
102 #include <vcl/graphite_cache.hxx>
103 #endif
106 namespace
108 typedef std::pair<gr::GlyphIterator, gr::GlyphIterator> glyph_range_t;
109 typedef std::pair<gr::GlyphSetIterator, gr::GlyphSetIterator> glyph_set_range_t;
111 inline long round(const float n) {
112 return long(n + (n < 0 ? -0.5 : 0.5));
116 template<typename T>
117 inline bool in_range(const T i, const T b, const T e) {
118 return !(b > i) && i < e;
122 template<typename T>
123 inline bool is_subrange(const T sb, const T se, const T b, const T e) {
124 return !(b > sb || se > e);
128 template<typename T>
129 inline bool is_subrange(const std::pair<T, T> &s, const T b, const T e) {
130 return is_subrange(s.first, s.second, b, e);
133 int findSameDirLimit(const xub_Unicode* buffer, int charCount, bool rtl)
135 UErrorCode status = U_ZERO_ERROR;
136 UBiDi *ubidi = ubidi_openSized(charCount, 0, &status);
137 int limit = 0;
138 ubidi_setPara(ubidi, reinterpret_cast<const UChar *>(buffer), charCount,
139 (rtl)?UBIDI_DEFAULT_RTL:UBIDI_DEFAULT_LTR, NULL, &status);
140 UBiDiLevel level = 0;
141 ubidi_getLogicalRun(ubidi, 0, &limit, &level);
142 ubidi_close(ubidi);
143 if ((rtl && !(level & 1)) || (!rtl && (level & 1)))
145 limit = 0;
147 return limit;
150 } // namespace
154 // Impementation of the GraphiteLayout::Glyphs container class.
155 // This is an extended vector class with methods added to enable
156 // o Correctly filling with glyphs.
157 // o Querying clustering relationships.
158 // o manipulations that affect neighouring glyphs.
160 const int GraphiteLayout::EXTRA_CONTEXT_LENGTH = 10;
161 #ifdef GRCACHE
162 GraphiteCacheHandler GraphiteCacheHandler::instance;
163 #endif
165 // The Graphite glyph stream is really a sequence of glyph attachment trees
166 // each rooted at a non-attached base glyph. fill_from walks the glyph stream
167 // find each non-attached base glyph and calls append to record them as a
168 // sequence of clusters.
169 void
170 GraphiteLayout::Glyphs::fill_from(gr::Segment & rSegment, ImplLayoutArgs &rArgs,
171 bool bRtl, long &rWidth, float fScaling, std::vector<int> & rChar2Base, std::vector<int> & rGlyph2Char, std::vector<int> & rCharDxs)
173 // Create a glyph item for each of the glyph and append it to the base class glyph list.
174 typedef std::pair< gr::GlyphSetIterator, gr::GlyphSetIterator > GrGlyphSet;
175 int nChar = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
176 glyph_range_t iGlyphs = rSegment.glyphs();
177 int nGlyphs = iGlyphs.second - iGlyphs.first;
178 gr::GlyphIterator prevBase = iGlyphs.second;
179 float fMinX = rSegment.advanceWidth();
180 float fMaxX = 0.0f;
181 rGlyph2Char.assign(nGlyphs, -1);
182 long nDxOffset = 0;
183 int nGlyphIndex = (bRtl)? (nGlyphs - 1) : 0;
184 // OOo always expects the glyphs in ltr order
185 int nDelta = (bRtl)? -1 : 1;
187 int nLastGlyph = (bRtl)? nGlyphs - 1: 0;
188 int nNextChar = (bRtl)? (rSegment.stopCharacter() - 1) : rSegment.startCharacter();//rArgs.mnMinCharPos;
189 // current glyph number (Graphite glyphs)
190 //int currGlyph = 0;
191 int nFirstCharInCluster = nNextChar;
192 int nFirstGlyphInCluster = nLastGlyph;
194 // ltr first char in cluster is lowest, same is true for rtl
195 // ltr first glyph in cluster is lowest, rtl first glyph is highest
197 // loop over the glyphs determining which characters are linked to them
198 gr::GlyphIterator gi;
199 for (gi = iGlyphs.first + nGlyphIndex;
200 nGlyphIndex >= 0 && nGlyphIndex < nGlyphs;
201 nGlyphIndex+= nDelta, gi = iGlyphs.first + nGlyphIndex)
203 gr::GlyphInfo info = (*gi);
204 #ifdef GRLAYOUT_DEBUG
205 fprintf(grLog(),"Glyph %d %f,%f\n", (int)info.logicalIndex(), info.origin(), info.yOffset());
206 #endif
207 // the last character associated with this glyph is after
208 // our current cluster buffer position
209 if ((bRtl && ((signed)info.firstChar() <= nNextChar)) ||
210 (!bRtl && ((signed)info.lastChar() >= nNextChar)))
212 if ((bRtl && nGlyphIndex < nLastGlyph) ||
213 (!bRtl && nGlyphIndex > nLastGlyph))
215 // this glyph is after the previous one left->right
216 // if insertion is allowed before it then we are in a
217 // new cluster
218 int nAttachedBase = (*(info.attachedClusterBase())).logicalIndex();
219 if (!info.isAttached() ||
220 !in_range(nAttachedBase, nFirstGlyphInCluster, nGlyphIndex))
222 if (in_range(nFirstCharInCluster, rArgs.mnMinCharPos, rArgs.mnEndCharPos) &&
223 nFirstGlyphInCluster != nGlyphIndex)
225 std::pair <float,float> aBounds =
226 appendCluster(rSegment, rArgs, bRtl, nFirstCharInCluster,
227 nNextChar, nFirstGlyphInCluster, nGlyphIndex, fScaling,
228 rChar2Base, rGlyph2Char, rCharDxs, nDxOffset);
229 fMinX = std::min(aBounds.first, fMinX);
230 fMaxX = std::max(aBounds.second, fMaxX);
232 nFirstCharInCluster = (bRtl)? info.lastChar() : info.firstChar();
233 nFirstGlyphInCluster = nGlyphIndex;
235 nLastGlyph = (bRtl)? std::min(nGlyphIndex, nAttachedBase) :
236 std::max(nGlyphIndex, nAttachedBase);
238 // loop over chacters associated with this glyph and characters
239 // between nextChar and the last character associated with this glyph
240 // giving them the current cluster id. This allows for character /glyph
241 // order reversal.
242 // For each character we do a reverse glyph id look up
243 // and store the glyph id with the highest logical index in nLastGlyph
244 while ((bRtl && ((signed)info.firstChar() <= nNextChar)) ||
245 (!bRtl && (signed)info.lastChar() >= nNextChar))
247 GrGlyphSet charGlyphs = rSegment.charToGlyphs(nNextChar);
248 nNextChar += nDelta;
249 gr::GlyphSetIterator gj = charGlyphs.first;
250 while (gj != charGlyphs.second)
252 nLastGlyph = (bRtl)? min(nLastGlyph, (signed)(*gj).logicalIndex()) : max(nLastGlyph, (signed)(*gj).logicalIndex());
253 ++gj;
256 // Loop over attached glyphs and make sure they are all in the cluster since you
257 // can have glyphs attached with another base glyph in between
258 glyph_set_range_t iAttached = info.attachedClusterGlyphs();
259 for (gr::GlyphSetIterator agi = iAttached.first; agi != iAttached.second; ++agi)
261 nLastGlyph = (bRtl)? min(nLastGlyph, (signed)(*agi).logicalIndex()) : max(nLastGlyph, (signed)(*agi).logicalIndex());
264 // if this is a rtl attached glyph, then we need to include its
265 // base in the cluster, which will have a lower graphite index
266 if (bRtl)
268 if ((signed)info.attachedClusterBase()->logicalIndex() < nLastGlyph)
270 nLastGlyph = info.attachedClusterBase()->logicalIndex();
275 // it is possible for the lastChar to be after nextChar and
276 // firstChar to be before the nFirstCharInCluster in rare
277 // circumstances e.g. Myanmar word for cemetery
278 if ((bRtl && ((signed)info.lastChar() > nFirstCharInCluster)) ||
279 (!bRtl && ((signed)info.firstChar() < nFirstCharInCluster)))
281 nFirstCharInCluster = info.firstChar();
284 // process last cluster
285 if (in_range(nFirstCharInCluster, rArgs.mnMinCharPos, rArgs.mnEndCharPos) &&
286 nFirstGlyphInCluster != nGlyphIndex)
288 std::pair <float,float> aBounds =
289 appendCluster(rSegment, rArgs, bRtl, nFirstCharInCluster, nNextChar,
290 nFirstGlyphInCluster, nGlyphIndex, fScaling,
291 rChar2Base, rGlyph2Char, rCharDxs, nDxOffset);
292 fMinX = std::min(aBounds.first, fMinX);
293 fMaxX = std::max(aBounds.second, fMaxX);
295 long nXOffset = round(fMinX * fScaling);
296 rWidth = round(fMaxX * fScaling) - nXOffset + nDxOffset;
297 if (rWidth < 0)
299 // This can happen when there was no base inside the range
300 rWidth = 0;
302 // fill up non-base char dx with cluster widths from previous base glyph
303 if (bRtl)
305 if (rCharDxs[nChar-1] == -1)
306 rCharDxs[nChar-1] = 0;
307 else
308 rCharDxs[nChar-1] -= nXOffset;
309 for (int i = nChar - 2; i >= 0; i--)
311 if (rCharDxs[i] == -1) rCharDxs[i] = rCharDxs[i+1];
312 else rCharDxs[i] -= nXOffset;
315 else
317 if (rCharDxs[0] == -1)
318 rCharDxs[0] = 0;
319 else
320 rCharDxs[0] -= nXOffset;
321 for (int i = 1; i < nChar; i++)
323 if (rCharDxs[i] == -1) rCharDxs[i] = rCharDxs[i-1];
324 else rCharDxs[i] -= nXOffset;
327 #ifdef GRLAYOUT_DEBUG
328 fprintf(grLog(),"Glyphs xOff%ld dropDx%ld w%ld\n", nXOffset, nDxOffset, rWidth);
329 #endif
330 // remove offset due to context if there is one
331 if (nXOffset != 0)
333 for (size_t i = 0; i < size(); i++)
334 (*this)[i].maLinearPos.X() -= nXOffset;
338 std::pair<float,float> GraphiteLayout::Glyphs::appendCluster(gr::Segment & rSeg,
339 ImplLayoutArgs & rArgs, bool bRtl, int nFirstCharInCluster, int nNextChar,
340 int nFirstGlyphInCluster, int nNextGlyph, float fScaling,
341 std::vector<int> & rChar2Base, std::vector<int> & rGlyph2Char,
342 std::vector<int> & rCharDxs, long & rDXOffset)
344 glyph_range_t iGlyphs = rSeg.glyphs();
345 int nGlyphs = iGlyphs.second - iGlyphs.first;
346 int nDelta = (bRtl)? -1 : 1;
347 gr::GlyphInfo aFirstGlyph = *(iGlyphs.first + nFirstGlyphInCluster);
348 std::pair <float, float> aBounds;
349 aBounds.first = aFirstGlyph.origin();
350 aBounds.second = aFirstGlyph.origin();
351 // before we add the glyphs to this vector, we record the
352 // glyph's index in the vector (which is not the same as
353 // the Segment's glyph index!)
354 assert(size() < rGlyph2Char.size());
355 rChar2Base[nFirstCharInCluster-rArgs.mnMinCharPos] = size();
356 rGlyph2Char[size()] = nFirstCharInCluster;
357 bool bBaseGlyph = true;
358 for (int j = nFirstGlyphInCluster;
359 j != nNextGlyph; j += nDelta)
361 long nNextOrigin;
362 float fNextOrigin;
363 gr::GlyphInfo aGlyph = *(iGlyphs.first + j);
364 if (j + nDelta >= nGlyphs || j + nDelta < 0) // at rhs ltr,rtl
366 fNextOrigin = rSeg.advanceWidth();
367 nNextOrigin = round(rSeg.advanceWidth() * fScaling + rDXOffset);
368 aBounds.second = std::max(rSeg.advanceWidth(), aBounds.second);
370 else
372 gr::GlyphInfo aNextGlyph = *(iGlyphs.first + j + nDelta);
373 fNextOrigin = std::max(aNextGlyph.attachedClusterBase()->origin(), aNextGlyph.origin());
374 aBounds.second = std::max(fNextOrigin, aBounds.second);
375 nNextOrigin = round(fNextOrigin * fScaling + rDXOffset);
377 aBounds.first = std::min(aGlyph.origin(), aBounds.first);
378 if ((signed)aGlyph.firstChar() < rArgs.mnEndCharPos &&
379 (signed)aGlyph.firstChar() >= rArgs.mnMinCharPos)
381 rCharDxs[aGlyph.firstChar()-rArgs.mnMinCharPos] = nNextOrigin;
383 if ((signed)aGlyph.attachedClusterBase()->logicalIndex() == j)
385 append(rSeg, rArgs, aGlyph, fNextOrigin, fScaling, rChar2Base, rGlyph2Char, rCharDxs, rDXOffset, bBaseGlyph);
386 bBaseGlyph = false;
389 // from the point of view of the dx array, the xpos is
390 // the origin of the first glyph of the next cluster ltr
391 // rtl it is the origin of the 1st glyph of the cluster
392 long nXPos = (bRtl)?
393 round(aFirstGlyph.attachedClusterBase()->origin() * fScaling) + rDXOffset :
394 round(aBounds.second * fScaling) + rDXOffset;
395 // force the last char in range to have the width of the cluster
396 if (bRtl)
398 for (int n = nNextChar + 1; n <= nFirstCharInCluster; n++)
400 if ((n < rArgs.mnEndCharPos) && (n >= rArgs.mnMinCharPos))
401 rCharDxs[n-rArgs.mnMinCharPos] = nXPos;
404 else
406 for (int n = nNextChar - 1; n >= nFirstCharInCluster; n--)
408 if (n < rArgs.mnEndCharPos && n >= rArgs.mnMinCharPos)
409 rCharDxs[n-rArgs.mnMinCharPos] = nXPos;
412 #ifdef GRLAYOUT_DEBUG
413 fprintf(grLog(),"Cluster g[%d-%d) c[%d-%d)%x x%ld y%f\n", nFirstGlyphInCluster, nNextGlyph, nFirstCharInCluster, nNextChar, rArgs.mpStr[nFirstCharInCluster], nXPos, aFirstGlyph.yOffset());
414 #endif
415 return aBounds;
418 // append walks an attachment tree, flattening it, and converting it into a
419 // sequence of GlyphItem objects which we can later manipulate.
420 void
421 GraphiteLayout::Glyphs::append(gr::Segment &segment, ImplLayoutArgs &args, gr::GlyphInfo & gi, float nextGlyphOrigin, float scaling, std::vector<int> & rChar2Base, std::vector<int> & rGlyph2Char, std::vector<int> & rCharDxs, long & rDXOffset, bool bIsBase)
423 float nextOrigin = nextGlyphOrigin;
424 int firstChar = std::min(gi.firstChar(), gi.lastChar());
425 assert(size() < rGlyph2Char.size());
426 if (!bIsBase) rGlyph2Char[size()] = firstChar;
427 // is the next glyph attached or in the next cluster?
428 glyph_set_range_t iAttached = gi.attachedClusterGlyphs();
429 if (iAttached.first != iAttached.second)
431 nextOrigin = iAttached.first->origin();
433 long glyphId = gi.glyphID();
434 long deltaOffset = 0;
435 int glyphWidth = round(nextOrigin * scaling) - round(gi.origin() * scaling);
436 #ifdef GRLAYOUT_DEBUG
437 fprintf(grLog(),"c%d g%d gWidth%d x%f ", firstChar, (int)gi.logicalIndex(), glyphWidth, nextOrigin);
438 #endif
439 if (glyphId == 0)
441 args.NeedFallback(
442 firstChar,
443 gr::RightToLeftDir(gr::DirCode(gi.directionality())));
444 if( (SAL_LAYOUT_FOR_FALLBACK & args.mnFlags ))
446 glyphId = GF_DROPPED;
447 deltaOffset -= glyphWidth;
448 glyphWidth = 0;
451 else if(args.mnFlags & SAL_LAYOUT_FOR_FALLBACK)
453 #ifdef GRLAYOUT_DEBUG
454 fprintf(grLog(),"fallback c%d %x in run %d\n", firstChar, args.mpStr[firstChar],
455 args.maRuns.PosIsInAnyRun(firstChar));
456 #endif
457 // glyphs that aren't requested for fallback will be taken from base
458 // layout, so mark them as dropped (should this wait until Simplify(false) is called?)
459 if (!args.maRuns.PosIsInAnyRun(firstChar) &&
460 in_range(firstChar, args.mnMinCharPos, args.mnEndCharPos))
462 glyphId = GF_DROPPED;
463 deltaOffset -= glyphWidth;
464 glyphWidth = 0;
467 // append this glyph.
468 long nGlyphFlags = bIsBase ? 0 : GlyphItem::IS_IN_CLUSTER;
469 // directionality seems to be unreliable
470 //nGlyphFlags |= gr::RightToLeftDir(gr::DirCode(gi.attachedClusterBase()->directionality())) ? GlyphItem::IS_RTL_GLYPH : 0;
471 nGlyphFlags |= (gi.directionLevel() & 0x1)? GlyphItem::IS_RTL_GLYPH : 0;
472 GlyphItem aGlyphItem(size(),//gi.logicalIndex(),
473 glyphId,
474 Point(round(gi.origin() * scaling + rDXOffset),
475 round((-gi.yOffset() * scaling) - segment.AscentOffset()* scaling)),
476 nGlyphFlags,
477 glyphWidth);
478 aGlyphItem.mnOrigWidth = round(gi.advanceWidth() * scaling);
479 push_back(aGlyphItem);
481 // update the offset if this glyph was dropped
482 rDXOffset += deltaOffset;
484 // Recursively apply append all the attached glyphs.
485 for (gr::GlyphSetIterator agi = iAttached.first; agi != iAttached.second; ++agi)
487 if (agi + 1 == iAttached.second)
488 append(segment, args, *agi, nextGlyphOrigin, scaling, rChar2Base, rGlyph2Char,rCharDxs, rDXOffset, false);
489 else
490 append(segment, args, *agi, (agi + 1)->origin(), scaling, rChar2Base, rGlyph2Char, rCharDxs, rDXOffset, false);
495 // An implementation of the SalLayout interface to enable Graphite enabled fonts to be used.
497 GraphiteLayout::GraphiteLayout(const gr::Font & font, const grutils::GrFeatureParser * pFeatures) throw()
498 : mpTextSrc(0),
499 mrFont(font),
500 mnWidth(0),
501 mfScaling(1.0),
502 mpFeatures(pFeatures)
504 // Line settings can have subtle affects on space handling
505 // since we don't really know whether it is the end of a line or just a run
506 // in the middle, it is hard to know what to set them to.
507 // If true, it can cause end of line spaces to be hidden e.g. Doulos SIL
508 maLayout.setStartOfLine(false);
509 maLayout.setEndOfLine(false);
510 // maLayout.setDumbFallback(false);
511 // trailing ws doesn't seem to always take affect if end of line is true
512 maLayout.setTrailingWs(gr::ktwshAll);
513 #ifdef GRLAYOUT_DEBUG
514 gr::ScriptDirCode aDirCode = font.getSupportedScriptDirections();
515 fprintf(grLog(),"GraphiteLayout scripts %x %lx\n", aDirCode, long(this));
516 #endif
520 GraphiteLayout::~GraphiteLayout() throw()
522 clear();
523 // the features are owned by the platform layers
524 mpFeatures = NULL;
527 void GraphiteLayout::clear()
529 // Destroy the segment and text source from any previous invocation of
530 // LayoutText
531 mvGlyphs.clear();
532 mvCharDxs.clear();
533 mvChar2BaseGlyph.clear();
534 mvGlyph2Char.clear();
536 #ifndef GRCACHE
537 delete mpTextSrc;
538 #endif
540 // Reset the state to the empty state.
541 mpTextSrc=0;
542 mnWidth = 0;
543 // Don't reset the scaling, because it is set before LayoutText
546 // This method shouldn't be called on windows, since it needs the dc reset
547 bool GraphiteLayout::LayoutText(ImplLayoutArgs & rArgs)
549 #ifdef GRCACHE
550 GrSegRecord * pSegRecord = NULL;
551 gr::Segment * pSegment = CreateSegment(rArgs, &pSegRecord);
552 if (!pSegment)
553 return false;
555 // layout the glyphs as required by OpenOffice
556 bool success = LayoutGlyphs(rArgs, pSegment, pSegRecord);
558 if (pSegRecord) pSegRecord->unlock();
559 else delete pSegment;
560 #else
561 gr::Segment * pSegment = CreateSegment(rArgs);
562 bool success = LayoutGlyphs(rArgs, pSegment);
563 delete pSegment;
564 #endif
565 return success;
568 #ifdef GRCACHE
569 class GrFontHasher : public gr::Font
571 public:
572 GrFontHasher(const gr::Font & aFont) : gr::Font(aFont), mrRealFont(const_cast<gr::Font&>(aFont)) {};
573 ~GrFontHasher(){};
574 virtual bool bold() { return mrRealFont.bold(); };
575 virtual bool italic() { return mrRealFont.italic(); };
576 virtual float ascent() { return mrRealFont.ascent(); };
577 virtual float descent() { return mrRealFont.descent(); };
578 virtual float height() { return mrRealFont.height(); };
579 virtual gr::Font* copyThis() { return mrRealFont.copyThis(); };
580 virtual unsigned int getDPIx() { return mrRealFont.getDPIx(); };
581 virtual unsigned int getDPIy() { return mrRealFont.getDPIy(); };
582 virtual const void* getTable(gr::fontTableId32 nId, size_t* nSize)
583 { return mrRealFont.getTable(nId,nSize); }
584 virtual void getFontMetrics(float*pA, float*pB, float*pC) { mrRealFont.getFontMetrics(pA,pB,pC); };
586 sal_Int32 hashCode(const grutils::GrFeatureParser * mpFeatures)
588 // is this sufficient?
589 std::wstring aFace;
590 bool bBold;
591 bool bItalic;
592 UniqueCacheInfo(aFace, bBold, bItalic);
593 sal_Unicode uName[32]; // max length used in gr::Font
594 // Note: graphite stores font names as UTF-16 even if wchar_t is 32bit
595 // this conversion should be OK.
596 for (size_t i = 0; i < aFace.size() && i < 32; i++)
598 uName[i] = aFace[i];
600 size_t iSize = aFace.size();
601 if (0 == iSize) return 0;
602 sal_Int32 hash = rtl_ustr_hashCode_WithLength(uName, iSize);
603 hash ^= static_cast<sal_Int32>(height());
604 hash |= (bBold)? 0x1000000 : 0;
605 hash |= (bItalic)? 0x2000000 : 0;
606 if (mpFeatures)
607 hash ^= mpFeatures->hashCode();
608 #ifdef GRLAYOUT_DEBUG
609 fprintf(grLog(), "font hash %x size %f\n", (int)hash, height());
610 #endif
611 return hash;
614 private:
615 gr::Font & mrRealFont;
617 #endif
619 #ifdef GRCACHE
620 gr::Segment * GraphiteLayout::CreateSegment(ImplLayoutArgs& rArgs, GrSegRecord ** pSegRecord)
621 #else
622 gr::Segment * GraphiteLayout::CreateSegment(ImplLayoutArgs& rArgs)
623 #endif
625 assert(rArgs.mnLength >= 0);
627 gr::Segment * pSegment = NULL;
629 // Set the SalLayouts values to be the inital ones.
630 SalLayout::AdjustLayout(rArgs);
631 // TODO check if this is needed
632 if (mnUnitsPerPixel > 1)
633 mfScaling = 1.0f / mnUnitsPerPixel;
635 // Clear out any previous buffers
636 clear();
637 bool bRtl = mnLayoutFlags & SAL_LAYOUT_BIDI_RTL;
640 // Don't set RTL if font doesn't support it otherwise it forces rtl on
641 // everything
642 if (bRtl && (mrFont.getSupportedScriptDirections() & gr::kfsdcHorizRtl))
643 maLayout.setRightToLeft(bRtl);
645 #ifdef GRCACHE
646 GrFontHasher hasher(mrFont);
647 sal_Int32 aFontHash = hasher.hashCode(mpFeatures);
648 GraphiteSegmentCache * pCache =
649 (GraphiteCacheHandler::instance).getCache(aFontHash);
650 if (pCache)
652 *pSegRecord = pCache->getSegment(rArgs, bRtl);
653 if (*pSegRecord)
655 pSegment = (*pSegRecord)->getSegment();
656 mpTextSrc = (*pSegRecord)->getTextSrc();
657 maLayout.setRightToLeft((*pSegRecord)->isRtl());
658 if (rArgs.mpStr != mpTextSrc->getLayoutArgs().mpStr ||
659 rArgs.mnMinCharPos != mpTextSrc->getLayoutArgs().mnMinCharPos ||
660 rArgs.mnEndCharPos != mpTextSrc->getLayoutArgs().mnEndCharPos ||
661 (SAL_LAYOUT_FOR_FALLBACK & rArgs.mnFlags) )
663 (*pSegRecord)->clearVectors();
665 mpTextSrc->switchLayoutArgs(rArgs);
666 return pSegment;
669 #endif
671 // Context is often needed beyond the specified end, however, we don't
672 // want it if there has been a direction change, since it is hard
673 // to tell between reordering within one direction and multi-directional
674 // text.
675 const int segCharLimit = min(rArgs.mnLength, mnEndCharPos + EXTRA_CONTEXT_LENGTH);
676 int limit = rArgs.mnEndCharPos;
677 if (segCharLimit > limit)
679 limit += findSameDirLimit(rArgs.mpStr + rArgs.mnEndCharPos,
680 segCharLimit - rArgs.mnEndCharPos, bRtl);
683 // Create a new TextSource object for the engine.
684 mpTextSrc = new TextSourceAdaptor(rArgs, limit);
685 if (mpFeatures) mpTextSrc->setFeatures(mpFeatures);
687 pSegment = new gr::RangeSegment((gr::Font *)&mrFont, mpTextSrc, &maLayout, mnMinCharPos, limit);
688 if (pSegment != NULL)
690 #ifdef GRLAYOUT_DEBUG
691 fprintf(grLog(),"Gr::LayoutText %d-%d, context %d,len%d rtl%d/%d scaling %f\n", rArgs.mnMinCharPos,
692 rArgs.mnEndCharPos, limit, rArgs.mnLength, maLayout.rightToLeft(), pSegment->rightToLeft(), mfScaling);
693 #endif
694 #ifdef GRCACHE
695 // on a new segment rightToLeft should be correct
696 *pSegRecord = pCache->cacheSegment(mpTextSrc, pSegment, pSegment->rightToLeft());
697 #endif
699 else
701 clear();
702 return NULL;
705 catch (...)
707 clear(); // destroy the text source and any partially built segments.
708 return NULL;
710 return pSegment;
713 #ifdef GRCACHE
714 bool GraphiteLayout::LayoutGlyphs(ImplLayoutArgs& rArgs, gr::Segment * pSegment, GrSegRecord * pSegRecord)
715 #else
716 bool GraphiteLayout::LayoutGlyphs(ImplLayoutArgs& rArgs, gr::Segment * pSegment)
717 #endif
719 #ifdef GRCACHE
720 #ifdef GRCACHE_REUSE_VECTORS
721 // if we have an exact match, then we can reuse the glyph vectors from before
722 if (pSegRecord && (pSegRecord->glyphs().size() > 0) &&
723 !(SAL_LAYOUT_FOR_FALLBACK & rArgs.mnFlags) )
725 mnWidth = pSegRecord->width();
726 mvGlyphs = pSegRecord->glyphs();
727 mvCharDxs = pSegRecord->charDxs();
728 mvChar2BaseGlyph = pSegRecord->char2BaseGlyph();
729 mvGlyph2Char = pSegRecord->glyph2Char();
730 return true;
732 #endif
733 #endif
734 // Calculate the initial character dxs.
735 mvCharDxs.assign(mnEndCharPos - mnMinCharPos, -1);
736 mvChar2BaseGlyph.assign(mnEndCharPos - mnMinCharPos, -1);
737 mnWidth = 0;
738 if (mvCharDxs.size() > 0)
740 // Discover all the clusters.
743 // Note: we use the layout rightToLeft() because in cached segments
744 // rightToLeft() may no longer be valid if the engine has been run
745 // ltr since the segment was created.
746 #ifdef GRCACHE
747 bool bRtl = pSegRecord? pSegRecord->isRtl() : pSegment->rightToLeft();
748 #else
749 bool bRtl = pSegment->rightToLeft();
750 #endif
751 mvGlyphs.fill_from(*pSegment, rArgs, bRtl,
752 mnWidth, mfScaling, mvChar2BaseGlyph, mvGlyph2Char, mvCharDxs);
754 if (bRtl)
756 // not needed for adjacent differences, but for mouse clicks to char
757 std::transform(mvCharDxs.begin(), mvCharDxs.end(), mvCharDxs.begin(),
758 std::bind1st(std::minus<long>(), mnWidth));
759 // fixup last dx to ensure it always equals the width
760 mvCharDxs[mvCharDxs.size() - 1] = mnWidth;
762 #ifdef GRCACHE
763 #ifdef GRCACHE_REUSE_VECTORS
764 if (pSegRecord && rArgs.maReruns.IsEmpty() &&
765 !(SAL_LAYOUT_FOR_FALLBACK & rArgs.mnFlags))
767 pSegRecord->setGlyphVectors(mnWidth, mvGlyphs, mvCharDxs,
768 mvChar2BaseGlyph, mvGlyph2Char);
770 #endif
771 #endif
773 catch (std::exception e)
775 #ifdef GRLAYOUT_DEBUG
776 fprintf(grLog(),"LayoutGlyphs failed %s\n", e.what());
777 #endif
778 return false;
780 catch (...)
782 #ifdef GRLAYOUT_DEBUG
783 fprintf(grLog(),"LayoutGlyphs failed with exception");
784 #endif
785 return false;
788 else
790 mnWidth = 0;
792 return true;
795 int GraphiteLayout::GetTextBreak(long maxmnWidth, long char_extra, int factor) const
797 // Adjust maxmnWidth so FindNextBreakPoint returns a sensible answer.
798 maxmnWidth -= (mnEndCharPos-mnMinCharPos-1)*char_extra; // extra character spacing.
799 maxmnWidth /= factor; // scaling factor.
801 // Ask the segment for the nearest whole letter break for the width.
802 //float width;
803 float targetWidth = maxmnWidth/mfScaling;
804 // return quickly if this segment is narrower than the target width
805 // (sometimes graphite doesn't seem to realise this!)
806 if (targetWidth > mnWidth)
807 return STRING_LEN;
808 //int nBreak = mpSegment->findNextBreakPoint(mnMinCharPos,
809 // gr::klbWordBreak, gr::klbLetterBreak, targetWidth, &width);
811 // LineFillSegment seems to give better results that findNextBreakPoint
812 // though it may be slower
813 gr::LayoutEnvironment aLE;
814 gr::LineFillSegment lineSeg(const_cast<gr::Font *>(&mrFont), mpTextSrc, &aLE,
815 mnMinCharPos, mpTextSrc->getContextLength(),
816 targetWidth);
817 int nBreak = lineSeg.stopCharacter();
819 if (nBreak > mnEndCharPos) nBreak = STRING_LEN;
820 else if (nBreak < mnMinCharPos) nBreak = mnMinCharPos;
821 return nBreak;
825 long GraphiteLayout::FillDXArray( sal_Int32* pDXArray ) const
827 if (mnEndCharPos == mnMinCharPos)
828 // Then we must be zero width!
829 return 0;
831 if (pDXArray)
833 for (size_t i = 0; i < mvCharDxs.size(); i++)
835 assert((mvChar2BaseGlyph[i] >= -1) && (mvChar2BaseGlyph[i] < (signed)mvGlyphs.size()));
836 if (mvChar2BaseGlyph[i] != -1 &&
837 mvGlyphs[mvChar2BaseGlyph[i]].mnGlyphIndex == GF_DROPPED)
839 // when used in MultiSalLayout::GetTextBreak dropped glyphs
840 // must have zero width
841 pDXArray[i] = 0;
843 else
845 pDXArray[i] = mvCharDxs[i];
846 if (i > 0) pDXArray[i] -= mvCharDxs[i-1];
848 #ifdef GRLAYOUT_DEBUG
849 fprintf(grLog(),"%d,%d,%ld ", (int)i, (int)mvCharDxs[i], pDXArray[i]);
850 #endif
852 //std::adjacent_difference(mvCharDxs.begin(), mvCharDxs.end(), pDXArray);
853 //for (size_t i = 0; i < mvCharDxs.size(); i++)
854 // fprintf(grLog(),"%d,%d,%d ", (int)i, (int)mvCharDxs[i], pDXArray[i]);
855 //fprintf(grLog(),"FillDX %ld,%d\n", mnWidth, std::accumulate(pDXArray, pDXArray + mvCharDxs.size(), 0));
857 #ifdef GRLAYOUT_DEBUG
858 fprintf(grLog(),"FillDXArray %d-%d,%d=%ld\n", mnMinCharPos, mnEndCharPos, (int)mpTextSrc->getLength(), mnWidth);
859 #endif
860 return mnWidth;
864 void GraphiteLayout::AdjustLayout(ImplLayoutArgs& rArgs)
866 SalLayout::AdjustLayout(rArgs);
868 if(rArgs.mpDXArray)
870 std::vector<int> vDeltaWidths(mvGlyphs.size(), 0);
871 ApplyDXArray(rArgs, vDeltaWidths);
873 if( (mnLayoutFlags & SAL_LAYOUT_BIDI_RTL) &&
874 !(rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK) )
876 // check if this is a kashida script
877 bool bKashidaScript = false;
878 for (int i = rArgs.mnMinCharPos; i < rArgs.mnEndCharPos; i++)
880 UErrorCode aStatus = U_ZERO_ERROR;
881 UScriptCode scriptCode = uscript_getScript(rArgs.mpStr[i], &aStatus);
882 if (scriptCode == USCRIPT_ARABIC || scriptCode == USCRIPT_SYRIAC)
884 bKashidaScript = true;
885 break;
888 int nKashidaWidth = 0;
889 int nKashidaIndex = getKashidaGlyph(nKashidaWidth);
890 if( nKashidaIndex != 0 && bKashidaScript)
892 kashidaJustify( vDeltaWidths, nKashidaIndex, nKashidaWidth );
899 void GraphiteLayout::ApplyDXArray(ImplLayoutArgs &args, std::vector<int> & rDeltaWidth)
901 const size_t nChars = args.mnEndCharPos - args.mnMinCharPos;
902 if (nChars == 0) return;
904 #ifdef GRLAYOUT_DEBUG
905 for (size_t iDx = 0; iDx < mvCharDxs.size(); iDx++)
906 fprintf(grLog(),"%d,%d,%ld ", (int)iDx, (int)mvCharDxs[iDx], args.mpDXArray[iDx]);
907 fprintf(grLog(),"ApplyDx\n");
908 #endif
909 bool bRtl = mnLayoutFlags & SAL_LAYOUT_BIDI_RTL;
910 int nXOffset = 0;
911 if (bRtl)
913 nXOffset = args.mpDXArray[nChars - 1] - mvCharDxs[nChars - 1];
915 int nPrevClusterGlyph = (bRtl)? mvGlyphs.size() : -1;
916 int nPrevClusterLastChar = -1;
917 for (size_t i = 0; i < nChars; i++)
919 if (mvChar2BaseGlyph[i] > -1 && mvChar2BaseGlyph[i] != nPrevClusterGlyph)
921 assert((mvChar2BaseGlyph[i] > -1) && (mvChar2BaseGlyph[i] < (signed)mvGlyphs.size()));
922 GlyphItem & gi = mvGlyphs[mvChar2BaseGlyph[i]];
923 if (!gi.IsClusterStart())
924 continue;
926 // find last glyph of this cluster
927 size_t j = i + 1;
928 int nLastChar = i;
929 int nLastGlyph = mvChar2BaseGlyph[i];
930 for (; j < nChars; j++)
932 assert((mvChar2BaseGlyph[j] >= -1) && (mvChar2BaseGlyph[j] < (signed)mvGlyphs.size()));
933 if (mvChar2BaseGlyph[j] != -1 && mvGlyphs[mvChar2BaseGlyph[j]].IsClusterStart())
935 nLastGlyph = mvChar2BaseGlyph[j] + ((bRtl)? 1 : -1);
936 nLastChar = j - 1;
937 break;
940 if (nLastGlyph < 0)
942 nLastGlyph = mvChar2BaseGlyph[i];
944 // Its harder to find the last glyph rtl, since the first of
945 // cluster is still on the left so we need to search towards
946 // the previous cluster to the right
947 if (bRtl)
949 nLastGlyph = mvChar2BaseGlyph[i];
950 while (nLastGlyph + 1 < (signed)mvGlyphs.size() &&
951 !mvGlyphs[nLastGlyph+1].IsClusterStart())
953 ++nLastGlyph;
956 if (j == nChars)
958 nLastChar = nChars - 1;
959 if (!bRtl) nLastGlyph = mvGlyphs.size() - 1;
961 assert((nLastChar > -1) && (nLastChar < (signed)nChars));
962 long nNewClusterWidth = args.mpDXArray[nLastChar];
963 long nOrigClusterWidth = mvCharDxs[nLastChar];
964 long nDGlyphOrigin = 0;
965 if (nPrevClusterLastChar > - 1)
967 assert(nPrevClusterLastChar < (signed)nChars);
968 nNewClusterWidth -= args.mpDXArray[nPrevClusterLastChar];
969 nOrigClusterWidth -= mvCharDxs[nPrevClusterLastChar];
970 nDGlyphOrigin = args.mpDXArray[nPrevClusterLastChar] - mvCharDxs[nPrevClusterLastChar];
972 long nDWidth = nNewClusterWidth - nOrigClusterWidth;
973 #ifdef GRLAYOUT_DEBUG
974 fprintf(grLog(), "c%d last glyph %d/%d\n", i, nLastGlyph, mvGlyphs.size());
975 #endif
976 assert((nLastGlyph > -1) && (nLastGlyph < (signed)mvGlyphs.size()));
977 mvGlyphs[nLastGlyph].mnNewWidth += nDWidth;
978 if (gi.mnGlyphIndex != GF_DROPPED)
979 mvGlyphs[nLastGlyph].mnNewWidth += nDWidth;
980 else
981 nDGlyphOrigin += nDWidth;
982 // update glyph positions
983 if (bRtl)
985 for (int n = mvChar2BaseGlyph[i]; n <= nLastGlyph; n++)
987 assert((n > - 1) && (n < (signed)mvGlyphs.size()));
988 mvGlyphs[n].maLinearPos.X() += -nDGlyphOrigin + nXOffset;
991 else
993 for (int n = mvChar2BaseGlyph[i]; n <= nLastGlyph; n++)
995 assert((n > - 1) && (n < (signed)mvGlyphs.size()));
996 mvGlyphs[n].maLinearPos.X() += nDGlyphOrigin + nXOffset;
999 rDeltaWidth[mvChar2BaseGlyph[i]] = nDWidth;
1000 #ifdef GRLAYOUT_DEBUG
1001 fprintf(grLog(),"c%d g%d-%d dW%ld-%ld=%ld dX%ld x%ld\t", (int)i, mvChar2BaseGlyph[i], nLastGlyph, nNewClusterWidth, nOrigClusterWidth, nDWidth, nDGlyphOrigin, mvGlyphs[mvChar2BaseGlyph[i]].maLinearPos.X());
1002 #endif
1003 nPrevClusterGlyph = mvChar2BaseGlyph[i];
1004 nPrevClusterLastChar = nLastChar;
1005 i = nLastChar;
1008 // Update the dx vector with the new values.
1009 std::copy(args.mpDXArray, args.mpDXArray + nChars,
1010 mvCharDxs.begin() + (args.mnMinCharPos - mnMinCharPos));
1011 #ifdef GRLAYOUT_DEBUG
1012 fprintf(grLog(),"ApplyDx %ld(%ld)\n", args.mpDXArray[nChars - 1], mnWidth);
1013 #endif
1014 mnWidth = args.mpDXArray[nChars - 1];
1017 void GraphiteLayout::kashidaJustify(std::vector<int>& rDeltaWidths, sal_GlyphId nKashidaIndex, int nKashidaWidth)
1019 // skip if the kashida glyph in the font looks suspicious
1020 if( nKashidaWidth <= 0 )
1021 return;
1023 // calculate max number of needed kashidas
1024 Glyphs::iterator i = mvGlyphs.begin();
1025 int nKashidaCount = 0;
1026 int nOrigGlyphIndex = -1;
1027 int nGlyphIndex = -1;
1028 while (i != mvGlyphs.end())
1030 nOrigGlyphIndex++;
1031 nGlyphIndex++;
1032 // only inject kashidas in RTL contexts
1033 if( !(*i).IsRTLGlyph() )
1035 ++i;
1036 continue;
1038 // no kashida-injection for blank justified expansion either
1039 if( IsSpacingGlyph( (*i).mnGlyphIndex ) )
1041 ++i;
1042 continue;
1044 // calculate gap, ignore if too small
1045 int nGapWidth = rDeltaWidths[nOrigGlyphIndex];;
1046 // worst case is one kashida even for mini-gaps
1047 if( 3 * nGapWidth < nKashidaWidth )
1049 ++i;
1050 continue;
1052 nKashidaCount = 1 + (nGapWidth / nKashidaWidth);
1053 #ifdef GRLAYOUT_DEBUG
1054 printf("inserting %d kashidas at %ld\n", nKashidaCount, (*i).mnGlyphIndex);
1055 #endif
1056 GlyphItem glyphItem = *i;
1057 Point aPos(0, 0);
1058 aPos.X() = (*i).maLinearPos.X();
1059 GlyphItem newGi(glyphItem.mnCharPos, nKashidaIndex, aPos,
1060 GlyphItem::IS_IN_CLUSTER|GlyphItem::IS_RTL_GLYPH, nKashidaWidth);
1061 mvGlyphs.reserve(mvGlyphs.size() + nKashidaCount);
1062 i = mvGlyphs.begin() + nGlyphIndex;
1063 mvGlyphs.insert(i, nKashidaCount, newGi);
1064 i = mvGlyphs.begin() + nGlyphIndex;
1065 nGlyphIndex += nKashidaCount;
1066 // now fix up the kashida positions
1067 for (int j = 0; j < nKashidaCount; j++)
1069 (*(i)).maLinearPos.X() -= nGapWidth;
1070 nGapWidth -= nKashidaWidth;
1071 i++;
1074 // fixup rightmost kashida for gap remainder
1075 if( nGapWidth < 0 )
1077 if( nKashidaCount <= 1 )
1078 nGapWidth /= 2; // for small gap move kashida to middle
1079 (*(i-1)).mnNewWidth += nGapWidth; // adjust kashida width to gap width
1080 (*(i-1)).maLinearPos.X() += nGapWidth;
1083 (*i).mnNewWidth = (*i).mnOrigWidth;
1084 ++i;
1089 void GraphiteLayout::GetCaretPositions( int nArraySize, sal_Int32* pCaretXArray ) const
1091 // For each character except the last discover the caret positions
1092 // immediatly before and after that character.
1093 // This is used for underlines in the GUI amongst other things.
1094 // It may be used from MultiSalLayout, in which case it must take into account
1095 // glyphs that have been moved.
1096 std::fill(pCaretXArray, pCaretXArray + nArraySize, -1);
1097 // the layout method doesn't modify the layout even though it isn't
1098 // const in the interface
1099 bool bRtl = const_cast<GraphiteLayout*>(this)->maLayout.rightToLeft();
1100 int prevBase = -1;
1101 long prevClusterWidth = 0;
1102 for (int i = 0, nCharSlot = 0; i < nArraySize && nCharSlot < static_cast<int>(mvCharDxs.size()); ++nCharSlot, i+=2)
1104 if (mvChar2BaseGlyph[nCharSlot] != -1)
1106 assert((mvChar2BaseGlyph[nCharSlot] > -1) && (mvChar2BaseGlyph[nCharSlot] < (signed)mvGlyphs.size()));
1107 GlyphItem gi = mvGlyphs[mvChar2BaseGlyph[nCharSlot]];
1108 if (gi.mnGlyphIndex == GF_DROPPED)
1110 continue;
1112 int nCluster = mvChar2BaseGlyph[nCharSlot];
1113 long origClusterWidth = gi.mnNewWidth;
1114 long nMin = gi.maLinearPos.X();
1115 long nMax = gi.maLinearPos.X() + gi.mnNewWidth;
1116 // attached glyphs are always stored after their base rtl or ltr
1117 while (++nCluster < static_cast<int>(mvGlyphs.size()) &&
1118 !mvGlyphs[nCluster].IsClusterStart())
1120 origClusterWidth += mvGlyphs[nCluster].mnNewWidth;
1121 if (mvGlyph2Char[nCluster] == nCharSlot)
1123 nMin = std::min(nMin, mvGlyphs[nCluster].maLinearPos.X());
1124 nMax = std::min(nMax, mvGlyphs[nCluster].maLinearPos.X() + mvGlyphs[nCluster].mnNewWidth);
1127 if (bRtl)
1129 pCaretXArray[i+1] = nMin;
1130 pCaretXArray[i] = nMax;
1132 else
1134 pCaretXArray[i] = nMin;
1135 pCaretXArray[i+1] = nMax;
1137 prevBase = mvChar2BaseGlyph[nCharSlot];
1138 prevClusterWidth = origClusterWidth;
1140 else if (prevBase > -1)
1142 // this could probably be improved
1143 assert((prevBase > -1) && (prevBase < (signed)mvGlyphs.size()));
1144 GlyphItem gi = mvGlyphs[prevBase];
1145 int nGlyph = prevBase + 1;
1146 // try to find a better match, otherwise default to complete cluster
1147 for (; nGlyph < static_cast<int>(mvGlyphs.size()) &&
1148 !mvGlyphs[nGlyph].IsClusterStart(); nGlyph++)
1150 if (mvGlyph2Char[nGlyph] == nCharSlot)
1152 gi = mvGlyphs[nGlyph];
1153 break;
1156 long nGWidth = gi.mnNewWidth;
1157 // if no match position at end of cluster
1158 if (nGlyph == static_cast<int>(mvGlyphs.size()) ||
1159 mvGlyphs[nGlyph].IsClusterStart())
1161 nGWidth = prevClusterWidth;
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,%ld-%ld\t", nCharSlot, pCaretXArray[i], pCaretXArray[i+1]);
1193 #endif
1195 #ifdef GRLAYOUT_DEBUG
1196 fprintf(grLog(),"\n");
1197 #endif
1201 // GetNextGlyphs returns a contiguous sequence of glyphs that can be
1202 // rendered together. It should never return a dropped glyph.
1203 // The glyph_slot returned should be the index of the next visible
1204 // glyph after the last glyph returned by this call.
1205 // The char_index array should be filled with the characters corresponding
1206 // to each glyph returned.
1207 // glyph_adv array should be a virtual width such that if successive
1208 // glyphs returned by this method are added one after the other they
1209 // have the correct spacing.
1210 // The logic in this method must match that expected in MultiSalLayout which
1211 // is used when glyph fallback is in operation.
1212 int GraphiteLayout::GetNextGlyphs( int length, sal_GlyphId * glyph_out,
1213 ::Point & aPosOut, int &glyph_slot, sal_Int32 * glyph_adv, int *char_index) 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 = std::min(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);
1247 for (;;) // Forever
1249 // last index of the range from glyph_to_chars does not include this glyph
1250 if (char_index)
1252 assert((glyph_slot >= -1) && (glyph_slot < (signed)mvGlyph2Char.size()));
1253 if (mvGlyph2Char[glyph_slot] == -1)
1254 *char_index++ = mvCharDxs.size();
1255 else
1256 *char_index++ = mvGlyph2Char[glyph_slot];
1258 // Copy out this glyphs data.
1259 ++glyph_slot;
1260 *glyph_out++ = glyph_itr->mnGlyphIndex;
1262 // Find the actual advance - this must be correct if called from
1263 // MultiSalLayout::AdjustLayout which requests one glyph at a time.
1264 const long nGlyphAdvance = (glyph_slot == static_cast<int>(mvGlyphs.size()))?
1265 glyph_itr->mnNewWidth :
1266 ((glyph_itr+1)->maLinearPos.X() - glyph_itr->maLinearPos.X());
1268 #ifdef GRLAYOUT_DEBUG
1269 fprintf(grLog(),"GetNextGlyphs g%d c%d x%ld,%ld adv%ld, pos %ld,%ld\n", glyph_slot - 1,
1270 mvGlyph2Char[glyph_slot-1], glyph_itr->maLinearPos.X(), glyph_itr->maLinearPos.Y(), nGlyphAdvance,
1271 aPosOut.X(), aPosOut.Y());
1272 #endif
1274 if (glyph_adv) // If we are returning advance store it.
1275 *glyph_adv++ = nGlyphAdvance;
1276 else // Stop when next advance is unexpected.
1277 if (glyph_itr->mnOrigWidth != nGlyphAdvance) break;
1279 // Have fetched all the glyphs we need to
1280 if (glyph_slot == nGlyphSlotEnd)
1281 break;
1283 ++glyph_itr;
1284 // Stop when next y position is unexpected.
1285 if (initial_y_pos != glyph_itr->maLinearPos.Y())
1286 break;
1288 // Stop if glyph dropped
1289 if (glyph_itr->mnGlyphIndex == GF_DROPPED)
1290 break;
1292 int numGlyphs = glyph_slot - glyph_slot_begin;
1293 // move the next glyph_slot to a glyph that hasn't been dropped
1294 while (glyph_slot < static_cast<int>(mvGlyphs.size()) &&
1295 (mvGlyphs.begin() + glyph_slot)->mnGlyphIndex == GF_DROPPED)
1296 ++glyph_slot;
1297 return numGlyphs;
1301 void GraphiteLayout::MoveGlyph( int nGlyphIndex, long nNewPos )
1303 // TODO it might be better to actualy implement simplify properly, but this
1304 // needs to be done carefully so the glyph/char maps are maintained
1305 // If a glyph has been dropped then it wasn't returned by GetNextGlyphs, so
1306 // the index here may be wrong
1307 while ((mvGlyphs[nGlyphIndex].mnGlyphIndex == GF_DROPPED) &&
1308 (nGlyphIndex < (signed)mvGlyphs.size()))
1310 nGlyphIndex++;
1312 const long dx = nNewPos - mvGlyphs[nGlyphIndex].maLinearPos.X();
1314 if (dx == 0) return;
1315 // GenericSalLayout only changes maLinearPos, mvCharDxs doesn't change
1316 #ifdef GRLAYOUT_DEBUG
1317 fprintf(grLog(),"Move %d (%ld,%ld) c%d by %ld\n", nGlyphIndex, mvGlyphs[nGlyphIndex].maLinearPos.X(), nNewPos, mvGlyph2Char[nGlyphIndex], dx);
1318 #endif
1319 for (size_t gi = nGlyphIndex; gi < mvGlyphs.size(); gi++)
1321 mvGlyphs[gi].maLinearPos.X() += dx;
1323 // width does need to be updated for correct fallback
1324 mnWidth += dx;
1328 void GraphiteLayout::DropGlyph( int nGlyphIndex )
1330 if(nGlyphIndex >= signed(mvGlyphs.size()))
1331 return;
1333 GlyphItem & glyph = mvGlyphs[nGlyphIndex];
1334 glyph.mnGlyphIndex = GF_DROPPED;
1335 #ifdef GRLAYOUT_DEBUG
1336 fprintf(grLog(),"Dropped %d\n", nGlyphIndex);
1337 #endif
1340 void GraphiteLayout::Simplify( bool isBaseLayout )
1342 const sal_GlyphId dropMarker = isBaseLayout ? GF_DROPPED : 0;
1344 Glyphs::iterator gi = mvGlyphs.begin();
1345 // TODO check whether we need to adjust positions here
1346 // MultiSalLayout seems to move the glyphs itself, so it may not be needed.
1347 long deltaX = 0;
1348 while (gi != mvGlyphs.end())
1350 if (gi->mnGlyphIndex == dropMarker)
1352 deltaX += gi->mnNewWidth;
1353 gi->mnNewWidth = 0;
1355 else
1357 deltaX = 0;
1359 //mvCharDxs[mvGlyph2Char[gi->mnCharPos]] -= deltaX;
1360 ++gi;
1362 #ifdef GRLAYOUT_DEBUG
1363 fprintf(grLog(),"Simplify base%d dx=%ld newW=%ld\n", isBaseLayout, deltaX, mnWidth - deltaX);
1364 #endif
1365 // discard width from trailing dropped glyphs, but not those in the middle
1366 mnWidth -= deltaX;