Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
SpriteFontAssetCompiler.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 #pragma warning disable 162 // Unreachable code detected (due to useCacheFonts)
4 using System.Collections.Generic;
5 using System.IO;
6 using System.Linq;
7 using System.Threading.Tasks;
8 
9 using SharpDX.DirectWrite;
10 
11 using SiliconStudio.Assets;
12 using SiliconStudio.Assets.Compiler;
13 using SiliconStudio.BuildEngine;
14 using SiliconStudio.Core.IO;
15 using SiliconStudio.Core.Serialization;
16 using SiliconStudio.Core.Serialization.Assets;
17 using SiliconStudio.Paradox.Assets.SpriteFont.Compiler;
18 using SiliconStudio.Paradox.Graphics;
20 
23 
24 namespace SiliconStudio.Paradox.Assets.SpriteFont
25 {
26  public class SpriteFontAssetCompiler : AssetCompilerBase<SpriteFontAsset>
27  {
28  protected override void Compile(AssetCompilerContext context, string urlInStorage, UFile assetAbsolutePath, SpriteFontAsset asset, AssetCompilerResult result)
29  {
30  if (asset.IsDynamic)
31  {
32  UFile fontPathOnDisk;
33  if (asset.Source != null)
34  {
35  var assetDirectory = assetAbsolutePath.GetParent();
36  fontPathOnDisk = UPath.Combine(assetDirectory, asset.Source);
37  if (!File.Exists(fontPathOnDisk))
38  {
39  result.Error("The font source '{0}' does not exist on the PC.", asset.FontName);
40  return;
41  }
42  // set the source filename as font name instead of the font family.
43  asset.FontName = fontPathOnDisk.GetFileName();
44  }
45  else
46  {
47  fontPathOnDisk = GetFontPath(asset, result);
48  if (fontPathOnDisk == null)
49  {
50  result.Error("The font named '{0}' could not be located on the PC.", asset.FontName);
51  return;
52  }
53  }
54  var fontImportLocation = FontHelper.GetFontPath(asset.FontName, asset.Style);
55 
56  result.BuildSteps = new ListBuildStep
57  {
58  new ImportStreamCommand { SourcePath = fontPathOnDisk, Location = fontImportLocation },
59  new DynamicFontCommand(urlInStorage, asset)
60  };
61  }
62  else
63  {
64  // copy the asset and transform the source and character set file path to absolute paths
65  var assetClone = (SpriteFontAsset)AssetCloner.Clone(asset);
66  var assetDirectory = assetAbsolutePath.GetParent();
67  assetClone.Source = asset.Source != null? UPath.Combine(assetDirectory, asset.Source): null;
68  assetClone.CharacterSet = asset.CharacterSet != null ? UPath.Combine(assetDirectory, asset.CharacterSet): null;
69 
70  result.BuildSteps = new ListBuildStep { new StaticFontCommand(urlInStorage, assetAbsolutePath, assetClone) };
71  }
72  }
73 
74  internal class StaticFontCommand : AssetCommand<SpriteFontAsset>
75  {
76  private readonly UFile assetAbsolutePath;
77 
78  public StaticFontCommand(string url, UFile assetAbsolutePath, SpriteFontAsset description)
79  : base(url, description)
80  {
81  this.assetAbsolutePath = assetAbsolutePath;
82  }
83 
84  public override IEnumerable<ObjectUrl> GetInputFiles()
85  {
86  foreach (var inputFile in base.GetInputFiles())
87  {
88  yield return inputFile;
89  }
90 
91  if(File.Exists(asset.CharacterSet))
92  yield return new ObjectUrl(UrlType.File, asset.CharacterSet);
93  }
94 
95  protected override Task<ResultStatus> DoCommandOverride(ICommandContext commandContext)
96  {
97  const bool useCacheFonts = false;
98 
99  // compute the path of the cache files
100  var cachedImagePath = assetAbsolutePath.GetFileName() + ".CachedImage";
101  var cachedFontGlyphs = assetAbsolutePath.GetFileName() + ".CachedGlyphs";
102 
103  // try to import the font from the original bitmap or ttf file
105  try
106  {
107  data = FontCompiler.Compile(asset);
108  }
109  catch (FontNotFoundException ex)
110  {
111  if (!useCacheFonts)
112  {
113  commandContext.Logger.Error("Font [{0}] was not found on this machine.", ex.FontName);
114  return Task.FromResult(ResultStatus.Failed);
115  }
116  else
117  {
118  // If the original fo
119  commandContext.Logger.Warning("Font [{0}] was not found on this machine. Trying to use cached glyphs/image", ex.FontName);
120  if (!File.Exists(cachedFontGlyphs))
121  {
122  commandContext.Logger.Error("Expecting cached glyphs [{0}]", cachedFontGlyphs);
123  return Task.FromResult(ResultStatus.Failed);
124  }
125 
126  if (!File.Exists(cachedImagePath))
127  {
128  commandContext.Logger.Error("Expecting cached image [{0}]", cachedImagePath);
129  return Task.FromResult(ResultStatus.Failed);
130  }
131 
132  // read the cached glyphs
133  using (var glyphStream = File.OpenRead(cachedFontGlyphs))
134  data = BinarySerialization.Read<StaticSpriteFontData>(glyphStream);
135 
136  // read the cached image
137  data.Bitmaps = new[] { new ContentReference<Image>() };
138  using (var imageStream = File.OpenRead(cachedImagePath))
139  data.Bitmaps[0].Value = Image.Load(imageStream);
140  }
141  }
142 
143  // check that the font data is valid
144  if (data == null || data.Bitmaps.Length == 0)
145  return Task.FromResult(ResultStatus.Failed);
146 
147  // save the data into the database
148  var imageUrl = Url + "__image";
149  data.Bitmaps[0].Location = imageUrl;
150  var assetManager = new AssetManager();
151  assetManager.Save(Url, data);
152 
153  var image = data.Bitmaps[0].Value;
154 
155  // cache the generated data
156  if (useCacheFonts)
157  {
158  try
159  {
160  // the image
161  using (var imageStream = File.OpenWrite(cachedImagePath))
162  image.Save(imageStream, ImageFileType.Paradox);
163 
164  // the glyphs
165  data.Bitmaps = null;
166  using (var glyphStream = File.OpenWrite(cachedFontGlyphs))
167  BinarySerialization.Write(glyphStream, data);
168  }
169  catch (IOException ex)
170  {
171  commandContext.Logger.Warning("Cannot save cached glyphs [{0}] or image [{1}]", ex, cachedFontGlyphs, cachedImagePath);
172  }
173  }
174 
175  // free the objects
176  image.Dispose();
177 
178  return Task.FromResult(ResultStatus.Successful);
179  }
180  }
181 
182  private static string GetFontPath(SpriteFontAsset asset, AssetCompilerResult result)
183  {
184  using (var factory = new Factory())
185  {
186  Font font;
187 
188  using (var fontCollection = factory.GetSystemFontCollection(false))
189  {
190  int index;
191  if (!fontCollection.FindFamilyName(asset.FontName, out index))
192  {
193  result.Error("Can't find font '{0}'.", asset.FontName);
194  return null;
195  }
196 
197  using (var fontFamily = fontCollection.GetFontFamily(index))
198  {
199  var weight = FontWeight.Regular;
200  var style = SharpDX.DirectWrite.FontStyle.Normal;
201  switch (asset.Style)
202  {
203  case FontStyle.Bold:
204  weight = FontWeight.Bold;
205  break;
206  case FontStyle.Italic:
207  weight = FontWeight.Regular;
208  style = SharpDX.DirectWrite.FontStyle.Italic;
209  break;
210  case FontStyle.Regular:
211  weight = FontWeight.Regular;
212  break;
213  }
214 
215  font = fontFamily.GetFirstMatchingFont(weight, FontStretch.Normal, style);
216  if (font == null)
217  {
218  result.Error("Cannot find style '{0}' for font family {1}.", asset.Style, asset.FontName);
219  return null;
220  }
221  }
222  }
223 
224  var fontFace = new FontFace(font);
225 
226  // get the font path on the hard drive
227  var file = fontFace.GetFiles().First();
228  var referenceKey = file.GetReferenceKey();
229  var originalLoader = (FontFileLoaderNative)file.Loader;
230  var loader = originalLoader.QueryInterface<LocalFontFileLoader>();
231  return loader.GetFilePath(referenceKey);
232  }
233  }
234 
235  internal class DynamicFontCommand : AssetCommand<SpriteFontAsset>
236  {
237  public DynamicFontCommand(string url, SpriteFontAsset description)
238  : base(url, description)
239  {
240  }
241 
242  protected override Task<ResultStatus> DoCommandOverride(ICommandContext commandContext)
243  {
244  var dynamicFont = new DynamicSpriteFontData
245  {
246  Size = FontHelper.PointsToPixels(asset.Size),
247  DefaultCharacter = asset.DefaultCharacter,
248  FontName = asset.FontName,
249  ExtraLineSpacing = asset.LineSpacing,
250  DefaultSize = asset.Size,
251  ExtraSpacing = asset.Spacing,
252  Style = asset.Style,
253  UseKerning = asset.UseKerning,
254  AntiAlias = asset.AntiAlias,
255  };
256 
257  var assetManager = new AssetManager();
258  assetManager.Save(Url, dynamicFont);
259 
260  return Task.FromResult(ResultStatus.Successful);
261  }
262  }
263  }
264 }
Result of a compilation of assets when using IAssetCompiler.Compile
bool IsDynamic
Determine if the characters are pre-generated off-line or at run-time.
SharpDX.DirectWrite.Factory Factory
char DefaultCharacter
The default character fall-back.
override void Compile(AssetCompilerContext context, string urlInStorage, UFile assetAbsolutePath, SpriteFontAsset asset, AssetCompilerResult result)
A command processing an Asset.
Definition: AssetCommand.cs:11
The context used when compiling an asset in a Package.
SharpDX.DirectWrite.Font Font
System.IO.File File
SpriteFont to use with SpriteBatch. See SpriteFont to learn how to use it.
Definition: SpriteFont.cs:26
object Clone()
Clones the current value of this cloner with the specified new shadow registry (optional) ...
Definition: AssetCloner.cs:43
UFile Source
Gets or sets the source file containing the font data if required.
Data for a static SpriteFont object that supports kerning.
SiliconStudio.Paradox.Graphics.Font.FontStyle FontStyle
Allows to clone an asset or values stored in an asset.
Definition: AssetCloner.cs:14
Defines a normalized file path. See UPath for details. This class cannot be inherited.
Definition: UFile.cs:13