Graphics.MeasureCharacterRanges 给出错误的尺寸计算

2023-11-24

我正在尝试将一些文本渲染到 Web 表单应用程序中图像的特定部分。文本将由用户输入,因此我想改变字体大小以确保它适合边界框。

我的代码在概念验证实现上做得很好,但我现在正在针对设计器的资产进行尝试,这些资产更大,并且我得到了一些奇怪的结果。

我正在按如下方式运行尺寸计算:

StringFormat fmt = new StringFormat();
fmt.Alignment = StringAlignment.Center;
fmt.LineAlignment = StringAlignment.Near;
fmt.FormatFlags = StringFormatFlags.NoClip;
fmt.Trimming = StringTrimming.None;

int size = __startingSize;
Font font = __fonts.GetFontBySize(size);

while (GetStringBounds(text, font, fmt).IsLargerThan(__textBoundingBox))
{
    context.Trace.Write("MyHandler.ProcessRequest",
        "Decrementing font size to " + size + ", as size is "
        + GetStringBounds(text, font, fmt).Size()
        + " and limit is " + __textBoundingBox.Size());

    size--;

    if (size < __minimumSize)
    {
        break;
    }

    font = __fonts.GetFontBySize(size);
}

context.Trace.Write("MyHandler.ProcessRequest", "Writing " + text + " in "
    + font.FontFamily.Name + " at " + font.SizeInPoints + "pt, size is "
    + GetStringBounds(text, font, fmt).Size()
    + " and limit is " + __textBoundingBox.Size());

然后,我使用以下行将文本渲染到从文件系统中提取的图像上:

g.DrawString(text, font, __brush, __textBoundingBox, fmt);

where:

  • __fonts is a PrivateFontCollection,
  • PrivateFontCollection.GetFontBySize是一个扩展方法,返回一个FontFamily
  • RectangleF __textBoundingBox = new RectangleF(150, 110, 212, 64);
  • int __minimumSize = 8;
  • int __startingSize = 48;
  • Brush __brush = Brushes.White;
  • int size从 48 开始并在循环内递减
  • Graphics g has SmoothingMode.AntiAlias and TextRenderingHint.AntiAlias set
  • context is a System.Web.HttpContext(这是摘录自ProcessRequest的方法IHttpHandler)

其他方法是:

private static RectangleF GetStringBounds(string text, Font font,
    StringFormat fmt)  
{  
    CharacterRange[] range = { new CharacterRange(0, text.Length) };  
    StringFormat myFormat = fmt.Clone() as StringFormat;  
    myFormat.SetMeasurableCharacterRanges(range);  

    using (Graphics g = Graphics.FromImage(new Bitmap(
       (int) __textBoundingBox.Width - 1,
       (int) __textBoundingBox.Height - 1)))
    {
        g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
        g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
        
        Region[] regions = g.MeasureCharacterRanges(text, font,
            __textBoundingBox, myFormat);
        return regions[0].GetBounds(g);
    }  
}

public static string Size(this RectangleF rect)
{
    return rect.Width + "×" + rect.Height;
}

public static bool IsLargerThan(this RectangleF a, RectangleF b)
{
    return (a.Width > b.Width) || (a.Height > b.Height);
}

现在我有两个问题。

首先,正文有时坚持通过在单词中插入换行符来换行,而此时它应该无法容纳并导致 while 循环再次递减。我不明白为什么会这样Graphics.MeasureCharacterRanges认为当它不应该在单词中自动换行时,它就适合在框中。无论使用什么字符集(我以拉丁字母单词以及 Unicode 范围的其他部分,如西里尔文、希腊文、格鲁吉亚文和亚美尼亚文)都会表现出这种行为。我应该使用一些设置来强制Graphics.MeasureCharacterRanges仅在空白字符(或连字符)处自动换行?第一个问题与帖子 2499067.

其次,在放大到新图像和字体大小时,Graphics.MeasureCharacterRanges给我的高度相差很大。这RectangleF我正在绘制的内容对应于图像的视觉上明显的区域,因此我可以轻松地看到文本何时减少超过必要的数量。然而,当我向它传递一些文本时,GetBounds呼叫给我的高度几乎是实际高度的两倍。

