xref: /aoo42x/main/vcl/source/fontsubset/gsub.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_vcl.hxx"
30 
31 #include "sft.hxx"
32 
33 #include "gsub.h"
34 
35 #include <osl/diagnose.h>
36 
37 #include <vector>
38 #include <map>
39 #include <algorithm>
40 
41 namespace vcl
42 {
43 
44 typedef sal_uIntPtr sal_uLong;
45 typedef sal_uInt8 FT_Byte;
46 
47 typedef std::map<sal_uInt16,sal_uInt16> GlyphSubstitution;
48 
49 
50 inline long NEXT_Long( const unsigned char* &p )
51 {
52     long nVal = (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3];
53     p += 4;
54     return nVal;
55 }
56 
57 inline sal_uInt16 NEXT_UShort( const unsigned char* &p )
58 {
59     sal_uInt16 nVal = (p[0]<<8) + p[1];
60     p += 2;
61     return nVal;
62 }
63 
64 #define MKTAG(s) ((((((s[0]<<8)+s[1])<<8)+s[2])<<8)+s[3])
65 
66 int ReadGSUB( struct _TrueTypeFont* pTTFile,
67     int nRequestedScript, int nRequestedLangsys )
68 {
69     const FT_Byte* pGsubBase = (FT_Byte*)pTTFile->tables[ O_gsub ];
70     if( !pGsubBase )
71         return -1;
72 
73     // #129682# check offsets inside GSUB table
74     const FT_Byte* pGsubLimit = pGsubBase + pTTFile->tlens[ O_gsub ];
75 
76     // parse GSUB header
77     const FT_Byte* pGsubHeader = pGsubBase;
78     const sal_uLong nVersion            = NEXT_Long( pGsubHeader );
79     const sal_uInt16 nOfsScriptList     = NEXT_UShort( pGsubHeader );
80     const sal_uInt16 nOfsFeatureTable   = NEXT_UShort( pGsubHeader );
81     const sal_uInt16 nOfsLookupList     = NEXT_UShort( pGsubHeader );
82 
83     // sanity check the GSUB header
84     if( nVersion != 0x00010000 )
85         if( nVersion != 0x00001000 )    // workaround for SunBatang etc.
86             return -1;                  // unknown format or broken
87 
88     typedef std::vector<sal_uLong> ReqFeatureTagList;
89     ReqFeatureTagList aReqFeatureTagList;
90 
91     aReqFeatureTagList.push_back( MKTAG("vert") );
92 
93     typedef std::vector<sal_uInt16> UshortList;
94     UshortList aFeatureIndexList;
95     UshortList aFeatureOffsetList;
96 
97     // parse Script Table
98     const FT_Byte* pScriptHeader = pGsubBase + nOfsScriptList;
99     const sal_uInt16 nCntScript = NEXT_UShort( pScriptHeader );
100     if( pGsubLimit < pScriptHeader + 6 * nCntScript )
101         return false;
102     for( sal_uInt16 nScriptIndex = 0; nScriptIndex < nCntScript; ++nScriptIndex )
103     {
104         const sal_uLong nTag            = NEXT_Long( pScriptHeader ); // e.g. hani/arab/kana/hang
105         const sal_uInt16 nOfsScriptTable= NEXT_UShort( pScriptHeader );
106         if( (nTag != (sal_uInt16)nRequestedScript) && (nRequestedScript != 0) )
107             continue;
108 
109         const FT_Byte* pScriptTable     = pGsubBase + nOfsScriptList + nOfsScriptTable;
110         if( pGsubLimit < pScriptTable + 4 )
111             return false;
112         const sal_uInt16 nDefaultLangsysOfs = NEXT_UShort( pScriptTable );
113         const sal_uInt16 nCntLangSystem     = NEXT_UShort( pScriptTable );
114         sal_uInt16 nLangsysOffset = 0;
115         if( pGsubLimit < pScriptTable + 6 * nCntLangSystem )
116             return false;
117         for( sal_uInt16 nLangsysIndex = 0; nLangsysIndex < nCntLangSystem; ++nLangsysIndex )
118         {
119             const sal_uLong nInnerTag = NEXT_Long( pScriptTable );    // e.g. KOR/ZHS/ZHT/JAN
120             const sal_uInt16 nOffset= NEXT_UShort( pScriptTable );
121             if( (nInnerTag != (sal_uInt16)nRequestedLangsys) && (nRequestedLangsys != 0) )
122                 continue;
123             nLangsysOffset = nOffset;
124             break;
125         }
126 
127         if( (nDefaultLangsysOfs != 0) && (nDefaultLangsysOfs != nLangsysOffset) )
128         {
129             const FT_Byte* pLangSys = pGsubBase + nOfsScriptList + nOfsScriptTable + nDefaultLangsysOfs;
130             if( pGsubLimit < pLangSys + 6 )
131                 return false;
132             /*const sal_uInt16 nLookupOrder   =*/ NEXT_UShort( pLangSys );
133             const sal_uInt16 nReqFeatureIdx = NEXT_UShort( pLangSys );
134             const sal_uInt16 nCntFeature    = NEXT_UShort( pLangSys );
135             if( pGsubLimit < pLangSys + 2 * nCntFeature )
136                 return false;
137             aFeatureIndexList.push_back( nReqFeatureIdx );
138             for( sal_uInt16 i = 0; i < nCntFeature; ++i )
139             {
140                 const sal_uInt16 nFeatureIndex = NEXT_UShort( pLangSys );
141                 aFeatureIndexList.push_back( nFeatureIndex );
142             }
143         }
144 
145         if( nLangsysOffset != 0 )
146         {
147             const FT_Byte* pLangSys = pGsubBase + nOfsScriptList + nOfsScriptTable + nLangsysOffset;
148             if( pGsubLimit < pLangSys + 6 )
149                 return false;
150             /*const sal_uInt16 nLookupOrder   =*/ NEXT_UShort( pLangSys );
151             const sal_uInt16 nReqFeatureIdx = NEXT_UShort( pLangSys );
152             const sal_uInt16 nCntFeature    = NEXT_UShort( pLangSys );
153             if( pGsubLimit < pLangSys + 2 * nCntFeature )
154                 return false;
155             aFeatureIndexList.push_back( nReqFeatureIdx );
156             for( sal_uInt16 i = 0; i < nCntFeature; ++i )
157             {
158                 const sal_uInt16 nFeatureIndex = NEXT_UShort( pLangSys );
159                 aFeatureIndexList.push_back( nFeatureIndex );
160             }
161         }
162     }
163 
164     if( !aFeatureIndexList.size() )
165         return true;
166 
167     UshortList aLookupIndexList;
168     UshortList aLookupOffsetList;
169 
170     // parse Feature Table
171     const FT_Byte* pFeatureHeader = pGsubBase + nOfsFeatureTable;
172     if( pGsubLimit < pFeatureHeader + 2 )
173           return false;
174     const sal_uInt16 nCntFeature = NEXT_UShort( pFeatureHeader );
175     if( pGsubLimit < pFeatureHeader + 6 * nCntFeature )
176           return false;
177     for( sal_uInt16 nFeatureIndex = 0; nFeatureIndex < nCntFeature; ++nFeatureIndex )
178     {
179         const sal_uLong nTag    = NEXT_Long( pFeatureHeader ); // e.g. locl/vert/trad/smpl/liga/fina/...
180         const sal_uInt16 nOffset= NEXT_UShort( pFeatureHeader );
181 
182         // ignore unneeded feature lookups
183         if( aFeatureIndexList[0] != nFeatureIndex ) // do not ignore the required feature
184         {
185             const int nRequested = std::count( aFeatureIndexList.begin(), aFeatureIndexList.end(), nFeatureIndex);
186             if( !nRequested )	// ignore features that are not requested
187                 continue;
188             const int nAvailable = std::count( aReqFeatureTagList.begin(), aReqFeatureTagList.end(), nTag);
189             if( !nAvailable )	// some fonts don't provide features they request!
190                 continue;
191         }
192 
193         const FT_Byte* pFeatureTable = pGsubBase + nOfsFeatureTable + nOffset;
194         if( pGsubLimit < pFeatureTable + 2 )
195             return false;
196         const sal_uInt16 nCntLookups = NEXT_UShort( pFeatureTable );
197         if( pGsubLimit < pFeatureTable + 2 * nCntLookups )
198             return false;
199         for( sal_uInt16 i = 0; i < nCntLookups; ++i )
200         {
201             const sal_uInt16 nLookupIndex = NEXT_UShort( pFeatureTable );
202             aLookupIndexList.push_back( nLookupIndex );
203         }
204         if( nCntLookups == 0 ) //### hack needed by Mincho/Gothic/Mingliu/Simsun/...
205             aLookupIndexList.push_back( 0 );
206     }
207 
208     // parse Lookup List
209     const FT_Byte* pLookupHeader = pGsubBase + nOfsLookupList;
210     if( pGsubLimit < pLookupHeader + 2 )
211         return false;
212     const sal_uInt16 nCntLookupTable = NEXT_UShort( pLookupHeader );
213     if( pGsubLimit < pLookupHeader + 2 * nCntLookupTable )
214         return false;
215     for( sal_uInt16 nLookupIdx = 0; nLookupIdx < nCntLookupTable; ++nLookupIdx )
216     {
217         const sal_uInt16 nOffset = NEXT_UShort( pLookupHeader );
218         if( std::count( aLookupIndexList.begin(), aLookupIndexList.end(), nLookupIdx ) )
219             aLookupOffsetList.push_back( nOffset );
220     }
221 
222     UshortList::const_iterator it = aLookupOffsetList.begin();
223     for(; it != aLookupOffsetList.end(); ++it )
224     {
225         const sal_uInt16 nOfsLookupTable = *it;
226         const FT_Byte* pLookupTable = pGsubBase + nOfsLookupList + nOfsLookupTable;
227         if( pGsubLimit < pLookupTable + 6 )
228             return false;
229         const sal_uInt16 eLookupType        = NEXT_UShort( pLookupTable );
230         /*const sal_uInt16 eLookupFlag        =*/ NEXT_UShort( pLookupTable );
231         const sal_uInt16 nCntLookupSubtable = NEXT_UShort( pLookupTable );
232 
233         // TODO: switch( eLookupType )
234         if( eLookupType != 1 )  // TODO: once we go beyond SingleSubst
235             continue;
236 
237         if( pGsubLimit < pLookupTable + 2 * nCntLookupSubtable )
238             return false;
239         for( sal_uInt16 nSubTableIdx = 0; nSubTableIdx < nCntLookupSubtable; ++nSubTableIdx )
240         {
241             const sal_uInt16 nOfsSubLookupTable = NEXT_UShort( pLookupTable );
242             const FT_Byte* pSubLookup = pGsubBase + nOfsLookupList + nOfsLookupTable + nOfsSubLookupTable;
243             if( pGsubLimit < pSubLookup + 6 )
244                 return false;
245             const sal_uInt16 nFmtSubstitution   = NEXT_UShort( pSubLookup );
246             const sal_uInt16 nOfsCoverage       = NEXT_UShort( pSubLookup );
247 
248             typedef std::pair<sal_uInt16,sal_uInt16> GlyphSubst;
249             typedef std::vector<GlyphSubst> SubstVector;
250             SubstVector aSubstVector;
251 
252             const FT_Byte* pCoverage    = pGsubBase
253                 + nOfsLookupList + nOfsLookupTable + nOfsSubLookupTable + nOfsCoverage;
254             if( pGsubLimit < pCoverage + 4 )
255                 return false;
256             const sal_uInt16 nFmtCoverage   = NEXT_UShort( pCoverage );
257             switch( nFmtCoverage )
258             {
259                 case 1:         // Coverage Format 1
260                 {
261                     const sal_uInt16 nCntGlyph = NEXT_UShort( pCoverage );
262                     if( pGsubLimit < pCoverage + 2 * nCntGlyph )
263                         // TODO? nCntGlyph = (pGsubLimit - pCoverage) / 2;
264                         return false;
265                     aSubstVector.reserve( nCntGlyph );
266                     for( sal_uInt16 i = 0; i < nCntGlyph; ++i )
267                     {
268                         const sal_uInt16 nGlyphId = NEXT_UShort( pCoverage );
269                         aSubstVector.push_back( GlyphSubst( nGlyphId, 0 ) );
270                     }
271                 }
272                 break;
273 
274                 case 2:         // Coverage Format 2
275                 {
276                     const sal_uInt16 nCntRange = NEXT_UShort( pCoverage );
277                     if( pGsubLimit < pCoverage + 6 * nCntRange )
278                         // TODO? nCntGlyph = (pGsubLimit - pCoverage) / 6;
279                         return false;
280                     for( int i = nCntRange; --i >= 0; )
281                     {
282                         const sal_uInt32 nGlyph0 = NEXT_UShort( pCoverage );
283                         const sal_uInt32 nGlyph1 = NEXT_UShort( pCoverage );
284                         const sal_uInt16 nCovIdx = NEXT_UShort( pCoverage );
285                         for( sal_uInt32 j = nGlyph0; j <= nGlyph1; ++j )
286                             aSubstVector.push_back( GlyphSubst( static_cast<sal_uInt16>(j + nCovIdx), 0 ) );
287                     }
288                 }
289                 break;
290             }
291 
292             SubstVector::iterator subst_it( aSubstVector.begin() );
293 
294             switch( nFmtSubstitution )
295             {
296                 case 1:     // Single Substitution Format 1
297                 {
298                     const sal_uInt16 nDeltaGlyphId = NEXT_UShort( pSubLookup );
299 
300                     for(; subst_it != aSubstVector.end(); ++subst_it )
301                         (*subst_it).second = (*subst_it).first + nDeltaGlyphId;
302                 }
303                 break;
304 
305                 case 2:     // Single Substitution Format 2
306                 {
307                     const sal_uInt16 nCntGlyph = NEXT_UShort( pSubLookup );
308                     for( int i = nCntGlyph; (subst_it != aSubstVector.end()) && (--i>=0); ++subst_it )
309                     {
310                         if( pGsubLimit < pSubLookup + 2 )
311                             return false;
312                         const sal_uInt16 nGlyphId = NEXT_UShort( pSubLookup );
313                         (*subst_it).second = nGlyphId;
314                     }
315                 }
316                 break;
317             }
318 
319             // now apply the glyph substitutions that have been collected in this subtable
320             if( aSubstVector.size() > 0 )
321             {
322                 GlyphSubstitution* pGSubstitution = new GlyphSubstitution;
323                 pTTFile->pGSubstitution = (void*)pGSubstitution;
324                 for( subst_it = aSubstVector.begin(); subst_it != aSubstVector.end(); ++subst_it )
325                     (*pGSubstitution)[ (*subst_it).first ] =  (*subst_it).second;
326             }
327         }
328     }
329     return true;
330 }
331 
332 void ReleaseGSUB(struct _TrueTypeFont* pTTFile)
333 {
334     GlyphSubstitution* pGlyphSubstitution = (GlyphSubstitution*)pTTFile->pGSubstitution;
335     if( pGlyphSubstitution )
336         delete pGlyphSubstitution;
337 }
338 
339 int UseGSUB( struct _TrueTypeFont* pTTFile, int nGlyph, int /*wmode*/ )
340 {
341     GlyphSubstitution* pGlyphSubstitution = (GlyphSubstitution*)pTTFile->pGSubstitution;
342     if( pGlyphSubstitution != 0 )
343     {
344         GlyphSubstitution::const_iterator it( pGlyphSubstitution->find( sal::static_int_cast<sal_uInt16>(nGlyph) ) );
345         if( it != pGlyphSubstitution->end() )
346             nGlyph = (*it).second;
347     }
348 
349     return nGlyph;
350 }
351 
352 int HasVerticalGSUB( struct _TrueTypeFont* pTTFile )
353 {
354     GlyphSubstitution* pGlyphSubstitution = (GlyphSubstitution*)pTTFile->pGSubstitution;
355     return pGlyphSubstitution ? +1 : 0;
356 }
357 
358 }
359