Subject: 31.0.50; `dom-print' often fails to print an SVG DOM

From: David Ponce <[email-masked]>
Date: Wed, 29 Jan 2025 16:52:28 +0100

Hello,

Currently `dom-print' is mostly unusable to print an SVG DOM because it
fails as soon as it encounters an attribute value which is not a string.
This is unfortunate, because `dom-print' can be very useful for
examining a DOM by pretty-printing it.

Here is a simple example that illustrates the problem:

(require 'svg)
(let ((svg (svg-create 100 100)))
   (svg-rectangle svg 0 0 "100%" "100%" :fill "blue")
   (svg-text svg "A text" :x 0 :y 55 :stroke "yellow" :fill "yellow")
   (dom-print svg t t))

Which produce this backtrace when evaluated in the *scratch* buffer:

Debugger entered--Lisp error: (wrong-type-argument sequencep 100)
   replace-regexp-in-string("[&<>\"]" #f(compiled-function (c) #<bytecode 0x1119c0c6891a2a03>) 100)
   url-insert-entities-in-string(100)
   dom-print((svg ((width . 100) (height . 100) (version . "1.1") (xmlns . "http://www.w3.org/2000/svg") (xmlns:xlink . "http://www.w3.org/1999/xlink")) (rect ((width . "100%") (height . "100%") (x . 0) (y . 0) (fill . "blue"))) (text ((fill . "yellow") (stroke . "yellow") (y . 55) (x . 0)) "A text")) t t)
   (let ((svg (svg-create 100 100))) (svg-rectangle svg 0 0 "100%" "100%" :fill "blue") (svg-text svg "A text" :x 0 :y 55 :stroke "yellow" :fill "yellow") (dom-print svg t t))
   (progn (let ((svg (svg-create 100 100))) (svg-rectangle svg 0 0 "100%" "100%" :fill "blue") (svg-text svg "A text" :x 0 :y 55 :stroke "yellow" :fill "yellow") (dom-print svg t t)))
   eval((progn (let ((svg (svg-create 100 100))) (svg-rectangle svg 0 0 "100%" "100%" :fill "blue") (svg-text svg "A text" :x 0 :y 55 :stroke "yellow" :fill "yellow") (dom-print svg t t))) t)
   elisp--eval-last-sexp(t)
   #f(compiled-function () #<bytecode 0x299b97085c5ba>)()
   handler-bind-1(#f(compiled-function () #<bytecode 0x299b97085c5ba>) (error) eval-expression--debug)
   eval-last-sexp(t)
   eval-print-last-sexp(nil)
   funcall-interactively(eval-print-last-sexp nil)
   call-interactively(eval-print-last-sexp nil nil)
   command-execute(eval-print-last-sexp)
  
The cause is that `dom-print' calls the function
`url-insert-entities-in-string' to convert special characters in
attribute value assuming this value is a string, which is not always
true in an SVG DOM.

I propose the attached very simple patch that solves the issue.
Here is a possible changelog:

2025-01-29  David Ponce  <[email-masked]>

	* dom.el (dom-print): Ensure to pass a string to
	`url-insert-entities-in-string'.

With this patch applied the above example prints the expected result:

<svg width="100" height="100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
   <rect width="100%" height="100%" x="0" y="0" fill="blue" />
   <text fill="yellow" stroke="yellow" y="55" x="0">A text</text>
</svg>nil

In GNU Emacs 31.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version
  3.24.43, cairo version 1.18.0) of 2025-01-28
Repository revision: 8c4a4b4eab8ac4dc26e65d552821f3f0b49c79c4
Repository branch: master
Windowing system distributor 'The X.Org Foundation', version 11.0.12014000
System Description: Fedora Linux 40 (KDE Plasma)

Configured using:
  'configure --with-native-compilation=no'

Configured features:
ACL CAIRO DBUS FREETYPE GIF GLIB GMP GNUTLS GPM GSETTINGS HARFBUZZ JPEG
LCMS2 LIBOTF LIBSELINUX LIBSYSTEMD LIBXML2 M17N_FLT MODULES NOTIFY
INOTIFY PDUMPER PNG RSVG SECCOMP SOUND SQLITE3 THREADS TIFF
TOOLKIT_SCROLL_BARS TREE_SITTER WEBP X11 XDBE XIM XINPUT2 XPM GTK3 ZLIB

Important settings:
   value of $LC_TIME: fr_FR.utf8
   value of $LANG: fr_FR.UTF-8
   locale-coding-system: utf-8-unix

Major mode: Lisp Interaction

Minor modes in effect:
   tooltip-mode: t
   global-eldoc-mode: t
   eldoc-mode: t
   show-paren-mode: t
   electric-indent-mode: t
   mouse-wheel-mode: t
   tool-bar-mode: t
   menu-bar-mode: t
   file-name-shadow-mode: t
   global-font-lock-mode: t
   font-lock-mode: t
   blink-cursor-mode: t
   minibuffer-regexp-mode: t
   line-number-mode: t
   indent-tabs-mode: t
   transient-mark-mode: t
   auto-composition-mode: t
   auto-encryption-mode: t
   auto-compression-mode: t

Load-path shadows:
None found.

diff --git a/lisp/dom.el b/lisp/dom.el
index fc032058e9f..9eb7c58c38a 100644
--- a/lisp/dom.el
+++ b/lisp/dom.el
@@ -280,7 +280,8 @@ dom-print
 			 (not xml))
 		    (format " %s" (car elem))
 		  (format " %s=\"%s\"" (car elem)
-	                  (url-insert-entities-in-string (cdr elem)))))))
+	                  (url-insert-entities-in-string
+                           (format "%s" (cdr elem))))))))
     (let* ((children (dom-children dom))
 	   (non-text nil))
       (if (null children)