使用反复试验来设置__minimumSize为了强制退出 while 循环,我可以看到 24pt 文本适合边界框,但是Graphics.MeasureCharacterRanges报告显示,渲染到图像后,该文本的高度为 122 像素(当边界框高 64 像素并且适合该框时)。事实上,在不强迫的情况下,while 循环迭代到 18pt,此时Graphics.MeasureCharacterRanges返回一个合适的值。

跟踪日志摘录如下:

将字体大小减小到 24,因为大小为 193×122,限制为 212×64
将字体大小减小到 23,因为大小为 191×117,限制为 212×64
将字体大小减小到 22,因为大小为 200×75,限制为 212×64
将字体大小减小到 21,因为大小为 192×71,限制为 212×64
将字体大小减小到 20,因为大小为 198×68,限制为 212×64
将字体大小减小到 19,因为大小为 185×65,限制为 212×64
以 DIN-Black 18pt 书写 HESSELINK 的 VENNEGOOR,尺寸为 178×61,限制为 212×64

那么为什么是Graphics.MeasureCharacterRanges给我一个错误的结果?如果循环停止在 21pt 左右,我可以理解它是字体的行高(如果我截图结果并在 Paint.Net 中测量它,这在视觉上会合适),但它比它应该做的要远得多因为,坦率地说,它返回了错误的该死的结果。


我有一个类似的问题。我想确切地知道我要绘制的文本有多大,以及它会出现在哪里。我没有遇到断行问题,所以我认为我无法帮助你。我在使用所有可用的各种测量技术时遇到了同样的问题,包括最终使用 MeasureCharacterRanges,它对于左侧和右侧工作正常,但对于高度和顶部则完全不起作用。 (不过,对于一些罕见的应用程序来说,使用基线可以很好地工作。)

我最终得到了一个非常不优雅、低效但有效的解决方案,至少对于我的用例来说是这样。我在位图上绘制文本,检查这些位以查看它们最终的位置,这就是我的范围。由于我主要绘制小字体和短字符串,因此它对我来说已经足够快了(尤其是我添加的记忆功能)。也许这并不完全是您所需要的,但也许它无论如何都能引导您走上正确的道路。

请注意,它需要编译项目以允许目前不安全的代码,因为我试图从中榨取每一点效率,但如果您愿意,可以删除该限制。此外,它不像现在那样线程安全,如果需要,您可以轻松添加它。

