最近有国内新客户抱怨我们产品显示的原理图太不专业了,在原理图上使用宋体GB2312设计好中文图表,经过几次缩放时,表格内的文字居然会跑到表格外边,更要命的是打印出来的文档也存在同样的问题。 我研究了一下,原来又是Windows GDI Text APIs的一个大坑!

问题详细描述

用Tcl/tk script可以很容易地重现这个问题。如下图,第一行文字使用的Arial字体rendering by GDI APIs, 第二行文字使用的是OF Helvetica字体rendering by Freetype APIs,两行文字都用和字体无关的方式加了边框。

  • 刚运行脚本时,两行文字都是8pt,虽然显示效果不一样,但看着是一样大。 

  • 放大几次后,第一行的GDI文字明显跑到框外了。 

再看一下这时的log: 
font:-family Arial -size 20.118148965022 -weight normal -slant roman -underline 0 -overstrike 0 -type truetype 
font:-family {OF Helvetica}
 -size 19.53125 -weight normal -slant roman -underline 0 -overstrike 0 -type outline 
zoom fact:2.44140625

缩放比例2.44140625,outline字体的大小是19.53125ot,正好等于 8 * 2.44140625,没有一点精度损失;而truetype字体的大小是20.118148965022pt,明显偏大了。

原因分析

Tcl tk在Windows上使用GDI font engine来渲染显示Truetype字体。GDI Text APIs 的font size必须是int类型,在代码调用过程中,需要把界面指定的phsicial font size(pt) 转换为 logical font size,从而导致了精度损失;作为对比,如果使用我们产品内置的Outline字体就没有类似的问题,因为Outline字体是我们过去为了支持跨平台,自己移植的Freetype字体,对应使用的是Freetype font engine, Freetype
APIs的font size都是float类型的,从phsicial font size 转换为 logical font size时不会产生精度损失。为了支持中文,用户只能使用基于GDI render 的truetype字体,因为Freetype字体库中还没有支持中文的字体,这次看来必须要对Tcl/tk的font engine做个大手术,用DirectWrite float-based APIs 更换老旧的GDI APIs了。

