You are viewing a plain text version of this content. The canonical link for it is here.
Posted to batik-commits@xmlgraphics.apache.org by de...@apache.org on 2005/09/25 21:53:33 UTC

svn commit: r291468 [3/19] - in /xmlgraphics/batik/trunk: ./ samples/tests/resources/wmf/ sources/org/apache/batik/ext/awt/geom/ sources/org/apache/batik/svggen/ sources/org/apache/batik/transcoder/ sources/org/apache/batik/transcoder/wmf/ sources/org/...

Modified: xmlgraphics/batik/trunk/sources/org/apache/batik/transcoder/wmf/tosvg/WMFRecordStore.java
URL: http://svn.apache.org/viewcvs/xmlgraphics/batik/trunk/sources/org/apache/batik/transcoder/wmf/tosvg/WMFRecordStore.java?rev=291468&r1=291467&r2=291468&view=diff
==============================================================================
--- xmlgraphics/batik/trunk/sources/org/apache/batik/transcoder/wmf/tosvg/WMFRecordStore.java (original)
+++ xmlgraphics/batik/trunk/sources/org/apache/batik/transcoder/wmf/tosvg/WMFRecordStore.java Sun Sep 25 12:51:54 2005
@@ -30,9 +30,10 @@
  * @author <a href="mailto:luano@asd.ie">Luan O'Carroll</a>
  * @version $Id$
  */
