42.17.6 SVG 图像

SVG(可缩放矢量图形)是用于描述图像的 XML 格式。 SVG 图像支持以下附加图像描述符属性:

:foreground foreground

foregroundnil,则应为表示颜色的字符串,用作图像前景色。若值为 nil,则默认使用当前文本视觉样式的前景色。

:background background

backgroundnil,则应为表示颜色的字符串,在图像支持透明时用作图像背景色。若值为 nil,则默认使用当前文本视觉样式的背景色。

:css css

cssnil,则应为字符串,指定用于覆盖生成图像时默认样式的 CSS。

SVG 库

若你编译的 Emacs 带有 SVG 支持,可使用 svg.el 库中的以下函数创建与操作这类图像。

Function: svg-create width height &rest args

创建指定尺寸的新空白 SVG 图像。 args 为属性参数列表,可指定以下内容:

:stroke-width

所创建所有线条的默认宽度(单位:像素)。

:stroke

所创建所有线条的默认描边颜色。

该函数返回一个 SVG 对象,即用于描述 SVG 图像的 Lisp 数据结构,后续所有函数均操作该结构。以下函数中的参数 svg 均指代此类 SVG 对象。

Function: svg-gradient svg id type stops

svg 中创建标识符为 id 的渐变。type 指定渐变类型,可为 linear(线性)或 radial(径向)。stops 为百分比/颜色对构成的列表。

以下示例创建一个线性渐变:起点为红色,25% 处为绿色,终点为蓝色:

(svg-gradient svg "gradient1" 'linear
              '((0 . "red") (25 . "green") (100 . "blue")))

创建并插入到 SVG 对象中的渐变,可被后续所有创建图形的函数使用。

以下所有函数均接受可选关键字参数列表,用于修改各类属性的默认值。可用属性包括:

:stroke-width

所绘制线条与实心图形轮廓的宽度(单位:像素)。

:stroke-color

所绘制线条与实心图形轮廓的颜色。

:fill-color

实心图形的填充颜色。

:id

图形的标识符。

:gradient

若指定,应为预先定义的渐变对象标识符。

:clip-path

裁剪路径的标识符。

Function: svg-rectangle svg x y width height &rest args

svg 添加矩形,其左上角坐标为 x/y,尺寸为 width/height

(svg-rectangle svg 100 100 500 500 :gradient "gradient1")
Function: svg-circle svg x y radius &rest args

svg 添加圆形,其圆心坐标为 x/y,半径为 radius

Function: svg-ellipse svg x y x-radius y-radius &rest args

svg 添加椭圆,其圆心坐标为 x/y,水平半径为 x-radius,垂直半径为 y-radius

Function: svg-line svg x1 y1 x2 y2 &rest args

svg 添加直线,起点为 x1/y1,终点为 x2/y2

Function: svg-polyline svg points &rest args

svg 添加多段线(又称 “折线(polyline)”),路径经过 points,即 X/Y 坐标对构成的列表。

(svg-polyline svg '((200 . 100) (500 . 450) (80 . 100))
              :stroke-color "green")
Function: svg-polygon svg points &rest args

svg 添加多边形,其中 points 为描述多边形外轮廓的 X/Y 坐标对列表。

(svg-polygon svg '((100 . 100) (200 . 150) (150 . 90))
             :stroke-color "blue" :fill-color "red")
Function: svg-path svg commands &rest args

根据 commandssvg 添加图形轮廓,参见 SVG Path Commands

坐标默认为绝对坐标。若要使用相对于上一点(初始时为原点)的坐标,可将属性 :relative 设为 t。该属性可作用于整个函数或单个命令。若作用于函数,则所有命令默认使用相对坐标。若要让单个命令使用绝对坐标,可将其 :relative 设为 nil

Add the outline of a shape to svg according to commands, see SVG Path Commands.

(svg-path svg
	  '((moveto ((100 . 100)))
	    (lineto ((200 . 0) (0 . 200) (-200 . 0)))
	    (lineto ((100 . 100)) :relative nil))
	  :stroke-color "blue"
	  :fill-color "lightblue"
	  :relative t)
Function: svg-text svg text &rest args

svg 添加指定文本 text

(svg-text
 svg "This is a text"
 :font-size "40"
 :font-weight "bold"
 :stroke "black"
 :fill "white"
 :font-family "impact"
 :letter-spacing "4pt"
 :x 300
 :y 400
 :stroke-width 1)
