1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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
23 // Enable lots of debug info
24 #if OSL_DEBUG_LEVEL > 1
26 #define GRLAYOUT_DEBUG 1
30 // #define GRLAYOUT_DEBUG 1
39 #include <config_global.h>
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>
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");
83 inline long round_to_long(const float n
) {
84 return long(n
+ (n
< 0 ? -0.5 : 0.5));
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
);
97 ubidi_setPara(ubidi
, reinterpret_cast<const UChar
*>(buffer
), charCount
,
98 (rtl
)?UBIDI_DEFAULT_RTL
:UBIDI_DEFAULT_LTR
, NULL
, &status
);
100 ubidi_getLogicalRun(ubidi
, 0, &limit
, &level
);
102 if ((rtl
&& !(level
& 1)) || (!rtl
&& (level
& 1)))
109 template <typename T
>
112 return (a
> b
)? a
: b
;
114 template <typename T
>
117 return (a
< b
)? a
: b
;
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
)
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
);
144 if (gr_slot_origin_X(*first
) > gr_slot_origin_X(attachment
))
146 const gr_slot
* attachmentNext
= (bRtl
)?
147 gr_slot_prev_in_segment(attachment
) : gr_slot_next_in_segment(attachment
);
150 if (*after
&& (gr_slot_origin_X(*after
) < gr_slot_origin_X(attachmentNext
)))
151 *after
= attachmentNext
;
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.
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
);
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
);
188 const gr_slot
* baseSlot
= gr_seg_last_slot(pSegment
);
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
199 bool bCluster
= !reordered
;
200 const gr_slot
* clusterFirst
= NULL
;
201 const gr_slot
* clusterAfter
= NULL
;
204 findFirstClusterSlot(baseSlot
, &clusterFirst
, &clusterAfter
, &firstChar
, &lastChar
, bRtl
);
205 iNextChar
= minimum
<int>(firstChar
, iNextChar
);
208 nBaseGlyphIndex
= mvGlyphs
.size();
209 mvGlyph2Char
[nBaseGlyphIndex
] = iChar
+ mnSegCharOffset
;
210 nFirstCharInCluster
= firstChar
;
211 nLastCharInCluster
= lastChar
;
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
);
223 lastChar
< iChar
&& clusterAfter
&&
224 (gr_cinfo_after(gr_seg_cinfo(pSegment
, iChar
)) >
225 static_cast<int>(gr_slot_index(clusterAfter
)))
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
);
242 for (int i
= nFirstCharInCluster
; i
<= nLastCharInCluster
; i
++)
244 if (mnSegCharOffset
+ i
>= mnEndCharPos
)
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
)
259 baseSlot
= gr_slot_next_sibling_attachment(baseSlot
);
264 const gr_slot
* baseSlot
= gr_seg_first_slot(pSegment
);
266 while (baseSlot
&& (gr_slot_attached_to(baseSlot
) != NULL
))
267 baseSlot
= gr_slot_next_in_segment(baseSlot
);
268 int iChar
= 0; // relative to segment
270 bool reordered
= false;
271 int nBaseGlyphIndex
= 0;
272 // now loop over bases
275 bool bCluster
= !reordered
;
276 const gr_slot
* clusterFirst
= NULL
;
277 const gr_slot
* clusterAfter
= NULL
;
280 findFirstClusterSlot(baseSlot
, &clusterFirst
, &clusterAfter
, &firstChar
, &lastChar
, bRtl
);
281 iNextChar
= maximum
<int>(lastChar
, iNextChar
);
284 nBaseGlyphIndex
= mvGlyphs
.size();
285 mvGlyph2Char
[nBaseGlyphIndex
] = iChar
+ mnSegCharOffset
;
286 nFirstCharInCluster
= firstChar
;
287 nLastCharInCluster
= lastChar
;
291 mvGlyph2Char
[mvGlyphs
.size()] = firstChar
+ mnSegCharOffset
;
292 nFirstCharInCluster
= minimum
<int>(firstChar
, nFirstCharInCluster
);
293 nLastCharInCluster
= maximum
<int>(lastChar
, nLastCharInCluster
);
297 (gr_cinfo_before(gr_seg_cinfo(pSegment
, iChar
)) >
298 static_cast<int>(gr_slot_index(clusterFirst
)))
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
);
319 for (int i
= nFirstCharInCluster
; i
<= nLastCharInCluster
; i
++)
321 int ibase
= gr_cinfo_base(gr_seg_cinfo(pSegment
, i
));
322 if (mnSegCharOffset
+ ibase
>= mnEndCharPos
)
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
)
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
;
345 // This can happen when there was no base inside the range
348 // fill up non-base char dx with cluster widths from previous base glyph
351 if (mvCharDxs
[nCharRequested
-1] == -1)
352 mvCharDxs
[nCharRequested
-1] = 0;
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
;
363 if (mvCharDxs
[0] == -1)
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
]);
376 // remove offset due to context if there is one
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
);
387 // append walks an attachment tree, flattening it, and converting it into a
388 // sequence of GlyphItem objects which we can later manipulate.
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
);
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
);
406 nextOrigin
= gr_slot_origin_X(pFirstAttached
);
407 else if (!bIsBase
&& pNextSibling
)
408 nextOrigin
= gr_slot_origin_X(pNextSibling
);
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)
417 // nextOrigin = gOrigin;
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
);
426 rArgs
.NeedFallback(firstChar
, bRtl
);
427 if( (SalLayoutFlags::ForFallback
& rArgs
.mnFlags
))
429 glyphId
= GF_DROPPED
;
430 deltaOffset
-= glyphWidth
;
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
));
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
;
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(),
455 Point(scaledGlyphPos
+ rDXOffset
,
456 round_to_long((-gr_slot_origin_Y(gi
) * scaling
))),
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
);
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()
483 , mpFeatures(pFeatures
)
488 GraphiteLayout::~GraphiteLayout() throw()
491 // the features and font are owned by the platform layers
496 void GraphiteLayout::clear()
498 // Destroy the segment and text source from any previous invocation of
502 mvChar2BaseGlyph
.clear();
503 mvGlyph2Char
.clear();
505 // Reset the state to the empty state.
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
)
514 if (rArgs
.mnMinCharPos
< rArgs
.mnEndCharPos
)
516 gr_segment
* pSegment
= CreateSegment(rArgs
);
519 success
= LayoutGlyphs(rArgs
, pSegment
);
522 gr_seg_destroy(pSegment
);
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
547 bool bRtl(mnLayoutFlags
& SalLayoutFlags::BiDiRtl
);
550 // Don't set RTL if font doesn't support it otherwise it forces rtl on
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
;
592 pSegment
= gr_make_seg(mpFont
, mpFace
, 0, mpFeatures
->values(), gr_utf16
,
593 rArgs
.mpStr
+ mnSegCharOffset
, numchars
, bRtl
);
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");
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");
625 clear(); // destroy the text source and any partially built segments.
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);
638 if (mvCharDxs
.size() > 0)
640 // Discover all the clusters.
643 bool bRtl(mnLayoutFlags
& SalLayoutFlags::BiDiRtl
);
644 fillFrom(pSegment
, rArgs
, mfScaling
);
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());
666 #ifdef GRLAYOUT_DEBUG
667 fprintf(grLog(),"LayoutGlyphs failed with exception");
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
);
686 // return quickly if this segment is narrower than the target width
687 if (maxmnWidth
> mnWidth
* factor
+ char_extra
* (mnEndCharPos
- mnMinCharPos
- 1))
690 DeviceCoordinate nWidth
= mvCharDxs
[0] * factor
;
694 for (size_t i
= 1; i
< mvCharDxs
.size(); i
++)
696 nWidth
+= char_extra
;
697 if (nWidth
> maxmnWidth
) break;
698 if (mvChar2BaseGlyph
[i
] != -1)
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
);
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
;
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]);
723 if (nBreak
> mnEndCharPos
)
725 else if (nBreak
< mnMinCharPos
)
726 nBreak
= mnMinCharPos
;
730 DeviceCoordinate
GraphiteLayout::FillDXArray( DeviceCoordinate
* pDXArray
) const
732 if (mnEndCharPos
== mnMinCharPos
)
733 // Then we must be zero width!
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
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
]);
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
);
769 void GraphiteLayout::AdjustLayout(ImplLayoutArgs
& rArgs
)
771 SalLayout::AdjustLayout(rArgs
);
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;
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
);
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())
825 if (nClusterCount
> 1)
827 float fExtraPerCluster
= static_cast<float>(nDeltaWidth
) / static_cast<float>(nClusterCount
- 1);
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
)
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
;
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());
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
);
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");
895 bool bRtl(mnLayoutFlags
& SalLayoutFlags::BiDiRtl
);
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())
913 // find last glyph of this cluster
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);
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
937 nLastGlyph
= nChar2Base
;
938 while (nLastGlyph
+ 1 < (signed)mvGlyphs
.size() &&
939 !mvGlyphs
[nLastGlyph
+1].IsClusterStart())
946 nLastChar
= nChars
- 1;
947 if (!bRtl
) nLastGlyph
= mvGlyphs
.size() - 1;
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());
970 assert((nLastGlyph
> -1) && (nLastGlyph
< (signed)mvGlyphs
.size()));
971 mvGlyphs
[nLastGlyph
].mnNewWidth
+= nDWidth
;
972 if (gi
.maGlyphId
!= GF_DROPPED
)
973 mvGlyphs
[nLastGlyph
].mnNewWidth
+= nDWidth
;
975 nDGlyphOrigin
+= nDWidth
;
976 long nDOriginPerBase
= (nBaseCount
> 0)? nDWidth
/ nBaseCount
: 0;
978 // update glyph positions
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
;
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());
1001 nPrevClusterGlyph
= nChar2Base
;
1002 nPrevClusterLastChar
= 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
);
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 )
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())
1030 // only inject kashidas in RTL contexts
1031 if( !(*i
).IsRTLGlyph() )
1036 // no kashida-injection for blank justified expansion either
1037 if( IsSpacingGlyph( (*i
).maGlyphId
) )
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
)
1050 nKashidaCount
= 1 + (nGapWidth
/ nKashidaWidth
);
1051 #ifdef GRLAYOUT_DEBUG
1052 printf("inserting %d kashidas at %u\n", nKashidaCount
, (*i
).maGlyphId
);
1054 GlyphItem glyphItem
= *i
;
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
;
1072 // fixup rightmost kashida for gap remainder
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
;
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();
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
)
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
);
1128 pCaretXArray
[i
+1] = nMin
;
1129 pCaretXArray
[i
] = nMax
;
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
];
1155 // if no match position at end of cluster
1156 if (nGlyph
== static_cast<int>(mvGlyphs
.size()) ||
1157 mvGlyphs
[nGlyph
].IsClusterStart())
1161 pCaretXArray
[i
+1] = gi
.maLinearPos
.X();
1162 pCaretXArray
[i
] = gi
.maLinearPos
.X();
1166 pCaretXArray
[i
] = gi
.maLinearPos
.X() + prevClusterWidth
;
1167 pCaretXArray
[i
+1] = gi
.maLinearPos
.X() + prevClusterWidth
;
1174 pCaretXArray
[i
+1] = gi
.maLinearPos
.X();
1175 pCaretXArray
[i
] = gi
.maLinearPos
.X() + gi
.mnNewWidth
;
1179 pCaretXArray
[i
] = gi
.maLinearPos
.X();
1180 pCaretXArray
[i
+1] = gi
.maLinearPos
.X() + gi
.mnNewWidth
;
1186 pCaretXArray
[i
] = pCaretXArray
[i
+1] = 0;
1188 #ifdef GRLAYOUT_DEBUG
1189 fprintf(grLog(),"%d,%ld-%ld\t", nCharSlot
, pCaretXArray
[i
], pCaretXArray
[i
+1]);
1192 #ifdef GRLAYOUT_DEBUG
1193 fprintf(grLog(),"\n");
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();
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
);
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
)
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
);
1245 // last index of the range from glyph_to_chars does not include this glyph
1248 if (glyph_slot
>= (signed)mvGlyph2Char
.size())
1250 *char_index
++ = mnMinCharPos
+ mvCharDxs
.size();
1254 assert(glyph_slot
> -1);
1255 if (mvGlyph2Char
[glyph_slot
] == -1)
1256 *char_index
++ = mnMinCharPos
+ mvCharDxs
.size();
1258 *char_index
++ = mvGlyph2Char
[glyph_slot
];
1261 // Copy out this glyphs data.
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());
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
)
1288 // Stop when next y position is unexpected.
1289 if (initial_y_pos
!= glyph_itr
->maLinearPos
.Y())
1292 // Stop if glyph dropped
1293 if (glyph_itr
->maGlyphId
== GF_DROPPED
)
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
)
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()))
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
);
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
1330 void GraphiteLayout::DropGlyph( int nGlyphIndex
)
1332 if(nGlyphIndex
>= signed(mvGlyphs
.size()))
1335 GlyphItem
& glyph
= mvGlyphs
[nGlyphIndex
];
1336 glyph
.maGlyphId
= GF_DROPPED
;
1337 #ifdef GRLAYOUT_DEBUG
1338 fprintf(grLog(),"Dropped %d\n", nGlyphIndex
);
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.
1350 while (gi
!= mvGlyphs
.end())
1352 if (gi
->maGlyphId
== dropMarker
)
1354 deltaX
+= gi
->mnNewWidth
;
1363 #ifdef GRLAYOUT_DEBUG
1364 fprintf(grLog(),"Simplify base%d dx=%ld newW=%ld\n", isBaseLayout
, deltaX
, mnWidth
- deltaX
);
1366 // discard width from trailing dropped glyphs, but not those in the middle
1370 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */