zoukankan      html  css  js  c++  java
  • 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测试代码如下
    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%[^]" 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
  • 相关阅读:
    html例题——简历
    求值
    c#语句实例(排大小)
    3.6语言基础笔记
    2016.3.5进制间的转换
    3.26-1
    3.23(网页)
    3.23
    3.22
    3.20
  • 原文地址:https://www.cnblogs.com/egoechog/p/6391073.html
Copyright © 2011-2022 走看看