Function: svg-embed svg image image-type datap &rest args

svg 添加嵌入的(光栅)图像。若 datapnil,则 image 应为文件名;否则应为包含原始字节图像数据的字符串。image-type 应为 MIME 图像类型,例如 "image/jpeg"

(svg-embed svg "~/rms.jpg" "image/jpeg" nil
           :width "100px" :height "100px"
           :x "50px" :y "75px")
Function: svg-embed-base-uri-image svg relative-filename &rest args

svg 添加位于 relative-filename 的嵌入(光栅)图像。relative-filename 会在 SVG 图像属性 :base-uri 所在的 file-name-directory 中查找。 :base-uri 指定待创建 SVG 图像的文件名(可不存在),因此所有嵌入文件均相对于该文件名所在目录查找。若省略 :base-uri,则使用加载 SVG 图像的源文件名。与 svg-embed 相比,使用 :base-uri 可提升嵌入大图像的性能,因为所有工作均由 librsvg 直接完成。

;; 嵌入 /tmp/subdir/rms.jpg 与 /tmp/another/rms.jpg
(svg-embed-base-uri-image svg "subdir/rms.jpg"
           :width "100px" :height "100px"
           :x "50px" :y "75px")
(svg-embed-base-uri-image svg "another/rms.jpg"
           :width "100px" :height "100px"
           :x "75px" :y "50px")
(svg-image svg :scale 1.0
           :base-uri "/tmp/dummy"
           :width 175 :height 175)
Function: svg-clip-path svg &rest args

svg 添加裁剪路径。若通过 :clip-path 属性应用到某个图形,该图形位于裁剪路径外的部分将不会被绘制。

(let ((clip-path (svg-clip-path svg :id "foo")))
  (svg-circle clip-path 200 200 175))
(svg-rectangle svg 50 50 300 300
               :fill-color "red"
               :clip-path "url(#foo)")
Function: svg-node svg tag &rest args

svg 添加自定义节点 tag

(svg-node svg
          'rect
          :width 300 :height 200 :x 50 :y 100 :fill-color "green")
Function: svg-remove svg id

svg 中移除标识符为 id 的元素。

Function: svg-image svg

最后,svg-image 接受 SVG 对象作为参数,并返回可用于 insert-image 等函数的图像对象。

以下是完整示例,创建并插入一个带圆形的图像:

(let ((svg (svg-create 400 400 :stroke-width 10)))
  (svg-gradient svg "gradient1" 'linear '((0 . "red") (100 . "blue")))
  (svg-circle svg 200 200 100 :gradient "gradient1"
                  :stroke-color "green")
  (insert-image (svg-image svg)))

SVG 路径命令

SVG 路径可通过组合直线、曲线、圆弧等基础图形创建复杂图像。下文所述函数支持在 Lisp 程序中调用 SVG 路径命令。

Command: moveto points

将画笔移动到 points 中的第一个点。后续点由直线连接。points 为 X/Y 坐标对列表。后续的 moveto 命令代表新 子路径(subpath) 的起点。

(svg-path svg '((moveto ((200 . 100) (100 . 200) (0 . 100))))
          :fill "white" :stroke "black")
Command: closepath

将当前子路径连接回起点以结束该路径,并绘制连接线。

(svg-path svg '((moveto ((200 . 100) (100 . 200) (0 . 100)))
                (closepath)
                (moveto ((75 . 125) (100 . 150) (125 . 125)))
                (closepath))
          :fill "red" :stroke "black")
Command: lineto points

从当前点向 points 中的第一个点绘制直线,points 为 X/Y 坐标对列表。若指定多个点,则绘制折线。

(svg-path svg '((moveto ((200 . 100)))
                (lineto ((100 . 200) (0 . 100))))
          :fill "yellow" :stroke "red")
Command: horizontal-lineto x-coordinates

从当前点向 x-coordinates 中的第一个点绘制水平线。可指定多个坐标,但通常无实际意义。

(svg-path svg '((moveto ((100 . 200)))
                (horizontal-lineto (300)))
          :stroke "green")
Command: vertical-lineto y-coordinates

绘制竖直线。

(svg-path svg '((moveto ((200 . 100)))
                (vertical-lineto (300)))
          :stroke "green")
Command: curveto coordinate-sets

