diff options
| author | Felix E. Klee | 2019-07-15 17:07:56 +0200 |
|---|---|---|
| committer | Lars Ingebrigtsen | 2019-07-15 17:07:56 +0200 |
| commit | d6bc55ae2dc98c83e58a28e380ce4bcf2ed00bb3 (patch) | |
| tree | 47e633d687d0bda97e8613c57bed4ab816e55d44 /lisp | |
| parent | 3b6992118501d0a17b6817a91011f8e8dcdf8164 (diff) | |
| download | emacs-d6bc55ae2dc98c83e58a28e380ce4bcf2ed00bb3.tar.gz emacs-d6bc55ae2dc98c83e58a28e380ce4bcf2ed00bb3.zip | |
Add support for paths to svg.el
* doc/lispref/display.texi (SVG Images): Document svg-path,
svg-clip-path and svg-node (bug#32359).
* doc/lispref/display.texi (SVG Path Commands): New node.
* lisp/svg.el (svg--plist-delete, svg--path-command-symbol)
(svg--elliptical-arc-coordinates, svg--elliptical-arc-command)
(svg--moveto-command, svg--closepath-command)
(svg--lineto-command, svg--horizontal-lineto-command)
(svg--vertical-lineto-command, svg--curveto-command)
(svg--smooth-curveto-command)
(svg--quadratic-bezier-curveto-command)
(svg--smooth-quadratic-bezier-curveto-command)
(svg--eval-path-command, svg-path, svg-clip-path, svg-node): New
functions.
Diffstat (limited to 'lisp')
| -rw-r--r-- | lisp/svg.el | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/lisp/svg.el b/lisp/svg.el index 86b56a03d56..2ab56d3960d 100644 --- a/lisp/svg.el +++ b/lisp/svg.el | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | ;; Copyright (C) 2014-2019 Free Software Foundation, Inc. | 3 | ;; Copyright (C) 2014-2019 Free Software Foundation, Inc. |
| 4 | 4 | ||
| 5 | ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org> | 5 | ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org> |
| 6 | ;; Felix E. Klee <felix.klee@inka.de> | ||
| 6 | ;; Keywords: image | 7 | ;; Keywords: image |
| 7 | ;; Version: 1.0 | 8 | ;; Version: 1.0 |
| 8 | ;; Package-Requires: ((emacs "25")) | 9 | ;; Package-Requires: ((emacs "25")) |
| @@ -324,6 +325,153 @@ If the SVG is later changed, the image will also be updated." | |||
| 324 | "\\'"))))) | 325 | "\\'"))))) |
| 325 | (when node (dom-remove-node svg node)))) | 326 | (when node (dom-remove-node svg node)))) |
| 326 | 327 | ||
| 328 | ;; Function body copied from `org-plist-delete' in Emacs 26.1. | ||
| 329 | (defun svg--plist-delete (plist property) | ||
| 330 | "Delete PROPERTY from PLIST. | ||
| 331 | This is in contrast to merely setting it to 0." | ||
| 332 | (let (p) | ||
| 333 | (while plist | ||
| 334 | (if (not (eq property (car plist))) | ||
| 335 | (setq p (plist-put p (car plist) (nth 1 plist)))) | ||
| 336 | (setq plist (cddr plist))) | ||
| 337 | p)) | ||
| 338 | |||
| 339 | (defun svg--path-command-symbol (command-symbol command-args) | ||
| 340 | (let ((char (symbol-name command-symbol)) | ||
| 341 | (relative (if (plist-member command-args :relative) | ||
| 342 | (plist-get command-args :relative) | ||
| 343 | (plist-get command-args :default-relative)))) | ||
| 344 | (intern (if relative (downcase char) (upcase char))))) | ||
| 345 | |||
| 346 | (defun svg--elliptical-arc-coordinates | ||
| 347 | (rx ry x y &rest args) | ||
| 348 | (list | ||
| 349 | rx ry | ||
| 350 | (or (plist-get args :x-axis-rotation) 0) | ||
| 351 | (if (plist-get args :large-arc) 1 0) | ||
| 352 | (if (plist-get args :sweep) 1 0) | ||
| 353 | x y)) | ||
| 354 | |||
| 355 | (defun svg--elliptical-arc-command (coordinates-list &rest args) | ||
| 356 | (cons | ||
| 357 | (svg--path-command-symbol 'a args) | ||
| 358 | (apply 'append | ||
| 359 | (mapcar | ||
| 360 | (lambda (coordinates) | ||
| 361 | (apply 'svg--elliptical-arc-coordinates | ||
| 362 | coordinates)) | ||
| 363 | coordinates-list)))) | ||
| 364 | |||
| 365 | (defun svg--moveto-command (coordinates-list &rest args) | ||
| 366 | (cons | ||
| 367 | (svg--path-command-symbol 'm args) | ||
| 368 | (apply 'append | ||
| 369 | (mapcar | ||
| 370 | (lambda (coordinates) | ||
| 371 | (list (car coordinates) (cdr coordinates))) | ||
| 372 | coordinates-list)))) | ||
| 373 | |||
| 374 | (defun svg--closepath-command (&rest args) | ||
| 375 | (list (svg--path-command-symbol 'z args))) | ||
| 376 | |||
| 377 | (defun svg--lineto-command (coordinates-list &rest args) | ||
| 378 | (cons | ||
| 379 | (svg--path-command-symbol 'l args) | ||
| 380 | (apply 'append | ||
| 381 | (mapcar | ||
| 382 | (lambda (coordinates) | ||
| 383 | (list (car coordinates) (cdr coordinates))) | ||
| 384 | coordinates-list)))) | ||
| 385 | |||
| 386 | (defun svg--horizontal-lineto-command (coordinate-list &rest args) | ||
| 387 | (cons | ||
| 388 | (svg--path-command-symbol 'h args) | ||
| 389 | coordinate-list)) | ||
| 390 | |||
| 391 | (defun svg--vertical-lineto-command (coordinate-list &rest args) | ||
| 392 | (cons | ||
| 393 | (svg--path-command-symbol 'v args) | ||
| 394 | coordinate-list)) | ||
| 395 | |||
| 396 | (defun svg--curveto-command (coordinates-list &rest args) | ||
| 397 | (cons | ||
| 398 | (svg--path-command-symbol 'c args) | ||
| 399 | (apply 'append coordinates-list))) | ||
| 400 | |||
| 401 | (defun svg--smooth-curveto-command (coordinates-list &rest args) | ||
| 402 | (cons | ||
| 403 | (svg--path-command-symbol 's args) | ||
| 404 | (apply 'append coordinates-list))) | ||
| 405 | |||
| 406 | (defun svg--quadratic-bezier-curveto-command (coordinates-list | ||
| 407 | &rest args) | ||
| 408 | (cons | ||
| 409 | (svg--path-command-symbol 'q args) | ||
| 410 | (apply 'append coordinates-list))) | ||
| 411 | |||
| 412 | (defun svg--smooth-quadratic-bezier-curveto-command (coordinates-list | ||
| 413 | &rest args) | ||
| 414 | (cons | ||
| 415 | (svg--path-command-symbol 't args) | ||
| 416 | (apply 'append coordinates-list))) | ||
| 417 | |||
| 418 | (defun svg--eval-path-command (command default-relative) | ||
| 419 | (cl-letf | ||
| 420 | (((symbol-function 'moveto) #'svg--moveto-command) | ||
| 421 | ((symbol-function 'closepath) #'svg--closepath-command) | ||
| 422 | ((symbol-function 'lineto) #'svg--lineto-command) | ||
| 423 | ((symbol-function 'horizontal-lineto) | ||
| 424 | #'svg--horizontal-lineto-command) | ||
| 425 | ((symbol-function 'vertical-lineto) | ||
| 426 | #'svg--vertical-lineto-command) | ||
| 427 | ((symbol-function 'curveto) #'svg--curveto-command) | ||
| 428 | ((symbol-function 'smooth-curveto) | ||
| 429 | #'svg--smooth-curveto-command) | ||
| 430 | ((symbol-function 'quadratic-bezier-curveto) | ||
| 431 | #'svg--quadratic-bezier-curveto-command) | ||
| 432 | ((symbol-function 'smooth-quadratic-bezier-curveto) | ||
| 433 | #'svg--smooth-quadratic-bezier-curveto-command) | ||
| 434 | ((symbol-function 'elliptical-arc) | ||
| 435 | #'svg--elliptical-arc-command) | ||
| 436 | (extended-command (append command (list :default-relative | ||
| 437 | default-relative)))) | ||
| 438 | (mapconcat 'prin1-to-string (apply extended-command) " "))) | ||
| 439 | |||
| 440 | (defun svg-path (svg commands &rest args) | ||
| 441 | "Add the outline of a shape to SVG according to COMMANDS. | ||
| 442 | Coordinates by default are absolute. ARGS is a plist of | ||
| 443 | modifiers. If :relative is t, then coordinates are relative to | ||
| 444 | the last position, or -- initially -- to the origin." | ||
| 445 | (let* ((default-relative (plist-get args :relative)) | ||
| 446 | (stripped-args (svg--plist-delete args :relative)) | ||
| 447 | (d (mapconcat 'identity | ||
| 448 | (mapcar | ||
| 449 | (lambda (command) | ||
| 450 | (svg--eval-path-command command | ||
| 451 | default-relative)) | ||
| 452 | commands) " "))) | ||
| 453 | (svg--append | ||
| 454 | svg | ||
| 455 | (dom-node 'path | ||
| 456 | `((d . ,d) | ||
| 457 | ,@(svg--arguments svg stripped-args)))))) | ||
| 458 | |||
| 459 | (defun svg-clip-path (svg &rest args) | ||
| 460 | "Add a clipping path to SVG, where ARGS is a plist of modifiers. | ||
| 461 | If applied to a shape via the :clip-path property, parts of that | ||
| 462 | shape which lie outside of the clipping path are not drawn." | ||
| 463 | (let ((new-dom-node (dom-node 'clipPath | ||
| 464 | `(,@(svg--arguments svg args))))) | ||
| 465 | (svg--append svg new-dom-node) | ||
| 466 | new-dom-node)) | ||
| 467 | |||
| 468 | (defun svg-node (svg tag &rest args) | ||
| 469 | "Add the custom node TAG to SVG." | ||
| 470 | (let ((new-dom-node (dom-node tag | ||
| 471 | `(,@(svg--arguments svg args))))) | ||
| 472 | (svg--append svg new-dom-node) | ||
| 473 | new-dom-node)) | ||
| 474 | |||
| 327 | (provide 'svg) | 475 | (provide 'svg) |
| 328 | 476 | ||
| 329 | ;;; svg.el ends here | 477 | ;;; svg.el ends here |