summaryrefslogtreecommitdiff
path: root/vcl/source/fontsubset/gsub.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/source/fontsubset/gsub.cxx')
-rw-r--r--vcl/source/fontsubset/gsub.cxx360
1 files changed, 360 insertions, 0 deletions
diff --git a/vcl/source/fontsubset/gsub.cxx b/vcl/source/fontsubset/gsub.cxx
new file mode 100644
index 000000000000..37650003eefa
--- /dev/null
+++ b/vcl/source/fontsubset/gsub.cxx
@@ -0,0 +1,360 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * 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
+ * <http://www.openoffice.org/license.html>
+ * 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 <osl/diagnose.h>
+
+#include <vector>
+#include <map>
+#include <algorithm>
+
+namespace vcl
+{
+
+typedef sal_uInt32 ULONG;
+typedef sal_uInt32 UINT32;
+typedef sal_uInt16 USHORT;
+typedef sal_uInt8 FT_Byte;
+
+typedef std::map<USHORT,USHORT> 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<ULONG> ReqFeatureTagList;
+ ReqFeatureTagList aReqFeatureTagList;
+
+ aReqFeatureTagList.push_back( MKTAG("vert") );
+
+ typedef std::vector<USHORT> 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<USHORT,USHORT> GlyphSubst;
+ typedef std::vector<GlyphSubst> 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 UINT32 nGlyph0 = NEXT_UShort( pCoverage );
+ const UINT32 nGlyph1 = NEXT_UShort( pCoverage );
+ const USHORT nCovIdx = NEXT_UShort( pCoverage );
+ for( UINT32 j = nGlyph0; j <= nGlyph1; ++j )
+ aSubstVector.push_back( GlyphSubst( static_cast<USHORT>(j + nCovIdx), 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<USHORT>(nGlyph) ) );
+ if( it != pGlyphSubstitution->end() )
+ nGlyph = (*it).second;
+ }
+
+ return nGlyph;
+}
+
+int HasVerticalGSUB( struct _TrueTypeFont* pTTFile )
+{
+ GlyphSubstitution* pGlyphSubstitution = (GlyphSubstitution*)pTTFile->pGSubstitution;
+ return pGlyphSubstitution ? +1 : 0;
+}
+
+}