/************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2008 by Sun Microsystems, Inc. * * OpenOffice.org - a multi-platform office productivity suite * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_vcl.hxx" #include "sft.hxx" #include "gsub.h" #include #include #include #include namespace vcl { typedef sal_uInt32 ULONG; typedef sal_uInt16 USHORT; typedef sal_uInt8 FT_Byte; typedef std::map 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 USHORT NEXT_UShort( const unsigned char* &p ) { USHORT 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 ULONG nVersion = NEXT_Long( pGsubHeader ); const USHORT nOfsScriptList = NEXT_UShort( pGsubHeader ); const USHORT nOfsFeatureTable = NEXT_UShort( pGsubHeader ); const USHORT 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 ReqFeatureTagList; ReqFeatureTagList aReqFeatureTagList; aReqFeatureTagList.push_back( MKTAG("vert") ); typedef std::vector UshortList; UshortList aFeatureIndexList; UshortList aFeatureOffsetList; // parse Script Table const FT_Byte* pScriptHeader = pGsubBase + nOfsScriptList; const USHORT nCntScript = NEXT_UShort( pScriptHeader ); if( pGsubLimit < pScriptHeader + 6 * nCntScript ) return false; for( USHORT nScriptIndex = 0; nScriptIndex < nCntScript; ++nScriptIndex ) { const ULONG nTag = NEXT_Long( pScriptHeader ); // e.g. hani/arab/kana/hang const USHORT nOfsScriptTable= NEXT_UShort( pScriptHeader ); if( (nTag != (USHORT)nRequestedScript) && (nRequestedScript != 0) ) continue; const FT_Byte* pScriptTable = pGsubBase + nOfsScriptList + nOfsScriptTable; if( pGsubLimit < pScriptTable + 4 ) return false; const USHORT nDefaultLangsysOfs = NEXT_UShort( pScriptTable ); const USHORT nCntLangSystem = NEXT_UShort( pScriptTable ); USHORT nLangsysOffset = 0; if( pGsubLimit < pScriptTable + 6 * nCntLangSystem ) return false; for( USHORT nLangsysIndex = 0; nLangsysIndex < nCntLangSystem; ++nLangsysIndex ) { const ULONG nInnerTag = NEXT_Long( pScriptTable ); // e.g. KOR/ZHS/ZHT/JAN const USHORT nOffset= NEXT_UShort( pScriptTable ); if( (nInnerTag != (USHORT)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 USHORT nLookupOrder =*/ NEXT_UShort( pLangSys ); const USHORT nReqFeatureIdx = NEXT_UShort( pLangSys ); const USHORT nCntFeature = NEXT_UShort( pLangSys ); if( pGsubLimit < pLangSys + 2 * nCntFeature ) return false; aFeatureIndexList.push_back( nReqFeatureIdx ); for( USHORT i = 0; i < nCntFeature; ++i ) { const USHORT 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 USHORT nLookupOrder =*/ NEXT_UShort( pLangSys ); const USHORT nReqFeatureIdx = NEXT_UShort( pLangSys ); const USHORT nCntFeature = NEXT_UShort( pLangSys ); if( pGsubLimit < pLangSys + 2 * nCntFeature ) return false; aFeatureIndexList.push_back( nReqFeatureIdx ); for( USHORT i = 0; i < nCntFeature; ++i ) { const USHORT 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 USHORT nCntFeature = NEXT_UShort( pFeatureHeader ); if( pGsubLimit < pFeatureHeader + 6 * nCntFeature ) return false; for( USHORT nFeatureIndex = 0; nFeatureIndex < nCntFeature; ++nFeatureIndex ) { const ULONG nTag = NEXT_Long( pFeatureHeader ); // e.g. locl/vert/trad/smpl/liga/fina/... const USHORT 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 USHORT nCntLookups = NEXT_UShort( pFeatureTable ); if( pGsubLimit < pFeatureTable + 2 * nCntLookups ) return false; for( USHORT i = 0; i < nCntLookups; ++i ) { const USHORT 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 USHORT nCntLookupTable = NEXT_UShort( pLookupHeader ); if( pGsubLimit < pLookupHeader + 2 * nCntLookupTable ) return false; for( USHORT nLookupIdx = 0; nLookupIdx < nCntLookupTable; ++nLookupIdx ) { const USHORT 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 USHORT nOfsLookupTable = *it; const FT_Byte* pLookupTable = pGsubBase + nOfsLookupList + nOfsLookupTable; if( pGsubLimit < pLookupTable + 6 ) return false; const USHORT eLookupType = NEXT_UShort( pLookupTable ); /*const USHORT eLookupFlag =*/ NEXT_UShort( pLookupTable ); const USHORT 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( USHORT nSubTableIdx = 0; nSubTableIdx < nCntLookupSubtable; ++nSubTableIdx ) { const USHORT nOfsSubLookupTable = NEXT_UShort( pLookupTable ); const FT_Byte* pSubLookup = pGsubBase + nOfsLookupList + nOfsLookupTable + nOfsSubLookupTable; if( pGsubLimit < pSubLookup + 6 ) return false; const USHORT nFmtSubstitution = NEXT_UShort( pSubLookup ); const USHORT nOfsCoverage = NEXT_UShort( pSubLookup ); typedef std::pair GlyphSubst; typedef std::vector SubstVector; SubstVector aSubstVector; const FT_Byte* pCoverage = pGsubBase + nOfsLookupList + nOfsLookupTable + nOfsSubLookupTable + nOfsCoverage; if( pGsubLimit < pCoverage + 4 ) return false; const USHORT nFmtCoverage = NEXT_UShort( pCoverage ); switch( nFmtCoverage ) { case 1: // Coverage Format 1 { const USHORT nCntGlyph = NEXT_UShort( pCoverage ); if( pGsubLimit < pCoverage + 2 * nCntGlyph ) // TODO? nCntGlyph = (pGsubLimit - pCoverage) / 2; return false; aSubstVector.reserve( nCntGlyph ); for( USHORT i = 0; i < nCntGlyph; ++i ) { const USHORT nGlyphId = NEXT_UShort( pCoverage ); aSubstVector.push_back( GlyphSubst( nGlyphId, 0 ) ); } } break; case 2: // Coverage Format 2 { const USHORT nCntRange = NEXT_UShort( pCoverage ); if( pGsubLimit < pCoverage + 6 * nCntRange ) // TODO? nCntGlyph = (pGsubLimit - pCoverage) / 6; return false; for( int i = nCntRange; --i >= 0; ) { const USHORT nGlyph0 = NEXT_UShort( pCoverage ); const USHORT nGlyph1 = NEXT_UShort( pCoverage ); const USHORT nStartCoverageIndex = NEXT_UShort( pCoverage ); OSL_ENSURE( aSubstVector.size() == nStartCoverageIndex, "coverage index mismatch"); (void)nStartCoverageIndex; for( USHORT j = nGlyph0; j <= nGlyph1; ++j ) aSubstVector.push_back( GlyphSubst( j, 0 ) ); } } break; } SubstVector::iterator subst_it( aSubstVector.begin() ); switch( nFmtSubstitution ) { case 1: // Single Substitution Format 1 { const USHORT 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 USHORT nCntGlyph = NEXT_UShort( pSubLookup ); for( int i = nCntGlyph; (subst_it != aSubstVector.end()) && (--i>=0); ++subst_it ) { if( pGsubLimit < pSubLookup + 2 ) return false; const USHORT 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(nGlyph) ) ); if( it != pGlyphSubstitution->end() ) nGlyph = (*it).second; } return nGlyph; } int HasVerticalGSUB( struct _TrueTypeFont* pTTFile ) { GlyphSubstitution* pGlyphSubstitution = (GlyphSubstitution*)pTTFile->pGSubstitution; return pGlyphSubstitution ? +1 : 0; } }