为什么 Graphics.MeasureString() 返回的数字高于预期数字?

2023-11-21

我正在生成收据,并使用 Graphics 对象调用 DrawString 方法来打印所需的文本。

graphics.DrawString(string, font, brush, widthOfPage / 2F, yPoint, stringformat);

这对于我需要它做的事情来说效果很好。我总是知道我要打印的内容,因此我可以手动修剪任何字符串,使其适合 80 毫米收据纸。然后我必须添加一些额外的功能,以使其更加灵活。用户可以传入将添加到底部的字符串。

因为我不知道他们要放什么,所以我只是创建了自己的自动换行函数,该函数接受许多要换行的字符和字符串本身。为了找出字符数,我做了这样的事情:

float width = document.DefaultPageSettings.PrintableArea.Width;
int max = (int)(width / graphics.MeasureString("a", font).Width);

现在宽度返回 283,以毫米为单位约为 72,当您考虑 80 毫米纸张上的边距时,这是有意义的。

但 MeasureString 方法在 Courier New 8pt 字体上返回 10.5。因此,我得到的不是 36 - 40,而是 26,导致 2 行文本变成了 3-4。

PrintableArea.Width 的单位是 1/100 英寸,图形对象的 PageUnit 是 Display(对于打印机来说通常是 1/100 英寸)。那么为什么我只得到 26 呢?


来自 WindowsClient.net:

GDI+ 在显示的每个字符串的每一端添加少量 (1/6 em)。此 1/6 em 允许具有悬垂末端的字形(例如斜体 'f'),并且还为 GDI+ 提供了少量的余地来帮助网格拟合扩展。

默认动作为DrawString在显示相邻运行时会对您不利:

  • 首先,默认的 StringFormat 在每个输出的每一端添加额外的 1/6 em;
  • 其次,当网格安装宽度小于设计宽度时,允许绳子收缩最多一个em。

为了避免这些问题:

  • 总是通过MeasureString and DrawString基于印刷字符串格式的 StringFormat (StringFormat.GenericTypographic).
    设置图形TextRenderingHint to TextRenderingHintAntiAlias。这种渲染方法使用抗锯齿和子像素字形定位来避免网格拟合的需要,因此本质上与分辨率无关。

在 .NET 中绘制文本有两种方法:

  • GDI+ (graphics.MeasureString and graphics.DrawString)
  • GDI (TextRenderer.MeasureText and TextRenderer.DrawText)

来自 Michael Kaplan 的(翻录)优秀博客全部整理出来, 在 .NET 1.1 中使用的一切GDI+用于文本渲染。但也存在一些问题:

  • GDI+ 的无状态特性会导致一些性能问题,其中设备上下文将被设置,然后在每次调用后恢复原始状态。
  • Windows/Uniscribe 和 Avalon (Windows Presentation Foundation) 的国际文本整形引擎已更新多次,但尚未更新 GDI+,这导致对新语言的国际渲染支持不具有相同的质量水平。

所以他们知道他们想要更改 .NET 框架以停止使用GDI+的文本渲染系统,并使用GDI。起初他们希望能够简单地改变:

graphics.DrawString

打电话给旧的DrawTextAPI 而不是 GDI+。但他们无法使文本换行和间距与 GDI+ 完全匹配。所以他们被迫保留graphics.DrawString调用 GDI+(兼容性原因;调用的人graphics.DrawString会突然发现他们的文本没有像以前那样换行)。

一个新的静态TextRenderer创建类是为了包装 GDI 文本渲染。它有两种方法:

TextRenderer.MeasureText
TextRenderer.DrawText

Note: TextRenderer是 GDI 的包装,而graphics.DrawString仍然是 GDI+ 的包装。


然后是如何处理所有现有 .NET 控件的问题,例如:

  • Label
  • Button
  • TextBox

他们想把它们转过来使用TextRenderer(即 GDI),但他们必须小心。可能有人像在 .NET 1.1 中那样依赖控件绘制。于是就诞生了“兼容的文本渲染".

