Tcl/tk缩放Truetype字体时的精度问题
2017-02-11 19:21
561 查看
最近有国内新客户抱怨我们产品显示的原理图太不专业了,在原理图上使用宋体GB2312设计好中文图表,经过几次缩放时,表格内的文字居然会跑到表格外边,更要命的是打印出来的文档也存在同样的问题。我研究了一下,原来又是WindowsGDITextAPIs的一个大坑!
问题详细描述
用Tcl/tkscript可以很容易地重现这个问题。如下图,第一行文字使用的Arial字体renderingbyGDIAPIs,第二行文字使用的是OFHelvetica字体renderingbyFreetypeAPIs,两行文字都用和字体无关的方式加了边框。
刚运行脚本时,两行文字都是8pt,虽然显示效果不一样,但看着是一样大。
放大几次后,第一行的GDI文字明显跑到框外了。
再看一下这时的log:
font:-familyArial -size20.118148965022 -weightnormal-slantroman-underline0-overstrike0-typetruetype
font:-family{OFHelvetica} -size19.53125 -weightnormal-slantroman-underline0-overstrike0-typeoutline
zoomfact:2.44140625
缩放比例2.44140625,outline字体的大小是19.53125ot,正好等于8*2.44140625,没有一点精度损失;而truetype字体的大小是20.118148965022pt,明显偏大了。
原因分析
Tcltk在Windows上使用GDIfontengine来渲染显示Truetype字体。GDITextAPIs的fontsize必须是int类型,在代码调用过程中,需要把界面指定的phsicialfontsize(pt)转换为logicalfontsize,从而导致了精度损失;作为对比,如果使用我们产品内置的Outline字体就没有类似的问题,因为Outline字体是我们过去为了支持跨平台,自己移植的Freetype字体,对应使用的是Freetypefontengine,Freetype
APIs的fontsize都是float类型的,从phsicialfontsize转换为logicalfontsize时不会产生精度损失。为了支持中文,用户只能使用基于GDIrender的truetype字体,因为Freetype字体库中还没有支持中文的字体,这次看来必须要对Tcl/tk的fontengine做个大手术,用DirectWritefloat-basedAPIs更换老旧的GDIAPIs了。
tcl/tk测试代码如下
问题详细描述
用Tcl/tkscript可以很容易地重现这个问题。如下图,第一行文字使用的Arial字体renderingbyGDIAPIs,第二行文字使用的是OFHelvetica字体renderingbyFreetypeAPIs,两行文字都用和字体无关的方式加了边框。
刚运行脚本时,两行文字都是8pt,虽然显示效果不一样,但看着是一样大。
放大几次后,第一行的GDI文字明显跑到框外了。
再看一下这时的log:
font:-familyArial -size20.118148965022 -weightnormal-slantroman-underline0-overstrike0-typetruetype
font:-family{OFHelvetica} -size19.53125 -weightnormal-slantroman-underline0-overstrike0-typeoutline
zoomfact:2.44140625
缩放比例2.44140625,outline字体的大小是19.53125ot,正好等于8*2.44140625,没有一点精度损失;而truetype字体的大小是20.118148965022pt,明显偏大了。
原因分析
Tcltk在Windows上使用GDIfontengine来渲染显示Truetype字体。GDITextAPIs的fontsize必须是int类型,在代码调用过程中,需要把界面指定的phsicialfontsize(pt)转换为logicalfontsize,从而导致了精度损失;作为对比,如果使用我们产品内置的Outline字体就没有类似的问题,因为Outline字体是我们过去为了支持跨平台,自己移植的Freetype字体,对应使用的是Freetypefontengine,Freetype
APIs的fontsize都是float类型的,从phsicialfontsize转换为logicalfontsize时不会产生精度损失。为了支持中文,用户只能使用基于GDIrender的truetype字体,因为Freetype字体库中还没有支持中文的字体,这次看来必须要对Tcl/tk的fontengine做个大手术,用DirectWritefloat-basedAPIs更换老旧的GDIAPIs了。
tcl/tk测试代码如下
proczoominit{c{zfact{1.1}}}{
#savezoomstateinaglobalvariablewiththesamenameasthecanvashandle
upvar#0$cdata
setdata(zdepth)1.0
}
proczoom{cfact}{
upvar#0$cdata
#zoomatthecurrentmouseposition
setx[$ccanvasx[expr{[winfopointerx$c]-[winforootx$c]}]]
sety[$ccanvasy[expr{[winfopointery$c]-[winforooty$c]}]]
$cscaleall$x$y$fact$fact
#savenewzoomdepth
setdata(zdepth)[expr{$data(zdepth)*$fact}]
#zoomtextsincethe"canvasscaleall"commanddoesn'taccountfortextitems
zoomtext$c
puts"zoomfact:$data(zdepth)"
}
proczoomtext{c}{
upvar#0$cdata
#adjustfonts
foreach{i}[$cfindall]{
if{![stringequal[$ctype$i]text]}{continue}
setfontsize0
#getoriginalfontsizeandtextfromtagsiftheywerepreviouslyrecorded
foreach{tag}[$cgettags$i]{
scan$tag{_f%f}fontsize
scan$tag"_t%\[^\0\]"text
}
#ifnot,thenrecordcurrentfontsizeandtextandusethem
setfont[$citemcget$i-font]
if{!$fontsize}{
settext[$citemcget$i-text]
setfontsize[fontactual$font-size]
$caddtag_f$fontsizewithtag$i
$caddtag_t$textwithtag$i
}
#scalefont
setnewsize[expr{$fontsize*$data(zdepth)}]
setindex[lsearch-exact$font-size]
incrindex
setfont[lreplace$font$index$index$newsize]
$citemconfigure$i-font$font-text$text-anchorw
puts"font:$font"
}
#updatecanvasscrollregion
setbbox[$cbboxall]
if{[llength$bbox]}{
$cconfigure-scrollregion$bbox
}{
$cconfigure-scrollregion[list-4-4\
[expr{[winfowidth$c]-4}]\
[expr{[winfoheight$c]-4}]]
}
#movesrollbartotoprightinordertoeasilyobservewhetherthezoomedtext
#goesoutsideoftherectangle
$cxviewmoveto[winfowidth$c]
$cyviewmoveto0
}
#testcode
setc[canvas.c-width600-height500\
-xscrollcommand".shorizset"\
-yscrollcommand".svertset"]
#addx/yscrollbarforthecanvas
scrollbar.svert-orientv-command"$cyview"
scrollbar.shoriz-orienth-command"$cxview"
grid.c-row0-column0-columnspan3-stickynews
grid.svert-row0-column3-columnspan1-stickyns
grid.shoriz-row1-column0-columnspan3-stickyew
gridcolumnconfigure.0-weight1
gridcolumnconfigure.1-weight1
gridcolumnconfigure.2-weight1
gridrowconfigure.0-weight1
zoominit.c
button.zoomin-text"ZoomIn"-command"zoom$c1.25"
button.zoomout-text"ZoomOut"-command"zoom$c0.8"
grid.zoomin.zoomout
settext"Hello,World!Here'sasimpleprocedurecalledzoomthatmighthelptogetyoustarted."
#tfontisatruetypefontrenderingbyWindowsGDIAPIs(GDIfontengine)
settfont[fontactual"-familyArial-size8"]
#ofontisaoutlinefontrenderingbyfreetypefontenginetransplantedbyourself
setofont[fontactual"-family{OFHelvetica}-size8"]
.ccreatetext450-text$text-font$tfont-anchorw-tagtfont
#createarectanglewiththesamesizeofthebboxofthetext.zoomin/outthecanvasandwe
#canobservewhetherthetextiszoomedinthesameproportionastherectangle
.ccreaterect[.cbboxtfont]
.ccreatetext4100-text$text-font$ofont-anchorw-tagofont
.ccreaterect[.cbboxofont]
代码参考资源链接:Canvaszoomingusingmousewheel
相关文章推荐
- tcl/tk 访问oracle 时,使用oratcl 乱码问题
- ubuntu安装ns2时tcl/tk的问题
- The History of Tcl/Tk
- 如何去掉鼠标滚轮缩放图片和解决签名图片变形问题。
- 再谈JavaScript时钟中的16ms精度问题.
- 关于double精度问题
- 关于TCL中的编码问题
- DateTime之时间比较(CompareTo)精度问题
- 再谈JavaScript时钟中的16ms精度问题.
- matlab中的显示精度问题
- TCL中用exec启动的进程占用SOCKET端口问题分析
- java浮点运算精度问题的解决
- Tcl/Tk的一些笔记
- SUM()求和时的精度问题,跟数据表字段定义有关
- JS Float精度问题 toPrecision
- Tcl/tk是比较烦人的语言,没有什么好多编译工具啊
- double值格式化输出的精度问题解决
- Tcl/tk以及SQLite在BCM7038平台上面的编译及运行备忘
- 再谈JavaScript时钟中的16ms精度问题.
- 彻底解决SVG图形的缩放问题