Skip to content

Instantly share code, notes, and snippets.

@fliedonion
Last active September 4, 2017 23:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fliedonion/c4b43ef62fe8080cd30d54360db44af8 to your computer and use it in GitHub Desktop.
Save fliedonion/c4b43ef62fe8080cd30d54360db44af8 to your computer and use it in GitHub Desktop.
TextRender-Wrap-sample
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WrapCalc {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
// textBox1.Text = "あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよらりるれろわをん";
textBox1.Text = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
}
private void button1_Click(object sender, EventArgs e) {
var helper = new WrapCalcHelper();
var g = Graphics.FromHwnd(label1.Handle);
var result = helper.Wrap(g, label1.Font, textBox1.Text, label1.ClientRectangle.Width, 30, 10, Flags);
var freeText = helper.Wrap(g, label1.Font, "CC", label1.ClientRectangle.Width, result.RightOfLastLine, 10, Flags);
var lastRegion = DrawLabel(result.Lines, 0, 30, 10, label1.ForeColor);
var lastRegion2 = DrawLabel(freeText.Lines, lastRegion.Top, result.RightOfLastLine, 10, Color.Red);
}
TextFormatFlags Flags { get {
return TextFormatFlags.SingleLine | TextFormatFlags.NoPrefix | TextFormatFlags.NoPadding;
} }
private Rectangle DrawLabel(List<string> lines, int y, int indent1, int indent2, Color foreColor) {
// label1.Invalidate();
using (var g = Graphics.FromHwnd(label1.Handle)) {
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
var left = indent1;
var top = y;
var lastRegion = new Rectangle(new Point(left, top), Size.Empty);
for(var i = 0; i < lines.Count; i++) {
var line = lines[i];
if(line == "") {
// ブランクでは空行にならないので、non-break spaceにする。
line = WrapCalcHelper.Nbsp;
}
var m = TextRenderer.MeasureText(g, line, label1.Font, new Size(int.MaxValue, int.MaxValue), Flags);
TextRenderer.DrawText(g, line, label1.Font, new Point(left, top), foreColor, Flags);
lastRegion = new Rectangle(new Point(left, top), m);
left = indent2;
top += m.Height;
}
return lastRegion;
}
}
}
}
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using WrapCalc;
// NUnit 3.8 and ChainnigAssertion.NUnit
namespace WrapCalcTest
{
[TestFixture]
public class Class1
{
Graphics g;
Label target = null;
WrapCalcHelper helper = null;
[SetUp]
public void BeforeEach() {
helper = new WrapCalcHelper();
target = new Label();
g = Graphics.FromHwnd(target.Handle);
}
[TearDown]
public void AfterEach() {
if (g != null) g.Dispose();
if (target != null) target.Dispose();
}
[Test]
public void MsGothicTest() {
TextFormatFlags flags = TextFormatFlags.NoPadding | TextFormatFlags.NoPrefix | TextFormatFlags.SingleLine;
var actual = helper.Wrap(g, new Font("MS ゴシック", 11, FontStyle.Bold), "abcdef", 30, 4, 3, flags);
actual.Lines.Count.Is(3);
}
[Test]
public void WhenGiveNotEnoghWidthGetAllStringInCanNotWrite() {
var input = "abcdef";
TextFormatFlags flags = TextFormatFlags.NoPadding | TextFormatFlags.NoPrefix | TextFormatFlags.SingleLine;
var actual = helper.Wrap(g, new Font("MS ゴシック", 11, FontStyle.Bold), input, 1, 4, 3, flags);
actual.Lines.Count.Is(0);
actual.CanNotWrite.Is(input);
}
[Test]
public void WhenGiveStringWithLineBreakGetUnExpectedRightOfLastLine() {
var input = "abcd\nef";
TextFormatFlags flags = TextFormatFlags.NoPadding | TextFormatFlags.NoPrefix ;
var expect = helper.Wrap(g, new Font("MS ゴシック", 11, FontStyle.Bold), input.Split('\n')[1], 1000, 0, 0, flags).RightOfLastLine;
var actual = helper.Wrap(g, new Font("MS ゴシック", 11, FontStyle.Bold), input, 1000, 0, 0, flags);
actual.RightOfLastLine.IsNot(expect);
}
}
}
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WrapCalc {
class WrapCalcHelper {
public class WrapCalcResult {
public List<string> Lines { get; set; }
public string CanNotWrite { get; set; }
public int RightOfLastLine { get; set; }
}
public WrapCalcResult Wrap(Graphics g, Font f, string line, int width, int indent1, int indent2, TextFormatFlags flags) {
// TODO: 1~文字数の中で、widthよりも小さい、最大の文字数を返す。
var returnList = new List<string>();
var curIndent = indent1;
var rest = null as string;
while(line != "") {
Debug.Print(line);
var res = Search(g, f, line, width, curIndent, flags);
if(res == "" && curIndent == indent2) {
// 2段目以降だが書くことができないなら無限ループするので終了する。
// 1段目と2段目が同じインデントサイズであっても終了してよい。
rest = line;
break;
}
curIndent = indent2;
returnList.Add(res);
line = line.Substring(res.Length);
}
var result = new WrapCalcResult {
Lines = returnList,
CanNotWrite = rest,
RightOfLastLine = 0,
};
if (returnList.Count == 0) {
// なにも出力できてないので最終行の右位置は 0のまま返す。
return result;
}
// 最終行の幅を返す。
var lastLine = returnList[returnList.Count - 1];
var s = MeasureText(g, lastLine, f, flags);
if (returnList.Count == 1) {
// 1行目以降のインデントを使って右位置を計算。
result.RightOfLastLine = s.Width + indent1;
return result;
}
else {
// 2行目以降のインデントを使って右位置を計算。
result.RightOfLastLine = s.Width + indent2;
return result;
}
}
private string Search(Graphics g, Font f, string line, int width, int indent, TextFormatFlags flags) {
if(IsSatisfy(g, line, f, width, indent, flags)) {
// 全文字出力可能なら全て返して終了。
return line;
}
if (!IsSatisfy(g, line.Substring(0, 1), f, width, indent, flags)) {
// 1文字も出せないなら終了。
return "";
}
var left = 0;
var right = line.Length;
var mid = -1;
var max = mid;
while(right > left + 1) {
mid = (right + left) / 2;
var cur = line.Substring(0, mid);
if (IsSatisfy(g, cur, f, width, indent, flags)) {
if (mid > max) max = mid;
left = mid;
}
else {
right = mid;
}
}
if (max == -1) {
return "";
}
return line.Substring(0, max);
}
Size MaxSize = new Size(int.MaxValue, int.MaxValue);
private bool IsSatisfy(Graphics g, string line, Font f, int width, int indent, TextFormatFlags flags) {
var needWidth = MeasureText(g, line, f, flags).Width;
return needWidth <= (width - indent);
}
public readonly static string Nbsp = Encoding.UTF8.GetString(new byte[] { 0xc2, 0xa0 });
private Size MeasureText(Graphics g, string line, Font f, TextFormatFlags flags) {
if (line == "") line = Nbsp;
return TextRenderer.MeasureText(g, line, f, MaxSize, flags);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment