svgcommon-lispcl-who

Mixed case tag names in cl-who


I'm using cl-who to generate svg, and it is working fine up until I need a mixed case tag:

(with-html-output (*standard-output*)
  (:defs
    (:|radialGradient| :id "grad1" :cy "20" :fx "10%" :fy "50%" :r "8"
      (:stop :offset "0%" :stop-color "#fff")
      (:stop :offset "100%" :stop-color "#000"))))

There is a variable, *downcase-tokens-p*, for situations like this. It's a bit hard to work with:

(let ((*downcase-tokens-p* nil))
  (with-html-output (*standard-output*)
    (:defs
        (:|radialGradient| :id "grad1" :cy "20" :fx "10%" :fy "50%" :r "8"))))

Output:

<defs>
  <radialgradient id='grad1' cy='20' fx='10%' fy='50%' r='8'>
  </radialgradient>
</defs>

Wrapping with let has no effect because *downcase-tokens-p* was evidently set T at macro expansion time.

So we need to haul out eval:

(let ((*downcase-tokens-p* nil))
  (eval
  '(with-html-output (*standard-output*)
    (:defs
      (:|radialGradient| :id "grad1" :cy "20" :fx "10%" :fy "50%" :r "8")))))

Output:

<DEFS>
  <radialGradient ID='grad1' CY='20' FX='10%' FY='50%' R='8'>
  </radialGradient>
</DEFS>

This works for the radialGradient tag, but now I'll need to || wrap everything else.

What is the simplest way to get the radialGradient tag to display properly while leaving everything else alone?

Edit: examples added.


Solution

  • Here's a generic solution:

    (defmethod convert-tag-to-string-list :around ((tag t) attr-list body body-fn)
      (if (find-if #'lower-case-p (symbol-name tag))
          (nconc (list* "<"
                        (symbol-name tag)
                        (convert-attributes attr-list))
                 (list ">")
                 (funcall body-fn body)
                 (list (format nil "</~a>" (symbol-name tag))))
          (call-next-method)))
    

    Results:

    CL-USER> (with-html-output (*standard-output*)
               (:asdf
                (:ASDF
                 (:|aSDf|
                   (:|ASDF|)))))
    <asdf><asdf><aSDf><asdf></asdf></aSDf></asdf></asdf>