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