Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / wpf / src / Core / CSharp / MS / Internal / Shaping / OpenTypeLayout.cs / 1 / OpenTypeLayout.cs
//+------------------------------------------------------------------------
//
// Microsoft Windows Client Platform
// Copyright (C) Microsoft Corporation, 2002
//
// File: OpenTypeLayout.cs
//
// Contents: OpentTypeLayout interfaces
//
// contact: sergeym
//
// History: 2002-03-02 Created (sergeym)
//
//-----------------------------------------------------------------------
using System;
using System.Security;
using System.Security.Permissions;
using System.IO;
using System.Diagnostics;
using MS.Internal.FontCache;
namespace MS.Internal.Shaping
{
internal struct LayoutOffset
{
public LayoutOffset(int dx, int dy) { this.dx=dx; this.dy=dy; }
public int dx;
public int dy;
}
///
/// Tags used in OpenTypeLayout
///
internal enum OpenTypeTags :uint
{
Null = 0x00000000,
GSUB = 0x47535542,
GPOS = 0x47504F53,
GDEF = 0x47444546,
BASE = 0x42415345,
name = 0x6e616D65,
post = 0x706F7374,
dflt = 0x64666c74,
head = 0x68656164,
//GSUB feature tags
locl = 0x6c6f636c,
ccmp = 0x63636d70,
rlig = 0x726c6967,
liga = 0x6c696761,
clig = 0x636c6967,
pwid = 0x70776964,
init = 0x696e6974,
medi = 0x6d656469,
fina = 0x66696e61,
isol = 0x69736f6c,
calt = 0x63616c74,
//Indic subst
nukt = 0x6e756b74,
akhn = 0x616b686e,
rphf = 0x72706866,
blwf = 0x626c7766,
half = 0x68616c66,
vatu = 0x76617475,
pres = 0x70726573,
abvs = 0x61627673,
blws = 0x626c7773,
psts = 0x70737473,
haln = 0x68616c6e,
//GPOS feature tags
kern = 0x6b65726e,
mark = 0x6d61726b,
mkmk = 0x6d6b6d6b,
curs = 0x63757273,
//Indic pos
abvm = 0x6162766d,
blwm = 0x626c776d,
dist = 0x64697374,
//script tags
latn = 0x6c61746e
}
///
/// FeatureInfo flags, describing actions implemented in OT feature
///
[Flags]
internal enum TagInfoFlags : uint
{
Substitution = 0x01, // does glyph substitution
Positioning = 0x02, // does glyph positioning
Both = 0x03, // does both substitution and positioning
None = 0x00 // neither of them
}
/* Used by commented code below
///
/// OpenType feature information. Returned from GetFeatureList method
///
internal struct TagInfo
{
public uint Tag;
public TagInfoFlags TagFlags;
public static bool IsNewTag(TagInfo[] Tags, uint Tag)
{
for(int i=0; i
/// Table pointer wrapper. Checking table boundaries
///
internal unsafe class FontTable
{
///
/// Critical: This code is unsafe and stores a byte ptr
///
[SecurityCritical]
public FontTable(byte* data)
{
m_data = data;
if (data != null)
{
m_length = *((uint*)data);
}
else
{
m_length = 0;
}
}
public const int InvalidOffset = int.MaxValue;
public const int NullOffset = 0;
///
/// Critical: This code acceses font table.
/// Safe : This code doesn't expose any value from font table.
///
public bool IsPresent
{
[SecurityCritical,SecurityTreatAsSafe]
get
{
return (m_data!=null);
}
}
///
/// Critical: This code acceses font table data.
///
[SecurityCritical]
public ushort GetUShort(int offset)
{
Invariant.Assert(m_data!= null);
if ((offset + 1) >= m_length) throw new FileFormatException();
return (ushort)((m_data[offset]<<8) + m_data[offset+1]);
}
///
/// Critical: This code acceses font table data.
///
[SecurityCritical]
public short GetShort(int offset)
{
Invariant.Assert(m_data != null);
if ((offset + 1) >= m_length) throw new FileFormatException();
return (short)((m_data[offset]<<8) + m_data[offset+1]);
}
///
/// Critical: This code acceses font table data.
///
[SecurityCritical]
public uint GetUInt(int offset)
{
Invariant.Assert(m_data != null);
if ((offset + 3) >= m_length) throw new FileFormatException();
return (uint)((m_data[offset]<<24) + (m_data[offset+1]<<16) + (m_data[offset+2]<<8) + m_data[offset+3]);
}
///
/// Critical: This code acceses font table data.
///
[SecurityCritical]
public ushort GetOffset(int offset)
{
Invariant.Assert(m_data != null);
if ((offset+1)>=m_length) throw new FileFormatException();
return (ushort)((m_data[offset]<<8) + m_data[offset+1]);
}
///
/// Critical:This code is unsafe to expose
///
[SecurityCritical]
private byte* m_data;
///
/// Critical:This code is used to validate length and dereference pointers.
///
[SecurityCritical]
private uint m_length;
}
///
/// Font file access callbacks
///
internal interface IOpenTypeFont
{
///
/// Returns array containing font table data
/// Return empty array if table does not exist.
///
///
/// Critical - as this accesses FontFaceLayoutInfo.Gdef which exposes font info.
///
[SecurityCritical]
FontTable GetFontTable(OpenTypeTags TableTag);
///
/// Returns glyph coordinate
///
LayoutOffset GetGlyphPointCoord(ushort Glyph, ushort PointIndex);
///
/// Returns cache for layout table. If cache not found, return null Checked pointer
///
CheckedPointer GetTableCache(OpenTypeTags tableTag);
///
/// Allocate space for layout table cache. If space is not available
/// client should return null checked pointer.
/// Only font cache implementation need to implement this interface.
/// Normal layout funcitons will not call it.
///
CheckedPointer AllocateTableCache(OpenTypeTags tableTag, int size);
}
///
/// Text direction
///
internal enum TextFlowDirection : ushort
{
LTR,
RTL,
TTB,
BTT
}
///
/// Layout metrics
///
internal struct LayoutMetrics
{
public TextFlowDirection Direction;
//if DesignEmHeight==0, result requested in design units
public ushort DesignEmHeight; // font design units per Em
public ushort PixelsEmWidth; // Em width in pixels
public ushort PixelsEmHeight; // Em height in pixels
public LayoutMetrics(TextFlowDirection Direction,
ushort DesignEmHeight,
ushort PixelsEmWidth,
ushort PixelsEmHeight)
{
this.Direction=Direction;
this.DesignEmHeight=DesignEmHeight;
this.PixelsEmWidth=PixelsEmWidth;
this.PixelsEmHeight=PixelsEmHeight;
}
}
internal class Feature
{
public Feature(
ushort startIndex,
ushort length,
uint tag,
uint parameter //0 if disabled
)
{
_startIndex = startIndex;
_length = length;
_tag = tag;
_parameter = parameter;
}
public uint Tag
{
get { return _tag; }
set { _tag = value; }
}
public uint Parameter
{
get { return _parameter; }
set { _parameter = value; }
}
public ushort StartIndex
{
get { return _startIndex; }
set { _startIndex = value; }
}
public ushort Length
{
get { return _length; }
set { _length = value; }
}
private ushort _startIndex; // first to be applied
private ushort _length; // length to be applied
private uint _tag; // OpenType feature tag
private uint _parameter; // feature parameter
}
///
/// OpenTypeLayout class provides access to OpenType Layout services
///
internal static unsafe class OpenTypeLayout
{
///
///
/// Font
/// Script to find
/// TagInfo, if script not present flags == None
///
/// Critical - Access protected font information (raw bytes)
///
[SecurityCritical]
internal static TagInfoFlags FindScript(
IOpenTypeFont Font, // In: Font access interface
uint ScriptTag // In
)
{
TagInfoFlags flags = TagInfoFlags.None;
try
{
FontTable gsubTable = Font.GetFontTable(OpenTypeTags.GSUB);
if (gsubTable.IsPresent)
{
GSUBHeader gsubHeader = new GSUBHeader(0);
if (!gsubHeader.GetScriptList(gsubTable).FindScript(gsubTable,ScriptTag).IsNull)
{
flags |= TagInfoFlags.Substitution;
}
}
}
catch (FileFormatException)
{
return TagInfoFlags.None;
}
try
{
FontTable gposTable = Font.GetFontTable(OpenTypeTags.GPOS);
if (gposTable.IsPresent)
{
GPOSHeader gposHeader = new GPOSHeader(0);
if (!gposHeader.GetScriptList(gposTable).FindScript(gposTable,ScriptTag).IsNull)
{
flags |= TagInfoFlags.Positioning;
}
}
}
catch (FileFormatException)
{
return TagInfoFlags.None;
}
return flags;
}
///
///
///
/// Font
/// Script to search in
/// LangGys to search for
/// TagInfoFlags, if script not present == None
///
/// Critical - access protected font resource (FontTable)
///
[SecurityCritical]
internal static TagInfoFlags FindLangSys(
IOpenTypeFont Font,
uint ScriptTag,
uint LangSysTag
)
{
TagInfoFlags flags = TagInfoFlags.None;
try
{
FontTable gsubTable = Font.GetFontTable(OpenTypeTags.GSUB);
if (gsubTable.IsPresent)
{
GSUBHeader gsubHeader = new GSUBHeader(0);
ScriptTable gsubScript = gsubHeader.GetScriptList(gsubTable).FindScript(gsubTable,ScriptTag);
if (!gsubScript.IsNull && !gsubScript.FindLangSys(gsubTable,LangSysTag).IsNull)
{
flags |= TagInfoFlags.Substitution;
}
}
}
catch (FileFormatException)
{
return TagInfoFlags.None;
}
try
{
FontTable gposTable = Font.GetFontTable(OpenTypeTags.GPOS);
if (gposTable.IsPresent)
{
GPOSHeader gposHeader = new GPOSHeader(0);
ScriptTable gposScript = gposHeader.GetScriptList(gposTable).FindScript(gposTable,ScriptTag);
if (!gposScript.IsNull && !gposScript.FindLangSys(gposTable,LangSysTag).IsNull)
{
flags |= TagInfoFlags.Positioning;
}
}
}
catch (FileFormatException)
{
return TagInfoFlags.None;
}
return flags;
}
/* This is unused code, but will be used later so it is just commented out for now.
///
/// Enumerates scripts in a font
///
internal static OpenTypeLayoutResult GetScriptList (
IOpenTypeFont Font, // In: Font access interface
out TagInfo[] Scripts // Out: Array of scripts supported
)
{
ushort i;
ushort GposNewTags;
Scripts=null; // Assignment required, because of out attribute.
// This value should be owerwritten later.
try
{
FontTable GsubTable = Font.GetFontTable(OpenTypeTags.GSUB);
FontTable GposTable = Font.GetFontTable(OpenTypeTags.GPOS);
GSUBHeader GsubHeader = new GSUBHeader(0);
GPOSHeader GposHeader = new GPOSHeader(0);
ScriptList GsubScriptList;
ScriptList GposScriptList;
ushort GsubScriptCount;
ushort GposScriptCount;
if (GsubTable.IsNotPresent && GposTable.IsNotPresent)
{
Scripts = new TagInfo[0];
return OpenTypeLayoutResult.Success;
}
if (GsubTable.IsPresent)
{
GsubScriptList = GsubHeader.GetScriptList(GsubTable);
GsubScriptCount = GsubScriptList.GetScriptCount(GsubTable);
}
else
{
GsubScriptList = new ScriptList(FontTable.InvalidOffset);
GsubScriptCount = 0;
}
if (GposTable.IsPresent)
{
GposScriptList = GposHeader.GetScriptList(GposTable);
GposScriptCount = GposScriptList.GetScriptCount(GposTable);
}
else
{
GposScriptList = new ScriptList(FontTable.InvalidOffset);
GposScriptCount = 0;
}
//This is true in most cases that there is no new tags in GPOS.
//So, we allocate this array then check GPOS for new tags
Scripts = new TagInfo[GsubScriptCount];
for(i=0; i0)
{
int CurrentScriptIndex=GposScriptCount;
//Allocate new array to fit all tags
TagInfo[] tmp = Scripts;
Scripts = new TagInfo[GsubScriptCount+GposNewTags];
Array.Copy(tmp,0,Scripts,0,tmp.Length);
for(i=0;i
/// Enumerates language systems for script
///
internal static OpenTypeLayoutResult GetLangSysList (
IOpenTypeFont Font, // In: Font access interface
uint ScriptTag, // In: Script tag
out TagInfo[] LangSystems // Out: Array of LangSystems for Script
)
{
ushort i;
ushort GposNewTags;
LangSystems=null; // Assignment required, because of out attribute.
// This value should be owerwritten later.
try
{
FontTable GsubTable = Font.GetFontTable(OpenTypeTags.GSUB);
FontTable GposTable = Font.GetFontTable(OpenTypeTags.GPOS);
GSUBHeader GsubHeader = new GSUBHeader(0);
GPOSHeader GposHeader = new GPOSHeader(0);
ScriptList GsubScriptList;
ScriptList GposScriptList;
ScriptTable GsubScript;
ScriptTable GposScript;
ushort GsubLangSysCount;
ushort GposLangSysCount;
if (GsubTable.IsNotPresent && GposTable.IsNotPresent)
{
return OpenTypeLayoutResult.ScriptNotFound;
}
if (GsubTable.IsPresent)
{
GsubScriptList = GsubHeader.GetScriptList(GsubTable);
GsubScript = GsubScriptList.FindScript(GsubTable,ScriptTag);
}
else
{
GsubScript = new ScriptTable(FontTable.InvalidOffset);
}
if (GposTable.IsPresent)
{
GposScriptList = GposHeader.GetScriptList(GposTable);
GposScript = GposScriptList.FindScript(GposTable,ScriptTag);
}
else
{
GposScript = new ScriptTable(FontTable.InvalidOffset);
}
if (GsubScript.IsNull && GposScript.IsNull)
{
return OpenTypeLayoutResult.ScriptNotFound;
}
if (!GsubScript.IsNull)
{
GsubLangSysCount = GsubScript.GetLangSysCount(GsubTable);
}
else
{
GsubLangSysCount = 0;
}
if (!GposScript.IsNull)
{
GposLangSysCount = GposScript.GetLangSysCount(GposTable);
}
else
{
GposLangSysCount = 0;
}
//This is true in most cases that there is no new tags in GPOS.
//So, we allocate this array then check GPOS for new tags
ushort CurrentLangSysIndex;
if (GsubScript.IsDefaultLangSysExists(GsubTable))
{
LangSystems = new TagInfo[GsubLangSysCount+1];
LangSystems[0].Tag = (uint)OpenTypeTags.dflt;
LangSystems[0].TagFlags = TagInfoFlags.Substitution;
CurrentLangSysIndex = 1;
}
else
{
LangSystems = new TagInfo[GsubLangSysCount];
CurrentLangSysIndex = 0;
}
for(i=0; i0)
{
//Allocate new array to fit all tags
TagInfo[] tmp = LangSystems;
LangSystems = new TagInfo[GsubLangSysCount+GposNewTags];
Array.Copy(tmp,0,LangSystems,0,tmp.Length);
if (GposScript.IsDefaultLangSysExists(GposTable))
{
if (TagInfo.IsNewTag(LangSystems,(uint)OpenTypeTags.dflt))
{
LangSystems[CurrentLangSysIndex].Tag = (uint)OpenTypeTags.dflt;
LangSystems[CurrentLangSysIndex].TagFlags = TagInfoFlags.Positioning;
++CurrentLangSysIndex;
}
else
{
int LangSysIndex = TagInfo.GetTagIndex(LangSystems,(uint)OpenTypeTags.dflt);
LangSystems[LangSysIndex].TagFlags |= TagInfoFlags.Positioning;
}
}
for(i=0;i
/// Enumerates features in a language system
///
internal static OpenTypeLayoutResult GetFeatureList (
IOpenTypeFont Font, // In: Font access interface
uint ScriptTag, // In: Script tag
uint LangSysTag, // In: LangSys tag
out TagInfo[] Features // Out: Array of features
)
{
ushort i;
ushort GposNewTags;
Features=null; // Assignment required, because of out attribute.
// This value should be owerwritten later.
try
{
FontTable GsubTable = Font.GetFontTable(OpenTypeTags.GSUB);
FontTable GposTable = Font.GetFontTable(OpenTypeTags.GPOS);
GSUBHeader GsubHeader = new GSUBHeader(0);
GPOSHeader GposHeader = new GPOSHeader(0);
ScriptList GsubScriptList;
ScriptList GposScriptList;
ScriptTable GsubScript;
ScriptTable GposScript;
LangSysTable GsubLangSys;
LangSysTable GposLangSys;
ushort GsubFeatureCount;
ushort GposFeatureCount;
FeatureList GsubFeatureList;
FeatureList GposFeatureList;
if (GsubTable.IsNotPresent && GposTable.IsNotPresent)
{
return OpenTypeLayoutResult.ScriptNotFound;
}
if (GsubTable.IsPresent)
{
GsubScriptList = GsubHeader.GetScriptList(GsubTable);
GsubScript = GsubScriptList.FindScript(GsubTable,ScriptTag);
GsubLangSys = GsubScript.FindLangSys(GsubTable,LangSysTag);
GsubFeatureList = GsubHeader.GetFeatureList(GsubTable);
}
else
{
GsubScript = new ScriptTable(FontTable.InvalidOffset);
GsubLangSys = new LangSysTable(FontTable.InvalidOffset);
GsubFeatureList = new FeatureList(FontTable.InvalidOffset);
}
if (GposTable.IsPresent)
{
GposScriptList = GposHeader.GetScriptList(GposTable);
GposScript = GposScriptList.FindScript(GposTable,ScriptTag);
GposLangSys = GposScript.FindLangSys(GposTable,LangSysTag);
GposFeatureList = GposHeader.GetFeatureList(GposTable);
}
else
{
GposScript = new ScriptTable(FontTable.InvalidOffset);
GposLangSys = new LangSysTable(FontTable.InvalidOffset);
GposFeatureList = new FeatureList(FontTable.InvalidOffset);
}
if (GsubScript.IsNull && GposScript.IsNull)
{
return OpenTypeLayoutResult.ScriptNotFound;
}
if (GsubLangSys.IsNull && GposLangSys.IsNull)
{
return OpenTypeLayoutResult.LangSysNotFound;
}
if (!GsubLangSys.IsNull)
{
GsubFeatureCount = GsubLangSys.FeatureCount(GsubTable);
}
else
{
GsubFeatureCount = 0;
}
if (!GposLangSys.IsNull)
{
GposFeatureCount = GposLangSys.FeatureCount(GposTable);
}
else
{
GposFeatureCount = 0;
}
Features = new TagInfo[GsubFeatureCount];
int CurrentFeatureIndex = 0;
for(i=0; i0)
{
//Allocate new array to fit all tags
TagInfo[] tmp = Features;
Features = new TagInfo[GsubFeatureCount+GposNewTags];
Array.Copy(tmp,0,Features,0,tmp.Length);
for(i=0;i
/// Substitutes glyphs according to features defined in the font.
///
/// In: Font access interface
/// In: Workspace for layout engine
/// In: Script tag
/// In: LangSys tag
/// In: List of features to apply
/// In: Actual number of features in
/// In: offset of input characters inside FeatureSet
/// In: Characters count (i.e. .Length);
/// In/out: Char to glyph mapping
/// In/out: List of GlyphInfo structs
/// Substitution result
///
/// Critical - access fonttable, which is protected... in addition charcount
/// parameters are passed directly to other code, which could result
/// in buffer reads outside of fonttable.
///
[SecurityCritical]
internal static OpenTypeLayoutResult SubstituteGlyphs(
IOpenTypeFont Font, // In: Font access interface
OpenTypeLayoutWorkspace workspace, // In: Workspace for layout engine
uint ScriptTag, // In: Script tag
uint LangSysTag, // In: LangSys tag
Feature[] FeatureSet, // In: List of features to apply
int featureCount, // In: Actual number of features in FeatureSet
int featureSetOffset,
int CharCount, // In: Characters count (i.e. Charmap.Length);
UshortList Charmap, // In/out: Char to glyph mapping
GlyphInfoList Glyphs // In/out: List of GlyphInfo structs
)
{
try
{
FontTable GsubTable = Font.GetFontTable(OpenTypeTags.GSUB);
if (!GsubTable.IsPresent) {return OpenTypeLayoutResult.ScriptNotFound;}
GSUBHeader GsubHeader = new GSUBHeader(0);
ScriptList ScriptList = GsubHeader.GetScriptList(GsubTable);
ScriptTable Script = ScriptList.FindScript(GsubTable,ScriptTag);
if (Script.IsNull) {return OpenTypeLayoutResult.ScriptNotFound;}
LangSysTable LangSys = Script.FindLangSys(GsubTable,LangSysTag);
if (LangSys.IsNull) {return OpenTypeLayoutResult.LangSysNotFound;}
FeatureList FeatureList = GsubHeader.GetFeatureList(GsubTable);
LookupList LookupList = GsubHeader.GetLookupList(GsubTable);
LayoutEngine.ApplyFeatures(
Font,
workspace,
OpenTypeTags.GSUB,
GsubTable,
new LayoutMetrics(), //it is not needed for substitution
LangSys,
FeatureList,
LookupList,
FeatureSet,
featureCount,
featureSetOffset,
CharCount,
Charmap,
Glyphs,
null,
null
);
}
catch (FileFormatException)
{
return OpenTypeLayoutResult.BadFontTable;
}
return OpenTypeLayoutResult.Success;
}
///
/// Position glyphs according to features defined in the font.
///
/// In: Font access interface
/// In: Workspace for layout engine
/// In: Script tag
/// In: LangSys tag
/// In: LayoutMetrics
/// In: List of features to apply
/// In: Actual number of features in
/// In: offset of input characters inside FeatureSet
/// In: Characters count (i.e. .Length);
/// In: Char to glyph mapping
/// In/out: List of GlyphInfo structs
/// In/out: Glyphs adv.widths
/// In/out: Glyph offsets
/// Substitution result
///
/// Critical - access fonttable, which is protected... in addition charcount
/// parameters are passed directly to other code, which could result
/// in buffer reads outside of fonttable.
///
[SecurityCritical]
internal static OpenTypeLayoutResult PositionGlyphs(
IOpenTypeFont Font,
OpenTypeLayoutWorkspace workspace,
uint ScriptTag,
uint LangSysTag,
LayoutMetrics Metrics,
Feature[] FeatureSet,
int featureCount,
int featureSetOffset,
int CharCount,
UshortList Charmap,
GlyphInfoList Glyphs,
int* Advances,
LayoutOffset* Offsets
)
{
try
{
FontTable GposTable = Font.GetFontTable(OpenTypeTags.GPOS);
if (!GposTable.IsPresent) {return OpenTypeLayoutResult.ScriptNotFound;}
GPOSHeader GposHeader = new GPOSHeader(0);
ScriptList ScriptList = GposHeader.GetScriptList(GposTable);
ScriptTable Script = ScriptList.FindScript(GposTable,ScriptTag);
if (Script.IsNull) {return OpenTypeLayoutResult.ScriptNotFound;}
LangSysTable LangSys = Script.FindLangSys(GposTable,LangSysTag);
if (LangSys.IsNull) {return OpenTypeLayoutResult.LangSysNotFound;}
FeatureList FeatureList = GposHeader.GetFeatureList(GposTable);
LookupList LookupList = GposHeader.GetLookupList(GposTable);
LayoutEngine.ApplyFeatures(
Font,
workspace,
OpenTypeTags.GPOS,
GposTable,
Metrics,
LangSys,
FeatureList,
LookupList,
FeatureSet,
featureCount,
featureSetOffset,
CharCount,
Charmap,
Glyphs,
Advances,
Offsets
);
}
catch (FileFormatException)
{
return OpenTypeLayoutResult.BadFontTable;
}
return OpenTypeLayoutResult.Success;
}
///
///
///
///
/// Critical - access fonttable, which is protected... in addition glyph range
/// parameters are passed directly to other code, which could result
/// in buffer reads outside of fonttable.
///
[SecurityCritical]
internal static OpenTypeLayoutResult CreateLayoutCache (
IOpenTypeFont font, // In: Font access interface
int maxCacheSize // In: Maximum cache size allowed
)
{
OpenTypeLayoutCache.CreateCache(font, maxCacheSize);
return OpenTypeLayoutResult.Success;
}
///
/// Internal method to test layout tables if they are uitable for fast path.
/// Returns list of script-langauge pairs that are not optimizable.
///
///
/// Critical - access fonttable, which is protected... in addition glyph range
/// parameters are passed directly to other code, which could result
/// in buffer reads outside of fonttable.
///
[SecurityCritical]
internal static OpenTypeLayoutResult GetComplexLanguageList (
IOpenTypeFont Font, //In: Font access interface
uint[] featureList, //In: Feature to look in
uint[] glyphBits,
ushort minGlyphId,
ushort maxGlyphId,
out WritingSystem[] complexLanguages
// Out: List of script/langauge pair
// that are not optimizable
)
{
try
{
WritingSystem[] gsubComplexLanguages = null;
WritingSystem[] gposComplexLanguages = null;
int gsubComplexLanguagesCount = 0;
int gposComplexLanguagesCount = 0;
FontTable GsubTable = Font.GetFontTable(OpenTypeTags.GSUB);
FontTable GposTable = Font.GetFontTable(OpenTypeTags.GPOS);
if (GsubTable.IsPresent)
{
LayoutEngine.GetComplexLanguageList(
OpenTypeTags.GSUB,
GsubTable,
featureList,
glyphBits,
minGlyphId,
maxGlyphId,
out gsubComplexLanguages,
out gsubComplexLanguagesCount
);
}
if (GposTable.IsPresent)
{
LayoutEngine.GetComplexLanguageList(
OpenTypeTags.GPOS,
GposTable,
featureList,
glyphBits,
minGlyphId,
maxGlyphId,
out gposComplexLanguages,
out gposComplexLanguagesCount
);
}
if (gsubComplexLanguages == null && gposComplexLanguages == null)
{
complexLanguages = null;
return OpenTypeLayoutResult.Success;
}
// Both tables have complex scrips, merge results
// Count gpos unique Languages
// and pack them at the same time
// so we do not research them again.
int gposNewLanguages=0, i, j;
for(i = 0; i < gposComplexLanguagesCount ;i++)
{
bool foundInGsub = false;
for(j = 0; j < gsubComplexLanguagesCount ;j++)
{
if (gsubComplexLanguages[j].scriptTag == gposComplexLanguages[i].scriptTag &&
gsubComplexLanguages[j].langSysTag == gposComplexLanguages[i].langSysTag
)
{
foundInGsub = true;
break;
};
}
if (!foundInGsub)
{
if (gposNewLanguages < i)
{
gposComplexLanguages[gposNewLanguages] = gposComplexLanguages[i];
}
gposNewLanguages++;
}
}
//realloc array for merged results, merge both arrays
complexLanguages = new WritingSystem[gsubComplexLanguagesCount + gposNewLanguages];
for(i = 0; i < gsubComplexLanguagesCount; i++)
{
complexLanguages[i] = gsubComplexLanguages[i];
}
for(i = 0; i < gposNewLanguages; i++)
{
complexLanguages[gsubComplexLanguagesCount + i] = gposComplexLanguages[i];
}
return OpenTypeLayoutResult.Success;
}
catch (FileFormatException)
{
complexLanguages = null;
return OpenTypeLayoutResult.BadFontTable;
}
}
}
internal struct WritingSystem
{
internal uint scriptTag;
internal uint langSysTag;
}
///
///
internal enum OpenTypeLayoutResult
{
Success,
InvalidParameter,
TableNotFound,
ScriptNotFound,
LangSysNotFound,
BadFontTable,
UnderConstruction
}
///
/// Class for internal OpenType use to store per font
/// information and temporary buffers.
///
/// We do not use fontcache now, so this information
/// will be recreated every time shaping engine
/// will be called, so
///
internal class OpenTypeLayoutWorkspace
{
///
/// Init buffers to initial values.
///
///
/// Critical: Calls unsafe code
/// Safe: Does not actually access data through the pointers
///
[SecurityCritical, SecurityTreatAsSafe]
internal unsafe OpenTypeLayoutWorkspace()
{
_bytesPerLookup = 0;
_lookupUsageFlags = null;
_cachePointers = null;
}
///
/// Reset all structures to the new font/OTTable/script/langsys.
///
/// Client need to call it only once per shaping engine call.
/// This is client's responsibility to ensure that workspace is
/// used for single font/OTTable/script/langsys between Init() calls
///
///In: Font access interface
///In: Font table tag
///In: Script tag
///In: Language System tag
///Success if workspace is initialized succesfully, specific error if failed
internal OpenTypeLayoutResult Init(
IOpenTypeFont font,
OpenTypeTags tableTag,
uint scriptTag,
uint langSysTag
)
{
// Currently all buffers are per call,
// no need to do anything.
return OpenTypeLayoutResult.Success;
}
#region Lookup flags
//lookup usage flags access
private const byte AggregatedFlagMask = 0x01;
private const byte RequiredFeatureFlagMask = 0x02;
private const int FeatureFlagsStartBit = 2;
public void InitLookupUsageFlags(int lookupCount, int featureCount)
{
_bytesPerLookup = (featureCount + FeatureFlagsStartBit + 7) >> 3;
int requiredLookupUsageArraySize = lookupCount * _bytesPerLookup;
if ( _lookupUsageFlags == null ||
_lookupUsageFlags.Length < requiredLookupUsageArraySize)
{
_lookupUsageFlags = new byte[requiredLookupUsageArraySize];
}
Array.Clear(_lookupUsageFlags, 0, requiredLookupUsageArraySize);
}
public bool IsAggregatedFlagSet(int lookupIndex)
{
return ((_lookupUsageFlags[lookupIndex * _bytesPerLookup] & AggregatedFlagMask) != 0);
}
public bool IsFeatureFlagSet(int lookupIndex, int featureIndex)
{
int flagIndex = featureIndex + FeatureFlagsStartBit;
int flagByte = (lookupIndex * _bytesPerLookup) + (flagIndex >> 3);
byte flagMask = (byte)(1 << (flagIndex % 8));
return ((_lookupUsageFlags[flagByte] & flagMask) != 0);
}
public bool IsRequiredFeatureFlagSet(int lookupIndex)
{
return ((_lookupUsageFlags[lookupIndex * _bytesPerLookup] & RequiredFeatureFlagMask) != 0);
}
public void SetFeatureFlag(int lookupIndex, int featureIndex)
{
int startLookupByte = lookupIndex * _bytesPerLookup;
int flagIndex = featureIndex + FeatureFlagsStartBit;
int flagByte = startLookupByte + (flagIndex >> 3);
byte flagMask = (byte)(1 << (flagIndex % 8));
if (flagByte >= _lookupUsageFlags.Length)
{
//This should be invalid font. Lookup associated with the feature is not in lookup array.
throw new FileFormatException();
}
_lookupUsageFlags[flagByte] |= flagMask;
// Also set agregated usage flag
_lookupUsageFlags[startLookupByte] |= AggregatedFlagMask;
}
public void SetRequiredFeatureFlag(int lookupIndex)
{
int flagByte = lookupIndex * _bytesPerLookup;
if (flagByte >= _lookupUsageFlags.Length)
{
//This should be invalid font. Lookup associated with the feature is not in lookup array.
throw new FileFormatException();
}
//set RequiredFeature and aggregated flag at the same time
_lookupUsageFlags[flagByte] |= (AggregatedFlagMask | RequiredFeatureFlagMask);
}
// Define cache which lookup is enabled by which feature.
// Buffer grows with number of features applied
private int _bytesPerLookup;
private byte[] _lookupUsageFlags;
#endregion Lookup flags
#region Layout cache pointers
///
/// Allocate enough memory for array of cache pointers, parallel to glyph run.
///
/// These method should not be used directly, it is only called by OpenTypeLayputCache.
///
///
///In: Size of a glyph run
///
/// Critical: Calls unsafe code
/// Safe: Does not actually access data through the pointers
///
[SecurityCritical, SecurityTreatAsSafe]
public unsafe void AllocateCachePointers(int glyphRunLength)
{
if (_cachePointers != null && _cachePointers.Length >= glyphRunLength) return;
_cachePointers = new ushort*[glyphRunLength];
}
///
/// If glyph run is cahnged, update pointers according to the change. Reallocate array if necessary.
///
/// These method should not be used directly, it is only called by OpenTypeLayputCache.
///
///
///In: Number of glyphs in the run before change
///In: Number of glyphs in the run after change
///In: Index of the first changed glyph
///In: Index of the glyph after last changed
///
/// Critical: Calls unsafe code
/// Safe: Does not actually access data through the pointers
///
[SecurityCritical, SecurityTreatAsSafe]
public unsafe void UpdateCachePointers(
int oldLength,
int newLength,
int firstGlyphChanged,
int afterLastGlyphChanged
)
{
if (oldLength != newLength)
{
int oldAfterLastGlyphChanged = afterLastGlyphChanged - (newLength - oldLength);
if (_cachePointers.Length < newLength)
{
ushort*[] tmp = new ushort*[newLength];
Array.Copy(_cachePointers, tmp, firstGlyphChanged);
Array.Copy(_cachePointers, oldAfterLastGlyphChanged, tmp, afterLastGlyphChanged, oldLength - oldAfterLastGlyphChanged);
_cachePointers = tmp;
}
else
{
Array.Copy(_cachePointers, oldAfterLastGlyphChanged, _cachePointers, afterLastGlyphChanged, oldLength - oldAfterLastGlyphChanged);
}
}
}
///
/// Critical: Exposes font cache raw pointers
///
public unsafe ushort*[] CachePointers
{
[SecurityCritical]
get { return _cachePointers; }
}
///
/// Critical: Exposes font cache raw pointers
///
public unsafe byte* TableCacheData
{
[SecurityCritical]
get { return _tableCache; }
[SecurityCritical]
set { _tableCache = value; }
}
// Array of cache pointers, per glyph
///
/// Critical: This holds font cache raw pointers
///
[SecurityCritical]
private unsafe ushort*[] _cachePointers;
// Pointer to the table cache
///
/// Critical: This holds font cache raw pointers
///
[SecurityCritical]
private unsafe byte* _tableCache;
#endregion Layout cache pointers
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//+------------------------------------------------------------------------
//
// Microsoft Windows Client Platform
// Copyright (C) Microsoft Corporation, 2002
//
// File: OpenTypeLayout.cs
//
// Contents: OpentTypeLayout interfaces
//
// contact: sergeym
//
// History: 2002-03-02 Created (sergeym)
//
//-----------------------------------------------------------------------
using System;
using System.Security;
using System.Security.Permissions;
using System.IO;
using System.Diagnostics;
using MS.Internal.FontCache;
namespace MS.Internal.Shaping
{
internal struct LayoutOffset
{
public LayoutOffset(int dx, int dy) { this.dx=dx; this.dy=dy; }
public int dx;
public int dy;
}
///
/// Tags used in OpenTypeLayout
///
internal enum OpenTypeTags :uint
{
Null = 0x00000000,
GSUB = 0x47535542,
GPOS = 0x47504F53,
GDEF = 0x47444546,
BASE = 0x42415345,
name = 0x6e616D65,
post = 0x706F7374,
dflt = 0x64666c74,
head = 0x68656164,
//GSUB feature tags
locl = 0x6c6f636c,
ccmp = 0x63636d70,
rlig = 0x726c6967,
liga = 0x6c696761,
clig = 0x636c6967,
pwid = 0x70776964,
init = 0x696e6974,
medi = 0x6d656469,
fina = 0x66696e61,
isol = 0x69736f6c,
calt = 0x63616c74,
//Indic subst
nukt = 0x6e756b74,
akhn = 0x616b686e,
rphf = 0x72706866,
blwf = 0x626c7766,
half = 0x68616c66,
vatu = 0x76617475,
pres = 0x70726573,
abvs = 0x61627673,
blws = 0x626c7773,
psts = 0x70737473,
haln = 0x68616c6e,
//GPOS feature tags
kern = 0x6b65726e,
mark = 0x6d61726b,
mkmk = 0x6d6b6d6b,
curs = 0x63757273,
//Indic pos
abvm = 0x6162766d,
blwm = 0x626c776d,
dist = 0x64697374,
//script tags
latn = 0x6c61746e
}
///
/// FeatureInfo flags, describing actions implemented in OT feature
///
[Flags]
internal enum TagInfoFlags : uint
{
Substitution = 0x01, // does glyph substitution
Positioning = 0x02, // does glyph positioning
Both = 0x03, // does both substitution and positioning
None = 0x00 // neither of them
}
/* Used by commented code below
///
/// OpenType feature information. Returned from GetFeatureList method
///
internal struct TagInfo
{
public uint Tag;
public TagInfoFlags TagFlags;
public static bool IsNewTag(TagInfo[] Tags, uint Tag)
{
for(int i=0; i
/// Table pointer wrapper. Checking table boundaries
///
internal unsafe class FontTable
{
///
/// Critical: This code is unsafe and stores a byte ptr
///
[SecurityCritical]
public FontTable(byte* data)
{
m_data = data;
if (data != null)
{
m_length = *((uint*)data);
}
else
{
m_length = 0;
}
}
public const int InvalidOffset = int.MaxValue;
public const int NullOffset = 0;
///
/// Critical: This code acceses font table.
/// Safe : This code doesn't expose any value from font table.
///
public bool IsPresent
{
[SecurityCritical,SecurityTreatAsSafe]
get
{
return (m_data!=null);
}
}
///
/// Critical: This code acceses font table data.
///
[SecurityCritical]
public ushort GetUShort(int offset)
{
Invariant.Assert(m_data!= null);
if ((offset + 1) >= m_length) throw new FileFormatException();
return (ushort)((m_data[offset]<<8) + m_data[offset+1]);
}
///
/// Critical: This code acceses font table data.
///
[SecurityCritical]
public short GetShort(int offset)
{
Invariant.Assert(m_data != null);
if ((offset + 1) >= m_length) throw new FileFormatException();
return (short)((m_data[offset]<<8) + m_data[offset+1]);
}
///
/// Critical: This code acceses font table data.
///
[SecurityCritical]
public uint GetUInt(int offset)
{
Invariant.Assert(m_data != null);
if ((offset + 3) >= m_length) throw new FileFormatException();
return (uint)((m_data[offset]<<24) + (m_data[offset+1]<<16) + (m_data[offset+2]<<8) + m_data[offset+3]);
}
///
/// Critical: This code acceses font table data.
///
[SecurityCritical]
public ushort GetOffset(int offset)
{
Invariant.Assert(m_data != null);
if ((offset+1)>=m_length) throw new FileFormatException();
return (ushort)((m_data[offset]<<8) + m_data[offset+1]);
}
///
/// Critical:This code is unsafe to expose
///
[SecurityCritical]
private byte* m_data;
///
/// Critical:This code is used to validate length and dereference pointers.
///
[SecurityCritical]
private uint m_length;
}
///
/// Font file access callbacks
///
internal interface IOpenTypeFont
{
///
/// Returns array containing font table data
/// Return empty array if table does not exist.
///
///
/// Critical - as this accesses FontFaceLayoutInfo.Gdef which exposes font info.
///
[SecurityCritical]
FontTable GetFontTable(OpenTypeTags TableTag);
///
/// Returns glyph coordinate
///
LayoutOffset GetGlyphPointCoord(ushort Glyph, ushort PointIndex);
///
/// Returns cache for layout table. If cache not found, return null Checked pointer
///
CheckedPointer GetTableCache(OpenTypeTags tableTag);
///
/// Allocate space for layout table cache. If space is not available
/// client should return null checked pointer.
/// Only font cache implementation need to implement this interface.
/// Normal layout funcitons will not call it.
///
CheckedPointer AllocateTableCache(OpenTypeTags tableTag, int size);
}
///
/// Text direction
///
internal enum TextFlowDirection : ushort
{
LTR,
RTL,
TTB,
BTT
}
///
/// Layout metrics
///
internal struct LayoutMetrics
{
public TextFlowDirection Direction;
//if DesignEmHeight==0, result requested in design units
public ushort DesignEmHeight; // font design units per Em
public ushort PixelsEmWidth; // Em width in pixels
public ushort PixelsEmHeight; // Em height in pixels
public LayoutMetrics(TextFlowDirection Direction,
ushort DesignEmHeight,
ushort PixelsEmWidth,
ushort PixelsEmHeight)
{
this.Direction=Direction;
this.DesignEmHeight=DesignEmHeight;
this.PixelsEmWidth=PixelsEmWidth;
this.PixelsEmHeight=PixelsEmHeight;
}
}
internal class Feature
{
public Feature(
ushort startIndex,
ushort length,
uint tag,
uint parameter //0 if disabled
)
{
_startIndex = startIndex;
_length = length;
_tag = tag;
_parameter = parameter;
}
public uint Tag
{
get { return _tag; }
set { _tag = value; }
}
public uint Parameter
{
get { return _parameter; }
set { _parameter = value; }
}
public ushort StartIndex
{
get { return _startIndex; }
set { _startIndex = value; }
}
public ushort Length
{
get { return _length; }
set { _length = value; }
}
private ushort _startIndex; // first to be applied
private ushort _length; // length to be applied
private uint _tag; // OpenType feature tag
private uint _parameter; // feature parameter
}
///
/// OpenTypeLayout class provides access to OpenType Layout services
///
internal static unsafe class OpenTypeLayout
{
///
///
/// Font
/// Script to find
/// TagInfo, if script not present flags == None
///
/// Critical - Access protected font information (raw bytes)
///
[SecurityCritical]
internal static TagInfoFlags FindScript(
IOpenTypeFont Font, // In: Font access interface
uint ScriptTag // In
)
{
TagInfoFlags flags = TagInfoFlags.None;
try
{
FontTable gsubTable = Font.GetFontTable(OpenTypeTags.GSUB);
if (gsubTable.IsPresent)
{
GSUBHeader gsubHeader = new GSUBHeader(0);
if (!gsubHeader.GetScriptList(gsubTable).FindScript(gsubTable,ScriptTag).IsNull)
{
flags |= TagInfoFlags.Substitution;
}
}
}
catch (FileFormatException)
{
return TagInfoFlags.None;
}
try
{
FontTable gposTable = Font.GetFontTable(OpenTypeTags.GPOS);
if (gposTable.IsPresent)
{
GPOSHeader gposHeader = new GPOSHeader(0);
if (!gposHeader.GetScriptList(gposTable).FindScript(gposTable,ScriptTag).IsNull)
{
flags |= TagInfoFlags.Positioning;
}
}
}
catch (FileFormatException)
{
return TagInfoFlags.None;
}
return flags;
}
///
///
///
/// Font
/// Script to search in
/// LangGys to search for
/// TagInfoFlags, if script not present == None
///
/// Critical - access protected font resource (FontTable)
///
[SecurityCritical]
internal static TagInfoFlags FindLangSys(
IOpenTypeFont Font,
uint ScriptTag,
uint LangSysTag
)
{
TagInfoFlags flags = TagInfoFlags.None;
try
{
FontTable gsubTable = Font.GetFontTable(OpenTypeTags.GSUB);
if (gsubTable.IsPresent)
{
GSUBHeader gsubHeader = new GSUBHeader(0);
ScriptTable gsubScript = gsubHeader.GetScriptList(gsubTable).FindScript(gsubTable,ScriptTag);
if (!gsubScript.IsNull && !gsubScript.FindLangSys(gsubTable,LangSysTag).IsNull)
{
flags |= TagInfoFlags.Substitution;
}
}
}
catch (FileFormatException)
{
return TagInfoFlags.None;
}
try
{
FontTable gposTable = Font.GetFontTable(OpenTypeTags.GPOS);
if (gposTable.IsPresent)
{
GPOSHeader gposHeader = new GPOSHeader(0);
ScriptTable gposScript = gposHeader.GetScriptList(gposTable).FindScript(gposTable,ScriptTag);
if (!gposScript.IsNull && !gposScript.FindLangSys(gposTable,LangSysTag).IsNull)
{
flags |= TagInfoFlags.Positioning;
}
}
}
catch (FileFormatException)
{
return TagInfoFlags.None;
}
return flags;
}
/* This is unused code, but will be used later so it is just commented out for now.
///
/// Enumerates scripts in a font
///
internal static OpenTypeLayoutResult GetScriptList (
IOpenTypeFont Font, // In: Font access interface
out TagInfo[] Scripts // Out: Array of scripts supported
)
{
ushort i;
ushort GposNewTags;
Scripts=null; // Assignment required, because of out attribute.
// This value should be owerwritten later.
try
{
FontTable GsubTable = Font.GetFontTable(OpenTypeTags.GSUB);
FontTable GposTable = Font.GetFontTable(OpenTypeTags.GPOS);
GSUBHeader GsubHeader = new GSUBHeader(0);
GPOSHeader GposHeader = new GPOSHeader(0);
ScriptList GsubScriptList;
ScriptList GposScriptList;
ushort GsubScriptCount;
ushort GposScriptCount;
if (GsubTable.IsNotPresent && GposTable.IsNotPresent)
{
Scripts = new TagInfo[0];
return OpenTypeLayoutResult.Success;
}
if (GsubTable.IsPresent)
{
GsubScriptList = GsubHeader.GetScriptList(GsubTable);
GsubScriptCount = GsubScriptList.GetScriptCount(GsubTable);
}
else
{
GsubScriptList = new ScriptList(FontTable.InvalidOffset);
GsubScriptCount = 0;
}
if (GposTable.IsPresent)
{
GposScriptList = GposHeader.GetScriptList(GposTable);
GposScriptCount = GposScriptList.GetScriptCount(GposTable);
}
else
{
GposScriptList = new ScriptList(FontTable.InvalidOffset);
GposScriptCount = 0;
}
//This is true in most cases that there is no new tags in GPOS.
//So, we allocate this array then check GPOS for new tags
Scripts = new TagInfo[GsubScriptCount];
for(i=0; i0)
{
int CurrentScriptIndex=GposScriptCount;
//Allocate new array to fit all tags
TagInfo[] tmp = Scripts;
Scripts = new TagInfo[GsubScriptCount+GposNewTags];
Array.Copy(tmp,0,Scripts,0,tmp.Length);
for(i=0;i
/// Enumerates language systems for script
///
internal static OpenTypeLayoutResult GetLangSysList (
IOpenTypeFont Font, // In: Font access interface
uint ScriptTag, // In: Script tag
out TagInfo[] LangSystems // Out: Array of LangSystems for Script
)
{
ushort i;
ushort GposNewTags;
LangSystems=null; // Assignment required, because of out attribute.
// This value should be owerwritten later.
try
{
FontTable GsubTable = Font.GetFontTable(OpenTypeTags.GSUB);
FontTable GposTable = Font.GetFontTable(OpenTypeTags.GPOS);
GSUBHeader GsubHeader = new GSUBHeader(0);
GPOSHeader GposHeader = new GPOSHeader(0);
ScriptList GsubScriptList;
ScriptList GposScriptList;
ScriptTable GsubScript;
ScriptTable GposScript;
ushort GsubLangSysCount;
ushort GposLangSysCount;
if (GsubTable.IsNotPresent && GposTable.IsNotPresent)
{
return OpenTypeLayoutResult.ScriptNotFound;
}
if (GsubTable.IsPresent)
{
GsubScriptList = GsubHeader.GetScriptList(GsubTable);
GsubScript = GsubScriptList.FindScript(GsubTable,ScriptTag);
}
else
{
GsubScript = new ScriptTable(FontTable.InvalidOffset);
}
if (GposTable.IsPresent)
{
GposScriptList = GposHeader.GetScriptList(GposTable);
GposScript = GposScriptList.FindScript(GposTable,ScriptTag);
}
else
{
GposScript = new ScriptTable(FontTable.InvalidOffset);
}
if (GsubScript.IsNull && GposScript.IsNull)
{
return OpenTypeLayoutResult.ScriptNotFound;
}
if (!GsubScript.IsNull)
{
GsubLangSysCount = GsubScript.GetLangSysCount(GsubTable);
}
else
{
GsubLangSysCount = 0;
}
if (!GposScript.IsNull)
{
GposLangSysCount = GposScript.GetLangSysCount(GposTable);
}
else
{
GposLangSysCount = 0;
}
//This is true in most cases that there is no new tags in GPOS.
//So, we allocate this array then check GPOS for new tags
ushort CurrentLangSysIndex;
if (GsubScript.IsDefaultLangSysExists(GsubTable))
{
LangSystems = new TagInfo[GsubLangSysCount+1];
LangSystems[0].Tag = (uint)OpenTypeTags.dflt;
LangSystems[0].TagFlags = TagInfoFlags.Substitution;
CurrentLangSysIndex = 1;
}
else
{
LangSystems = new TagInfo[GsubLangSysCount];
CurrentLangSysIndex = 0;
}
for(i=0; i0)
{
//Allocate new array to fit all tags
TagInfo[] tmp = LangSystems;
LangSystems = new TagInfo[GsubLangSysCount+GposNewTags];
Array.Copy(tmp,0,LangSystems,0,tmp.Length);
if (GposScript.IsDefaultLangSysExists(GposTable))
{
if (TagInfo.IsNewTag(LangSystems,(uint)OpenTypeTags.dflt))
{
LangSystems[CurrentLangSysIndex].Tag = (uint)OpenTypeTags.dflt;
LangSystems[CurrentLangSysIndex].TagFlags = TagInfoFlags.Positioning;
++CurrentLangSysIndex;
}
else
{
int LangSysIndex = TagInfo.GetTagIndex(LangSystems,(uint)OpenTypeTags.dflt);
LangSystems[LangSysIndex].TagFlags |= TagInfoFlags.Positioning;
}
}
for(i=0;i
/// Enumerates features in a language system
///
internal static OpenTypeLayoutResult GetFeatureList (
IOpenTypeFont Font, // In: Font access interface
uint ScriptTag, // In: Script tag
uint LangSysTag, // In: LangSys tag
out TagInfo[] Features // Out: Array of features
)
{
ushort i;
ushort GposNewTags;
Features=null; // Assignment required, because of out attribute.
// This value should be owerwritten later.
try
{
FontTable GsubTable = Font.GetFontTable(OpenTypeTags.GSUB);
FontTable GposTable = Font.GetFontTable(OpenTypeTags.GPOS);
GSUBHeader GsubHeader = new GSUBHeader(0);
GPOSHeader GposHeader = new GPOSHeader(0);
ScriptList GsubScriptList;
ScriptList GposScriptList;
ScriptTable GsubScript;
ScriptTable GposScript;
LangSysTable GsubLangSys;
LangSysTable GposLangSys;
ushort GsubFeatureCount;
ushort GposFeatureCount;
FeatureList GsubFeatureList;
FeatureList GposFeatureList;
if (GsubTable.IsNotPresent && GposTable.IsNotPresent)
{
return OpenTypeLayoutResult.ScriptNotFound;
}
if (GsubTable.IsPresent)
{
GsubScriptList = GsubHeader.GetScriptList(GsubTable);
GsubScript = GsubScriptList.FindScript(GsubTable,ScriptTag);
GsubLangSys = GsubScript.FindLangSys(GsubTable,LangSysTag);
GsubFeatureList = GsubHeader.GetFeatureList(GsubTable);
}
else
{
GsubScript = new ScriptTable(FontTable.InvalidOffset);
GsubLangSys = new LangSysTable(FontTable.InvalidOffset);
GsubFeatureList = new FeatureList(FontTable.InvalidOffset);
}
if (GposTable.IsPresent)
{
GposScriptList = GposHeader.GetScriptList(GposTable);
GposScript = GposScriptList.FindScript(GposTable,ScriptTag);
GposLangSys = GposScript.FindLangSys(GposTable,LangSysTag);
GposFeatureList = GposHeader.GetFeatureList(GposTable);
}
else
{
GposScript = new ScriptTable(FontTable.InvalidOffset);
GposLangSys = new LangSysTable(FontTable.InvalidOffset);
GposFeatureList = new FeatureList(FontTable.InvalidOffset);
}
if (GsubScript.IsNull && GposScript.IsNull)
{
return OpenTypeLayoutResult.ScriptNotFound;
}
if (GsubLangSys.IsNull && GposLangSys.IsNull)
{
return OpenTypeLayoutResult.LangSysNotFound;
}
if (!GsubLangSys.IsNull)
{
GsubFeatureCount = GsubLangSys.FeatureCount(GsubTable);
}
else
{
GsubFeatureCount = 0;
}
if (!GposLangSys.IsNull)
{
GposFeatureCount = GposLangSys.FeatureCount(GposTable);
}
else
{
GposFeatureCount = 0;
}
Features = new TagInfo[GsubFeatureCount];
int CurrentFeatureIndex = 0;
for(i=0; i0)
{
//Allocate new array to fit all tags
TagInfo[] tmp = Features;
Features = new TagInfo[GsubFeatureCount+GposNewTags];
Array.Copy(tmp,0,Features,0,tmp.Length);
for(i=0;i
/// Substitutes glyphs according to features defined in the font.
///
/// In: Font access interface
/// In: Workspace for layout engine
/// In: Script tag
/// In: LangSys tag
/// In: List of features to apply
/// In: Actual number of features in
/// In: offset of input characters inside FeatureSet
/// In: Characters count (i.e. .Length);
/// In/out: Char to glyph mapping
/// In/out: List of GlyphInfo structs
/// Substitution result
///
/// Critical - access fonttable, which is protected... in addition charcount
/// parameters are passed directly to other code, which could result
/// in buffer reads outside of fonttable.
///
[SecurityCritical]
internal static OpenTypeLayoutResult SubstituteGlyphs(
IOpenTypeFont Font, // In: Font access interface
OpenTypeLayoutWorkspace workspace, // In: Workspace for layout engine
uint ScriptTag, // In: Script tag
uint LangSysTag, // In: LangSys tag
Feature[] FeatureSet, // In: List of features to apply
int featureCount, // In: Actual number of features in FeatureSet
int featureSetOffset,
int CharCount, // In: Characters count (i.e. Charmap.Length);
UshortList Charmap, // In/out: Char to glyph mapping
GlyphInfoList Glyphs // In/out: List of GlyphInfo structs
)
{
try
{
FontTable GsubTable = Font.GetFontTable(OpenTypeTags.GSUB);
if (!GsubTable.IsPresent) {return OpenTypeLayoutResult.ScriptNotFound;}
GSUBHeader GsubHeader = new GSUBHeader(0);
ScriptList ScriptList = GsubHeader.GetScriptList(GsubTable);
ScriptTable Script = ScriptList.FindScript(GsubTable,ScriptTag);
if (Script.IsNull) {return OpenTypeLayoutResult.ScriptNotFound;}
LangSysTable LangSys = Script.FindLangSys(GsubTable,LangSysTag);
if (LangSys.IsNull) {return OpenTypeLayoutResult.LangSysNotFound;}
FeatureList FeatureList = GsubHeader.GetFeatureList(GsubTable);
LookupList LookupList = GsubHeader.GetLookupList(GsubTable);
LayoutEngine.ApplyFeatures(
Font,
workspace,
OpenTypeTags.GSUB,
GsubTable,
new LayoutMetrics(), //it is not needed for substitution
LangSys,
FeatureList,
LookupList,
FeatureSet,
featureCount,
featureSetOffset,
CharCount,
Charmap,
Glyphs,
null,
null
);
}
catch (FileFormatException)
{
return OpenTypeLayoutResult.BadFontTable;
}
return OpenTypeLayoutResult.Success;
}
///
/// Position glyphs according to features defined in the font.
///
/// In: Font access interface
/// In: Workspace for layout engine
/// In: Script tag
/// In: LangSys tag
/// In: LayoutMetrics
/// In: List of features to apply
/// In: Actual number of features in
/// In: offset of input characters inside FeatureSet
/// In: Characters count (i.e. .Length);
/// In: Char to glyph mapping
/// In/out: List of GlyphInfo structs
/// In/out: Glyphs adv.widths
/// In/out: Glyph offsets
/// Substitution result
///
/// Critical - access fonttable, which is protected... in addition charcount
/// parameters are passed directly to other code, which could result
/// in buffer reads outside of fonttable.
///
[SecurityCritical]
internal static OpenTypeLayoutResult PositionGlyphs(
IOpenTypeFont Font,
OpenTypeLayoutWorkspace workspace,
uint ScriptTag,
uint LangSysTag,
LayoutMetrics Metrics,
Feature[] FeatureSet,
int featureCount,
int featureSetOffset,
int CharCount,
UshortList Charmap,
GlyphInfoList Glyphs,
int* Advances,
LayoutOffset* Offsets
)
{
try
{
FontTable GposTable = Font.GetFontTable(OpenTypeTags.GPOS);
if (!GposTable.IsPresent) {return OpenTypeLayoutResult.ScriptNotFound;}
GPOSHeader GposHeader = new GPOSHeader(0);
ScriptList ScriptList = GposHeader.GetScriptList(GposTable);
ScriptTable Script = ScriptList.FindScript(GposTable,ScriptTag);
if (Script.IsNull) {return OpenTypeLayoutResult.ScriptNotFound;}
LangSysTable LangSys = Script.FindLangSys(GposTable,LangSysTag);
if (LangSys.IsNull) {return OpenTypeLayoutResult.LangSysNotFound;}
FeatureList FeatureList = GposHeader.GetFeatureList(GposTable);
LookupList LookupList = GposHeader.GetLookupList(GposTable);
LayoutEngine.ApplyFeatures(
Font,
workspace,
OpenTypeTags.GPOS,
GposTable,
Metrics,
LangSys,
FeatureList,
LookupList,
FeatureSet,
featureCount,
featureSetOffset,
CharCount,
Charmap,
Glyphs,
Advances,
Offsets
);
}
catch (FileFormatException)
{
return OpenTypeLayoutResult.BadFontTable;
}
return OpenTypeLayoutResult.Success;
}
///
///
///
///
/// Critical - access fonttable, which is protected... in addition glyph range
/// parameters are passed directly to other code, which could result
/// in buffer reads outside of fonttable.
///
[SecurityCritical]
internal static OpenTypeLayoutResult CreateLayoutCache (
IOpenTypeFont font, // In: Font access interface
int maxCacheSize // In: Maximum cache size allowed
)
{
OpenTypeLayoutCache.CreateCache(font, maxCacheSize);
return OpenTypeLayoutResult.Success;
}
///
/// Internal method to test layout tables if they are uitable for fast path.
/// Returns list of script-langauge pairs that are not optimizable.
///
///
/// Critical - access fonttable, which is protected... in addition glyph range
/// parameters are passed directly to other code, which could result
/// in buffer reads outside of fonttable.
///
[SecurityCritical]
internal static OpenTypeLayoutResult GetComplexLanguageList (
IOpenTypeFont Font, //In: Font access interface
uint[] featureList, //In: Feature to look in
uint[] glyphBits,
ushort minGlyphId,
ushort maxGlyphId,
out WritingSystem[] complexLanguages
// Out: List of script/langauge pair
// that are not optimizable
)
{
try
{
WritingSystem[] gsubComplexLanguages = null;
WritingSystem[] gposComplexLanguages = null;
int gsubComplexLanguagesCount = 0;
int gposComplexLanguagesCount = 0;
FontTable GsubTable = Font.GetFontTable(OpenTypeTags.GSUB);
FontTable GposTable = Font.GetFontTable(OpenTypeTags.GPOS);
if (GsubTable.IsPresent)
{
LayoutEngine.GetComplexLanguageList(
OpenTypeTags.GSUB,
GsubTable,
featureList,
glyphBits,
minGlyphId,
maxGlyphId,
out gsubComplexLanguages,
out gsubComplexLanguagesCount
);
}
if (GposTable.IsPresent)
{
LayoutEngine.GetComplexLanguageList(
OpenTypeTags.GPOS,
GposTable,
featureList,
glyphBits,
minGlyphId,
maxGlyphId,
out gposComplexLanguages,
out gposComplexLanguagesCount
);
}
if (gsubComplexLanguages == null && gposComplexLanguages == null)
{
complexLanguages = null;
return OpenTypeLayoutResult.Success;
}
// Both tables have complex scrips, merge results
// Count gpos unique Languages
// and pack them at the same time
// so we do not research them again.
int gposNewLanguages=0, i, j;
for(i = 0; i < gposComplexLanguagesCount ;i++)
{
bool foundInGsub = false;
for(j = 0; j < gsubComplexLanguagesCount ;j++)
{
if (gsubComplexLanguages[j].scriptTag == gposComplexLanguages[i].scriptTag &&
gsubComplexLanguages[j].langSysTag == gposComplexLanguages[i].langSysTag
)
{
foundInGsub = true;
break;
};
}
if (!foundInGsub)
{
if (gposNewLanguages < i)
{
gposComplexLanguages[gposNewLanguages] = gposComplexLanguages[i];
}
gposNewLanguages++;
}
}
//realloc array for merged results, merge both arrays
complexLanguages = new WritingSystem[gsubComplexLanguagesCount + gposNewLanguages];
for(i = 0; i < gsubComplexLanguagesCount; i++)
{
complexLanguages[i] = gsubComplexLanguages[i];
}
for(i = 0; i < gposNewLanguages; i++)
{
complexLanguages[gsubComplexLanguagesCount + i] = gposComplexLanguages[i];
}
return OpenTypeLayoutResult.Success;
}
catch (FileFormatException)
{
complexLanguages = null;
return OpenTypeLayoutResult.BadFontTable;
}
}
}
internal struct WritingSystem
{
internal uint scriptTag;
internal uint langSysTag;
}
///
///
internal enum OpenTypeLayoutResult
{
Success,
InvalidParameter,
TableNotFound,
ScriptNotFound,
LangSysNotFound,
BadFontTable,
UnderConstruction
}
///
/// Class for internal OpenType use to store per font
/// information and temporary buffers.
///
/// We do not use fontcache now, so this information
/// will be recreated every time shaping engine
/// will be called, so
///
internal class OpenTypeLayoutWorkspace
{
///
/// Init buffers to initial values.
///
///
/// Critical: Calls unsafe code
/// Safe: Does not actually access data through the pointers
///
[SecurityCritical, SecurityTreatAsSafe]
internal unsafe OpenTypeLayoutWorkspace()
{
_bytesPerLookup = 0;
_lookupUsageFlags = null;
_cachePointers = null;
}
///
/// Reset all structures to the new font/OTTable/script/langsys.
///
/// Client need to call it only once per shaping engine call.
/// This is client's responsibility to ensure that workspace is
/// used for single font/OTTable/script/langsys between Init() calls
///
///In: Font access interface
///In: Font table tag
///In: Script tag
///In: Language System tag
///Success if workspace is initialized succesfully, specific error if failed
internal OpenTypeLayoutResult Init(
IOpenTypeFont font,
OpenTypeTags tableTag,
uint scriptTag,
uint langSysTag
)
{
// Currently all buffers are per call,
// no need to do anything.
return OpenTypeLayoutResult.Success;
}
#region Lookup flags
//lookup usage flags access
private const byte AggregatedFlagMask = 0x01;
private const byte RequiredFeatureFlagMask = 0x02;
private const int FeatureFlagsStartBit = 2;
public void InitLookupUsageFlags(int lookupCount, int featureCount)
{
_bytesPerLookup = (featureCount + FeatureFlagsStartBit + 7) >> 3;
int requiredLookupUsageArraySize = lookupCount * _bytesPerLookup;
if ( _lookupUsageFlags == null ||
_lookupUsageFlags.Length < requiredLookupUsageArraySize)
{
_lookupUsageFlags = new byte[requiredLookupUsageArraySize];
}
Array.Clear(_lookupUsageFlags, 0, requiredLookupUsageArraySize);
}
public bool IsAggregatedFlagSet(int lookupIndex)
{
return ((_lookupUsageFlags[lookupIndex * _bytesPerLookup] & AggregatedFlagMask) != 0);
}
public bool IsFeatureFlagSet(int lookupIndex, int featureIndex)
{
int flagIndex = featureIndex + FeatureFlagsStartBit;
int flagByte = (lookupIndex * _bytesPerLookup) + (flagIndex >> 3);
byte flagMask = (byte)(1 << (flagIndex % 8));
return ((_lookupUsageFlags[flagByte] & flagMask) != 0);
}
public bool IsRequiredFeatureFlagSet(int lookupIndex)
{
return ((_lookupUsageFlags[lookupIndex * _bytesPerLookup] & RequiredFeatureFlagMask) != 0);
}
public void SetFeatureFlag(int lookupIndex, int featureIndex)
{
int startLookupByte = lookupIndex * _bytesPerLookup;
int flagIndex = featureIndex + FeatureFlagsStartBit;
int flagByte = startLookupByte + (flagIndex >> 3);
byte flagMask = (byte)(1 << (flagIndex % 8));
if (flagByte >= _lookupUsageFlags.Length)
{
//This should be invalid font. Lookup associated with the feature is not in lookup array.
throw new FileFormatException();
}
_lookupUsageFlags[flagByte] |= flagMask;
// Also set agregated usage flag
_lookupUsageFlags[startLookupByte] |= AggregatedFlagMask;
}
public void SetRequiredFeatureFlag(int lookupIndex)
{
int flagByte = lookupIndex * _bytesPerLookup;
if (flagByte >= _lookupUsageFlags.Length)
{
//This should be invalid font. Lookup associated with the feature is not in lookup array.
throw new FileFormatException();
}
//set RequiredFeature and aggregated flag at the same time
_lookupUsageFlags[flagByte] |= (AggregatedFlagMask | RequiredFeatureFlagMask);
}
// Define cache which lookup is enabled by which feature.
// Buffer grows with number of features applied
private int _bytesPerLookup;
private byte[] _lookupUsageFlags;
#endregion Lookup flags
#region Layout cache pointers
///
/// Allocate enough memory for array of cache pointers, parallel to glyph run.
///
/// These method should not be used directly, it is only called by OpenTypeLayputCache.
///
///
///In: Size of a glyph run
///
/// Critical: Calls unsafe code
/// Safe: Does not actually access data through the pointers
///
[SecurityCritical, SecurityTreatAsSafe]
public unsafe void AllocateCachePointers(int glyphRunLength)
{
if (_cachePointers != null && _cachePointers.Length >= glyphRunLength) return;
_cachePointers = new ushort*[glyphRunLength];
}
///
/// If glyph run is cahnged, update pointers according to the change. Reallocate array if necessary.
///
/// These method should not be used directly, it is only called by OpenTypeLayputCache.
///
///
///In: Number of glyphs in the run before change
///In: Number of glyphs in the run after change
///In: Index of the first changed glyph
///In: Index of the glyph after last changed
///
/// Critical: Calls unsafe code
/// Safe: Does not actually access data through the pointers
///
[SecurityCritical, SecurityTreatAsSafe]
public unsafe void UpdateCachePointers(
int oldLength,
int newLength,
int firstGlyphChanged,
int afterLastGlyphChanged
)
{
if (oldLength != newLength)
{
int oldAfterLastGlyphChanged = afterLastGlyphChanged - (newLength - oldLength);
if (_cachePointers.Length < newLength)
{
ushort*[] tmp = new ushort*[newLength];
Array.Copy(_cachePointers, tmp, firstGlyphChanged);
Array.Copy(_cachePointers, oldAfterLastGlyphChanged, tmp, afterLastGlyphChanged, oldLength - oldAfterLastGlyphChanged);
_cachePointers = tmp;
}
else
{
Array.Copy(_cachePointers, oldAfterLastGlyphChanged, _cachePointers, afterLastGlyphChanged, oldLength - oldAfterLastGlyphChanged);
}
}
}
///
/// Critical: Exposes font cache raw pointers
///
public unsafe ushort*[] CachePointers
{
[SecurityCritical]
get { return _cachePointers; }
}
///
/// Critical: Exposes font cache raw pointers
///
public unsafe byte* TableCacheData
{
[SecurityCritical]
get { return _tableCache; }
[SecurityCritical]
set { _tableCache = value; }
}
// Array of cache pointers, per glyph
///
/// Critical: This holds font cache raw pointers
///
[SecurityCritical]
private unsafe ushort*[] _cachePointers;
// Pointer to the table cache
///
/// Critical: This holds font cache raw pointers
///
[SecurityCritical]
private unsafe byte* _tableCache;
#endregion Layout cache pointers
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- XmlSchemaChoice.cs
- EncoderParameter.cs
- WebRequestModuleElementCollection.cs
- TimeSpanValidator.cs
- RepeaterItemEventArgs.cs
- SignedPkcs7.cs
- Message.cs
- XPathDocument.cs
- EditorZone.cs
- SystemColorTracker.cs
- ContentElementAutomationPeer.cs
- FontWeightConverter.cs
- SecurityKeyEntropyMode.cs
- SoapUnknownHeader.cs
- StrokeRenderer.cs
- mediaeventshelper.cs
- Encoding.cs
- ProgressBarHighlightConverter.cs
- Clause.cs
- DiscoveryReference.cs
- Constraint.cs
- DeviceContexts.cs
- UndoUnit.cs
- CacheMode.cs
- StsCommunicationException.cs
- VisualTreeUtils.cs
- BinaryUtilClasses.cs
- HttpSysSettings.cs
- TransformPatternIdentifiers.cs
- UserPersonalizationStateInfo.cs
- EnlistmentState.cs
- C14NUtil.cs
- InvalidContentTypeException.cs
- GradientStopCollection.cs
- CollectionType.cs
- FixedDocument.cs
- XmlILModule.cs
- AutomationPatternInfo.cs
- UrlMappingsModule.cs
- PrinterSettings.cs
- DataGridViewRowsRemovedEventArgs.cs
- NamedPipeAppDomainProtocolHandler.cs
- XslException.cs
- RenamedEventArgs.cs
- WindowsFormsHelpers.cs
- SQLMoneyStorage.cs
- CriticalExceptions.cs
- AuthenticationService.cs
- ListItemViewControl.cs
- HttpConfigurationSystem.cs
- LicenseManager.cs
- TreeNode.cs
- SelectorAutomationPeer.cs
- SafeEventLogWriteHandle.cs
- EventData.cs
- QilInvokeLateBound.cs
- Point3D.cs
- Soap11ServerProtocol.cs
- XmlSchemaRedefine.cs
- BufferedOutputStream.cs
- SizeConverter.cs
- TreeNodeBindingCollection.cs
- SelectionHighlightInfo.cs
- Helpers.cs
- ReflectEventDescriptor.cs
- CancellationState.cs
- BitmapSourceSafeMILHandle.cs
- BamlLocalizer.cs
- ExpressionPrefixAttribute.cs
- ObjectFullSpanRewriter.cs
- FrameworkElementFactoryMarkupObject.cs
- RemotingException.cs
- UnicodeEncoding.cs
- BoolExpr.cs
- CommandField.cs
- BinHexDecoder.cs
- TemplatedMailWebEventProvider.cs
- WeakReferenceEnumerator.cs
- WindowsSolidBrush.cs
- OleDbFactory.cs
- XmlDocumentFragment.cs
- ReflectionHelper.cs
- DrawingCollection.cs
- SoapWriter.cs
- TabOrder.cs
- XpsPackagingPolicy.cs
- DataException.cs
- DoubleConverter.cs
- StoreItemCollection.Loader.cs
- Stack.cs
- Paragraph.cs
- HttpDebugHandler.cs
- ToolStripDropTargetManager.cs
- RC2.cs
- MediaElementAutomationPeer.cs
- PermissionSetTriple.cs
- CursorConverter.cs
- QueryParameter.cs
- ScriptMethodAttribute.cs
- MultiDataTrigger.cs