build fix: no comphelper/profilezone.hxx in this branch
[LibreOffice.git] / vcl / source / fontsubset / gsub.cxx
blob52e6d8cc0a764202092a8bfef1192ae855b38ea0
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include "sft.hxx"
22 #include "gsub.h"
24 #include <osl/diagnose.h>
26 #include <vector>
27 #include <map>
28 #include <algorithm>
30 namespace vcl
33 typedef sal_uInt8 FT_Byte;
35 typedef std::map<sal_uInt16,sal_uInt16> GlyphSubstitution;
37 inline sal_uInt32 NEXT_Long( const unsigned char* &p )
39 sal_uInt32 nVal = (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3];
40 p += 4;
41 return nVal;
44 inline sal_uInt16 NEXT_UShort( const unsigned char* &p )
46 sal_uInt16 nVal = (p[0]<<8) + p[1];
47 p += 2;
48 return nVal;
51 #define MKTAG(s) ((((((s[0]<<8)+s[1])<<8)+s[2])<<8)+s[3])
53 bool ReadGSUB( struct TrueTypeFont* pTTFile,
54 int nRequestedScript, int nRequestedLangsys )
56 const FT_Byte* pGsubBase = pTTFile->tables[ O_gsub ];
57 if( !pGsubBase )
58 return false;
60 // #129682# check offsets inside GSUB table
61 const FT_Byte* pGsubLimit = pGsubBase + pTTFile->tlens[ O_gsub ];
63 // parse GSUB header
64 const FT_Byte* pGsubHeader = pGsubBase;
65 const sal_uInt32 nVersion = NEXT_Long( pGsubHeader );
66 const sal_uInt16 nOfsScriptList = NEXT_UShort( pGsubHeader );
67 const sal_uInt16 nOfsFeatureTable = NEXT_UShort( pGsubHeader );
68 const sal_uInt16 nOfsLookupList = NEXT_UShort( pGsubHeader );
70 // sanity check the GSUB header
71 if( nVersion != 0x00010000 )
72 if( nVersion != 0x00001000 ) // workaround for SunBatang etc.
73 return false; // unknown format or broken
75 std::vector<sal_uInt32> aReqFeatureTagList;
77 aReqFeatureTagList.push_back( MKTAG("vert") );
79 std::vector<sal_uInt16> aFeatureIndexList;
81 // parse Script Table
82 const FT_Byte* pScriptHeader = pGsubBase + nOfsScriptList;
83 const sal_uInt16 nCntScript = NEXT_UShort( pScriptHeader );
84 if( pGsubLimit < pScriptHeader + 6 * nCntScript )
85 return false;
86 for( sal_uInt16 nScriptIndex = 0; nScriptIndex < nCntScript; ++nScriptIndex )
88 const sal_uInt32 nTag = NEXT_Long( pScriptHeader ); // e.g. hani/arab/kana/hang
89 const sal_uInt16 nOfsScriptTable= NEXT_UShort( pScriptHeader );
90 if( (nTag != (sal_uInt16)nRequestedScript) && (nRequestedScript != 0) )
91 continue;
93 const FT_Byte* pScriptTable = pGsubBase + nOfsScriptList + nOfsScriptTable;
94 if( pGsubLimit < pScriptTable + 4 )
95 return false;
96 const sal_uInt16 nDefaultLangsysOfs = NEXT_UShort( pScriptTable );
97 const sal_uInt16 nCntLangSystem = NEXT_UShort( pScriptTable );
98 sal_uInt16 nLangsysOffset = 0;
99 if( pGsubLimit < pScriptTable + 6 * nCntLangSystem )
100 return false;
101 for( sal_uInt16 nLangsysIndex = 0; nLangsysIndex < nCntLangSystem; ++nLangsysIndex )
103 const sal_uInt32 nInnerTag = NEXT_Long( pScriptTable ); // e.g. KOR/ZHS/ZHT/JAN
104 const sal_uInt16 nOffset= NEXT_UShort( pScriptTable );
105 if( (nInnerTag != (sal_uInt16)nRequestedLangsys) && (nRequestedLangsys != 0) )
106 continue;
107 nLangsysOffset = nOffset;
108 break;
111 if( (nDefaultLangsysOfs != 0) && (nDefaultLangsysOfs != nLangsysOffset) )
113 const FT_Byte* pLangSys = pGsubBase + nOfsScriptList + nOfsScriptTable + nDefaultLangsysOfs;
114 if( pGsubLimit < pLangSys + 6 )
115 return false;
116 /*const sal_uInt16 nLookupOrder =*/ NEXT_UShort( pLangSys );
117 const sal_uInt16 nReqFeatureIdx = NEXT_UShort( pLangSys );
118 const sal_uInt16 nCntFeature = NEXT_UShort( pLangSys );
119 if( pGsubLimit < pLangSys + 2 * nCntFeature )
120 return false;
121 aFeatureIndexList.push_back( nReqFeatureIdx );
122 for( sal_uInt16 i = 0; i < nCntFeature; ++i )
124 const sal_uInt16 nFeatureIndex = NEXT_UShort( pLangSys );
125 aFeatureIndexList.push_back( nFeatureIndex );
129 if( nLangsysOffset != 0 )
131 const FT_Byte* pLangSys = pGsubBase + nOfsScriptList + nOfsScriptTable + nLangsysOffset;
132 if( pGsubLimit < pLangSys + 6 )
133 return false;
134 /*const sal_uInt16 nLookupOrder =*/ NEXT_UShort( pLangSys );
135 const sal_uInt16 nReqFeatureIdx = NEXT_UShort( pLangSys );
136 const sal_uInt16 nCntFeature = NEXT_UShort( pLangSys );
137 if( pGsubLimit < pLangSys + 2 * nCntFeature )
138 return false;
139 aFeatureIndexList.push_back( nReqFeatureIdx );
140 for( sal_uInt16 i = 0; i < nCntFeature; ++i )
142 const sal_uInt16 nFeatureIndex = NEXT_UShort( pLangSys );
143 aFeatureIndexList.push_back( nFeatureIndex );
148 if( aFeatureIndexList.empty() )
149 return true;
151 std::vector<sal_uInt16> aLookupIndexList;
152 std::vector<sal_uInt16> aLookupOffsetList;
154 // parse Feature Table
155 const FT_Byte* pFeatureHeader = pGsubBase + nOfsFeatureTable;
156 if( pGsubLimit < pFeatureHeader + 2 )
157 return false;
158 const sal_uInt16 nCntFeature = NEXT_UShort( pFeatureHeader );
159 if( pGsubLimit < pFeatureHeader + 6 * nCntFeature )
160 return false;
161 for( sal_uInt16 nFeatureIndex = 0; nFeatureIndex < nCntFeature; ++nFeatureIndex )
163 const sal_uInt32 nTag = NEXT_Long( pFeatureHeader ); // e.g. locl/vert/trad/smpl/liga/fina/...
164 const sal_uInt16 nOffset= NEXT_UShort( pFeatureHeader );
166 // ignore unneeded feature lookups
167 if( aFeatureIndexList[0] != nFeatureIndex ) // do not ignore the required feature
169 const int nRequested = std::count( aFeatureIndexList.begin(), aFeatureIndexList.end(), nFeatureIndex);
170 if( !nRequested ) // ignore features that are not requested
171 continue;
172 const int nAvailable = std::count( aReqFeatureTagList.begin(), aReqFeatureTagList.end(), nTag);
173 if( !nAvailable ) // some fonts don't provide features they request!
174 continue;
177 const FT_Byte* pFeatureTable = pGsubBase + nOfsFeatureTable + nOffset;
178 if( pGsubLimit < pFeatureTable + 2 )
179 return false;
180 const sal_uInt16 nCntLookups = NEXT_UShort( pFeatureTable );
181 if( pGsubLimit < pFeatureTable + 2 * nCntLookups )
182 return false;
183 for( sal_uInt16 i = 0; i < nCntLookups; ++i )
185 const sal_uInt16 nLookupIndex = NEXT_UShort( pFeatureTable );
186 aLookupIndexList.push_back( nLookupIndex );
188 if( nCntLookups == 0 ) //### hack needed by Mincho/Gothic/Mingliu/Simsun/...
189 aLookupIndexList.push_back( 0 );
192 // parse Lookup List
193 const FT_Byte* pLookupHeader = pGsubBase + nOfsLookupList;
194 if( pGsubLimit < pLookupHeader + 2 )
195 return false;
196 const sal_uInt16 nCntLookupTable = NEXT_UShort( pLookupHeader );
197 if( pGsubLimit < pLookupHeader + 2 * nCntLookupTable )
198 return false;
199 for( sal_uInt16 nLookupIdx = 0; nLookupIdx < nCntLookupTable; ++nLookupIdx )
201 const sal_uInt16 nOffset = NEXT_UShort( pLookupHeader );
202 if( std::count( aLookupIndexList.begin(), aLookupIndexList.end(), nLookupIdx ) )
203 aLookupOffsetList.push_back( nOffset );
206 std::vector<sal_uInt16>::const_iterator it = aLookupOffsetList.begin();
207 for(; it != aLookupOffsetList.end(); ++it )
209 const sal_uInt16 nOfsLookupTable = *it;
210 const FT_Byte* pLookupTable = pGsubBase + nOfsLookupList + nOfsLookupTable;
211 if( pGsubLimit < pLookupTable + 6 )
212 return false;
213 const sal_uInt16 eLookupType = NEXT_UShort( pLookupTable );
214 /*const sal_uInt16 eLookupFlag =*/ NEXT_UShort( pLookupTable );
215 const sal_uInt16 nCntLookupSubtable = NEXT_UShort( pLookupTable );
217 // TODO: switch( eLookupType )
218 if( eLookupType != 1 ) // TODO: once we go beyond SingleSubst
219 continue;
221 if( pGsubLimit < pLookupTable + 2 * nCntLookupSubtable )
222 return false;
223 for( sal_uInt16 nSubTableIdx = 0; nSubTableIdx < nCntLookupSubtable; ++nSubTableIdx )
225 const sal_uInt16 nOfsSubLookupTable = NEXT_UShort( pLookupTable );
226 const FT_Byte* pSubLookup = pGsubBase + nOfsLookupList + nOfsLookupTable + nOfsSubLookupTable;
227 if( pGsubLimit < pSubLookup + 6 )
228 return false;
229 const sal_uInt16 nFmtSubstitution = NEXT_UShort( pSubLookup );
230 const sal_uInt16 nOfsCoverage = NEXT_UShort( pSubLookup );
232 typedef std::pair<sal_uInt16,sal_uInt16> GlyphSubst;
233 std::vector<GlyphSubst> aSubstVector;
235 const FT_Byte* pCoverage = pGsubBase
236 + nOfsLookupList + nOfsLookupTable + nOfsSubLookupTable + nOfsCoverage;
237 if( pGsubLimit < pCoverage + 4 )
238 return false;
239 const sal_uInt16 nFmtCoverage = NEXT_UShort( pCoverage );
240 switch( nFmtCoverage )
242 case 1: // Coverage Format 1
244 const sal_uInt16 nCntGlyph = NEXT_UShort( pCoverage );
245 if( pGsubLimit < pCoverage + 2 * nCntGlyph )
246 // TODO? nCntGlyph = (pGsubLimit - pCoverage) / 2;
247 return false;
248 aSubstVector.reserve( nCntGlyph );
249 for( sal_uInt16 i = 0; i < nCntGlyph; ++i )
251 const sal_uInt16 nGlyphId = NEXT_UShort( pCoverage );
252 aSubstVector.push_back( GlyphSubst( nGlyphId, 0 ) );
255 break;
257 case 2: // Coverage Format 2
259 const sal_uInt16 nCntRange = NEXT_UShort( pCoverage );
260 if( pGsubLimit < pCoverage + 6 * nCntRange )
261 // TODO? nCntGlyph = (pGsubLimit - pCoverage) / 6;
262 return false;
263 for( int i = nCntRange; --i >= 0; )
265 const sal_uInt32 nGlyph0 = NEXT_UShort( pCoverage );
266 const sal_uInt32 nGlyph1 = NEXT_UShort( pCoverage );
267 const sal_uInt16 nCovIdx = NEXT_UShort( pCoverage );
268 for( sal_uInt32 j = nGlyph0; j <= nGlyph1; ++j )
269 aSubstVector.push_back( GlyphSubst( static_cast<sal_uInt16>(j + nCovIdx), 0 ) );
272 break;
275 std::vector<GlyphSubst>::iterator subst_it( aSubstVector.begin() );
277 switch( nFmtSubstitution )
279 case 1: // Single Substitution Format 1
281 const sal_uInt16 nDeltaGlyphId = NEXT_UShort( pSubLookup );
283 for(; subst_it != aSubstVector.end(); ++subst_it )
284 (*subst_it).second = (*subst_it).first + nDeltaGlyphId;
286 break;
288 case 2: // Single Substitution Format 2
290 const sal_uInt16 nCntGlyph = NEXT_UShort( pSubLookup );
291 for( int i = nCntGlyph; (subst_it != aSubstVector.end()) && (--i>=0); ++subst_it )
293 if( pGsubLimit < pSubLookup + 2 )
294 return false;
295 const sal_uInt16 nGlyphId = NEXT_UShort( pSubLookup );
296 (*subst_it).second = nGlyphId;
299 break;
302 // now apply the glyph substitutions that have been collected in this subtable
303 if( !aSubstVector.empty() )
305 GlyphSubstitution* pGSubstitution = new GlyphSubstitution;
306 pTTFile->pGSubstitution = static_cast<void*>(pGSubstitution);
307 for( subst_it = aSubstVector.begin(); subst_it != aSubstVector.end(); ++subst_it )
308 (*pGSubstitution)[ (*subst_it).first ] = (*subst_it).second;
312 return true;
315 void ReleaseGSUB(struct TrueTypeFont* pTTFile)
317 GlyphSubstitution* pGlyphSubstitution = static_cast<GlyphSubstitution*>(pTTFile->pGSubstitution);
318 delete pGlyphSubstitution;
321 int UseGSUB( struct TrueTypeFont* pTTFile, int nGlyph )
323 GlyphSubstitution* pGlyphSubstitution = static_cast<GlyphSubstitution*>(pTTFile->pGSubstitution);
324 if( pGlyphSubstitution != nullptr )
326 GlyphSubstitution::const_iterator it( pGlyphSubstitution->find( sal::static_int_cast<sal_uInt16>(nGlyph) ) );
327 if( it != pGlyphSubstitution->end() )
328 nGlyph = (*it).second;
331 return nGlyph;
334 int HasVerticalGSUB( struct TrueTypeFont* pTTFile )
336 GlyphSubstitution* pGlyphSubstitution = static_cast<GlyphSubstitution*>(pTTFile->pGSubstitution);
337 return pGlyphSubstitution ? +1 : 0;
342 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */