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