You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在NCL中绘制SVG路径?基于TTF转SVG实现多语言文字绘制

在NCL中绘制SVG路径实现自定义文字渲染

当然可以实现!NCL本身没有直接内置解析SVG路径的函数,但我们可以手动解析SVG的d属性指令,将其转换成NCL支持的绘图命令,完美解决你用SVG路径渲染中文(或任意语言文字)的需求——毕竟把TTF转成SVG路径后,文字就变成了纯粹的矢量图形,完全摆脱了NCL对特定字体的依赖。

下面一步步来说具体怎么做:

核心思路

SVG的d属性是一系列绘图指令(比如M移动、L画线、Q二次贝塞尔等),我们需要把这些指令拆解,转换成NCL的gspolyline(折线)、gsbezier2d(二次贝塞尔)、gsbezier3d(三次贝塞尔)等函数能识别的坐标点,同时处理好坐标系统的差异(SVG是左上角原点,y轴向下;NCL绘图区域通常y轴向上)。

第一步:解析SVG路径指令

先写一个NCL函数来解析d字符串,处理最常用的指令(包括绝对/相对坐标):

function parse_svg_d(d_str, viewport_h, wks, plot)
    begin
        ; 拆分指令和坐标(匹配所有SVG路径指令和空格)
        tokens = strsplit(d_str, "[MLHVQCSZTmlhvqcszt ]+", 1)
        current_x = 0.0
        current_y = 0.0
        paths = new(0, float, "append")  ; 存储路径点序列
        
        do i = 0, dimsizes(tokens)-1
            token = tokens(i)
            if (token.eq."") continue
            
            cmd = strget(token, 0)
            coords_str = strsubstr(token, 1)
            coords = strsplit(coords_str, "[, ]+", 1)
            coords = tofloat(coords)
            
            select case cmd
                ; 移动指令(M/m):绝对/相对定位
                case "M", "m":
                    do j=0, dimsizes(coords)-1, 2
                        x = coords(j)
                        y = coords(j+1)
                        if (cmd.eq."m") then
                            x += current_x
                            y += current_y
                        endif
                        ; 翻转y坐标,适配NCL的y轴方向
                        y = viewport_h - y
                        paths = [paths, x, y]
                        current_x = x
                        current_y = y
                    end do
                ; 直线指令(L/l):绝对/相对画线
                case "L", "l":
                    do j=0, dimsizes(coords)-1, 2
                        x = coords(j)
                        y = coords(j+1)
                        if (cmd.eq."l") then
                            x += current_x
                            y += current_y
                        endif
                        y = viewport_h - y
                        paths = [paths, x, y]
                        current_x = x
                        current_y = y
                    end do
                ; 水平直线(H/h)
                case "H", "h":
                    x = coords(0)
                    if (cmd.eq."h") x += current_x
                    y = viewport_h - current_y
                    paths = [paths, x, y]
                    current_x = x
                ; 垂直直线(V/v)
                case "V", "v":
                    y = coords(0)
                    if (cmd.eq."v") y += current_y
                    y = viewport_h - y
                    paths = [paths, current_x, y]
                    current_y = y
                ; 闭合路径(Z/z)
                case "Z", "z":
                    paths = [paths, paths(0), paths(1)]
                ; 二次贝塞尔曲线(Q/q)
                case "Q", "q":
                    do j=0, dimsizes(coords)-1, 4
                        cx = coords(j)
                        cy = coords(j+1)
                        x = coords(j+2)
                        y = coords(j+3)
                        if (cmd.eq."q") then
                            cx += current_x
                            cy += current_y
                            x += current_x
                            y += current_y
                        endif
                        cy = viewport_h - cy
                        y = viewport_h - y
                        ; 直接绘制贝塞尔曲线
                        bezier_res = True
                        bezier_res@gsLineColor = "black"
                        gsbezier2d(wks, plot, [current_x, current_y, cx, cy, x, y], bezier_res)
                        current_x = x
                        current_y = y
                    end do
            end select
        end do
        
        return(paths)
    end

第二步:在主脚本中调用解析并绘制

下面是完整的示例脚本,用你提供的SVG路径数据来绘制:

begin
    ; 打开绘图工作区
    wks = gsn_open_wks("png", "chinese_svg_text")
    gsn_define_colormap(wks, "default")
    
    ; 设置绘图区域参数(根据你的SVG尺寸调整)
    viewport_w = 1500
    viewport_h = 2000
    plot_res = True
    plot_res@gsnMaximize = False
    plot_res@vpWidthF = 0.8
    plot_res@vpHeightF = 0.8
    plot_res@vpXF = 0.1
    plot_res@vpYF = 0.9
    plot_res@trXMinF = 0
    plot_res@trXMaxF = viewport_w
    plot_res@trYMinF = 0
    plot_res@trYMaxF = viewport_h
    
    ; 创建空白绘图区域
    plot = gsn_csm_blank_plot(wks, plot_res)
    
    ; 你的SVG路径d属性字符串
    svg_d = "M1204 1679h162v-677q273 140 510 307l100 -144q-216 -136 -610 -333v-735q0 -136 150 -136h140q144 0 162 136q18 126 32 328q88 -36 170 -64q-20 -190 -44 -324q-38 -228 -282 -228h-216q-274 0 -274 270v1600zM26 99q261 90 618 236v630h-576v148h57..."
    
    ; 解析路径
    line_points = parse_svg_d(svg_d, viewport_h, wks, plot)
    
    ; 配置路径样式(描边)
    line_res = True
    line_res@gsLineColor = "black"
    line_res@gsLineThicknessF = 1.5
    gspolyline(wks, plot, line_points, line_res)
    
    ; 如果需要填充路径(比如实心文字)
    ; fill_res = True
    ; fill_res@gsFillColor = "darkblue"
    ; gsfill(wks, plot, line_points, fill_res)
    
    ; 输出图像
    frame(wks)
end

关键注意事项

  1. 坐标系统转换:一定要翻转y坐标(y = viewport_h - y),否则绘制的图形会上下颠倒。
  2. 相对坐标处理:SVG小写指令是相对当前点的坐标,解析时要加上当前点的x/y值。
  3. 复杂指令扩展:如果你的SVG路径包含三次贝塞尔曲线(C/c)或圆弧(A/a),可以参考NCL的gsbezier3d函数和圆弧计算公式来扩展解析函数。
  4. 性能优化:如果需要绘制大量文字(比如整段中文),可以把多个字符的SVG路径合并解析,减少重复计算。

这种方法完全可以满足你用SVG路径渲染任意语言文字的需求,因为本质上是直接绘制矢量图形,和字体无关。

内容的提问来源于stack exchange,提问作者Jeff Zhang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:21:32