Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 :
21 : #include "sft.hxx"
22 :
23 : #include "gsub.h"
24 :
25 : #include <osl/diagnose.h>
26 :
27 : #include <vector>
28 : #include <map>
29 : #include <algorithm>
30 :
31 : namespace vcl
32 : {
33 :
34 : typedef sal_uIntPtr sal_uLong;
35 : typedef sal_uInt8 FT_Byte;
36 :
37 : typedef std::map<sal_uInt16,sal_uInt16> GlyphSubstitution;
38 :
39 :
40 302708 : inline sal_uInt32 NEXT_Long( const unsigned char* &p )
41 : {
42 302708 : sal_uInt32 nVal = (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3];
43 302708 : p += 4;
44 302708 : return nVal;
45 : }
46 :
47 1444717 : inline sal_uInt16 NEXT_UShort( const unsigned char* &p )
48 : {
49 1444717 : sal_uInt16 nVal = (p[0]<<8) + p[1];
50 1444717 : p += 2;
51 1444717 : return nVal;
52 : }
53 :
54 : #define MKTAG(s) ((((((s[0]<<8)+s[1])<<8)+s[2])<<8)+s[3])
55 :
56 15944 : int ReadGSUB( struct _TrueTypeFont* pTTFile,
57 : int nRequestedScript, int nRequestedLangsys )
58 : {
59 15944 : const FT_Byte* pGsubBase = (FT_Byte*)pTTFile->tables[ O_gsub ];
60 15944 : if( !pGsubBase )
61 750 : return -1;
62 :
63 : // #129682# check offsets inside GSUB table
64 15194 : const FT_Byte* pGsubLimit = pGsubBase + pTTFile->tlens[ O_gsub ];
65 :
66 : // parse GSUB header
67 15194 : const FT_Byte* pGsubHeader = pGsubBase;
68 15194 : const sal_uLong nVersion = NEXT_Long( pGsubHeader );
69 15194 : const sal_uInt16 nOfsScriptList = NEXT_UShort( pGsubHeader );
70 15194 : const sal_uInt16 nOfsFeatureTable = NEXT_UShort( pGsubHeader );
71 15194 : const sal_uInt16 nOfsLookupList = NEXT_UShort( pGsubHeader );
72 :
73 : // sanity check the GSUB header
74 15194 : if( nVersion != 0x00010000 )
75 0 : if( nVersion != 0x00001000 ) // workaround for SunBatang etc.
76 0 : return -1; // unknown format or broken
77 :
78 : typedef std::vector<sal_uLong> ReqFeatureTagList;
79 15194 : ReqFeatureTagList aReqFeatureTagList;
80 :
81 15194 : aReqFeatureTagList.push_back( MKTAG("vert") );
82 :
83 : typedef std::vector<sal_uInt16> UshortList;
84 30388 : UshortList aFeatureIndexList;
85 :
86 : // parse Script Table
87 15194 : const FT_Byte* pScriptHeader = pGsubBase + nOfsScriptList;
88 15194 : const sal_uInt16 nCntScript = NEXT_UShort( pScriptHeader );
89 15194 : if( pGsubLimit < pScriptHeader + 6 * nCntScript )
90 0 : return false;
91 89539 : for( sal_uInt16 nScriptIndex = 0; nScriptIndex < nCntScript; ++nScriptIndex )
92 : {
93 74345 : const sal_uLong nTag = NEXT_Long( pScriptHeader ); // e.g. hani/arab/kana/hang
94 74345 : const sal_uInt16 nOfsScriptTable= NEXT_UShort( pScriptHeader );
95 74345 : if( (nTag != (sal_uInt16)nRequestedScript) && (nRequestedScript != 0) )
96 0 : continue;
97 :
98 74345 : const FT_Byte* pScriptTable = pGsubBase + nOfsScriptList + nOfsScriptTable;
99 74345 : if( pGsubLimit < pScriptTable + 4 )
100 0 : return false;
101 74345 : const sal_uInt16 nDefaultLangsysOfs = NEXT_UShort( pScriptTable );
102 74345 : const sal_uInt16 nCntLangSystem = NEXT_UShort( pScriptTable );
103 74345 : sal_uInt16 nLangsysOffset = 0;
104 74345 : if( pGsubLimit < pScriptTable + 6 * nCntLangSystem )
105 0 : return false;
106 74345 : for( sal_uInt16 nLangsysIndex = 0; nLangsysIndex < nCntLangSystem; ++nLangsysIndex )
107 : {
108 17794 : const sal_uLong nInnerTag = NEXT_Long( pScriptTable ); // e.g. KOR/ZHS/ZHT/JAN
109 17794 : const sal_uInt16 nOffset= NEXT_UShort( pScriptTable );
110 17794 : if( (nInnerTag != (sal_uInt16)nRequestedLangsys) && (nRequestedLangsys != 0) )
111 0 : continue;
112 17794 : nLangsysOffset = nOffset;
113 17794 : break;
114 : }
115 :
116 74345 : if( (nDefaultLangsysOfs != 0) && (nDefaultLangsysOfs != nLangsysOffset) )
117 : {
118 69399 : const FT_Byte* pLangSys = pGsubBase + nOfsScriptList + nOfsScriptTable + nDefaultLangsysOfs;
119 69399 : if( pGsubLimit < pLangSys + 6 )
120 0 : return false;
121 69399 : /*const sal_uInt16 nLookupOrder =*/ NEXT_UShort( pLangSys );
122 69399 : const sal_uInt16 nReqFeatureIdx = NEXT_UShort( pLangSys );
123 69399 : const sal_uInt16 nCntFeature = NEXT_UShort( pLangSys );
124 69399 : if( pGsubLimit < pLangSys + 2 * nCntFeature )
125 0 : return false;
126 69399 : aFeatureIndexList.push_back( nReqFeatureIdx );
127 317679 : for( sal_uInt16 i = 0; i < nCntFeature; ++i )
128 : {
129 248280 : const sal_uInt16 nFeatureIndex = NEXT_UShort( pLangSys );
130 248280 : aFeatureIndexList.push_back( nFeatureIndex );
131 : }
132 : }
133 :
134 74345 : if( nLangsysOffset != 0 )
135 : {
136 17794 : const FT_Byte* pLangSys = pGsubBase + nOfsScriptList + nOfsScriptTable + nLangsysOffset;
137 17794 : if( pGsubLimit < pLangSys + 6 )
138 0 : return false;
139 17794 : /*const sal_uInt16 nLookupOrder =*/ NEXT_UShort( pLangSys );
140 17794 : const sal_uInt16 nReqFeatureIdx = NEXT_UShort( pLangSys );
141 17794 : const sal_uInt16 nCntFeature = NEXT_UShort( pLangSys );
142 17794 : if( pGsubLimit < pLangSys + 2 * nCntFeature )
143 0 : return false;
144 17794 : aFeatureIndexList.push_back( nReqFeatureIdx );
145 102950 : for( sal_uInt16 i = 0; i < nCntFeature; ++i )
146 : {
147 85156 : const sal_uInt16 nFeatureIndex = NEXT_UShort( pLangSys );
148 85156 : aFeatureIndexList.push_back( nFeatureIndex );
149 : }
150 : }
151 : }
152 :
153 15194 : if( aFeatureIndexList.empty() )
154 0 : return true;
155 :
156 30388 : UshortList aLookupIndexList;
157 30388 : UshortList aLookupOffsetList;
158 :
159 : // parse Feature Table
160 15194 : const FT_Byte* pFeatureHeader = pGsubBase + nOfsFeatureTable;
161 15194 : if( pGsubLimit < pFeatureHeader + 2 )
162 0 : return false;
163 15194 : const sal_uInt16 nCntFeature = NEXT_UShort( pFeatureHeader );
164 15194 : if( pGsubLimit < pFeatureHeader + 6 * nCntFeature )
165 0 : return false;
166 210569 : for( sal_uInt16 nFeatureIndex = 0; nFeatureIndex < nCntFeature; ++nFeatureIndex )
167 : {
168 195375 : const sal_uLong nTag = NEXT_Long( pFeatureHeader ); // e.g. locl/vert/trad/smpl/liga/fina/...
169 195375 : const sal_uInt16 nOffset= NEXT_UShort( pFeatureHeader );
170 :
171 : // ignore unneeded feature lookups
172 195375 : if( aFeatureIndexList[0] != nFeatureIndex ) // do not ignore the required feature
173 : {
174 194537 : const int nRequested = std::count( aFeatureIndexList.begin(), aFeatureIndexList.end(), nFeatureIndex);
175 194537 : if( !nRequested ) // ignore features that are not requested
176 223617 : continue;
177 164832 : const int nAvailable = std::count( aReqFeatureTagList.begin(), aReqFeatureTagList.end(), nTag);
178 164832 : if( !nAvailable ) // some fonts don't provide features they request!
179 164207 : continue;
180 : }
181 :
182 1463 : const FT_Byte* pFeatureTable = pGsubBase + nOfsFeatureTable + nOffset;
183 1463 : if( pGsubLimit < pFeatureTable + 2 )
184 0 : return false;
185 1463 : const sal_uInt16 nCntLookups = NEXT_UShort( pFeatureTable );
186 1463 : if( pGsubLimit < pFeatureTable + 2 * nCntLookups )
187 0 : return false;
188 1463 : for( sal_uInt16 i = 0; i < nCntLookups; ++i )
189 : {
190 0 : const sal_uInt16 nLookupIndex = NEXT_UShort( pFeatureTable );
191 0 : aLookupIndexList.push_back( nLookupIndex );
192 : }
193 1463 : if( nCntLookups == 0 ) //### hack needed by Mincho/Gothic/Mingliu/Simsun/...
194 1463 : aLookupIndexList.push_back( 0 );
195 : }
196 :
197 : // parse Lookup List
198 15194 : const FT_Byte* pLookupHeader = pGsubBase + nOfsLookupList;
199 15194 : if( pGsubLimit < pLookupHeader + 2 )
200 0 : return false;
201 15194 : const sal_uInt16 nCntLookupTable = NEXT_UShort( pLookupHeader );
202 15194 : if( pGsubLimit < pLookupHeader + 2 * nCntLookupTable )
203 0 : return false;
204 322176 : for( sal_uInt16 nLookupIdx = 0; nLookupIdx < nCntLookupTable; ++nLookupIdx )
205 : {
206 306982 : const sal_uInt16 nOffset = NEXT_UShort( pLookupHeader );
207 306982 : if( std::count( aLookupIndexList.begin(), aLookupIndexList.end(), nLookupIdx ) )
208 1463 : aLookupOffsetList.push_back( nOffset );
209 : }
210 :
211 15194 : UshortList::const_iterator it = aLookupOffsetList.begin();
212 16657 : for(; it != aLookupOffsetList.end(); ++it )
213 : {
214 1463 : const sal_uInt16 nOfsLookupTable = *it;
215 1463 : const FT_Byte* pLookupTable = pGsubBase + nOfsLookupList + nOfsLookupTable;
216 1463 : if( pGsubLimit < pLookupTable + 6 )
217 0 : return false;
218 1463 : const sal_uInt16 eLookupType = NEXT_UShort( pLookupTable );
219 1463 : /*const sal_uInt16 eLookupFlag =*/ NEXT_UShort( pLookupTable );
220 1463 : const sal_uInt16 nCntLookupSubtable = NEXT_UShort( pLookupTable );
221 :
222 : // TODO: switch( eLookupType )
223 1463 : if( eLookupType != 1 ) // TODO: once we go beyond SingleSubst
224 1338 : continue;
225 :
226 125 : if( pGsubLimit < pLookupTable + 2 * nCntLookupSubtable )
227 0 : return false;
228 500 : for( sal_uInt16 nSubTableIdx = 0; nSubTableIdx < nCntLookupSubtable; ++nSubTableIdx )
229 : {
230 125 : const sal_uInt16 nOfsSubLookupTable = NEXT_UShort( pLookupTable );
231 125 : const FT_Byte* pSubLookup = pGsubBase + nOfsLookupList + nOfsLookupTable + nOfsSubLookupTable;
232 125 : if( pGsubLimit < pSubLookup + 6 )
233 0 : return false;
234 125 : const sal_uInt16 nFmtSubstitution = NEXT_UShort( pSubLookup );
235 125 : const sal_uInt16 nOfsCoverage = NEXT_UShort( pSubLookup );
236 :
237 : typedef std::pair<sal_uInt16,sal_uInt16> GlyphSubst;
238 : typedef std::vector<GlyphSubst> SubstVector;
239 125 : SubstVector aSubstVector;
240 :
241 : const FT_Byte* pCoverage = pGsubBase
242 125 : + nOfsLookupList + nOfsLookupTable + nOfsSubLookupTable + nOfsCoverage;
243 125 : if( pGsubLimit < pCoverage + 4 )
244 0 : return false;
245 125 : const sal_uInt16 nFmtCoverage = NEXT_UShort( pCoverage );
246 125 : switch( nFmtCoverage )
247 : {
248 : case 1: // Coverage Format 1
249 : {
250 125 : const sal_uInt16 nCntGlyph = NEXT_UShort( pCoverage );
251 125 : if( pGsubLimit < pCoverage + 2 * nCntGlyph )
252 : // TODO? nCntGlyph = (pGsubLimit - pCoverage) / 2;
253 0 : return false;
254 125 : aSubstVector.reserve( nCntGlyph );
255 4500 : for( sal_uInt16 i = 0; i < nCntGlyph; ++i )
256 : {
257 4375 : const sal_uInt16 nGlyphId = NEXT_UShort( pCoverage );
258 4375 : aSubstVector.push_back( GlyphSubst( nGlyphId, 0 ) );
259 : }
260 : }
261 125 : break;
262 :
263 : case 2: // Coverage Format 2
264 : {
265 0 : const sal_uInt16 nCntRange = NEXT_UShort( pCoverage );
266 0 : if( pGsubLimit < pCoverage + 6 * nCntRange )
267 : // TODO? nCntGlyph = (pGsubLimit - pCoverage) / 6;
268 0 : return false;
269 0 : for( int i = nCntRange; --i >= 0; )
270 : {
271 0 : const sal_uInt32 nGlyph0 = NEXT_UShort( pCoverage );
272 0 : const sal_uInt32 nGlyph1 = NEXT_UShort( pCoverage );
273 0 : const sal_uInt16 nCovIdx = NEXT_UShort( pCoverage );
274 0 : for( sal_uInt32 j = nGlyph0; j <= nGlyph1; ++j )
275 0 : aSubstVector.push_back( GlyphSubst( static_cast<sal_uInt16>(j + nCovIdx), 0 ) );
276 : }
277 : }
278 0 : break;
279 : }
280 :
281 125 : SubstVector::iterator subst_it( aSubstVector.begin() );
282 :
283 125 : switch( nFmtSubstitution )
284 : {
285 : case 1: // Single Substitution Format 1
286 : {
287 0 : const sal_uInt16 nDeltaGlyphId = NEXT_UShort( pSubLookup );
288 :
289 0 : for(; subst_it != aSubstVector.end(); ++subst_it )
290 0 : (*subst_it).second = (*subst_it).first + nDeltaGlyphId;
291 : }
292 0 : break;
293 :
294 : case 2: // Single Substitution Format 2
295 : {
296 125 : const sal_uInt16 nCntGlyph = NEXT_UShort( pSubLookup );
297 4500 : for( int i = nCntGlyph; (subst_it != aSubstVector.end()) && (--i>=0); ++subst_it )
298 : {
299 4375 : if( pGsubLimit < pSubLookup + 2 )
300 0 : return false;
301 4375 : const sal_uInt16 nGlyphId = NEXT_UShort( pSubLookup );
302 4375 : (*subst_it).second = nGlyphId;
303 : }
304 : }
305 125 : break;
306 : }
307 :
308 : // now apply the glyph substitutions that have been collected in this subtable
309 125 : if( !aSubstVector.empty() )
310 : {
311 125 : GlyphSubstitution* pGSubstitution = new GlyphSubstitution;
312 125 : pTTFile->pGSubstitution = (void*)pGSubstitution;
313 4500 : for( subst_it = aSubstVector.begin(); subst_it != aSubstVector.end(); ++subst_it )
314 4375 : (*pGSubstitution)[ (*subst_it).first ] = (*subst_it).second;
315 : }
316 125 : }
317 : }
318 30388 : return true;
319 : }
320 :
321 15944 : void ReleaseGSUB(struct _TrueTypeFont* pTTFile)
322 : {
323 15944 : GlyphSubstitution* pGlyphSubstitution = (GlyphSubstitution*)pTTFile->pGSubstitution;
324 15944 : if( pGlyphSubstitution )
325 125 : delete pGlyphSubstitution;
326 15944 : }
327 :
328 0 : int UseGSUB( struct _TrueTypeFont* pTTFile, int nGlyph, int /*wmode*/ )
329 : {
330 0 : GlyphSubstitution* pGlyphSubstitution = (GlyphSubstitution*)pTTFile->pGSubstitution;
331 0 : if( pGlyphSubstitution != 0 )
332 : {
333 0 : GlyphSubstitution::const_iterator it( pGlyphSubstitution->find( sal::static_int_cast<sal_uInt16>(nGlyph) ) );
334 0 : if( it != pGlyphSubstitution->end() )
335 0 : nGlyph = (*it).second;
336 : }
337 :
338 0 : return nGlyph;
339 : }
340 :
341 15944 : int HasVerticalGSUB( struct _TrueTypeFont* pTTFile )
342 : {
343 15944 : GlyphSubstitution* pGlyphSubstitution = (GlyphSubstitution*)pTTFile->pGSubstitution;
344 15944 : return pGlyphSubstitution ? +1 : 0;
345 : }
346 :
347 : }
348 :
349 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|