2 using System.Collections.Generic;
5 using System.Runtime.InteropServices;
7 using System.Drawing.Drawing2D;
8 using System.Diagnostics;
12 using Rectangle = System.Drawing.Rectangle;
13 using RectangleF = System.Drawing.RectangleF;
14 using Color = System.Drawing.Color;
15 using Point = System.Drawing.Point;
16 using GraphicPath = System.Drawing.Drawing2D.GraphicsPath;
21 /// プライベートフォントでの描画を扱うクラス。
23 /// <exception cref="FileNotFoundException">フォントファイルが見つからない時に例外発生</exception>
24 /// <exception cref="ArgumentException">スタイル指定不正時に例外発生</exception>
27 /// CPrivateFont prvFont = new CPrivateFont( CSkin.Path( @"Graphics\fonts\mplus-1p-bold.ttf" ), 36 ); // プライベートフォント
29 /// CPrivateFont prvFont = new CPrivateFont( new FontFamily("MS UI Gothic"), 36, FontStyle.Bold ); // システムフォント
31 /// Bitmap bmp = prvFont.DrawPrivateFont( "ABCDE", Color.White, Color.Black ); // フォント色=白、縁の色=黒の例。縁の色は省略可能
33 /// Bitmap bmp = prvFont.DrawPrivateFont( "ABCDE", Color.White, Color.Black, Color.Yellow, Color.OrangeRed ); // 上下グラデーション(Yellow→OrangeRed)
35 /// CTexture ctBmp = TextureFactory.tテクスチャの生成( bmp, false );
36 /// ctBMP.t2D描画( ~~~ );
40 /// 任意のフォントでのレンダリングは結構負荷が大きいので、なるべくなら描画フレーム毎にフォントを再レンダリングするようなことはせず、
41 /// 一旦レンダリングしたものを描画に使い回すようにしてください。
42 /// また、長い文字列を与えると、返されるBitmapも横長になります。この横長画像をそのままテクスチャとして使うと、
43 /// 古いPCで問題を発生させやすいです。これを回避するには、一旦Bitmapとして取得したのち、256pixや512pixで分割して
44 /// テクスチャに定義するようにしてください。FDKをお使いの場合は、CTexture()の代わりにCTextureAf()を使うと、
45 /// このような縦長/横長の画像をクラス内部で2^n平方の正方形に近いテクスチャに折りたたんで登録する一方で、
46 /// 表示時は縦長/横長のままのテクスチャとして扱うことができて便利です。
48 public class CPrivateFont : IDisposable
51 /// プライベートフォントのFontクラス。CPrivateFont()の初期化後に使用可能となる。
52 /// プライベートフォントでDrawString()したい場合にご利用ください。
60 /// フォント登録失敗時に代替使用するフォント名。システムフォントのみ設定可能。
61 /// 後日外部指定できるようにします。(=コンストラクタで指定できるようにします)
63 private string strAlternativeFont = "MS PGothic";
67 public CPrivateFont(FontFamily fontfamily, int pt, FontStyle style)
69 Initialize(null, null, fontfamily, pt, style);
71 public CPrivateFont(FontFamily fontfamily, int pt)
73 Initialize(null, null, fontfamily, pt, FontStyle.Regular);
75 public CPrivateFont(string fontpath, FontFamily fontfamily, int pt, FontStyle style)
77 Initialize(fontpath, null, fontfamily, pt, style);
79 public CPrivateFont(string fontpath, int pt, FontStyle style)
81 Initialize(fontpath, null, null, pt, style);
83 public CPrivateFont(string fontpath, int pt)
85 Initialize(fontpath, null, null, pt, FontStyle.Regular);
89 //throw new ArgumentException("CPrivateFont: 引数があるコンストラクタを使用してください。");
93 protected void Initialize(string fontpath, string baseFontPath, FontFamily fontfamily, int pt, FontStyle style)
96 this._fontfamily = null;
99 this._rectStrings = new Rectangle(0, 0, 0, 0);
100 this._ptOrigin = new Point(0, 0);
101 this.bDispose完了済み = false;
102 this._baseFontname = baseFontPath;
104 if (fontfamily != null)
106 this._fontfamily = fontfamily;
110 if (Path.GetFileName(fontpath) == "")
112 Trace.TraceWarning($"No font filename is specified (only path is specified, etc). Trying to use MS PGothic as alternative. ({fontpath}, {baseFontPath}, {fontfamily}, {pt}, {style})");
119 if (Path.GetExtension(fontpath) != "")
122 this._pfc = new System.Drawing.Text.PrivateFontCollection(); //PrivateFontCollectionオブジェクトを作成する
123 this._pfc.AddFontFile(fontpath); //PrivateFontCollectionにフォントを追加する
124 _fontfamily = _pfc.Families[0];
128 // "MS Gothic"などを指定した場合
129 this._fontfamily = new FontFamily(Path.GetFileNameWithoutExtension(fontpath));
132 catch (Exception e) when (e is System.IO.FileNotFoundException || e is System.Runtime.InteropServices.ExternalException)
134 Trace.TraceWarning(e.Message);
135 Trace.TraceWarning("プライベートフォントの追加に失敗しました({0})。代わりにMS PGothicの使用を試みます。", fontpath);
136 //throw new FileNotFoundException( "プライベートフォントの追加に失敗しました。({0})", Path.GetFileName( fontpath ) );
141 catch (IndexOutOfRangeException e)
143 Trace.TraceWarning(e.Message);
144 Trace.TraceWarning($"AddFontFile() succeeded, but not reflected to the Array of Families. Failed to add PrivateFont({fontpath}).");
148 //foreach ( FontFamily ff in _pfc.Families )
150 // Debug.WriteLine( "fontname=" + ff.Name );
151 // if ( ff.Name == Path.GetFileNameWithoutExtension( fontpath ) )
157 //if ( _fontfamily == null )
159 // Trace.TraceError( "プライベートフォントの追加後、検索に失敗しました。({0})", fontpath );
166 // 指定されたフォントスタイルが適用できない場合は、フォント内で定義されているスタイルから候補を選んで使用する
167 // 何もスタイルが使えないようなフォントなら、例外を出す。
168 if (_fontfamily != null)
170 if (!_fontfamily.IsStyleAvailable(style))
172 FontStyle[] FS = { FontStyle.Regular, FontStyle.Bold, FontStyle.Italic, FontStyle.Underline, FontStyle.Strikeout };
173 style = FontStyle.Regular | FontStyle.Bold | FontStyle.Italic | FontStyle.Underline | FontStyle.Strikeout; // null非許容型なので、代わりに全盛をNGワードに設定
174 foreach (FontStyle ff in FS)
176 if (this._fontfamily.IsStyleAvailable(ff))
179 Trace.TraceWarning("フォント{0}へのスタイル指定を、{1}に変更しました。", Path.GetFileName(fontpath), style.ToString());
183 if (style == (FontStyle.Regular | FontStyle.Bold | FontStyle.Italic | FontStyle.Underline | FontStyle.Strikeout))
185 Trace.TraceWarning("フォント{0}は適切なスタイル{1}を選択できませんでした。", Path.GetFileName(fontpath), style.ToString());
188 //this._font = new Font(this._fontfamily, pt, style); //PrivateFontCollectionの先頭のフォントのFontオブジェクトを作成する
189 float emSize = pt * 96.0f / 72.0f;
190 this._font = new Font(this._fontfamily, emSize, style, GraphicsUnit.Pixel); //PrivateFontCollectionの先頭のフォントのFontオブジェクトを作成する
191 //HighDPI対応のため、pxサイズで指定
194 // フォントファイルが見つからなかった場合 (MS PGothicを代わりに指定する)
196 float emSize = pt * 96.0f / 72.0f;
197 this._font = new Font(strAlternativeFont, emSize, style, GraphicsUnit.Pixel); //MS PGothicのFontオブジェクトを作成する
198 FontFamily[] ffs = new System.Drawing.Text.InstalledFontCollection().Families;
199 int lcid = System.Globalization.CultureInfo.GetCultureInfo("en-us").LCID;
200 foreach (FontFamily ff in ffs)
202 // Trace.WriteLine( lcid ) );
203 if (ff.GetName(lcid) == strAlternativeFont)
205 this._fontfamily = ff;
206 Trace.TraceInformation($"{strAlternativeFont}を代わりに指定しました。");
210 throw new FileNotFoundException($"プライベートフォントの追加に失敗し、{strAlternativeFont}での代替処理にも失敗しました。({Path.GetFileName(fontpath)})");
215 protected enum DrawMode
222 #region [ DrawPrivateFontのオーバーロード群 ]
226 /// <param name="drawstr">描画文字列</param>
227 /// <param name="fontColor">描画色</param>
228 /// <returns>描画済テクスチャ</returns>
229 public Bitmap DrawPrivateFont(string drawstr, Color fontColor, Size? sz = null)
231 return DrawPrivateFont(drawstr, DrawMode.Normal, fontColor, Color.White, Color.White, Color.White, sz);
237 /// <param name="drawstr">描画文字列</param>
238 /// <param name="fontColor">描画色</param>
239 /// <param name="edgeColor">縁取色</param>
240 /// <returns>描画済テクスチャ</returns>
241 public Bitmap DrawPrivateFont(string drawstr, Color fontColor, Color edgeColor, Size? sz = null)
243 return DrawPrivateFont(drawstr, DrawMode.Edge, fontColor, edgeColor, Color.White, Color.White, sz);
249 /// <param name="drawstr">描画文字列</param>
250 /// <param name="fontColor">描画色</param>
251 /// <param name="gradationTopColor">グラデーション 上側の色</param>
252 /// <param name="gradationBottomColor">グラデーション 下側の色</param>
253 /// <returns>描画済テクスチャ</returns>
254 //public Bitmap DrawPrivateFont( string drawstr, Color fontColor, Color gradationTopColor, Color gradataionBottomColor )
256 // return DrawPrivateFont( drawstr, DrawMode.Gradation, fontColor, Color.White, gradationTopColor, gradataionBottomColor );
262 /// <param name="drawstr">描画文字列</param>
263 /// <param name="fontColor">描画色</param>
264 /// <param name="edgeColor">縁取色</param>
265 /// <param name="gradationTopColor">グラデーション 上側の色</param>
266 /// <param name="gradationBottomColor">グラデーション 下側の色</param>
267 /// <returns>描画済テクスチャ</returns>
268 public Bitmap DrawPrivateFont(string drawstr, Color fontColor, Color edgeColor, Color gradationTopColor, Color gradataionBottomColor, Size? sz = null)
270 return DrawPrivateFont(drawstr, DrawMode.Edge | DrawMode.Gradation, fontColor, edgeColor, gradationTopColor, gradataionBottomColor,sz);
273 #if こちらは使わない // (Bitmapではなく、CTextureを返す版)
277 /// <param name="drawstr">描画文字列</param>
278 /// <param name="fontColor">描画色</param>
279 /// <returns>描画済テクスチャ</returns>
280 public CTexture DrawPrivateFont( string drawstr, Color fontColor )
282 Bitmap bmp = DrawPrivateFont( drawstr, DrawMode.Normal, fontColor, Color.White, Color.White, Color.White );
283 return TextureFactory.tテクスチャの生成( bmp, false );
289 /// <param name="drawstr">描画文字列</param>
290 /// <param name="fontColor">描画色</param>
291 /// <param name="edgeColor">縁取色</param>
292 /// <returns>描画済テクスチャ</returns>
293 public CTexture DrawPrivateFont( string drawstr, Color fontColor, Color edgeColor )
295 Bitmap bmp = DrawPrivateFont( drawstr, DrawMode.Edge, fontColor, edgeColor, Color.White, Color.White );
296 return TextureFactory.tテクスチャの生成( bmp, false );
302 /// <param name="drawstr">描画文字列</param>
303 /// <param name="fontColor">描画色</param>
304 /// <param name="gradationTopColor">グラデーション 上側の色</param>
305 /// <param name="gradationBottomColor">グラデーション 下側の色</param>
306 /// <returns>描画済テクスチャ</returns>
307 //public CTexture DrawPrivateFont( string drawstr, Color fontColor, Color gradationTopColor, Color gradataionBottomColor )
309 // Bitmap bmp = DrawPrivateFont( drawstr, DrawMode.Gradation, fontColor, Color.White, gradationTopColor, gradataionBottomColor );
310 // return TextureFactory.tテクスチャの生成( bmp, false );
316 /// <param name="drawstr">描画文字列</param>
317 /// <param name="fontColor">描画色</param>
318 /// <param name="edgeColor">縁取色</param>
319 /// <param name="gradationTopColor">グラデーション 上側の色</param>
320 /// <param name="gradationBottomColor">グラデーション 下側の色</param>
321 /// <returns>描画済テクスチャ</returns>
322 public CTexture DrawPrivateFont( string drawstr, Color fontColor, Color edgeColor, Color gradationTopColor, Color gradataionBottomColor )
324 Bitmap bmp = DrawPrivateFont( drawstr, DrawMode.Edge | DrawMode.Gradation, fontColor, edgeColor, gradationTopColor, gradataionBottomColor );
325 return TextureFactory.tテクスチャの生成( bmp, false );
331 /// 文字列を描画したテクスチャを返す(メイン処理)
333 /// <param name="rectDrawn">描画された領域</param>
334 /// <param name="ptOrigin">描画文字列</param>
335 /// <param name="drawstr">描画文字列</param>
336 /// <param name="drawmode">描画モード</param>
337 /// <param name="fontColor">描画色</param>
338 /// <param name="edgeColor">縁取色</param>
339 /// <param name="gradationTopColor">グラデーション 上側の色</param>
340 /// <param name="gradationBottomColor">グラデーション 下側の色</param>
341 /// <param name="sz">描画領域(省略可; 省略時は横長の改行なし・中央寄せで、指定時は描画領域内に改行あり・Near寄せでbitmapを生成)</param>
342 /// <returns>描画済テクスチャ</returns>
343 protected Bitmap DrawPrivateFont(string drawstr, DrawMode drawmode, Color fontColor, Color edgeColor, Color gradationTopColor, Color gradationBottomColor, Size? sz = null)
345 if (this._fontfamily == null || drawstr == null || drawstr == "")
347 // nullを返すと、その後bmp→texture処理や、textureのサイズを見て・・の処理で全部例外が発生することになる。
348 // それは非常に面倒なので、最小限のbitmapを返してしまう。
349 // まずはこの仕様で進めますが、問題有れば(上位側からエラー検出が必要であれば)例外を出したりエラー状態であるプロパティを定義するなり検討します。
352 Trace.TraceWarning("DrawPrivateFont()の入力不正。最小値のbitmapを返します。");
354 _rectStrings = new Rectangle(0, 0, 0, 0);
355 _ptOrigin = new Point(0, 0);
356 return new Bitmap(1, 1);
358 bool bEdge = drawmode.HasFlag(DrawMode.Edge);
359 bool bGradation = drawmode.HasFlag(DrawMode.Gradation);
361 // 縁取りの縁のサイズは、とりあえずフォントの大きさの1/4とする
362 int nEdgePt = (bEdge) ? _pt / 4 : _pt /8;
364 // 描画サイズを測定する (外部から描画領域を指定した場合は、それを使う)
368 stringSize = sz.Value;
372 using (Bitmap b = new Bitmap(1, 1))
373 using (Graphics g = Graphics.FromImage(b))
374 using (StringFormat sf = new StringFormat(StringFormat.GenericTypographic))
376 //g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
377 //sf.FormatFlags = StringFormatFlags.NoClip | StringFormatFlags.NoWrap;
379 //SizeF sizef = g.MeasureString(drawstr, this._font, int.MaxValue, sf) ;
380 //stringSize = sizef.ToSize();
381 stringSize = System.Windows.Forms.TextRenderer.MeasureText(drawstr, this._font, new Size(int.MaxValue, int.MaxValue),
382 System.Windows.Forms.TextFormatFlags.NoPrefix |
383 System.Windows.Forms.TextFormatFlags.NoPadding |
384 System.Windows.Forms.TextFormatFlags.Bottom
387 //Rectangle rc = MeasureStringPrecisely(g, drawstr, this._font, new Size(stringSize.Width * 2, stringSize.Height * 2), sf);
388 //stringSize = new Size(rc.Width, rc.Height);
393 // 文字数をカウントする (横幅に文字数*2の縁取り幅を確保するために用いる)
394 System.Globalization.StringInfo si = new System.Globalization.StringInfo(drawstr);
395 int len = si.LengthInTextElements;
397 Size stringSizeWithEdge = sz.HasValue ?
398 // sz.Value : new Size((int)(stringSize.Width + nEdgePt * len), stringSize.Height + nEdgePt * 2);
399 //sz.Value : stringSize;
400 sz.Value : new Size((int)(stringSize.Width * 1.05f), stringSize.Height);
402 //取得した描画サイズを基に、描画先のbitmapを作成する
403 //Bitmap bmp = new Bitmap(stringSize.Width + nEdgePt * 2, stringSize.Height + nEdgePt * 2);
404 Bitmap bmp = new Bitmap(stringSizeWithEdge.Width, stringSizeWithEdge.Height);
405 bmp.MakeTransparent();
407 using (Graphics g = Graphics.FromImage(bmp))
409 g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
410 g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
411 g.PixelOffsetMode = PixelOffsetMode.HighQuality;
413 using (StringFormat sf = new StringFormat())
418 sf.LineAlignment = StringAlignment.Near;
420 sf.Alignment = StringAlignment.Near;
421 sf.FormatFlags = StringFormatFlags.LineLimit;
426 sf.LineAlignment = StringAlignment.Near;
428 //sf.Alignment = StringAlignment.Center;
429 sf.Alignment = StringAlignment.Near;
430 sf.FormatFlags = StringFormatFlags.NoWrap;
433 //Rectangle r = new Rectangle(0, 0, stringSize.Width + nEdgePt * 2, stringSize.Height + nEdgePt * 2);
434 //Rectangle r = new Rectangle(0, 0, stringSizeWithEdge.Width, stringSizeWithEdge.Height);
435 Rectangle r = (sz.HasValue)?
436 new Rectangle(0, 0, (int)(stringSize.Width), stringSize.Height) :
437 new Rectangle(0, 0, (int)(stringSize.Width * 1.2f), stringSize.Height);
442 // DrawPathで、ポイントサイズを使って描画するために、DPIを使って単位変換する
443 // (これをしないと、単位が違うために、小さめに描画されてしまう)
444 float sizeInPixels = _font.SizeInPoints * g.DpiY / 72; // 1 inch = 72 points
446 using (GraphicsPath gp = new GraphicsPath())
448 gp.AddString(drawstr, this._fontfamily, (int)this._font.Style, sizeInPixels, r, sf);
451 using (Pen p = new Pen(edgeColor, nEdgePt))
453 p.LineJoin = System.Drawing.Drawing2D.LineJoin.Round;
457 using (Brush br = bGradation ?
458 new LinearGradientBrush(r, gradationTopColor, gradationBottomColor, LinearGradientMode.Vertical) as Brush :
459 new SolidBrush(fontColor) as Brush)
469 using (Brush br = new SolidBrush(fontColor))
471 g.DrawString(drawstr, _font, br, 0f, 0f);
473 // System.Windows.Forms.TextRenderer.DrawText(g, drawstr, _font, new Point(0, 0), fontColor);
476 g.DrawRectangle( new Pen( Color.White, 1 ), new Rectangle( 1, 1, stringSizeWithEdge.Width-1, stringSizeWithEdge.Height-1 ) );
477 g.DrawRectangle( new Pen( Color.Green, 1 ), new Rectangle( 0, 0, bmp.Width - 1, bmp.Height - 1 ) );
479 _rectStrings = new Rectangle(0, 0, stringSize.Width, stringSize.Height);
480 _ptOrigin = new Point(nEdgePt * 2, nEdgePt * 2);
488 /// 最後にDrawPrivateFont()した文字列の描画領域を取得します。
490 public Rectangle RectStrings
498 _rectStrings = value;
501 public Point PtOrigin
525 #region [ IDisposable 実装 ]
527 public void Dispose()
529 if (!this.bDispose完了済み)
531 if (this._font != null)
533 this._font.Dispose();
536 if (this._pfc != null)
541 if (this._fontfamily != null)
543 this._fontfamily.Dispose();
544 this._fontfamily = null;
547 this.bDispose完了済み = true;
555 protected bool bDispose完了済み;
556 protected Font _font = null;
558 private System.Drawing.Text.PrivateFontCollection _pfc;
559 private FontFamily _fontfamily = null;
561 private Rectangle _rectStrings;
562 private Point _ptOrigin;
563 private string _baseFontname = null;