blob: 796116ff0dfe1f2fb304dfc792650a85136d39e9 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_vcl.hxx"
#include "sft.hxx"
#include "gsub.h"
#include <osl/diagnose.h>
#include <vector>
#include <map>
#include <algorithm>
namespace vcl
{
typedef sal_uIntPtr sal_uLong;
typedef sal_uInt8 FT_Byte;
typedef std::map<sal_uInt16,sal_uInt16> GlyphSubstitution;
inline long NEXT_Long( const unsigned char* &p )
{
long nVal = (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3];
p += 4;
return nVal;
}
inline sal_uInt16 NEXT_UShort( const unsigned char* &p )
{
sal_uInt16 nVal = (p[0]<<8) + p[1];
p += 2;
return nVal;
}
#define MKTAG(s) ((((((s[0]<<8)+s[1])<<8)+s[2])<<8)+s[3])
int ReadGSUB( struct _TrueTypeFont* pTTFile,
int nRequestedScript, int nRequestedLangsys )
{
const FT_Byte* pGsubBase = (FT_Byte*)pTTFile->tables[ O_gsub ];
if( !pGsubBase )
return -1;
// #129682# check offsets inside GSUB table
const FT_Byte* pGsubLimit = pGsubBase + pTTFile->tlens[ O_gsub ];
// parse GSUB header
const FT_Byte* pGsubHeader = pGsubBase;
const sal_uLong nVersion = NEXT_Long( pGsubHeader );
const sal_uInt16 nOfsScriptList = NEXT_UShort( pGsubHeader );
const sal_uInt16 nOfsFeatureTable = NEXT_UShort( pGsubHeader );
const sal_uInt16 nOfsLookupList = NEXT_UShort( pGsubHeader );
// sanity check the GSUB header
if( nVersion != 0x00010000 )
if( nVersion != 0x00001000 ) // workaround for SunBatang etc.
return -1; // unknown format or broken
typedef std::vector<sal_uLong> ReqFeatureTagList;
ReqFeatureTagList aReqFeatureTagList;
aReqFeatureTagList.push_back( MKTAG("vert") );
typedef std::vector<sal_uInt16> UshortList;
UshortList aFeatureIndexList;
UshortList aFeatureOffsetList;
// parse Script Table
const FT_Byte* pScriptHeader = pGsubBase + nOfsScriptList;
const sal_uInt16 nCntScript = NEXT_UShort( pScriptHeader );
if( pGsubLimit < pScriptHeader + 6 * nCntScript )
return false;
for( sal_uInt16 nScriptIndex = 0; nScriptIndex < nCntScript; ++nScriptIndex )
{
const sal_uLong nTag = NEXT_Long( pScriptHeader ); // e.g. hani/arab/kana/hang
const sal_uInt16 nOfsScriptTable= NEXT_UShort( pScriptHeader );
if( (nTag != (sal_uInt16)nRequestedScript) && (nRequestedScript != 0) )
continue;
const FT_Byte* pScriptTable = pGsubBase + nOfsScriptList + nOfsScriptTable;
if( pGsubLimit < pScriptTable + 4 )
return false;
const sal_uInt16 nDefaultLangsysOfs = NEXT_UShort( pScriptTable );
const sal_uInt16 nCntLangSystem = NEXT_UShort( pScriptTable );
sal_uInt16 nLangsysOffset = 0;
if( pGsubLimit < pScriptTable + 6 * nCntLangSystem )
return false;
for( sal_uInt16 nLangsysIndex = 0; nLangsysIndex < nCntLangSystem; ++nLangsysIndex )
{
const sal_uLong nInnerTag = NEXT_Long( pScriptTable ); // e.g. KOR/ZHS/ZHT/JAN
const sal_uInt16 nOffset= NEXT_UShort( pScriptTable );
if( (nInnerTag != (sal_uInt16)nRequestedLangsys) && (nRequestedLangsys != 0) )
continue;
nLangsysOffset = nOffset;
break;
}
if( (nDefaultLangsysOfs != 0) && (nDefaultLangsysOfs != nLangsysOffset) )
{
const FT_Byte* pLangSys = pGsubBase + nOfsScriptList + nOfsScriptTable + nDefaultLangsysOfs;
if( pGsubLimit < pLangSys + 6 )
return false;
/*const sal_uInt16 nLookupOrder =*/ NEXT_UShort( pLangSys );
const sal_uInt16 nReqFeatureIdx = NEXT_UShort( pLangSys );
const sal_uInt16 nCntFeature = NEXT_UShort( pLangSys );
if( pGsubLimit < pLangSys + 2 * nCntFeature )
return false;
aFeatureIndexList.push_back( nReqFeatureIdx );
for( sal_uInt16 i = 0; i < nCntFeature; ++i )
{
const sal_uInt16 nFeatureIndex = NEXT_UShort( pLangSys );
aFeatureIndexList.push_back( nFeatureIndex );
}
}
if( nLangsysOffset != 0 )
{
const FT_Byte* pLangSys = pGsubBase + nOfsScriptList + nOfsScriptTable + nLangsysOffset;
if( pGsubLimit < pLangSys + 6 )
return false;
/*const sal_uInt16 nLookupOrder =*/ NEXT_UShort( pLangSys );
const sal_uInt16 nReqFeatureIdx = NEXT_UShort( pLangSys );
const sal_uInt16 nCntFeature = NEXT_UShort( pLangSys );
if( pGsubLimit < pLangSys + 2 * nCntFeature )
return false;
aFeatureIndexList.push_back( nReqFeatureIdx );
for( sal_uInt16 i = 0; i < nCntFeature; ++i )
{
const sal_uInt16 nFeatureIndex = NEXT_UShort( pLangSys );
aFeatureIndexList.push_back( nFeatureIndex );
}
}
}
if( !aFeatureIndexList.size() )
return true;
UshortList aLookupIndexList;
UshortList aLookupOffsetList;
// parse Feature Table
const FT_Byte* pFeatureHeader = pGsubBase + nOfsFeatureTable;
if( pGsubLimit < pFeatureHeader + 2 )
return false;
const sal_uInt16 nCntFeature = NEXT_UShort( pFeatureHeader );
if( pGsubLimit < pFeatureHeader + 6 * nCntFeature )
return false;
for( sal_uInt16 nFeatureIndex = 0; nFeatureIndex < nCntFeature; ++nFeatureIndex )
{
const sal_uLong nTag = NEXT_Long( pFeatureHeader ); // e.g. locl/vert/trad/smpl/liga/fina/...
const sal_uInt16 nOffset= NEXT_UShort( pFeatureHeader );
// ignore unneeded feature lookups
if( aFeatureIndexList[0] != nFeatureIndex ) // do not ignore the required feature
{
const int nRequested = std::count( aFeatureIndexList.begin(), aFeatureIndexList.end(), nFeatureIndex);
if( !nRequested ) // ignore features that are not requested
continue;
const int nAvailable = std::count( aReqFeatureTagList.begin(), aReqFeatureTagList.end(), nTag);
if( !nAvailable ) // some fonts don't provide features they request!
continue;
}
const FT_Byte* pFeatureTable = pGsubBase + nOfsFeatureTable + nOffset;
if( pGsubLimit < pFeatureTable + 2 )
return false;
const sal_uInt16 nCntLookups = NEXT_UShort( pFeatureTable );
if( pGsubLimit < pFeatureTable + 2 * nCntLookups )
return false;
for( sal_uInt16 i = 0; i < nCntLookups; ++i )
{
const sal_uInt16 nLookupIndex = NEXT_UShort( pFeatureTable );
aLookupIndexList.push_back( nLookupIndex );
}
if( nCntLookups == 0 ) //### hack needed by Mincho/Gothic/Mingliu/Simsun/...
aLookupIndexList.push_back( 0 );
}
// parse Lookup List
const FT_Byte* pLookupHeader = pGsubBase + nOfsLookupList;
if( pGsubLimit < pLookupHeader + 2 )
return false;
const sal_uInt16 nCntLookupTable = NEXT_UShort( pLookupHeader );
if( pGsubLimit < pLookupHeader + 2 * nCntLookupTable )
return false;
for( sal_uInt16 nLookupIdx = 0; nLookupIdx < nCntLookupTable; ++nLookupIdx )
{
const sal_uInt16 nOffset = NEXT_UShort( pLookupHeader );
if( std::count( aLookupIndexList.begin(), aLookupIndexList.end(), nLookupIdx ) )
aLookupOffsetList.push_back( nOffset );
}
UshortList::const_iterator it = aLookupOffsetList.begin();
for(; it != aLookupOffsetList.end(); ++it )
{
const sal_uInt16 nOfsLookupTable = *it;
const FT_Byte* pLookupTable = pGsubBase + nOfsLookupList + nOfsLookupTable;
if( pGsubLimit < pLookupTable + 6 )
return false;
const sal_uInt16 eLookupType = NEXT_UShort( pLookupTable );
/*const sal_uInt16 eLookupFlag =*/ NEXT_UShort( pLookupTable );
const sal_uInt16 nCntLookupSubtable = NEXT_UShort( pLookupTable );
// TODO: switch( eLookupType )
if( eLookupType != 1 ) // TODO: once we go beyond SingleSubst
continue;
if( pGsubLimit < pLookupTable + 2 * nCntLookupSubtable )
return false;
for( sal_uInt16 nSubTableIdx = 0; nSubTableIdx < nCntLookupSubtable; ++nSubTableIdx )
{
const sal_uInt16 nOfsSubLookupTable = NEXT_UShort( pLookupTable );
const FT_Byte* pSubLookup = pGsubBase + nOfsLookupList + nOfsLookupTable + nOfsSubLookupTable;
if( pGsubLimit < pSubLookup + 6 )
return false;
const sal_uInt16 nFmtSubstitution = NEXT_UShort( pSubLookup );
const sal_uInt16 nOfsCoverage = NEXT_UShort( pSubLookup );
typedef std::pair<sal_uInt16,sal_uInt16> GlyphSubst;
typedef std::vector<GlyphSubst> SubstVector;
SubstVector aSubstVector;
const FT_Byte* pCoverage = pGsubBase
+ nOfsLookupList + nOfsLookupTable + nOfsSubLookupTable + nOfsCoverage;
if( pGsubLimit < pCoverage + 4 )
return false;
const sal_uInt16 nFmtCoverage = NEXT_UShort( pCoverage );
switch( nFmtCoverage )
{
case 1: // Coverage Format 1
{
const sal_uInt16 nCntGlyph = NEXT_UShort( pCoverage );
if( pGsubLimit < pCoverage + 2 * nCntGlyph )
// TODO? nCntGlyph = (pGsubLimit - pCoverage) / 2;
return false;
aSubstVector.reserve( nCntGlyph );
for( sal_uInt16 i = 0; i < nCntGlyph; ++i )
{
const sal_uInt16 nGlyphId = NEXT_UShort( pCoverage );
aSubstVector.push_back( GlyphSubst( nGlyphId, 0 ) );
}
}
break;
case 2: // Coverage Format 2
{
const sal_uInt16 nCntRange = NEXT_UShort( pCoverage );
if( pGsubLimit < pCoverage + 6 * nCntRange )
// TODO? nCntGlyph = (pGsubLimit - pCoverage) / 6;
return false;
for( int i = nCntRange; --i >= 0; )
{
const sal_uInt32 nGlyph0 = NEXT_UShort( pCoverage );
const sal_uInt32 nGlyph1 = NEXT_UShort( pCoverage );
const sal_uInt16 nCovIdx = NEXT_UShort( pCoverage );
for( sal_uInt32 j = nGlyph0; j <= nGlyph1; ++j )
aSubstVector.push_back( GlyphSubst( static_cast<sal_uInt16>(j + nCovIdx), 0 ) );
}
}
break;
}
SubstVector::iterator subst_it( aSubstVector.begin() );
switch( nFmtSubstitution )
{
case 1: // Single Substitution Format 1
{
const sal_uInt16 nDeltaGlyphId = NEXT_UShort( pSubLookup );
for(; subst_it != aSubstVector.end(); ++subst_it )
(*subst_it).second = (*subst_it).first + nDeltaGlyphId;
}
break;
case 2: // Single Substitution Format 2
{
const sal_uInt16 nCntGlyph = NEXT_UShort( pSubLookup );
for( int i = nCntGlyph; (subst_it != aSubstVector.end()) && (--i>=0); ++subst_it )
{
if( pGsubLimit < pSubLookup + 2 )
return false;
const sal_uInt16 nGlyphId = NEXT_UShort( pSubLookup );
(*subst_it).second = nGlyphId;
}
}
break;
}
// now apply the glyph substitutions that have been collected in this subtable
if( aSubstVector.size() > 0 )
{
GlyphSubstitution* pGSubstitution = new GlyphSubstitution;
pTTFile->pGSubstitution = (void*)pGSubstitution;
for( subst_it = aSubstVector.begin(); subst_it != aSubstVector.end(); ++subst_it )
(*pGSubstitution)[ (*subst_it).first ] = (*subst_it).second;
}
}
}
return true;
}
void ReleaseGSUB(struct _TrueTypeFont* pTTFile)
{
GlyphSubstitution* pGlyphSubstitution = (GlyphSubstitution*)pTTFile->pGSubstitution;
if( pGlyphSubstitution )
delete pGlyphSubstitution;
}
int UseGSUB( struct _TrueTypeFont* pTTFile, int nGlyph, int /*wmode*/ )
{
GlyphSubstitution* pGlyphSubstitution = (GlyphSubstitution*)pTTFile->pGSubstitution;
if( pGlyphSubstitution != 0 )
{
GlyphSubstitution::const_iterator it( pGlyphSubstitution->find( sal::static_int_cast<sal_uInt16>(nGlyph) ) );
if( it != pGlyphSubstitution->end() )
nGlyph = (*it).second;
}
return nGlyph;
}
int HasVerticalGSUB( struct _TrueTypeFont* pTTFile )
{
GlyphSubstitution* pGlyphSubstitution = (GlyphSubstitution*)pTTFile->pGSubstitution;
return pGlyphSubstitution ? +1 : 0;
}
}