.NET 4.5 / C#で、指定した長方形に合うように適応可能なサイズのフォントを作成するにはどうすればよいですか?
文字列の長さに基づいてサイズを変更します。文字列が長いほどフォントサイズは小さくなりますが、文字列が長すぎるとテキストが非常に小さくなるため、うまく機能しません。この方法の問題は、長方形のサイズを変更すると、すべてのフォントサイズが再び良くないことです。
久しぶりですが、この問題に出くわしました。MSDNは、このプロセスに役立つGetAdjustedFontメソッドのサンプルを提供しています。
( https://msdn.microsoft.com/en-us/library/bb986765.aspxから恥知らずに盗まれました)
public Font GetAdjustedFont(Graphics g, string graphicString, Font originalFont, int containerWidth, int maxFontSize, int minFontSize, bool smallestOnFail)
{
Font testFont = null;
// We utilize MeasureString which we get via a control instance
for (int adjustedSize = maxFontSize; adjustedSize >= minFontSize; adjustedSize--)
{
testFont = new Font(originalFont.Name, adjustedSize, originalFont.Style);
// Test the string with the new size
SizeF adjustedSizeNew = g.MeasureString(graphicString, testFont);
if (containerWidth > Convert.ToInt32(adjustedSizeNew.Width))
{
// Good font, return it
return testFont;
}
}
// If you get here there was no fontsize that worked
// return minimumSize or original?
if (smallestOnFail)
{
return testFont;
}
else
{
return originalFont;
}
}
Graphics.MeasureStringを使用すると、文字列のサイズを測定できるため、必要なものを計算できます。
MSDNからのサンプル
private void MeasureStringMin(PaintEventArgs e)
{
// Set up string.
string measureString = "Measure String";
Font stringFont = new Font("Arial", 16);
// Measure string.
SizeF stringSize = new SizeF();
stringSize = e.Graphics.MeasureString(measureString, stringFont);
// Draw rectangle representing size of string.
e.Graphics.DrawRectangle(new Pen(Color.Red, 1), 0.0F, 0.0F, stringSize.Width, stringSize.Height);
// Draw string to screen.
e.Graphics.DrawString(measureString, stringFont, Brushes.Black, new PointF(0, 0));
}
文字列の幅は任意のフォントサイズ(16など)で測定でき、次のように目的のフォントサイズを計算できます。
fontSize = (previouslyMeasuredFontSize * targetWidthOfString) / previouslyMeasuredStringWidth;
私も似たようなものが必要でした。いくつかの自動フォントサイズオプションを使用して、定義された長方形内にテキストを描画する必要性に答えるために、いくつかのコードを作成しました(DrawMethod列挙型を参照)。また、自動フォントサイズの有無にかかわらず自動ワープをサポートします。私の解決策は、上記の答えに触発されました。必ずWindowsBaseおよびPresentationCoreアセンブリを追加してください。
DrawTextToBitmap.cs
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ReportPrepare
{
class TextDrawing
{
public enum DrawMethod
{
AutosizeAccordingToText, // create the smallest bitmap needed to draw the text without word warp
AutoFitInConstantRectangleWithoutWarp, // draw text with the biggest font possible while not exceeding rectangle dimensions, without word warp
AutoWarpInConstantRectangle, // draw text in rectangle while performing word warp. font size is a constant input. drawing may exceed bitmap rectangle.
AutoFitInConstantRectangleWithWarp // draw text with the biggest font possible while not exceeding rectangle dimensions, with word warp
}
private static void SetGraphicsHighQualityForTextRendering(Graphics g)
{
// The smoothing mode specifies whether lines, curves, and the edges of filled areas use smoothing (also called antialiasing). One exception is that path gradient brushes do not obey the smoothing mode. Areas filled using a PathGradientBrush are rendered the same way (aliased) regardless of the SmoothingMode property.
g.SmoothingMode = SmoothingMode.AntiAlias;
// The interpolation mode determines how intermediate values between two endpoints are calculated.
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
// Use this property to specify either higher quality, slower rendering, or lower quality, faster rendering of the contents of this Graphics object.
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
// This one is important
g.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
}
public static Size MeasureDrawTextBitmapSize(string text, Font font)
{
Bitmap bmp = new Bitmap(1, 1);
using (Graphics g = Graphics.FromImage(bmp))
{
SizeF size = g.MeasureString(text, font);
return new Size((int)(Math.Ceiling(size.Width)), (int)(Math.Ceiling(size.Height)));
}
}
public static int GetMaximumFontSizeFitInRectangle(string text, Font font, RectangleF rectanglef, bool isWarp, int MinumumFontSize=6, int MaximumFontSize=1000)
{
Font newFont;
Rectangle rect = Rectangle.Ceiling(rectanglef);
for (int newFontSize = MinumumFontSize; ; newFontSize++)
{
newFont = new Font(font.FontFamily, newFontSize, font.Style);
List<string> ls = WarpText(text, newFont, rect.Width);
StringBuilder sb = new StringBuilder();
if (isWarp)
{
for (int i = 0; i < ls.Count; ++i)
{
sb.Append(ls[i] + Environment.NewLine);
}
}
else
{
sb.Append(text);
}
Size size = MeasureDrawTextBitmapSize(sb.ToString(), newFont);
if (size.Width > rectanglef.Width || size.Height > rectanglef.Height)
{
return (newFontSize - 1);
}
if (newFontSize >= MaximumFontSize)
{
return (newFontSize - 1);
}
}
}
public static List<string> WarpText(string text, Font font, int lineWidthInPixels)
{
string[] originalLines = text.Split(new string[] { " " }, StringSplitOptions.None);
List<string> wrappedLines = new List<string>();
StringBuilder actualLine = new StringBuilder();
double actualWidthInPixels = 0;
foreach (string str in originalLines)
{
Size size = MeasureDrawTextBitmapSize(str, font);
actualLine.Append(str + " ");
actualWidthInPixels += size.Width;
if (actualWidthInPixels > lineWidthInPixels)
{
actualLine = actualLine.Remove(actualLine.ToString().Length - str.Length - 1, str.Length);
wrappedLines.Add(actualLine.ToString());
actualLine.Clear();
actualLine.Append(str + " ");
actualWidthInPixels = size.Width;
}
}
if (actualLine.Length > 0)
{
wrappedLines.Add(actualLine.ToString());
}
return wrappedLines;
}
public static Bitmap DrawTextToBitmap(string text, Font font, Color color, DrawMethod mode, RectangleF rectanglef)
{
StringFormat drawFormat = new StringFormat();
Bitmap bmp;
switch (mode)
{
case DrawMethod.AutosizeAccordingToText:
{
Size size = MeasureDrawTextBitmapSize(text, font);
if (size.Width == 0 || size.Height == 0)
{
bmp = new Bitmap(1, 1);
}
else
{
bmp = new Bitmap(size.Width, size.Height);
}
using (Graphics g = Graphics.FromImage(bmp))
{
SetGraphicsHighQualityForTextRendering(g);
g.DrawString(text, font, new SolidBrush(color), 0, 0);
return bmp;
}
}
case DrawMethod.AutoWarpInConstantRectangle:
{
Rectangle rect = Rectangle.Ceiling(rectanglef);
bmp = new Bitmap(rect.Width,rect.Height);
if (rect.Width == 0 || rect.Height == 0)
{
bmp = new Bitmap(1, 1);
}
else
{
bmp = new Bitmap(rect.Width, rect.Height);
}
using (Graphics g = Graphics.FromImage(bmp))
{
SetGraphicsHighQualityForTextRendering(g);
g.DrawString(text, font, new SolidBrush(color), rectanglef, drawFormat);
return bmp;
}
}
case DrawMethod.AutoFitInConstantRectangleWithoutWarp:
{
Rectangle rect = Rectangle.Ceiling(rectanglef);
bmp = new Bitmap(rect.Width, rect.Height);
if (rect.Width == 0 || rect.Height == 0)
{
bmp = new Bitmap(1, 1);
}
else
{
bmp = new Bitmap(rect.Width, rect.Height);
}
using (Graphics g = Graphics.FromImage(bmp))
{
int fontSize = GetMaximumFontSizeFitInRectangle(text, font, rectanglef, false);
SetGraphicsHighQualityForTextRendering(g);
g.DrawString(text, new Font(font.FontFamily, fontSize,font.Style, GraphicsUnit.Point), new SolidBrush(color), rectanglef, drawFormat);
return bmp;
}
}
case DrawMethod.AutoFitInConstantRectangleWithWarp:
{
Rectangle rect = Rectangle.Ceiling(rectanglef);
if (rect.Width == 0 || rect.Height == 0)
{
bmp = new Bitmap(1, 1);
}
else
{
bmp = new Bitmap(rect.Width, rect.Height);
}
using (Graphics g = Graphics.FromImage(bmp))
{
int fontSize = GetMaximumFontSizeFitInRectangle(text, font, rectanglef, true);
SetGraphicsHighQualityForTextRendering(g);
g.DrawString(text, new Font(font.FontFamily, fontSize, font.Style, GraphicsUnit.Point), new SolidBrush(color), rectanglef, drawFormat);
return bmp;
}
}
}
return null;
}
}
}
使用例:Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
// add WindowsBase and PresentationCore assemblies
namespace ReportPrepare
{
public partial class Form1 : Form
{
PictureBox picbox = new PictureBox();
int i = 0;
Timer t = new Timer();
public Form1()
{
InitializeComponent();
this.Controls.Add(picbox);
picbox.Dock = DockStyle.Fill;
t.Interval = 5000;
t.Tick += t_Tick;
t.Enabled = true;
this.Shown += Form1_Shown;
this.SizeChanged += Form1_SizeChanged;
this.Size = new Size(812, 400);
this.StartPosition = FormStartPosition.CenterScreen;
}
void Form1_Shown(object sender, EventArgs e)
{
DrawText();
}
void t_Tick(object sender, EventArgs e)
{
i++;
if (i > 3)
{
i = 0;
}
DrawText();
}
private void DrawText()
{
// text and font
string text = "one two three four five six seven eight nine ten eleven twelve";
Font font = new System.Drawing.Font("Arial", 30, FontStyle.Regular, GraphicsUnit.Point);
switch (i)
{
case 0:
picbox.Image = TextDrawing.DrawTextToBitmap(text, font, Color.Red, TextDrawing.DrawMethod.AutosizeAccordingToText, new RectangleF(0, 0, picbox.Width, picbox.Height));
break;
case 1:
picbox.Image = TextDrawing.DrawTextToBitmap(text, font, Color.Red, TextDrawing.DrawMethod.AutoFitInConstantRectangleWithoutWarp, new RectangleF(0, 0, picbox.Width, picbox.Height));
break;
case 2:
picbox.Image = TextDrawing.DrawTextToBitmap(text, font, Color.Red, TextDrawing.DrawMethod.AutoWarpInConstantRectangle, new RectangleF(0, 0, picbox.Width, picbox.Height));
break;
case 3:
picbox.Image = TextDrawing.DrawTextToBitmap(text, font, Color.Red, TextDrawing.DrawMethod.AutoFitInConstantRectangleWithWarp, new RectangleF(0, 0, picbox.Width, picbox.Height));
break;
}
this.Text = ((TextDrawing.DrawMethod)(i)).ToString() + " Please resize window size by mouse to see drawing methods differences";
}
private void Form1_SizeChanged(object sender, EventArgs e)
{
t.Enabled = false;
t.Enabled = true;
DrawText();
}
}
}
この例では、5秒ごとに1回自動的に描画モードを切り替えます。ピクチャーボックスはメインフォーム内にドッキングされています。フォームのサイズを変更すると、描画モードの違いがユーザーに示されます。