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