1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
24 #include <osl/diagnose.h>
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];
44 inline sal_uInt16
NEXT_UShort( const unsigned char* &p
)
46 sal_uInt16 nVal
= (p
[0]<<8) + p
[1];
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
];
60 // #129682# check offsets inside GSUB table
61 const FT_Byte
* pGsubLimit
= pGsubBase
+ pTTFile
->tlens
[ O_gsub
];
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
;
82 const FT_Byte
* pScriptHeader
= pGsubBase
+ nOfsScriptList
;
83 const sal_uInt16 nCntScript
= NEXT_UShort( pScriptHeader
);
84 if( pGsubLimit
< pScriptHeader
+ 6 * nCntScript
)
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) )
93 const FT_Byte
* pScriptTable
= pGsubBase
+ nOfsScriptList
+ nOfsScriptTable
;
94 if( pGsubLimit
< pScriptTable
+ 4 )
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
)
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) )
107 nLangsysOffset
= nOffset
;
111 if( (nDefaultLangsysOfs
!= 0) && (nDefaultLangsysOfs
!= nLangsysOffset
) )
113 const FT_Byte
* pLangSys
= pGsubBase
+ nOfsScriptList
+ nOfsScriptTable
+ nDefaultLangsysOfs
;
114 if( pGsubLimit
< pLangSys
+ 6 )
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
)
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 )
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
)
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() )
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 )
158 const sal_uInt16 nCntFeature
= NEXT_UShort( pFeatureHeader
);
159 if( pGsubLimit
< pFeatureHeader
+ 6 * nCntFeature
)
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
172 const int nAvailable
= std::count( aReqFeatureTagList
.begin(), aReqFeatureTagList
.end(), nTag
);
173 if( !nAvailable
) // some fonts don't provide features they request!
177 const FT_Byte
* pFeatureTable
= pGsubBase
+ nOfsFeatureTable
+ nOffset
;
178 if( pGsubLimit
< pFeatureTable
+ 2 )
180 const sal_uInt16 nCntLookups
= NEXT_UShort( pFeatureTable
);
181 if( pGsubLimit
< pFeatureTable
+ 2 * nCntLookups
)
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 );
193 const FT_Byte
* pLookupHeader
= pGsubBase
+ nOfsLookupList
;
194 if( pGsubLimit
< pLookupHeader
+ 2 )
196 const sal_uInt16 nCntLookupTable
= NEXT_UShort( pLookupHeader
);
197 if( pGsubLimit
< pLookupHeader
+ 2 * nCntLookupTable
)
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 )
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
221 if( pGsubLimit
< pLookupTable
+ 2 * nCntLookupSubtable
)
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 )
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 )
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;
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 ) );
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;
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 ) );
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
;
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 )
295 const sal_uInt16 nGlyphId
= NEXT_UShort( pSubLookup
);
296 (*subst_it
).second
= nGlyphId
;
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
;
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
;
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: */