123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708 |
- #if !WITHOUT_UNISCRIBE
- using FastReport.Fonts;
- using FastReport.Utils;
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Drawing;
- using System.Drawing.Imaging;
- using System.IO;
- using System.Linq;
- using System.Runtime.InteropServices;
- namespace FastReport.Export.TTF
- {
- internal partial class ExportTTFFont
- {
- #region DLL import
- [DllImport("Gdi32.dll")]
- private static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
- [DllImport("Gdi32.dll")]
- private static extern IntPtr DeleteObject(IntPtr hgdiobj);
- [DllImport("Gdi32.dll")]
- private static extern int GetOutlineTextMetrics(IntPtr hdc, int cbData, ref TrueTypeFont.OutlineTextMetric lpOTM);
- [DllImport("Gdi32.dll", CharSet = CharSet.Auto)]
- private static extern uint GetFontData(IntPtr hdc, uint dwTable, uint dwOffset, [In, Out] IntPtr lpvBuffer, uint cbData);
- [DllImport("Gdi32.dll")]
- private static extern int GetCharWidthI(IntPtr hdc, int giFirst, int cgi, [MarshalAs(UnmanagedType.LPArray)] ushort[] pgi, [Out, MarshalAs(UnmanagedType.LPArray)] int[] piWidths);
- [DllImport("usp10.dll")]
- private static extern int ScriptFreeCache(ref IntPtr psc);
- [DllImport("usp10.dll")]
- private static extern int ScriptIsComplex(
- [MarshalAs(UnmanagedType.LPWStr)] string pwcInChars,
- int cInChars, int dwFlags);
- [DllImport("usp10.dll")]
- private static extern int ScriptItemize(
- [MarshalAs(UnmanagedType.LPWStr)] string pwcInChars, int cInChars, int cMaxItems,
- ref SCRIPT_CONTROL psControl, ref SCRIPT_STATE psState, [In, Out] SCRIPT_ITEM[] pItems, ref int pcItems);
- [DllImport("usp10.dll")]
- private static extern int ScriptLayout(
- int cRuns, [MarshalAs(UnmanagedType.LPArray)] byte[] pbLevel,
- [MarshalAs(UnmanagedType.LPArray)] int[] piVisualToLogical,
- [MarshalAs(UnmanagedType.LPArray)] int[] piLogicalToVisual);
- [DllImport("usp10.dll")]
- private static extern int ScriptShape(
- IntPtr hdc, ref IntPtr psc, [MarshalAs(UnmanagedType.LPWStr)] string pwcChars,
- int cChars, int cMaxGlyphs, ref SCRIPT_ANALYSIS psa,
- [Out, MarshalAs(UnmanagedType.LPArray)] ushort[] pwOutGlyphs,
- [Out, MarshalAs(UnmanagedType.LPArray)] ushort[] pwLogClust,
- [Out, MarshalAs(UnmanagedType.LPArray)] SCRIPT_VISATTR[] psva, ref int pcGlyphs);
- [DllImport("usp10.dll")]
- private static extern int ScriptPlace(
- IntPtr hdc, ref IntPtr psc, [MarshalAs(UnmanagedType.LPArray)] ushort[] pwGlyphs,
- int cGlyphs, [MarshalAs(UnmanagedType.LPArray)] SCRIPT_VISATTR[] psva,
- ref SCRIPT_ANALYSIS psa, [MarshalAs(UnmanagedType.LPArray)] int[] piAdvance,
- [Out, MarshalAs(UnmanagedType.LPArray)] GOFFSET[] pGoffset, ref ABC pABC);
- [DllImport("usp10.dll")]
- private static extern uint ScriptRecordDigitSubstitution(uint lcid, ref SCRIPT_DIGITSUBSTITUTE psds);
- [DllImport("usp10.dll")]
- private static extern int ScriptApplyDigitSubstitution(
- ref SCRIPT_DIGITSUBSTITUTE psds, ref SCRIPT_CONTROL psc, ref SCRIPT_STATE pss);
- #endregion
- #region Uniscribe Structures
- [StructLayout(LayoutKind.Sequential)]
- public struct SCRIPT_STATE
- {
- public short data;
- public int uBidiLevel
- {
- get { return data & 0x001F; }
- }
- public void SetRtl()
- {
- data = 0x801;
- }
- }
- [StructLayout(LayoutKind.Sequential)]
- public struct SCRIPT_ANALYSIS
- {
- public short data;
- public SCRIPT_STATE state;
- }
- [StructLayout(LayoutKind.Sequential)]
- public struct SCRIPT_CONTROL
- {
- public int data;
- }
- [StructLayout(LayoutKind.Sequential)]
- public struct SCRIPT_DIGITSUBSTITUTE
- {
- public short NationalDigitLanguage;
- public short TraditionalDigitLanguage;
- public byte DigitSubstitute;
- public int dwReserved;
- }
- [StructLayout(LayoutKind.Sequential)]
- public struct SCRIPT_ITEM
- {
- public int iCharPos;
- public SCRIPT_ANALYSIS analysis;
- }
- [StructLayout(LayoutKind.Sequential)]
- public struct SCRIPT_VISATTR
- {
- // WORD uJustification : 4;
- // WORD fClusterStart : 1;
- // WORD fDiacritic : 1;
- // WORD fZeroWidth : 1;
- // WORD fReserved : 1;
- // WORD fShapeReserved : 8;
- public ushort data;
- public ushort uJustification
- {
- get { return (ushort)(data & 15u); }
- }
- public bool fClusterStart
- {
- get { return (data & 16u) == 16u; }
- }
- public bool fDiacritic
- {
- get { return (data & 32u) == 32u; }
- }
- public bool fZeroWidth
- {
- get { return (data & 64u) == 64u; }
- }
- public bool fReserved
- {
- get { return (data & 128u) == 128u; }
- }
- public byte fShapeReserved
- {
- get { return (byte)((data & 65280u) / 256); }
- }
- }
- [StructLayout(LayoutKind.Sequential)]
- public struct GOFFSET
- {
- public int du;
- public int dv;
- }
- [StructLayout(LayoutKind.Sequential)]
- public struct ABC
- {
- public int abcA;
- public int abcB;
- public int abcC;
- }
- #endregion
- #region EMF structures
- public const int LF_FACESIZE = 32;
- public const int LF_FULLFACESIZE = 64;
- public const int ELF_VENDOR_SIZE = 4;
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
- public struct EMREXTCREATEFONTINDIRECT
- {
- public int ihFont; // Font handle index
- public EXTLOGFONT elfw;
- }
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
- public struct EXTLOGFONT
- {
- public LOGFONT elfLogFont;
- [MarshalAs(UnmanagedType.ByValTStr, SizeConst = LF_FULLFACESIZE)]
- public string elfFullName;
- [MarshalAs(UnmanagedType.ByValTStr, SizeConst = LF_FACESIZE)]
- public string elfStyle;
- public int elfVersion; // 0 for the first release of NT
- public int elfStyleSize;
- public int elfMatch;
- public int elfReserved;
- [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I1, SizeConst = ELF_VENDOR_SIZE)]
- public byte[] elfVendorId;
- public int elfCulture; // 0 for Latin
- public PANOSE elfPanose;
- }
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Size = 92)]
- public struct LOGFONT
- {
- public int lfHeight;
- public int lfWidth;
- public int lfEscapement;
- public int lfOrientation;
- public int lfWeight;
- public byte lfItalic;
- public byte lfUnderline;
- public byte lfStrikeOut;
- public byte lfCharSet;
- public byte lfOutPrecision;
- public byte lfClipPrecision;
- public byte lfQuality;
- public byte lfPitchAndFamily;
- [MarshalAs(UnmanagedType.ByValTStr, SizeConst = LF_FACESIZE)]
- public string lfFaceName;
- }
- [StructLayout(LayoutKind.Sequential)]
- public struct PANOSE
- {
- public byte bFamilyType;
- public byte bSerifStyle;
- public byte bWeight;
- public byte bProportion;
- public byte bContrast;
- public byte bStrokeVariation;
- public byte bArmStyle;
- public byte bLetterform;
- public byte bMidline;
- public byte bXHeight;
- }
- #endregion
- #region Private variables
- private Cache cache = new Cache();
- private SCRIPT_DIGITSUBSTITUTE digitSubstitute;
- #endregion
- #region Private methods
- public void FillOutlineTextMetrix()
- {
- if (sourceFont != null)
- {
- using (Graphics g = Graphics.FromImage(tempBitmap))
- {
- IntPtr hdc = g.GetHdc();
- IntPtr f = sourceFont.ToHfont();
- try
- {
- SelectObject(hdc, f);
- GetOutlineTextMetrics(hdc, Marshal.SizeOf(typeof(TrueTypeFont.OutlineTextMetric)), ref textMetric);
- }
- finally
- {
- DeleteObject(f);
- g.ReleaseHdc(hdc);
- }
- }
- }
- }
- internal static void GDI32_GetFontData(Font font, out IntPtr font_data, out FontType CollectionMode, out uint fontDataSize)
- {
- font_data = IntPtr.Zero;
- Bitmap tempBitmap = new Bitmap(1, 1);
- using (Graphics g = Graphics.FromImage(tempBitmap))
- {
- IntPtr hdc = g.GetHdc();
- IntPtr f = font.ToHfont();
- SelectObject(hdc, f);
- try
- {
- // Try to read TrueTypeCollection
- CollectionMode = FontType.TrueTypeCollection;
- fontDataSize = GetFontData(hdc, (uint)CollectionMode, 0, IntPtr.Zero, 0);
- if (fontDataSize == uint.MaxValue)
- {
- CollectionMode = FontType.TrueTypeFont;
- fontDataSize = GetFontData(hdc, (uint)CollectionMode, 0, IntPtr.Zero, 0);
- }
- font_data = Marshal.AllocHGlobal((int)fontDataSize);
- GetFontData(hdc, (uint)CollectionMode, 0, font_data, fontDataSize);
- }
- finally
- {
- DeleteObject(f);
- g.ReleaseHdc(hdc);
- tempBitmap.Dispose();
- }
- }
- }
- internal static FontStream GetFontStream(Font source_font)
- {
- MemoryStream mem_stream;
- IntPtr font_image;
- FontType mode;
- uint size;
- GDI32_GetFontData(source_font, out font_image, out mode, out size);
- byte[] buffer = new byte[size];
- Marshal.Copy(font_image, buffer, 0, buffer.Length);
- mem_stream = new MemoryStream(buffer);
- buffer = null;
- return new FontStream(mem_stream);
- }
- private Font[] GetFallbackFonts(IntPtr hdc, Font originalFont, string text)
- {
- // sad but true. To obtain a fallback font, we draw a string on a metafile, then analyse its records.
- // https://chromium.googlesource.com/chromium/src/+/22aed04422b04b2cf04f7b7d61392da4e9a2c85a/ui/gfx/font_fallback_win.cc
- Metafile mf = new Metafile(hdc, EmfType.EmfOnly);
- using (Graphics g = Graphics.FromImage(mf))
- {
- g.DrawString(text, originalFont, Brushes.Black, 0, 0);
- }
- List<Font> fallbackFonts = new List<Font>();
- using (Graphics g = Graphics.FromHdc(hdc))
- {
- g.EnumerateMetafile(mf, Point.Empty, (EmfPlusRecordType recordType, int flags, int dataSize, IntPtr data, PlayRecordCallback callbackData) =>
- {
- if (recordType == EmfPlusRecordType.EmfExtCreateFontIndirect)
- {
- EMREXTCREATEFONTINDIRECT rec = (EMREXTCREATEFONTINDIRECT)Marshal.PtrToStructure(data, typeof(EMREXTCREATEFONTINDIRECT));
- fallbackFonts.Add(Font.FromLogFont(rec.elfw.elfLogFont));
- }
- return true;
- });
- }
- return fallbackFonts.ToArray();
- }
- private RunInfo GetRunInfo(IntPtr hdc, Run run, Font originalFont, Font measureFont)
- {
- // initialize structures
- int maxGlyphs = run.text.Length * 3;
- SCRIPT_ANALYSIS psa = run.analysis;
- ushort[] clusters = new ushort[maxGlyphs];
- SCRIPT_VISATTR[] psva = new SCRIPT_VISATTR[maxGlyphs];
- GOFFSET[] gOffset = new GOFFSET[maxGlyphs];
- ABC abc = new ABC();
- ushort[] glyphs = new ushort[maxGlyphs];
- int[] widths = new int[maxGlyphs];
- int numGlyphs = 0;
- int fallbackIndex = -1;
- bool hasEmptyGlyphs = false;
- Font font = originalFont;
- Font[] fallbackFonts = null;
- var cacheItem = cache.GetCacheItem(hdc, measureFont);
- while (true)
- {
- // make glyphs
- ScriptShape(hdc, ref cacheItem.usCache, run.text, run.text.Length, glyphs.Length, ref psa, glyphs, clusters, psva, ref numGlyphs);
- // check missing glyphs
- hasEmptyGlyphs = false;
- if (numGlyphs == 0)
- hasEmptyGlyphs = true;
- else
- {
- for (int i = 0; i < numGlyphs; i++)
- {
- if (glyphs[i] == 0)
- {
- hasEmptyGlyphs = true;
- break;
- }
- }
- }
- if (!hasEmptyGlyphs)
- break;
- else
- {
- // first call - generate fallback fonts list
- if (fallbackIndex == -1)
- {
- // obtain fallback fonts. Use sourceFont instead of originalFont as a source (because of round errors in case of small fonts).
- // sourceFont is 750pt size.
- fallbackFonts = GetFallbackFonts(hdc, sourceFont, run.text);
- }
- fallbackIndex++;
- if (fallbackIndex >= fallbackFonts.Length)
- break;
- Font fallbackFont = fallbackFonts[fallbackIndex];
- // fallback font size may not be the same as original (case: japanese fonts)
- float fallbackSizeRatio = fallbackFont.SizeInPoints / sourceFont.SizeInPoints;
- // measureFont must be the same size as sourceFont (750pt)
- measureFont = new Font(fallbackFont.FontFamily, sourceFont.Size, sourceFont.Style);
- // finally, the font that will be used in runInfo
- font = new Font(fallbackFont.FontFamily, originalFont.Size * fallbackSizeRatio, originalFont.Style);
- cacheItem = cache.GetCacheItem(hdc, measureFont);
- }
- }
- // make widths
- ScriptPlace(hdc, ref cacheItem.usCache, glyphs, numGlyphs, psva, ref psa, widths, gOffset, ref abc);
- RunInfo runInfo = new RunInfo(numGlyphs)
- {
- Font = font
- };
- if (numGlyphs > 0)
- {
- // make ToUnicode
- for (int i = 0; i < run.text.Length; i++)
- {
- int glyphIndex = clusters[i];
- if (String.IsNullOrEmpty(runInfo.GlyphToUnicode[glyphIndex]))
- runInfo.GlyphToUnicode[glyphIndex] = "";
- runInfo.GlyphToUnicode[glyphIndex] += run.text[i];
- }
- }
- // get glyph widths (non-shaped) and use it instead advance widths provided by ScriptPlace
- // The reason is that gdi+ does not use kerning. Widths returned by ScriptPlace may vary,
- // we need constant glyph width to set up pdf Widths array
- int[] gwidths = font == originalFont ?
- GetGlyphWidths(hdc, numGlyphs, glyphs) : // no font fallback, use faster method
- GetGlyphWidthsSlow(hdc, numGlyphs, glyphs);
- for (int i = 0; i < numGlyphs; i++)
- {
- runInfo.Glyphs[i] = glyphs[i];
- runInfo.Widths[i] = gwidths[i];
- // we may get kerning like this, but it will result in wrong alignment of right- or center-aligned text because gdi+ draws/measures text w/o kerning
- //runInfo.Kernings[i] = widths[i] - gwidths[i];
- // the only kerning we use is one for complex script's special symbols
- // works but requires width correction
- /*runInfo.Kernings[i] = -gOffset[i].du + widths[i] - gwidths[i];
- if (i > 0)
- {
- runInfo.Kernings[i - 1] += gOffset[i].du;
- }
- else
- {
- runInfo.ZeroOffsetX = gOffset[i].du * font.Size / sourceFont.Size;
- }
- // this should be converted to objfont units
- runInfo.VerticalOffsets[i] = -gOffset[i].dv * font.Size / sourceFont.Size;*/
- // works
- if (gOffset[i].du != 0 || gOffset[i].dv != 0)
- {
- if (i > 0)
- {
- if (gOffset[i - 1].du != 0)
- runInfo.Kernings[i - 1] += gOffset[i].du; // do not kern twice (case: Calibri + arabic)
- else
- runInfo.Kernings[i - 1] = widths[i - 1] - gwidths[i - 1] + gOffset[i].du;
- }
- else
- {
- runInfo.ZeroOffsetX = gOffset[i].du * font.Size / sourceFont.Size;
- }
- runInfo.Kernings[i] = widths[i] - gwidths[i] - gOffset[i].du;
- // this should be converted to objfont units
- runInfo.VerticalOffsets[i] = -gOffset[i].dv * font.Size / sourceFont.Size;
- }
- }
- return runInfo;
- }
- private RunInfo GetRunInfoNonComplex(string text, Font originalFont)
- {
- int numGlyphs;
- ushort[] glyphs;
- float[] widths;
- float[] kernings;
- using (var ttfState = new TTFState(sourceFont))
- {
- numGlyphs = ttfState.Value.GetGlyphIndices(text, 1000, out glyphs, out widths, out kernings, false);
- }
- RunInfo runInfo = new RunInfo(numGlyphs)
- {
- Font = originalFont
- };
- for (int i = 0; i < numGlyphs; i++)
- {
- runInfo.Glyphs[i] = glyphs[i];
- runInfo.Widths[i] = (float)Math.Round(widths[i]);
- runInfo.GlyphToUnicode[i] = text[i].ToString();
- }
- return runInfo;
- }
- private int[] GetGlyphWidthsSlow(IntPtr hdc, int numGlyphs, ushort[] glyphs)
- {
- int[] gwidths = new int[numGlyphs];
- // this api is slow, avoid using it at any cost
- GetCharWidthI(hdc, 0, numGlyphs, glyphs, gwidths);
- return gwidths;
- }
- private int[] GetGlyphWidths(IntPtr hdc, int numGlyphs, ushort[] glyphs)
- {
- int[] glyphWidths = new int[numGlyphs];
- for (int i = 0; i < numGlyphs; i++)
- {
- ushort glyph = glyphs[i];
- if (usedGlyphChars.TryGetValue(glyph, out GlyphChar glyphChar))
- {
- // get existing glyph width
- glyphWidths[i] = (int)(glyphChar.Width);
- }
- else
- {
- // use slow api to get single glyph width.
- // Note that it is better to get all missing glyphs at a time; but in real word this won't be any faster
- glyphWidths[i] = GetGlyphWidthsSlow(hdc, 1, new ushort[] { glyph })[0];
- }
- }
- return glyphWidths;
- }
- private List<Run> Itemize(string s, bool rtl)
- {
- int maxItems = s.Length * 2;
- SCRIPT_ITEM[] pItems = new SCRIPT_ITEM[maxItems];
- int pcItems = 0;
- // initialize Control and State
- SCRIPT_CONTROL control = new SCRIPT_CONTROL();
- SCRIPT_STATE state = new SCRIPT_STATE();
- if (rtl)
- {
- // this is needed to start paragraph from right
- state.SetRtl();
- // to substitute arabic digits
- ScriptApplyDigitSubstitution(ref digitSubstitute, ref control, ref state);
- }
- // itemize
- ScriptItemize(s, s.Length, pItems.Length, ref control, ref state, pItems, ref pcItems);
- // create Run list. Note that ScriptItemize actually returns pcItems+1 items,
- // so this can be used to calculate char range easily
- List<Run> list = new List<Run>();
- for (int i = 0; i < pcItems; i++)
- {
- string text = s.Substring(pItems[i].iCharPos, pItems[i + 1].iCharPos - pItems[i].iCharPos);
- list.Add(new Run(text, pItems[i].analysis));
- }
- return list;
- }
- private List<Run> Layout(List<Run> runs)
- {
- byte[] pbLevel = new byte[runs.Count];
- int[] piVisualToLogical = new int[runs.Count];
- // build the pbLevel array
- for (int i = 0; i < runs.Count; i++)
- pbLevel[i] = (byte)runs[i].analysis.state.uBidiLevel;
- // layout runs
- ScriptLayout(runs.Count, pbLevel, piVisualToLogical, null);
- // return runs in their visual order
- List<Run> visualRuns = new List<Run>();
- for (int i = 0; i < piVisualToLogical.Length; i++)
- visualRuns.Add(runs[piVisualToLogical[i]]);
- return visualRuns;
- }
- private List<RunInfo> LayoutString(string str, bool rtl, Font originalFont)
- {
- List<RunInfo> result = new List<RunInfo>();
- bool isComplex = ScriptIsComplex(str, str.Length, 1 /*SIC_COMPLEX*/) != 1;
- if (!isComplex)
- {
- RunInfo runInfo = GetRunInfoNonComplex(str, originalFont);
- result.Add(runInfo);
- return result;
- }
- using (Bitmap b = new Bitmap(1, 1))
- {
- using (Graphics g = Graphics.FromImage(b))
- {
- IntPtr hdc = g.GetHdc();
- try
- {
- List<Run> runs = Itemize(str, rtl);
- runs = Layout(runs);
- float offsetX = 0;
- foreach (Run run in runs)
- {
- RunInfo runInfo = GetRunInfo(hdc, run, originalFont, sourceFont);
- offsetX += runInfo.ZeroOffsetX;
- runInfo.OffsetX = offsetX;
- offsetX += (runInfo.Widths.Sum() + runInfo.Kernings.Sum()) * runInfo.Font.Size / sourceFont.Size;
- result.Add(runInfo);
- }
- return result;
- }
- finally
- {
- g.ReleaseHdc(hdc);
- }
- }
- }
- }
- private void InternalInit()
- {
- dpiFX = 96f / DrawUtils.ScreenDpi;
- digitSubstitute = new SCRIPT_DIGITSUBSTITUTE();
- ScriptRecordDigitSubstitution(0x0400, ref digitSubstitute);
- }
- private void InternalDispose()
- {
- cache.Clear();
- }
- #endregion
- #region Private classes
- private class Run
- {
- public SCRIPT_ANALYSIS analysis;
- public string text;
- public Run(string text, SCRIPT_ANALYSIS a)
- {
- this.text = text;
- this.analysis = a;
- }
- }
- #if !CACHED_POINTERS
- private class Cache
- {
- private Dictionary<Font, CacheItem> cache = new Dictionary<Font, CacheItem>();
- public CacheItem GetCacheItem(IntPtr hdc, Font f)
- {
- lock (cache)
- {
- CacheItem cacheItem;
- if (!cache.TryGetValue(f, out cacheItem))
- {
- cacheItem = new CacheItem() { hFont = f.ToHfont(), usCache = IntPtr.Zero };
- cache.Add(f, cacheItem);
- }
- SelectObject(hdc, cacheItem.hFont);
- return cacheItem;
- }
- }
- public void Clear()
- {
- var values = cache.Values.ToArray();
- for (int i = 0; i < values.Length; i++)
- {
- var item = values[i];
- ScriptFreeCache(ref item.usCache);
- DeleteObject(item.hFont);
- }
- cache.Clear();
- }
- }
- private class CacheItem
- {
- public IntPtr hFont;
- public IntPtr usCache;
- }
- #endif
- #endregion
- }
- }
- #endif
|