xref: /trunk/main/vcl/source/gdi/impfont.cxx (revision fb3d7646)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 // MARKER(update_precomp.py): autogen include statement, do not remove
23 #include "precompiled_vcl.hxx"
24 
25 #include <vcl/metric.hxx>
26 #include <outfont.hxx>
27 #include <impfont.hxx>
28 
29 #include <vector>
30 #include <set>
31 
32 // =======================================================================
33 
CmapResult(bool bSymbolic,const sal_UCS4 * pRangeCodes,int nRangeCount,const int * pStartGlyphs,const sal_uInt16 * pExtraGlyphIds)34 CmapResult::CmapResult( bool bSymbolic,
35 	const sal_UCS4* pRangeCodes, int nRangeCount,
36 	const int* pStartGlyphs, const sal_uInt16* pExtraGlyphIds )
37 :	mpRangeCodes( pRangeCodes)
38 ,	mpStartGlyphs( pStartGlyphs)
39 ,	mpGlyphIds( pExtraGlyphIds)
40 ,	mnRangeCount( nRangeCount)
41 ,	mbSymbolic( bSymbolic)
42 ,	mbRecoded( false)
43 {}
44 
45 // =======================================================================
46 
ImplFontCharMap(const CmapResult & rCR)47 ImplFontCharMap::ImplFontCharMap( const CmapResult& rCR )
48 :	mpRangeCodes( rCR.mpRangeCodes )
49 ,	mpStartGlyphs( rCR.mpStartGlyphs )
50 ,	mpGlyphIds( rCR.mpGlyphIds )
51 ,	mnRangeCount( rCR.mnRangeCount )
52 ,	mnCharCount( 0 )
53 ,	mnRefCount( 1 )
54 {
55 	const sal_UCS4* pRangePtr = mpRangeCodes;
56 	for( int i = mnRangeCount; --i >= 0; pRangePtr += 2 )
57 	{
58 		sal_UCS4 cFirst = pRangePtr[0];
59 		sal_UCS4 cLast  = pRangePtr[1];
60 		mnCharCount += cLast - cFirst;
61 	}
62 }
63 
64 static ImplFontCharMap* pDefaultImplFontCharMap = NULL;
65 static const sal_UCS4 aDefaultUnicodeRanges[] = {0x0020,0xD800, 0xE000,0xFFF0};
66 static const sal_UCS4 aDefaultSymbolRanges[] = {0x0020,0x0100, 0xF020,0xF100};
67 
68 // -----------------------------------------------------------------------
69 
IsDefaultMap() const70 bool ImplFontCharMap::IsDefaultMap() const
71 {
72 	const bool bIsDefault = (mpRangeCodes == aDefaultUnicodeRanges) || (mpRangeCodes == aDefaultSymbolRanges);
73 	return bIsDefault;
74 }
75 
76 // -----------------------------------------------------------------------
77 
~ImplFontCharMap()78 ImplFontCharMap::~ImplFontCharMap()
79 {
80 	if( IsDefaultMap() )
81 		return;
82 	delete[] mpRangeCodes;
83 	delete[] mpStartGlyphs;
84 	delete[] mpGlyphIds;
85 }
86 
87 // -----------------------------------------------------------------------
88 
GetDefaultMap(bool bSymbols)89 ImplFontCharMap* ImplFontCharMap::GetDefaultMap( bool bSymbols)
90 {
91 	if( pDefaultImplFontCharMap )
92 		pDefaultImplFontCharMap->AddReference();
93 	else
94 	{
95 		const sal_UCS4* pRangeCodes = aDefaultUnicodeRanges;
96 		int nCodesCount = sizeof(aDefaultUnicodeRanges) / sizeof(*pRangeCodes);
97 		if( bSymbols )
98 		{
99 			pRangeCodes	= aDefaultSymbolRanges;
100 			nCodesCount	= sizeof(aDefaultSymbolRanges) / sizeof(*pRangeCodes);
101 		}
102 
103 		CmapResult aDefaultCR( bSymbols, pRangeCodes, nCodesCount/2 );
104 		pDefaultImplFontCharMap = new ImplFontCharMap( aDefaultCR );
105 	}
106 
107 	return pDefaultImplFontCharMap;
108 }
109 
110 // -----------------------------------------------------------------------
111 
AddReference(void) const112 void ImplFontCharMap::AddReference( void) const
113 {
114 	++mnRefCount;
115 }
116 
117 // -----------------------------------------------------------------------
118 
DeReference(void) const119 void ImplFontCharMap::DeReference( void) const
120 {
121 	if( --mnRefCount <= 0 )
122 		if( this != pDefaultImplFontCharMap )
123 			delete this;
124 }
125 
126 // -----------------------------------------------------------------------
127 
GetCharCount() const128 int ImplFontCharMap::GetCharCount() const
129 {
130 	return mnCharCount;
131 }
132 
133 // -----------------------------------------------------------------------
134 
ImplFindRangeIndex(sal_UCS4 cChar) const135 int ImplFontCharMap::ImplFindRangeIndex( sal_UCS4 cChar ) const
136 {
137 	int nLower = 0;
138 	int nMid   = mnRangeCount;
139 	int nUpper = 2 * mnRangeCount - 1;
140 	while( nLower < nUpper )
141 	{
142 		if( cChar >= mpRangeCodes[ nMid ] )
143 			nLower = nMid;
144 		else
145 			nUpper = nMid - 1;
146 		nMid = (nLower + nUpper + 1) / 2;
147 	}
148 
149 	return nMid;
150 }
151 
152 // -----------------------------------------------------------------------
153 
HasChar(sal_UCS4 cChar) const154 bool ImplFontCharMap::HasChar( sal_UCS4 cChar ) const
155 {
156 	bool bHasChar = false;
157 
158 	if( mpStartGlyphs  == NULL ) { // only the char-ranges are known
159 		const int nRange = ImplFindRangeIndex( cChar );
160 		if( nRange==0 && cChar<mpRangeCodes[0] )
161 			return false;
162 		bHasChar = ((nRange & 1) == 0); // inside a range
163 	} else { // glyph mapping is available
164 		const int nGlyphIndex = GetGlyphIndex( cChar );
165 		bHasChar = (nGlyphIndex != 0); // not the notdef-glyph
166 	}
167 
168 	return bHasChar;
169 }
170 
171 // -----------------------------------------------------------------------
172 
GetGlyphIndex(sal_UCS4 cChar) const173 int ImplFontCharMap::GetGlyphIndex( sal_UCS4 cChar ) const
174 {
175 	// return -1 if the object doesn't know the glyph ids
176 	if( !mpStartGlyphs )
177 		return -1;
178 
179 	// return 0 if the unicode doesn't have a matching glyph
180 	int nRange = ImplFindRangeIndex( cChar );
181 	// check that we are inside any range
182 	if( (nRange == 0) && (cChar < mpRangeCodes[0]) ) {
183 		// symbol aliasing gives symbol fonts a second chance
184 		const bool bSymbolic = (mpRangeCodes[0]>=0xF000) & (mpRangeCodes[1]<=0xF0FF);
185 		if( !bSymbolic )
186 			return 0;
187 		// check for symbol aliasing (U+F0xx -> U+00xx)
188 		nRange = ImplFindRangeIndex( cChar | 0xF000 );
189 	}
190 	// check that we are inside a range
191 	if( (nRange & 1) != 0 )
192 		return 0;
193 
194 	// get glyph index directly or indirectly
195 	int nGlyphIndex = cChar - mpRangeCodes[ nRange ];
196 	const int nStartIndex = mpStartGlyphs[ nRange/2 ];
197 	if( nStartIndex >= 0 ) {
198 		// the glyph index can be calculated
199 		nGlyphIndex += nStartIndex;
200 	} else {
201 		// the glyphid array has the glyph index
202 		nGlyphIndex = mpGlyphIds[ nGlyphIndex - nStartIndex];
203 	}
204 
205 	return nGlyphIndex;
206 }
207 
208 // -----------------------------------------------------------------------
209 
210 // returns the number of chars supported by the font, which
211 // are inside the unicode range from cMin to cMax (inclusive)
CountCharsInRange(sal_UCS4 cMin,sal_UCS4 cMax) const212 int ImplFontCharMap::CountCharsInRange( sal_UCS4 cMin, sal_UCS4 cMax ) const
213 {
214 	int nCount = 0;
215 
216 	// find and adjust range and char count for cMin
217 	int nRangeMin = ImplFindRangeIndex( cMin );
218 	if( nRangeMin & 1 )
219 		++nRangeMin;
220 	else if( cMin > mpRangeCodes[ nRangeMin ] )
221 		nCount -= cMin - mpRangeCodes[ nRangeMin ];
222 
223 	// find and adjust range and char count for cMax
224 	int nRangeMax = ImplFindRangeIndex( cMax );
225 	if( nRangeMax & 1 )
226 		--nRangeMax;
227 	else
228 		nCount -= mpRangeCodes[ nRangeMax+1 ] - cMax - 1;
229 
230 	// count chars in complete ranges between cMin and cMax
231 	for( int i = nRangeMin; i <= nRangeMax; i+=2 )
232 		nCount += mpRangeCodes[i+1] - mpRangeCodes[i];
233 
234 	return nCount;
235 }
236 
237 // -----------------------------------------------------------------------
238 
GetFirstChar() const239 sal_UCS4 ImplFontCharMap::GetFirstChar() const
240 {
241 	return mpRangeCodes[0];
242 }
243 
244 // -----------------------------------------------------------------------
245 
GetLastChar() const246 sal_UCS4 ImplFontCharMap::GetLastChar() const
247 {
248 	return (mpRangeCodes[ 2*mnRangeCount-1 ] - 1);
249 }
250 
251 // -----------------------------------------------------------------------
252 
GetNextChar(sal_UCS4 cChar) const253 sal_UCS4 ImplFontCharMap::GetNextChar( sal_UCS4 cChar ) const
254 {
255 	if( cChar < GetFirstChar() )
256 		return GetFirstChar();
257 	if( cChar >= GetLastChar() )
258 		return GetLastChar();
259 
260 	int nRange = ImplFindRangeIndex( cChar + 1 );
261 	if( nRange & 1 )                       // outside of range?
262 		return mpRangeCodes[ nRange + 1 ]; // => first in next range
263 	return (cChar + 1);
264 }
265 
266 // -----------------------------------------------------------------------
267 
GetPrevChar(sal_UCS4 cChar) const268 sal_UCS4 ImplFontCharMap::GetPrevChar( sal_UCS4 cChar ) const
269 {
270 	if( cChar <= GetFirstChar() )
271 		return GetFirstChar();
272 	if( cChar > GetLastChar() )
273 		return GetLastChar();
274 
275 	int nRange = ImplFindRangeIndex( cChar - 1 );
276 	if( nRange & 1 )                            // outside a range?
277 		return (mpRangeCodes[ nRange ] - 1);    // => last in prev range
278 	return (cChar - 1);
279 }
280 
281 // -----------------------------------------------------------------------
282 
GetIndexFromChar(sal_UCS4 cChar) const283 int ImplFontCharMap::GetIndexFromChar( sal_UCS4 cChar ) const
284 {
285 	// TODO: improve linear walk?
286 	int nCharIndex = 0;
287 	const sal_UCS4* pRange = &mpRangeCodes[0];
288 	for( int i = 0; i < mnRangeCount; ++i )
289 	{
290 		sal_UCS4 cFirst = *(pRange++);
291 		sal_UCS4 cLast  = *(pRange++);
292 		if( cChar >= cLast )
293 			nCharIndex += cLast - cFirst;
294 		else if( cChar >= cFirst )
295 			return nCharIndex + (cChar - cFirst);
296 		else
297 			break;
298 	}
299 
300 	return -1;
301 }
302 
303 // -----------------------------------------------------------------------
304 
GetCharFromIndex(int nCharIndex) const305 sal_UCS4 ImplFontCharMap::GetCharFromIndex( int nCharIndex ) const
306 {
307 	// TODO: improve linear walk?
308 	const sal_UCS4* pRange = &mpRangeCodes[0];
309 	for( int i = 0; i < mnRangeCount; ++i )
310 	{
311 		sal_UCS4 cFirst = *(pRange++);
312 		sal_UCS4 cLast  = *(pRange++);
313 		nCharIndex -= cLast - cFirst;
314 		if( nCharIndex < 0 )
315 			return (cLast + nCharIndex);
316 	}
317 
318 	// we can only get here with an out-of-bounds charindex
319 	return mpRangeCodes[0];
320 }
321 
322 // =======================================================================
323 
GetUInt(const unsigned char * p)324 static unsigned GetUInt( const unsigned char* p ) { return((p[0]<<24)+(p[1]<<16)+(p[2]<<8)+p[3]);}
Getsal_uInt16(const unsigned char * p)325 static unsigned Getsal_uInt16( const unsigned char* p ){ return((p[0]<<8) | p[1]);}
GetSShort(const unsigned char * p)326 static int GetSShort( const unsigned char* p ){ return((static_cast<signed char>(p[0])<<8)|p[1]);}
327 
328 // TODO: move CMAP parsing directly into the ImplFontCharMap class
ParseCMAP(const unsigned char * pCmap,int nLength,CmapResult & rResult)329 bool ParseCMAP( const unsigned char* pCmap, int nLength, CmapResult& rResult )
330 {
331     rResult.mpRangeCodes = NULL;
332     rResult.mpStartGlyphs= NULL;
333     rResult.mpGlyphIds	 = NULL;
334     rResult.mnRangeCount = 0;
335     rResult.mbRecoded    = false;
336     rResult.mbSymbolic   = false;
337 
338     // parse the table header and check for validity
339     if( !pCmap || (nLength < 24) )
340         return false;
341 
342     if( Getsal_uInt16( pCmap ) != 0x0000 ) // simple check for CMAP corruption
343         return false;
344 
345     int nSubTables = Getsal_uInt16( pCmap + 2 );
346     if( (nSubTables <= 0) || (nLength < (24 + 8*nSubTables)) )
347         return false;
348 
349     // find the most interesting subtable in the CMAP
350     rtl_TextEncoding eRecodeFrom = RTL_TEXTENCODING_UNICODE;
351     int nOffset = 0;
352     int nFormat = -1;
353     int nBestVal = 0;
354     for( const unsigned char* p = pCmap + 4; --nSubTables >= 0; p += 8 )
355     {
356         int nPlatform = Getsal_uInt16( p );
357         int nEncoding = Getsal_uInt16( p+2 );
358         int nPlatformEncoding = (nPlatform << 8) + nEncoding;
359 
360         int nValue;
361         rtl_TextEncoding eTmpEncoding = RTL_TEXTENCODING_UNICODE;
362         switch( nPlatformEncoding )
363         {
364             case 0x000: nValue = 20; break;                             // Unicode 1.0
365             case 0x001: nValue = 21; break;                             // Unicode 1.1
366             case 0x002: nValue = 22; break;                             // iso10646_1993
367             case 0x003: nValue = 23; break;                             // UCS-2
368             case 0x004: nValue = 24; break;                             // UCS-4
369             case 0x100: nValue = 22; break;                             // Mac Unicode<2.0
370             case 0x103: nValue = 23; break;                             // Mac Unicode>2.0
371             case 0x300: nValue =  5; rResult.mbSymbolic = true; break;  // Win Symbol
372             case 0x301: nValue = 28; break;                             // Win UCS-2
373             case 0x30A: nValue = 29; break;                             // Win-UCS-4
374             case 0x302: nValue = 11; eTmpEncoding = RTL_TEXTENCODING_SHIFT_JIS; break;
375             case 0x303: nValue = 12; eTmpEncoding = RTL_TEXTENCODING_GB_18030; break;
376             case 0x304: nValue = 11; eTmpEncoding = RTL_TEXTENCODING_BIG5; break;
377             case 0x305: nValue = 11; eTmpEncoding = RTL_TEXTENCODING_MS_949; break;
378             case 0x306: nValue = 11; eTmpEncoding = RTL_TEXTENCODING_MS_1361; break;
379             default:    nValue = 0; break;
380         }
381 
382         if( nValue <= 0 )   // ignore unknown encodings
383             continue;
384 
385         int nTmpOffset = GetUInt( p+4 );
386         int nTmpFormat = Getsal_uInt16( pCmap + nTmpOffset );
387         if( nTmpFormat == 12 )                  // 32bit code -> glyph map format
388             nValue += 3;
389         else if( nTmpFormat != 4 )              // 16bit code -> glyph map format
390             continue;                           // ignore other formats
391 
392         if( nBestVal < nValue )
393         {
394             nBestVal = nValue;
395             nOffset = nTmpOffset;
396             nFormat = nTmpFormat;
397             eRecodeFrom = eTmpEncoding;
398         }
399     }
400 
401     // parse the best CMAP subtable
402     int nRangeCount = 0;
403     sal_UCS4* pCodePairs = NULL;
404     int* pStartGlyphs = NULL;
405 
406     typedef std::vector<sal_uInt16> U16Vector;
407     U16Vector aGlyphIdArray;
408     aGlyphIdArray.reserve( 0x1000 );
409     aGlyphIdArray.push_back( 0 );
410 
411     // format 4, the most common 16bit char mapping table
412     if( (nFormat == 4) && ((nOffset+16) < nLength) )
413     {
414         int nSegCountX2 = Getsal_uInt16( pCmap + nOffset + 6 );
415         nRangeCount = nSegCountX2/2 - 1;
416         pCodePairs = new sal_UCS4[ nRangeCount * 2 ];
417         pStartGlyphs = new int[ nRangeCount ];
418         const unsigned char* pLimitBase = pCmap + nOffset + 14;
419         const unsigned char* pBeginBase = pLimitBase + nSegCountX2 + 2;
420         const unsigned char* pDeltaBase = pBeginBase + nSegCountX2;
421         const unsigned char* pOffsetBase = pDeltaBase + nSegCountX2;
422         sal_UCS4* pCP = pCodePairs;
423         for( int i = 0; i < nRangeCount; ++i )
424         {
425             const sal_UCS4 cMinChar = Getsal_uInt16( pBeginBase + 2*i );
426             const sal_UCS4 cMaxChar = Getsal_uInt16( pLimitBase + 2*i );
427             const int nGlyphDelta  = GetSShort( pDeltaBase + 2*i );
428             const int nRangeOffset = Getsal_uInt16( pOffsetBase + 2*i );
429             if( cMinChar > cMaxChar )   // no sane font should trigger this
430                 break;
431             if( cMaxChar == 0xFFFF )
432                 break;
433             *(pCP++) = cMinChar;
434             *(pCP++) = cMaxChar + 1;
435             if( !nRangeOffset ) {
436                 // glyphid can be calculated directly
437                 pStartGlyphs[i] = (cMinChar + nGlyphDelta) & 0xFFFF;
438             } else {
439                 // update the glyphid-array with the glyphs in this range
440                 pStartGlyphs[i] = -(int)aGlyphIdArray.size();
441                 const unsigned char* pGlyphIdPtr = pOffsetBase + 2*i + nRangeOffset;
442                 for( sal_UCS4 c = cMinChar; c <= cMaxChar; ++c, pGlyphIdPtr+=2 ) {
443                     const int nGlyphIndex = Getsal_uInt16( pGlyphIdPtr ) + nGlyphDelta;
444                     aGlyphIdArray.push_back( static_cast<sal_uInt16>(nGlyphIndex) );
445                 }
446             }
447         }
448         nRangeCount = (pCP - pCodePairs) / 2;
449     }
450     // format 12, the most common 32bit char mapping table
451     else if( (nFormat == 12) && ((nOffset+16) < nLength) )
452     {
453         nRangeCount = GetUInt( pCmap + nOffset + 12 );
454         pCodePairs = new sal_UCS4[ nRangeCount * 2 ];
455         pStartGlyphs = new int[ nRangeCount ];
456         const unsigned char* pGroup = pCmap + nOffset + 16;
457         sal_UCS4* pCP = pCodePairs;
458         for( int i = 0; i < nRangeCount; ++i )
459         {
460             sal_UCS4 cMinChar = GetUInt( pGroup + 0 );
461             sal_UCS4 cMaxChar = GetUInt( pGroup + 4 );
462             int nGlyphId = GetUInt( pGroup + 8 );
463             pGroup += 12;
464 #if 0       // TODO: remove unicode baseplane clipping for UCS-4 support
465             if( cMinChar > 0xFFFF )
466                 continue;
467             if( cMaxChar > 0xFFFF )
468                 cMaxChar = 0xFFFF;
469 #else
470             if( cMinChar > cMaxChar )   // no sane font should trigger this
471                 break;
472 #endif
473             *(pCP++) = cMinChar;
474             *(pCP++) = cMaxChar + 1;
475             pStartGlyphs[i] = nGlyphId;
476         }
477         nRangeCount = (pCP - pCodePairs) / 2;
478     }
479 
480     // check if any subtable resulted in something usable
481     if( nRangeCount <= 0 )
482     {
483         delete[] pCodePairs;
484         delete[] pStartGlyphs;
485 
486         // even when no CMAP is available we know it for symbol fonts
487         if( rResult.mbSymbolic )
488         {
489             pCodePairs = new sal_UCS4[4];
490             pCodePairs[0] = 0x0020;    // aliased symbols
491             pCodePairs[1] = 0x0100;
492             pCodePairs[2] = 0xF020;    // original symbols
493             pCodePairs[3] = 0xF100;
494             rResult.mpRangeCodes = pCodePairs;
495             rResult.mnRangeCount = 2;
496             return true;
497         }
498 
499         return false;
500     }
501 
502     // recode the code ranges to their unicode encoded ranges if needed
503     rtl_TextToUnicodeConverter aConverter = NULL;
504     rtl_UnicodeToTextContext aCvtContext = NULL;
505 
506     rResult.mbRecoded = ( eRecodeFrom != RTL_TEXTENCODING_UNICODE );
507     if( rResult.mbRecoded )
508     {
509         aConverter = rtl_createTextToUnicodeConverter( eRecodeFrom );
510         aCvtContext = rtl_createTextToUnicodeContext( aConverter );
511     }
512 
513     if( aConverter && aCvtContext )
514     {
515         // determine the set of supported unicodes from encoded ranges
516         typedef std::set<sal_UCS4> Ucs4Set;
517         Ucs4Set aSupportedUnicodes;
518 
519         static const int NINSIZE = 64;
520         static const int NOUTSIZE = 64;
521         sal_Char    cCharsInp[ NINSIZE ];
522         sal_Unicode cCharsOut[ NOUTSIZE ];
523         sal_UCS4* pCP = pCodePairs;
524         for( int i = 0; i < nRangeCount; ++i )
525         {
526             sal_UCS4 cMin = *(pCP++);
527             sal_UCS4 cEnd = *(pCP++);
528             while( cMin < cEnd )
529             {
530                 int j = 0;
531                 for(; (cMin < cEnd) && (j < NINSIZE); ++cMin )
532                 {
533                     if( cMin >= 0x0100 )
534                         cCharsInp[ j++ ] = static_cast<sal_Char>(cMin >> 8);
535                     if( (cMin >= 0x0100) || (cMin < 0x00A0)  )
536                         cCharsInp[ j++ ] = static_cast<sal_Char>(cMin);
537                 }
538 
539                 sal_uInt32 nCvtInfo;
540                 sal_Size nSrcCvtBytes;
541                 int nOutLen = rtl_convertTextToUnicode(
542                     aConverter, aCvtContext,
543                     cCharsInp, j, cCharsOut, NOUTSIZE,
544                     RTL_TEXTTOUNICODE_FLAGS_INVALID_IGNORE
545                     | RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_IGNORE,
546                     &nCvtInfo, &nSrcCvtBytes );
547 
548                 for( j = 0; j < nOutLen; ++j )
549                     aSupportedUnicodes.insert( cCharsOut[j] );
550             }
551         }
552 
553         rtl_destroyTextToUnicodeContext( aConverter, aCvtContext );
554         rtl_destroyTextToUnicodeConverter( aConverter );
555 
556         // convert the set of supported unicodes to ranges
557         typedef std::vector<sal_UCS4> Ucs4Vector;
558         Ucs4Vector aSupportedRanges;
559 
560         Ucs4Set::const_iterator itChar = aSupportedUnicodes.begin();
561         for(; itChar != aSupportedUnicodes.end(); ++itChar )
562         {
563             if( aSupportedRanges.empty()
564             || (aSupportedRanges.back() != *itChar) )
565             {
566                 // add new range beginning with current unicode
567                 aSupportedRanges.push_back( *itChar );
568                 aSupportedRanges.push_back( 0 );
569             }
570 
571             // extend existing range to include current unicode
572             aSupportedRanges.back() = *itChar + 1;
573         }
574 
575         // glyph mapping for non-unicode fonts not implemented
576         delete[] pStartGlyphs;
577         pStartGlyphs = NULL;
578         aGlyphIdArray.clear();
579 
580         // make a pCodePairs array using the vector from above
581         delete[] pCodePairs;
582         nRangeCount = aSupportedRanges.size() / 2;
583         if( nRangeCount <= 0 )
584             return false;
585         pCodePairs = new sal_UCS4[ nRangeCount * 2 ];
586         Ucs4Vector::const_iterator itInt = aSupportedRanges.begin();
587         for( pCP = pCodePairs; itInt != aSupportedRanges.end(); ++itInt )
588             *(pCP++) = *itInt;
589     }
590 
591     // prepare the glyphid-array if needed
592     // TODO: merge ranges if they are close enough?
593     sal_uInt16* pGlyphIds = NULL;
594     if( !aGlyphIdArray.empty())
595     {
596         pGlyphIds = new sal_uInt16[ aGlyphIdArray.size() ];
597         sal_uInt16* pOut = pGlyphIds;
598         U16Vector::const_iterator it = aGlyphIdArray.begin();
599         while( it != aGlyphIdArray.end() )
600             *(pOut++) = *(it++);
601     }
602 
603     // update the result struct
604     rResult.mpRangeCodes = pCodePairs;
605     rResult.mpStartGlyphs = pStartGlyphs;
606     rResult.mnRangeCount = nRangeCount;
607     rResult.mpGlyphIds = pGlyphIds;
608     return true;
609 }
610 
611 // =======================================================================
612 
FontCharMap()613 FontCharMap::FontCharMap()
614 :   mpImpl( ImplFontCharMap::GetDefaultMap() )
615 {}
616 
617 // -----------------------------------------------------------------------
618 
~FontCharMap()619 FontCharMap::~FontCharMap()
620 {
621 	mpImpl->DeReference();
622 	mpImpl = NULL;
623 }
624 
625 // -----------------------------------------------------------------------
626 
GetCharCount() const627 int FontCharMap::GetCharCount() const
628 {
629 	return mpImpl->GetCharCount();
630 }
631 
632 // -----------------------------------------------------------------------
633 
CountCharsInRange(sal_UCS4 cMin,sal_UCS4 cMax) const634 int FontCharMap::CountCharsInRange( sal_UCS4 cMin, sal_UCS4 cMax ) const
635 {
636 	return mpImpl->CountCharsInRange( cMin, cMax );
637 }
638 
639 // -----------------------------------------------------------------------
640 
Reset(const ImplFontCharMap * pNewMap)641 void FontCharMap::Reset( const ImplFontCharMap* pNewMap )
642 {
643 	if( pNewMap == NULL )
644 	{
645 		mpImpl->DeReference();
646 		mpImpl = ImplFontCharMap::GetDefaultMap();
647 	}
648 	else if( pNewMap != mpImpl )
649 	{
650 		mpImpl->DeReference();
651 		mpImpl = pNewMap;
652 		mpImpl->AddReference();
653 	}
654 }
655 
656 // -----------------------------------------------------------------------
657 
IsDefaultMap() const658 bool FontCharMap::IsDefaultMap() const
659 {
660 	return mpImpl->IsDefaultMap();
661 }
662 
663 // -----------------------------------------------------------------------
664 
HasChar(sal_UCS4 cChar) const665 bool FontCharMap::HasChar( sal_UCS4 cChar ) const
666 {
667 	return mpImpl->HasChar( cChar );
668 }
669 
670 // -----------------------------------------------------------------------
671 
GetFirstChar() const672 sal_UCS4 FontCharMap::GetFirstChar() const
673 {
674 	return mpImpl->GetFirstChar();
675 }
676 
677 // -----------------------------------------------------------------------
678 
GetLastChar() const679 sal_UCS4 FontCharMap::GetLastChar() const
680 {
681 	return mpImpl->GetLastChar();
682 }
683 
684 // -----------------------------------------------------------------------
685 
GetNextChar(sal_UCS4 cChar) const686 sal_UCS4 FontCharMap::GetNextChar( sal_UCS4 cChar ) const
687 {
688 	return mpImpl->GetNextChar( cChar );
689 }
690 
691 // -----------------------------------------------------------------------
692 
GetPrevChar(sal_UCS4 cChar) const693 sal_UCS4 FontCharMap::GetPrevChar( sal_UCS4 cChar ) const
694 {
695 	return mpImpl->GetPrevChar( cChar );
696 }
697 
698 // -----------------------------------------------------------------------
699 
GetIndexFromChar(sal_UCS4 cChar) const700 int FontCharMap::GetIndexFromChar( sal_UCS4 cChar ) const
701 {
702 	return mpImpl->GetIndexFromChar( cChar );
703 }
704 
705 // -----------------------------------------------------------------------
706 
GetCharFromIndex(int nIndex) const707 sal_UCS4 FontCharMap::GetCharFromIndex( int nIndex ) const
708 {
709 	return mpImpl->GetCharFromIndex( nIndex );
710 }
711 
712 // =======================================================================
713 
714 // on some systems we have to get the font attributes from the name table
715 // since neither head's macStyle nor OS/2's panose are easily available
716 // during font enumeration. macStyle bits would be not sufficient anyway
717 // and SFNT fonts on Mac usually do not contain an OS/2 table.
UpdateAttributesFromPSName(const String & rPSName,ImplDevFontAttributes & rDFA)718 void UpdateAttributesFromPSName( const String& rPSName, ImplDevFontAttributes& rDFA )
719 {
720 	ByteString aPSName( rPSName, RTL_TEXTENCODING_UTF8 );
721 	aPSName.ToLowerAscii();
722 
723 	// TODO: use a multi-string ignore-case matcher once it becomes available
724 	if( (aPSName.Search("regular") != STRING_NOTFOUND)
725 	||  (aPSName.Search("normal") != STRING_NOTFOUND)
726 	||  (aPSName.Search("roman") != STRING_NOTFOUND)
727 	||  (aPSName.Search("medium") != STRING_NOTFOUND)
728 	||  (aPSName.Search("plain") != STRING_NOTFOUND)
729 	||  (aPSName.Search("standard") != STRING_NOTFOUND)
730 	||  (aPSName.Search("std") != STRING_NOTFOUND) )
731 	{
732 		rDFA.meWidthType = WIDTH_NORMAL;
733 		rDFA.meWeight    = WEIGHT_NORMAL;
734 		rDFA.meItalic    = ITALIC_NONE;
735 	}
736 
737 	// heuristics for font weight
738 	if (aPSName.Search("extrablack") != STRING_NOTFOUND)
739 		rDFA.meWeight = WEIGHT_BLACK;
740 	else if (aPSName.Search("black") != STRING_NOTFOUND)
741 		rDFA.meWeight = WEIGHT_BLACK;
742 #if 1
743 	else if (aPSName.Search("book") != STRING_NOTFOUND)
744 		rDFA.meWeight = WEIGHT_NORMAL;
745 #endif
746 	else if( (aPSName.Search("semibold") != STRING_NOTFOUND)
747 	||       (aPSName.Search("smbd") != STRING_NOTFOUND))
748 		rDFA.meWeight = WEIGHT_SEMIBOLD;
749 	else if( aPSName.Search("ultrabold") != STRING_NOTFOUND)
750 		rDFA.meWeight = WEIGHT_ULTRABOLD;
751 	else if( aPSName.Search("extrabold") != STRING_NOTFOUND)
752 		rDFA.meWeight = WEIGHT_BLACK;
753 	else if( (aPSName.Search("bold") != STRING_NOTFOUND)
754 	||       (aPSName.Search("-bd") != STRING_NOTFOUND))
755 		rDFA.meWeight = WEIGHT_BOLD;
756 	else if( aPSName.Search("extralight") != STRING_NOTFOUND)
757 		rDFA.meWeight = WEIGHT_ULTRALIGHT;
758 	else if( aPSName.Search("ultralight") != STRING_NOTFOUND)
759 		rDFA.meWeight = WEIGHT_ULTRALIGHT;
760 	else if( aPSName.Search("light") != STRING_NOTFOUND)
761 		rDFA.meWeight = WEIGHT_LIGHT;
762 	else if( aPSName.Search("thin") != STRING_NOTFOUND)
763 		rDFA.meWeight = WEIGHT_THIN;
764 	else if( aPSName.Search("-w3") != STRING_NOTFOUND)
765 		rDFA.meWeight = WEIGHT_LIGHT;
766 	else if( aPSName.Search("-w4") != STRING_NOTFOUND)
767 		rDFA.meWeight = WEIGHT_SEMILIGHT;
768 	else if( aPSName.Search("-w5") != STRING_NOTFOUND)
769 		rDFA.meWeight = WEIGHT_NORMAL;
770 	else if( aPSName.Search("-w6") != STRING_NOTFOUND)
771 		rDFA.meWeight = WEIGHT_SEMIBOLD;
772 	else if( aPSName.Search("-w7") != STRING_NOTFOUND)
773 		rDFA.meWeight = WEIGHT_BOLD;
774 	else if( aPSName.Search("-w8") != STRING_NOTFOUND)
775 		rDFA.meWeight = WEIGHT_ULTRABOLD;
776 	else if( aPSName.Search("-w9") != STRING_NOTFOUND)
777 		rDFA.meWeight = WEIGHT_BLACK;
778 
779     // heuristics for font slant
780     if( (aPSName.Search("italic") != STRING_NOTFOUND)
781     ||  (aPSName.Search(" ital") != STRING_NOTFOUND)
782     ||  (aPSName.Search("cursive") != STRING_NOTFOUND)
783     ||  (aPSName.Search("-it") != STRING_NOTFOUND)
784     ||  (aPSName.Search("lightit") != STRING_NOTFOUND)
785     ||  (aPSName.Search("mediumit") != STRING_NOTFOUND)
786     ||  (aPSName.Search("boldit") != STRING_NOTFOUND)
787     ||  (aPSName.Search("cnit") != STRING_NOTFOUND)
788     ||  (aPSName.Search("bdcn") != STRING_NOTFOUND)
789     ||  (aPSName.Search("bdit") != STRING_NOTFOUND)
790     ||  (aPSName.Search("condit") != STRING_NOTFOUND)
791     ||  (aPSName.Search("bookit") != STRING_NOTFOUND)
792     ||  (aPSName.Search("blackit") != STRING_NOTFOUND) )
793 	    rDFA.meItalic = ITALIC_NORMAL;
794     if( (aPSName.Search("oblique") != STRING_NOTFOUND)
795     ||  (aPSName.Search("inclined") != STRING_NOTFOUND)
796     ||  (aPSName.Search("slanted") != STRING_NOTFOUND) )
797 	    rDFA.meItalic = ITALIC_OBLIQUE;
798 
799     // heuristics for font width
800     if( (aPSName.Search("condensed") != STRING_NOTFOUND)
801     ||  (aPSName.Search("-cond") != STRING_NOTFOUND)
802     ||  (aPSName.Search("boldcond") != STRING_NOTFOUND)
803     ||  (aPSName.Search("boldcn") != STRING_NOTFOUND)
804     ||  (aPSName.Search("cnit") != STRING_NOTFOUND) )
805 	    rDFA.meWidthType = WIDTH_CONDENSED;
806     else if (aPSName.Search("narrow") != STRING_NOTFOUND)
807 	    rDFA.meWidthType = WIDTH_SEMI_CONDENSED;
808     else if (aPSName.Search("expanded") != STRING_NOTFOUND)
809 	    rDFA.meWidthType = WIDTH_EXPANDED;
810     else if (aPSName.Search("wide") != STRING_NOTFOUND)
811 	    rDFA.meWidthType = WIDTH_EXPANDED;
812 
813     // heuristics for font pitch
814     if( (aPSName.Search("mono") != STRING_NOTFOUND)
815     ||  (aPSName.Search("courier") != STRING_NOTFOUND)
816     ||  (aPSName.Search("monaco") != STRING_NOTFOUND)
817     ||  (aPSName.Search("typewriter") != STRING_NOTFOUND) )
818 	    rDFA.mePitch = PITCH_FIXED;
819 
820     // heuristics for font family type
821     if( (aPSName.Search("script") != STRING_NOTFOUND)
822     ||  (aPSName.Search("chancery") != STRING_NOTFOUND)
823     ||  (aPSName.Search("zapfino") != STRING_NOTFOUND))
824 	    rDFA.meFamily = FAMILY_SCRIPT;
825     else if( (aPSName.Search("comic") != STRING_NOTFOUND)
826     ||  (aPSName.Search("outline") != STRING_NOTFOUND)
827     ||  (aPSName.Search("pinpoint") != STRING_NOTFOUND) )
828 	    rDFA.meFamily = FAMILY_DECORATIVE;
829     else if( (aPSName.Search("sans") != STRING_NOTFOUND)
830     ||  (aPSName.Search("arial") != STRING_NOTFOUND) )
831 	    rDFA.meFamily = FAMILY_SWISS;
832     else if( (aPSName.Search("roman") != STRING_NOTFOUND)
833     ||  (aPSName.Search("times") != STRING_NOTFOUND) )
834 	    rDFA.meFamily = FAMILY_ROMAN;
835 
836     // heuristics for codepoint semantic
837     if( (aPSName.Search("symbol") != STRING_NOTFOUND)
838     ||  (aPSName.Search("dings") != STRING_NOTFOUND)
839     ||  (aPSName.Search("dingbats") != STRING_NOTFOUND)
840     ||  (aPSName.Search("braille") != STRING_NOTFOUND)
841     ||  (aPSName.Search("ornaments") != STRING_NOTFOUND)
842     ||  (aPSName.Search("embellishments") != STRING_NOTFOUND) )
843         rDFA.mbSymbolFlag  = true;
844 
845    // #i100020# special heuristic for names with single-char styles
846    // NOTE: we are checking name that hasn't been lower-cased
847    if( rPSName.Len() > 3 )
848    {
849         int i = rPSName.Len();
850         sal_Unicode c = rPSName.GetChar( --i );
851         if( c == 'C' ) { // "capitals"
852             rDFA.meFamily = FAMILY_DECORATIVE;
853             c = rPSName.GetChar( --i );
854         }
855         if( c == 'O' ) { // CFF-based OpenType
856             c = rPSName.GetChar( --i );
857         }
858         if( c == 'I' ) { // "italic"
859             rDFA.meItalic = ITALIC_NORMAL;
860             c = rPSName.GetChar( --i );
861         }
862         if( c == 'B' )   // "bold"
863             rDFA.meWeight = WEIGHT_BOLD;
864         if( c == 'C' )   // "capitals"
865             rDFA.meFamily = FAMILY_DECORATIVE;
866         // TODO: check that all single-char styles have been resolved?
867     }
868 }
869 
870 // =======================================================================
871 
872