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
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
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.
40 // Enable lots of debug info
42 //#define GRLAYOUT_DEBUG 1
58 #include <tools/svwin.h>
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.
86 FILE * grLogFile
= NULL
;
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
);
102 #include <vcl/graphite_cache.hxx>
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));
117 inline bool in_range(const T i
, const T b
, const T e
) {
118 return !(b
> i
) && i
< e
;
123 inline bool is_subrange(const T sb
, const T se
, const T b
, const T e
) {
124 return !(b
> sb
|| se
> e
);
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
);
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
);
143 if ((rtl
&& !(level
& 1)) || (!rtl
&& (level
& 1)))
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;
162 GraphiteCacheHandler
GraphiteCacheHandler::instance
;
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.
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();
181 rGlyph2Char
.assign(nGlyphs
, -1);
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)
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());
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
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
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
);
249 gr::GlyphSetIterator gj
= charGlyphs
.first
;
250 while (gj
!= charGlyphs
.second
)
252 nLastGlyph
= (bRtl
)? min(nLastGlyph
, (signed)(*gj
).logicalIndex()) : max(nLastGlyph
, (signed)(*gj
).logicalIndex());
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
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
;
299 // This can happen when there was no base inside the range
302 // fill up non-base char dx with cluster widths from previous base glyph
305 if (rCharDxs
[nChar
-1] == -1)
306 rCharDxs
[nChar
-1] = 0;
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
;
317 if (rCharDxs
[0] == -1)
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
);
330 // remove offset due to context if there is one
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
)
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
);
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
);
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
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
398 for (int n
= nNextChar
+ 1; n
<= nFirstCharInCluster
; n
++)
400 if ((n
< rArgs
.mnEndCharPos
) && (n
>= rArgs
.mnMinCharPos
))
401 rCharDxs
[n
-rArgs
.mnMinCharPos
] = nXPos
;
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());
418 // append walks an attachment tree, flattening it, and converting it into a
419 // sequence of GlyphItem objects which we can later manipulate.
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
);
443 gr::RightToLeftDir(gr::DirCode(gi
.directionality())));
444 if( (SAL_LAYOUT_FOR_FALLBACK
& args
.mnFlags
))
446 glyphId
= GF_DROPPED
;
447 deltaOffset
-= glyphWidth
;
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
));
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
;
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(),
474 Point(round(gi
.origin() * scaling
+ rDXOffset
),
475 round((-gi
.yOffset() * scaling
) - segment
.AscentOffset()* scaling
)),
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);
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()
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));
520 GraphiteLayout::~GraphiteLayout() throw()
523 // the features are owned by the platform layers
527 void GraphiteLayout::clear()
529 // Destroy the segment and text source from any previous invocation of
533 mvChar2BaseGlyph
.clear();
534 mvGlyph2Char
.clear();
540 // Reset the state to the empty state.
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
)
550 GrSegRecord
* pSegRecord
= NULL
;
551 gr::Segment
* pSegment
= CreateSegment(rArgs
, &pSegRecord
);
555 // layout the glyphs as required by OpenOffice
556 bool success
= LayoutGlyphs(rArgs
, pSegment
, pSegRecord
);
558 if (pSegRecord
) pSegRecord
->unlock();
559 else delete pSegment
;
561 gr::Segment
* pSegment
= CreateSegment(rArgs
);
562 bool success
= LayoutGlyphs(rArgs
, pSegment
);
569 class GrFontHasher
: public gr::Font
572 GrFontHasher(const gr::Font
& aFont
) : gr::Font(aFont
), mrRealFont(const_cast<gr::Font
&>(aFont
)) {};
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?
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
++)
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;
607 hash
^= mpFeatures
->hashCode();
608 #ifdef GRLAYOUT_DEBUG
609 fprintf(grLog(), "font hash %x size %f\n", (int)hash
, height());
615 gr::Font
& mrRealFont
;
620 gr::Segment
* GraphiteLayout::CreateSegment(ImplLayoutArgs
& rArgs
, GrSegRecord
** pSegRecord
)
622 gr::Segment
* GraphiteLayout::CreateSegment(ImplLayoutArgs
& rArgs
)
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
637 bool bRtl
= mnLayoutFlags
& SAL_LAYOUT_BIDI_RTL
;
640 // Don't set RTL if font doesn't support it otherwise it forces rtl on
642 if (bRtl
&& (mrFont
.getSupportedScriptDirections() & gr::kfsdcHorizRtl
))
643 maLayout
.setRightToLeft(bRtl
);
646 GrFontHasher
hasher(mrFont
);
647 sal_Int32 aFontHash
= hasher
.hashCode(mpFeatures
);
648 GraphiteSegmentCache
* pCache
=
649 (GraphiteCacheHandler::instance
).getCache(aFontHash
);
652 *pSegRecord
= pCache
->getSegment(rArgs
, bRtl
);
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
);
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
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
);
695 // on a new segment rightToLeft should be correct
696 *pSegRecord
= pCache
->cacheSegment(mpTextSrc
, pSegment
, pSegment
->rightToLeft());
707 clear(); // destroy the text source and any partially built segments.
714 bool GraphiteLayout::LayoutGlyphs(ImplLayoutArgs
& rArgs
, gr::Segment
* pSegment
, GrSegRecord
* pSegRecord
)
716 bool GraphiteLayout::LayoutGlyphs(ImplLayoutArgs
& rArgs
, gr::Segment
* pSegment
)
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();
734 // Calculate the initial character dxs.
735 mvCharDxs
.assign(mnEndCharPos
- mnMinCharPos
, -1);
736 mvChar2BaseGlyph
.assign(mnEndCharPos
- mnMinCharPos
, -1);
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.
747 bool bRtl
= pSegRecord
? pSegRecord
->isRtl() : pSegment
->rightToLeft();
749 bool bRtl
= pSegment
->rightToLeft();
751 mvGlyphs
.fill_from(*pSegment
, rArgs
, bRtl
,
752 mnWidth
, mfScaling
, mvChar2BaseGlyph
, mvGlyph2Char
, mvCharDxs
);
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
;
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
);
773 catch (std::exception e
)
775 #ifdef GRLAYOUT_DEBUG
776 fprintf(grLog(),"LayoutGlyphs failed %s\n", e
.what());
782 #ifdef GRLAYOUT_DEBUG
783 fprintf(grLog(),"LayoutGlyphs failed with exception");
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.
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
)
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(),
817 int nBreak
= lineSeg
.stopCharacter();
819 if (nBreak
> mnEndCharPos
) nBreak
= STRING_LEN
;
820 else if (nBreak
< mnMinCharPos
) nBreak
= mnMinCharPos
;
825 long GraphiteLayout::FillDXArray( sal_Int32
* pDXArray
) const
827 if (mnEndCharPos
== mnMinCharPos
)
828 // Then we must be zero width!
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
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
]);
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
);
864 void GraphiteLayout::AdjustLayout(ImplLayoutArgs
& rArgs
)
866 SalLayout::AdjustLayout(rArgs
);
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;
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");
909 bool bRtl
= mnLayoutFlags
& SAL_LAYOUT_BIDI_RTL
;
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())
926 // find last glyph of this cluster
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);
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
949 nLastGlyph
= mvChar2BaseGlyph
[i
];
950 while (nLastGlyph
+ 1 < (signed)mvGlyphs
.size() &&
951 !mvGlyphs
[nLastGlyph
+1].IsClusterStart())
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());
976 assert((nLastGlyph
> -1) && (nLastGlyph
< (signed)mvGlyphs
.size()));
977 mvGlyphs
[nLastGlyph
].mnNewWidth
+= nDWidth
;
978 if (gi
.mnGlyphIndex
!= GF_DROPPED
)
979 mvGlyphs
[nLastGlyph
].mnNewWidth
+= nDWidth
;
981 nDGlyphOrigin
+= nDWidth
;
982 // update glyph positions
985 for (int n
= mvChar2BaseGlyph
[i
]; n
<= nLastGlyph
; n
++)
987 assert((n
> - 1) && (n
< (signed)mvGlyphs
.size()));
988 mvGlyphs
[n
].maLinearPos
.X() += -nDGlyphOrigin
+ nXOffset
;
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());
1003 nPrevClusterGlyph
= mvChar2BaseGlyph
[i
];
1004 nPrevClusterLastChar
= 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
);
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 )
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())
1032 // only inject kashidas in RTL contexts
1033 if( !(*i
).IsRTLGlyph() )
1038 // no kashida-injection for blank justified expansion either
1039 if( IsSpacingGlyph( (*i
).mnGlyphIndex
) )
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
)
1052 nKashidaCount
= 1 + (nGapWidth
/ nKashidaWidth
);
1053 #ifdef GRLAYOUT_DEBUG
1054 printf("inserting %d kashidas at %ld\n", nKashidaCount
, (*i
).mnGlyphIndex
);
1056 GlyphItem glyphItem
= *i
;
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
;
1074 // fixup rightmost kashida for gap remainder
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
;
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();
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
)
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
);
1129 pCaretXArray
[i
+1] = nMin
;
1130 pCaretXArray
[i
] = nMax
;
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
];
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
;
1164 pCaretXArray
[i
+1] = gi
.maLinearPos
.X();
1165 pCaretXArray
[i
] = gi
.maLinearPos
.X();
1169 pCaretXArray
[i
] = gi
.maLinearPos
.X() + prevClusterWidth
;
1170 pCaretXArray
[i
+1] = gi
.maLinearPos
.X() + prevClusterWidth
;
1177 pCaretXArray
[i
+1] = gi
.maLinearPos
.X();
1178 pCaretXArray
[i
] = gi
.maLinearPos
.X() + gi
.mnNewWidth
;
1182 pCaretXArray
[i
] = gi
.maLinearPos
.X();
1183 pCaretXArray
[i
+1] = gi
.maLinearPos
.X() + gi
.mnNewWidth
;
1189 pCaretXArray
[i
] = pCaretXArray
[i
+1] = 0;
1191 #ifdef GRLAYOUT_DEBUG
1192 fprintf(grLog(),"%d,%ld-%ld\t", nCharSlot
, pCaretXArray
[i
], pCaretXArray
[i
+1]);
1195 #ifdef GRLAYOUT_DEBUG
1196 fprintf(grLog(),"\n");
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();
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
);
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
)
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
);
1249 // last index of the range from glyph_to_chars does not include this glyph
1252 assert((glyph_slot
>= -1) && (glyph_slot
< (signed)mvGlyph2Char
.size()));
1253 if (mvGlyph2Char
[glyph_slot
] == -1)
1254 *char_index
++ = mvCharDxs
.size();
1256 *char_index
++ = mvGlyph2Char
[glyph_slot
];
1258 // Copy out this glyphs data.
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());
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
)
1284 // Stop when next y position is unexpected.
1285 if (initial_y_pos
!= glyph_itr
->maLinearPos
.Y())
1288 // Stop if glyph dropped
1289 if (glyph_itr
->mnGlyphIndex
== GF_DROPPED
)
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
)
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()))
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
);
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
1328 void GraphiteLayout::DropGlyph( int nGlyphIndex
)
1330 if(nGlyphIndex
>= signed(mvGlyphs
.size()))
1333 GlyphItem
& glyph
= mvGlyphs
[nGlyphIndex
];
1334 glyph
.mnGlyphIndex
= GF_DROPPED
;
1335 #ifdef GRLAYOUT_DEBUG
1336 fprintf(grLog(),"Dropped %d\n", nGlyphIndex
);
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.
1348 while (gi
!= mvGlyphs
.end())
1350 if (gi
->mnGlyphIndex
== dropMarker
)
1352 deltaX
+= gi
->mnNewWidth
;
1359 //mvCharDxs[mvGlyph2Char[gi->mnCharPos]] -= deltaX;
1362 #ifdef GRLAYOUT_DEBUG
1363 fprintf(grLog(),"Simplify base%d dx=%ld newW=%ld\n", isBaseLayout
, deltaX
, mnWidth
- deltaX
);
1365 // discard width from trailing dropped glyphs, but not those in the middle