-public class WMFRecordStore implements WMFConstants{
+public class WMFRecordStore extends AbstractWMFReader {
 
-    public WMFRecordStore(){
+    public WMFRecordStore() {
+      super();
       reset();
     }
 
@@ -45,90 +46,37 @@
       vpY = 0;
       vpW = 1000;
       vpH = 1000;
-      numObjects = 0;
+      scaleX = 1;
+      scaleY = 1;      
+      inch = 0;
       records = new Vector( 20, 20 );
-      objectVector = new Vector();
-    }
-
-    private short readShort( DataInputStream is  ) throws IOException{
-        byte js[] = new byte[ 2 ];
-        is.read( js );
-        int iTemp = ((0xff) & js[ 1 ] ) << 8;
-        short i = (short)(0xffff & iTemp);
-        i |= ((0xff) & js[ 0 ] );
-        return i;
-    }
-
-    private int readInt( DataInputStream is  ) throws IOException {
-        byte js[] = new byte[ 4 ];
-        is.read( js );
-        int i = ((0xff) & js[ 3 ] ) << 24;
-        i |= ((0xff) & js[ 2 ] ) << 16;
-        i |= ((0xff) & js[ 1 ] ) << 8;
-        i |= ((0xff) & js[ 0 ] );
-        return i;
     }
 
     /**
      * Reads the WMF file from the specified Stream.
      */
-    public boolean read( DataInputStream is ) throws IOException{
-        reset();
-
-        setReading( true );
-        int dwIsAldus = readInt( is );
-        if ( dwIsAldus == WMFConstants.META_ALDUS_APM ) {
-            // Read the aldus placeable header.
-            /* int   key      = dwIsAldus; */
-            /* short hmf      = */ readShort( is );
-            /* short left     = */ readShort( is );
-            /* short top      = */ readShort( is );
-            /* short right    = */ readShort( is );
-            /* short  bottom  = */ readShort( is );
-            /* short inch     = */ readShort( is );
-            /* int   reserved = */ readInt  ( is );
-            /* short checksum = */ readShort( is );
-        }
-        else {
-            System.out.println( "Unable to read file, it is not a Aldus Placable Metafile" );
-            setReading( false );
-            return false;
-        }
-
-        /* int mtType         = */ readShort( is );
-        /* int mtHeaderSize   = */ readShort( is );
-        /* int mtVersion      = */ readShort( is );
-        /* int mtSize         = */ readInt  ( is );
-        int mtNoObjects       =    readShort( is );
-        /* int mtMaxRecord    = */ readInt  ( is );
-        /* int mtNoParameters = */ readShort( is );
-
+    protected boolean readRecords( DataInputStream is ) throws IOException {
 
         short functionId = 1;
         int recSize = 0;
+        short recData;
 
         numRecords = 0;
 
-        numObjects = mtNoObjects;
-        objectVector.ensureCapacity( numObjects );
-        for ( int i = 0; i < numObjects; i++ ) {
-            objectVector.addElement( new GdiObject( i, false ));
-        }
-
-        while ( functionId > 0 ) {
+        while ( functionId > 0) {
             recSize = readInt( is );
             // Subtract size in 16-bit words of recSize and functionId;
             recSize -= 3;
             functionId = readShort( is );
             if ( functionId <= 0 )
-                break;
+            break;
 
             MetaRecord mr = new MetaRecord();
             switch ( functionId ) {
             case WMFConstants.META_DRAWTEXT:
                 {
                     for ( int i = 0; i < recSize; i++ )
-                        readShort( is );
+                        recData = readShort( is );
                     numRecords--;
                 }
                 break;
@@ -137,23 +85,50 @@
                 {
                     int yVal = readShort( is );
                     int xVal = readShort( is );
-                    int lenText = readInt( is );
-                    int len = 2*(recSize-4);
+                    int lenText = readShort( is );
+                    int flag = readShort( is );
+                    int read = 4; // used to track the actual size really read                   
+                    boolean clipped = false;
+                    int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
+                    int len;
+                    // determination of clipping property
+                    if ((flag & WMFConstants.ETO_CLIPPED) != 0) {
+                        x1 =  readShort( is );
+                        y1 =  readShort( is );
+                        x2 =  readShort( is );
+                        y2 =  readShort( is );
+                        read += 4;
+                        clipped = true;
+                    }
                     byte bstr[] = new byte[ lenText ];
-                    //is.read( bstr );
                     int i = 0;
-                    for ( ; i < lenText; i++ )
+                    for ( ; i < lenText; i++ ) {
                         bstr[ i ] = is.readByte();
-                    for ( ; i < len; i++ )
-                        is.readByte();
-
-                    String str = new String( bstr );
-                    mr = new StringRecord( str );
+                    }
+                    read += (lenText + 1)/2;                    
+                    /* must do this because WMF strings always have an even number of bytes, even
+                     * if there is an odd number of characters
+                     */
+                    if (lenText % 2 != 0) is.readByte();
+                    // if the record was not completely read, finish reading
+                    if (read < recSize) for (int j = read; j < recSize; j++) readShort( is );
+                    
+                    /* get the StringRecord, having decoded the String, using the current
+                     * charset (which was given by the last META_CREATEFONTINDIRECT)
+                     */                    
+                    mr = new MetaRecord.ByteRecord(bstr);
                     mr.numPoints = recSize;
                     mr.functionId = functionId;
 
                     mr.AddElement( new Integer( xVal ));
                     mr.AddElement( new Integer( yVal ));
+                    mr.AddElement( new Integer( flag ));
+                    if (clipped) {
+                        mr.AddElement( new Integer( x1 ));
+                        mr.AddElement( new Integer( y1 ));                        
+                        mr.AddElement( new Integer( x2 ));   
+                        mr.AddElement( new Integer( y2 ));
+                    }
                     records.addElement( mr );
                 }
                 break;
@@ -161,15 +136,27 @@
             case WMFConstants.META_TEXTOUT:
                 {
                     int len = readShort( is );
+                    int read = 1; // used to track the actual size really read
                     byte bstr[] = new byte[ len ];
-                    //is.read( bstr );
-                    for ( int i = 0; i < len; i++ )
+                    for ( int i = 0; i < len; i++ ) {
                         bstr[ i ] = is.readByte();
+                    }
+                    /* must do this because WMF strings always have an even number of bytes, even
+                     * if there is an odd number of characters
+                     */
+                    if (len % 2 != 0) is.readByte(); 
+                    read += (len + 1) / 2;
+                    
                     int yVal = readShort( is );
                     int xVal = readShort( is );
-
-                    String str = new String( bstr );
-                    mr = new StringRecord( str );
+                    read += 2;
+                    // if the record was not completely read, finish reading                    
+                    if (read < recSize) for (int j = read; j < recSize; j++) readShort( is );
+                    
+                    /* get the StringRecord, having decoded the String, using the current
+                     * charset (which was givben by the last META_CREATEFONTINDIRECT)
+                     */
+                    mr = new MetaRecord.ByteRecord(bstr);
                     mr.numPoints = recSize;
                     mr.functionId = functionId;
 
@@ -183,58 +170,110 @@
             case WMFConstants.META_CREATEFONTINDIRECT:
                 {
                     int lfHeight = readShort( is );
-                    /* int lfWidth       = */ readShort( is );
-                    /* int lfEscapement  = */ readShort( is );
-                    /* int lfOrientation = */ readShort( is );
+                    int lfWidth = readShort( is );
+                    int lfEscapement = readShort( is );
+                    int lfOrientation = readShort( is );
                     int lfWeight = readShort( is );
 
                     int lfItalic = is.readByte();
-                    /* int lfUnderline      = */ is.readByte();
-                    /* int lfStrikeOut      = */ is.readByte();
-                    /* int lfCharSet        = */ is.readByte();
-                    /* int lfOutPrecision   = */ is.readByte();
-                    /* int lfClipPrecision  = */ is.readByte();
-                    /* int lfQuality        = */ is.readByte();
-                    /* int lfPitchAndFamily = */ is.readByte();
-
-                    int len = (2*(recSize-9));//13));
+                    int lfUnderline = is.readByte();
+                    int lfStrikeOut = is.readByte();
+                    int lfCharSet = is.readByte() & 0x00ff;
+                    //System.out.println("lfCharSet: "+(lfCharSet & 0x00ff));
+                    int lfOutPrecision = is.readByte();
+                    int lfClipPrecision = is.readByte();
+                    int lfQuality = is.readByte();
+                    int lfPitchAndFamily = is.readByte();
+
+                    // don't need to read the end of the record, 
+                    // because it will always be completely used
+                    int len = (2*(recSize-9));
                     byte lfFaceName[] = new byte[ len ];
-                    for ( int i = 0; i < len; i++ )
-                        lfFaceName[ i ] = is.readByte();
-
+                    byte ch;
+                    for ( int i = 0; i < len; i++ ) lfFaceName[ i ] = is.readByte();
 
                     String str = new String( lfFaceName );
 
-                    mr = new StringRecord( str );
+                    mr = new MetaRecord.StringRecord( str );
                     mr.numPoints = recSize;
                     mr.functionId = functionId;
 
                     mr.AddElement( new Integer( lfHeight ));
                     mr.AddElement( new Integer( lfItalic ));
                     mr.AddElement( new Integer( lfWeight ));
+                    mr.AddElement( new Integer( lfCharSet));
+                    mr.AddElement( new Integer( lfUnderline));
+                    mr.AddElement( new Integer( lfStrikeOut));
+                    mr.AddElement( new Integer( lfOrientation));
+                    // escapement is the orientation of the text in tenth of degrees
+                    mr.AddElement( new Integer( lfEscapement));
                     records.addElement( mr );
                 }
                 break;
+                
+            case WMFConstants.META_SETMAPMODE: {
+                    mr.numPoints = recSize;
+                    mr.functionId = functionId;
 
+                    int mode = readShort( is );  
+                    mr.AddElement( new Integer( mode));
+                    records.addElement( mr );
+            }
+                break;
+                
+            case WMFConstants.META_SETVIEWPORTORG:
+            case WMFConstants.META_SETVIEWPORTEXT:
             case WMFConstants.META_SETWINDOWORG:
-            case WMFConstants.META_SETWINDOWEXT:
-                {
+            case WMFConstants.META_SETWINDOWEXT: {
                     mr.numPoints = recSize;
                     mr.functionId = functionId;
 
-                    int i0 = readShort( is );
-                    int i1 = readShort( is );
-                    mr.AddElement( new Integer( i1 ));
-                    mr.AddElement( new Integer( i0 ));
+                    int height = readShort( is );
+                    int width = readShort( is );
+                    mr.AddElement( new Integer( width ));
+                    mr.AddElement( new Integer( height ));
                     records.addElement( mr );
 
-                    if ( functionId == WMFConstants.META_SETWINDOWEXT ) {
-                      vpW = i0;
-                      vpH = i1;
+                    if (_bext && functionId == WMFConstants.META_SETWINDOWEXT) {
+                      vpW = width;
+                      vpH = height;
+                      _bext = false;
                     }
                 }
                 break;
+                
+            case WMFConstants.META_OFFSETVIEWPORTORG:                
+            case WMFConstants.META_OFFSETWINDOWORG: {
+                    mr.numPoints = recSize;
+                    mr.functionId = functionId;
+
+                    int y = readShort( is );
+                    int x = readShort( is );
+                    mr.AddElement( new Integer( x ));
+                    mr.AddElement( new Integer( y ));                
+                    records.addElement( mr );
+            }
+                break;
+                
+            case WMFConstants.META_SCALEVIEWPORTEXT:                
+            case WMFConstants.META_SCALEWINDOWEXT: {
+                    mr.numPoints = recSize;
+                    mr.functionId = functionId;
 
+                    int ydenom = readShort( is );
+                    int ynum = readShort( is );
+                    int xdenom= readShort( is );
+                    int xnum = readShort( is );
+                    mr.AddElement( new Integer( xdenom ));
+                    mr.AddElement( new Integer( ydenom ));
+                    mr.AddElement( new Integer( xnum ));
+                    mr.AddElement( new Integer( ynum ));
+                    records.addElement( mr );
+                    scaleX = scaleX * (float)xdenom / (float)xnum;
+                    scaleY = scaleY * (float)ydenom / (float)ynum;                    
+                }
+                break;
+                
             case WMFConstants.META_CREATEBRUSHINDIRECT:
                 {
                     mr.numPoints = recSize;
@@ -247,7 +286,7 @@
                     int red = colorref & 0xff;
                     int green = ( colorref & 0xff00 ) >> 8;
                     int blue = ( colorref & 0xff0000 ) >> 16;
-                    // int flags = ( colorref & 0x3000000 ) >> 24;
+                    int flags = ( colorref & 0x3000000 ) >> 24;
                     mr.AddElement( new Integer( red ));
                     mr.AddElement( new Integer( green ));
                     mr.AddElement( new Integer( blue ));
@@ -258,7 +297,7 @@
                     records.addElement( mr );
                 }
                 break;
-
+                
             case WMFConstants.META_CREATEPENINDIRECT:
                 {
                     mr.numPoints = recSize;
@@ -268,14 +307,22 @@
                     Integer style = new Integer( readShort( is ));
                     mr.AddElement( style );
 
-                    int width     =    readShort( is );
-                    int colorref  =    readInt  ( is );
-                    /* int height = */ readShort( is );
+                    int width = readInt( is );                    
+                    int colorref =  readInt( is );
+                    
+                    /** 
+                     * sometimes records generated by PPT have a
+                     * recSize of 6 and not 5 => in this case only we have
+                     * to read a last short element
+                     **/                    
+                    //int height = readShort( is );
+                    if (recSize == 6) readShort(is);
 
-                    int red   =   colorref & 0xff;
+                    int red = colorref & 0xff;
                     int green = ( colorref & 0xff00 ) >> 8;
-                    int blue  = ( colorref & 0xff0000 ) >> 16;
-                    // int flags = ( colorref & 0x3000000 ) >> 24;
+                    int blue = ( colorref & 0xff0000 ) >> 16;
+                    int flags = ( colorref & 0x3000000 ) >> 24;
+
                     mr.AddElement( new Integer( red ));
                     mr.AddElement( new Integer( green ));
                     mr.AddElement( new Integer( blue ));
@@ -285,8 +332,20 @@
 
                     records.addElement( mr );
                 }
-                break;
+                break; 
 
+            case WMFConstants.META_SETTEXTALIGN:
+                {
+                    mr.numPoints = recSize;
+                    mr.functionId = functionId;
+                    int align = readShort( is );
+                    // need to do this, because sometimes there is more than one short
+                    if (recSize > 1) for (int i = 1; i < recSize; i++) readShort( is );
+                    mr.AddElement( new Integer( align ));
+                    records.addElement( mr );
+                }
+                break;
+                
             case WMFConstants.META_SETTEXTCOLOR:
             case WMFConstants.META_SETBKCOLOR:
                 {
@@ -297,7 +356,7 @@
                     int red = colorref & 0xff;
                     int green = ( colorref & 0xff00 ) >> 8;
                     int blue = ( colorref & 0xff0000 ) >> 16;
-                    // int flags = ( colorref & 0x3000000 ) >> 24;
+                    int flags = ( colorref & 0x3000000 ) >> 24;
                     mr.AddElement( new Integer( red ));
                     mr.AddElement( new Integer( green ));
                     mr.AddElement( new Integer( blue ));
@@ -311,24 +370,37 @@
                     mr.numPoints = recSize;
                     mr.functionId = functionId;
 
-                    int i0 = readShort( is );
-                    int i1 = readShort( is );
-                    mr.AddElement( new Integer( i1 ));
-                    mr.AddElement( new Integer( i0 ));
+                    int y = readShort( is );
+                    int x = readShort( is );
+                    mr.AddElement( new Integer( x ));
+                    mr.AddElement( new Integer( y ));
                     records.addElement( mr );
                 }
                 break;
+                
+            case WMFConstants.META_SETPOLYFILLMODE :
+                {
+                    mr.numPoints = recSize;
+                    mr.functionId = functionId;
 
+                    int mode = readShort( is );
+                    // need to do this, because sometimes there is more than one short
+                    if (recSize > 1) for (int i = 1; i < recSize; i++) readShort( is );
+                    mr.AddElement( new Integer( mode ));
+                    records.addElement( mr );
+                }
+                break;
+                
             case WMFConstants.META_POLYPOLYGON:
                 {
                     mr.numPoints = recSize;
                     mr.functionId = functionId;
 
-                    int count = readShort( is );
+                    int count = readShort( is ); // number of polygons
                     int pts[] = new int[ count ];
                     int ptCount = 0;
                     for ( int i = 0; i < count; i++ ) {
-                        pts[ i ] = readShort( is );
+                        pts[ i ] = readShort( is ); // nomber of points for the polygon
                         ptCount += pts[ i ];
                     }
                     mr.AddElement( new Integer( count ));
@@ -336,16 +408,18 @@
                     for ( int i = 0; i < count; i++ )
                         mr.AddElement( new Integer( pts[ i ] ));
 
+                    int offset = count+1;
                     for ( int i = 0; i < count; i++ ) {
                         for ( int j = 0; j < pts[ i ]; j++ ) {
-                            mr.AddElement( new Integer( readShort( is )));
-                            mr.AddElement( new Integer( readShort( is )));
+                            mr.AddElement( new Integer( readShort( is ))); // x position of the polygon
+                            mr.AddElement( new Integer( readShort( is ))); // y position of the polygon
                         }
                     }
                     records.addElement( mr );
                 }
                 break;
 
+            case WMFConstants.META_POLYLINE:                
             case WMFConstants.META_POLYGON:
                 {
                     mr.numPoints = recSize;
@@ -360,7 +434,7 @@
                     records.addElement( mr );
                 }
                 break;
-
+                
             case WMFConstants.META_ELLIPSE:
             case WMFConstants.META_INTERSECTCLIPRECT:
             case WMFConstants.META_RECTANGLE:
@@ -368,35 +442,49 @@
                     mr.numPoints = recSize;
                     mr.functionId = functionId;
 
-                    int i0 = readShort( is );
-                    int i1 = readShort( is );
-                    int i2 = readShort( is );
-                    int i3 = readShort( is );
-                    mr.AddElement( new Integer( i3 ));
-                    mr.AddElement( new Integer( i2 ));
-                    mr.AddElement( new Integer( i1 ));
-                    mr.AddElement( new Integer( i0 ));
+                    int bottom = readShort( is );
+                    int right = readShort( is );
+                    int top = readShort( is );
+                    int left = readShort( is );
+                    mr.AddElement( new Integer( left ));
+                    mr.AddElement( new Integer( top ));
+                    mr.AddElement( new Integer( right ));
+                    mr.AddElement( new Integer( bottom ));
                     records.addElement( mr );
                 }
                 break;
 
-            case WMFConstants.META_ROUNDRECT:
-                {
+            case WMFConstants.META_CREATEREGION: {
+                    mr.numPoints = recSize;
+                    mr.functionId = functionId;
+                    int left = readShort( is );
+                    int top = readShort( is );
+                    int right = readShort( is );
+                    int bottom = readShort( is );
+                    mr.AddElement( new Integer( left ));
+                    mr.AddElement( new Integer( top ));
+                    mr.AddElement( new Integer( right ));
+                    mr.AddElement( new Integer( bottom ));
+                    records.addElement( mr );
+            }
+            break;
+
+            case WMFConstants.META_ROUNDRECT: {
                     mr.numPoints = recSize;
                     mr.functionId = functionId;
 
-                    int i0 = readShort( is );
-                    int i1 = readShort( is );
-                    int i2 = readShort( is );
-                    int i3 = readShort( is );
-                    int i4 = readShort( is );
-                    int i5 = readShort( is );
-                    mr.AddElement( new Integer( i5 ));
-                    mr.AddElement( new Integer( i4 ));
-                    mr.AddElement( new Integer( i3 ));
-                    mr.AddElement( new Integer( i2 ));
-                    mr.AddElement( new Integer( i1 ));
-                    mr.AddElement( new Integer( i0 ));
+                    int el_height = readShort( is );
+                    int el_width = readShort( is );
+                    int bottom = readShort( is );
+                    int right = readShort( is );
+                    int top = readShort( is );
+                    int left = readShort( is );
+                    mr.AddElement( new Integer( left ));
+                    mr.AddElement( new Integer( top ));
+                    mr.AddElement( new Integer( right ));
+                    mr.AddElement( new Integer( bottom ));
+                    mr.AddElement( new Integer( el_width ));
+                    mr.AddElement( new Integer( el_height ));
                     records.addElement( mr );
                 }
                 break;
@@ -407,26 +495,123 @@
                     mr.numPoints = recSize;
                     mr.functionId = functionId;
 
-                    int i0 = readShort( is );
-                    int i1 = readShort( is );
-                    int i2 = readShort( is );
-                    int i3 = readShort( is );
-                    int i4 = readShort( is );
-                    int i5 = readShort( is );
-                    int i6 = readShort( is );
-                    int i7 = readShort( is );
-                    mr.AddElement( new Integer( i7 ));
-                    mr.AddElement( new Integer( i6 ));
-                    mr.AddElement( new Integer( i5 ));
-                    mr.AddElement( new Integer( i4 ));
-                    mr.AddElement( new Integer( i3 ));
-                    mr.AddElement( new Integer( i2 ));
-                    mr.AddElement( new Integer( i1 ));
-                    mr.AddElement( new Integer( i0 ));
+                    int yend = readShort( is );
+                    int xend = readShort( is );
+                    int ystart = readShort( is );
+                    int xstart = readShort( is );
+                    int bottom = readShort( is );
+                    int right = readShort( is );
+                    int top = readShort( is );
+                    int left = readShort( is );
+                    mr.AddElement( new Integer( left ));
+                    mr.AddElement( new Integer( top ));
+                    mr.AddElement( new Integer( right ));
+                    mr.AddElement( new Integer( bottom ));
+                    mr.AddElement( new Integer( xstart ));
+                    mr.AddElement( new Integer( ystart ));
+                    mr.AddElement( new Integer( xend ));
+                    mr.AddElement( new Integer( yend ));
                     records.addElement( mr );
                 }
                 break;
 
+            // META_PATBLT added
+            case WMFConstants.META_PATBLT :
+                {
+                    mr.numPoints = recSize;
+                    mr.functionId = functionId;
+
+                    int rop = readInt( is );
+                    int height = readShort( is );
+                    int width = readShort( is );
+                    int left = readShort( is );
+                    int top = readShort( is );
+                    
+                    mr.AddElement( new Integer( rop ));
+                    mr.AddElement( new Integer( height ));
+                    mr.AddElement( new Integer( width ));
+                    mr.AddElement( new Integer( top ));
+                    mr.AddElement( new Integer( left ));
+                    
+                    records.addElement( mr );                    
+                }
+                break;                
+                
+            case WMFConstants.META_SETBKMODE:
+                {
+                    mr.numPoints = recSize;
+                    mr.functionId = functionId;
+
+                    int mode = readShort( is );                
+                    mr.AddElement( new Integer( mode ));
+                    //if (recSize > 1) readShort( is );
+                    if (recSize > 1) for (int i = 1; i < recSize; i++) readShort( is );
+                    records.addElement( mr );                                        
+                }
+                break;
+                
+            // UPDATED : META_SETROP2 added
+            case WMFConstants.META_SETROP2:
+                {
+                    mr.numPoints = recSize;
+                    mr.functionId = functionId;
+
+                    // rop should always be a short, but it is sometimes an int...
+                    int rop;
+                    if (recSize == 1) rop = readShort( is );
+                    else rop = readInt( is );
+                    
+                    mr.AddElement( new Integer( rop ));
+                    records.addElement( mr );
+                }
+                break;
+            // UPDATED : META_DIBSTRETCHBLT added
+            case WMFConstants.META_DIBSTRETCHBLT:
+                {
+                    int mode = is.readInt() & 0xff;
+                    int heightSrc = readShort( is );
+                    int widthSrc = readShort( is );
+                    int sy = readShort( is );
+                    int sx = readShort( is );
+                    int heightDst = readShort( is );
+                    int widthDst = readShort( is );                    
+                    int dy = readShort( is );                                        
+                    int dx = readShort( is );  
+                    
+                    int len = 2*recSize - 20;
+                    byte bitmap[] = new byte[len];                    
+                    for (int i = 0; i < len; i++) bitmap[i] = is.readByte();
+                    
+                    mr = new MetaRecord.ByteRecord(bitmap);
+                    mr.numPoints = recSize;
+                    mr.functionId = functionId;                    
+                    mr.AddElement( new Integer( mode ));
+                    mr.AddElement( new Integer( heightSrc ));                    
+                    mr.AddElement( new Integer( widthSrc ));                                        
+                    mr.AddElement( new Integer( sy ));
+                    mr.AddElement( new Integer( sx ));
+                    mr.AddElement( new Integer( heightDst )); 
+                    mr.AddElement( new Integer( widthDst )); 
+                    mr.AddElement( new Integer( dy ));
+                    mr.AddElement( new Integer( dx ));                      
+                    records.addElement( mr );
+                }
+                break;                
+            // UPDATED : META_CREATEPATTERNBRUSH added                
+            case WMFConstants.META_DIBCREATEPATTERNBRUSH:
+                {                    
+                    int type = is.readInt() & 0xff;
+                    int len = 2*recSize - 4;
+                    byte bitmap[] = new byte[len];                    
+                    for (int i = 0; i < len; i++) bitmap[i] = is.readByte();
+                    
+                    mr = new MetaRecord.ByteRecord(bitmap);
+                    mr.numPoints = recSize;
+                    mr.functionId = functionId;                    
+                    mr.AddElement(new Integer( type ));
+                    records.addElement( mr );
+                }
+                break;                                                
             default:
                 mr.numPoints = recSize;
                 mr.functionId = functionId;
@@ -446,51 +631,6 @@
         return true;
     }
 
-    public void addObject( int type, Object obj ){
-        int startIdx = 0;
-        //     if ( type == Wmf.PEN ) {
-        //       startIdx = 2;
-        //     }
-        for ( int i = startIdx; i < numObjects; i++ ) {
-            GdiObject gdi = (GdiObject)objectVector.elementAt( i );
-            if ( gdi.used == false ) {
-                gdi.Setup( type, obj );
-                lastObjectIdx = i;
-                break;
-            }
-        }
-    }
-
-    synchronized void setReading( boolean state ){
-      bReading = state;
-    }
-
-    synchronized boolean isReading(){
-      return bReading;
-    }
-
-    /**
-     * Adds a GdiObject to the internal handle table.
-     * Wmf files specify the index as given in EMF records such as
-     * EMRCREATEPENINDIRECT whereas WMF files always use 0.
-     *
-     * This function should not normally be called by an application.
-     */
-    public void addObjectAt( int type, Object obj, int idx ) {
-      if (( idx == 0 ) || ( idx > numObjects )) {
-        addObject( type, obj );
-        return;
-      }
-      lastObjectIdx = idx;
-      for ( int i = 0; i < numObjects; i++ ) {
-        GdiObject gdi = (GdiObject)objectVector.elementAt( i );
-        if ( i == idx ) {
-          gdi.Setup( type, obj );
-          break;
-        }
-      }
-    }
-
     /**
      * Returns the current URL
      */
@@ -506,13 +646,6 @@
     }
 
     /**
-     * Returns a GdiObject from the handle table
-     */
-    public GdiObject getObject( int idx ) {
-      return (GdiObject)objectVector.elementAt( idx );
-    }
-
-    /**
      * Returns a meta record.
      */
     public MetaRecord getRecord( int idx ) {
@@ -527,77 +660,39 @@
     }
 
     /**
-     * Returns the number of GdiObjects in the handle table
-     */
-    public int getNumObjects() {
-      return numObjects;
-    }
-
-    /**
      * Returns the viewport x origin
      */
-    public int getVpX() {
+    public float getVpX() {
       return vpX;
     }
 
     /**
      * Returns the viewport y origin
      */
-    public int getVpY() {
+    public float getVpY() {
       return vpY;
     }
 
     /**
-     * Returns the viewport width
-     */
-    public int getVpW() {
-      return vpW;
-    }
-
-    /**
-     * Returns the viewport height
-     */
-    public int getVpH() {
-      return vpH;
-    }
-
-    /**
      * Sets the viewport x origin
      */
-    public void setVpX( int newValue ) {
+    public void setVpX(float newValue ) {
       vpX = newValue;
     }
 
     /**
      * Sets the viewport y origin
      */
-    public void setVpY( int newValue ) {
+    public void setVpY(float newValue ) {
       vpY = newValue;
     }
 
-    /**
-     * Sets the viewport width
-     */
-    public void setVpW( int newValue ) {
-      vpW = newValue;
-    }
-
-    /**
-     * Sets the viewport height
-     */
-    public void setVpH( int newValue ) {
-      vpH = newValue;
-    }
-
-
     transient private URL url;
 
     transient protected int numRecords;
-    transient protected int numObjects;
-    transient public int lastObjectIdx;
-    transient protected int vpX, vpY, vpW, vpH;
+    transient protected float vpX, vpY;
     transient protected Vector	records;
-    transient protected Vector	objectVector;
 
     transient protected boolean bReading = false;
+    transient private boolean _bext = true;    
 }

Modified: xmlgraphics/batik/trunk/sources/org/apache/batik/transcoder/wmf/tosvg/WMFTranscoder.java
URL: http://svn.apache.org/viewcvs/xmlgraphics/batik/trunk/sources/org/apache/batik/transcoder/wmf/tosvg/WMFTranscoder.java?rev=291468&r1=291467&r2=291468&view=diff
==============================================================================
--- xmlgraphics/batik/trunk/sources/org/apache/batik/transcoder/wmf/tosvg/WMFTranscoder.java (original)
+++ xmlgraphics/batik/trunk/sources/org/apache/batik/transcoder/wmf/tosvg/WMFTranscoder.java Sun Sep 25 12:51:54 2005
@@ -15,9 +15,12 @@
    limitations under the License.
 
 */
+
 package org.apache.batik.transcoder.wmf.tosvg;
 
 import java.awt.Dimension;
+import java.awt.BasicStroke;
+import java.awt.geom.Rectangle2D;
 import java.io.BufferedInputStream;
 import java.io.DataInputStream;
 import java.io.File;
@@ -31,8 +34,10 @@
 import java.net.URL;
 import java.net.URLConnection;
 
+import org.w3c.dom.svg.SVGDocument;
+import org.apache.batik.transcoder.TranscodingHints;
+import org.apache.batik.transcoder.keys.*;
 import org.apache.batik.dom.svg.SVGDOMImplementation;
-import org.apache.batik.svggen.SVGGraphics2D;
 import org.apache.batik.transcoder.AbstractTranscoder;
 import org.apache.batik.transcoder.TranscoderException;
 import org.apache.batik.transcoder.TranscoderInput;
@@ -42,34 +47,63 @@
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.xml.sax.XMLFilter;
+import org.apache.batik.svggen.SVGGraphics2D;
+import org.apache.batik.util.SVGConstants;
+import org.apache.batik.transcoder.ToSVGAbstractTranscoder;
 
+import org.w3c.dom.Node;
+import org.w3c.dom.DocumentFragment;
 
-/**
- * This class implements the <tt>Transcoder</tt> interface and
- * can convert a WMF input document into an SVG document.
+/** This class implements the <tt>Transcoder</tt> interface and
+ *  can convert a WMF input document into an SVG document.
+ *  <p>This class is copied from 
+ *  batik org.apache.batik.transcoder.wmf.tosvg.WMFTranscoder class.</p>
+ *  <p>It can use <tt>TranscoderInput</tt> that are either a URI
+ *  or a <tt>InputStream</tt> or a <tt>Reader</tt>. The
+ *  <tt>XMLReader</tt> and <tt>Document</tt> <tt>TranscoderInput</tt>
+ *  types are not supported.</p>
  *
- * It can use <tt>TranscoderInput</tt> that are either a URI
- * or a <tt>InputStream</tt> or a <tt>Reader</tt>. The
- * <tt>XMLReader</tt> and <tt>Document</tt> <tt>TranscoderInput</tt>
- * types are not supported.
+ *  <p>This transcoder can use <tt>TranscoderOutputs</tt> that are
+ *  of any type except the <tt>XMLFilter</tt> type.</p>
  *
- * This transcoder can use <tt>TranscoderOutputs</tt> that are
- * of any type except the <tt>XMLFilter</tt> type.
+ *  <p>Corrected bugs from the original class:</p>
+ *  <ul>
+ *  <li> Manage images size</li>
+ *  </ul>
+ *  <p>Exemple of use :</p>
+ *  <pre>
+ *    WMFTranscoder transcoder = new WMFTranscoder();
+ *    try { 
+ *       TranscoderInput wmf = new TranscoderInput(wmffile.toURL().toString());
+ *       TranscoderOutput svg = new TranscoderOutput(new FileOutputStream(svgFile));
+ *       transcoder.transcode(wmf, svg);
+ *    } catch (MalformedURLException e){
+ *       throw new TranscoderException(e);
+ *    } catch (IOException e){
+ *       throw new TranscoderException(e);
+ *    }
+ *  </pre> 
+ *  <p>Several transcoding hints are available for this transcoder :</p>
+ *  <ul>
+ *  <li>KEY_INPUT_WIDTH, KEY_INPUT_HEIGHT, KEY_XOFFSET, KEY_YOFFSET : this Integer values allows to 
+ *  set the  portion of the image to transcode, defined by the width, height, and offset 
+ *  of this portion in Metafile units.
+ *  </ul>
+ *  <pre>
+ *     transcoder.addTranscodingHint(FromWMFTranscoder.KEY_INPUT_WIDTH, new Integer(input_width));
+ *  </pre>
+ *  </li>
+ *  <li>KEY_WIDTH, KEY_HEIGHT : this Float values allows to force the width and height of the output:
+ *  </ul>
+ *  <pre>
+ *     transcoder.addTranscodingHint(FromWMFTranscoder.KEY_WIDTH, new Float(width));
+ *  </pre>
+ *  </li>
+ *  </ul>
  *
- * @version $Id$
- * @author <a href="mailto:luano@asd.ie">Luan O'Carroll</a>
  */
-public class WMFTranscoder extends AbstractTranscoder
-    implements SVGConstants{
-
-    /**
-     * Error codes for the WMFTranscoder
-     */
-    public static final int WMF_TRANSCODER_ERROR_BASE = 0xff00;
-    public static final int ERROR_NULL_INPUT = WMF_TRANSCODER_ERROR_BASE + 0;
-    public static final int ERROR_INCOMPATIBLE_INPUT_TYPE = WMF_TRANSCODER_ERROR_BASE + 1;
-    public static final int ERROR_INCOMPATIBLE_OUTPUT_TYPE = WMF_TRANSCODER_ERROR_BASE + 2;
-
+public class WMFTranscoder extends ToSVGAbstractTranscoder {
+    
     /**
      * Default constructor
      */
@@ -93,136 +127,117 @@
         // Build a RecordStore from the input
         //
         WMFRecordStore currentStore = new WMFRecordStore();
-        try{
+        try {
             currentStore.read(is);
-        }catch(IOException e){
+        } catch (IOException e){
             handler.fatalError(new TranscoderException(e));
             return;
         }
 
-        //
+        // determines the width and height of output image
+        float wmfwidth; // width in pixels
+        float wmfheight; // height in pixels
+        float conv = 1f; // conversion factor
+        
+        if (hints.containsKey(KEY_INPUT_WIDTH)) {
+            wmfwidth = ((Integer)hints.get(KEY_INPUT_WIDTH)).intValue();
+            wmfheight = ((Integer)hints.get(KEY_INPUT_HEIGHT)).intValue();            
+        } else {
+            wmfwidth = currentStore.getWidthPixels();
+            wmfheight = currentStore.getHeightPixels();
+        }
+        float width = wmfwidth;
+        float height = wmfheight;
+        
+        // change the output width and height if required
+        if (hints.containsKey(KEY_WIDTH)) {
+            width = ((Float)hints.get(KEY_WIDTH)).floatValue();
+            conv = width / wmfwidth;
+            height = height * width / wmfwidth;
+        }
+        
+        // determine the offset values
+        int xOffset = 0;
+        int yOffset = 0;
+        if (hints.containsKey(KEY_XOFFSET)) {
+            xOffset = ((Integer)hints.get(KEY_XOFFSET)).intValue();
+        }        
+        if (hints.containsKey(KEY_YOFFSET)) {
+            yOffset = ((Integer)hints.get(KEY_YOFFSET)).intValue();
+        }        
+
+        // Set the size and viewBox on the output document
+        float sizeFactor = currentStore.getUnitsToPixels() * conv;
+        
+        int vpX = (int)(currentStore.getVpX() * sizeFactor);
+        int vpY = (int)(currentStore.getVpY() * sizeFactor);
+
+        int vpW;
+        int vpH;
+        // if we took only a part of the image, we use its dimension for computing
+        if (hints.containsKey(KEY_INPUT_WIDTH)) {
+            vpW = (int)(((Integer)hints.get(KEY_INPUT_WIDTH)).intValue() * conv);
+            vpH = (int)(((Integer)hints.get(KEY_INPUT_HEIGHT)).intValue() * conv);
+        // else we took the whole image dimension
+        } else {
+            vpW = (int)(currentStore.getWidthUnits() * sizeFactor);
+            vpH = (int)(currentStore.getHeightUnits() * sizeFactor);
+        }        
+        
         // Build a painter for the RecordStore
-        //
-        WMFPainter painter = new WMFPainter(currentStore);
+        WMFPainter painter = new WMFPainter(currentStore, xOffset, yOffset, conv);
 
-        //
         // Use SVGGraphics2D to generate SVG content
-        //
-        DOMImplementation domImpl
-            = SVGDOMImplementation.getDOMImplementation();
-
-        Document doc = domImpl.createDocument(SVG_NAMESPACE_URI,
-                                              SVG_SVG_TAG, null);
-
-        SVGGraphics2D svgGenerator = new SVGGraphics2D(doc);
+        Document doc = this.createDocument(output);
+        svgGenerator = new SVGGraphics2D(doc);
+                
+        /** set precision
+         ** otherwise Ellipses aren't working (for example) (because of Decimal format 
+         * modifications ins SVGGenerator Context
+         */
+        svgGenerator.getGeneratorContext().setPrecision(4);
 
         painter.paint(svgGenerator);
-
-        //
-        // Set the size and viewBox on the output document
-        //
-        int vpX = currentStore.getVpX();
-        int vpY = currentStore.getVpY();
-        int vpW = currentStore.getVpW();
-        int vpH = currentStore.getVpH();
+       
         svgGenerator.setSVGCanvasSize(new Dimension(vpW, vpH));
 
         Element svgRoot = svgGenerator.getRoot();
+        
         svgRoot.setAttributeNS(null, SVG_VIEW_BOX_ATTRIBUTE,
                                "" + vpX + " " + vpY + " " +
                                vpW + " " + vpH );
-
-        //
+        
         // Now, write the SVG content to the output
-        //
         writeSVGToOutput(svgGenerator, svgRoot, output);
     }
 
     /**
-     * Writes the SVG content held by the svgGenerator to the
-     * <tt>TranscoderOutput</tt>.
-     */
-    private void writeSVGToOutput(SVGGraphics2D svgGenerator,
-                                  Element svgRoot,
-                                  TranscoderOutput output)
-        throws TranscoderException {
-        // XMLFilter
-        XMLFilter xmlFilter = output.getXMLFilter();
-        if(xmlFilter != null){
-            handler.fatalError(new TranscoderException("" + ERROR_INCOMPATIBLE_OUTPUT_TYPE));
-        }
-
-        // <!> FIX ME: SHOULD HANDLE DOCUMENT INPUT
-        Document doc = output.getDocument();
-        if(doc != null){
-            handler.fatalError(new TranscoderException("" + ERROR_INCOMPATIBLE_OUTPUT_TYPE));
-        }
-
-        try{
-            // Output stream
-            OutputStream os = output.getOutputStream();
-            if( os != null ){
-                svgGenerator.stream(svgRoot, new OutputStreamWriter(os));
-                return;
-            }
-
-            // Writer
-            Writer wr = output.getWriter();
-            if( wr != null ){
-                svgGenerator.stream(svgRoot, wr);
-                return;
-            }
-
-            // URI
-            String uri = output.getURI();
-            if( uri != null ){
-                try{
-                    URL url = new URL(uri);
-                    URLConnection urlCnx = url.openConnection();
-                    os = urlCnx.getOutputStream();
-                    svgGenerator.stream(svgRoot, new OutputStreamWriter(os));
-                    return;
-                }catch(MalformedURLException e){
-                    handler.fatalError(new TranscoderException(e));
-                }catch(IOException e){
-                    handler.fatalError(new TranscoderException(e));
-                }
-            }
-        }catch(IOException e){
-            throw new TranscoderException(e);
-        }
-
-        throw new TranscoderException("" + ERROR_INCOMPATIBLE_OUTPUT_TYPE);
-
-    }
-
-    /**
      * Checks that the input is one of URI or an <tt>InputStream</tt>
      * returns it as a DataInputStream
      */
     private DataInputStream getCompatibleInput(TranscoderInput input)
         throws TranscoderException {
         // Cannot deal with null input
-        if(input == null){
+        if (input == null){
             handler.fatalError(new TranscoderException("" + ERROR_NULL_INPUT));
         }
 
         // Can deal with InputStream
         InputStream in = input.getInputStream();
-        if(in != null){
+        if (in != null){
             return new DataInputStream(new BufferedInputStream(in));
         }
 
         // Can deal with URI
         String uri = input.getURI();
-        if(uri != null){
+        if (uri != null){
             try{
                 URL url = new URL(uri);
                 in = url.openStream();
                 return new DataInputStream(new BufferedInputStream(in));
-            }catch(MalformedURLException e){
+            } catch (MalformedURLException e){
                 handler.fatalError(new TranscoderException(e));
-            }catch(IOException e){
+            } catch (IOException e){
                 handler.fatalError(new TranscoderException(e));
             }
         }
@@ -231,11 +246,6 @@
         return null;
     }
 
-    public static final String USAGE = "The WMFTranscoder converts a WMF document into an SVG document. \n" +
-        "This simple application generates SVG documents that have the same name, but a where the .wmf extension \n" +
-        "is replaced with .svg. To run the application, type the following at the command line: \n" +
-        "java org.apache.batik.transcoder.wmf.tosvg.WMFTranscoder fileName [fileName]+";
-
     public static final String WMF_EXTENSION = ".wmf";
     public static final String SVG_EXTENSION = ".svg";
 
@@ -244,7 +254,7 @@
      */
     public static void main(String args[]) throws TranscoderException {
         if(args.length < 1){
-            System.err.println(USAGE);
+            System.out.println("Usage : WMFTranscoder.main <file 1> ... <file n>"); 
             System.exit(1);
         }
 
@@ -261,7 +271,7 @@
                 String outputFileName = fileName.substring(0, fileName.toLowerCase().indexOf(WMF_EXTENSION)) + SVG_EXTENSION;
                 File inputFile = new File(fileName);
                 File outputFile = new File(outputFileName);
-                try{
+                try {
                     TranscoderInput input = new TranscoderInput(inputFile.toURL().toString());
                     TranscoderOutput output = new TranscoderOutput(new FileOutputStream(outputFile));
                     transcoder.transcode(input, output);

Modified: xmlgraphics/batik/trunk/test-references/org/apache/batik/svggen/ATransform.svg
URL: http://svn.apache.org/viewcvs/xmlgraphics/batik/trunk/test-references/org/apache/batik/svggen/ATransform.svg?rev=291468&r1=291467&r2=291468&view=diff
==============================================================================
--- xmlgraphics/batik/trunk/test-references/org/apache/batik/svggen/ATransform.svg (original)
+++ xmlgraphics/batik/trunk/test-references/org/apache/batik/svggen/ATransform.svg Sun Sep 25 12:51:54 2005
@@ -1,28 +1,33 @@
 <?xml version="1.0" encoding="UTF-8"?>
-
-<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.0//EN' 'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd'>
-<svg fill-opacity="1" xmlns:xlink="http://www.w3.org/1999/xlink" color-interpolation="auto" color-rendering="auto" text-rendering="auto" stroke="black" stroke-linecap="square" width="300" stroke-miterlimit="10" stroke-opacity="1" shape-rendering="auto" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" height="400" xmlns="http://www.w3.org/2000/svg" font-family="&apos;Arial&apos;" font-style="normal" stroke-linejoin="miter" font-size="12" image-rendering="auto" stroke-dashoffset="0">
-  <!--Generated by the Batik Graphics2D SVG Generator-->
-  <defs id="genericDefs" />
-  <g>
-    <g text-rendering="optimizeLegibility" transform="translate(0,30)">
-      <text xml:space="preserve" x="10" y="12" stroke="none">Default transform</text>
-      <rect width="50" x="10" height="30" y="20" stroke="none" />
-      <text xml:space="preserve" x="10" y="12" transform="translate(0,90)" stroke="none">Translate applied</text>
-      <rect x="10" y="20" transform="translate(0,90)" width="50" height="30" stroke="none" />
-      <text xml:space="preserve" x="10" y="12" transform="translate(35,215) rotate(90) translate(-35,-35)" stroke="none">Rotate about center</text>
-      <rect x="10" y="20" transform="translate(35,215) rotate(90) translate(-35,-35)" width="50" height="30" stroke="none" />
-    </g>
-    <g text-rendering="optimizeLegibility" transform="matrix(1,0,0,1,0,30) translate(150,0)">
-      <text xml:space="preserve" x="10" y="12" stroke="none">Scale (sx=2, sy=1)</text>
-      <rect x="10" y="20" transform="scale(2,1)" width="50" height="30" stroke="none" />
-    </g>
-    <g text-rendering="optimizeLegibility" transform="matrix(1,0,0,1,0,30) translate(150,90)">
-      <text xml:space="preserve" x="10" y="12" stroke="none">Shear</text>
-      <rect x="10" y="20" transform="matrix(1,1,0.2,1,0,0)" width="50" height="30" stroke="none" />
-    </g>
-    <g fill="rgb(0,0,0)" text-rendering="optimizeLegibility" fill-opacity="0.501960813999" stroke-opacity="0.501960813999" stroke="rgb(0,0,0)">
-      <rect width="56" x="164" height="80" y="150" stroke="none" />
-    </g>
-  </g>
-</svg>
+<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.0//EN'
+          'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd'>
+<svg fill-opacity="1" xmlns:xlink="http://www.w3.org/1999/xlink" color-interpolation="auto" color-rendering="auto" text-rendering="auto" stroke="black" stroke-linecap="square" width="300" stroke-miterlimit="10" stroke-opacity="1" shape-rendering="auto" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" height="400" xmlns="http://www.w3.org/2000/svg" font-family="&apos;Arial&apos;" font-style="normal" stroke-linejoin="miter" font-size="12" image-rendering="auto" stroke-dashoffset="0"
+><!--Generated by the Batik Graphics2D SVG Generator--><defs id="genericDefs"
+  /><g
+  ><g text-rendering="optimizeLegibility" transform="translate(0,30)"
+    ><text xml:space="preserve" x="10" y="12" stroke="none"
+      >Default transform</text
+      ><rect width="50" x="10" height="30" y="20" stroke="none"
+      /><text xml:space="preserve" x="10" y="12" transform="translate(0,90)" stroke="none"
+      >Translate applied</text
+      ><rect x="10" y="20" transform="translate(0,90)" width="50" height="30" stroke="none"
+      /><text xml:space="preserve" x="10" y="12" transform="translate(35,215) rotate(90) translate(-35,-35)" stroke="none"
+      >Rotate about center</text
+      ><rect x="10" y="20" transform="translate(35,215) rotate(90) translate(-35,-35)" width="50" height="30" stroke="none"
+    /></g
+    ><g text-rendering="optimizeLegibility" transform="matrix(1,0,0,1,0,30) translate(150,0)"
+    ><text xml:space="preserve" x="10" y="12" stroke="none"
+      >Scale (sx=2, sy=1)</text
+      ><rect x="10" y="20" transform="scale(2,1)" width="50" height="30" stroke="none"
+    /></g
+    ><g text-rendering="optimizeLegibility" transform="matrix(1,0,0,1,0,30) translate(150,90)"
+    ><text xml:space="preserve" x="10" y="12" stroke="none"
+      >Shear</text
+      ><rect x="10" y="20" transform="matrix(1,1,0.2,1,0,0)" width="50" height="30" stroke="none"
+    /></g
+    ><g fill="rgb(0,0,0)" text-rendering="optimizeLegibility" fill-opacity="0.501960813999" stroke-opacity="0.501960813999" stroke="rgb(0,0,0)"
+    ><rect width="56" x="164" height="80" y="150" stroke="none"
+    /></g
+  ></g
+></svg
+>