默认情况下,应用程序中的控件的行为就像在 .NET 1.1 中一样(它们是“兼容的").

You turn off兼容模式通过调用:

Application.SetCompatibleTextRenderingDefault(false);

这使您的申请更好、更快,并获得更好的国际支持。总结:

SetCompatibleTextRenderingDefault(true)  SetCompatibleTextRenderingDefault(false)
=======================================  ========================================
 default                                  opt-in
 bad                                      good
 the one we don't want to use             the one we want to use
 uses GDI+ for text rendering             uses GDI for text rendering
 graphics.MeasureString                   TextRenderer.MeasureText
 graphics.DrawString                      TextRenderer.DrawText
 Behaves same as 1.1                      Behaves *similar* to 1.1
                                          Looks better
                                          Localizes better
                                          Faster

注意 GDI+ 之间的映射也很有用TextRenderingHint以及相应的LOGFONT Quality用于GDI字体绘制:

TextRenderingHint           mapped by TextRenderer to LOGFONT quality
========================    =========================================================
ClearTypeGridFit            CLEARTYPE_QUALITY (5) (Windows XP: CLEARTYPE_NATURAL (6))
AntiAliasGridFit            ANTIALIASED_QUALITY (4)
AntiAlias                   ANTIALIASED_QUALITY (4)
SingleBitPerPixelGridFit    PROOF_QUALITY (2)
SingleBitPerPixel           DRAFT_QUALITY (1)
else (e.g.SystemDefault)    DEFAULT_QUALITY (0)

Samples

以下是 GDI+ (graphics.DrawString) 与 GDI (TextRenderer.DrawText) 文本渲染的一些比较:

GDI+: TextRenderingHintClearTypeGridFit, GDI: CLEARTYPE_QUALITY:

enter image description here

GDI+: TextRenderingHintAntiAlias, GDI: ANTIALIASED_QUALITY:

enter image description here

GDI+: TextRenderingHintAntiAliasGridFit, GDI: 不支持,使用ANTIALIASED_QUALITY:

enter image description here

GDI+: TextRenderingHintSingleBitPerPixelGridFit, GDI: PROOF_QUALITY:

enter image description here

GDI+: TextRenderingHintSingleBitPerPixel, GDI: DRAFT_QUALITY:

enter image description here

我觉得奇怪的是DRAFT_QUALITYPROOF_QUALITY,这与CLEARTYPE_QUALITY.

See also

  • UseCompatibleTextRendering - 与 whaaaaaat 兼容?
  • 整理一下:快速浏览一下 Whidbey 的 TextRenderer
  • MSDN:LOGFONT 结构
  • AppCompat Guy:GDI 与 GDI+ 文本渲染性能
  • GDI+ 文本、分辨率独立性和渲染方法。 或者 - 为什么我的文本在 GDI+ 和 GDI 中看起来不同?
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 Graphics.MeasureString() 返回的数字高于预期数字? 的相关文章

  • 如何在 DataColumn.Expression 中使用 IF/ELSE 或 CASE?

    我有一个包含 1 列的表 状态 我想添加另一列名为 Action 的列 其值如下 如果 Status Yes 则 Action Go 否则 Action Stop 我使用以下代码添加到 操作 列中 但它不起作用 myDataTable Co
  • 何时使用 =default 使析构函数默认?

    尽管对构造函数使用 default 对我来说很清楚 即强制编译器在其他构造函数存在时创建默认构造函数 但我仍然无法理解这两种类型的析构函数之间的区别 那些使用 default 的 那些没有显式定义并由编译器自动生成的 我唯一想到的是 gro
  • 使用 Enumerable.OfType() 或 LINQ 查找特定类型的所有子控件

    Existed MyControl1 Controls OfType
  • 如何在 C# / .NET 中创建内存泄漏[重复]

    这个问题在这里已经有答案了 可能的重复 托管代码中是否可能存在内存泄漏 特别是 C 3 0 https stackoverflow com questions 6436620 is it possible to have a memory
  • 读取 C# 中的默认应用程序设置

    我的自定义网格控件有许多应用程序设置 在用户范围内 其中大部分是颜色设置 我有一个表单 用户可以在其中自定义这些颜色 并且我想添加一个用于恢复默认颜色设置的按钮 如何读取默认设置 例如 我有一个名为的用户设置CellBackgroundCo
  • 防止 boost::asio::io_context 在空轮询调用时停止

    此代码调用发布的句柄 boost asio io context ioc boost asio post ioc std cout lt lt lol lt lt std endl ioc poll 而这并没有 boost asio io
  • 如何在 C# 控制台应用程序中将修饰符(ctrl、alt、shift)按键捕获为单个按键?

    Console ReadKey 仅在按下 正常 键时捕获输入 然后将修饰符 如果有 附加为键信息的一部分 如何将单个修饰键注册为输入 提供了一种解决方案这个链接 https blogs msdn microsoft com toub 200
  • 动态生成的控件 ID 返回为 NULL

    我可以在 Page PreInit 函数中创建动态控件 如何检索控件及其 ID 我的 C 代码用于创建动态控件之一 var btn new WebForms Button btn Text btn ID Addmore btn Click
  • fprintf() 线程安全吗?

    我正在为野人就餐问题的某些变量编写一个 C 解决方案 现在 我创建线程 每个线程都将 FILE 获取到同一个调试文件 在线程内我正在使用 fprintf 进行一些打印 打印的语句不受任何类型的互斥锁等保护 我没有在调试文件中观察到任何交错行
  • Visual Studio Code:如何配置 includePath 以获得更好的 IntelliSense 结果

    我是使用 Visual Studio Code 的完全初学者 我不知道我在做什么 我已经四处搜索 也许还不够 但我找不到像我这样的人如何配置的简单解释c cpp properties json每当我单击带有绿色波浪线下划线的行旁边的黄色灯泡
  • 如何从文本文件读取整数到数组

    这就是我想做的 我对此有些不满 但我希望你能容忍我 这对我来说是一个非常新的概念 1 在我的程序中 我希望创建一个包含 50 个整数的数组来保存来自文件的数据 我的程序必须获取用户的文档文件夹的路径 2 文件的名称为 grades txt
  • 将标量添加到特征矩阵(向量)

    我刚刚开始使用 Eigen 库 无法理解如何向所有矩阵成员添加标量值 假设我有一个矩阵 Eigen Matrix3Xf mtx Eigen Matrix3Xf Ones 3 4 mtx mtx 1 main cxx 104 13 error
  • 是否有相当于 Clang/LLVM 的 .spec 文件,在哪里可以找到参考?

    The gcc驱动程序可以配置为使用特定的链接器 特定的选项和其他细节 例如覆盖系统头 specs files 当前 截至撰写本文时 GCC 版本 4 9 0 的手册此处描述了规范文件 https gcc gnu org onlinedoc
  • C# 中的 strstr() 等效项

    我有两个byte 我想找到第二个的第一次出现byte 在第一个byte 或其中的一个范围 我不想使用字符串来提高效率 翻译第一个byte to a string会效率低下 基本上我相信就是这样strstr 在 C 中做 最好的方法是什么 这
  • 使用restsharp序列化对象并将其传递给WebApi而不是序列化列表

    我有一个看起来像的视图模型 public class StoreItemViewModel public Guid ItemId get set public List
  • C++ Streambuf 方法可以抛出异常吗?

    我正在尝试找到一种方法来获取读取或写入流的字符数 即使存在错误并且读 写结束时间较短 该方法也是可靠的 我正在做这样的事情 return stream rdbuf gt sputn buffer buffer size 但如果streamb
  • C++0x中disable_if在哪里?

    Boost 两者都有enable if and disable if 但 C 0x 似乎缺少后者 为什么它被排除在外 C 0x 中是否有元编程工具允许我构建disable if按照enable if 哦 我刚刚注意到std enable i
  • QFileDialog::getSaveFileName 和默认的 selectedFilter

    我有 getSaveFileName 和一些过滤器 我希望当用户打开 保存 对话框时选择其中之一 Qt 文档说明如下 可以通过将 selectedFilter 设置为所需的值来选择默认过滤器 我尝试以下变体 QString selFilte
  • 使我的 COM 程序集调用异步

    我刚刚 赢得 了在当前工作中维护用 C 编码的遗留库的特权 这个dll 公开使用 Uniface 构建的大型遗留系统的方法 除了调用 COM 对象之外别无选择 充当此遗留系统与另一个系统的 API 之间的链接 在某些情况下 使用 WinFo
  • Java 和/C++ 在多线程方面的差异

    我读过一些提示 多线程实现很大程度上取决于您正在使用的目标操作系统 操作系统最终提供了多线程能力 比如Linux有POSIX标准实现 而windows32有另一种方式 但我想知道编程语言水平的主要不同 C似乎为同步提供了更多选择 例如互斥锁

随机推荐

  • Spring boot - MySQL 设置不起作用

    我正在尝试使用 Spring boot 和 MySQL 开发一个应用程序 正如文档所说 首先我使用 Intelij Idea 使用 Springinitializr 创建了项目 配置了application properties文件 并写道
  • 如何动态更改 Crypt 在 Laravel 中使用的密钥?

    我一直在研究如何使用Laravel 加密因为建立一个宅基地加密平台是不受欢迎的 也是理所当然的 Illuminate Support Facades Crypt encryptString This is a secret message
  • 从量角器激活时激活 chrome 语言标志(selenium)

    我正在使用 Protractor 为一个有角度的网站编写端到端测试 我们必须支持某些语言 所以我想使用以下命令来初始化 chrome lang标记并用其他语言启动它 我搜索了网络 但找不到任何有关如何完成此操作的示例 我唯一的线索是我看到并
  • Jitsi SIP Communicator 安装程序

    在完成更改后 我必须为 Jitsi SIP Communicator 创建安装程序 我在网上搜索并发现了一些步骤 如下所示 软件要求 西格温 包含库 make gcc 和 g http cygwin com setup exe 明GW 仅
  • 使用内联CSS - 在某些情况下是禁忌还是可以? [复制]

    这个问题在这里已经有答案了 可能的重复 内联样式与 CSS 中的样式 我想我正在寻找对此的一些意见 我完全赞成在单独的 css 文件中使用 css 样式作为类 但每隔一段时间 我就会遇到一种情况 我只需要为特定元素进行一些填充 或者仅在一种
  • 使用.NET捕获屏幕截图[重复]

    这个问题在这里已经有答案了 可能的重复 如何以位图形式捕获屏幕 我需要制作一个应用程序 每当按下特定按钮时都会捕获当前屏幕的快照 我搜索了很多 但只找到了如何捕获当前窗口 您能帮我弄清楚如何在 NET 中执行此操作吗 我们可以通过点击打印屏
  • mysql RAND() 限制

    您好 我有一个这样的查询 SELECT otel id FROM YAZILIM menu icerik WHERE YAZILIM menu icerik menu id 39 AND otel id IN SELECT otel id
  • 比较 Javascript 中的日期和时区

    当使用 gt 和 当您将其转换为 JavaScript 时 将按照您的预期考虑时间戳字符串表示形式的时区部分Date对象 内部值是一个简单的标量 标准化为UTC 所以比较时不需要特殊的时区处理Date对象 var d1 new Date D
  • Javascript 日期,这是我的错误还是我发现了错误?

    我的应用程序中有一个简单的 Javascript 部分 其中有一个链接 添加日期 它会在日期上添加 1 天 它总是工作得很好 除非日期是 11 07 2010 然后链接突然不再工作 它真的很奇怪 因为它只在特定日期 11 07 2010 挂
  • GCC 编译时抛出错误:错误:未知类型名称“FILE”

    我正在制作一个只写的函数 hello 到一个文件 我已将其放在另一个文件中 并将其标头包含在程序中 但 gcc 给出了一个错误 error unknown type name FILE 代码如下 app c include
  • 缩放 SKNode 不一致

    我已经创建了自己的解决方案 用于放大或缩小特定的 SKNode 而无需缩放整个场景 它似乎主要按照我期望的方式工作 有 2 个值得注意的例外 我希望在这里得到输入 首先是代码 该控制语句位于touchesMoved方法内 if touche
  • 使用jquery更改给定特定宽度范围的类名(媒体查询)

    我正在尝试修改以下 html div class col1 width8 img src images entity jpg div 我想使用媒体查询 但我不想修改css 而是将类名从width8替换为width6 对于下面的标准媒体查询来
  • iPhone 3GS 上后台线程消耗 100% CPU 导致潜在主线程

    在我的应用程序中 我在 NSOperationQueue 中执行 10 个异步 NSURLConnections 作为 NSInitationOperations 为了防止每个操作在连接有机会完成之前返回 我调用 CFRunLoopRun
  • 编辑表单中的 Django-Taggit

    这是一个模型类 class ModelName models Model pasta TaggableManager verbose name u Pasta 和一个表单模板 正常 P form as p 我想让一切都非常干净和有用 但结果
  • 使用“::”代替“module ...”作为 Ruby 命名空间

    在 Ruby 中 写法有区别吗class Foo Bar and module Foo class Bar用于命名空间 如果是这样 那又怎样 如果你使用class Foo Bar 但是Foo模块尚未定义 将会引发异常 而module Foo
  • Solr 通配符搜索

    如果我有一条包含关键字 Chris Muench 的记录 我希望能够匹配 Mue 或 Chr 我怎样才能用 solr 查询来做到这一点 目前我执行以下操作 results solr gt search Apache Solr Service
  • 使用 matplotlib 的内存泄漏

    这并不是一个错误报告 即使这些泄漏可能是由于 mpl 错误造成的 请解释所提出的问题 以寻求解决方法 问题很简单 绘制大量数据 使用plot 或scatter 清除 释放所有内容 垃圾收集 但仍然没有释放几乎所有内存 Line Mem us
  • 如何使用 Moment.js 排除两个日期之间的周末

    我试图在我的 JavaScript 代码中排除周末 我使用 moment js 并且很难为 天 选择正确的变量 到目前为止 我认为我需要通过将工作日变量更改为仅从第 1 天计数到第 5 天来排除第 6 天 星期六 和第 0 天 星期日 但不
  • 无需重新加载页面即可收到通知(例如 facebook 或 google plus 通知)

    将 Facebook 等通知发送到仪表板的理想机制是什么 我认为最好的方法是每 5 秒对 php 页面进行一次 Ajax 调用并检索通知 有没有更好的方法来进行类似的更改 它也应该适用于所有移动浏览器 我按照以下方式做 use post在j
  • 为什么 Graphics.MeasureString() 返回的数字高于预期数字?

    我正在生成收据 并使用 Graphics 对象调用 DrawString 方法来打印所需的文本 graphics DrawString string font brush widthOfPage 2F yPoint stringformat