解决Metabase pulses发送图片中文乱码的问题
1 简介
关于metabase pulses的介绍可以参考上篇文章,对于生成的png图片中文显示乱码的问题,这篇文章把排查问题的过程一步步记录下来。
2 定位问题
首先要找到生成png图片的函数。
在发送图片的时候,可以看到log日志请求
GET /api/pulse/preview_card_info/
在代码中搜索preview_card_info,定位到文件src\metabase\api\pulse.clj,其中有一行
api/defendpoint GET "/preview_card_png/:id"
是把card转换为png文件,主要功能跳到src\metabase\pulse\render.clj文件:
render/render-pulse-card-to-png render-html-to-png ;; 应该是主要目标了,转换html到png文件。
在repl中测试这个函数,发现输出的图片是能正常显示中文的。
3 测试输出的文件
修改render.clj,添加测试代码:
1: ;;添加保存doc到html文件的函数 2: (import 'org.fit.cssbox.css.NormalOutput) 3: (defn write-html 4: "保存doc到文件中" 5: [doc-source file-name] 6: (with-open [out (io/output-stream (io/file file-name))] 7: (-> (NormalOutput. doc-source) 8: (.dumpTo out)))) 9: 10: ;;; 然后修改render-to-png 保存html文件 11: (defn- render-to-png 12: [^String html, ^ByteArrayOutputStream os, width] 13: (let [is (ByteArrayInputStream. (.getBytes html StandardCharsets/UTF_8)) 14: doc-source (StreamDocumentSource. is nil "text/html; charset=utf-8") 15: parser (DefaultDOMSource. doc-source) 16: doc (.parse parser) 17: window-size (Dimension. width 1) 18: media (doto (MediaSpec. "screen") 19: (.setDimensions (.width window-size) (.height window-size)) 20: (.setDeviceDimensions (.width window-size) (.height window-size))) 21: da (doto (DOMAnalyzer. doc (.getURL doc-source)) 22: (.setMediaSpec media) 23: .attributesToStyles 24: (.addStyleSheet nil (CSSNorm/stdStyleSheet) DOMAnalyzer$Origin/AGENT) 25: (.addStyleSheet nil (CSSNorm/userStyleSheet) DOMAnalyzer$Origin/AGENT) 26: (.addStyleSheet nil (CSSNorm/formsStyleSheet) DOMAnalyzer$Origin/AGENT) 27: .getStyleSheets) 28: content-canvas (doto (BrowserCanvas. (.getRoot da) da (.getURL doc-source)) 29: (.setAutoMediaUpdate false) 30: (.setAutoSizeUpdate true))] 31: (write-html doc "last-render.html") ;;; 只需要这里加一行,保存最后一次输出的html 32: (doto (.getConfig content-canvas) 33: (.setClipViewport false) 34: (.setLoadImages true) 35: (.setLoadBackgroundImages true)) 36: (.createLayout content-canvas window-size) 37: (write-image (.getImage content-canvas) "png" os))) 38: 39: ;;; 修改render-html-to-png 保存生成的png文件 40: (s/defn ^:private render-html-to-png :- bytes 41: [{:keys [content]} :- RenderedPulseCard 42: width] 43: (let [html (html [:html [:head 44: ;; 怀疑文件编码设置问题,加了一个meta做测试 45: [:meta {:charset "UTF-8"}]] 46: [:body {:style (style {:margin 0 47: :padding 0 48: :background-color :white})} 49: ;; 内置一句中文进行测试 50: [:p "测试中文"] 51: content]]) 52: os (ByteArrayOutputStream.)] 53: (render-to-png html os width) 54: ;; 保存最后一次生成的png图片到last-pic.png文件中 55: (let [png-bytes (.toByteArray os)] 56: (with-open [w (io/output-stream "last-pic.png")] 57: (.write w png-bytes) 58: png-bytes))))
保存文件后,重新编译项目:
bin/build no-translations
启动metabase后,新建一个pulse,查询结果包含中文,发送目标选择slack,点立即发送进行测试,发现中文还是乱码,不过自己添加的"测试中文"能正常显示,结果图片如下:
图1 添加测试代码后生成的图片截图
打开生成的html文件,发现中文编码是正常的,文件编码也是utf-8。
4 测试其他方法
搜索关于html转png图片的文章,有讲到如果中文是方块状,应该是缺少字体。 查看生成的html文件,发现生成的文字都有指定font-family属性,于是添加几个中文字体到font-family下,测试还是乱码,尝试把中文字体放在最前面,render.clj文件的font-style函数改为:
;; 修改后的font-style函数 (defn- font-style [] {:font-family "微软雅黑,Microsoft YaHei,SimSun,sans-serif,Helvetica, Arial, sans-serif"})
最后图片成功显示中文。
图2 html2image 成功显示中文
5 总结
html输出到图片,中文显示乱码,字体有设置,应该是org.fit.cssbox不会自动适配字体造成的,把中文字体放在第一个,成功解决。
Created: 2019-06-03 一 16:49