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