/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- *//* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * 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 . */#include"sft.hxx"#include"gsub.h"#include<osl/diagnose.h>#include<vector>#include<map>#include<algorithm>namespacevcl{typedefsal_uIntPtrsal_uLong;typedefsal_uInt8FT_Byte;typedefstd::map<sal_uInt16,sal_uInt16>GlyphSubstitution;inlinesal_uInt32NEXT_Long(constunsignedchar*&p){sal_uInt32nVal=(p[0]<<24)+(p[1]<<16)+(p[2]<<8)+p[3];p+=4;returnnVal;}inlinesal_uInt16NEXT_UShort(constunsignedchar*&p){sal_uInt16nVal=(p[0]<<8)+p[1];p+=2;returnnVal;}#define MKTAG(s) ((((((s[0]<<8)+s[1])<<8)+s[2])<<8)+s[3])intReadGSUB(struct_TrueTypeFont*pTTFile,intnRequestedScript,intnRequestedLangsys){constFT_Byte*pGsubBase=(FT_Byte*)pTTFile->tables[O_gsub];if(!pGsubBase)return-1;// #129682# check offsets inside GSUB tableconstFT_Byte*pGsubLimit=pGsubBase+pTTFile->tlens[O_gsub];// parse GSUB headerconstFT_Byte*pGsubHeader=pGsubBase;constsal_uLongnVersion=NEXT_Long(pGsubHeader);constsal_uInt16nOfsScriptList=NEXT_UShort(pGsubHeader);constsal_uInt16nOfsFeatureTable=NEXT_UShort(pGsubHeader);constsal_uInt16nOfsLookupList=NEXT_UShort(pGsubHeader);// sanity check the GSUB headerif(nVersion!=0x00010000)if(nVersion!=0x00001000)// workaround for SunBatang etc.return-1;// unknown format or brokentypedefstd::vector<sal_uLong>ReqFeatureTagList;ReqFeatureTagListaReqFeatureTagList;aReqFeatureTagList.push_back(MKTAG("vert"));typedefstd::vector<sal_uInt16>UshortList;UshortListaFeatureIndexList;// parse Script TableconstFT_Byte*pScriptHeader=pGsubBase+nOfsScriptList;constsal_uInt16nCntScript=NEXT_UShort(pScriptHeader);if(pGsubLimit<pScriptHeader+6*nCntScript)returnfalse;for(sal_uInt16nScriptIndex=0;nScriptIndex<nCntScript;++nScriptIndex){constsal_uLongnTag=NEXT_Long(pScriptHeader);// e.g. hani/arab/kana/hangconstsal_uInt16nOfsScriptTable=NEXT_UShort(pScriptHeader);if((nTag!=(sal_uInt16)nRequestedScript)&&(nRequestedScript!=0))continue;constFT_Byte*pScriptTable=pGsubBase+nOfsScriptList+nOfsScriptTable;if(pGsubLimit<pScriptTable+4)returnfalse;constsal_uInt16nDefaultLangsysOfs=NEXT_UShort(pScriptTable);constsal_uInt16nCntLangSystem=NEXT_UShort(pScriptTable);sal_uInt16nLangsysOffset=0;if(pGsubLimit<pScriptTable+6*nCntLangSystem)returnfalse;for(sal_uInt16nLangsysIndex=0;nLangsysIndex<nCntLangSystem;++nLangsysIndex){constsal_uLongnInnerTag=NEXT_Long(pScriptTable);// e.g. KOR/ZHS/ZHT/JANconstsal_uInt16nOffset=NEXT_UShort(pScriptTable);if((nInnerTag!=(sal_uInt16)nRequestedLangsys)&&(nRequestedLangsys!=0))continue;nLangsysOffset=nOffset;break;}if((nDefaultLangsysOfs!=0)&&(nDefaultLangsysOfs!=nLangsysOffset)){constFT_Byte*pLangSys=pGsubBase+nOfsScriptList+nOfsScriptTable+nDefaultLangsysOfs;if(pGsubLimit<pLangSys+6)returnfalse;/*const sal_uInt16 nLookupOrder =*/NEXT_UShort(pLangSys);constsal_uInt16nReqFeatureIdx=NEXT_UShort(pLangSys);constsal_uInt16nCntFeature=NEXT_UShort(pLangSys);if(pGsubLimit<pLangSys+2*nCntFeature)returnfalse;aFeatureIndexList.push_back(nReqFeatureIdx);for(sal_uInt16i=0;i<nCntFeature;++i){constsal_uInt16nFeatureIndex=NEXT_UShort(pLangSys);aFeatureIndexList.push_back(nFeatureIndex);}}if(nLangsysOffset!=0){constFT_Byte*pLangSys=pGsubBase+nOfsScriptList+nOfsScriptTable+nLangsysOffset;if(pGsubLimit<pLangSys+6)returnfalse;/*const sal_uInt16 nLookupOrder =*/NEXT_UShort(pLangSys);constsal_uInt16nReqFeatureIdx=NEXT_UShort(pLangSys);constsal_uInt16nCntFeature=NEXT_UShort(pLangSys);if(pGsubLimit<pLangSys+2*nCntFeature)returnfalse;aFeatureIndexList.push_back(nReqFeatureIdx);for(sal_uInt16i=0;i<nCntFeature;++i){constsal_uInt16nFeatureIndex=NEXT_UShort(pLangSys);aFeatureIndexList.push_back(nFeatureIndex);}}}if(aFeatureIndexList.empty())returntrue;UshortListaLookupIndexList;UshortListaLookupOffsetList;// parse Feature TableconstFT_Byte*pFeatureHeader=pGsubBase+nOfsFeatureTable;if(pGsubLimit<pFeatureHeader+2)returnfalse;constsal_uInt16nCntFeature=NEXT_UShort(pFeatureHeader);if(pGsubLimit<pFeatureHeader+6*nCntFeature)returnfalse;for(sal_uInt16nFeatureIndex=0;nFeatureIndex<nCntFeature;++nFeatureIndex){constsal_uLongnTag=NEXT_Long(pFeatureHeader);// e.g. locl/vert/trad/smpl/liga/fina/...constsal_uInt16nOffset=NEXT_UShort(pFeatureHeader);// ignore unneeded feature lookupsif(aFeatureIndexList[0]!=nFeatureIndex)// do not ignore the required feature{constintnRequested=std::count(aFeatureIndexList.begin(),aFeatureIndexList.end(),nFeatureIndex);if(!nRequested)// ignore features that are not requestedcontinue;constintnAvailable=std::count(aReqFeatureTagList.begin(),aReqFeatureTagList.end(),nTag);if(!nAvailable)// some fonts don't provide features they request!continue;}constFT_Byte*pFeatureTable=pGsubBase+nOfsFeatureTable+nOffset;if(pGsubLimit<pFeatureTable+2)returnfalse;constsal_uInt16nCntLookups=NEXT_UShort(pFeatureTable);if(pGsubLimit<pFeatureTable+2*nCntLookups)returnfalse;for(sal_uInt16i=0;i<nCntLookups;++i){constsal_uInt16nLookupIndex=NEXT_UShort(pFeatureTable);aLookupIndexList.push_back(nLookupIndex);}if(nCntLookups==0)//### hack needed by Mincho/Gothic/Mingliu/Simsun/...aLookupIndexList.push_back(0);}// parse Lookup ListconstFT_Byte*pLookupHeader=pGsubBase+nOfsLookupList;if(pGsubLimit<pLookupHeader+2)returnfalse;constsal_uInt16nCntLookupTable=NEXT_UShort(pLookupHeader);if(pGsubLimit<pLookupHeader+2*nCntLookupTable)returnfalse;for(sal_uInt16nLookupIdx=0;nLookupIdx<nCntLookupTable;++nLookupIdx){constsal_uInt16nOffset=NEXT_UShort(pLookupHeader);if(std::count(aLookupIndexList.begin(),aLookupIndexList.end(),nLookupIdx))aLookupOffsetList.push_back(nOffset);}UshortList::const_iteratorit=aLookupOffsetList.begin();for(;it!=aLookupOffsetList.end();++it){constsal_uInt16nOfsLookupTable=*it;constFT_Byte*pLookupTable=pGsubBase+nOfsLookupList+nOfsLookupTable;if(pGsubLimit<pLookupTable+6)returnfalse;constsal_uInt16eLookupType=NEXT_UShort(pLookupTable);/*const sal_uInt16 eLookupFlag =*/NEXT_UShort(pLookupTable);constsal_uInt16nCntLookupSubtable=NEXT_UShort(pLookupTable);// TODO: switch( eLookupType )if(eLookupType!=1)// TODO: once we go beyond SingleSubstcontinue;if(pGsubLimit<pLookupTable+2*nCntLookupSubtable)returnfalse;for(sal_uInt16nSubTableIdx=0;nSubTableIdx<nCntLookupSubtable;++nSubTableIdx){constsal_uInt16nOfsSubLookupTable=NEXT_UShort(pLookupTable);constFT_Byte*pSubLookup=pGsubBase+nOfsLookupList+nOfsLookupTable+nOfsSubLookupTable;if(pGsubLimit<pSubLookup+6)returnfalse;constsal_uInt16nFmtSubstitution=NEXT_UShort(pSubLookup);constsal_uInt16nOfsCoverage=NEXT_UShort(pSubLookup);typedefstd::pair<sal_uInt16,sal_uInt16>GlyphSubst;typedefstd::vector<GlyphSubst>SubstVector;SubstVectoraSubstVector;constFT_Byte*pCoverage=pGsubBase+nOfsLookupList+nOfsLookupTable+nOfsSubLookupTable+nOfsCoverage;if(pGsubLimit<pCoverage+4)returnfalse;constsal_uInt16nFmtCoverage=NEXT_UShort(pCoverage);switch(nFmtCoverage){case1:// Coverage Format 1{constsal_uInt16nCntGlyph=NEXT_UShort(pCoverage);if(pGsubLimit<pCoverage+2*nCntGlyph)// TODO? nCntGlyph = (pGsubLimit - pCoverage) / 2;returnfalse;aSubstVector.reserve(nCntGlyph);for(sal_uInt16i=0;i<nCntGlyph;++i){constsal_uInt16nGlyphId=NEXT_UShort(pCoverage);aSubstVector.push_back(GlyphSubst(nGlyphId,0));}}break;case2:// Coverage Format 2{constsal_uInt16nCntRange=NEXT_UShort(pCoverage);if(pGsubLimit<pCoverage+6*nCntRange)// TODO? nCntGlyph = (pGsubLimit - pCoverage) / 6;returnfalse;for(inti=nCntRange;--i>=0;){constsal_uInt32nGlyph0=NEXT_UShort(pCoverage);constsal_uInt32nGlyph1=NEXT_UShort(pCoverage);constsal_uInt16nCovIdx=NEXT_UShort(pCoverage);for(sal_uInt32j=nGlyph0;j<=nGlyph1;++j)aSubstVector.push_back(GlyphSubst(static_cast<sal_uInt16>(j+nCovIdx),0));}}break;}SubstVector::iteratorsubst_it(aSubstVector.begin());switch(nFmtSubstitution){case1:// Single Substitution Format 1{constsal_uInt16nDeltaGlyphId=NEXT_UShort(pSubLookup);for(;subst_it!=aSubstVector.end();++subst_it)(*subst_it).second=(*subst_it).first+nDeltaGlyphId;}break;case2:// Single Substitution Format 2{constsal_uInt16nCntGlyph=NEXT_UShort(pSubLookup);for(inti=nCntGlyph;(subst_it!=aSubstVector.end())&&(--i>=0);++subst_it){if(pGsubLimit<pSubLookup+2)returnfalse;constsal_uInt16nGlyphId=NEXT_UShort(pSubLookup);(*subst_it).second=nGlyphId;}}break;}// now apply the glyph substitutions that have been collected in this subtableif(!aSubstVector.empty()){GlyphSubstitution*pGSubstitution=newGlyphSubstitution;pTTFile->pGSubstitution=(void*)pGSubstitution;for(subst_it=aSubstVector.begin();subst_it!=aSubstVector.end();++subst_it)(*pGSubstitution)[(*subst_it).first]=(*subst_it).second;}}}returntrue;}voidReleaseGSUB(struct_TrueTypeFont*pTTFile){GlyphSubstitution*pGlyphSubstitution=(GlyphSubstitution*)pTTFile->pGSubstitution;if(pGlyphSubstitution)deletepGlyphSubstitution;}intUseGSUB(struct_TrueTypeFont*pTTFile,intnGlyph,int/*wmode*/){GlyphSubstitution*pGlyphSubstitution=(GlyphSubstitution*)pTTFile->pGSubstitution;if(pGlyphSubstitution!=0){GlyphSubstitution::const_iteratorit(pGlyphSubstitution->find(sal::static_int_cast<sal_uInt16>(nGlyph)));if(it!=pGlyphSubstitution->end())nGlyph=(*it).second;}returnnGlyph;}intHasVerticalGSUB(struct_TrueTypeFont*pTTFile){GlyphSubstitution*pGlyphSubstitution=(GlyphSubstitution*)pTTFile->pGSubstitution;returnpGlyphSubstitution?+1:0;}}/* vim:set shiftwidth=4 softtabstop=4 expandtab: */