At the Server
CanvasRenderingContext2D had to provide the drawing methods that the HTML5 canvas implementation provides, and then convert them into the equivalent Graphics2D drawing methods. The aim was to create the following transition:
The context also had to provide a function to get the image of the Graphics2D object. The Servlet could then return this once it had finished processing and rendering.
- Provide a browser environment for the DOM references in the libraries
Rhino also provides a convenient way of accessing properties of JavaBeans, so we could actually write:
ctx was then a reference to the Java object which provided the drawing methods. This meant that any function calls on ctx would equate to method calls on an instance of CanvasRenderingContext2D.
Canvas2D and Graphics2D
The next step was to extend AbstractGraphics in a way that would allow me to draw to a Server. I was aiming to implement SeverGraphics in the following way:
CanvasGraphics and ServerGraphics
This is done in the getContext function of CanvasGraphics, which is the only place I need to change. Once I modify getContext to return a reference to my Java object (which, remember, provides the same functions as a Canvas context), all function calls will go directly to the Java code. I changed getContext to read the following (similar to the example above):
Provided the methods I implement maintain the logic of the Canvas methods, I can safely change this reference and rely on the existing logic of CanvasGraphics to ensure that the correct behaviour will occur.
The Problem of Text
This is all done using a separate object in the Closure Library, called CanvasTextElement. The purpose of this object is to create a DIV element on the DOM with the text as the inner HTML, and then style the DIV based on the parameters to the element. This wouldn't work on the server, as there is no browser to display the text.
I created a ServerTextElement, which is almost identical to CanvasTextElement save the following:
- The constructor of the element doesn't create a DOM element
- There is no updating of Styles - just one draw function
The draw function makes a call to the context and performs its drawing processing as if it were any other element. A simple implementation of this method would be as follows:
.. but that's not the best solution. The Canvas API does provide methods to draw text, so we should use those:
This allows us to create a patch for CanvasGraphics, updating it to use the Canvas element to draw text, rather than the DOM.
Graphics2D also provides a TextMetrics object to get the measured width of a String in pixels; something which the Closure Library is lacking.
Similarly, measureText is specified by the Canvas2D API, so was implemented in my Java adapter. I could then add this functionality to ServerGraphics like so:
The final step was to configure Closure Library to recognise when it was running in a Server environment, and to react in an appropriate way.
Closure Library has its own user agent analysis; it determines which browser the viewer is using and their platform, using the User-Agent HTTP header. For example, if you're using Google Chrome, your User-Agent header may be something like this:
Closure will then pick up on the key word 'WebKit', and flag that you're running a WebKit browser. Other aspects of the Library code can then use this information. For example, a basic implementation of the createGraphics function in the graphics package would look something like this:
Creating and Using the User Agent
The user agent module of the Closure Library had to be extended so that it could recognise when it was being executed from a server environment. This was done by analysing the User-Agent header when ran through Rhino, which is the following:
Using the keyword 'Rhino', we can assert that we are running in a server. The first step to incorporating this information into the Library was to mimic the behaviour for other user agents in useragent.js, but for Rhino. This involved appending the following to the check in useragent.js:
The second step was to incorporate the new Rhino user agent defined in the first step, into the createGraphics function, so that a ServerGraphics object will be created. I extended Closure's original code by adding the following statement in the above conditional statement:
Support for the NodeList Interface
There was one additional problem I experienced when running the Library on the server.
.. the solution was simple:
The problem only occurred in XMLDataSource, and was simple to fix, making it an ideal patch submission!
I was very surprised at how well the Closure Library ported over to a server environment. With the exception of some DOM methods, which were easily solved using Envjs, and the NodeList interface, there were no problems in moving the Library to the server.