使用 coordinate-sets 中的第一个元素,从当前点绘制三次贝塞尔曲线。若存在多组坐标,则绘制多段贝塞尔曲线。每组坐标格式为 (x1 y1 x2 y2 x y),其中 (xy) 为曲线终点,(x1y1) 与 (x2y2) 分别为起点与终点的控制点。

(svg-path svg '((moveto ((100 . 100)))
                (curveto ((200 100 100 200 200 200)
                          (300 200 0 100 100 100))))
          :fill "transparent" :stroke "red")
Command: smooth-curveto coordinate-sets

使用 coordinate-sets 中的第一个元素,从当前点绘制三次贝塞尔曲线。若存在多组坐标,则绘制多段贝塞尔曲线。每组坐标格式为 (x2 y2 x y),其中 (xy) 为曲线终点,(x2y2) 为对应控制点。若上一条命令为 curvetosmooth-curveto,则第一个控制点为上一条命令第二个控制点相对于当前点的对称点;否则第一个控制点与当前点重合。

(svg-path svg '((moveto ((100 . 100)))
                (curveto ((200 100 100 200 200 200)))
                (smooth-curveto ((0 100 100 100))))
          :fill "transparent" :stroke "blue")
Command: quadratic-bezier-curveto coordinate-sets

使用 coordinate-sets 中的第一个元素,从当前点绘制二次贝塞尔曲线。若存在多组坐标,则绘制多段贝塞尔曲线。每组坐标格式为 (x1 y1 x y),其中 (xy) 为曲线终点,(x1y1) 为控制点。

(svg-path svg '((moveto ((200 . 100)))
                (quadratic-bezier-curveto ((300 100 300 200)))
                (quadratic-bezier-curveto ((300 300 200 300)))
                (quadratic-bezier-curveto ((100 300 100 200)))
                (quadratic-bezier-curveto ((100 100 200 100))))
          :fill "transparent" :stroke "pink")
Command: smooth-quadratic-bezier-curveto coordinate-sets

使用 coordinate-sets 中的第一个元素,从当前点绘制二次贝塞尔曲线。若存在多组坐标,则绘制多段贝塞尔曲线。每组坐标格式为 (x y),其中 (xy) 为曲线终点。若上一条命令为 quadratic-bezier-curvetosmooth-quadratic-bezier-curveto,则控制点为上一条命令控制点相对于当前点的对称点;否则控制点与当前点重合。

(svg-path svg '((moveto ((200 . 100)))
                (quadratic-bezier-curveto ((300 100 300 200)))
                (smooth-quadratic-bezier-curveto ((200 300)))
                (smooth-quadratic-bezier-curveto ((100 200)))
                (smooth-quadratic-bezier-curveto ((200 100))))
          :fill "transparent" :stroke "lightblue")
Command: elliptical-arc coordinate-sets

使用 coordinate-sets 中的第一个元素,从当前点绘制椭圆弧。若存在多组坐标,则绘制连续椭圆弧。每组坐标格式为 (rx ry x y),其中 (xy) 为椭圆终点,(rxry) 为椭圆半径。可向列表附加以下属性:

:x-axis-rotation

椭圆 X 轴相对于当前坐标系 X 轴的旋转角度(单位:度)。

:large-arc

若设为 t,则绘制大于或等于 180 度的弧;否则绘制小于或等于 180 度的弧。

:sweep

若设为 t,则沿 正角度方向(positive angle direction) 绘制弧;否则沿 负角度方向(negative angle direction) 绘制。

(svg-path svg '((moveto ((200 . 250)))
                (elliptical-arc ((75 75 200 350))))
          :fill "transparent" :stroke "red")
(svg-path svg '((moveto ((200 . 250)))
                (elliptical-arc ((75 75 200 350 :large-arc t))))
          :fill "transparent" :stroke "green")
(svg-path svg '((moveto ((200 . 250)))
                (elliptical-arc ((75 75 200 350 :sweep t))))
          :fill "transparent" :stroke "blue")
(svg-path svg '((moveto ((200 . 250)))
                (elliptical-arc ((75 75 200 350 :large-arc t
                                     :sweep t))))
          :fill "transparent" :stroke "gray")
(svg-path svg '((moveto ((160 . 100)))
                (elliptical-arc ((40 100 80 0)))
                (elliptical-arc ((40 100 -40 -70
                                     :x-axis-rotation -120)))
                (elliptical-arc ((40 100 -40 70
                                     :x-axis-rotation -240))))
          :stroke "pink" :fill "lightblue"
          :relative t)