node.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. package svgparser
  2. import (
  3. "bufio"
  4. "encoding/xml"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "golang.org/x/net/html/charset"
  9. )
  10. type Attr = xml.Attr
  11. type Node struct {
  12. Parent *Node
  13. Name xml.Name
  14. Attrs []Attr
  15. Children []any
  16. }
  17. func (n *Node) readFrom(r io.ReadSeeker) error {
  18. if n.Parent != nil {
  19. return errors.New("cannot read child node")
  20. }
  21. dec := xml.NewDecoder(r)
  22. dec.Strict = false
  23. dec.CharsetReader = charset.NewReaderLabel
  24. curNode := n
  25. for {
  26. // Save the current position to know where to read raw CData from.
  27. pos := dec.InputOffset()
  28. // Read raw token so decoder doesn't mess with attributes and namespaces.
  29. tok, err := dec.RawToken()
  30. if err == io.EOF {
  31. break
  32. }
  33. if err != nil {
  34. return err
  35. }
  36. switch t := tok.(type) {
  37. case xml.StartElement:
  38. // An element is opened, create a node for it
  39. el := &Node{
  40. Parent: curNode,
  41. Name: t.Name,
  42. Attrs: t.Attr,
  43. }
  44. // Append the node to the current node's children and make it current
  45. curNode.Children = append(curNode.Children, el)
  46. curNode = el
  47. case xml.EndElement:
  48. // If the current node has no parent, then we are at the root,
  49. // which can't be closed.
  50. if curNode.Parent == nil {
  51. return fmt.Errorf(
  52. "malformed XML: unexpected closing tag </%s> while no elements are opened",
  53. fullName(t.Name),
  54. )
  55. }
  56. // Closing tag name should match opened node name (which is current)
  57. if curNode.Name.Local != t.Name.Local || curNode.Name.Space != t.Name.Space {
  58. return fmt.Errorf(
  59. "malformed XML: unexpected closing tag </%s> for opened <%s> element",
  60. fullName(t.Name),
  61. fullName(curNode.Name),
  62. )
  63. }
  64. // The node is closed, return to its parent
  65. curNode = curNode.Parent
  66. case xml.CharData:
  67. // We want CData as is, so read it raw
  68. cdata, err := readRawCData(r, pos, dec.InputOffset()-pos)
  69. if err != nil {
  70. return err
  71. }
  72. curNode.Children = append(curNode.Children, cdata)
  73. case xml.Directive:
  74. curNode.Children = append(curNode.Children, t.Copy())
  75. case xml.Comment:
  76. curNode.Children = append(curNode.Children, t.Copy())
  77. case xml.ProcInst:
  78. curNode.Children = append(curNode.Children, t.Copy())
  79. }
  80. }
  81. return nil
  82. }
  83. func (n *Node) writeTo(w *bufio.Writer) error {
  84. if err := w.WriteByte('<'); err != nil {
  85. return err
  86. }
  87. if err := writeFullName(w, n.Name); err != nil {
  88. return err
  89. }
  90. n.writeAttrsTo(w)
  91. if len(n.Children) == 0 {
  92. if _, err := w.WriteString("/>"); err != nil {
  93. return err
  94. }
  95. return nil
  96. }
  97. if err := w.WriteByte('>'); err != nil {
  98. return err
  99. }
  100. if err := n.writeChildrenTo(w); err != nil {
  101. return err
  102. }
  103. if _, err := w.WriteString("</"); err != nil {
  104. return err
  105. }
  106. if err := writeFullName(w, n.Name); err != nil {
  107. return err
  108. }
  109. if err := w.WriteByte('>'); err != nil {
  110. return err
  111. }
  112. return nil
  113. }
  114. func (n *Node) writeAttrsTo(w *bufio.Writer) error {
  115. for _, attr := range n.Attrs {
  116. if err := w.WriteByte(' '); err != nil {
  117. return err
  118. }
  119. if err := writeFullName(w, attr.Name); err != nil {
  120. return err
  121. }
  122. if _, err := w.WriteString(`="`); err != nil {
  123. return err
  124. }
  125. if len(attr.Value) > 2 && attr.Value[0] == '&' && attr.Value[len(attr.Value)-1] == ';' {
  126. // Attribute value is an entity, write it as is
  127. if _, err := w.WriteString(attr.Value); err != nil {
  128. return err
  129. }
  130. } else {
  131. // Escape the attribute value
  132. if err := escapeString(w, attr.Value); err != nil {
  133. return err
  134. }
  135. }
  136. if err := w.WriteByte('"'); err != nil {
  137. return err
  138. }
  139. }
  140. return nil
  141. }
  142. func (n *Node) writeChildrenTo(w *bufio.Writer) error {
  143. for _, child := range n.Children {
  144. switch c := child.(type) {
  145. case *Node:
  146. if err := c.writeTo(w); err != nil {
  147. return err
  148. }
  149. case CData:
  150. if _, err := w.Write([]byte(c)); err != nil {
  151. return err
  152. }
  153. case Comment:
  154. if _, err := w.WriteString("<!--"); err != nil {
  155. return err
  156. }
  157. if _, err := w.Write([]byte(c)); err != nil {
  158. return err
  159. }
  160. if _, err := w.WriteString("-->"); err != nil {
  161. return err
  162. }
  163. case Directive:
  164. if _, err := w.WriteString("<!"); err != nil {
  165. return err
  166. }
  167. if _, err := w.Write([]byte(c)); err != nil {
  168. return err
  169. }
  170. if err := w.WriteByte('>'); err != nil {
  171. return err
  172. }
  173. case ProcInst:
  174. if _, err := w.WriteString("<?"); err != nil {
  175. return err
  176. }
  177. if _, err := w.WriteString(c.Target); err != nil {
  178. return err
  179. }
  180. if len(c.Inst) > 0 {
  181. if err := w.WriteByte(' '); err != nil {
  182. return err
  183. }
  184. if _, err := w.Write([]byte(c.Inst)); err != nil {
  185. return err
  186. }
  187. }
  188. if _, err := w.WriteString("?>"); err != nil {
  189. return err
  190. }
  191. default:
  192. return fmt.Errorf("unknown child type: %T", c)
  193. }
  194. }
  195. return nil
  196. }
  197. func fullName(name xml.Name) string {
  198. if len(name.Space) == 0 {
  199. return name.Local
  200. }
  201. return name.Space + ":" + name.Local
  202. }
  203. func writeFullName(w *bufio.Writer, name xml.Name) error {
  204. if len(name.Space) > 0 {
  205. if _, err := w.WriteString(name.Space); err != nil {
  206. return err
  207. }
  208. if err := w.WriteByte(':'); err != nil {
  209. return err
  210. }
  211. }
  212. if _, err := w.WriteString(name.Local); err != nil {
  213. return err
  214. }
  215. return nil
  216. }