Example program - Using SVG for vector graphics
SVG is the W3C standard markup language for vector graphics. It is both powerful and compact supporting advanced features such as animation, scripting and user interaction. It exploits existing W3C standards such as XML, DOM, CSS and Javascript which will be familiar to most developers.
SVG can be rendered in the KCML client by embedding the Adobe SVG viewer OCX/ActiveX. This OCX component will also add SVG support to Firefox and Internet Explorer neither of which will render vector grahics on their own. However there are Firefox and Mozilla builds available with native SVG support.
This example builds an XML DOM on the server containing the SVG markup for the picture. The DOM is then serialized to a file on the server and copied down to the client using COPY OBJECT. In the show() event the client will tell the OCX to load the file from the cache having discovered its location using the 'KCMLGetCacheFileName() internal $DECLARE.
This is not intended to be an SVG showcase and it just creates a few primitives such as rectangles and lines. You could use an SVG document or fragment created using other SVG applications such as Adobe Illustrator in a more complex example.
// Create SVG drawing object to display on a form using Adobe SVG viewer ActiveX // // While it would be neat to be able to do this in place using the DOM there are // a number of practical shortcoming. The MS HTML control has no native SVG support // (nor has Firefox 1) so we must use the Adobe ActiveX. It has no implementation support // for creating an empty document so we must create the document as a file separately. Theoretically // you should be able to manipulate the SVG DOM using getSVGDocument() but I have been unable // to get this to work. // The approach here is to create an SVG document as a file on the server and copy it down // to the client. DIM OBJECT x, OBJECT imp, OBJECT doc, OBJECT node, OBJECT a, OBJECT n, OBJECT dtd, OBJECT root DIM xml$0, OBJECT g OBJECT x = CREATE "dynamic", "dyndom" REM get the DOM implementation OBJECT imp = x.Implementation // standard DOCTYPE for SVG docs (required) OBJECT dtd = imp.createDocumentType("svg", "-//W3C//DTD SVG 1.0//EN", "http://www.w3.org/TR/SVG/DTD/svg10.dtd") // create an empty SVG document with namespace and DTD DIM SVG_namespace$="http://www.w3.org/2000/svg" OBJECT doc = imp.createDocument(0, "svg", OBJECT dtd) doc.Encoding$ = "UTF-8" // get the root node OBJECT root = doc.DocumentElement root.setAttribute("viewBox", "0 0 270 400") root.setAttribute("xml:space", "preserve") // inject some CSS stuff DIM css$0 REDIM css$ = ".str {stroke-width:1} " & ".fnt {font-size:20;font-family:'Arial'} " & " .red {fill:red}" & " .blue {fill:blue}" & " .yellow {fill:yellow}" & " .green {fill:green}" & " .frn {fill-rule:nonzero}" OBJECT n = doc.createElement("defs") root.appendChild(OBJECT n) // add a comment infront of this tag OBJECT a = doc.createComment("CSS styles") root.insertBefore(OBJECT a, OBJECT n) OBJECT a = doc.createElement("style") n.appendChild(OBJECT a) a.setAttribute("type", "text/css") OBJECT n = doc.createCDATASection(css$) a.appendChild(OBJECT n) OBJECT a = NULL // main drawing layer OBJECT g = doc.createElement("g") root.appendChild(OBJECT g) g.setAttribute("id", "mainlayer") // add a rect OBJECT n = 'NewObj(OBJECT g, "rect", "red str") 'Position(OBJECT n, 15, 15) 'Size(OBJECT n, 50, 100) 'caption(OBJECT g, "rect", 44, 88) // add rounded rect OBJECT n = 'NewObj(OBJECT g, "rect", "blue str") 'Position(OBJECT n, 150, 15) 'Size(OBJECT n, 50, 100) n.setAttribute("rx", "12") n.setAttribute("ry", "18") 'caption(OBJECT g, "rounded rect", 144, 88) 'Position(OBJECT n, 150, 15) // add some lines as axes 'straightline(0, 250, 200, 250) 'straightline(0, 250, 0, 100) // and a polyline as the graph OBJECT n = 'NewObj(OBJECT g, "polyline", "str") n.setAttribute("style", "fill:none; stroke: green;") n.setAttribute("points", "0,250 10,220 80,200 120,160") // add a circle OBJECT n = 'NewObj(OBJECT g, "circle", "yellow str") n.setAttribute("cx", "300") n.setAttribute("cy", "300") n.setAttribute("r", "50") // and an ellipse OBJECT n = 'NewObj(OBJECT g, "ellipse", "green str") n.setAttribute("cx", "0") n.setAttribute("cy", "300") n.setAttribute("rx", "50") n.setAttribute("ry", "20") // and animate the ellipse OBJECT n = 'NewObj(OBJECT n, "animate") n.setAttribute("attributeName", "fill") n.setAttribute("from", "green") n.setAttribute("to", "red") n.setAttribute("begin", "load") n.setAttribute("dur", "2s") n.setAttribute("fill", "restore") // serialize doc to a string REDIM xml$ = 'serialize$(OBJECT x, OBJECT doc) // drop the DOM OBJECT n = NULL OBJECT g = NULL OBJECT root = NULL OBJECT doc = NULL OBJECT dtd = NULL OBJECT imp = NULL OBJECT x = NULL // write to a temp file on the server DIM tmpfile$0, hFile, nLen REDIM tmpfile$ = $PRINTF("/tmp/svgdoc_%d.svg", #PART) hFile = OPEN tmpfile$, "w+" nLen = WRITE #hFile, xml$ CLOSE #hFile // and copy it into the client cache // the filename should be unique across all forms DIM cachefile$="svgdoc" COPY OBJECT tmpfile$ TO cachefile$ REMOVE tmpfile$ // open the form DEFFORM SVGTestForm()={.form,.form$,.Style=0x50c000c4,.Width=528,.Height=288,.Text$="SVG demo",.Id=1024,.Show()#},{.ok,.button$,.Style=0x50010001,.Left=474,.Top=4,.Width=50,.Height=14,.Text$="OK",.__Anchor=5,.Id=1},{.cancel,.button$,.Style=0x50010000,.Left=474,.Top=20,.Width=50,.Height=14,.Text$="Cancel",.__Anchor=5,.Id=2},{.ocxControl1,.kcmlocx$,.Style=0x50010000,.Left=59,.Top=18,.Width=379,.Height=234,.Id=1002,.Definition=.lblControl1},{.lblControl1,.ocxdef$,.OCX$="Adobe.SVGCtl"} LOCAL DIM OBJECT h, svgfile$0 DEFEVENT SVGTestForm.Show() OBJECT h = .ocxControl1 REDIM svgfile$ = "file://" & 'GetCacheFile$(BYREF cachefile$) h.setSrc(svgfile$) END EVENT FORM END SVGTestForm.Open() END DEFSUB 'serialize$(OBJECT x, OBJECT doc) // serialize tree LOCAL DIM OBJECT impls, OBJECT writer, OBJECT s // create a writer object and set attributes OBJECT impls = x.getDOMImplementation("LS") OBJECT writer = impls.createDOMWriter() writer.setFeature("format-pretty-print", TRUE) writer.NewLine$ = HEX(0A) // print to a string OBJECT s = writer.writeDocToString(OBJECT doc) RETURN s.Text$ END SUB DEFSUB 'NewObj(OBJECT parent, type$, class$="") LOCAL DIM OBJECT n OBJECT n = parent.OwnerDocument.createElement(type$) parent.appendChild(OBJECT n) IF (class$ != " ") n.setAttribute("class", class$) END IF RETURN OBJECT n END SUB DEFSUB 'Position(OBJECT node, x, y) // add position node.setAttribute("x", $PRINTF("%d", x)) node.setAttribute("y", $PRINTF("%d", y)) END SUB DEFSUB 'Size(OBJECT node, height, width) // add position node.setAttribute("height", $PRINTF("%d", height)) node.setAttribute("width", $PRINTF("%d", width)) END SUB DEFSUB 'lineends(OBJECT n, x1, y1, x2, y2) n.setAttribute("x1", $PRINTF("%d", x1)) n.setAttribute("y1", $PRINTF("%d", y1)) n.setAttribute("x2", $PRINTF("%d", x2)) n.setAttribute("y2", $PRINTF("%d", y2)) END SUB DEFSUB 'straightline(x1, y1, x2, y2) LOCAL DIM OBJECT n OBJECT n = 'NewObj(OBJECT g, "line", "") 'lineends(OBJECT n, x1, y1, x2, y2) n.setAttribute("style", "fill:none; stroke: black; stroke-width:2") RETURN OBJECT n END SUB DEFSUB 'caption(OBJECT parent, text$, x, y) // text caption LOCAL DIM OBJECT n, OBJECT t, OBJECT doc OBJECT doc = parent.OwnerDocument OBJECT n = doc.createElement("text") parent.appendChild(OBJECT n) n.setAttribute("class", "fnt") 'Position(OBJECT n, x, y) OBJECT t = doc.createTextNode(text$) n.appendChild(OBJECT t) RETURN OBJECT n END SUB DEFSUB 'GetCacheFile$(BYREF file$) // get directory on client used for cache $DECLARE 'KCMLGetCacheFileName(STR(),RETURN STR(),INT()) LOCAL DIM cache$_MAX_PATH 'KCMLGetCacheFileName(file$, cache$, LEN(STR(cache$))) RETURN cache$ END SUB
When creating your own forms you need to install the OCX in the forms editor (File|Install OCX...) before it will appear in the component palette. It is listed as "SVG document" and has a ProgId of "Adobe.SVGCtl".
An alternative approach might be to create the graphic as an XML file in a directory exposed through the web server component of the Connection Manager or through an Apache web server. Ensure the web server uses the correct MIME type (image/svg+xml) however. You could then launch an SVG enabled Firefox or IE session to view it using a $DECLARE of ShellExecute().
As far as I know it is not possible for the Adobe OCX to generate an event that KCML can handle on the server so while client side events are possible, they remain in the client. However a Javascript handler in the session could use XMLHttpRequest to post a SOAP call back to a web server.
See also
SVG zone [www.adobe.com]