svg devzone

"I'll let you figure out
which one has been slow in supporting SVG." Tim Berners-Lee

Factoriser votre code lors de creation SVG

Thu, 16 Jul

Batik: factorier votre code

Introduction

Dans un précédent article nous avons fait nos premier pas avec Batik en créant quelques formes assez simples.
Dans le code produit, la gestion du document SVG et son intégration au Java2D est un peu disparate !
C'est donc le moment de factoriser notre code pour nous faciliter un petit peu la création de formes vectorielles.

Quelques rappels

Pour pouvoir créer un graphique 2D vectoriel, nous avons besoin d'un document de base auquel , à travers un DOM, nous appliquons la DTD du SVG.
Notre nouveau document ainsi obtenu , nous sert à générer un objet SVGGraphics2D (java2D) surquel nous allons dessiner.

Une fois le dessin terminé, nous devons propager notre document à notre canvas (partie de notre fenêtre d'application sur lequel nous affichons le SVG). Bien entendu ce canvas est attaché à cette fenêtre d'application (JFrame).
Sans cela celle-ci n'affichera rien du tout ...

En regardant d'un peu plus près, on s'aperçoit rapidement que nous avons besoins de 3 concepts différents :

  • une vue
  • un document
  • un objet java2D

La vue

Qui est notre fenêtre d'application.
Elle comporte différents éléments (objets) tels le canvas et le Panel.

Le document

Qui suit la structure du SVG Dom et qui une fois créé nous sert à propager le dessin vers sa zone d'affichage : le canvas.

L'objet 2D

Notre objet de dessin SVGGraphics2D qui nous permet de travailler directement avec les méhodes java2D.
Il se sert du document qu'on a passé à son constructeur, pour générer automatiquement la structure de rendu.Mais cela est fait en transparence et ne nous regarde pas ... !

Ce que nous désirons

Notre but est de dessiner sans nous soucier de la manière dont est géré la création du document et de la façon dont il est passé à la vue.
Ce qui revient à gérer dans notre méthode principale :

  • la création d'un objet SVG 2D hérité de java2D
  • les méthodes de dessin
  • l'affichage de notre dessin

Ce qui revient à un code relativement simple également pour notre class Main :


public class Main{

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here
        Controller2D myApp = new Controller2D();

        myApp.setPaint(Color.green);
        Shape rectangle = new Rectangle(0,0,200,200);
        myApp.fill(rectangle);

        myApp.setPaint(Color.red);
        Shape cercle = new Ellipse2D.Double(80,80,50,50);
        myApp.fill(cercle);
        
        myApp.setView();
        myApp.setVisible();
    }

}

Lorsque nous avons notre objet Controller2D , nous utilisons les méthodes offertes par l'API Java2D pour dessiner, puis nous l'envoyons à la vue pour l'affichage.
Nous rendons notre application visible dans la méthode main, mais cela peut etre fait tout aussi bien dans la classe vue.

L'important ici est que nous n'avons pas à nous soucier dyu SVG ; Cela est fait en transparence.
Les classes que nous allons créer vont s'en occuper pour nous!

Un peu de logique

Ce que nous devons factoriser c'est la gestion du document et le rendu en svg.

A nouveau un petit rappel du code "svg" qui se balladait dans notre ancien code.


....

DOMImplementation impl = SVGDOMImplementation.getDOMImplementation();
doc = (SVGDocument) impl.createDocument(SVGDOMImplementation.SVG_NAMESPACE_URI,
	                                "svg",
					null);
...

SVG2D = new SVGGraphics2D(doc);

...

Element root = doc.getDocumentElement();
SVG2D.getRoot(root);
canvas.setSVGDocument(doc);
....

Ok ... nous devons donc gérer la création d'un document (class SvgDocument) ; Construire un objet 2D avec comme argument le document (class Controller2D) ; Passer le résultat après dessin à la vue (class View).

Un peu de papier et un crayon pour modéliser tout cela (et je suis vraiment pas expert UML !!!).

modelisation des classes SVG

Reprenons point à point

Notre application Controller2D est appelée dans la méthode 'main'.
Celle-ci étend SVGGraphics2D pour en profiter un maximum.
On a besoin de passer un un document svg à notre constructeur.Nous utilisons donc la méthode statique getSvgDocument de l'objet SvgDocument.

