2 * This file is part of the LibreOffice project.
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 * This file incorporates work covered by the following license notice:
10 * Licensed to the Apache Software Foundation (ASF) under one or more
11 * contributor license agreements. See the NOTICE file distributed
12 * with this work for additional information regarding copyright
13 * ownership. The ASF licenses this file to you under the Apache
14 * License, Version 2.0 (the "License"); you may not use this file
15 * except in compliance with the License. You may obtain a copy of
16 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 #include <vcl/fontcharmap.hxx>
19 #include <impfontcharmap.hxx>
20 #include <rtl/textcvt.h>
21 #include <rtl/textenc.h>
22 #include <sal/log.hxx>
26 #include <o3tl/sorted_vector.hxx>
28 CmapResult::CmapResult( bool bSymbolic
,
29 const sal_UCS4
* pRangeCodes
, int nRangeCount
)
30 : mpRangeCodes( pRangeCodes
)
31 , mpStartGlyphs( nullptr)
32 , mpGlyphIds( nullptr)
33 , mnRangeCount( nRangeCount
)
34 , mbSymbolic( bSymbolic
)
38 static ImplFontCharMapRef g_pDefaultImplFontCharMap
;
39 const sal_UCS4 aDefaultUnicodeRanges
[] = {0x0020,0xD800, 0xE000,0xFFF0};
40 const sal_UCS4 aDefaultSymbolRanges
[] = {0x0020,0x0100, 0xF020,0xF100};
42 ImplFontCharMap::~ImplFontCharMap()
46 delete[] mpRangeCodes
;
47 delete[] mpStartGlyphs
;
52 ImplFontCharMap::ImplFontCharMap( const CmapResult
& rCR
)
53 : mpRangeCodes( rCR
.mpRangeCodes
)
54 , mpStartGlyphs( rCR
.mpStartGlyphs
)
55 , mpGlyphIds( rCR
.mpGlyphIds
)
56 , mnRangeCount( rCR
.mnRangeCount
)
58 , m_bSymbolic(rCR
.mbSymbolic
)
60 const sal_UCS4
* pRangePtr
= mpRangeCodes
;
61 for( int i
= mnRangeCount
; --i
>= 0; pRangePtr
+= 2 )
63 sal_UCS4 cFirst
= pRangePtr
[0];
64 sal_UCS4 cLast
= pRangePtr
[1];
65 mnCharCount
+= cLast
- cFirst
;
69 ImplFontCharMapRef
const & ImplFontCharMap::getDefaultMap( bool bSymbols
)
71 const sal_UCS4
* pRangeCodes
= aDefaultUnicodeRanges
;
72 int nCodesCount
= SAL_N_ELEMENTS(aDefaultUnicodeRanges
);
75 pRangeCodes
= aDefaultSymbolRanges
;
76 nCodesCount
= SAL_N_ELEMENTS(aDefaultSymbolRanges
);
79 CmapResult
aDefaultCR( bSymbols
, pRangeCodes
, nCodesCount
/2 );
80 g_pDefaultImplFontCharMap
= ImplFontCharMapRef(new ImplFontCharMap(aDefaultCR
));
82 return g_pDefaultImplFontCharMap
;
85 bool ImplFontCharMap::isDefaultMap() const
87 const bool bIsDefault
= (mpRangeCodes
== aDefaultUnicodeRanges
) || (mpRangeCodes
== aDefaultSymbolRanges
);
91 static unsigned GetUInt( const unsigned char* p
) { return((p
[0]<<24)+(p
[1]<<16)+(p
[2]<<8)+p
[3]);}
92 static unsigned GetUShort( const unsigned char* p
){ return((p
[0]<<8) | p
[1]);}
93 static int GetSShort( const unsigned char* p
){ return static_cast<sal_Int16
>((p
[0]<<8)|p
[1]);}
95 // TODO: move CMAP parsing directly into the ImplFontCharMap class
96 bool ParseCMAP( const unsigned char* pCmap
, int nLength
, CmapResult
& rResult
)
98 rResult
.mpRangeCodes
= nullptr;
99 rResult
.mpStartGlyphs
= nullptr;
100 rResult
.mpGlyphIds
= nullptr;
101 rResult
.mnRangeCount
= 0;
102 rResult
.mbRecoded
= false;
103 rResult
.mbSymbolic
= false;
105 // parse the table header and check for validity
106 if( !pCmap
|| (nLength
< 24) )
109 if( GetUShort( pCmap
) != 0x0000 ) // simple check for CMAP corruption
112 int nSubTables
= GetUShort( pCmap
+ 2 );
113 if( (nSubTables
<= 0) || (nLength
< (24 + 8*nSubTables
)) )
116 const unsigned char* pEndValidArea
= pCmap
+ nLength
;
118 // find the most interesting subtable in the CMAP
119 rtl_TextEncoding eRecodeFrom
= RTL_TEXTENCODING_UNICODE
;
123 for( const unsigned char* p
= pCmap
+ 4; --nSubTables
>= 0; p
+= 8 )
125 int nPlatform
= GetUShort( p
);
126 int nEncoding
= GetUShort( p
+2 );
127 int nPlatformEncoding
= (nPlatform
<< 8) + nEncoding
;
130 rtl_TextEncoding eTmpEncoding
= RTL_TEXTENCODING_UNICODE
;
131 switch( nPlatformEncoding
)
133 case 0x000: nValue
= 20; break; // Unicode 1.0
134 case 0x001: nValue
= 21; break; // Unicode 1.1
135 case 0x002: nValue
= 22; break; // iso10646_1993
136 case 0x003: nValue
= 23; break; // UCS-2
137 case 0x004: nValue
= 24; break; // UCS-4
138 case 0x100: nValue
= 22; break; // Mac Unicode<2.0
139 case 0x103: nValue
= 23; break; // Mac Unicode>2.0
140 case 0x300: nValue
= 5; rResult
.mbSymbolic
= true; break; // Win Symbol
141 case 0x301: nValue
= 28; break; // Win UCS-2
142 case 0x30A: nValue
= 29; break; // Win-UCS-4
143 case 0x302: nValue
= 11; eTmpEncoding
= RTL_TEXTENCODING_SHIFT_JIS
; break;
144 case 0x303: nValue
= 12; eTmpEncoding
= RTL_TEXTENCODING_GB_18030
; break;
145 case 0x304: nValue
= 11; eTmpEncoding
= RTL_TEXTENCODING_BIG5
; break;
146 case 0x305: nValue
= 11; eTmpEncoding
= RTL_TEXTENCODING_MS_949
; break;
147 case 0x306: nValue
= 11; eTmpEncoding
= RTL_TEXTENCODING_MS_1361
; break;
148 default: nValue
= 0; break;
151 if( nValue
<= 0 ) // ignore unknown encodings
154 int nTmpOffset
= GetUInt( p
+4 );
156 if (nTmpOffset
> nLength
- 2 || nTmpOffset
< 0)
159 int nTmpFormat
= GetUShort( pCmap
+ nTmpOffset
);
160 if( nTmpFormat
== 12 ) // 32bit code -> glyph map format
162 else if( nTmpFormat
!= 4 ) // 16bit code -> glyph map format
163 continue; // ignore other formats
165 if( nBestVal
< nValue
)
168 nOffset
= nTmpOffset
;
169 nFormat
= nTmpFormat
;
170 eRecodeFrom
= eTmpEncoding
;
174 // parse the best CMAP subtable
176 sal_UCS4
* pCodePairs
= nullptr;
177 int* pStartGlyphs
= nullptr;
179 std::vector
<sal_uInt16
> aGlyphIdArray
;
180 aGlyphIdArray
.reserve( 0x1000 );
181 aGlyphIdArray
.push_back( 0 );
183 // format 4, the most common 16bit char mapping table
184 if( (nFormat
== 4) && ((nOffset
+16) < nLength
) )
186 int nSegCountX2
= GetUShort( pCmap
+ nOffset
+ 6 );
187 nRangeCount
= nSegCountX2
/2 - 1;
190 SAL_WARN("vcl.gdi", "negative RangeCount");
194 const unsigned char* pLimitBase
= pCmap
+ nOffset
+ 14;
195 const unsigned char* pBeginBase
= pLimitBase
+ nSegCountX2
+ 2;
196 const unsigned char* pDeltaBase
= pBeginBase
+ nSegCountX2
;
197 const unsigned char* pOffsetBase
= pDeltaBase
+ nSegCountX2
;
199 const int nOffsetBaseStart
= pOffsetBase
- pCmap
;
200 const int nRemainingLen
= nLength
- nOffsetBaseStart
;
201 const int nMaxPossibleRangeOffsets
= nRemainingLen
/ 2;
202 if (nRangeCount
> nMaxPossibleRangeOffsets
)
204 SAL_WARN("vcl.gdi", "more range offsets requested then space available");
205 nRangeCount
= std::max(0, nMaxPossibleRangeOffsets
);
208 pCodePairs
= new sal_UCS4
[ nRangeCount
* 2 ];
209 pStartGlyphs
= new int[ nRangeCount
];
211 sal_UCS4
* pCP
= pCodePairs
;
212 for( int i
= 0; i
< nRangeCount
; ++i
)
214 const sal_UCS4 cMinChar
= GetUShort( pBeginBase
+ 2*i
);
215 const sal_UCS4 cMaxChar
= GetUShort( pLimitBase
+ 2*i
);
216 const int nGlyphDelta
= GetSShort( pDeltaBase
+ 2*i
);
217 const int nRangeOffset
= GetUShort( pOffsetBase
+ 2*i
);
218 if( cMinChar
> cMaxChar
) { // no sane font should trigger this
219 SAL_WARN("vcl.gdi", "Min char should never be more than the max char!");
222 if( cMaxChar
== 0xFFFF ) {
223 SAL_WARN("vcl.gdi", "Format 4 char should not be 0xFFFF");
226 if( !nRangeOffset
) {
227 // glyphid can be calculated directly
228 pStartGlyphs
[i
] = (cMinChar
+ nGlyphDelta
) & 0xFFFF;
230 // update the glyphid-array with the glyphs in this range
231 pStartGlyphs
[i
] = -static_cast<int>(aGlyphIdArray
.size());
232 const unsigned char* pGlyphIdPtr
= pOffsetBase
+ 2*i
+ nRangeOffset
;
233 const size_t nRemainingSize
= pEndValidArea
>= pGlyphIdPtr
? pEndValidArea
- pGlyphIdPtr
: 0;
234 const size_t nMaxPossibleRecords
= nRemainingSize
/2;
235 if (nMaxPossibleRecords
== 0) { // no sane font should trigger this
236 SAL_WARN("vcl.gdi", "More indexes claimed that space available in font!");
239 const size_t nMaxLegalChar
= cMinChar
+ nMaxPossibleRecords
-1;
240 if (cMaxChar
> nMaxLegalChar
) { // no sane font should trigger this
241 SAL_WARN("vcl.gdi", "More indexes claimed that space available in font!");
244 for( sal_UCS4 c
= cMinChar
; c
<= cMaxChar
; ++c
, pGlyphIdPtr
+=2 ) {
245 const int nGlyphIndex
= GetUShort( pGlyphIdPtr
) + nGlyphDelta
;
246 aGlyphIdArray
.push_back( static_cast<sal_uInt16
>(nGlyphIndex
) );
250 *(pCP
++) = cMaxChar
+ 1;
252 nRangeCount
= (pCP
- pCodePairs
) / 2;
254 // format 12, the most common 32bit char mapping table
255 else if( (nFormat
== 12) && ((nOffset
+16) < nLength
) )
257 nRangeCount
= GetUInt( pCmap
+ nOffset
+ 12 );
260 SAL_WARN("vcl.gdi", "negative RangeCount");
264 const int nGroupOffset
= nOffset
+ 16;
265 const int nRemainingLen
= nLength
- nGroupOffset
;
266 const int nMaxPossiblePairs
= nRemainingLen
/ 12;
267 if (nRangeCount
> nMaxPossiblePairs
)
269 SAL_WARN("vcl.gdi", "more code pairs requested then space available");
270 nRangeCount
= std::max(0, nMaxPossiblePairs
);
273 pCodePairs
= new sal_UCS4
[ nRangeCount
* 2 ];
274 pStartGlyphs
= new int[ nRangeCount
];
276 const unsigned char* pGroup
= pCmap
+ nGroupOffset
;
277 sal_UCS4
* pCP
= pCodePairs
;
278 for( int i
= 0; i
< nRangeCount
; ++i
)
280 sal_UCS4 cMinChar
= GetUInt( pGroup
+ 0 );
281 sal_UCS4 cMaxChar
= GetUInt( pGroup
+ 4 );
282 int nGlyphId
= GetUInt( pGroup
+ 8 );
285 if( cMinChar
> cMaxChar
) { // no sane font should trigger this
286 SAL_WARN("vcl.gdi", "Min char should never be more than the max char!");
291 *(pCP
++) = cMaxChar
+ 1;
292 pStartGlyphs
[i
] = nGlyphId
;
294 nRangeCount
= (pCP
- pCodePairs
) / 2;
297 // check if any subtable resulted in something usable
298 if( nRangeCount
<= 0 )
301 delete[] pStartGlyphs
;
303 // even when no CMAP is available we know it for symbol fonts
304 if( rResult
.mbSymbolic
)
306 pCodePairs
= new sal_UCS4
[4];
307 pCodePairs
[0] = 0x0020; // aliased symbols
308 pCodePairs
[1] = 0x0100;
309 pCodePairs
[2] = 0xF020; // original symbols
310 pCodePairs
[3] = 0xF100;
311 rResult
.mpRangeCodes
= pCodePairs
;
312 rResult
.mnRangeCount
= 2;
319 // recode the code ranges to their unicode encoded ranges if needed
320 rtl_TextToUnicodeConverter aConverter
= nullptr;
321 rtl_UnicodeToTextContext aCvtContext
= nullptr;
323 rResult
.mbRecoded
= ( eRecodeFrom
!= RTL_TEXTENCODING_UNICODE
);
324 if( rResult
.mbRecoded
)
326 aConverter
= rtl_createTextToUnicodeConverter( eRecodeFrom
);
327 aCvtContext
= rtl_createTextToUnicodeContext( aConverter
);
330 if( aConverter
&& aCvtContext
)
332 // determine the set of supported code points from encoded ranges
333 o3tl::sorted_vector
<sal_UCS4
> aSupportedCodePoints
;
334 aSupportedCodePoints
.reserve(256);
336 static const int NINSIZE
= 64;
337 static const int NOUTSIZE
= 64;
338 std::vector
<char> cCharsInp
;
339 cCharsInp
.reserve(NINSIZE
);
340 sal_Unicode cCharsOut
[ NOUTSIZE
];
341 sal_UCS4
* pCP
= pCodePairs
;
342 for( int i
= 0; i
< nRangeCount
; ++i
)
344 sal_UCS4 cMin
= *(pCP
++);
345 sal_UCS4 cEnd
= *(pCP
++);
346 // ofz#25868 the conversion only makes sense with
347 // input codepoints in 0..SAL_MAX_UINT16 range
348 while (cMin
< cEnd
&& cMin
<= SAL_MAX_UINT16
)
350 for (int j
= 0; (cMin
< cEnd
) && (j
< NINSIZE
); ++cMin
, ++j
)
353 cCharsInp
.push_back(static_cast<char>(cMin
>> 8));
354 if( (cMin
>= 0x0100) || (cMin
< 0x00A0) )
355 cCharsInp
.push_back(static_cast<char>(cMin
));
359 sal_Size nSrcCvtBytes
;
360 int nOutLen
= rtl_convertTextToUnicode(
361 aConverter
, aCvtContext
,
362 cCharsInp
.data(), cCharsInp
.size(), cCharsOut
, NOUTSIZE
,
363 RTL_TEXTTOUNICODE_FLAGS_INVALID_IGNORE
364 | RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_IGNORE
,
365 &nCvtInfo
, &nSrcCvtBytes
);
369 for (int j
= 0; j
< nOutLen
; ++j
)
370 aSupportedCodePoints
.insert( cCharsOut
[j
] );
374 rtl_destroyTextToUnicodeConverter( aCvtContext
);
375 rtl_destroyTextToUnicodeConverter( aConverter
);
377 // convert the set of supported code points to ranges
378 std::vector
<sal_UCS4
> aSupportedRanges
;
380 for (auto const& supportedPoint
: aSupportedCodePoints
)
382 if( aSupportedRanges
.empty()
383 || (aSupportedRanges
.back() != supportedPoint
) )
385 // add new range beginning with current unicode
386 aSupportedRanges
.push_back(supportedPoint
);
387 aSupportedRanges
.push_back( 0 );
390 // extend existing range to include current unicode
391 aSupportedRanges
.back() = supportedPoint
+ 1;
394 // glyph mapping for non-unicode fonts not implemented
395 delete[] pStartGlyphs
;
396 pStartGlyphs
= nullptr;
397 aGlyphIdArray
.clear();
399 // make a pCodePairs array using the vector from above
401 nRangeCount
= aSupportedRanges
.size() / 2;
402 if( nRangeCount
<= 0 )
404 pCodePairs
= new sal_UCS4
[ nRangeCount
* 2 ];
406 for (auto const& supportedRange
: aSupportedRanges
)
407 *(pCP
++) = supportedRange
;
410 // prepare the glyphid-array if needed
411 // TODO: merge ranges if they are close enough?
412 sal_uInt16
* pGlyphIds
= nullptr;
413 if( !aGlyphIdArray
.empty())
415 pGlyphIds
= new sal_uInt16
[ aGlyphIdArray
.size() ];
416 sal_uInt16
* pOut
= pGlyphIds
;
417 for (auto const& glyphId
: aGlyphIdArray
)
421 // update the result struct
422 rResult
.mpRangeCodes
= pCodePairs
;
423 rResult
.mpStartGlyphs
= pStartGlyphs
;
424 rResult
.mnRangeCount
= nRangeCount
;
425 rResult
.mpGlyphIds
= pGlyphIds
;
429 FontCharMap::FontCharMap()
430 : mpImplFontCharMap( ImplFontCharMap::getDefaultMap() )
434 FontCharMap::FontCharMap( ImplFontCharMapRef
const & pIFCMap
)
435 : mpImplFontCharMap( pIFCMap
)
439 FontCharMap::FontCharMap( const CmapResult
& rCR
)
440 : mpImplFontCharMap(new ImplFontCharMap(rCR
))
444 FontCharMap::~FontCharMap()
446 mpImplFontCharMap
= nullptr;
449 FontCharMapRef
FontCharMap::GetDefaultMap( bool bSymbol
)
451 FontCharMapRef
xFontCharMap( new FontCharMap( ImplFontCharMap::getDefaultMap( bSymbol
) ) );
455 bool FontCharMap::IsDefaultMap() const
457 return mpImplFontCharMap
->isDefaultMap();
460 bool FontCharMap::isSymbolic() const { return mpImplFontCharMap
->m_bSymbolic
; }
462 int FontCharMap::GetCharCount() const
464 return mpImplFontCharMap
->mnCharCount
;
467 int FontCharMap::CountCharsInRange( sal_UCS4 cMin
, sal_UCS4 cMax
) const
471 // find and adjust range and char count for cMin
472 int nRangeMin
= findRangeIndex( cMin
);
475 else if( cMin
> mpImplFontCharMap
->mpRangeCodes
[ nRangeMin
] )
476 nCount
-= cMin
- mpImplFontCharMap
->mpRangeCodes
[ nRangeMin
];
478 // find and adjust range and char count for cMax
479 int nRangeMax
= findRangeIndex( cMax
);
483 nCount
-= mpImplFontCharMap
->mpRangeCodes
[ nRangeMax
+1 ] - cMax
- 1;
485 // count chars in complete ranges between cMin and cMax
486 for( int i
= nRangeMin
; i
<= nRangeMax
; i
+=2 )
487 nCount
+= mpImplFontCharMap
->mpRangeCodes
[i
+1] - mpImplFontCharMap
->mpRangeCodes
[i
];
492 bool FontCharMap::HasChar( sal_UCS4 cChar
) const
494 bool bHasChar
= false;
496 if( mpImplFontCharMap
->mpStartGlyphs
== nullptr ) { // only the char-ranges are known
497 const int nRange
= findRangeIndex( cChar
);
498 if( nRange
==0 && cChar
< mpImplFontCharMap
->mpRangeCodes
[0] )
500 bHasChar
= ((nRange
& 1) == 0); // inside a range
501 } else { // glyph mapping is available
502 const int nGlyphIndex
= GetGlyphIndex( cChar
);
503 bHasChar
= (nGlyphIndex
!= 0); // not the notdef-glyph
509 sal_UCS4
FontCharMap::GetFirstChar() const
511 return mpImplFontCharMap
->mpRangeCodes
[0];
514 sal_UCS4
FontCharMap::GetLastChar() const
516 return (mpImplFontCharMap
->mpRangeCodes
[ 2*mpImplFontCharMap
->mnRangeCount
-1 ] - 1);
519 sal_UCS4
FontCharMap::GetNextChar( sal_UCS4 cChar
) const
521 if( cChar
< GetFirstChar() )
522 return GetFirstChar();
523 if( cChar
>= GetLastChar() )
524 return GetLastChar();
526 int nRange
= findRangeIndex( cChar
+ 1 );
527 if( nRange
& 1 ) // outside of range?
528 return mpImplFontCharMap
->mpRangeCodes
[ nRange
+ 1 ]; // => first in next range
532 sal_UCS4
FontCharMap::GetPrevChar( sal_UCS4 cChar
) const
534 if( cChar
<= GetFirstChar() )
535 return GetFirstChar();
536 if( cChar
> GetLastChar() )
537 return GetLastChar();
539 int nRange
= findRangeIndex( cChar
- 1 );
540 if( nRange
& 1 ) // outside a range?
541 return (mpImplFontCharMap
->mpRangeCodes
[ nRange
] - 1); // => last in prev range
545 int FontCharMap::GetIndexFromChar( sal_UCS4 cChar
) const
547 // TODO: improve linear walk?
549 const sal_UCS4
* pRange
= &mpImplFontCharMap
->mpRangeCodes
[0];
550 for( int i
= 0; i
< mpImplFontCharMap
->mnRangeCount
; ++i
)
552 sal_UCS4 cFirst
= *(pRange
++);
553 sal_UCS4 cLast
= *(pRange
++);
555 nCharIndex
+= cLast
- cFirst
;
556 else if( cChar
>= cFirst
)
557 return nCharIndex
+ (cChar
- cFirst
);
565 sal_UCS4
FontCharMap::GetCharFromIndex( int nIndex
) const
567 // TODO: improve linear walk?
568 const sal_UCS4
* pRange
= &mpImplFontCharMap
->mpRangeCodes
[0];
569 for( int i
= 0; i
< mpImplFontCharMap
->mnRangeCount
; ++i
)
571 sal_UCS4 cFirst
= *(pRange
++);
572 sal_UCS4 cLast
= *(pRange
++);
573 nIndex
-= cLast
- cFirst
;
575 return (cLast
+ nIndex
);
578 // we can only get here with an out-of-bounds charindex
579 return mpImplFontCharMap
->mpRangeCodes
[0];
582 int FontCharMap::findRangeIndex( sal_UCS4 cChar
) const
585 int nMid
= mpImplFontCharMap
->mnRangeCount
;
586 int nUpper
= 2 * mpImplFontCharMap
->mnRangeCount
- 1;
587 while( nLower
< nUpper
)
589 if( cChar
>= mpImplFontCharMap
->mpRangeCodes
[ nMid
] )
593 nMid
= (nLower
+ nUpper
+ 1) / 2;
599 int FontCharMap::GetGlyphIndex( sal_UCS4 cChar
) const
601 // return -1 if the object doesn't know the glyph ids
602 if( !mpImplFontCharMap
->mpStartGlyphs
)
605 // return 0 if the unicode doesn't have a matching glyph
606 int nRange
= findRangeIndex( cChar
);
607 // check that we are inside any range
608 if( (nRange
== 0) && (cChar
< mpImplFontCharMap
->mpRangeCodes
[0]) ) {
609 // symbol aliasing gives symbol fonts a second chance
610 const bool bSymbolic
= cChar
<= 0xFF && (mpImplFontCharMap
->mpRangeCodes
[0]>=0xF000) &&
611 (mpImplFontCharMap
->mpRangeCodes
[1]<=0xF0FF);
614 // check for symbol aliasing (U+F0xx -> U+00xx)
616 nRange
= findRangeIndex( cChar
);
617 if( (nRange
== 0) && (cChar
< mpImplFontCharMap
->mpRangeCodes
[0]) ) {
621 // check that we are inside a range
622 if( (nRange
& 1) != 0 )
625 // get glyph index directly or indirectly
626 int nGlyphIndex
= cChar
- mpImplFontCharMap
->mpRangeCodes
[ nRange
];
627 const int nStartIndex
= mpImplFontCharMap
->mpStartGlyphs
[ nRange
/2 ];
628 if( nStartIndex
>= 0 ) {
629 // the glyph index can be calculated
630 nGlyphIndex
+= nStartIndex
;
632 // the glyphid array has the glyph index
633 nGlyphIndex
= mpImplFontCharMap
->mpGlyphIds
[ nGlyphIndex
- nStartIndex
];
639 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */