123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- #if SKIA
- using FastReport.Fonts;
- using SkiaSharp;
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Drawing;
- using System.Drawing.Text;
- using System.IO;
- using System.Runtime.InteropServices;
- namespace FastReport
- {
- public static partial class FontManager
- {
- // do not remove
- private static readonly CharacterMatcher characterMatcher = new();
- private static readonly FontFamilyMatcher1 fontFamilyMatcher1 = new();
- // Note to future implementors:
- // this might be added to gdi/uniscribe as well, but it produces not-so-good results,
- // because we cannot apply this to text measure phase, only to pdf render phase.
- private static List<FontFallback> FallbackFonts { get; } = new();
- /// <summary>
- /// Adds a new fallback font.
- /// </summary>
- /// <param name="fontName">The font name, e.g. "Noto Sans CJK JP".</param>
- /// <remarks>
- /// Fallback font is used to display characters that not present in the original font. It is often used for CJK.
- /// For example, you may add "Noto Sans CJK JP" fallback. User defined fallback fonts will then be checked first.
- /// </remarks>
- public static void AddFallbackFont(string fontName)
- {
- FallbackFonts.Add(new(fontName));
- }
- /// <summary>
- /// Removes specified fallback font.
- /// </summary>
- /// <param name="fontName">The font name.</param>
- public static void RemoveFallbackFont(string fontName)
- {
- for (int i = 0; i < FallbackFonts.Count; i++)
- {
- if (FallbackFonts[i].Name == fontName)
- {
- FallbackFonts.RemoveAt(i);
- i--;
- }
- }
- }
- /// <summary>
- /// Clears all fallback fonts.
- /// </summary>
- public static void ClearFallbackFonts()
- {
- FallbackFonts.Clear();
- }
- private static void AddFontInternal(byte[] buffer, PrivateFontCollection collection)
- {
- var ms = new MemoryStream(buffer);
- using (FontStream fs = new FontStream(ms))
- {
- var list = TrueTypeCollection.CreateFonts(fs);
- for (int ttcIndex = 0; ttcIndex < list.Count; ttcIndex++)
- {
- var ttf = list[ttcIndex];
- collection.AddFont(buffer, ttf.Family, ttcIndex);
- }
- }
- }
- /// <summary>
- /// Adds a font from a memory buffer.
- /// </summary>
- /// <param name="buffer">The buffer containing font data.</param>
- public static void AddFont(byte[] buffer)
- {
- AddFontInternal(buffer, PrivateFontCollection);
- }
- /// <summary>
- /// Adds a font contained in system memory.
- /// </summary>
- /// <param name="memory">The memory address of the font to add.</param>
- /// <param name="length">The memory length of the font to add.</param>
- public static void AddFont(IntPtr memory, int length)
- {
- byte[] buffer = new byte[length];
- Marshal.Copy(memory, buffer, 0, length);
- AddFont(buffer);
- }
- /// <summary>
- /// Adds a font from a stream.
- /// </summary>
- /// <param name="stream">The stream containing font data.</param>
- public static void AddFont(Stream stream)
- {
- var ms = new MemoryStream();
- stream.CopyTo(ms);
- AddFont(ms.ToArray());
- }
- /// <summary>
- /// Adds a font from a file.
- /// </summary>
- /// <param name="fileName">The name of the font file.</param>
- /// <returns>true if file exists, otherwise false.</returns>
- public static bool AddFont(string fileName)
- {
- if (File.Exists(fileName))
- {
- AddFont(File.ReadAllBytes(fileName));
- return true;
- }
- else
- {
- Debug.WriteLine($"Font file not found {fileName}");
- }
- return false;
- }
- /// <summary>
- /// Adds a font from a file to a temporary font list.
- /// </summary>
- /// <param name="fileName">The name of the font file.</param>
- /// <returns>true if file exists, otherwise false.</returns>
- public static bool AddTempFont(string fileName)
- {
- if (File.Exists(fileName))
- {
- TemporaryFontCollection ??= new PrivateFontCollection();
- AddFontInternal(File.ReadAllBytes(fileName), TemporaryFontCollection);
- return true;
- }
- else
- {
- Debug.WriteLine($"Font file not found {fileName}");
- }
- return false;
- }
- /// <summary>
- /// Adds a font from a stream to a temporary font list.
- /// </summary>
- /// <param name="stream">The stream containing font data.</param>
- public static void AddTempFont(Stream stream)
- {
- TemporaryFontCollection ??= new PrivateFontCollection();
- var ms = new MemoryStream();
- stream.CopyTo(ms);
- AddFontInternal(ms.ToArray(), TemporaryFontCollection);
- }
- /// <summary>
- /// Clears temporary fonts.
- /// </summary>
- public static void ClearTempFonts()
- {
- var collection = TemporaryFontCollection;
- TemporaryFontCollection = null;
- collection?.Dispose();
- }
- // Defines a font fallback item.
- private class FontFallback
- {
- private FontFamily _family;
- public string Name { get; }
-
- // The font family, if found. May be null.
- public FontFamily FontFamily => _family ?? FindFontFamily(Name, SearchScope.NonInstalled);
- public FontFallback(string name)
- {
- Name = name;
- // do initial search in installed fonts. Other collections should be checked later.
- _family = FindFontFamily(name, SearchScope.Installed);
- }
- }
- // used by the font fallback code from RichTextKit to find a typeface that contains a given character
- private class CharacterMatcher : Topten.RichTextKit.ICharacterMatcher
- {
- private readonly Topten.RichTextKit.ICharacterMatcher oldCharacterMatcher;
- public CharacterMatcher()
- {
- oldCharacterMatcher = Topten.RichTextKit.FontFallback.CharacterMatcher;
- Topten.RichTextKit.FontFallback.CharacterMatcher = this;
- }
- public SKTypeface MatchCharacter(string familyName, int weight, int width, SKFontStyleSlant slant, string[] bcp47, int character)
- {
- // check user defined fallbacks first
- foreach (var ff in FallbackFonts)
- {
- var tf = ff.FontFamily?.GetTypeface(weight, width, slant);
- if (tf != null && tf.ContainsGlyph(character))
- {
- return tf;
- }
- }
- // check private fonts
- if (TemporaryFontCollection != null)
- {
- foreach (var f in TemporaryFontCollection.Families)
- {
- var tf = f.GetTypeface(weight, width, slant);
- if (tf.ContainsGlyph(character))
- {
- return tf;
- }
- }
- }
- foreach (var f in PrivateFontCollection.Families)
- {
- var tf = f.GetTypeface(weight, width, slant);
- if (tf.ContainsGlyph(character))
- {
- return tf;
- }
- }
- // do default check
- return oldCharacterMatcher?.MatchCharacter(familyName, weight, width, slant, bcp47, character);
- }
- }
- // used in the FontFamily to look up family name in all font collections
- private class FontFamilyMatcher1 : FontFamily.IFontFamilyMatcher
- {
- public FontFamilyMatcher1()
- {
- FontFamily.FontFamilyMatcher = this;
- }
- public FontFamily GetFontFamilyOrDefault(string name) =>
- FontManager.GetFontFamilyOrDefault(name);
- public FontFamily GetFontFamilyForTypeface(SKTypeface typeface, FontStyle style)
- {
- var family = TemporaryFontCollection?.Find(typeface, style);
- family ??= PrivateFontCollection.Find(typeface, style);
- family ??= InstalledFontCollection.Find(typeface, style);
- return family;
- }
- }
- }
- }
- #endif
|