svg.go 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080
  1. // Package svg provides an API for generating Scalable Vector Graphics (SVG)
  2. package svg
  3. // package main
  4. //
  5. // import (
  6. // "github.com/ajstarks/svgo"
  7. // "os"
  8. // )
  9. //
  10. // var (
  11. // width = 500
  12. // height = 500
  13. // canvas = svg.New(os.Stdout)
  14. // )
  15. //
  16. // func main() {
  17. // canvas.Start(width, height)
  18. // canvas.Circle(width/2, height/2, 100)
  19. // canvas.Text(width/2, height/2, "Hello, SVG",
  20. // "text-anchor:middle;font-size:30px;fill:white")
  21. // canvas.End()
  22. // }
  23. //
  24. import (
  25. "fmt"
  26. "io"
  27. "encoding/xml"
  28. "strings"
  29. )
  30. // SVG defines the location of the generated SVG
  31. type SVG struct {
  32. Writer io.Writer
  33. }
  34. // Offcolor defines the offset and color for gradients
  35. type Offcolor struct {
  36. Offset uint8
  37. Color string
  38. Opacity float64
  39. }
  40. // Filterspec defines the specification of SVG filters
  41. type Filterspec struct {
  42. In, In2, Result string
  43. }
  44. const (
  45. svgtop = `<?xml version="1.0"?>
  46. <!-- Generated by SVGo -->
  47. <svg`
  48. svginitfmt = `%s width="%d%s" height="%d%s"`
  49. svgns = `
  50. xmlns="http://www.w3.org/2000/svg"
  51. xmlns:xlink="http://www.w3.org/1999/xlink">`
  52. vbfmt = `viewBox="%d %d %d %d"`
  53. emptyclose = "/>\n"
  54. )
  55. // New is the SVG constructor, specifying the io.Writer where the generated SVG is written.
  56. func New(w io.Writer) *SVG { return &SVG{w} }
  57. func (svg *SVG) print(a ...interface{}) (n int, errno error) {
  58. return fmt.Fprint(svg.Writer, a...)
  59. }
  60. func (svg *SVG) println(a ...interface{}) (n int, errno error) {
  61. return fmt.Fprintln(svg.Writer, a...)
  62. }
  63. func (svg *SVG) printf(format string, a ...interface{}) (n int, errno error) {
  64. return fmt.Fprintf(svg.Writer, format, a...)
  65. }
  66. func (svg *SVG) genattr(ns []string) {
  67. for _, v := range ns {
  68. svg.printf("\n %s", v)
  69. }
  70. svg.println(svgns)
  71. }
  72. // Structure, Metadata, Scripting, Style, Transformation, and Links
  73. // Start begins the SVG document with the width w and height h.
  74. // Other attributes may be optionally added, for example viewbox or additional namespaces
  75. // Standard Reference: http://www.w3.org/TR/SVG11/struct.html#SVGElement
  76. func (svg *SVG) Start(w int, h int, ns ...string) {
  77. svg.printf(svginitfmt, svgtop, w, "", h, "")
  78. svg.genattr(ns)
  79. }
  80. // Startunit begins the SVG document, with width and height in the specified units
  81. // Other attributes may be optionally added, for example viewbox or additional namespaces
  82. func (svg *SVG) Startunit(w int, h int, unit string, ns ...string) {
  83. svg.printf(svginitfmt, svgtop, w, unit, h, unit)
  84. svg.genattr(ns)
  85. }
  86. // Startpercent begins the SVG document, with width and height as percentages
  87. // Other attributes may be optionally added, for example viewbox or additional namespaces
  88. func (svg *SVG) Startpercent(w int, h int, ns ...string) {
  89. svg.printf(svginitfmt, svgtop, w, "%", h, "%")
  90. svg.genattr(ns)
  91. }
  92. // Startview begins the SVG document, with the specified width, height, and viewbox
  93. // Other attributes may be optionally added, for example viewbox or additional namespaces
  94. func (svg *SVG) Startview(w, h, minx, miny, vw, vh int) {
  95. svg.Start(w, h, fmt.Sprintf(vbfmt, minx, miny, vw, vh))
  96. }
  97. // StartviewUnit begins the SVG document with the specified width, height, and unit
  98. func (svg *SVG) StartviewUnit(w, h int, unit string, minx, miny, vw, vh int) {
  99. svg.Startunit(w, h, unit, fmt.Sprintf(vbfmt, minx, miny, vw, vh))
  100. }
  101. // Startraw begins the SVG document, passing arbitrary attributes
  102. func (svg *SVG) Startraw(ns ...string) {
  103. svg.printf(svgtop)
  104. svg.genattr(ns)
  105. }
  106. // End the SVG document
  107. func (svg *SVG) End() { svg.println("</svg>") }
  108. // linkembed defines an element with a specified type,
  109. // (for example "application/javascript", or "text/css").
  110. // if the first variadic argument is a link, use only the link reference.
  111. // Otherwise, treat those arguments as the text of the script (marked up as CDATA).
  112. // if no data is specified, just close the element
  113. func (svg *SVG) linkembed(tag string, scriptype string, data ...string) {
  114. svg.printf(`<%s type="%s"`, tag, scriptype)
  115. switch {
  116. case len(data) == 1 && islink(data[0]):
  117. svg.printf(" %s/>\n", href(data[0]))
  118. case len(data) > 0:
  119. svg.printf(">\n<![CDATA[\n")
  120. for _, v := range data {
  121. svg.println(v)
  122. }
  123. svg.printf("]]>\n</%s>\n", tag)
  124. default:
  125. svg.println(`/>`)
  126. }
  127. }
  128. // Script defines a script with a specified type, (for example "application/javascript").
  129. func (svg *SVG) Script(scriptype string, data ...string) {
  130. svg.linkembed("script", scriptype, data...)
  131. }
  132. // Style defines the specified style (for example "text/css")
  133. func (svg *SVG) Style(scriptype string, data ...string) {
  134. svg.linkembed("style", scriptype, data...)
  135. }
  136. // Gstyle begins a group, with the specified style.
  137. // Standard Reference: http://www.w3.org/TR/SVG11/struct.html#GElement
  138. func (svg *SVG) Gstyle(s string) { svg.println(group("style", s)) }
  139. // Gtransform begins a group, with the specified transform
  140. // Standard Reference: http://www.w3.org/TR/SVG11/coords.html#TransformAttribute
  141. func (svg *SVG) Gtransform(s string) { svg.println(group("transform", s)) }
  142. // Translate begins coordinate translation, end with Gend()
  143. // Standard Reference: http://www.w3.org/TR/SVG11/coords.html#TransformAttribute
  144. func (svg *SVG) Translate(x, y int) { svg.Gtransform(translate(x, y)) }
  145. // Scale scales the coordinate system by n, end with Gend()
  146. // Standard Reference: http://www.w3.org/TR/SVG11/coords.html#TransformAttribute
  147. func (svg *SVG) Scale(n float64) { svg.Gtransform(scale(n)) }
  148. // ScaleXY scales the coordinate system by dx and dy, end with Gend()
  149. // Standard Reference: http://www.w3.org/TR/SVG11/coords.html#TransformAttribute
  150. func (svg *SVG) ScaleXY(dx, dy float64) { svg.Gtransform(scaleXY(dx, dy)) }
  151. // SkewX skews the x coordinate system by angle a, end with Gend()
  152. // Standard Reference: http://www.w3.org/TR/SVG11/coords.html#TransformAttribute
  153. func (svg *SVG) SkewX(a float64) { svg.Gtransform(skewX(a)) }
  154. // SkewY skews the y coordinate system by angle a, end with Gend()
  155. // Standard Reference: http://www.w3.org/TR/SVG11/coords.html#TransformAttribute
  156. func (svg *SVG) SkewY(a float64) { svg.Gtransform(skewY(a)) }
  157. // SkewXY skews x and y coordinates by ax, ay respectively, end with Gend()
  158. // Standard Reference: http://www.w3.org/TR/SVG11/coords.html#TransformAttribute
  159. func (svg *SVG) SkewXY(ax, ay float64) { svg.Gtransform(skewX(ax) + " " + skewY(ay)) }
  160. // Rotate rotates the coordinate system by r degrees, end with Gend()
  161. // Standard Reference: http://www.w3.org/TR/SVG11/coords.html#TransformAttribute
  162. func (svg *SVG) Rotate(r float64) { svg.Gtransform(rotate(r)) }
  163. // TranslateRotate translates the coordinate system to (x,y), then rotates to r degrees, end with Gend()
  164. func (svg *SVG) TranslateRotate(x, y int, r float64) {
  165. svg.Gtransform(translate(x, y) + " " + rotate(r))
  166. }
  167. // RotateTranslate rotates the coordinate system r degrees, then translates to (x,y), end with Gend()
  168. func (svg *SVG) RotateTranslate(x, y int, r float64) {
  169. svg.Gtransform(rotate(r) + " " + translate(x, y))
  170. }
  171. // Group begins a group with arbitrary attributes
  172. func (svg *SVG) Group(s ...string) { svg.printf("<g %s\n", endstyle(s, `>`)) }
  173. // Gid begins a group, with the specified id
  174. func (svg *SVG) Gid(s string) {
  175. svg.print(`<g id="`)
  176. xml.Escape(svg.Writer, []byte(s))
  177. svg.println(`">`)
  178. }
  179. // Gend ends a group (must be paired with Gsttyle, Gtransform, Gid).
  180. func (svg *SVG) Gend() { svg.println(`</g>`) }
  181. // ClipPath defines a clip path
  182. func (svg *SVG) ClipPath(s ...string) { svg.printf(`<clipPath %s`, endstyle(s, `>`)) }
  183. // ClipEnd ends a ClipPath
  184. func (svg *SVG) ClipEnd() {
  185. svg.println(`</clipPath>`)
  186. }
  187. // Def begins a defintion block.
  188. // Standard Reference: http://www.w3.org/TR/SVG11/struct.html#DefsElement
  189. func (svg *SVG) Def() { svg.println(`<defs>`) }
  190. // DefEnd ends a defintion block.
  191. func (svg *SVG) DefEnd() { svg.println(`</defs>`) }
  192. // Marker defines a marker
  193. // Standard reference: http://www.w3.org/TR/SVG11/painting.html#MarkerElement
  194. func (svg *SVG) Marker(id string, x, y, width, height int, s ...string) {
  195. svg.printf(`<marker id="%s" refX="%d" refY="%d" markerWidth="%d" markerHeight="%d" %s`,
  196. id, x, y, width, height, endstyle(s, ">\n"))
  197. }
  198. // MarkerEnd ends a marker
  199. func (svg *SVG) MarkerEnd() { svg.println(`</marker>`) }
  200. // Pattern defines a pattern with the specified dimensions.
  201. // The putype can be either "user" or "obj", which sets the patternUnits
  202. // attribute to be either userSpaceOnUse or objectBoundingBox
  203. // Standard reference: http://www.w3.org/TR/SVG11/pservers.html#Patterns
  204. func (svg *SVG) Pattern(id string, x, y, width, height int, putype string, s ...string) {
  205. puattr := "userSpaceOnUse"
  206. if putype != "user" {
  207. puattr = "objectBoundingBox"
  208. }
  209. svg.printf(`<pattern id="%s" x="%d" y="%d" width="%d" height="%d" patternUnits="%s" %s`,
  210. id, x, y, width, height, puattr, endstyle(s, ">\n"))
  211. }
  212. // PatternEnd ends a marker
  213. func (svg *SVG) PatternEnd() { svg.println(`</pattern>`) }
  214. // Desc specified the text of the description tag.
  215. // Standard Reference: http://www.w3.org/TR/SVG11/struct.html#DescElement
  216. func (svg *SVG) Desc(s string) { svg.tt("desc", s) }
  217. // Title specified the text of the title tag.
  218. // Standard Reference: http://www.w3.org/TR/SVG11/struct.html#TitleElement
  219. func (svg *SVG) Title(s string) { svg.tt("title", s) }
  220. // Link begins a link named "name", with the specified title.
  221. // Standard Reference: http://www.w3.org/TR/SVG11/linking.html#Links
  222. func (svg *SVG) Link(href string, title string) {
  223. svg.printf("<a xlink:href=\"%s\" xlink:title=\"", href)
  224. xml.Escape(svg.Writer, []byte(title))
  225. svg.println("\">")
  226. }
  227. // LinkEnd ends a link.
  228. func (svg *SVG) LinkEnd() { svg.println(`</a>`) }
  229. // Use places the object referenced at link at the location x, y, with optional style.
  230. // Standard Reference: http://www.w3.org/TR/SVG11/struct.html#UseElement
  231. func (svg *SVG) Use(x int, y int, link string, s ...string) {
  232. svg.printf(`<use %s %s %s`, loc(x, y), href(link), endstyle(s, emptyclose))
  233. }
  234. // Mask creates a mask with a specified id, dimension, and optional style.
  235. func (svg *SVG) Mask(id string, x int, y int, w int, h int, s ...string) {
  236. svg.printf(`<mask id="%s" x="%d" y="%d" width="%d" height="%d" %s`, id, x, y, w, h, endstyle(s, `>`))
  237. }
  238. // MaskEnd ends a Mask.
  239. func (svg *SVG) MaskEnd() { svg.println(`</mask>`) }
  240. // Shapes
  241. // Circle centered at x,y, with radius r, with optional style.
  242. // Standard Reference: http://www.w3.org/TR/SVG11/shapes.html#CircleElement
  243. func (svg *SVG) Circle(x int, y int, r int, s ...string) {
  244. svg.printf(`<circle cx="%d" cy="%d" r="%d" %s`, x, y, r, endstyle(s, emptyclose))
  245. }
  246. // Ellipse centered at x,y, centered at x,y with radii w, and h, with optional style.
  247. // Standard Reference: http://www.w3.org/TR/SVG11/shapes.html#EllipseElement
  248. func (svg *SVG) Ellipse(x int, y int, w int, h int, s ...string) {
  249. svg.printf(`<ellipse cx="%d" cy="%d" rx="%d" ry="%d" %s`,
  250. x, y, w, h, endstyle(s, emptyclose))
  251. }
  252. // Polygon draws a series of line segments using an array of x, y coordinates, with optional style.
  253. // Standard Reference: http://www.w3.org/TR/SVG11/shapes.html#PolygonElement
  254. func (svg *SVG) Polygon(x []int, y []int, s ...string) {
  255. svg.poly(x, y, "polygon", s...)
  256. }
  257. // Rect draws a rectangle with upper left-hand corner at x,y, with width w, and height h, with optional style
  258. // Standard Reference: http://www.w3.org/TR/SVG11/shapes.html#RectElement
  259. func (svg *SVG) Rect(x int, y int, w int, h int, s ...string) {
  260. svg.printf(`<rect %s %s`, dim(x, y, w, h), endstyle(s, emptyclose))
  261. }
  262. // CenterRect draws a rectangle with its center at x,y, with width w, and height h, with optional style
  263. func (svg *SVG) CenterRect(x int, y int, w int, h int, s ...string) {
  264. svg.Rect(x-(w/2), y-(h/2), w, h, s...)
  265. }
  266. // Roundrect draws a rounded rectangle with upper the left-hand corner at x,y,
  267. // with width w, and height h. The radii for the rounded portion
  268. // are specified by rx (width), and ry (height).
  269. // Style is optional.
  270. // Standard Reference: http://www.w3.org/TR/SVG11/shapes.html#RectElement
  271. func (svg *SVG) Roundrect(x int, y int, w int, h int, rx int, ry int, s ...string) {
  272. svg.printf(`<rect %s rx="%d" ry="%d" %s`, dim(x, y, w, h), rx, ry, endstyle(s, emptyclose))
  273. }
  274. // Square draws a square with upper left corner at x,y with sides of length l, with optional style.
  275. func (svg *SVG) Square(x int, y int, l int, s ...string) {
  276. svg.Rect(x, y, l, l, s...)
  277. }
  278. // Paths
  279. // Path draws an arbitrary path, the caller is responsible for structuring the path data
  280. func (svg *SVG) Path(d string, s ...string) {
  281. svg.printf(`<path d="%s" %s`, d, endstyle(s, emptyclose))
  282. }
  283. // Arc draws an elliptical arc, with optional style, beginning coordinate at sx,sy, ending coordinate at ex, ey
  284. // width and height of the arc are specified by ax, ay, the x axis rotation is r
  285. // if sweep is true, then the arc will be drawn in a "positive-angle" direction (clockwise), if false,
  286. // the arc is drawn counterclockwise.
  287. // if large is true, the arc sweep angle is greater than or equal to 180 degrees,
  288. // otherwise the arc sweep is less than 180 degrees
  289. // http://www.w3.org/TR/SVG11/paths.html#PathDataEllipticalArcCommands
  290. func (svg *SVG) Arc(sx int, sy int, ax int, ay int, r int, large bool, sweep bool, ex int, ey int, s ...string) {
  291. svg.printf(`%s A%s %d %s %s %s" %s`,
  292. ptag(sx, sy), coord(ax, ay), r, onezero(large), onezero(sweep), coord(ex, ey), endstyle(s, emptyclose))
  293. }
  294. // Bezier draws a cubic bezier curve, with optional style, beginning at sx,sy, ending at ex,ey
  295. // with control points at cx,cy and px,py.
  296. // Standard Reference: http://www.w3.org/TR/SVG11/paths.html#PathDataCubicBezierCommands
  297. func (svg *SVG) Bezier(sx int, sy int, cx int, cy int, px int, py int, ex int, ey int, s ...string) {
  298. svg.printf(`%s C%s %s %s" %s`,
  299. ptag(sx, sy), coord(cx, cy), coord(px, py), coord(ex, ey), endstyle(s, emptyclose))
  300. }
  301. // Qbez draws a quadratic bezier curver, with optional style
  302. // beginning at sx,sy, ending at ex, sy with control points at cx, cy
  303. // Standard Reference: http://www.w3.org/TR/SVG11/paths.html#PathDataQuadraticBezierCommands
  304. func (svg *SVG) Qbez(sx int, sy int, cx int, cy int, ex int, ey int, s ...string) {
  305. svg.printf(`%s Q%s %s" %s`,
  306. ptag(sx, sy), coord(cx, cy), coord(ex, ey), endstyle(s, emptyclose))
  307. }
  308. // Qbezier draws a Quadratic Bezier curve, with optional style, beginning at sx, sy, ending at tx,ty
  309. // with control points are at cx,cy, ex,ey.
  310. // Standard Reference: http://www.w3.org/TR/SVG11/paths.html#PathDataQuadraticBezierCommands
  311. func (svg *SVG) Qbezier(sx int, sy int, cx int, cy int, ex int, ey int, tx int, ty int, s ...string) {
  312. svg.printf(`%s Q%s %s T%s" %s`,
  313. ptag(sx, sy), coord(cx, cy), coord(ex, ey), coord(tx, ty), endstyle(s, emptyclose))
  314. }
  315. // Lines
  316. // Line draws a straight line between two points, with optional style.
  317. // Standard Reference: http://www.w3.org/TR/SVG11/shapes.html#LineElement
  318. func (svg *SVG) Line(x1 int, y1 int, x2 int, y2 int, s ...string) {
  319. svg.printf(`<line x1="%d" y1="%d" x2="%d" y2="%d" %s`, x1, y1, x2, y2, endstyle(s, emptyclose))
  320. }
  321. // Polyline draws connected lines between coordinates, with optional style.
  322. // Standard Reference: http://www.w3.org/TR/SVG11/shapes.html#PolylineElement
  323. func (svg *SVG) Polyline(x []int, y []int, s ...string) {
  324. svg.poly(x, y, "polyline", s...)
  325. }
  326. // Image places at x,y (upper left hand corner), the image with
  327. // width w, and height h, referenced at link, with optional style.
  328. // Standard Reference: http://www.w3.org/TR/SVG11/struct.html#ImageElement
  329. func (svg *SVG) Image(x int, y int, w int, h int, link string, s ...string) {
  330. svg.printf(`<image %s %s %s`, dim(x, y, w, h), href(link), endstyle(s, emptyclose))
  331. }
  332. // Text places the specified text, t at x,y according to the style specified in s
  333. // Standard Reference: http://www.w3.org/TR/SVG11/text.html#TextElement
  334. func (svg *SVG) Text(x int, y int, t string, s ...string) {
  335. svg.printf(`<text %s %s`, loc(x, y), endstyle(s, ">"))
  336. xml.Escape(svg.Writer, []byte(t))
  337. svg.println(`</text>`)
  338. }
  339. // Textspan begins text, assuming a tspan will be included, end with TextEnd()
  340. // Standard Reference: https://www.w3.org/TR/SVG11/text.html#TSpanElement
  341. func (svg *SVG) Textspan(x int, y int, t string, s ...string) {
  342. svg.printf(`<text %s %s`, loc(x, y), endstyle(s, ">"))
  343. xml.Escape(svg.Writer, []byte(t))
  344. }
  345. // Span makes styled spanned text, should be proceeded by Textspan
  346. // Standard Reference: https://www.w3.org/TR/SVG11/text.html#TSpanElement
  347. func (svg *SVG) Span(t string, s ...string) {
  348. if len(s) == 0 {
  349. xml.Escape(svg.Writer, []byte(t))
  350. return
  351. }
  352. svg.printf(`<tspan %s`, endstyle(s, ">"))
  353. xml.Escape(svg.Writer, []byte(t))
  354. svg.printf(`</tspan>`)
  355. }
  356. // TextEnd ends spanned text
  357. // Standard Reference: https://www.w3.org/TR/SVG11/text.html#TSpanElement
  358. func (svg *SVG) TextEnd() {
  359. svg.println(`</text>`)
  360. }
  361. // Textpath places text optionally styled text along a previously defined path
  362. // Standard Reference: http://www.w3.org/TR/SVG11/text.html#TextPathElement
  363. func (svg *SVG) Textpath(t string, pathid string, s ...string) {
  364. svg.printf("<text %s<textPath xlink:href=\"%s\">", endstyle(s, ">"), pathid)
  365. xml.Escape(svg.Writer, []byte(t))
  366. svg.println(`</textPath></text>`)
  367. }
  368. // Textlines places a series of lines of text starting at x,y, at the specified size, fill, and alignment.
  369. // Each line is spaced according to the spacing argument
  370. func (svg *SVG) Textlines(x, y int, s []string, size, spacing int, fill, align string) {
  371. svg.Gstyle(fmt.Sprintf("font-size:%dpx;fill:%s;text-anchor:%s", size, fill, align))
  372. for _, t := range s {
  373. svg.Text(x, y, t)
  374. y += spacing
  375. }
  376. svg.Gend()
  377. }
  378. // Colors
  379. // RGB specifies a fill color in terms of a (r)ed, (g)reen, (b)lue triple.
  380. // Standard reference: http://www.w3.org/TR/css3-color/
  381. func (svg *SVG) RGB(r int, g int, b int) string {
  382. return fmt.Sprintf(`fill:rgb(%d,%d,%d)`, r, g, b)
  383. }
  384. // RGBA specifies a fill color in terms of a (r)ed, (g)reen, (b)lue triple and opacity.
  385. func (svg *SVG) RGBA(r int, g int, b int, a float64) string {
  386. return fmt.Sprintf(`fill-opacity:%.2f; %s`, a, svg.RGB(r, g, b))
  387. }
  388. // Gradients
  389. // LinearGradient constructs a linear color gradient identified by id,
  390. // along the vector defined by (x1,y1), and (x2,y2).
  391. // The stop color sequence defined in sc. Coordinates are expressed as percentages.
  392. func (svg *SVG) LinearGradient(id string, x1, y1, x2, y2 uint8, sc []Offcolor) {
  393. svg.printf("<linearGradient id=\"%s\" x1=\"%d%%\" y1=\"%d%%\" x2=\"%d%%\" y2=\"%d%%\">\n",
  394. id, pct(x1), pct(y1), pct(x2), pct(y2))
  395. svg.stopcolor(sc)
  396. svg.println("</linearGradient>")
  397. }
  398. // RadialGradient constructs a radial color gradient identified by id,
  399. // centered at (cx,cy), with a radius of r.
  400. // (fx, fy) define the location of the focal point of the light source.
  401. // The stop color sequence defined in sc.
  402. // Coordinates are expressed as percentages.
  403. func (svg *SVG) RadialGradient(id string, cx, cy, r, fx, fy uint8, sc []Offcolor) {
  404. svg.printf("<radialGradient id=\"%s\" cx=\"%d%%\" cy=\"%d%%\" r=\"%d%%\" fx=\"%d%%\" fy=\"%d%%\">\n",
  405. id, pct(cx), pct(cy), pct(r), pct(fx), pct(fy))
  406. svg.stopcolor(sc)
  407. svg.println("</radialGradient>")
  408. }
  409. // stopcolor is a utility function used by the gradient functions
  410. // to define a sequence of offsets (expressed as percentages) and colors
  411. func (svg *SVG) stopcolor(oc []Offcolor) {
  412. for _, v := range oc {
  413. svg.printf("<stop offset=\"%d%%\" stop-color=\"%s\" stop-opacity=\"%.2f\"/>\n",
  414. pct(v.Offset), v.Color, v.Opacity)
  415. }
  416. }
  417. // Filter Effects:
  418. // Most functions have common attributes (in, in2, result) defined in type Filterspec
  419. // used as a common first argument.
  420. // Filter begins a filter set
  421. // Standard reference: http://www.w3.org/TR/SVG11/filters.html#FilterElement
  422. func (svg *SVG) Filter(id string, s ...string) {
  423. svg.printf(`<filter id="%s" %s`, id, endstyle(s, ">\n"))
  424. }
  425. // Fend ends a filter set
  426. // Standard reference: http://www.w3.org/TR/SVG11/filters.html#FilterElement
  427. func (svg *SVG) Fend() {
  428. svg.println(`</filter>`)
  429. }
  430. // FeBlend specifies a Blend filter primitive
  431. // Standard reference: http://www.w3.org/TR/SVG11/filters.html#feBlendElement
  432. func (svg *SVG) FeBlend(fs Filterspec, mode string, s ...string) {
  433. switch mode {
  434. case "normal", "multiply", "screen", "darken", "lighten":
  435. break
  436. default:
  437. mode = "normal"
  438. }
  439. svg.printf(`<feBlend %s mode="%s" %s`,
  440. fsattr(fs), mode, endstyle(s, emptyclose))
  441. }
  442. // FeColorMatrix specifies a color matrix filter primitive, with matrix values
  443. // Standard reference: http://www.w3.org/TR/SVG11/filters.html#feColorMatrixElement
  444. func (svg *SVG) FeColorMatrix(fs Filterspec, values [20]float64, s ...string) {
  445. svg.printf(`<feColorMatrix %s type="matrix" values="`, fsattr(fs))
  446. for _, v := range values {
  447. svg.printf(`%g `, v)
  448. }
  449. svg.printf(`" %s`, endstyle(s, emptyclose))
  450. }
  451. // FeColorMatrixHue specifies a color matrix filter primitive, with hue rotation values
  452. // Standard reference: http://www.w3.org/TR/SVG11/filters.html#feColorMatrixElement
  453. func (svg *SVG) FeColorMatrixHue(fs Filterspec, value float64, s ...string) {
  454. if value < -360 || value > 360 {
  455. value = 0
  456. }
  457. svg.printf(`<feColorMatrix %s type="hueRotate" values="%g" %s`,
  458. fsattr(fs), value, endstyle(s, emptyclose))
  459. }
  460. // FeColorMatrixSaturate specifies a color matrix filter primitive, with saturation values
  461. // Standard reference: http://www.w3.org/TR/SVG11/filters.html#feColorMatrixElement
  462. func (svg *SVG) FeColorMatrixSaturate(fs Filterspec, value float64, s ...string) {
  463. if value < 0 || value > 1 {
  464. value = 1
  465. }
  466. svg.printf(`<feColorMatrix %s type="saturate" values="%g" %s`,
  467. fsattr(fs), value, endstyle(s, emptyclose))
  468. }
  469. // FeColorMatrixLuminence specifies a color matrix filter primitive, with luminence values
  470. // Standard reference: http://www.w3.org/TR/SVG11/filters.html#feColorMatrixElement
  471. func (svg *SVG) FeColorMatrixLuminence(fs Filterspec, s ...string) {
  472. svg.printf(`<feColorMatrix %s type="luminenceToAlpha" %s`,
  473. fsattr(fs), endstyle(s, emptyclose))
  474. }
  475. // FeComponentTransfer begins a feComponent filter element
  476. // Standard reference: http://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
  477. func (svg *SVG) FeComponentTransfer() {
  478. svg.println(`<feComponentTransfer>`)
  479. }
  480. // FeCompEnd ends a feComponent filter element
  481. // Standard reference: http://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
  482. func (svg *SVG) FeCompEnd() {
  483. svg.println(`</feComponentTransfer>`)
  484. }
  485. // FeComposite specifies a feComposite filter primitive
  486. // Standard reference: http://www.w3.org/TR/SVG11/filters.html#feCompositeElement
  487. func (svg *SVG) FeComposite(fs Filterspec, operator string, k1, k2, k3, k4 int, s ...string) {
  488. switch operator {
  489. case "over", "in", "out", "atop", "xor", "arithmetic":
  490. break
  491. default:
  492. operator = "over"
  493. }
  494. svg.printf(`<feComposite %s operator="%s" k1="%d" k2="%d" k3="%d" k4="%d" %s`,
  495. fsattr(fs), operator, k1, k2, k3, k4, endstyle(s, emptyclose))
  496. }
  497. // FeConvolveMatrix specifies a feConvolveMatrix filter primitive
  498. // Standard referencd: http://www.w3.org/TR/SVG11/filters.html#feConvolveMatrixElement
  499. func (svg *SVG) FeConvolveMatrix(fs Filterspec, matrix [9]int, s ...string) {
  500. svg.printf(`<feConvolveMatrix %s kernelMatrix="%d %d %d %d %d %d %d %d %d" %s`,
  501. fsattr(fs),
  502. matrix[0], matrix[1], matrix[2],
  503. matrix[3], matrix[4], matrix[5],
  504. matrix[6], matrix[7], matrix[8], endstyle(s, emptyclose))
  505. }
  506. // FeDiffuseLighting specifies a diffuse lighting filter primitive,
  507. // a container for light source elements, end with DiffuseEnd()
  508. // Standard reference: http://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
  509. func (svg *SVG) FeDiffuseLighting(fs Filterspec, scale, constant float64, s ...string) {
  510. svg.printf(`<feDiffuseLighting %s surfaceScale="%g" diffuseConstant="%g" %s`,
  511. fsattr(fs), scale, constant, endstyle(s, `>`))
  512. }
  513. // FeDiffEnd ends a diffuse lighting filter primitive container
  514. // Standard reference: http://www.w3.org/TR/SVG11/filters.html#feDiffuseLightingElement
  515. func (svg *SVG) FeDiffEnd() {
  516. svg.println(`</feDiffuseLighting>`)
  517. }
  518. // FeDisplacementMap specifies a feDisplacementMap filter primitive
  519. // Standard reference: http://www.w3.org/TR/SVG11/filters.html#feDisplacementMapElement
  520. func (svg *SVG) FeDisplacementMap(fs Filterspec, scale float64, xchannel, ychannel string, s ...string) {
  521. svg.printf(`<feDisplacementMap %s scale="%g" xChannelSelector="%s" yChannelSelector="%s" %s`,
  522. fsattr(fs), scale, imgchannel(xchannel), ychannel, endstyle(s, emptyclose))
  523. }
  524. // FeDistantLight specifies a feDistantLight filter primitive
  525. // Standard reference: http://www.w3.org/TR/SVG11/filters.html#feDistantLightElement
  526. func (svg *SVG) FeDistantLight(fs Filterspec, azimuth, elevation float64, s ...string) {
  527. svg.printf(`<feDistantLight %s azimuth="%g" elevation="%g" %s`,
  528. fsattr(fs), azimuth, elevation, endstyle(s, emptyclose))
  529. }
  530. // FeFlood specifies a flood filter primitive
  531. // Standard reference: http://www.w3.org/TR/SVG11/filters.html#feFloodElement
  532. func (svg *SVG) FeFlood(fs Filterspec, color string, opacity float64, s ...string) {
  533. svg.printf(`<feFlood %s flood-color="%s" flood-opacity="%g" %s`,
  534. fsattr(fs), color, opacity, endstyle(s, emptyclose))
  535. }
  536. // FeFunc{linear|Gamma|Table|Discrete} specify various types of feFunc{R|G|B|A} filter primitives
  537. // Standard reference: http://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
  538. // FeFuncLinear specifies a linear style function for the feFunc{R|G|B|A} filter element
  539. // Standard reference: http://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
  540. func (svg *SVG) FeFuncLinear(channel string, slope, intercept float64) {
  541. svg.printf(`<feFunc%s type="linear" slope="%g" intercept="%g"%s`,
  542. imgchannel(channel), slope, intercept, emptyclose)
  543. }
  544. // FeFuncGamma specifies the curve values for gamma correction for the feFunc{R|G|B|A} filter element
  545. // Standard reference: http://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
  546. func (svg *SVG) FeFuncGamma(channel string, amplitude, exponent, offset float64) {
  547. svg.printf(`<feFunc%s type="gamma" amplitude="%g" exponent="%g" offset="%g"%s`,
  548. imgchannel(channel), amplitude, exponent, offset, emptyclose)
  549. }
  550. // FeFuncTable specifies the table of values for the feFunc{R|G|B|A} filter element
  551. // Standard reference: http://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
  552. func (svg *SVG) FeFuncTable(channel string, tv []float64) {
  553. svg.printf(`<feFunc%s type="table"`, imgchannel(channel))
  554. svg.tablevalues(`tableValues`, tv)
  555. }
  556. // FeFuncDiscrete specifies the discrete values for the feFunc{R|G|B|A} filter element
  557. // Standard reference: http://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
  558. func (svg *SVG) FeFuncDiscrete(channel string, tv []float64) {
  559. svg.printf(`<feFunc%s type="discrete"`, imgchannel(channel))
  560. svg.tablevalues(`tableValues`, tv)
  561. }
  562. // FeGaussianBlur specifies a Gaussian Blur filter primitive
  563. // Standard reference: http://www.w3.org/TR/SVG11/filters.html#feGaussianBlurElement
  564. func (svg *SVG) FeGaussianBlur(fs Filterspec, stdx, stdy float64, s ...string) {
  565. if stdx < 0 {
  566. stdx = 0
  567. }
  568. if stdy < 0 {
  569. stdy = 0
  570. }
  571. svg.printf(`<feGaussianBlur %s stdDeviation="%g %g" %s`,
  572. fsattr(fs), stdx, stdy, endstyle(s, emptyclose))
  573. }
  574. // FeImage specifies a feImage filter primitive
  575. // Standard reference: http://www.w3.org/TR/SVG11/filters.html#feImageElement
  576. func (svg *SVG) FeImage(href string, result string, s ...string) {
  577. svg.printf(`<feImage xlink:href="%s" result="%s" %s`,
  578. href, result, endstyle(s, emptyclose))
  579. }
  580. // FeMerge specifies a feMerge filter primitive, containing feMerge elements
  581. // Standard reference: http://www.w3.org/TR/SVG11/filters.html#feMergeElement
  582. func (svg *SVG) FeMerge(nodes []string, s ...string) {
  583. svg.println(`<feMerge>`)
  584. for _, n := range nodes {
  585. svg.printf("<feMergeNode in=\"%s\"/>\n", n)
  586. }
  587. svg.println(`</feMerge>`)
  588. }
  589. // FeMorphology specifies a feMorphologyLight filter primitive
  590. // Standard reference: http://www.w3.org/TR/SVG11/filters.html#feMorphologyElement
  591. func (svg *SVG) FeMorphology(fs Filterspec, operator string, xradius, yradius float64, s ...string) {
  592. switch operator {
  593. case "erode", "dilate":
  594. break
  595. default:
  596. operator = "erode"
  597. }
  598. svg.printf(`<feMorphology %s operator="%s" radius="%g %g" %s`,
  599. fsattr(fs), operator, xradius, yradius, endstyle(s, emptyclose))
  600. }
  601. // FeOffset specifies the feOffset filter primitive
  602. // Standard reference: http://www.w3.org/TR/SVG11/filters.html#feOffsetElement
  603. func (svg *SVG) FeOffset(fs Filterspec, dx, dy int, s ...string) {
  604. svg.printf(`<feOffset %s dx="%d" dy="%d" %s`,
  605. fsattr(fs), dx, dy, endstyle(s, emptyclose))
  606. }
  607. // FePointLight specifies a fePpointLight filter primitive
  608. // Standard reference: http://www.w3.org/TR/SVG11/filters.html#fePointLightElement
  609. func (svg *SVG) FePointLight(x, y, z float64, s ...string) {
  610. svg.printf(`<fePointLight x="%g" y="%g" z="%g" %s`,
  611. x, y, z, endstyle(s, emptyclose))
  612. }
  613. // FeSpecularLighting specifies a specular lighting filter primitive,
  614. // a container for light source elements, end with SpecularEnd()
  615. // Standard reference: http://www.w3.org/TR/SVG11/filters.html#feSpecularLightingElement
  616. func (svg *SVG) FeSpecularLighting(fs Filterspec, scale, constant float64, exponent int, color string, s ...string) {
  617. svg.printf(`<feSpecularLighting %s surfaceScale="%g" specularConstant="%g" specularExponent="%d" lighting-color="%s" %s`,
  618. fsattr(fs), scale, constant, exponent, color, endstyle(s, ">\n"))
  619. }
  620. // FeSpecEnd ends a specular lighting filter primitive container
  621. // Standard reference: http://www.w3.org/TR/SVG11/filters.html#feSpecularLightingElement
  622. func (svg *SVG) FeSpecEnd() {
  623. svg.println(`</feSpecularLighting>`)
  624. }
  625. // FeSpotLight specifies a feSpotLight filter primitive
  626. // Standard reference: http://www.w3.org/TR/SVG11/filters.html#feSpotLightElement
  627. func (svg *SVG) FeSpotLight(fs Filterspec, x, y, z, px, py, pz float64, s ...string) {
  628. svg.printf(`<feSpotLight %s x="%g" y="%g" z="%g" pointsAtX="%g" pointsAtY="%g" pointsAtZ="%g" %s`,
  629. fsattr(fs), x, y, z, px, py, pz, endstyle(s, emptyclose))
  630. }
  631. // FeTile specifies the tile utility filter primitive
  632. // Standard reference: http://www.w3.org/TR/SVG11/filters.html#feTileElement
  633. func (svg *SVG) FeTile(fs Filterspec, in string, s ...string) {
  634. svg.printf(`<feTile %s %s`, fsattr(fs), endstyle(s, emptyclose))
  635. }
  636. // FeTurbulence specifies a turbulence filter primitive
  637. // Standard reference: http://www.w3.org/TR/SVG11/filters.html#feTurbulenceElement
  638. func (svg *SVG) FeTurbulence(fs Filterspec, ftype string, bfx, bfy float64, octaves int, seed int64, stitch bool, s ...string) {
  639. if bfx < 0 || bfx > 1 {
  640. bfx = 0
  641. }
  642. if bfy < 0 || bfy > 1 {
  643. bfy = 0
  644. }
  645. switch ftype[0:1] {
  646. case "f", "F":
  647. ftype = "fractalNoise"
  648. case "t", "T":
  649. ftype = "turbulence"
  650. default:
  651. ftype = "turbulence"
  652. }
  653. var ss string
  654. if stitch {
  655. ss = "stitch"
  656. } else {
  657. ss = "noStitch"
  658. }
  659. svg.printf(`<feTurbulence %s type="%s" baseFrequency="%.2f %.2f" numOctaves="%d" seed="%d" stitchTiles="%s" %s`,
  660. fsattr(fs), ftype, bfx, bfy, octaves, seed, ss, endstyle(s, emptyclose))
  661. }
  662. // Filter Effects convenience functions, modeled after CSS versions
  663. // Blur emulates the CSS blur filter
  664. func (svg *SVG) Blur(p float64) {
  665. svg.FeGaussianBlur(Filterspec{}, p, p)
  666. }
  667. // Brightness emulates the CSS brightness filter
  668. func (svg *SVG) Brightness(p float64) {
  669. svg.FeComponentTransfer()
  670. svg.FeFuncLinear("R", p, 0)
  671. svg.FeFuncLinear("G", p, 0)
  672. svg.FeFuncLinear("B", p, 0)
  673. svg.FeCompEnd()
  674. }
  675. // Contrast emulates the CSS contrast filter
  676. //func (svg *SVG) Contrast(p float64) {
  677. //}
  678. // Dropshadow emulates the CSS dropshadow filter
  679. //func (svg *SVG) Dropshadow(p float64) {
  680. //}
  681. // Grayscale eumulates the CSS grayscale filter
  682. func (svg *SVG) Grayscale() {
  683. svg.FeColorMatrixSaturate(Filterspec{}, 0)
  684. }
  685. // HueRotate eumulates the CSS huerotate filter
  686. func (svg *SVG) HueRotate(a float64) {
  687. svg.FeColorMatrixHue(Filterspec{}, a)
  688. }
  689. // Invert eumulates the CSS invert filter
  690. func (svg *SVG) Invert() {
  691. svg.FeComponentTransfer()
  692. svg.FeFuncTable("R", []float64{1, 0})
  693. svg.FeFuncTable("G", []float64{1, 0})
  694. svg.FeFuncTable("B", []float64{1, 0})
  695. svg.FeCompEnd()
  696. }
  697. // Saturate eumulates the CSS saturate filter
  698. func (svg *SVG) Saturate(p float64) {
  699. svg.FeColorMatrixSaturate(Filterspec{}, p)
  700. }
  701. // Sepia applies a sepia tone, emulating the CSS sepia filter
  702. func (svg *SVG) Sepia() {
  703. var sepiamatrix = [20]float64{
  704. 0.280, 0.450, 0.05, 0, 0,
  705. 0.140, 0.390, 0.04, 0, 0,
  706. 0.080, 0.280, 0.03, 0, 0,
  707. 0, 0, 0, 1, 0,
  708. }
  709. svg.FeColorMatrix(Filterspec{}, sepiamatrix)
  710. }
  711. // Animation
  712. // Animate animates the specified link, using the specified attribute
  713. // The animation starts at coordinate from, terminates at to, and repeats as specified
  714. func (svg *SVG) Animate(link, attr string, from, to int, duration float64, repeat int, s ...string) {
  715. svg.printf(`<animate %s attributeName="%s" from="%d" to="%d" dur="%gs" repeatCount="%s" %s`,
  716. href(link), attr, from, to, duration, repeatString(repeat), endstyle(s, emptyclose))
  717. }
  718. // AnimateMotion animates the referenced object along the specified path
  719. func (svg *SVG) AnimateMotion(link, path string, duration float64, repeat int, s ...string) {
  720. svg.printf(`<animateMotion %s dur="%gs" repeatCount="%s" %s<mpath %s/></animateMotion>
  721. `, href(link), duration, repeatString(repeat), endstyle(s, ">"), href(path))
  722. }
  723. // AnimateTransform animates in the context of SVG transformations
  724. func (svg *SVG) AnimateTransform(link, ttype, from, to string, duration float64, repeat int, s ...string) {
  725. svg.printf(`<animateTransform %s attributeName="transform" type="%s" from="%s" to="%s" dur="%gs" repeatCount="%s" %s`,
  726. href(link), ttype, from, to, duration, repeatString(repeat), endstyle(s, emptyclose))
  727. }
  728. // AnimateTranslate animates the translation transformation
  729. func (svg *SVG) AnimateTranslate(link string, fx, fy, tx, ty int, duration float64, repeat int, s ...string) {
  730. svg.AnimateTransform(link, "translate", coordpair(fx, fy), coordpair(tx, ty), duration, repeat, s...)
  731. }
  732. // AnimateRotate animates the rotation transformation
  733. func (svg *SVG) AnimateRotate(link string, fs, fc, fe, ts, tc, te int, duration float64, repeat int, s ...string) {
  734. svg.AnimateTransform(link, "rotate", sce(fs, fc, fe), sce(ts, tc, te), duration, repeat, s...)
  735. }
  736. // AnimateScale animates the scale transformation
  737. func (svg *SVG) AnimateScale(link string, from, to, duration float64, repeat int, s ...string) {
  738. svg.AnimateTransform(link, "scale", fmt.Sprintf("%g", from), fmt.Sprintf("%g", to), duration, repeat, s...)
  739. }
  740. // AnimateSkewX animates the skewX transformation
  741. func (svg *SVG) AnimateSkewX(link string, from, to, duration float64, repeat int, s ...string) {
  742. svg.AnimateTransform(link, "skewX", fmt.Sprintf("%g", from), fmt.Sprintf("%g", to), duration, repeat, s...)
  743. }
  744. // AnimateSkewY animates the skewY transformation
  745. func (svg *SVG) AnimateSkewY(link string, from, to, duration float64, repeat int, s ...string) {
  746. svg.AnimateTransform(link, "skewY", fmt.Sprintf("%g", from), fmt.Sprintf("%g", to), duration, repeat, s...)
  747. }
  748. // Utility
  749. // Grid draws a grid at the specified coordinate, dimensions, and spacing, with optional style.
  750. func (svg *SVG) Grid(x int, y int, w int, h int, n int, s ...string) {
  751. if len(s) > 0 {
  752. svg.Gstyle(s[0])
  753. }
  754. for ix := x; ix <= x+w; ix += n {
  755. svg.Line(ix, y, ix, y+h)
  756. }
  757. for iy := y; iy <= y+h; iy += n {
  758. svg.Line(x, iy, x+w, iy)
  759. }
  760. if len(s) > 0 {
  761. svg.Gend()
  762. }
  763. }
  764. // Support functions
  765. // coordpair returns a coordinate pair as a string
  766. func coordpair(x, y int) string {
  767. return fmt.Sprintf("%d %d", x, y)
  768. }
  769. // sce makes start, center, end coordinates string for animate transformations
  770. func sce(start, center, end int) string {
  771. return fmt.Sprintf("%d %d %d", start, center, end)
  772. }
  773. // repeatString computes the repeat string for animation methods
  774. // repeat <= 0 --> "indefinite", otherwise the integer string
  775. func repeatString(n int) string {
  776. if n > 0 {
  777. return fmt.Sprintf("%d", n)
  778. }
  779. return "indefinite"
  780. }
  781. // style returns a style name,attribute string
  782. func style(s string) string {
  783. if len(s) > 0 {
  784. return fmt.Sprintf(`style="%s"`, s)
  785. }
  786. return s
  787. }
  788. // pp returns a series of polygon points
  789. func (svg *SVG) pp(x []int, y []int, tag string) {
  790. svg.print(tag)
  791. if len(x) != len(y) {
  792. svg.print(" ")
  793. return
  794. }
  795. lx := len(x) - 1
  796. for i := 0; i < lx; i++ {
  797. svg.print(coord(x[i], y[i]) + " ")
  798. }
  799. svg.print(coord(x[lx], y[lx]))
  800. }
  801. // endstyle modifies an SVG object, with either a series of name="value" pairs,
  802. // or a single string containing a style
  803. func endstyle(s []string, endtag string) string {
  804. if len(s) > 0 {
  805. nv := ""
  806. for i := 0; i < len(s); i++ {
  807. if strings.Index(s[i], "=") > 0 {
  808. nv += (s[i]) + " "
  809. } else {
  810. nv += style(s[i]) + " "
  811. }
  812. }
  813. return nv + endtag
  814. }
  815. return endtag
  816. }
  817. // tt creates a xml element, tag containing s
  818. func (svg *SVG) tt(tag string, s string) {
  819. svg.print("<" + tag + ">")
  820. xml.Escape(svg.Writer, []byte(s))
  821. svg.println("</" + tag + ">")
  822. }
  823. // poly compiles the polygon element
  824. func (svg *SVG) poly(x []int, y []int, tag string, s ...string) {
  825. svg.pp(x, y, "<"+tag+" points=\"")
  826. svg.print(`" ` + endstyle(s, "/>\n"))
  827. }
  828. // onezero returns "0" or "1"
  829. func onezero(flag bool) string {
  830. if flag {
  831. return "1"
  832. }
  833. return "0"
  834. }
  835. // pct returns a percetage, capped at 100
  836. func pct(n uint8) uint8 {
  837. if n > 100 {
  838. return 100
  839. }
  840. return n
  841. }
  842. // islink determines if a string is a script reference
  843. func islink(link string) bool {
  844. return strings.HasPrefix(link, "http://") || strings.HasPrefix(link, "#") ||
  845. strings.HasPrefix(link, "../") || strings.HasPrefix(link, "./")
  846. }
  847. // group returns a group element
  848. func group(tag string, value string) string { return fmt.Sprintf(`<g %s="%s">`, tag, value) }
  849. // scale return the scale string for the transform
  850. func scale(n float64) string { return fmt.Sprintf(`scale(%g)`, n) }
  851. // scaleXY return the scale string for the transform
  852. func scaleXY(dx, dy float64) string { return fmt.Sprintf(`scale(%g,%g)`, dx, dy) }
  853. // skewx returns the skewX string for the transform
  854. func skewX(angle float64) string { return fmt.Sprintf(`skewX(%g)`, angle) }
  855. // skewx returns the skewX string for the transform
  856. func skewY(angle float64) string { return fmt.Sprintf(`skewY(%g)`, angle) }
  857. // rotate returns the rotate string for the transform
  858. func rotate(r float64) string { return fmt.Sprintf(`rotate(%g)`, r) }
  859. // translate returns the translate string for the transform
  860. func translate(x, y int) string { return fmt.Sprintf(`translate(%d,%d)`, x, y) }
  861. // coord returns a coordinate string
  862. func coord(x int, y int) string { return fmt.Sprintf(`%d,%d`, x, y) }
  863. // ptag returns the beginning of the path element
  864. func ptag(x int, y int) string { return fmt.Sprintf(`<path d="M%s`, coord(x, y)) }
  865. // loc returns the x and y coordinate attributes
  866. func loc(x int, y int) string { return fmt.Sprintf(`x="%d" y="%d"`, x, y) }
  867. // href returns the href name and attribute
  868. func href(s string) string { return fmt.Sprintf(`xlink:href="%s"`, s) }
  869. // dim returns the dimension string (x, y coordinates and width, height)
  870. func dim(x int, y int, w int, h int) string {
  871. return fmt.Sprintf(`x="%d" y="%d" width="%d" height="%d"`, x, y, w, h)
  872. }
  873. // fsattr returns the XML attribute representation of a filterspec, ignoring empty attributes
  874. func fsattr(s Filterspec) string {
  875. attrs := ""
  876. if len(s.In) > 0 {
  877. attrs += fmt.Sprintf(`in="%s" `, s.In)
  878. }
  879. if len(s.In2) > 0 {
  880. attrs += fmt.Sprintf(`in2="%s" `, s.In2)
  881. }
  882. if len(s.Result) > 0 {
  883. attrs += fmt.Sprintf(`result="%s" `, s.Result)
  884. }
  885. return attrs
  886. }
  887. // tablevalues outputs a series of values as a XML attribute
  888. func (svg *SVG) tablevalues(s string, t []float64) {
  889. svg.printf(` %s="`, s)
  890. for i := 0; i < len(t)-1; i++ {
  891. svg.printf("%g ", t[i])
  892. }
  893. svg.printf(`%g"%s`, t[len(t)-1], emptyclose)
  894. }
  895. // imgchannel validates the image channel indicator
  896. func imgchannel(c string) string {
  897. switch c {
  898. case "R", "G", "B", "A":
  899. return c
  900. case "r", "g", "b", "a":
  901. return strings.ToUpper(c)
  902. case "red", "green", "blue", "alpha":
  903. return strings.ToUpper(c[0:1])
  904. case "Red", "Green", "Blue", "Alpha":
  905. return c[0:1]
  906. }
  907. return "R"
  908. }