tcl/tk测试代码如下
  1. proc zoominit {c {zfact {1.1}}}{
  2. # save zoom state in a global variable with the same name as the canvas handle
  3. upvar #0 $c data
  4. set data(zdepth)1.0
  5. }
  6. proc zoom {c fact}{
  7. upvar #0 $c data
  8. # zoom at the current mouse position
  9. set x [$c canvasx [expr {[winfo pointerx $c]-[winfo rootx $c]}]]
  10. set y [$c canvasy [expr {[winfo pointery $c]-[winfo rooty $c]}]]
  11. $c scale all $x $y $fact $fact
  12. # save new zoom depth
  13. set data(zdepth)[expr {$data(zdepth)* $fact}]
  14. # zoom text since the "canvas scale all" command doesn't account for text items
  15. zoomtext $c
  16. puts "zoom fact:$data(zdepth)"
  17. }
  18. proc zoomtext {c}{
  19. upvar #0 $c data
  20. # adjust fonts
  21. foreach{i}[$c find all]{
  22. if{![string equal [$c type $i] text]}{continue}
  23. set fontsize 0
  24. # get original fontsize and text from tags if they were previously recorded
  25. foreach{tag}[$c gettags $i]{
  26. scan $tag {_f%f} fontsize
  27. scan $tag "_t%\[^\0\]" text
  28. }
  29. # if not, then record current fontsize and text and use them
  30. set font [$c itemcget $i -font]
  31. if{!$fontsize}{
  32. set text [$c itemcget $i -text]
  33. set fontsize [font actual $font -size]
  34. $c addtag _f$fontsize withtag $i
  35. $c addtag _t$text withtag $i
  36. }
  37. # scale font
  38. set newsize [expr {$fontsize * $data(zdepth)}]
  39. set index [lsearch -exact $font -size]
  40. incr index
  41. set font [lreplace $font $index $index $newsize]
  42. $c itemconfigure $i -font $font -text $text -anchor w
  43. puts "font:$font"
  44. }
  45. # update canvas scrollregion
  46. set bbox [$c bbox all]
  47. if{[llength $bbox]}{
  48. $c configure -scrollregion $bbox
  49. }{
  50. $c configure -scrollregion [list -4-4 \
  51. [expr {[winfo width $c]-4}] \
  52. [expr {[winfo height $c]-4}]]
  53. }
  54. # move srollbar to topright in order to easily observe whether the zoomed text
  55. # goes outside of the rectangle
  56. $c xview moveto [winfo width $c]
  57. $c yview moveto 0
  58. }
  59. # test code
  60. set c [canvas .c -width 600-height 500 \
  61. -xscrollcommand ".shoriz set" \
  62. -yscrollcommand ".svert set"]
  63. # add x/y scrollbar for the canvas
  64. scrollbar .svert -orient v -command "$c yview"
  65. scrollbar .shoriz -orient h -command "$c xview"
  66. grid .c -row 0-column 0-columnspan 3-sticky news
  67. grid .svert -row 0-column 3-columnspan 1-sticky ns
  68. grid .shoriz -row 1-column 0-columnspan 3-sticky ew
  69. grid columnconfigure .0-weight 1
  70. grid columnconfigure .1-weight 1
  71. grid columnconfigure .2-weight 1
  72. grid rowconfigure .0-weight 1
  73. zoominit .c
  74. button .zoomin -text "Zoom In"-command "zoom $c 1.25"
  75. button .zoomout -text "Zoom Out"-command "zoom $c 0.8"
  76. grid .zoomin .zoomout
  77. set text "Hello, World!Here's a simple procedure called zoom that might help to get you started."
  78. # tfont is a truetype font rendering by Windows GDI APIs (GDI font engine)
  79. set tfont [font actual "-family Arial -size 8"]
  80. # ofont is a outline font rendering by freetype font engine transplanted by ourself
  81. set ofont [font actual "-family {OF Helvetica} -size 8"]
  82. .c create text 450-text $text -font $tfont -anchor w -tag tfont
  83. # create a rectangle with the same size of the bbox of the text. zoom in/out the canvas and we
  84. # can observe whether the text is zoomed in the same proportion as the rectangle
  85. .c create rect [.c bbox tfont]
  86. .c create text 4100-text $text -font $ofont -anchor w -tag ofont
  87. .c create rect [.c bbox ofont]
代码参考资源链接: 
Canvas zooming using mousewheel

Tcl/tk缩放Truetype字体时的精度问题的更多相关文章

  1. SpriteBuilder中使用TrueType字体的一些障碍

    在实践中,有一些小的陷阱和障碍可能阻止你使用一般的TrueType字体. 第一个,必须要有一个有效的字体文件.在Finder中双击该.ttf文件,应该会打开Font Book app,显示一个象形符号 ...

  2. [转]TrueType字体结构

    TrueType字体通常包含在单个TrueType字体文件中,其文件后缀为.TTF. OpenType字体是以类似于TrueType字体的格式编码的POSTSCRIPT字体.OPENTYPE字体使用. ...

  3. Tcl Tk Introduction

    Tcl Tk Introduction eryar@163.com 摘要Abstract:Tcl/Tck脚本可以很容易实现用户自定义的命令,方便的创建图形化的用户界面GUI,所以Tcl和Tk的应用领域 ...

  4. freewrap——将tcl/tk脚本转变为可执行文件

     FreeWrap可以把TCL/TK的脚本和二进制文件打包成应用程序,FreeWrap将所有的文件组合成一个单独的可执行文件.     FreeWrap的原理是把脚本和tcl/tk解释器和库文件都打包 ...

  5. 用Tcl/Tk脚本计算圆周率

      读了阮一峰的蒙特卡罗方法入门,用概率统计的方式求解棘手的数学问题还挺有意思的,尤其是利用正方形和它的内切圆之间的面积关系来建模求解圆周率的方法精巧又简单,比投针实验好理解也好实现多了.建模可不是M ...

  6. linux下ruby使用tcl/tk编程环境设置

    正常情况下最新的ruby都是不带tcl/tk选项编译的,所以我们在运行tcl/tk代码时都会发生找不到tk库的错误.解决办法很简单只要以tcl/tk选项编译ruby即可. 这里以ubuntu 15.0 ...

  7. Mac OS X下让ruby支持tcl/tk

    我记得在老早在OS X10.8下使用ruby1.9.x的时候只要到下载安装ActiveTcl8.5,没怎么配置就运行tk好好的.但是近日想重新执行下tk代码,发现在require 'tk'的时候就报错 ...

  8. OpenType字体与TrueType字体的区别

    TrueType采用几何学中二次B样条曲线及直线来描述字体的外形轮廓,其特点是:TrueType既可以作打印字体,又可以用作屏幕显示:由于它是由指令对字形进行描述,因此它与分辨率无关,输出时总是按照打 ...

  9. CVE-2012-0774:Adobe Reader TrueType 字体整数溢出漏洞调试分析

    0x01 TrueType 字体 TTF 字体是 Apple 和 Microsoft 两家公司共同推出的字体格式,现在已经广泛的运用于 Windows 操作系统,其中 PDF 文档也可以嵌入 TTF ...