Notre controlleur a besoin de passer le résultat à la vue. Nous initialisons donc un objet View dans notre constructeur.
Nous créons une méthode setView qui va passer tout ce petit monde au canvas (rappellez vous du code de l'article précédent).
Pour retrouver notre Canvas, on a bien sûr créer une methode getCanvas dans notre Objet View.

Nous avons notre code pour le Controller2D :


public class Controller2D extends SVGGraphics2D{
    //private SVGGraphics2D svg2d = null;
    private View view = null;

    public Controller2D(){
        super(SvgDocument.getSvgDocument());
        this.view = new View();
    }

    public void setView(){
        Element root = SvgDocument.getSvgDocument().getDocumentElement();
        getRoot(root);
        View.getCanvas().setSVGDocument(SvgDocument.getSvgDocument());
    }

    public View getView(){
        return this.view;
    }

    public void setVisible(){
        this.view.setVisible(true);
    }
}

Super(SvgDocument.getSvgDocument())

Nous passons en paramètre au constructeur un objet de type SvgDocument ; Petit rappel de l'ancien code : "SVG2D = new SVGGraphics2D(doc); ".
Pour comprendre ce que nous faisons , il faut jetter un oeil à l'API de SVGGraphics2D. Et dans notre cas, c'est le premier "type de constructeur qui nous intéresse.

public SVGGraphics2D(Document domFactory)
Parameters:
domFactory - Factory which will produce Elements for
the DOM tree this Graphics2D generates.

C'est ici le nerf de notre factorisation ; Pour passer en paramètre le bon objet on utilise la méthode statique de notre Objet SvgDocument getSvgDocument(). C'est cette classe qui fait initialiser en transparence le document SVG et le retourner quand on l'appelle (un objet bien discipliné en somme !).

Notre Objet SvgDocument

Le code n'est pas bien difficile. On utilise le pattern Singleton pour retourner l'objet SvgDocument càd retourne l'instance de svgDocument s'il existe sinon crée une instance et retourne le !


public class SvgDocument {
    private static SVGDocument svgDocument = null;
    
    public SvgDocument(){
        DOMImplementation dom = SVGDOMImplementation.getDOMImplementation();
        String svgns = SVGDOMImplementation.SVG_NAMESPACE_URI;
        svgDocument = (SVGDocument)dom.createDocument(svgns, "svg", null);
    }

    public static SVGDocument getSvgDocument(){
        if(SvgDocument.svgDocument != null)
            return SvgDocument.svgDocument;
        else{
            SvgDocument svg = new SvgDocument();
            return SvgDocument.svgDocument;
        }
    }
}

De cette façon nous ne travaillons que sur un seul objet SVG à la fois !
Si nous voulons travailler avec 2 documents SVG nous initialiserons alors 2 Objets différents dans notre Main.

La vue

Nontrer le résultat de notre graphique c'est un peu le but de notre code !!!
Pour cela à nouveau rien de bien sorcier puisque nous utilisons les Objets de Java2D.

Le seul problème à résoudre ici c'est de passer au canvas notre document SVG.
Comme c'est notre objet Controller2D qui s'occupe de dessiner notre graphique, c'est à lui qu'incombe le rôle de passer à la vue le résultat. Il est le seul à savoir quand le dessin est terminé (en plus de vous bien sûr).

Notre vue ne fait donc rien de plus que de créer un élément Jframe et un élément JSVGCanvas qui est notre espace de rendu.
Le controller2D dessine le graphique et passe le résultat au canvas. Pour cela il doit le récupérer à la classe View.


public class View extends JFrame{

    private static JSVGCanvas canvas = null;
    private JPanel panel = null;

    public View(){
        super("myapp");
        View.canvas = new JSVGCanvas();
        View.canvas.setPreferredSize(new Dimension(400,400));
        this.setSize(new Dimension(400,400));
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.panel = new JPanel();

        this.panel.add(View.getCanvas());

        this.setContentPane(this.panel);
    }

    public static JSVGCanvas getCanvas(){
        return View.canvas;
    }
}

C'est la méthode static getCanvas qui nous servira depuis le Controller2D pour récupérer une instance de notre canvas et qui lui passera le résultat à afficher.

Je reviens sur le code de notre Controller2D qui se sert de getCanvas.


public void setView(){
        Element root = SvgDocument.getSvgDocument().getDocumentElement();
        getRoot(root);
        View.getCanvas().setSVGDocument(SvgDocument.getSvgDocument());
    }

Nous utilisons à nouveau notre méthode getSvgDocument pour récupérer l'unique instance de l'objet SvgDocument et initialisons l'objet Element root.
Rien de nouveau non plus ici par rapport à notre ancien code à par que nous l'avons factoriser. L'appel à cette méthode setView va faire le boulot d'associer la zone de dessin JSVGCanvas et notre document SVG.

Et voilà notre factorisation terminée.
Maintenant vous dessinez directement depuis la méthode main comme si vous travailliez avec un "simple" objet Java2D. Le rendu se fera automatiquement en SVG.

Pour le fun


public static void main(String[] args) {
        // TODO code application logic here
        Controller2D myApp = new Controller2D();

        myApp.setPaint(new GradientPaint(0, 0, Color.white, 110, 40, Color.black, true));
        Shape s1 = new Ellipse2D.Double(10,0,100,100);
        //myApp.draw(s1);
        
        //myApp.setPaint(Color.black);
        Shape s2 = new Ellipse2D.Double(50,0,100,100);
        //myApp.draw(s2);

        Shape s3 = new Ellipse2D.Double(70,50,50,50);
        myApp.fill(s3);

        //myApp.setPaint(Color.black);
        
        Area a1 = new Area(s1);
        Area a2 = new Area(s2);

        a2.subtract(a1);
        myApp.fill(a2);

        myApp.setPaint(Color.white);
        Shape s4 = new Ellipse2D.Double(70,10,50,50);
        myApp.fill(s4);
        
        myApp.setView();
        myApp.setVisible();
    }

exemple d'application SVG avec Batik

Articles

svg-developers at yahoo group svg

Re: SVG in html5

Yes I want to understand how much is expected from an editing tool which does the validation and highlightling of all the tags and attributes and provides...

... lire la suite