Tcl/tk缩放Truetype字体时的精度问题
最近有国内新客户抱怨我们产品显示的原理图太不专业了,在原理图上使用宋体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测试代码如下
proc zoominit {c {zfact {1.1}}}{# save zoom state in a global variable with the same name as the canvas handleupvar #0 $c dataset data(zdepth)1.0}proc zoom {c fact}{upvar #0 $c data# zoom at the current mouse positionset x [$c canvasx [expr {[winfo pointerx $c]-[winfo rootx $c]}]]set y [$c canvasy [expr {[winfo pointery $c]-[winfo rooty $c]}]]$c scale all $x $y $fact $fact# save new zoom depthset data(zdepth)[expr {$data(zdepth)* $fact}]# zoom text since the "canvas scale all" command doesn't account for text itemszoomtext $cputs "zoom fact:$data(zdepth)"}proc zoomtext {c}{upvar #0 $c data# adjust fontsforeach{i}[$c find all]{if{![string equal [$c type $i] text]}{continue}set fontsize 0# get original fontsize and text from tags if they were previously recordedforeach{tag}[$c gettags $i]{scan $tag {_f%f} fontsizescan $tag "_t%\[^\0\]" text}# if not, then record current fontsize and text and use themset font [$c itemcget $i -font]if{!$fontsize}{set text [$c itemcget $i -text]set fontsize [font actual $font -size]$c addtag _f$fontsize withtag $i$c addtag _t$text withtag $i}# scale fontset newsize [expr {$fontsize * $data(zdepth)}]set index [lsearch -exact $font -size]incr indexset font [lreplace $font $index $index $newsize]$c itemconfigure $i -font $font -text $text -anchor wputs "font:$font"}# update canvas scrollregionset bbox [$c bbox all]if{[llength $bbox]}{$c configure -scrollregion $bbox}{$c configure -scrollregion [list -4-4 \[expr {[winfo width $c]-4}] \[expr {[winfo height $c]-4}]]}# move srollbar to topright in order to easily observe whether the zoomed text# goes outside of the rectangle$c xview moveto [winfo width $c]$c yview moveto 0}# test codeset c [canvas .c -width 600-height 500 \-xscrollcommand ".shoriz set" \-yscrollcommand ".svert set"]# add x/y scrollbar for the canvasscrollbar .svert -orient v -command "$c yview"scrollbar .shoriz -orient h -command "$c xview"grid .c -row 0-column 0-columnspan 3-sticky newsgrid .svert -row 0-column 3-columnspan 1-sticky nsgrid .shoriz -row 1-column 0-columnspan 3-sticky ewgrid columnconfigure .0-weight 1grid columnconfigure .1-weight 1grid columnconfigure .2-weight 1grid rowconfigure .0-weight 1zoominit .cbutton .zoomin -text "Zoom In"-command "zoom $c 1.25"button .zoomout -text "Zoom Out"-command "zoom $c 0.8"grid .zoomin .zoomoutset text "Hello, World!Here's a simple procedure called zoom that might help to get you started."# tfont is a truetype font rendering by Windows GDI APIs (GDI font engine)set tfont [font actual "-family Arial -size 8"]# ofont is a outline font rendering by freetype font engine transplanted by ourselfset ofont [font actual "-family {OF Helvetica} -size 8"].c create text 450-text $text -font $tfont -anchor w -tag tfont# create a rectangle with the same size of the bbox of the text. zoom in/out the canvas and we# can observe whether the text is zoomed in the same proportion as the rectangle.c create rect [.c bbox tfont].c create text 4100-text $text -font $ofont -anchor w -tag ofont.c create rect [.c bbox ofont]
代码参考资源链接:
Canvas zooming using mousewheel
Tcl/tk缩放Truetype字体时的精度问题的更多相关文章
- SpriteBuilder中使用TrueType字体的一些障碍
在实践中,有一些小的陷阱和障碍可能阻止你使用一般的TrueType字体. 第一个,必须要有一个有效的字体文件.在Finder中双击该.ttf文件,应该会打开Font Book app,显示一个象形符号 ...
- [转]TrueType字体结构
TrueType字体通常包含在单个TrueType字体文件中,其文件后缀为.TTF. OpenType字体是以类似于TrueType字体的格式编码的POSTSCRIPT字体.OPENTYPE字体使用. ...
- Tcl Tk Introduction
Tcl Tk Introduction eryar@163.com 摘要Abstract:Tcl/Tck脚本可以很容易实现用户自定义的命令,方便的创建图形化的用户界面GUI,所以Tcl和Tk的应用领域 ...
- freewrap——将tcl/tk脚本转变为可执行文件
FreeWrap可以把TCL/TK的脚本和二进制文件打包成应用程序,FreeWrap将所有的文件组合成一个单独的可执行文件. FreeWrap的原理是把脚本和tcl/tk解释器和库文件都打包 ...
- 用Tcl/Tk脚本计算圆周率
读了阮一峰的蒙特卡罗方法入门,用概率统计的方式求解棘手的数学问题还挺有意思的,尤其是利用正方形和它的内切圆之间的面积关系来建模求解圆周率的方法精巧又简单,比投针实验好理解也好实现多了.建模可不是M ...
- linux下ruby使用tcl/tk编程环境设置
正常情况下最新的ruby都是不带tcl/tk选项编译的,所以我们在运行tcl/tk代码时都会发生找不到tk库的错误.解决办法很简单只要以tcl/tk选项编译ruby即可. 这里以ubuntu 15.0 ...
- Mac OS X下让ruby支持tcl/tk
我记得在老早在OS X10.8下使用ruby1.9.x的时候只要到下载安装ActiveTcl8.5,没怎么配置就运行tk好好的.但是近日想重新执行下tk代码,发现在require 'tk'的时候就报错 ...
- OpenType字体与TrueType字体的区别
TrueType采用几何学中二次B样条曲线及直线来描述字体的外形轮廓,其特点是:TrueType既可以作打印字体,又可以用作屏幕显示:由于它是由指令对字形进行描述,因此它与分辨率无关,输出时总是按照打 ...
- CVE-2012-0774:Adobe Reader TrueType 字体整数溢出漏洞调试分析
0x01 TrueType 字体 TTF 字体是 Apple 和 Microsoft 两家公司共同推出的字体格式,现在已经广泛的运用于 Windows 操作系统,其中 PDF 文档也可以嵌入 TTF ...
随机推荐
- ASP.NET异步处理
前一篇:详解 .NET 异步 在前文中,介绍了.NET下的多种异步的形式,在WEB程序中,天生就是多线程的,因此使用异步应该更为谨慎.本文将着重展开ASP.NET中的异步. [注意]本文中提到的异步指 ...
- 数据可视化-OmniGraffle软件
OmniGraffle Pro for mac破解版是一款运行在MAC OS平台上的思维导图流程图制作软件,通过思维导图软件(OmniGraffle Pro MAC)帮你组织头脑中思考的信息,组织头脑 ...
- javascript中的__proto__和prototype
一.2个参考网址: http://icekiller110.iteye.com/blog/1566768 http://www.cnblogs.com/snandy/archive/2012/09/0 ...
- [Angular Tutorial] 8 - Templating Links & Images
在这一步中,我们将会在电话列表中为电话添加略图,并附上链接,当然现在也不会链接去哪.在随后的步骤中,我们将使用这些链接来展示电话列表中额外的信息. ·现在电话列表中会有链接和图片. 最重要的不同在下面 ...
- Java中实现Serializable接口为什么要声明serialVersionUID?
什么情况下需要修改serialVersionUID 的值? 序列化运行时使用一个称为 serialVersionUID 的版本号与每个可序列化类相关联,该序列号在反序列化过程中用于验证序列化 ...
- 升级R版本后,更新Package
升级R版本后,若重新安装所有的package将非常麻烦,可以尝试运行一下程序: 1)在旧版本中的R中运行 #--run in the old version of R setwd("C:/T ...
- poi jar包介绍
来自官网: Component Application type Maven artifactId Notes POIFS OLE2 Filesystem poi Required to work w ...
- 使用DatePickerDialog、TimePickerDialog
DatePickerDialog与TimerPicker的功能比较简单,用户也简单,只要如下两步即可. ①通过new关键字创建DatePickerDialog.TimePickerDialog实例,调 ...
- Clojure发音
Clojure的发音和单词closure是一样的.Clojure之父是这样解释Clojure名字来历的 “我想把这就几个元素包含在里面: C (C#), L (Lisp) and J (Java). ...
- 必应地图api文档,微软必应地图web开发版详解,可以在国内使用国外地图
最近,公司项目要求在页面中嵌入地图,需求还算简单,但是由于必须具备响应式(主要是pc和移动端),而且由于公司业务是全球性的,要支持国外地点搜索.考虑到百度,腾讯,高德等等国内地图无法显示国外数据,谷歌 ...