【问题标题】:Custom richtextbox control kerning issues自定义richtextbox控件的字距问题
【发布时间】:2015-05-15 02:53:21
【问题描述】:

好的,所以我已经做了一段时间的工作,我已经到了计划文本渲染部分的地步。

我已经可以用两种方式绘制文本字符串了; DrawString 和 TextRenderer.DrawText。我更喜欢 DrawText,因为使用 TextRenderer.Measure text 时测量文本更准确。

我有一堂课:

public class Character 
{
public string character {get; set; } 
public Font font {get; set; }
public Point position {get; set; }
} 

以及所有按下的字符的列表:

public List<Character> chars = new List<Character>();

现在我的问题是我需要能够在运行时为任何给定的选定字符或单词设置不同的字体和颜色以及粗体或斜体。所以我不能只画一个完整的字符串,因为这样我就无法为用户选择更改的每个字符设置单独的字体设置。

所以我需要能够为每个字符存储不同的字体样式信息,然后将它们全部添加到一个列表中,这样我就可以遍历每个字符并按照应该绘制的方式绘制每个字符(即每个字符都有自己的风格等)。

这个解决方案对我来说很好。而且由于几个月来我无法在任何地方找到有关此的任何信息,我完全被困住了。

我的主要问题是,因为我是逐个字符地绘制,所以我不知道每个字符应该与之前绘制的字符相距多远(紧缩)。

对于输入(文本框)控件,我们如何自定义绘制文本并允许用户将单词的一部分设为蓝色,而将单词的另一半设为不同的大小、颜色和样式,例如,同时仍然遵守正确的字距设置?

我们如何知道在哪里绘制每个字符?

人们说只需立即重新启动整个字符串。但这并不能解决我最初的问题。我需要能够逐个绘制每个字符,以便保存有关它的字体信息。

【问题讨论】:

  • 您可能想看看this post,我在其中展示了如何使用 GraphicsPath 进行测量。还有this post,我在这里展示了如何检测像素精确碰撞。后者可用于创建 Kerning 表..
  • 对于更简单的解决方案(没有字距调整),请确保在测量单个字符时使用必要的GenericTypographic option
  • 我已经解决了创建字距调整表的问题。它将由一个标识符组成,该标识符包含 两个 字符及其两个 Fonts 作为键和一个 int 作为值。请注意,字距调整表始终包含字符 pairs;仅一个角色无法知道与下一个角色的最佳距离!
  • 非常感谢您的 cmets @TaW。昨晚本来想回复的,结果睡着了。您的一个链接中的代码非常有用。

标签: c# .net winforms gdi kerning


【解决方案1】:

字距调整字符间距是不同的,如果您想完全控制代码打印的内容,您可能需要同时实现两者。

让我们先看一个示例输出:

图片一显示直接输出,额外字符间距为 1 像素,无字距调整。

图像二 应用了一些字距调整,但仅适用于三个字距调整对。

我试图通过绘制字符文本测量的结果来使事情更清楚。还有一个平铺的 1 像素光栅作为面板 BackgroundImage。 (为了看得更清楚,您可能需要下载 png 文件!)

private void panel2_Paint(object sender, PaintEventArgs e)
{
   string fullText = "Text;1/2' LTA";
   StringFormat strgfmt = StringFormat.GenericTypographic;
   Font font = new Font("Times", 60f, FontStyle.Regular);
   float x = 0f;
   using (SolidBrush brush = new SolidBrush(Color.FromArgb(127, 0, 127, 127)))
   {
        for (int i = 0; i < fullText.Length; i++)
        {
            string text = fullText.Substring(i, 1);
            SizeF sf = e.Graphics.MeasureString(text, font, 9999, strgfmt );
            e.Graphics.FillRectangle(brush, new RectangleF(new PointF(x, 0f), sf));
            e.Graphics.DrawString(text, font, Brushes.Black, x, 0, strgfmt );
            x += sf.Width + 1;  // character spacing = +1

            //if (i < fullText.Length - 1) doKerning(fullText.Substring(i, 2), ref x);
        }
   }
}

void doKerning(string c12, ref float x)
{
    if (smallKerningTable.ContainsKey(c12)) x -= smallKerningTable[c12];
}

Dictionary<string, float> smallKerningTable = new Dictionary<string, float>();

void initKerningTable()
{
    smallKerningTable.Add("Te", 7f);
    smallKerningTable.Add("LT", 8f);
    smallKerningTable.Add("TA", 11f);
    //..
}

背景是这样创建的:

public Form1()
{
   InitializeComponent(); 
   Bitmap bmpCheck2 = new Bitmap(2, 2);   
   bmpCheck2.SetPixel(0, 0, Color.FromArgb(127, 127, 127, 0));
   panel2.BackgroundImage = bmpCheck2;
   panel2.BackgroundImageLayout = ImageLayout.Tile;
   //..
 }

如果您想使用字距调整,您需要构建一个更长的字距调整表。

在现实生活中,排版师和字体设计师手动进行,仔细查看字形,调整字距,直到看起来非常好。

这是相当昂贵的,仍然不包括字体混合。

所以你可能想要

  • 毕竟不要使用字距调整。 确保使用 StringFormat.GenericTypographic 选项来测量和绘制琴弦!
  • 为一些特别有问题的字符创建一个小的字距调整表,例如“L”、“T”、“W”、“V”和“A”..
  • 编写代码为您需要的所有对创建完整的字距调整表,或者..
  • 所有对

要编写代码来创建字距调整表,您可以:

  • 为每个字符创建一个位图
  • 遍历所有对并
  • 将第二个位图向左移动,直到一些不透明/黑色像素发生碰撞。
  • 移动的距离不应超过宽度的一半,否则距离应重置为 0,因为某些字符对根本不会发生冲突并且不应有任何字距调整,例如:'^_' 或'.-'

如果您想混合字体和/或 FontStyles,则必须扩展字距调整表的键以包含字符具有的两种相应字体和样式的一些 ID..

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多