Dictionary<Tuple<string, Font, Brush>, Rectangle> cachedTextBounds = new Dictionary<Tuple<string, Font, Brush>, Rectangle>();
/// <summary>
/// Determines bounds of some text by actually drawing the text to a bitmap and
/// reading the bits to see where it ended up.  Bounds assume you draw at 0, 0.  If
/// drawing elsewhere, you can easily offset the resulting rectangle appropriately.
/// </summary>
/// <param name="text">The text to be drawn</param>
/// <param name="font">The font to use when drawing the text</param>
/// <param name="brush">The brush to be used when drawing the text</param>
/// <returns>The bounding rectangle of the rendered text</returns>
private unsafe Rectangle RenderedTextBounds(string text, Font font, Brush brush) {

  // First check memoization
  Tuple<string, Font, Brush> t = new Tuple<string, Font, Brush>(text, font, brush);
  try {
    return cachedTextBounds[t];
  }
  catch(KeyNotFoundException) {
    // not cached
  }

  // Draw the string on a bitmap
  Rectangle bounds = new Rectangle();
  Size approxSize = TextRenderer.MeasureText(text, font);
  using(Bitmap bitmap = new Bitmap((int)(approxSize.Width*1.5), (int)(approxSize.Height*1.5))) {
    using(Graphics g = Graphics.FromImage(bitmap))
      g.DrawString(text, font, brush, 0, 0);
    // Unsafe LockBits code takes a bit over 10% of time compared to safe GetPixel code
    BitmapData bd = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
    byte* row = (byte*)bd.Scan0;
    // Find left, looking for first bit that has a non-zero alpha channel, so it's not clear
    for(int x = 0; x < bitmap.Width; x++)
      for(int y = 0; y < bitmap.Height; y++)
        if(((byte*)bd.Scan0)[y*bd.Stride + 4*x + 3] != 0) {
          bounds.X = x;
          goto foundX;
        }
  foundX:
    // Right
    for(int x = bitmap.Width - 1; x >= 0; x--)
      for(int y = 0; y < bitmap.Height; y++)
        if(((byte*)bd.Scan0)[y*bd.Stride + 4*x + 3] != 0) {
          bounds.Width = x - bounds.X + 1;
          goto foundWidth;
        }
  foundWidth:
    // Top
    for(int y = 0; y < bitmap.Height; y++)
      for(int x = 0; x < bitmap.Width; x++)
        if(((byte*)bd.Scan0)[y*bd.Stride + 4*x + 3] != 0) {
          bounds.Y = y;
          goto foundY;
        }
  foundY:
    // Bottom
    for(int y = bitmap.Height - 1; y >= 0; y--)
      for(int x = 0; x < bitmap.Width; x++)
        if(((byte*)bd.Scan0)[y*bd.Stride + 4*x + 3] != 0) {
          bounds.Height = y - bounds.Y + 1;
          goto foundHeight;
        }
  foundHeight:
    bitmap.UnlockBits(bd);
  }
  cachedTextBounds[t] = bounds;
  return bounds;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Graphics.MeasureCharacterRanges 给出错误的尺寸计算 的相关文章

  • 如何缝合重叠很少的图像?

    我正在尝试使用重叠很少的图像创建全景图 但我知道相机的角度 因此我确切地知道有多少重叠 并且我知道图像的顺序 因此我知道每个图像在全景图中的位置 作为第一步 我只是将图像连接在一起 但结果不够好 有没有办法将位图裁剪为梯形以消除 大部分 重
  • 在 3D 空间中旋转图像的一部分

    设置如下 这是一个电子商务艺术网站 其中一些绘画是画布转移 这幅画环绕画布的侧面 顶部和底部 我们拥有整幅画的高分辨率图像 但我们想要显示的是图像的准 3D 表示 您可以在其中看到画作的侧面如何环绕画布 这是我正在谈论的内容的粗略草图 我的
  • 我们应该使用 OpenGL 来处理 2D 图形吗?

    如果我们想做一个像MS Paint这样的应用程序 我们应该使用OpenGL来渲染图形吗 我想谈谈使用传统 GDI 与 OpenGL 时的性能 如果有一些更好的库用于此目的 请看我的一个 GDI X11 OpenGL 都是渲染 API 即您通
  • Windows 或 ASP.NET 服务中的 System.Drawing

    根据MSDN http msdn microsoft com en us library system drawing aspx 在 中使用类并不是一个特别好的主意系统图Windows 服务或 ASP NET 服务中的命名空间 现在我正在开
  • GDI+ / C#:如何将图像另存为 EMF?

    如果您使用 Image Save 方法将图像保存到 EMF WMF 则会出现异常 http msdn microsoft com en us library ktx83wah aspx http msdn microsoft com en
  • 活动滚动时绘制面板边框

    当我尝试制作面板边框时 我遇到了问题 首先我将属性面板设置为 自动滚动 真 然后我将边框绘制代码放在面板事件中 ControlPaint DrawBorder e Graphics ClientRectangle Color Black 5
  • 如何使用c++/WinAPI绘制透明背景的文本?

    如何使用WinAPI绘制透明颜色的文本 通常我使用 SetBkMode hDC TRANSPARENT 但现在我需要使用双缓冲区 这样 图像绘制正确 但文本绘制不正确 黑色背景 case WM PAINT hDC BeginPaint hW
  • BitBlt 不在硬件加速模式下捕获窗口

    我目前正在使用 GDI32 dll 捕获窗口快照 尽管我遇到了硬件加速 Windows 的问题 我想知道是否有办法规避 我在这里发现了这段令人惊奇的代码 public static Image CaptureWindow IntPtr ha
  • 如何计算数字的正确宽度(以像素为单位)?

    我有一个自定义控件 将来可能有用户可自定义的字体 缩放已经实现 我必须在两位数字下方填充一个矩形 形成以 10 为基数的数字 我对零个 一个或两个数字有不同的颜色 使用字体 Name Microsoft Sans Serif Size 16
  • 如何在c#中向图标添加文本?

    我想在系统托盘中显示一个图标 ico 文件 并在运行时添加一些文本 有没有原生的 WPF 方法可以做到这一点 或 GDI 的片段也将不胜感激 谢谢 这是对我有用的代码 public static Icon GetIcon string te
  • 双缓冲? Win32 C++

    我正在尝试实现双缓冲 但它似乎不起作用 即图形仍然闪烁 每次鼠标移动时都会调用 WM PAINT WM MOUSEMOVE 粘贴 WM PAINT 如下 case WM PAINT hdc BeginPaint hWnd ps TODO A
  • 使用 gdi+ 将 png 转换为 gif (C#)

    我有一个 png 文件 必须将其转换为 gif 文件 里面有一个透明的部分 当我保存它时 透明的部分是黑色的而不是透明的 这是我的代码 FileStream imgStream new FileStream outputFile FileM
  • AutoHotkey-GDIp:从硬件加速窗口捕获屏幕截图

    我目前正在编写一个小脚本 该脚本可以从 BlueStacks 中的硬件加速窗口捕获屏幕截图 问题是 看起来窗口必须是硬件加速的 因此屏幕捕获保存了一个黑色方块 我使用 AutoHotkey 编写脚本 并添加了 GDIp 库来访问 GDI 我
  • 使 GDI 绘图不可点击

    我有一个带有 GDI 绘图的透明 WinForms 应用程序 我将其用作覆盖层 问题是 每当我单击 GDI 绘图时 焦点就会转到应用程序窗口 我该如何扭转这种情况 您需要使用right颜色如TransparencyKey 一切使得Form
  • 比较两个 Color 对象

    这是 VS2010 和 NET 4 0 我正在尝试比较两个System Drawing Color对象 的价值mStartColor ToArgb is 16777215 的价值Color Transparent ToArgb is 167
  • DPI 意识真的需要吗?

    我正在学习如何使用 GDI GDI 绘制 GUI 我发现了这个http msdn microsoft com en us library windows desktop dd756596 v vs 85 aspx step 2 declar
  • Windows 字体安装后无法立即在应用程序中使用?

    每当我在 Windows 2003 服务器上安装新字体时 我都无法立即在我的 asp net Web 应用程序中使用它 应用程序通过以下方式获取字体CreateFontIndirectgdi32 dll win api 然后使用此字体在我的
  • gdi+ 中的半色调效果

    我该如何去模仿this http www juicybitssoftware com halftone GDI 中的半色调效果 它几乎看起来像弗洛伊德 斯坦伯格 http en wikipedia org wiki Floyd E2 80
  • 我需要为我的应用程序制作和加载多大尺寸的 ImageList 图标(考虑更高的 DPI)?

    我有一个CListCtrl http msdn microsoft com en us library hfshke78 aspx控制 或ListView http msdn microsoft com en us library wind
  • 椭圆绘制WPF动画

    我正在开发一个矩形区域的控件 当触发发生时 将在矩形区域中绘制一个椭圆形 该控件将能够承载其他控件 例如文本框 按钮等 因此在触发时将在它们周围绘制圆圈 我希望将圆圈绘制为动画 就像您用笔圈出内部控件一样 实现这一目标的最佳方法是什么 我一

随机推荐

  • 在php中调整图像的透明度

    我已经仔细研究了在调整 png 大小时如何正确管理 alpha 我设法让它保持透明度 但仅限于完全透明的像素 这是我的代码 src image imagecreatefrompng file dir this gt file name ds
  • C++20 中的指定初始值设定项

    我有一个关于 c 20 功能之一的问题 指定初始化程序 有关此功能的更多信息here include
  • 请求在 v2.3 API 中获取 Facebook 页面点赞总数

    以前我为此使用 FQL 但从 v2 1 开始已弃用 我将使用图形边缘 likes 转向 v2 3 这是我的网址 https graph facebook com v2 3
  • 如何根据标签更改 Chart.js 点的颜色

    我有一个 Chart js 折线图 其中标签是星期几 我想根据具体日期 周一至周日 更改点背景 我可以根据数据值更改背景颜色 但这不是我需要的 相反 我想给每一天 标签 一个不同的色点 例如 这就是我如何根据数据值更改点 不是我需要的 ch
  • 带有 EduTools 插件的 Kotlin Koans:“无法启动检查”

    我正在尝试遵循科特林公案Android Studio 中的教程安装 EduTools 插件 and 选择 Kotlin Koans 课程 一切正常 但是当我尝试时 检查任务 in the 任务描述面板 我收到此错误 启动检查失败 我也尝试了
  • 覆盖功能

    我正在学习一所著名大学提供的 iOS 在线课程 我不明白为什么使用以下代码override这是合法的 根据官方定义 我们使用override重写超类的方法 下面代码中的子类和超类在哪里 什么被覆盖以及被什么覆盖 public overrid
  • 使用 cv::Mat 进行高效的 C++ 四元数乘法

    我想乘以 2 个四元数 它们存储在cv Mat结构 我希望该功能尽可能高效 到目前为止我有以下代码 Quaternion multiplication void multiplyQuaternion const Mat q1 const M
  • 使用 ServiceStack.ORMLite 实现工作单元和存储库模式的最佳实践

    假设有两个存储库接口 interface IFooRepository void Delete int id interface IBarRepository void Delete int id 以及工作单元界面 例如 interface
  • React 从 URL 内联导入 SVG

    我正在解决一个问题 其中 SVG 图像需要从 URL AWS S3 加载到反应组件 我能够使用本地文件中的 SVG 内联反应组件成功显示和加载图像 但是 svg 文件需要从 S3 存储桶内联加载 JS svg 导入不适用于 URL 所以我想
  • 从 Python 中的 webbrowser.get() 调用 Chrome Web 浏览器

    我应该如何调用 webbrowser get 函数以便打开 chrome 网络浏览器 我正在运行 Ubuntu 11 04 和 Python 版本 2 7 使用 webbrowser get chrome 会产生错误 快速解决方法是将 Ch
  • HttpClient 不支持 PostAsJsonAsync 方法 C#

    我正在尝试从我的 Web 应用程序调用 Web API 我正在使用 Net 4 5 在编写代码时出现错误HttpClient不包含定义PostAsJsonAsync method 下面是代码 HttpClient client new Ht
  • 批处理在 for /f 命令上过早关闭

    我有一个批处理文件 在 Windows XP 中 激活了命令扩展 其中包含以下行 for f s in type version txt do set VERSION s 在某些计算机上 它工作得很好 如图所示这个问题 但在其他方面它杀死了
  • UISplitViewController 和方向 - iOS < 5.0

    我使用 splitviewcontroller 作为我的应用程序的根视图 我需要将登录和注册视图显示为 splitviewcontroller 顶部的模式视图 当我尝试从 splitViewController 的 rootview 的 v
  • “RepeatedCompositeContainer”类型的对象不可 JSON 序列化

    使用 Google Client Library 与视觉库交互 我有一个从图像中检测标签的功能 GoogleVision py import os from google cloud import vision from google cl
  • HTML Canvas - 绘制弯曲箭头

    我正在尝试在 html 画布中绘制弯曲的箭头 我画一条曲线没有问题 但我不知道如何放置 gt 在线的末端 方向 ctx beginPath ctx fillStyle rgba 55 217 56 opacity ctx moveTo th
  • 是什么触发了此异常实例:“java.lang.IllegalArgumentException:观察者为空。”如何避免呢?

    返回原始版本时出现此异常ListActivity使用用户选择的项目的内容打开新活动后 它只出现在冰淇淋三明治上 这是痕迹 java lang IllegalArgumentException The observer is null at
  • 如何从 String 中获取 Date 对象

    DateFormat formatter new SimpleDateFormat MM dd yyyy HH mm ss Date d Date formatter parse dateTime System out println da
  • C++ 将字符串转换为十六进制[重复]

    这个问题在这里已经有答案了 可能的重复 C 将十六进制字符串转换为有符号整数 我已经在谷歌上搜索过 但没有找到任何帮助 所以这是我的问题 我有已经包含十六进制代码的字符串 例如 string s1 5f0066 我想将此字符串转换为十六进制
  • Tablesorter zebra 在排序之前不会条纹

    我有我的桌子 它们很棒 我可以对它们进行排序 而且效果非常好 只是在我第一次对它们进行排序之前 它们不会进行斑马条纹 我的理解是 一旦表排序器初始化 它们就会被条带化 不是这样吗 这是来自此处的 tablesorter v 2 10 最新版
  • Graphics.MeasureCharacterRanges 给出错误的尺寸计算

    我正在尝试将一些文本渲染到 Web 表单应用程序中图像的特定部分 文本将由用户输入 因此我想改变字体大小以确保它适合边界框 我的代码在概念验证实现上做得很好 但我现在正在针对设计器的资产进行尝试 这些资产更大 并且我得到了一些奇怪的结果 我