Graphics.DrawString绘制指定Point单位字体时,打印输出尺寸不符合预期的原因是什么?
看起来你遇到的这个问题真的挺挠头的——明明所有单位转换都算对了,结果打印出来的字体尺寸就是不对,而且不管怎么换单位设置都没改善。我来帮你拆解一下可能的原因和对应的解决思路:
最可能的元凶:打印机/PrintDocument的自动缩放设置
你提到不管怎么切换PageUnit、怎么转换字体尺寸,打印出来的物理高度都是0.12英寸(12个Display单位),这个数值刚好是12pt的72%(12*(72/100)=8.64pt,8.64/72=0.12英寸),这大概率是自动缩放在搞鬼:
- 检查
PrintDocument.DefaultPageSettings.Zoom:如果这个值不是1.0f(也就是100%),那所有绘制内容都会被按比例缩放。比如设置成0.72的话,12pt的字体就会被缩成8.64pt,对应0.12英寸的物理高度。直接把它设为1.0f试试。 - 检查打印机驱动的默认设置:很多打印机默认会开“适应纸张大小”“缩小到可打印区域”这类选项,会强制缩放所有内容。进打印机设置里把这些自动缩放的选项全部关掉,确保打印是1:1的。
第二可能:Graphics对象的PageScale被篡改
在打印上下文里,有时候系统或第三方代码会偷偷修改e.Graphics.PageScale的值。如果这个值不是1.0f,所有绘制的图形和文本都会被缩放。你可以在DrawString前加一行调试代码:
Debug.WriteLine($"Current PageScale: {e.Graphics.PageScale}");
如果输出不是1.0,手动设置回来:
e.Graphics.PageScale = 1.0f;
GDI+ DrawString的打印单位处理bug
如果上面的设置都没问题,那可能是GDI+在打印场景下对Display单位的字体渲染有bug——它完全忽略了Font指定的GraphicsUnit.Point,直接把Font的Size值当成了Display单位的数值来用(也就是把12pt当成12个Display单位)。这时候你可以换两种思路解决:
1. 手动转换Font尺寸为当前Graphics的PageUnit
既然它不自动转换,那我们自己来:把12pt转换为Display单位的数值(也就是16.666666f),然后用这个数值创建Font,单位设为Display:
float convertedFontSize = GrphxUtils.ConvertGraphicsUnit(12f, GraphicsUnit.Point, e.Graphics.PageUnit, e.Graphics.DpiY); Font Arial12B = new Font("Arial", convertedFontSize, FontStyle.Bold, e.Graphics.PageUnit);
这样DrawString就会直接用正确的Display单位尺寸来渲染字体。
2. 切换用TextRenderer.DrawText替代
GDI+的DrawString在打印场景下的单位处理一直有不少坑,换成GDI的TextRenderer.DrawText可能会更靠谱,它的单位处理更直接:
// 先设置Graphics的PageUnit为Point,直接用Point单位布局 e.Graphics.PageUnit = GraphicsUnit.Point; TextFormatFlags flags = TextFormatFlags.HorizontalCenter | TextFormatFlags.Top | TextFormatFlags.NoClipping | TextFormatFlags.NoPadding; Rectangle layoutRect = new Rectangle((int)x, (int)y, (int)pageWidthInPoints, 12); // 12pt高度 TextRenderer.DrawText(e.Graphics, "Foo", Arial12B, layoutRect, Color.Black, flags);
最后排查字体替换问题
虽然概率低,但也有可能系统找不到你指定的Arial Bold 12pt字体,自动用了一个尺寸不对的替代字体。可以用下面的代码检查字体是否可用:
if (!new FontFamily("Arial").IsStyleAvailable(FontStyle.Bold)) { Debug.WriteLine("Arial Bold is not available on this system!"); }
如果确实不可用,换成系统肯定有的字体,比如FontFamily.GenericSansSerif来测试。
内容来源于stack exchange




