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 2712 : inline sal_uInt32 NEXT_Long( const unsigned char* &p )
41 : {
42 2712 : sal_uInt32 nVal = (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3];
43 2712 : p += 4;
44 2712 : return nVal;
45 : }
46 :
47 13316 : inline sal_uInt16 NEXT_UShort( const unsigned char* &p )
48 : {
49 13316 : sal_uInt16 nVal = (p[0]<<8) + p[1];
50 13316 : p += 2;
51 13316 : return nVal;
52 : }
53 :
54 : #define MKTAG(s) ((((((s[0]<<8)+s[1])<<8)+s[2])<<8)+s[3])
55 :
56 158 : int ReadGSUB( struct _TrueTypeFont* pTTFile,
57 : int nRequestedScript, int nRequestedLangsys )
58 : {
59 158 : const FT_Byte* pGsubBase = (FT_Byte*)pTTFile->tables[ O_gsub ];
60 158 : if( !pGsubBase )
61 12 : return -1;
62 :
63 : // #129682# check offsets inside GSUB table
64 146 : const FT_Byte* pGsubLimit = pGsubBase + pTTFile->tlens[ O_gsub ];
65 :
66 : // parse GSUB header
67 146 : const FT_Byte* pGsubHeader = pGsubBase;
68 146 : const sal_uLong nVersion = NEXT_Long( pGsubHeader );
69 146 : const sal_uInt16 nOfsScriptList = NEXT_UShort( pGsubHeader );
70 146 : const sal_uInt16 nOfsFeatureTable = NEXT_UShort( pGsubHeader );
71 146 : const sal_uInt16 nOfsLookupList = NEXT_UShort( pGsubHeader );
72 :
73 : // sanity check the GSUB header
74 146 : 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 146 : ReqFeatureTagList aReqFeatureTagList;
80 :
81 146 : aReqFeatureTagList.push_back( MKTAG("vert") );
82 :
83 : typedef std::vector<sal_uInt16> UshortList;
84 146 : UshortList aFeatureIndexList;
85 :
86 : // parse Script Table
87 146 : const FT_Byte* pScriptHeader = pGsubBase + nOfsScriptList;
88 146 : const sal_uInt16 nCntScript = NEXT_UShort( pScriptHeader );
89 146 : if( pGsubLimit < pScriptHeader + 6 * nCntScript )
90 0 : return false;
91 840 : for( sal_uInt16 nScriptIndex = 0; nScriptIndex < nCntScript; ++nScriptIndex )
92 : {
93 694 : const sal_uLong nTag = NEXT_Long( pScriptHeader ); // e.g. hani/arab/kana/hang
94 694 : const sal_uInt16 nOfsScriptTable= NEXT_UShort( pScriptHeader );
95 694 : if( (nTag != (sal_uInt16)nRequestedScript) && (nRequestedScript != 0) )
96 0 : continue;
97 :
98 694 : const FT_Byte* pScriptTable = pGsubBase + nOfsScriptList + nOfsScriptTable;
99 694 : if( pGsubLimit < pScriptTable + 4 )
100 0 : return false;
101 694 : const sal_uInt16 nDefaultLangsysOfs = NEXT_UShort( pScriptTable );
102 694 : const sal_uInt16 nCntLangSystem = NEXT_UShort( pScriptTable );
103 694 : sal_uInt16 nLangsysOffset = 0;
104 694 : if( pGsubLimit < pScriptTable + 6 * nCntLangSystem )
105 0 : return false;
106 1388 : for( sal_uInt16 nLangsysIndex = 0; nLangsysIndex < nCntLangSystem; ++nLangsysIndex )
107 : {
108 166 : const sal_uLong nInnerTag = NEXT_Long( pScriptTable ); // e.g. KOR/ZHS/ZHT/JAN
109 166 : const sal_uInt16 nOffset= NEXT_UShort( pScriptTable );
110 166 : if( (nInnerTag != (sal_uInt16)nRequestedLangsys) && (nRequestedLangsys != 0) )
111 0 : continue;
112 166 : nLangsysOffset = nOffset;
113 166 : break;
114 : }
115 :
116 694 : if( (nDefaultLangsysOfs != 0) && (nDefaultLangsysOfs != nLangsysOffset) )
117 : {
118 648 : const FT_Byte* pLangSys = pGsubBase + nOfsScriptList + nOfsScriptTable + nDefaultLangsysOfs;
119 648 : if( pGsubLimit < pLangSys + 6 )
120 0 : return false;
121 648 : /*const sal_uInt16 nLookupOrder =*/ NEXT_UShort( pLangSys );
122 648 : const sal_uInt16 nReqFeatureIdx = NEXT_UShort( pLangSys );
123 648 : const sal_uInt16 nCntFeature = NEXT_UShort( pLangSys );
124 648 : if( pGsubLimit < pLangSys + 2 * nCntFeature )
125 0 : return false;
126 648 : aFeatureIndexList.push_back( nReqFeatureIdx );
127 2518 : for( sal_uInt16 i = 0; i < nCntFeature; ++i )
128 : {
129 1870 : const sal_uInt16 nFeatureIndex = NEXT_UShort( pLangSys );
130 1870 : aFeatureIndexList.push_back( nFeatureIndex );
131 : }
132 : }
133 :
134 694 : if( nLangsysOffset != 0 )
135 : {
136 166 : const FT_Byte* pLangSys = pGsubBase + nOfsScriptList + nOfsScriptTable + nLangsysOffset;
137 166 : if( pGsubLimit < pLangSys + 6 )
138 0 : return false;
139 166 : /*const sal_uInt16 nLookupOrder =*/ NEXT_UShort( pLangSys );
140 166 : const sal_uInt16 nReqFeatureIdx = NEXT_UShort( pLangSys );
141 166 : const sal_uInt16 nCntFeature = NEXT_UShort( pLangSys );
142 166 : if( pGsubLimit < pLangSys + 2 * nCntFeature )
143 0 : return false;
144 166 : aFeatureIndexList.push_back( nReqFeatureIdx );
145 842 : for( sal_uInt16 i = 0; i < nCntFeature; ++i )
146 : {
147 676 : const sal_uInt16 nFeatureIndex = NEXT_UShort( pLangSys );
148 676 : aFeatureIndexList.push_back( nFeatureIndex );
149 : }
150 : }
151 : }
152 :
153 146 : if( aFeatureIndexList.empty() )
154 0 : return true;
155 :
156 146 : UshortList aLookupIndexList;
157 146 : UshortList aLookupOffsetList;
158 :
159 : // parse Feature Table
160 146 : const FT_Byte* pFeatureHeader = pGsubBase + nOfsFeatureTable;
161 146 : if( pGsubLimit < pFeatureHeader + 2 )
162 0 : return false;
163 146 : const sal_uInt16 nCntFeature = NEXT_UShort( pFeatureHeader );
164 146 : if( pGsubLimit < pFeatureHeader + 6 * nCntFeature )
165 0 : return false;
166 1852 : for( sal_uInt16 nFeatureIndex = 0; nFeatureIndex < nCntFeature; ++nFeatureIndex )
167 : {
168 1706 : const sal_uLong nTag = NEXT_Long( pFeatureHeader ); // e.g. locl/vert/trad/smpl/liga/fina/...
169 1706 : const sal_uInt16 nOffset= NEXT_UShort( pFeatureHeader );
170 :
171 : // ignore unneeded feature lookups
172 1706 : if( aFeatureIndexList[0] != nFeatureIndex ) // do not ignore the required feature
173 : {
174 1698 : const int nRequested = std::count( aFeatureIndexList.begin(), aFeatureIndexList.end(), nFeatureIndex);
175 1698 : if( !nRequested ) // ignore features that are not requested
176 282 : continue;
177 1416 : const int nAvailable = std::count( aReqFeatureTagList.begin(), aReqFeatureTagList.end(), nTag);
178 1416 : if( !nAvailable ) // some fonts don't provide features they request!
179 1406 : continue;
180 : }
181 :
182 18 : const FT_Byte* pFeatureTable = pGsubBase + nOfsFeatureTable + nOffset;
183 18 : if( pGsubLimit < pFeatureTable + 2 )
184 0 : return false;
185 18 : const sal_uInt16 nCntLookups = NEXT_UShort( pFeatureTable );
186 18 : if( pGsubLimit < pFeatureTable + 2 * nCntLookups )
187 0 : return false;
188 18 : 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 18 : if( nCntLookups == 0 ) //### hack needed by Mincho/Gothic/Mingliu/Simsun/...
194 18 : aLookupIndexList.push_back( 0 );
195 : }
196 :
197 : // parse Lookup List
198 146 : const FT_Byte* pLookupHeader = pGsubBase + nOfsLookupList;
199 146 : if( pGsubLimit < pLookupHeader + 2 )
200 0 : return false;
201 146 : const sal_uInt16 nCntLookupTable = NEXT_UShort( pLookupHeader );
202 146 : if( pGsubLimit < pLookupHeader + 2 * nCntLookupTable )
203 0 : return false;
204 3420 : for( sal_uInt16 nLookupIdx = 0; nLookupIdx < nCntLookupTable; ++nLookupIdx )
205 : {
206 3274 : const sal_uInt16 nOffset = NEXT_UShort( pLookupHeader );
207 3274 : if( std::count( aLookupIndexList.begin(), aLookupIndexList.end(), nLookupIdx ) )
208 18 : aLookupOffsetList.push_back( nOffset );
209 : }
210 :
211 146 : UshortList::const_iterator it = aLookupOffsetList.begin();
212 164 : for(; it != aLookupOffsetList.end(); ++it )
213 : {
214 18 : const sal_uInt16 nOfsLookupTable = *it;
215 18 : const FT_Byte* pLookupTable = pGsubBase + nOfsLookupList + nOfsLookupTable;
216 18 : if( pGsubLimit < pLookupTable + 6 )
217 0 : return false;
218 18 : const sal_uInt16 eLookupType = NEXT_UShort( pLookupTable );
219 18 : /*const sal_uInt16 eLookupFlag =*/ NEXT_UShort( pLookupTable );
220 18 : const sal_uInt16 nCntLookupSubtable = NEXT_UShort( pLookupTable );
221 :
222 : // TODO: switch( eLookupType )
223 18 : if( eLookupType != 1 ) // TODO: once we go beyond SingleSubst
224 16 : continue;
225 :
226 2 : if( pGsubLimit < pLookupTable + 2 * nCntLookupSubtable )
227 0 : return false;
228 4 : for( sal_uInt16 nSubTableIdx = 0; nSubTableIdx < nCntLookupSubtable; ++nSubTableIdx )
229 : {
230 2 : const sal_uInt16 nOfsSubLookupTable = NEXT_UShort( pLookupTable );
231 2 : const FT_Byte* pSubLookup = pGsubBase + nOfsLookupList + nOfsLookupTable + nOfsSubLookupTable;
232 2 : if( pGsubLimit < pSubLookup + 6 )
233 0 : return false;
234 2 : const sal_uInt16 nFmtSubstitution = NEXT_UShort( pSubLookup );
235 2 : const sal_uInt16 nOfsCoverage = NEXT_UShort( pSubLookup );
236 :
237 : typedef std::pair<sal_uInt16,sal_uInt16> GlyphSubst;
238 : typedef std::vector<GlyphSubst> SubstVector;
239 2 : SubstVector aSubstVector;
240 :
241 : const FT_Byte* pCoverage = pGsubBase
242 2 : + nOfsLookupList + nOfsLookupTable + nOfsSubLookupTable + nOfsCoverage;
243 2 : if( pGsubLimit < pCoverage + 4 )
244 0 : return false;
245 2 : const sal_uInt16 nFmtCoverage = NEXT_UShort( pCoverage );
246 2 : switch( nFmtCoverage )
247 : {
248 : case 1: // Coverage Format 1
249 : {
250 2 : const sal_uInt16 nCntGlyph = NEXT_UShort( pCoverage );
251 2 : if( pGsubLimit < pCoverage + 2 * nCntGlyph )
252 : // TODO? nCntGlyph = (pGsubLimit - pCoverage) / 2;
253 0 : return false;
254 2 : aSubstVector.reserve( nCntGlyph );
255 72 : for( sal_uInt16 i = 0; i < nCntGlyph; ++i )
256 : {
257 70 : const sal_uInt16 nGlyphId = NEXT_UShort( pCoverage );
258 70 : aSubstVector.push_back( GlyphSubst( nGlyphId, 0 ) );
259 : }
260 : }
261 2 : 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 2 : SubstVector::iterator subst_it( aSubstVector.begin() );
282 :
283 2 : 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 2 : const sal_uInt16 nCntGlyph = NEXT_UShort( pSubLookup );
297 72 : for( int i = nCntGlyph; (subst_it != aSubstVector.end()) && (--i>=0); ++subst_it )
298 : {
299 70 : if( pGsubLimit < pSubLookup + 2 )
300 0 : return false;
301 70 : const sal_uInt16 nGlyphId = NEXT_UShort( pSubLookup );
302 70 : (*subst_it).second = nGlyphId;
303 : }
304 : }
305 2 : break;
306 : }
307 :
308 : // now apply the glyph substitutions that have been collected in this subtable
309 2 : if( !aSubstVector.empty() )
310 : {
311 2 : GlyphSubstitution* pGSubstitution = new GlyphSubstitution;
312 2 : pTTFile->pGSubstitution = (void*)pGSubstitution;
313 72 : for( subst_it = aSubstVector.begin(); subst_it != aSubstVector.end(); ++subst_it )
314 70 : (*pGSubstitution)[ (*subst_it).first ] = (*subst_it).second;
315 : }
316 2 : }
317 : }
318 146 : return true;
319 : }
320 :
321 158 : void ReleaseGSUB(struct _TrueTypeFont* pTTFile)
322 : {
323 158 : GlyphSubstitution* pGlyphSubstitution = (GlyphSubstitution*)pTTFile->pGSubstitution;
324 158 : if( pGlyphSubstitution )
325 2 : delete pGlyphSubstitution;
326 158 : }
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 158 : int HasVerticalGSUB( struct _TrueTypeFont* pTTFile )
342 : {
343 158 : GlyphSubstitution* pGlyphSubstitution = (GlyphSubstitution*)pTTFile->pGSubstitution;
344 158 : return pGlyphSubstitution ? +1 : 0;
345 : }
346 :
347 : }
348 :
349 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|