A Go library for SVG generation

1,635 views
Skip to first unread message

ajstarks

unread,
Mar 20, 2010, 1:53:41 PM3/20/10
to golang-nuts
(This message is a request for comments from the Go community on the
SVG library and 2D graphics APIs for Go.)

SVGo is a Go library that generates SVG as defined by the Scalable
Vector Graphics 1.1 Specification (http://www.w3.org/TR/SVG11/) to
standard output.

The code is hosted at http://github.com/ajstarks/svgo and example
outputs can be seen at http://www.flickr.com/photos/ajstarks/sets/72157623441699483/detail/

== Supported SVG elements ==

circle, ellipse, polygon, polyline, rect (including roundrects),
paths (arc,
cubic and quadratic bezier paths), line, image, text,

== Metadata elements ==

desc, defs, g (style, transform, id), title, (a)ddress, use

== Building and Usage ==

See svgdef.[svg|png|pdf] for a graphical view of the function calls

Usage: (where $GC and $GL are the Go compiler and linker for your
targer architecture, $A)

$ $GC svg.go = compile the library
$ $GC svgdef.go && $GL -o svgdef svgdef.$A = compile a client
program
$ ./svgdef = run the client program


a minimal program:


package main
import "svg"

func main() {
svg.Start(500, 500)
svg.Circle(250, 250, 100)
svg.End()
}

You may view the SVG output with a browser that supports SVG (tested
on Chrome, Opera, Firefox and Safari), or any other SVG user-agent
such as Batik Squiggle. The test-svgo script tries to use reasonable
defaults based on the GOOS and GOARCH environment variables

The command:

$ ./newsvg foo.go

creates a template Go source file ready for your code.


== Package contents ==

svg.go Library
test-svgo Compiles the library, builds the clients and
displays the results||
newsvg Coding template command||
svgdef.go Creates a SVG representation of the API||
vismem.go Visualize data from files||
randcomp.go Compare random number generators||
planets.go Show the scale of the Solar system||
rl.go Random lines (port of a Processing demo)||
imfade.go Show image fading||
images/* Images used by the client programs||


== Functions ==

Many functions use x, y to specify an object's location, and w, h to
specify the object's width and height.
Where applicable, a final optional argument specifies the style to be
applied to the object.
The style strings follow the SVG standard; name:value pairs delimited
by semicolons.
For example:"fill:none; opacity:0.3" (see: http://www.w3.org/TR/SVG11/styling.html)


=== Structure, Metadata and Links ===

Start(w int, h int)
begin the SVG document with the width w and height h
http://www.w3.org/TR/SVG11/struct.html=SVGElement

End()
end the SVG document

Gstyle(s string)
begin a group, with the specified style
http://www.w3.org/TR/SVG11/struct.html=GElement

Gtransform(s string)
begin a group, with the specified transform

Gid(s string)
begin a group, with the specified id

Gend()
end the group (must be paired with Gstyle, Gtransform, Gid)

Def()
begin a definition block
http://www.w3.org/TR/SVG11/struct.html=DefsElement

DefEnd()
end a definition block

Desc(s string)
specify the text of the description
http://www.w3.org/TR/SVG11/struct.html=DescElement

Title(s string)
specify the text of the title
http://www.w3.org/TR/SVG11/struct.html=TitleElement

Link(name string, title string)
begin a link named "name", with the specified title
http://www.w3.org/TR/SVG11/linking.html=Links

LinkEnd()
end the link

Use(x int, y int, link string, s ...string)
place the object referenced at link at the location x, y
http://www.w3.org/TR/SVG11/struct.html=UseElement

=== Shapes ===

Circle(x int, y int, r int, s ...string)
draw a circle, centered at x,y with radius r
http://www.w3.org/TR/SVG11/shapes.html=CircleElement

Ellipse(x int, y int, w int, h int, s ...string)
draw an ellipse, centered at x,y with radii w, and h
http://www.w3.org/TR/SVG11/shapes.html=EllipseElement

Polygon(x []int, y []int, s ...string)
draw a series of line segments using an array of x, y coordinates
http://www.w3.org/TR/SVG11/shapes.html=PolygonElement

Rect(x int, y int, w int, h int, s ...string)
draw a rectangle with upper left-hand corner at x,y, with width w,
and height h
http://www.w3.org/TR/SVG11/shapes.html=RectElement

Roundrect(x int, y int, w int, h int, rx int, ry int, s ...string)
draw a rounded rectangle with upper the left-hand corner at x,y,
with width w, and height h. The radii for the rounded portion
is specified by rx (width), and ry (height)

Square(x int, y int, s int, style ...string)
draw a square with upper left corner at x,y with sides of length s

=== Paths ===

Arc(sx int, sy int, ax int, ay int, r int, large bool, sweep bool, ex
int, ey int, s ...string)
draw an elliptical arc beginning coordinate at sx,sy, ending
coordinate at ex, ey
width and height of the arc are specified by ax, ay, the x axis
rotation is r
if sweep is true, then the arc will be drawn in a "positive-angle"
direction (clockwise), if false,
the arc is drawn counterclockwise.
if large is true, the arc sweep angle is greater than or equal to
180 degrees,
otherwise the arc sweep is less than 180 degrees
http://www.w3.org/TR/SVG11/paths.html=PathDataEllipticalArcCommands

Bezier(sx int, sy int, cx int, cy int, px int, py int, ex int, ey int,
s ...string)
draw a cubic bezier curve, beginning at sx,sy, ending at ex,ey
with control points at cx,cy and px,py
http://www.w3.org/TR/SVG11/paths.html=PathDataCubicBezierCommands

Qbezier(sx int, sy int, cx int, cy int, ex int, ey int, tx int, ty
int, s ...string)
draw a quadratic bezier curve, beginning at sx, sy, ending at tx,ty
with control points are at cx,cy, ex,ey
http://www.w3.org/TR/SVG11/paths.html=PathDataQuadraticBezierCommands

=== Lines ===

Line(x1 int, y1 int, x2 int, y2 int, s ...string)
draw a line segment between x1,y1 and x2,y2
http://www.w3.org/TR/SVG11/shapes.html=LineElement

Polyline(x []int, y []int, s ...string)
draw a polygon using coordinates specified in x,y arrays
http://www.w3.org/TR/SVG11/shapes.html=PolylineElement

=== Image and Text ===

Image(x int, y int, w int, h int, link string, s ...string)
place at x,y (upper left hand corner), the image with width w, and
height h, referenced at link
http://www.w3.org/TR/SVG11/struct.html=ImageElement

Text(x int, y int, t string, s ...string)
Place the specified text, t at x,y according to the style specified
in s
http://www.w3.org/TR/SVG11/text.html=TextElement

=== Color ===

RGB(r int, g int, b int) string
creates a style string for the fill color designated
by the (r)ed, g(reen), (b)lue components
http://www.w3.org/TR/css3-color/

RGBA(r int, g int, b int, a float) string
as above, but includes the color's opacity as a value
between 0.0 (fully transparent) and 1.0 (opaque)

=== Utility ===

Grid(x int, y int, w int, h int, n int, s ...string)
draws a grid of straight lines starting at x,y, with a width w, and
height h, and a size of n

Russ Cox

unread,
Mar 20, 2010, 3:54:53 PM3/20/10
to ajstarks, golang-nuts
This is nice. It would be great to have good support for SVG.
I wonder how far we can push it.

The current code writes to standard output, so it wouldn't work
inside, say, a web server generating pictures on demand.

It might be more general to have something like

func New(w io.Writer) *SVG

and then make all the top-level functions you have
methods on SVG.

Also, I wonder if maybe instead of the many functions
it might make sense to have many types instead, and then
a program can manipulate a picture as a data structure,
calling into the SVG package to marshal the data structure
out in wire format. The data structures would probably be a
better fit for the grouping concept (just a recursive SVG),
and package xml might make this very easy.
(And if it doesn't, maybe it would be a good test case for
making package xml more useful. See the xmpp code I
posted last week for an example of what I mean.)

It would be even cooler if the package could read SVG
back into memory, creating an equivalent data structure,
so that one could do SVG transformations or embed SVG
pictures inside other pictures.

Russ

Andrew Gerrand

unread,
Mar 21, 2010, 7:36:14 PM3/21/10
to r...@golang.org, ajstarks, golang-nuts
Nice package.

I'd like to reiterate this point:

On 21 March 2010 06:54, Russ Cox <r...@golang.org> wrote:
> The current code writes to standard output, so it wouldn't work
> inside, say, a web server generating pictures on demand.
>
> It might be more general to have something like
>
>    func New(w io.Writer) *SVG
>
> and then make all the top-level functions you have
> methods on SVG.

I would actually like to use this library inside a web server to make
a simple web-based strategy game. Without this change it wouldn't be
possible.

Andrew

Jonathan Wright

unread,
Mar 21, 2010, 10:26:00 PM3/21/10
to Andrew Gerrand, golang-nuts

Take a look at the svg.go. Ajstarks has done a good job of making it
extremely simple.

I almost had it converted over to use a writer last night, but wife
demanded attention. Would you be interested in a copy of the patch
when it is ready?

Jonathan.

ajstarks

unread,
Mar 22, 2010, 7:36:48 AM3/22/10
to golang-nuts
Yes, Please submit the patches.

ajstarks

unread,
Mar 22, 2010, 7:42:35 AM3/22/10
to golang-nuts
FYI, the links are incorrect (=) should be #

On Mar 20, 1:53 pm, ajstarks <ajsta...@gmail.com> wrote:
> (This message is a request for comments from the Go community on the
> SVG library and 2D graphics APIs for Go.)
>
> SVGo is a Go library that generates SVG as defined by the Scalable
> Vector Graphics 1.1 Specification (http://www.w3.org/TR/SVG11/) to
> standard output.
>

> The code is hosted athttp://github.com/ajstarks/svgoand example
> outputs can be seen athttp://www.flickr.com/photos/ajstarks/sets/72157623441699483/detail/

Jonathan Wright

unread,
Mar 22, 2010, 3:27:36 PM3/22/10
to ajstarks, golang-nuts
http://github.com/quag/svgo/tree/writer contains a patch[1] that
switches over to use an SVG object and writer.

The branch also contains two other patches, one for gofmting the
examples, and another for preventing rm failed error messages the
first time test-svgo is run.

[1] http://github.com/quag/svgo/commit/b787f6d95291af55bad12d03c361996f964134ee

Thanks,
Jonathan Wright.

> To unsubscribe from this group, send email to golang-nuts+unsubscribegooglegroups.com or reply to this email with the words "REMOVE ME" as the subject.
>

David Roundy

unread,
Mar 22, 2010, 3:36:54 PM3/22/10
to Jonathan Wright, ajstarks, golang-nuts
This looks pretty cool! What'll be *extra* cool is when we get a
second library with overlapping functionality (e.g. a gui library, or
maybe a pdf or bitmap library), so that one can write functions that
will either draw to a file or to the screen.

I would find the examples easier to read and understand, by the way,
if you didn't define "var svg" at the top level. It originally didn't
occur to me to look outside the main function, and I thought I was
looking at the wrong examples or the wrong version of the code.

David

Jonathan Wright

unread,
Mar 22, 2010, 3:58:42 PM3/22/10
to David Roundy, ajstarks, golang-nuts
On Tue, Mar 23, 2010 at 8:36 AM, David Roundy
<rou...@physics.oregonstate.edu> wrote:
> This looks pretty cool! What'll be *extra* cool is when we get a
> second library with overlapping functionality (e.g. a gui library, or
> maybe a pdf or bitmap library), so that one can write functions that
> will either draw to a file or to the screen.

That would be cool.

> I would find the examples easier to read and understand, by the way,
> if you didn't define "var svg" at the top level.  It originally didn't
> occur to me to look outside the main function, and I thought I was
> looking at the wrong examples or the wrong version of the code.

Quite right. The "var svg" hack is there as I didn't want to disturb
Antony's examples when putting in the writer change.

Antony, how do you feel about passing around svg pointers every where?

Thanks,
Jonathan.

Bob Cunningham

unread,
Mar 22, 2010, 5:13:54 PM3/22/10
to golang-nuts
On 03/20/2010 10:53 AM, ajstarks wrote:
> (This message is a request for comments from the Go community on the
> SVG library and 2D graphics APIs for Go.)
>
> SVGo is a Go library that generates SVG as defined by the Scalable
> Vector Graphics 1.1 Specification (http://www.w3.org/TR/SVG11/) to
> standard output.
>
> The code is hosted at http://github.com/ajstarks/svgo and example
> outputs can be seen at http://www.flickr.com/photos/ajstarks/sets/72157623441699483/detail/
>

Along with the comments and code posted by others, I'd like to toss in
another use-case:

Last year I was working on a mobile radiation detection instrument for
Homeland Security that needed to report its results not only on a local
GUI and local wireless PDAs, but also to "higher authority".
Unfortunately, no implementable standards for upstream reporting existed
(the closest in trial use was CAP - the Common Alerting Protocol).

The project ended last year, with no upstream protocol ever being
designed or implemented. But the problem has been nagging me ever
since. Since I'm primarily a sensor and processing/analysis wonk
(deeply embedded software), I had little clue what an upstream
high-level protocol should look like (from DHS's perspective). I knew
the data stream needed to be easy to archive, and fully tolerant of
latency, interruptions, and loss. It had to be effective over
intermittent one-way networks (no acks), yet permit some level of
interaction (when available or possible).

First, I knew I had to send live instrument data, along with at least
some meta-data (units, timestamps, etc.). For simplicity and
convenience, I chose a very simple XML document with an equally simple
DTD, so the data would be both human and machine readable. However, a
textual list of data would completely fail to communicate to the distant
listener the level of "situational awareness" the data contained. So I
decided to send a snapshot of the GUI, which would give a useful "big
picture view" to non-experts. I also wanted to eliminate any need to
install instrument-specific software outside of the instrument itself,
and to minimize demands on what will often be an unreliable upstream
network.

With the live data in XML, creating a GUI snapshot in SVG seemed to be a
no-brainer. Unfortunately, I have little experience with XML or SVG,
and my initial efforts (in Inkscape) were very crude (and were probably
Just Plain Wrong). But I did learn a few things, and most of all I
found myself wishing for an XML/SVG layer that would support and
simplify the following:

1. Send "live" XML data packets at full instrument rate (1 Hz), in plain
text.
2. Send XML instrument configuration and status information less often.
2. Send the DTD less often, in plain text.
3. Send an SVG snapshot of the GUI (occasionally), in plain text
(viewable in any SVG viewer).
4. Send an SVG template of the primary display screen that can be
parameterized by the XML data (to permit a remote GUI to update a GUI
image at full data rate without saturating the network with snapshots).
Since this assumes a higher level of end-processing, it can be sent gzipped.
5. Send additional gzipped SVG screen templates for other live data
views, and to view configuration and status information.
6. Send an "interaction overlay" (in ecmascript?) to permit a listener
with a basic browser to locally select between "live" SVG screens
(pseudo-interaction).
7. Send an "operator overlay" to permit a listener/talker to remotely
control some aspects of instrument operation (with access control), and
to communicate with local users/operators. This is the only layer that
requires a secure two-way connection.

Again, I'm pretty much an XML/SVG newbie, so a brain-dead simple
implementation strategy is highly desirable! Which leads to my main
question:

Is (or will) the SVGo package be suitable/helpful for generating some or
all of the above?

While my original project is dead and gone, it seems the above stack
would have general applicability ranging from my specific instrument
reporting example all the way up to remote interfaces for networks of
systems (for things like IT network maintenance). I suppose it could be
viewed as a kind of archivable RDP (remote desktop protocol) that works
at various levels of interaction.

And with Go, supporting multiple remote interactive users should be
effortless.


-BobC

Anthony Starks

unread,
Mar 22, 2010, 6:22:02 PM3/22/10
to golang-nuts, Jonathan Wright
I'm digging the changes. I like how you were able to get the I/O generality with minimal change to the examples.
How about an approach where a client would do:

svg.Start(width, height) // by default use stdout

and if you wanted to use it in a web server as Russ and Andrew have suggested:

svg.Start(width, height, w)

where w is your web server's io.Writer

David Roundy

unread,
Mar 22, 2010, 7:32:49 PM3/22/10
to Anthony Starks, golang-nuts, Jonathan Wright
On Mon, Mar 22, 2010 at 3:22 PM, Anthony Starks <ajst...@gmail.com> wrote:
> I'm digging the changes.  I like how you were able to get the I/O generality with minimal change to the examples.
> How about an  approach where a client would do:
>
>        svg.Start(width, height)   // by default use stdout
>
> and if you wanted to use it in a web server as Russ and Andrew have suggested:
>
>        svg.Start(width, height, w)
>
> where w is your web server's  io.Writer

The problem with that is that you can't then easily create two or more
svg outputs from one program. Often I'd like to create several plots
from the same data, and it'd be nice to be able to do that in
parallel. e.g. imagine reading some very large file (won't fit in
memory, maybe?) to plot every nth datum or to plot a moving average.
It's not a show stopper, but having an SVG object seems to me much the
cleaner approach. It also keeps the API flexible for if you want to
generate a figure that is used several times within a larger figure.

David

ajstarks

unread,
Mar 22, 2010, 7:36:46 PM3/22/10
to golang-nuts
It would appear that SVG would work for your use-case. You could
generate it directly or as a transformation of your private XML. The
recent changes to SVGo discussed here would add the generality for
streaming the SVG anywhere. As to a Go implementation, of course that
would depend on Go support for your platform.

On Mar 22, 5:13 pm, Bob Cunningham <FlyM...@gmail.com> wrote:
> On 03/20/2010 10:53 AM, ajstarks wrote:
>
> > (This message is a request for comments from the Go community on the
> > SVG library and 2D graphics APIs for Go.)
>
> > SVGo is a Go library that generates SVG as defined by the Scalable
> > Vector Graphics 1.1 Specification (http://www.w3.org/TR/SVG11/) to
> > standard output.
>

> > The code is hosted athttp://github.com/ajstarks/svgoand example
> > outputs can be seen athttp://www.flickr.com/photos/ajstarks/sets/72157623441699483/detail/

Anthony Starks

unread,
Mar 22, 2010, 8:04:54 PM3/22/10
to David Roundy, golang-nuts, Jonathan Wright
I get your point on SVG objects.
note that since w is an optional parameter, it's an array, and you could manage the different streams

If you want to reuse objects, here is an edited example from svgdef.go

const objstyle = "fill:none; stroke-width:2; stroke:rgb(127,0,0); opacity:0.75"
const textsize = 15

func defrect(id string, w int, h int, legend string) {
svg.Gid(id)
svg.Rect(0, 0, w, h, objstyle)
svg.Text(-textsize, (h / 2), "h", legendstyle)
svg.Text((w / 2), -textsize, "w", legendstyle)
svg.Gend()
}

func main() {

...

defrect("rectangle", 160, 100, "svg.Rect(x, y, w, h,...)") // define initial object
svg.Use(40, 80, "#rectangle") // initial use
svg.Use(400, 200, '#rectangle") // use it again
svg.Gtransform("rotate(30)") // use it again, with a transformation
svg.Use(100, 200, "#rectangle")
svg.Gend()

Russ Cox

unread,
Mar 22, 2010, 8:24:19 PM3/22/10
to Anthony Starks, golang-nuts, Jonathan Wright
It really should be an object with a constructor
function and then all methods. Squirreling away
global state in a language with such good
concurrency support is asking for trouble.
Making it optional is also asking for trouble.
It's trivial to change code using the current
global interface: you add one line to construct
it and then change svg. to x. throughout.

Russ

ajstarks

unread,
Mar 22, 2010, 8:56:38 PM3/22/10
to golang-nuts
cool. I get it.

ajstarks

unread,
Mar 23, 2010, 1:10:25 AM3/23/10
to golang-nuts
Github updated with Writer interface, two new examples.

andbelo

unread,
Apr 19, 2010, 10:16:10 AM4/19/10
to golang-nuts
Congrats Anthony, it's a nice work.

I started thinking about how to implement the SVG types and shapes as
Go types to use XML as suggested by Russ. I came up with a first draft
that I would like to share for discussion and improvement (see
svg_types.go below).

While doing that I have ran into problems with XML. More specifically:

1. package xml imports child names in arrays of particular types,
therefore loosing the original order. I read this is an inherent bug
in XML (http://golang.org/pkg/xml/#Bugs). That would not work to read
this simple example in which the order is important:

<svg x="200" y="200">
<circle cx="100" cy="100" r="100" fill="green"/>
<rect x="30" y="30" width="140" height="140" fill="yellow" />
<circle cx="100" cy="100" r="50" fill="red" />
</svg>

I was thinking that the following structure could help to address this
problem, provide as some modifications are made on the package XML.
Please note the first field, with name "svg", to be recognized from a
XML document and also to be exported as a svg name into XML. It has
type xml.Name. All other fields would be XML attributes not requiring
the tag "attr", which is currently used to read XML documents by the
xml package. Finally, one field named XMLChildName with type
[]xml.Name could store all child XML names, which by consequence would
be other structures with a xml.Name field. That would solve the
problem of not keeping the order of the XML names, but I'm not sure
that it can be implemented as I am not familiar with details of the
package xml.

type SVG struct {
svg xml.Name // field name to be used as XML Name
X, Y Coordinate
Width, Height Length
ViewBox string
PreserveAspectRatio string // 'xMidYMid meet'
ZoomAndPan string // 'magnify'
Version string // '1.1'
BaseProfile string
ContentScriptType string // 'text/ecmascript'
ContentStyleType string // 'text/css'
XMLChildName []xml.Name // reads all child XML names into this
array keeping the order
}


2. While reading a XML document with package xml, the fields in the Go
structure are required to be of type string. It would be nice if
functions or methods could be written to allow the transformation of
the strings coming from XML to the appropriate types, so that when the
type is manipulated later on, it already has the proper types in it.


These are some general thoughts, please, comments are very welcome.
Cheers


// svg_types.go

package svg

import (
"fmt"
"io"
"xml"
)

// SVG basic data types

// Store an angle in any of the "degree", "rad", or "grad" units.
type Angle struct {
n float
angle string
}

func (a *Angle) String() string {
return fmt.Sprint("%f%s", a.n, a.angle)
}

// Store a color in RGB format.
type Color struct {
r, g, b uint8
}

func (c *Color) String() string {
return fmt.Sprint("rgb(%d, %d, %d)", c.r, c.g, c.b)
}

// Store a coordinate point in the graph.
type Coordinate struct {
x, y Length
}

func (c *Coordinate) String() string {
return fmt.Sprint("%s, %s", c.x, c.y)
}

// Store frequency, which can be expressed in either "Hz" or "kHz".
type Frequency struct {
n float
frequency string
}

func (f *Frequency) String() string {
return fmt.Sprint("%f%s", f.n, f.frequency)
}

// Stores a distance measure without unit or with any of the "em",
"ex", "px",
// "cm", "mm", "in", "pt", "pc", or "%" units.
type Length struct {
n float
unit string
}

func (l *Length) String() string {
return fmt.Sprint("%f%s", l.n, l.unit)
}

// Stores a paint specification for the fill or stroke of a graphic
element.
type Paint string

// Stores a percentage value, which is a particular case of the type
of Length.
type Percentage struct {
n float
percent string
}

func (p *Percentage) String() string {
return fmt.Sprint("%s%", p.n)
}

// Stores a point.
type Point struct {
x, y float
}

func (p *Point) String() string {
return fmt.Sprint("%f,%f", p.x, p.y)
}

// Stores errors related to SVG read/write.
type SVGError struct {
caller string
error string
}

func (s *SVGError) String() string {
return fmt.Sprint("%s: %s", s.caller, s.error)
}

// Stores a time length in either "ms" or "s".
type Time struct {
n float
time string
}

func (t *Time) String() string {
return fmt.Sprint("%f%s", t.n, t.time)
}

// Stores a list of modifications of a graphic element.
type TransformList string

// String with address to resource on the web.
type URI string

// SVG basic shapes.

type Circle struct {
XMLName xml.Name "circle"
Cx, Cy Coordinate "attr"
R Length "attr"
Transform TransformList "attr"
}

type Ellipse struct {
XMLName xml.Name "ellipse"
Cx, Cy Coordinate "attr"
Rx, Ry Length "attr"
Transform TransformList "attr"
}

type Line struct {
XMLName xml.Name "line"
X1, Y1 Coordinate "attr"
X2, Y2 Coordinate "attr"
Transform TransformList "attr"
}

type Polyline struct {
XMLName xml.Name "polyline"
Points []Point "attr"
Transform TransformList "attr"
}

type Polygon struct {
XMLName xml.Name "polygon"
Points []Point "attr"
Transform TransformList "attr"
}

type Rect struct {
XMLName xml.Name "rect"
X, Y Coordinate "attr"
Width, Height Length "attr"
Rx, Ry Length "attr"
Transform TransformList "attr"
}

// SVG elements.

type SVG struct {
svg xml.Name // field name to be used as XML Name
X, Y Coordinate
Width, Height Length
ViewBox string
PreserveAspectRatio string // 'xMidYMid meet'
ZoomAndPan string // 'magnify'
Version string // '1.1'
BaseProfile string
ContentScriptType string // 'text/ecmascript'
ContentStyleType string // 'text/css'
XMLChildName []xml.Name // reads all child XML names into this
array keeping the order
}

func ReadSVG(r io.Reader) (s *SVG, err SVGError) {
//TODO
return s, err
}

func (s *SVG) WriteSVG(w io.Writer) (err SVGError) {
//TODO
return err
}


--
Subscription settings: http://groups.google.com/group/golang-nuts/subscribe?hl=en

ajstarks

unread,
Apr 19, 2010, 7:31:16 PM4/19/10
to golang-nuts
Thanks! How do you see client programs using this new design?

On Apr 19, 10:16 am, andbelo <andb...@gmail.com> wrote:
> Congrats Anthony, it's a nice work.

Thanks!

>
> I started thinking about how to implement the SVG types and shapes as
> Go types to use XML as suggested by Russ. I came up with a first draft
> that I would like to share for discussion and improvement (see
> svg_types.go below).

Interesting. How do you see client programs using this design?

Raif S. Naffah

unread,
Apr 20, 2010, 5:19:58 AM4/20/10
to andbelo, golang-nuts
hello andbelo,

few comments (in-lined) on the xml package.

On Mon, 2010-04-19 at 07:16 -0700, andbelo wrote:
...


> 2. While reading a XML document with package xml, the fields in the Go
> structure are required to be of type string.

the xml.Unmarshal will handle scalar types. see the scalar tests in
xml_test.go.


> It would be nice if
> functions or methods could be written to allow the transformation of
> the strings coming from XML to the appropriate types, so that when the
> type is manipulated later on, it already has the proper types in it.

that is in the pipe-line. see this thread for how it will be addressed,
hopefully in the near future:

http://groups.google.com/group/golang-nuts/browse_thread/thread/7c0a439ed54d964d/e7ac10187dfdc387?lnk=gst&q=xml#e7ac10187dfdc387


cheers;
rsn

signature.asc

andbelo

unread,
Apr 21, 2010, 10:32:19 PM4/21/10
to golang-nuts
> Thanks! How do you see client programs using this new design?


As far as I know, the client programs can use the library in the same
way they currently do as long as proper methods are defined for the
types.

The advantages are that the use of types allow reading data from a
reader and also editing the data. For example, if you have a type
named circle, you can read a .svg file and store the information of
the circle in the type, then edit the circle (i.e. change colors,
size, etc...), and finally send it to a writer.

I'm trying to get a working program. As soon as I get it I will post
in the list for further discussion.

andbelo

unread,
Apr 26, 2010, 11:08:44 AM4/26/10
to golang-nuts
I got a working example, which is not complete because some elements
are still missing but it servers the purpose of showing some ideas.
There are 2 files.

The first (write.go) is a file to be included in the package xml to
marshal(encode) xml elements. It has to be compiled with the xml.go
and read.go files of that package (i.e. put xml.go, read.go, and
write.go in a folder and type "6g -I folder/path xml.go read.go
write.go). The write.go file implements an interface to handle the
marshalling and some auxiliary functions and methods. I got these
ideas from the code in json that implements marshalling.

The second (svg.go) contain the SVG types and shapes, and also
functions and methods to export them.

The example in svg_test.go is a small program using the svg.go.

Do you think this might be of any interest? Also, do you have any
suggestions for improvement?
Please, let me know if you have any suggestion.
Thanks


write.go:

// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package xml

import (
"bufio"
"io"
"os"
)

var (
BufferSize = 4096

escStringQuot = "&#34;" // shorter than "&quot;"
escStringApos = "&#39;" // shorter than "&apos;"
escStringAmp = "&amp;"
escStringLt = "&lt;"
escStringGt = "&gt;"
)

// Escape writes to w the properly escaped XML equivalent
// of the plain text data s.
func EscapeString(s string) string {
b := ""
for _, c := range s {
switch c {
case '"':
b += escStringQuot
case '\'':
b += escStringApos
case '&':
b += escStringAmp
case '<':
b += escStringLt
case '>':
b += escStringGt
default:
b += string(c)
}
}
return b
}

// Marshal encodes an XML element into XML by calling its method
MarshalXML. It
// writes the output to io.Writer, returning the number of bytes
written and an
// os.Error if any error is found.
func Marshal(w io.Writer, m Marshaler) (int, os.Error) {
out, err := newBuffer(w)
if err != nil {
return 0, err
}
s, err := m.MarshalXML()
if err != nil {
return 0, err
}
n, err := writeBuffer(out, s)
return n, err
}

// MarshalIndent is like Marshal but applies Indent to the XML output.
func MarshalIndent(w io.Writer, m Marshaler, prefix, indent string)
(int, os.Error) {
out, err := newBuffer(w)
if err != nil {
return 0, err
}
s, err := m.MarshalXMLIndent(prefix, indent)
if err != nil {
return 0, err
}
n, err := writeBuffer(out, s)
return n, err
}

// Create a new buffer to export the XML marshaled code.
func newBuffer(w io.Writer) (*bufio.Writer, os.Error) {
out, err := bufio.NewWriterSize(w, BufferSize)
if err != nil {
return nil, err
}
return out, err
}

// Write the XML marshaled code to the io.Writer, break the string in
chunks
// corresponding to the buffer size.
func writeBuffer(out *bufio.Writer, s string) (int, os.Error) {
n := 0
for len(s) > BufferSize {
c, err := out.WriteString(string(s[0: BufferSize - 1]))
if err != nil {
return 0, err
}
err = out.Flush()
if err != nil {
return BufferSize, err
}
n += c
s = string(s[BufferSize:])
}
b, err := out.WriteString(s)
if err != nil {
return 0, err
}
err = out.Flush()
if err != nil {
return n, err
}
return n + b, err
}

// Marshaler is the interface implemented by objects that can marshal
themselves
// into valid XML.
type Marshaler interface {
MarshalXML() (string, os.Error)
MarshalXMLIndent(prefix, indent string) (string, os.Error)
}

// Specific methods and functions to marshal parts of the XML element.

func (n Name) String() string {
if n.Space != "" {
return n.Space + ":" + n.Local
}
return n.Local
}

func (a Attr) String() string {
return a.Name.String() + `="` + a.Value + `"`
}

func (s StartElement) String() string {
name := s.Name.String()
attr := ""
for _, a := range s.Attr {
attr += a.String()
}
return name + attr
}

func (e EndElement) String() string {
return e.Name.String()
}

func MarshalAttr(a []Attr) string {
s := ""
for _, att := range a {
s += " " + att.String()
}
return s
}

func MarshalInner(i []Marshaler) (string, os.Error) {
s := ""
for _, m := range i {
c, err := m.MarshalXML()
if err != nil {
return "", err
}
s += c
}
return s, nil
}

func MarshalInnerIndent(i []Marshaler, prefix, indent string) (string,
os.Error) {
s := ""
for _, m := range i {
c, err := m.MarshalXMLIndent(prefix, indent)
if err != nil {
return "", err
}
s += c
}
return s, nil
}

====================================================
svg.go:


// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Provides functions and methods to create, read, manipulate, and
export SVG
// as specified at http://www.w3.org/TR/SVG11.
package svg

import (
"fmt"
"os"
"strings"
"xml"
)

var (
XMLDeclaration = `<?xml version="1.0"?>`
SVGDocumentTypeDeclaration = `<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG
1.1//EN" ` +
`"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">`
XMLNS = `http://www.w3.org/2000/svg`

Aliceblue = "rgb(240, 248, 255)"
Antiquewhite = "rgb(250, 235, 215)"
Aqua = "rgb( 0, 255, 255)"
Aquamarine = "rgb(127, 255, 212)"
Azure = "rgb(240, 255, 255)"
Beige = "rgb(245, 245, 220)"
Bisque = "rgb(255, 228, 196)"
Black = "rgb( 0, 0, 0)"
Blanchedalmond = "rgb(255, 235, 205)"
Blue = "rgb( 0, 0, 255)"
Blueviolet = "rgb(138, 43, 226)"
Brown = "rgb(165, 42, 42)"
Burlywood = "rgb(222, 184, 135)"
Cadetblue = "rgb( 95, 158, 160)"
Chartreuse = "rgb(127, 255, 0)"
Chocolate = "rgb(210, 105, 30)"
Coral = "rgb(255, 127, 80)"
Cornflowerblue = "rgb(100, 149, 237)"
Cornsilk = "rgb(255, 248, 220)"
Crimson = "rgb(220, 20, 60)"
Cyan = "rgb( 0, 255, 255)"
Darkblue = "rgb( 0, 0, 139)"
Darkcyan = "rgb( 0, 139, 139)"
Darkgoldenrod = "rgb(184, 134, 11)"
Darkgray = "rgb(169, 169, 169)"
Darkgreen = "rgb( 0, 100, 0)"
Darkgrey = "rgb(169, 169, 169)"
Darkkhaki = "rgb(189, 183, 107)"
Darkmagenta = "rgb(139, 0, 139)"
Darkolivegreen = "rgb( 85, 107, 47)"
Darkorange = "rgb(255, 140, 0)"
Darkorchid = "rgb(153, 50, 204)"
Darkred = "rgb(139, 0, 0)"
Darksalmon = "rgb(233, 150, 122)"
Darkseagreen = "rgb(143, 188, 143)"
Darkslateblue = "rgb( 72, 61, 139)"
Darkslategray = "rgb( 47, 79, 79)"
Darkslategrey = "rgb( 47, 79, 79)"
Darkturquoise = "rgb( 0, 206, 209)"
Darkviolet = "rgb(148, 0, 211)"
Deeppink = "rgb(255, 20, 147)"
Deepskyblue = "rgb( 0, 191, 255)"
Dimgray = "rgb(105, 105, 105)"
Dimgrey = "rgb(105, 105, 105)"
Dodgerblue = "rgb( 30, 144, 255)"
Firebrick = "rgb(178, 34, 34)"
Floralwhite = "rgb(255, 250, 240)"
Forestgreen = "rgb( 34, 139, 34)"
Fuchsia = "rgb(255, 0, 255)"
Gainsboro = "rgb(220, 220, 220)"
Ghostwhite = "rgb(248, 248, 255)"
Gold = "rgb(255, 215, 0)"
Goldenrod = "rgb(218, 165, 32)"
Gray = "rgb(128, 128, 128)"
Grey = "rgb(128, 128, 128)"
Green = "rgb( 0, 128, 0)"
Greenyellow = "rgb(173, 255, 47)"
Honeydew = "rgb(240, 255, 240)"
Hotpink = "rgb(255, 105, 180)"
Indianred = "rgb(205, 92, 92)"
Indigo = "rgb( 75, 0, 130)"
Ivory = "rgb(255, 255, 240)"
Khaki = "rgb(240, 230, 140)"
Lavender = "rgb(230, 230, 250)"
Lavenderblush = "rgb(255, 240, 245)"
Lawngreen = "rgb(124, 252, 0)"
Lemonchiffon = "rgb(255, 250, 205)"
Lightblue = "rgb(173, 216, 230)"
Lightcoral = "rgb(240, 128, 128)"
Lightcyan = "rgb(224, 255, 255)"
Lightgoldenrodyellow = "rgb(250, 250, 210)"
Lightgray = "rgb(211, 211, 211)"
Lightgreen = "rgb(144, 238, 144)"
Lightgrey = "rgb(211, 211, 211)"
Lightpink = "rgb(255, 182, 193)"
Lightsalmon = "rgb(255, 160, 122)"
Lightseagreen = "rgb( 32, 178, 170)"
Lightskyblue = "rgb(135, 206, 250)"
Lightslategray = "rgb(119, 136, 153)"
Lightslategrey = "rgb(119, 136, 153)"
Lightsteelblue = "rgb(176, 196, 222)"
Lightyellow = "rgb(255, 255, 224)"
Lime = "rgb( 0, 255, 0)"
Limegreen = "rgb( 50, 205, 50)"
Linen = "rgb(250, 240, 230)"
Magenta = "rgb(255, 0, 255)"
Maroon = "rgb(128, 0, 0)"
Mediumaquamarine = "rgb(102, 205, 170)"
Mediumblue = "rgb( 0, 0, 205)"
Mediumorchid = "rgb(186, 85, 211)"
Mediumpurple = "rgb(147, 112, 219)"
Mediumseagreen = "rgb( 60, 179, 113)"
Mediumslateblue = "rgb(123, 104, 238)"
Mediumspringgreen = "rgb( 0, 250, 154)"
Mediumturquoise = "rgb( 72, 209, 204)"
Mediumvioletred = "rgb(199, 21, 133)"
Midnightblue = "rgb( 25, 25, 112)"
Mintcream = "rgb(245, 255, 250)"
Mistyrose = "rgb(255, 228, 225)"
Moccasin = "rgb(255, 228, 181)"
Navajowhite = "rgb(255, 222, 173)"
Navy = "rgb( 0, 0, 128)"
Oldlace = "rgb(253, 245, 230)"
Olive = "rgb(128, 128, 0)"
Olivedrab = "rgb(107, 142, 35)"
Orange = "rgb(255, 165, 0)"
Orangered = "rgb(255, 69, 0)"
Orchid = "rgb(218, 112, 214)"
Palegoldenrod = "rgb(238, 232, 170)"
Palegreen = "rgb(152, 251, 152)"
Paleturquoise = "rgb(175, 238, 238)"
Palevioletred = "rgb(219, 112, 147)"
Papayawhip = "rgb(255, 239, 213)"
Peachpuff = "rgb(255, 218, 185)"
Peru = "rgb(205, 133, 63)"
Pink = "rgb(255, 192, 203)"
Plum = "rgb(221, 160, 221)"
Powderblue = "rgb(176, 224, 230)"
Purple = "rgb(128, 0, 128)"
Red = "rgb(255, 0, 0)"
Rosybrown = "rgb(188, 143, 143)"
Royalblue = "rgb( 65, 105, 225)"
Saddlebrown = "rgb(139, 69, 19)"
Salmon = "rgb(250, 128, 114)"
Sandybrown = "rgb(244, 164, 96)"
Seagreen = "rgb( 46, 139, 87)"
Seashell = "rgb(255, 245, 238)"
Sienna = "rgb(160, 82, 45)"
Silver = "rgb(192, 192, 192)"
Skyblue = "rgb(135, 206, 235)"
Slateblue = "rgb(106, 90, 205)"
Slategray = "rgb(112, 128, 144)"
Slategrey = "rgb(112, 128, 144)"
Snow = "rgb(255, 250, 250)"
Springgreen = "rgb( 0, 255, 127)"
Steelblue = "rgb( 70, 130, 180)"
Tan = "rgb(210, 180, 140)"
Teal = "rgb( 0, 128, 128)"
Thistle = "rgb(216, 191, 216)"
Tomato = "rgb(255, 99, 71)"
Turquoise = "rgb( 64, 224, 208)"
Violet = "rgb(238, 130, 238)"
Wheat = "rgb(245, 222, 179)"
White = "rgb(255, 255, 255)"
Whitesmoke = "rgb(245, 245, 245)"
Yellow = "rgb(255, 255, 0)"
Yellowgreen = "rgb(154, 205, 50)"
)

// SVG basic unit types

// Stores an angle that can have unit "deg", "rad", or "grad".
type Angle struct {
N float
Unit string
}

func (a *Angle) String() string {
if a.Unit == "" {
return fmt.Sprintf("%.2f", a.N)
}
return fmt.Sprintf("%.2f", a.N, a.Unit)
}

// Stores a sRGB color.
type Color struct {
R, G, B uint8
}

func (c *Color) String() string {
return fmt.Sprintf("rgb(%d, %d, %d)", c.R, c.G, c.B)
}

// Stores a frequency value that can have either the unit "Hz" or
"kHz".
type Frequency struct {
N float
Unit string
}

func (f *Frequency) String() string {
return fmt.Sprintf("%f%s", f.N, f.Unit)
}

// Stores a distance measurement that can have unit "pt", "pc", "cm",
"mm", or "in".
type Length struct {
N float
Unit string
}

func (l Length) String() string {
if l.Unit == "" {
return fmt.Sprintf("%.2f", l.N)
}
return fmt.Sprintf("%.2f%s", l.N, l.Unit)
}

// Stores a value of specifications to paint elements.
type Paint string

// Stores a percentage value, which is a particular case of Length.
type Percentage struct {
N float
}

func (p *Percentage) String() string {
return fmt.Sprintf("%.2f%", p.N)
}

// Stores a point with X and Y values.
type Point struct {
X, Y float
}

type PointList []Point

func (p PointList) String() string {
s := ""
for i, pp := range p {
if i > 0 {
s += " "
}
s += fmt.Sprintf("%.2f,%.2f", pp.X, pp.Y)
}
return s
}

// Stores a time value that can have either the unit "s" or "ms".
type Time struct {
N float
Unit string
}

func (t *Time) String() string {
if t.Unit == "" {
return fmt.Sprintf("%.2f", t.N)
}
return fmt.Sprintf("%.2f%s", t.N, t.Unit)
}

// Stores a modification of a graphic element.
type Transform struct {
Key string
Value string
}

// Stores a list of modifications of a graphic element.
type TransformList []Transform

func (t TransformList) String() string {
s := ""
for i, tt := range t {
if i > 0 {
s += " "
}
s += tt.Key + `="` + tt.Value + `"`
}
return s
}


// SVG basic shapes

func getXMLAttr(a []string) ([]xml.Attr, SVGError) {
att := make([]xml.Attr, len(a))
for i, at := range a {
splitS := strings.Split(at, "=", 0)
if len(splitS) != 2 {
return nil, SVGError{"Error: incorrect XML attribute " + at}
}
att[i] = xml.Attr{xml.Name{"", splitS[0]}, splitS[1]}
}
return att, SVGError{}
}

// Represents a circle.
type Circle struct {
XMLName xml.Name
Cx, Cy float
R Length
XMLAttr []xml.Attr
}

func NewCircle(x, y, r float, a ...string) (c Circle, e SVGError) {
c.XMLName.Local = "circle"
c.Cx, c.Cy = x, y
c.R = Length{N: r}
c.XMLAttr, e = getXMLAttr(a)
return c, e
}

func (c Circle) MarshalXML() (string, os.Error) {
s := "<" + c.XMLName.String() +
fmt.Sprintf(" cx=\"%.2f\" cy=\"%.2f\" r=\"%s\"", c.Cx, c.Cy, c.R)
s += xml.MarshalAttr(c.XMLAttr) + " />"
return s, nil
}

func (c Circle) MarshalXMLIndent(prefix, indent string) (string,
os.Error) {
sufix, err := c.MarshalXML()
if err != nil {
return "", err
}
return prefix + indent + sufix, nil
}

// Represents an ellipse.
type Ellipse struct {
XMLName xml.Name
Cx, Cy float
Rx, Ry Length
XMLAttr []xml.Attr
}

func (e Ellipse) MarshalXML() (string, os.Error) {
s := "<" + e.XMLName.String() +
fmt.Sprintf(" cx=\"%.2f\" cy=\"%.2f\" rx=\"%s\" ry=\"%s\"",
e.Cx, e.Cy, e.Rx, e.Ry)
s += xml.MarshalAttr(e.XMLAttr) + " />"
return s, nil
}

func (e Ellipse) MarshalXMLIndent(prefix, indent string) (string,
os.Error) {
sufix, err := e.MarshalXML()
if err != nil {
return "", err
}
return prefix + indent + sufix, nil
}

// Represents a line.
type Line struct {
XMLName xml.Name
X1, Y1 float
X2, Y2 float
XMLAttr []xml.Attr
}

func (l Line) MarshalXML() (string, os.Error) {
s := "<" + l.XMLName.String() +
fmt.Sprintf(" x1=\"%.2f\" y1=\"%.2f\" x2=\"%.2f\" y2=\"%.2f\"",
l.X1, l.Y1, l.X2, l.Y2)
s += xml.MarshalAttr(l.XMLAttr) + " />"
return s, nil
}

func (l Line) MarshalXMLIndent(prefix, indent string) (string,
os.Error) {
sufix, err := l.MarshalXML()
if err != nil {
return "", err
}
return prefix + indent + sufix, nil
}

// Represents a rectangle.
type Rect struct {
XMLName xml.Name
X, Y float
Width, Height Length
Rx, Ry Length
XMLAttr []xml.Attr
}

func NewRect(x, y, w, h, rx, ry float, a ...string) (r Rect, e
SVGError) {
r.XMLName.Local = "rect"
r.X, r.Y = x, y
r.Width, r.Height = Length{N: w}, Length{N: h}
r.Rx, r.Ry = Length{N: rx}, Length{N: ry}
r.XMLAttr, e = getXMLAttr(a)
return r, e
}

func (r Rect) MarshalXML() (string, os.Error) {
s := "<" + r.XMLName.String() +
fmt.Sprintf(" x=\"%.2f\" y=\"%.2f\" width=\"%s\" height=\"%s\" rx=
\"%s\" ry=\"%s\"",
r.X, r.Y, r.Width, r.Height, r.Rx, r.Ry)
s += xml.MarshalAttr(r.XMLAttr) + " />"
return s, nil
}

func (r Rect) MarshalXMLIndent(prefix, indent string) (string,
os.Error) {
sufix, err := r.MarshalXML()
if err != nil {
return "", err
}
return prefix + indent + sufix, nil
}

// Represents a polyline.
type Polyline struct {
XMLName xml.Name
Points PointList
XMLAttr []xml.Attr
}

func (p Polyline) MarshalXML() (string, os.Error) {
s := "<" + p.XMLName.String() + p.Points.String()
s += xml.MarshalAttr(p.XMLAttr) + " />"
return s, nil
}

func (p Polyline) MarshalXMLIndent(prefix, indent string) (string,
os.Error) {
sufix, err := p.MarshalXML()
if err != nil {
return "", err
}
return prefix + indent + sufix, nil
}

// Represents a polygon.
type Polygon struct {
XMLName xml.Name
Points PointList
XMLAttr []xml.Attr
}

func (p Polygon) MarshalXML() (string, os.Error) {
s := "<" + p.XMLName.String() + p.Points.String()
s += xml.MarshalAttr(p.XMLAttr) + " />"
return s, nil
}

func (p Polygon) MarshalXMLIndent(prefix, indent string) (string,
os.Error) {
sufix, err := p.MarshalXML()
if err != nil {
return "", err
}
return prefix + indent + sufix, nil
}

// SVG document

type SVGdoc struct {
XMLDeclaration string
SVGDeclaration string
XMLInner []xml.Marshaler
}

func NewDocument() (s SVGdoc) {
s.XMLDeclaration = XMLDeclaration
s.SVGDeclaration = SVGDocumentTypeDeclaration
return s
}

func (s *SVGdoc) AddElement(m xml.Marshaler) {
n := make([]xml.Marshaler, len(s.XMLInner) + 1)
copy(n, s.XMLInner)
n[len(s.XMLInner)] = m
s.XMLInner = n
}

func (d SVGdoc) MarshalXML() (string, os.Error) {
s := d.XMLDeclaration + d.SVGDeclaration
c, err := xml.MarshalInner(d.XMLInner)
if err != nil {
return "", err
}
s += c
return s, nil
}

func (s SVGdoc) MarshalXMLIndent(prefix, indent string) (string,
os.Error) {
return s.XMLDeclaration + "\n" + s.SVGDeclaration + "\n", nil
}

// SVG elements

type SVGcontainer interface {
AddElement(xml.Marshaler)
}

// SVG is represented as an XML Element that can be marsheled into
XML.
type SVG struct {
XMLName xml.Name
XMLNS string
X, Y float
Width, Height Length
XMLAttr []xml.Attr
XMLInner []xml.Marshaler
}

func NewSVG(w, h float, a ...string) (s SVG, err SVGError) {
s.XMLName.Local = "svg"
s.XMLNS = XMLNS
s.Width.N, s.Height.N = w, h
s.XMLAttr, err = getXMLAttr(a)
return s, err
}

func (s SVG) MarshalXML() (string, os.Error) {
b := "<" + s.XMLName.String() + ` xmlns="` + s.XMLNS + `"`
b += ` width="` + s.Width.String() + `" height="` + s.Height.String()
+ `"`
b += xml.MarshalAttr(s.XMLAttr) + ">"
c, err := xml.MarshalInner(s.XMLInner)
if err != nil {
return "", nil
}
b += c + "</svg>"
return b, nil
}

func (s SVG) MarshalXMLIndent(prefix, indent string) (string,
os.Error) {
return "", nil
}

func (s *SVG) AddCircle(x, y, r float, a ...string) (err SVGError) {
c, e := NewCircle(x, y, r, a)
if e.String() != "" {
return e
}
if len(s.XMLInner) == 0 {
s.XMLInner = make([]xml.Marshaler, 1)
s.XMLInner[0] = c
} else if len(s.XMLInner) > 0 {
a := make([]xml.Marshaler, len(s.XMLInner) + 1)
copy(a, s.XMLInner)
a[len(s.XMLInner)] = c
s.XMLInner = a
}
return SVGError{}
}

func (s *SVG) AddRect(x, y, w, h, rx, ry float, a ...string) (err
SVGError) {
r, e := NewRect(x, y, w, h, rx, ry, a)
if e.String() != "" {
return e
}
if len(s.XMLInner) == 0 {
s.XMLInner = make([]xml.Marshaler, 1)
s.XMLInner[0] = r
} else if len(s.XMLInner) > 0 {
a := make([]xml.Marshaler, len(s.XMLInner) + 1)
copy(a, s.XMLInner)
a[len(s.XMLInner)] = r
s.XMLInner = a
}
return SVGError{}
}

type SVGError struct {
Error string
}

func (s SVGError) String() string {
return s.Error
}


===================================================
svg_test.go:



package main

import (
"./svg"
"xml"
"os"
)

func main() {
// create new SVG document
s := svg.NewDocument()

// create new SVG element
mySVG, _ := svg.NewSVG(200, 200)


mySVG.AddCircle(100, 100, 100, "fill=green")
mySVG.AddRect(30, 30, 140, 140, 0, 0, "fill=yellow")
mySVG.AddCircle(100, 100, 50, "fill=red")
s.AddElement(mySVG)
xml.Marshal(os.Stdout, s)
Reply all
Reply to author
Forward
0 new messages