随机推荐

  1. php 实例说明 socket通信机制

    php 实例说明 socket通信机制 张映 发表于 2010-04-24 分类目录: php 一,socket是什么 什么是socket 所谓socket通常也称作"套接字",用 ...

  2. java_web学习(3)XML基础

    XML 技术概述 XML是一种通用的数据交换格式.为实现计算机之间的文档交换而设计的文档内容编写规范,语法与HTML相似;XML的作用:统一信息的结构,实现不同系统之间的相互通信;目前许多系统的配置文 ...

  3. 勘误《iOS网络高级编程:iPhone和iPad的企业应用开发》

    VII页 , 倒数第二行, “其他” 应为 “其它” X页, 源代码有错误,说是有19章,实际一共13章 XI页,勘误表的链接也是错误的  .是该书的<Beginning ASP.NET 4.5 ...

  4. Linux CentOS 安装 httpd

    1.查看并安装服务器是否安装编译器 make gcc gcc-c++ 查看:rpm -q gcc-c++ 2.查看SELinux 和 iptables 的状态 3.在根目录新建文件夹 lamp 4.使 ...

  5. zoom:1-hasLayout

    在现代浏览器,如果子元素float,则父元素不会自动被撑开 #nofloatbox { border: 1px solid #FF0000; background: #CCC; width:200px ...

  6. Android中SharedPreferences介绍和使用方法

    1.SharedPreferences简介 为了保存软件的设置参数,Android 平台为我们提供了一个SharedPreferences 类,它是一个轻量级的存储类,特别适合用于保存软件配置参数.使 ...

  7. 如何编译POCO

    Poco C++库是: 一系列C++类库,类似Java类库,.Net框架,Apple的Cocoa; 侧重于互联网时代的网络应用程序 使用高效的,现代的标准ANSI/ISO C++,并基于STL 高可移 ...

  8. 负载均衡软件LVS分析二(安装)

    一.  安装LVS软件 1.安装前准备工作操作系统:统一采用Centos4.4版本.地址规划,如表1所示:表1 更详细的信息如图2所示: 图2  LVS DR模式安装部署结构图 图2中的VIP指的是虚 ...

  9. 支付宝 Android 版使用的开源组件

    支付宝 Android 版使用的开源组件 前言: 花了点时间整理了 支付宝 Android 客户端使用的开源组件,给需要的同学.在你不知道用什么开源框架的时候可以作下参考,毕竟支付宝是阿里的重量级产品 ...

  10. jmeter线程组配置

    线程组配置 线程组相当于有多个用户,同时去执行相同的一批次任务.每个线程之间都是隔离的,互不影响的.一个线程的执行过程中,操作的变量,不会影响其他线程的变量值. Delay Thread creati ...