Line data Source code
1 : /*
2 : * This file is part of the LibreOffice project.
3 : *
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/.
7 : *
8 : * This file incorporates work covered by the following license notice:
9 : *
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 .
17 : */
18 : #include <vcl/metric.hxx>
19 : #include <outfont.hxx>
20 : #include <impfont.hxx>
21 :
22 : #include <vector>
23 : #include <set>
24 :
25 8522 : CmapResult::CmapResult( bool bSymbolic,
26 : const sal_UCS4* pRangeCodes, int nRangeCount,
27 : const int* pStartGlyphs, const sal_uInt16* pExtraGlyphIds )
28 : : mpRangeCodes( pRangeCodes)
29 : , mpStartGlyphs( pStartGlyphs)
30 : , mpGlyphIds( pExtraGlyphIds)
31 : , mnRangeCount( nRangeCount)
32 : , mbSymbolic( bSymbolic)
33 8522 : , mbRecoded( false)
34 8522 : {}
35 :
36 267 : static ImplFontCharMapPtr pDefaultImplFontCharMap;
37 : static const sal_UCS4 aDefaultUnicodeRanges[] = {0x0020,0xD800, 0xE000,0xFFF0};
38 : static const sal_UCS4 aDefaultSymbolRanges[] = {0x0020,0x0100, 0xF020,0xF100};
39 :
40 17044 : ImplFontCharMap::~ImplFontCharMap()
41 : {
42 8522 : if( isDefaultMap() )
43 8479 : return;
44 43 : delete[] mpRangeCodes;
45 43 : delete[] mpStartGlyphs;
46 43 : delete[] mpGlyphIds;
47 17044 : }
48 :
49 8522 : ImplFontCharMap::ImplFontCharMap( const CmapResult& rCR )
50 : : mpRangeCodes( rCR.mpRangeCodes )
51 : , mpStartGlyphs( rCR.mpStartGlyphs )
52 : , mpGlyphIds( rCR.mpGlyphIds )
53 : , mnRangeCount( rCR.mnRangeCount )
54 : , mnCharCount( 0 )
55 8522 : , mnRefCount( 0 )
56 : {
57 8522 : const sal_UCS4* pRangePtr = mpRangeCodes;
58 31776 : for( int i = mnRangeCount; --i >= 0; pRangePtr += 2 )
59 : {
60 23254 : sal_UCS4 cFirst = pRangePtr[0];
61 23254 : sal_UCS4 cLast = pRangePtr[1];
62 23254 : mnCharCount += cLast - cFirst;
63 : }
64 8522 : }
65 :
66 8479 : ImplFontCharMapPtr ImplFontCharMap::getDefaultMap( bool bSymbols )
67 : {
68 8479 : const sal_UCS4* pRangeCodes = aDefaultUnicodeRanges;
69 8479 : int nCodesCount = sizeof(aDefaultUnicodeRanges) / sizeof(*pRangeCodes);
70 8479 : if( bSymbols )
71 : {
72 0 : pRangeCodes = aDefaultSymbolRanges;
73 0 : nCodesCount = sizeof(aDefaultSymbolRanges) / sizeof(*pRangeCodes);
74 : }
75 :
76 8479 : CmapResult aDefaultCR( bSymbols, pRangeCodes, nCodesCount/2 );
77 8479 : pDefaultImplFontCharMap.reset( new ImplFontCharMap( aDefaultCR ) );
78 :
79 8479 : return pDefaultImplFontCharMap;
80 : }
81 :
82 18534 : bool ImplFontCharMap::isDefaultMap() const
83 : {
84 18534 : const bool bIsDefault = (mpRangeCodes == aDefaultUnicodeRanges) || (mpRangeCodes == aDefaultSymbolRanges);
85 18534 : return bIsDefault;
86 : }
87 :
88 6393 : static unsigned GetUInt( const unsigned char* p ) { return((p[0]<<24)+(p[1]<<16)+(p[2]<<8)+p[3]);}
89 35888 : static unsigned Getsal_uInt16( const unsigned char* p ){ return((p[0]<<8) | p[1]);}
90 4216 : static int GetSShort( const unsigned char* p ){ return static_cast<sal_Int16>((p[0]<<8)|p[1]);}
91 :
92 : // TODO: move CMAP parsing directly into the ImplFontCharMap class
93 43 : bool ParseCMAP( const unsigned char* pCmap, int nLength, CmapResult& rResult )
94 : {
95 43 : rResult.mpRangeCodes = NULL;
96 43 : rResult.mpStartGlyphs= NULL;
97 43 : rResult.mpGlyphIds = NULL;
98 43 : rResult.mnRangeCount = 0;
99 43 : rResult.mbRecoded = false;
100 43 : rResult.mbSymbolic = false;
101 :
102 : // parse the table header and check for validity
103 43 : if( !pCmap || (nLength < 24) )
104 0 : return false;
105 :
106 43 : if( Getsal_uInt16( pCmap ) != 0x0000 ) // simple check for CMAP corruption
107 0 : return false;
108 :
109 43 : int nSubTables = Getsal_uInt16( pCmap + 2 );
110 43 : if( (nSubTables <= 0) || (nLength < (24 + 8*nSubTables)) )
111 0 : return false;
112 :
113 43 : const unsigned char* pEndValidArea = pCmap + nLength;
114 :
115 : // find the most interesting subtable in the CMAP
116 43 : rtl_TextEncoding eRecodeFrom = RTL_TEXTENCODING_UNICODE;
117 43 : int nOffset = 0;
118 43 : int nFormat = -1;
119 43 : int nBestVal = 0;
120 188 : for( const unsigned char* p = pCmap + 4; --nSubTables >= 0; p += 8 )
121 : {
122 145 : int nPlatform = Getsal_uInt16( p );
123 145 : int nEncoding = Getsal_uInt16( p+2 );
124 145 : int nPlatformEncoding = (nPlatform << 8) + nEncoding;
125 :
126 : int nValue;
127 145 : rtl_TextEncoding eTmpEncoding = RTL_TEXTENCODING_UNICODE;
128 145 : switch( nPlatformEncoding )
129 : {
130 0 : case 0x000: nValue = 20; break; // Unicode 1.0
131 0 : case 0x001: nValue = 21; break; // Unicode 1.1
132 0 : case 0x002: nValue = 22; break; // iso10646_1993
133 43 : case 0x003: nValue = 23; break; // UCS-2
134 8 : case 0x004: nValue = 24; break; // UCS-4
135 43 : case 0x100: nValue = 22; break; // Mac Unicode<2.0
136 0 : case 0x103: nValue = 23; break; // Mac Unicode>2.0
137 0 : case 0x300: nValue = 5; rResult.mbSymbolic = true; break; // Win Symbol
138 43 : case 0x301: nValue = 28; break; // Win UCS-2
139 8 : case 0x30A: nValue = 29; break; // Win-UCS-4
140 0 : case 0x302: nValue = 11; eTmpEncoding = RTL_TEXTENCODING_SHIFT_JIS; break;
141 0 : case 0x303: nValue = 12; eTmpEncoding = RTL_TEXTENCODING_GB_18030; break;
142 0 : case 0x304: nValue = 11; eTmpEncoding = RTL_TEXTENCODING_BIG5; break;
143 0 : case 0x305: nValue = 11; eTmpEncoding = RTL_TEXTENCODING_MS_949; break;
144 0 : case 0x306: nValue = 11; eTmpEncoding = RTL_TEXTENCODING_MS_1361; break;
145 0 : default: nValue = 0; break;
146 : }
147 :
148 145 : if( nValue <= 0 ) // ignore unknown encodings
149 0 : continue;
150 :
151 145 : int nTmpOffset = GetUInt( p+4 );
152 145 : int nTmpFormat = Getsal_uInt16( pCmap + nTmpOffset );
153 145 : if( nTmpFormat == 12 ) // 32bit code -> glyph map format
154 16 : nValue += 3;
155 129 : else if( nTmpFormat != 4 ) // 16bit code -> glyph map format
156 43 : continue; // ignore other formats
157 :
158 102 : if( nBestVal < nValue )
159 : {
160 102 : nBestVal = nValue;
161 102 : nOffset = nTmpOffset;
162 102 : nFormat = nTmpFormat;
163 102 : eRecodeFrom = eTmpEncoding;
164 : }
165 : }
166 :
167 : // parse the best CMAP subtable
168 43 : int nRangeCount = 0;
169 43 : sal_UCS4* pCodePairs = NULL;
170 43 : int* pStartGlyphs = NULL;
171 :
172 : typedef std::vector<sal_uInt16> U16Vector;
173 43 : U16Vector aGlyphIdArray;
174 43 : aGlyphIdArray.reserve( 0x1000 );
175 43 : aGlyphIdArray.push_back( 0 );
176 :
177 : // format 4, the most common 16bit char mapping table
178 43 : if( (nFormat == 4) && ((nOffset+16) < nLength) )
179 : {
180 35 : int nSegCountX2 = Getsal_uInt16( pCmap + nOffset + 6 );
181 35 : nRangeCount = nSegCountX2/2 - 1;
182 35 : pCodePairs = new sal_UCS4[ nRangeCount * 2 ];
183 35 : pStartGlyphs = new int[ nRangeCount ];
184 35 : const unsigned char* pLimitBase = pCmap + nOffset + 14;
185 35 : const unsigned char* pBeginBase = pLimitBase + nSegCountX2 + 2;
186 35 : const unsigned char* pDeltaBase = pBeginBase + nSegCountX2;
187 35 : const unsigned char* pOffsetBase = pDeltaBase + nSegCountX2;
188 35 : sal_UCS4* pCP = pCodePairs;
189 4251 : for( int i = 0; i < nRangeCount; ++i )
190 : {
191 4216 : const sal_UCS4 cMinChar = Getsal_uInt16( pBeginBase + 2*i );
192 4216 : const sal_UCS4 cMaxChar = Getsal_uInt16( pLimitBase + 2*i );
193 4216 : const int nGlyphDelta = GetSShort( pDeltaBase + 2*i );
194 4216 : const int nRangeOffset = Getsal_uInt16( pOffsetBase + 2*i );
195 4216 : if( cMinChar > cMaxChar ) { // no sane font should trigger this
196 : SAL_WARN("vcl.gdi", "Min char should never be more than the max char!");
197 0 : break;
198 : }
199 4216 : if( cMaxChar == 0xFFFF ) {
200 : SAL_WARN("vcl.gdi", "Format 4 char should not be 0xFFFF");
201 0 : break;
202 : }
203 4216 : if( !nRangeOffset ) {
204 : // glyphid can be calculated directly
205 3410 : pStartGlyphs[i] = (cMinChar + nGlyphDelta) & 0xFFFF;
206 : } else {
207 : // update the glyphid-array with the glyphs in this range
208 806 : pStartGlyphs[i] = -(int)aGlyphIdArray.size();
209 806 : const unsigned char* pGlyphIdPtr = pOffsetBase + 2*i + nRangeOffset;
210 806 : const size_t nRemainingSize = pEndValidArea - pGlyphIdPtr;
211 806 : const size_t nMaxPossibleRecords = nRemainingSize/2;
212 806 : if (nMaxPossibleRecords == 0) { // no sane font should trigger this
213 : SAL_WARN("vcl.gdi", "More indexes claimed that space available in font!");
214 0 : break;
215 : }
216 806 : const size_t nMaxLegalChar = cMinChar + nMaxPossibleRecords-1;
217 806 : if (cMaxChar > nMaxLegalChar) { // no sane font should trigger this
218 : SAL_WARN("vcl.gdi", "More indexes claimed that space available in font!");
219 0 : break;
220 : }
221 23490 : for( sal_UCS4 c = cMinChar; c <= cMaxChar; ++c, pGlyphIdPtr+=2 ) {
222 22684 : const int nGlyphIndex = Getsal_uInt16( pGlyphIdPtr ) + nGlyphDelta;
223 22684 : aGlyphIdArray.push_back( static_cast<sal_uInt16>(nGlyphIndex) );
224 : }
225 : }
226 4216 : *(pCP++) = cMinChar;
227 4216 : *(pCP++) = cMaxChar + 1;
228 : }
229 35 : nRangeCount = (pCP - pCodePairs) / 2;
230 : }
231 : // format 12, the most common 32bit char mapping table
232 8 : else if( (nFormat == 12) && ((nOffset+16) < nLength) )
233 : {
234 8 : nRangeCount = GetUInt( pCmap + nOffset + 12 );
235 8 : pCodePairs = new sal_UCS4[ nRangeCount * 2 ];
236 8 : pStartGlyphs = new int[ nRangeCount ];
237 8 : const unsigned char* pGroup = pCmap + nOffset + 16;
238 8 : sal_UCS4* pCP = pCodePairs;
239 2088 : for( int i = 0; i < nRangeCount; ++i )
240 : {
241 2080 : sal_UCS4 cMinChar = GetUInt( pGroup + 0 );
242 2080 : sal_UCS4 cMaxChar = GetUInt( pGroup + 4 );
243 2080 : int nGlyphId = GetUInt( pGroup + 8 );
244 2080 : pGroup += 12;
245 :
246 2080 : if( cMinChar > cMaxChar ) { // no sane font should trigger this
247 : SAL_WARN("vcl.gdi", "Min char should never be more than the max char!");
248 0 : break;
249 : }
250 :
251 2080 : *(pCP++) = cMinChar;
252 2080 : *(pCP++) = cMaxChar + 1;
253 2080 : pStartGlyphs[i] = nGlyphId;
254 : }
255 8 : nRangeCount = (pCP - pCodePairs) / 2;
256 : }
257 :
258 : // check if any subtable resulted in something usable
259 43 : if( nRangeCount <= 0 )
260 : {
261 0 : delete[] pCodePairs;
262 0 : delete[] pStartGlyphs;
263 :
264 : // even when no CMAP is available we know it for symbol fonts
265 0 : if( rResult.mbSymbolic )
266 : {
267 0 : pCodePairs = new sal_UCS4[4];
268 0 : pCodePairs[0] = 0x0020; // aliased symbols
269 0 : pCodePairs[1] = 0x0100;
270 0 : pCodePairs[2] = 0xF020; // original symbols
271 0 : pCodePairs[3] = 0xF100;
272 0 : rResult.mpRangeCodes = pCodePairs;
273 0 : rResult.mnRangeCount = 2;
274 0 : return true;
275 : }
276 :
277 0 : return false;
278 : }
279 :
280 : // recode the code ranges to their unicode encoded ranges if needed
281 43 : rtl_TextToUnicodeConverter aConverter = NULL;
282 43 : rtl_UnicodeToTextContext aCvtContext = NULL;
283 :
284 43 : rResult.mbRecoded = ( eRecodeFrom != RTL_TEXTENCODING_UNICODE );
285 43 : if( rResult.mbRecoded )
286 : {
287 0 : aConverter = rtl_createTextToUnicodeConverter( eRecodeFrom );
288 0 : aCvtContext = rtl_createTextToUnicodeContext( aConverter );
289 : }
290 :
291 43 : if( aConverter && aCvtContext )
292 : {
293 : // determine the set of supported unicodes from encoded ranges
294 : typedef std::set<sal_UCS4> Ucs4Set;
295 0 : Ucs4Set aSupportedUnicodes;
296 :
297 : static const int NINSIZE = 64;
298 : static const int NOUTSIZE = 64;
299 : sal_Char cCharsInp[ NINSIZE ];
300 : sal_Unicode cCharsOut[ NOUTSIZE ];
301 0 : sal_UCS4* pCP = pCodePairs;
302 0 : for( int i = 0; i < nRangeCount; ++i )
303 : {
304 0 : sal_UCS4 cMin = *(pCP++);
305 0 : sal_UCS4 cEnd = *(pCP++);
306 0 : while( cMin < cEnd )
307 : {
308 0 : int j = 0;
309 0 : for(; (cMin < cEnd) && (j < NINSIZE); ++cMin )
310 : {
311 0 : if( cMin >= 0x0100 )
312 0 : cCharsInp[ j++ ] = static_cast<sal_Char>(cMin >> 8);
313 0 : if( (cMin >= 0x0100) || (cMin < 0x00A0) )
314 0 : cCharsInp[ j++ ] = static_cast<sal_Char>(cMin);
315 : }
316 :
317 : sal_uInt32 nCvtInfo;
318 : sal_Size nSrcCvtBytes;
319 : int nOutLen = rtl_convertTextToUnicode(
320 : aConverter, aCvtContext,
321 : cCharsInp, j, cCharsOut, NOUTSIZE,
322 : RTL_TEXTTOUNICODE_FLAGS_INVALID_IGNORE
323 : | RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_IGNORE,
324 0 : &nCvtInfo, &nSrcCvtBytes );
325 :
326 0 : for( j = 0; j < nOutLen; ++j )
327 0 : aSupportedUnicodes.insert( cCharsOut[j] );
328 : }
329 : }
330 :
331 0 : rtl_destroyTextToUnicodeConverter( aCvtContext );
332 0 : rtl_destroyTextToUnicodeConverter( aConverter );
333 :
334 : // convert the set of supported unicodes to ranges
335 : typedef std::vector<sal_UCS4> Ucs4Vector;
336 0 : Ucs4Vector aSupportedRanges;
337 :
338 0 : Ucs4Set::const_iterator itChar = aSupportedUnicodes.begin();
339 0 : for(; itChar != aSupportedUnicodes.end(); ++itChar )
340 : {
341 0 : if( aSupportedRanges.empty()
342 0 : || (aSupportedRanges.back() != *itChar) )
343 : {
344 : // add new range beginning with current unicode
345 0 : aSupportedRanges.push_back( *itChar );
346 0 : aSupportedRanges.push_back( 0 );
347 : }
348 :
349 : // extend existing range to include current unicode
350 0 : aSupportedRanges.back() = *itChar + 1;
351 : }
352 :
353 : // glyph mapping for non-unicode fonts not implemented
354 0 : delete[] pStartGlyphs;
355 0 : pStartGlyphs = NULL;
356 0 : aGlyphIdArray.clear();
357 :
358 : // make a pCodePairs array using the vector from above
359 0 : delete[] pCodePairs;
360 0 : nRangeCount = aSupportedRanges.size() / 2;
361 0 : if( nRangeCount <= 0 )
362 0 : return false;
363 0 : pCodePairs = new sal_UCS4[ nRangeCount * 2 ];
364 0 : Ucs4Vector::const_iterator itInt = aSupportedRanges.begin();
365 0 : for( pCP = pCodePairs; itInt != aSupportedRanges.end(); ++itInt )
366 0 : *(pCP++) = *itInt;
367 : }
368 :
369 : // prepare the glyphid-array if needed
370 : // TODO: merge ranges if they are close enough?
371 43 : sal_uInt16* pGlyphIds = NULL;
372 43 : if( !aGlyphIdArray.empty())
373 : {
374 43 : pGlyphIds = new sal_uInt16[ aGlyphIdArray.size() ];
375 43 : sal_uInt16* pOut = pGlyphIds;
376 43 : U16Vector::const_iterator it = aGlyphIdArray.begin();
377 22813 : while( it != aGlyphIdArray.end() )
378 22727 : *(pOut++) = *(it++);
379 : }
380 :
381 : // update the result struct
382 43 : rResult.mpRangeCodes = pCodePairs;
383 43 : rResult.mpStartGlyphs = pStartGlyphs;
384 43 : rResult.mnRangeCount = nRangeCount;
385 43 : rResult.mpGlyphIds = pGlyphIds;
386 43 : return true;
387 : }
388 :
389 8479 : FontCharMap::FontCharMap()
390 : : mpImplFontCharMap( ImplFontCharMap::getDefaultMap() )
391 8479 : , mnRefCount(0)
392 8479 : {}
393 :
394 0 : FontCharMap::FontCharMap( ImplFontCharMapPtr pIFCMap )
395 : : mpImplFontCharMap( pIFCMap )
396 0 : , mnRefCount(0)
397 0 : {}
398 :
399 43 : FontCharMap::FontCharMap( const CmapResult& rCR )
400 43 : : mnRefCount(0)
401 : {
402 43 : ImplFontCharMapPtr pImplFontCharMap( new ImplFontCharMap(rCR) );
403 43 : mpImplFontCharMap = pImplFontCharMap;
404 43 : }
405 :
406 17044 : FontCharMap::~FontCharMap()
407 : {
408 8522 : mpImplFontCharMap = 0;
409 8522 : }
410 :
411 0 : FontCharMapPtr FontCharMap::GetDefaultMap( bool bSymbol )
412 : {
413 0 : FontCharMapPtr pFontCharMap( new FontCharMap( ImplFontCharMap::getDefaultMap( bSymbol ) ) );
414 0 : return pFontCharMap;
415 : }
416 :
417 10012 : bool FontCharMap::IsDefaultMap() const
418 : {
419 10012 : return mpImplFontCharMap->isDefaultMap();
420 : }
421 :
422 0 : int FontCharMap::GetCharCount() const
423 : {
424 0 : return mpImplFontCharMap->mnCharCount;
425 : }
426 :
427 0 : int FontCharMap::CountCharsInRange( sal_UCS4 cMin, sal_UCS4 cMax ) const
428 : {
429 0 : int nCount = 0;
430 :
431 : // find and adjust range and char count for cMin
432 0 : int nRangeMin = findRangeIndex( cMin );
433 0 : if( nRangeMin & 1 )
434 0 : ++nRangeMin;
435 0 : else if( cMin > mpImplFontCharMap->mpRangeCodes[ nRangeMin ] )
436 0 : nCount -= cMin - mpImplFontCharMap->mpRangeCodes[ nRangeMin ];
437 :
438 : // find and adjust range and char count for cMax
439 0 : int nRangeMax = findRangeIndex( cMax );
440 0 : if( nRangeMax & 1 )
441 0 : --nRangeMax;
442 : else
443 0 : nCount -= mpImplFontCharMap->mpRangeCodes[ nRangeMax+1 ] - cMax - 1;
444 :
445 : // count chars in complete ranges between cMin and cMax
446 0 : for( int i = nRangeMin; i <= nRangeMax; i+=2 )
447 0 : nCount += mpImplFontCharMap->mpRangeCodes[i+1] - mpImplFontCharMap->mpRangeCodes[i];
448 :
449 0 : return nCount;
450 : }
451 :
452 28068 : bool FontCharMap::HasChar( sal_UCS4 cChar ) const
453 : {
454 28068 : bool bHasChar = false;
455 :
456 28068 : if( mpImplFontCharMap->mpStartGlyphs == NULL ) { // only the char-ranges are known
457 0 : const int nRange = findRangeIndex( cChar );
458 0 : if( nRange==0 && cChar < mpImplFontCharMap->mpRangeCodes[0] )
459 0 : return false;
460 0 : bHasChar = ((nRange & 1) == 0); // inside a range
461 : } else { // glyph mapping is available
462 28068 : const int nGlyphIndex = GetGlyphIndex( cChar );
463 28068 : bHasChar = (nGlyphIndex != 0); // not the notdef-glyph
464 : }
465 :
466 28068 : return bHasChar;
467 : }
468 :
469 351 : sal_UCS4 FontCharMap::GetFirstChar() const
470 : {
471 351 : return mpImplFontCharMap->mpRangeCodes[0];
472 : }
473 :
474 2 : sal_UCS4 FontCharMap::GetLastChar() const
475 : {
476 2 : return (mpImplFontCharMap->mpRangeCodes[ 2*mpImplFontCharMap->mnRangeCount-1 ] - 1);
477 : }
478 :
479 1 : sal_UCS4 FontCharMap::GetNextChar( sal_UCS4 cChar ) const
480 : {
481 1 : if( cChar < GetFirstChar() )
482 0 : return GetFirstChar();
483 1 : if( cChar >= GetLastChar() )
484 0 : return GetLastChar();
485 :
486 1 : int nRange = findRangeIndex( cChar + 1 );
487 1 : if( nRange & 1 ) // outside of range?
488 1 : return mpImplFontCharMap->mpRangeCodes[ nRange + 1 ]; // => first in next range
489 0 : return (cChar + 1);
490 : }
491 :
492 0 : sal_UCS4 FontCharMap::GetPrevChar( sal_UCS4 cChar ) const
493 : {
494 0 : if( cChar <= GetFirstChar() )
495 0 : return GetFirstChar();
496 0 : if( cChar > GetLastChar() )
497 0 : return GetLastChar();
498 :
499 0 : int nRange = findRangeIndex( cChar - 1 );
500 0 : if( nRange & 1 ) // outside a range?
501 0 : return (mpImplFontCharMap->mpRangeCodes[ nRange ] - 1); // => last in prev range
502 0 : return (cChar - 1);
503 : }
504 :
505 0 : int FontCharMap::GetIndexFromChar( sal_UCS4 cChar ) const
506 : {
507 : // TODO: improve linear walk?
508 0 : int nCharIndex = 0;
509 0 : const sal_UCS4* pRange = &mpImplFontCharMap->mpRangeCodes[0];
510 0 : for( int i = 0; i < mpImplFontCharMap->mnRangeCount; ++i )
511 : {
512 0 : sal_UCS4 cFirst = *(pRange++);
513 0 : sal_UCS4 cLast = *(pRange++);
514 0 : if( cChar >= cLast )
515 0 : nCharIndex += cLast - cFirst;
516 0 : else if( cChar >= cFirst )
517 0 : return nCharIndex + (cChar - cFirst);
518 : else
519 0 : break;
520 : }
521 :
522 0 : return -1;
523 : }
524 :
525 0 : sal_UCS4 FontCharMap::GetCharFromIndex( int nIndex ) const
526 : {
527 : // TODO: improve linear walk?
528 0 : const sal_UCS4* pRange = &mpImplFontCharMap->mpRangeCodes[0];
529 0 : for( int i = 0; i < mpImplFontCharMap->mnRangeCount; ++i )
530 : {
531 0 : sal_UCS4 cFirst = *(pRange++);
532 0 : sal_UCS4 cLast = *(pRange++);
533 0 : nIndex -= cLast - cFirst;
534 0 : if( nIndex < 0 )
535 0 : return (cLast + nIndex);
536 : }
537 :
538 : // we can only get here with an out-of-bounds charindex
539 0 : return mpImplFontCharMap->mpRangeCodes[0];
540 : }
541 :
542 28069 : int FontCharMap::findRangeIndex( sal_UCS4 cChar ) const
543 : {
544 28069 : int nLower = 0;
545 28069 : int nMid = mpImplFontCharMap->mnRangeCount;
546 28069 : int nUpper = 2 * mpImplFontCharMap->mnRangeCount - 1;
547 280166 : while( nLower < nUpper )
548 : {
549 224028 : if( cChar >= mpImplFontCharMap->mpRangeCodes[ nMid ] )
550 140674 : nLower = nMid;
551 : else
552 83354 : nUpper = nMid - 1;
553 224028 : nMid = (nLower + nUpper + 1) / 2;
554 : }
555 :
556 28069 : return nMid;
557 : }
558 :
559 28068 : int FontCharMap::GetGlyphIndex( sal_UCS4 cChar ) const
560 : {
561 : // return -1 if the object doesn't know the glyph ids
562 28068 : if( !mpImplFontCharMap->mpStartGlyphs )
563 0 : return -1;
564 :
565 : // return 0 if the unicode doesn't have a matching glyph
566 28068 : int nRange = findRangeIndex( cChar );
567 : // check that we are inside any range
568 28068 : if( (nRange == 0) && (cChar < mpImplFontCharMap->mpRangeCodes[0]) ) {
569 : // symbol aliasing gives symbol fonts a second chance
570 0 : const bool bSymbolic = cChar <= 0xFF && (mpImplFontCharMap->mpRangeCodes[0]>=0xF000) &&
571 0 : (mpImplFontCharMap->mpRangeCodes[1]<=0xF0FF);
572 0 : if( !bSymbolic )
573 0 : return 0;
574 : // check for symbol aliasing (U+F0xx -> U+00xx)
575 0 : cChar |= 0xF000;
576 0 : nRange = findRangeIndex( cChar );
577 0 : if( (nRange == 0) && (cChar < mpImplFontCharMap->mpRangeCodes[0]) ) {
578 0 : return 0;
579 : }
580 : }
581 : // check that we are inside a range
582 28068 : if( (nRange & 1) != 0 )
583 25664 : return 0;
584 :
585 : // get glyph index directly or indirectly
586 2404 : int nGlyphIndex = cChar - mpImplFontCharMap->mpRangeCodes[ nRange ];
587 2404 : const int nStartIndex = mpImplFontCharMap->mpStartGlyphs[ nRange/2 ];
588 2404 : if( nStartIndex >= 0 ) {
589 : // the glyph index can be calculated
590 2404 : nGlyphIndex += nStartIndex;
591 : } else {
592 : // the glyphid array has the glyph index
593 0 : nGlyphIndex = mpImplFontCharMap->mpGlyphIds[ nGlyphIndex - nStartIndex];
594 : }
595 :
596 2404 : return nGlyphIndex;
597 : }
598 :
599 : // on some systems we have to get the font attributes from the name table
600 : // since neither head's macStyle nor OS/2's panose are easily available
601 : // during font enumeration. macStyle bits would be not sufficient anyway
602 : // and SFNT fonts on Mac usually do not contain an OS/2 table.
603 0 : void UpdateAttributesFromPSName( const OUString& rPSName, ImplDevFontAttributes& rDFA )
604 : {
605 0 : OString aPSName( OUStringToOString( rPSName, RTL_TEXTENCODING_UTF8 ).toAsciiLowerCase() );
606 :
607 : // TODO: use a multi-string ignore-case matcher once it becomes available
608 0 : if( (aPSName.indexOf("regular") != -1)
609 0 : || (aPSName.indexOf("normal") != -1)
610 0 : || (aPSName.indexOf("roman") != -1)
611 0 : || (aPSName.indexOf("medium") != -1)
612 0 : || (aPSName.indexOf("plain") != -1)
613 0 : || (aPSName.indexOf("standard") != -1)
614 0 : || (aPSName.indexOf("std") != -1) )
615 : {
616 0 : rDFA.SetWidthType(WIDTH_NORMAL);
617 0 : rDFA.SetWeight(WEIGHT_NORMAL);
618 0 : rDFA.SetItalic(ITALIC_NONE);
619 : }
620 :
621 : // heuristics for font weight
622 0 : if (aPSName.indexOf("extrablack") != -1)
623 0 : rDFA.SetWeight(WEIGHT_BLACK);
624 0 : else if (aPSName.indexOf("black") != -1)
625 0 : rDFA.SetWeight(WEIGHT_BLACK);
626 0 : else if( (aPSName.indexOf("semibold") != -1)
627 0 : || (aPSName.indexOf("smbd") != -1))
628 0 : rDFA.SetWeight(WEIGHT_SEMIBOLD);
629 0 : else if (aPSName.indexOf("ultrabold") != -1)
630 0 : rDFA.SetWeight(WEIGHT_ULTRABOLD);
631 0 : else if (aPSName.indexOf("extrabold") != -1)
632 0 : rDFA.SetWeight(WEIGHT_BLACK);
633 0 : else if( (aPSName.indexOf("bold") != -1)
634 0 : || (aPSName.indexOf("-bd") != -1))
635 0 : rDFA.SetWeight(WEIGHT_BOLD);
636 0 : else if (aPSName.indexOf("extralight") != -1)
637 0 : rDFA.SetWeight(WEIGHT_ULTRALIGHT);
638 0 : else if (aPSName.indexOf("ultralight") != -1)
639 0 : rDFA.SetWeight(WEIGHT_ULTRALIGHT);
640 0 : else if (aPSName.indexOf("light") != -1)
641 0 : rDFA.SetWeight(WEIGHT_LIGHT);
642 0 : else if (aPSName.indexOf("thin") != -1)
643 0 : rDFA.SetWeight(WEIGHT_THIN);
644 0 : else if (aPSName.indexOf("-w3") != -1)
645 0 : rDFA.SetWeight(WEIGHT_LIGHT);
646 0 : else if (aPSName.indexOf("-w4") != -1)
647 0 : rDFA.SetWeight(WEIGHT_SEMILIGHT);
648 0 : else if (aPSName.indexOf("-w5") != -1)
649 0 : rDFA.SetWeight(WEIGHT_NORMAL);
650 0 : else if (aPSName.indexOf("-w6") != -1)
651 0 : rDFA.SetWeight(WEIGHT_SEMIBOLD);
652 0 : else if (aPSName.indexOf("-w7") != -1)
653 0 : rDFA.SetWeight(WEIGHT_BOLD);
654 0 : else if (aPSName.indexOf("-w8") != -1)
655 0 : rDFA.SetWeight(WEIGHT_ULTRABOLD);
656 0 : else if (aPSName.indexOf("-w9") != -1)
657 0 : rDFA.SetWeight(WEIGHT_BLACK);
658 :
659 : // heuristics for font slant
660 0 : if( (aPSName.indexOf("italic") != -1)
661 0 : || (aPSName.indexOf(" ital") != -1)
662 0 : || (aPSName.indexOf("cursive") != -1)
663 0 : || (aPSName.indexOf("-it") != -1)
664 0 : || (aPSName.indexOf("lightit") != -1)
665 0 : || (aPSName.indexOf("mediumit") != -1)
666 0 : || (aPSName.indexOf("boldit") != -1)
667 0 : || (aPSName.indexOf("cnit") != -1)
668 0 : || (aPSName.indexOf("bdcn") != -1)
669 0 : || (aPSName.indexOf("bdit") != -1)
670 0 : || (aPSName.indexOf("condit") != -1)
671 0 : || (aPSName.indexOf("bookit") != -1)
672 0 : || (aPSName.indexOf("blackit") != -1) )
673 0 : rDFA.SetItalic(ITALIC_NORMAL);
674 0 : if( (aPSName.indexOf("oblique") != -1)
675 0 : || (aPSName.indexOf("inclined") != -1)
676 0 : || (aPSName.indexOf("slanted") != -1) )
677 0 : rDFA.SetItalic(ITALIC_OBLIQUE);
678 :
679 : // heuristics for font width
680 0 : if( (aPSName.indexOf("condensed") != -1)
681 0 : || (aPSName.indexOf("-cond") != -1)
682 0 : || (aPSName.indexOf("boldcond") != -1)
683 0 : || (aPSName.indexOf("boldcn") != -1)
684 0 : || (aPSName.indexOf("cnit") != -1) )
685 0 : rDFA.SetWidthType(WIDTH_CONDENSED);
686 0 : else if (aPSName.indexOf("narrow") != -1)
687 0 : rDFA.SetWidthType(WIDTH_SEMI_CONDENSED);
688 0 : else if (aPSName.indexOf("expanded") != -1)
689 0 : rDFA.SetWidthType(WIDTH_EXPANDED);
690 0 : else if (aPSName.indexOf("wide") != -1)
691 0 : rDFA.SetWidthType(WIDTH_EXPANDED);
692 :
693 : // heuristics for font pitch
694 0 : if( (aPSName.indexOf("mono") != -1)
695 0 : || (aPSName.indexOf("courier") != -1)
696 0 : || (aPSName.indexOf("monaco") != -1)
697 0 : || (aPSName.indexOf("typewriter") != -1) )
698 0 : rDFA.SetPitch(PITCH_FIXED);
699 :
700 : // heuristics for font family type
701 0 : if( (aPSName.indexOf("script") != -1)
702 0 : || (aPSName.indexOf("chancery") != -1)
703 0 : || (aPSName.indexOf("zapfino") != -1))
704 0 : rDFA.SetFamilyType(FAMILY_SCRIPT);
705 0 : else if( (aPSName.indexOf("comic") != -1)
706 0 : || (aPSName.indexOf("outline") != -1)
707 0 : || (aPSName.indexOf("pinpoint") != -1) )
708 0 : rDFA.SetFamilyType(FAMILY_DECORATIVE);
709 0 : else if( (aPSName.indexOf("sans") != -1)
710 0 : || (aPSName.indexOf("arial") != -1) )
711 0 : rDFA.SetFamilyType(FAMILY_SWISS);
712 0 : else if( (aPSName.indexOf("roman") != -1)
713 0 : || (aPSName.indexOf("times") != -1) )
714 0 : rDFA.SetFamilyType(FAMILY_ROMAN);
715 :
716 : // heuristics for codepoint semantic
717 0 : if( (aPSName.indexOf("symbol") != -1)
718 0 : || (aPSName.indexOf("dings") != -1)
719 0 : || (aPSName.indexOf("dingbats") != -1)
720 0 : || (aPSName.indexOf("ornaments") != -1)
721 0 : || (aPSName.indexOf("embellishments") != -1) )
722 0 : rDFA.SetSymbolFlag(true);
723 :
724 : // #i100020# special heuristic for names with single-char styles
725 : // NOTE: we are checking name that hasn't been lower-cased
726 0 : if( rPSName.getLength() > 3 )
727 : {
728 0 : int i = rPSName.getLength();
729 0 : sal_Unicode c = rPSName[--i];
730 0 : if( c == 'C' ) { // "capitals"
731 0 : rDFA.SetFamilyType(FAMILY_DECORATIVE);
732 0 : c = rPSName[--i];
733 : }
734 0 : if( c == 'O' ) { // CFF-based OpenType
735 0 : c = rPSName[--i];
736 : }
737 0 : if( c == 'I' ) { // "italic"
738 0 : rDFA.SetItalic(ITALIC_NORMAL);
739 0 : c = rPSName[--i];
740 : }
741 0 : if( c == 'B' ) // "bold"
742 0 : rDFA.SetWeight(WEIGHT_BOLD);
743 0 : if( c == 'C' ) // "capitals"
744 0 : rDFA.SetFamilyType(FAMILY_DECORATIVE);
745 : // TODO: check that all single-char styles have been resolved?
746 0 : }
747 801 : }
748 :
749 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|