/*

   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements.  See the NOTICE file distributed with
   this work for additional information regarding copyright ownership.
   The ASF licenses this file to You under the Apache License, Version 2.0
   (the "License"); you may not use this file except in compliance with
   the License.  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

 */

package org.apache.batik.transcoder.wmf.tosvg;

import java.awt.Color;
import java.awt.Font;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import org.apache.batik.ext.awt.geom.Polygon2D;
import org.apache.batik.ext.awt.geom.Polyline2D;
import org.apache.batik.transcoder.wmf.WMFConstants;

This class holds simple properties about a WMF Metafile. It can be used whenever general information must be retrieved about this file.
Version:$Id: WMFHeaderProperties.java 1831630 2018-05-15 12:56:55Z ssteiner $
/** * This class holds simple properties about a WMF Metafile. It can be used * whenever general information must be retrieved about this file. * * @version $Id: WMFHeaderProperties.java 1831630 2018-05-15 12:56:55Z ssteiner $ */
public class WMFHeaderProperties extends AbstractWMFReader { private static final Integer INTEGER_0 = 0; protected DataInputStream stream; private int _bleft, _bright, _btop, _bbottom, _bwidth, _bheight; private int _ileft, _iright, _itop, _ibottom; private float scale = 1.0f; private int startX = 0; private int startY = 0; private int currentHorizAlign = 0; private int currentVertAlign = 0; private WMFFont wf = null; private static final FontRenderContext fontCtx = new FontRenderContext(new AffineTransform(), false, true); private transient boolean firstEffectivePaint = true; public static final int PEN = 1; public static final int BRUSH = 2; public static final int FONT = 3; public static final int NULL_PEN = 4; public static final int NULL_BRUSH = 5; public static final int PALETTE = 6; public static final int OBJ_BITMAP = 7; public static final int OBJ_REGION = 8;
Creates a new WMFHeaderProperties, and sets the associated WMF File.
Params:
  • wmffile – the WMF Metafile
/** Creates a new WMFHeaderProperties, and sets the associated WMF File. * @param wmffile the WMF Metafile */
public WMFHeaderProperties(File wmffile) throws IOException { super(); reset(); stream = new DataInputStream(new BufferedInputStream(new FileInputStream(wmffile))); read(stream); stream.close(); }
Creates a new WMFHeaderProperties, with no associated file.
/** Creates a new WMFHeaderProperties, with no associated file. */
public WMFHeaderProperties() { super(); } public void closeResource() { try { if (stream != null) stream.close(); } catch (IOException e) { } }
Creates the properties associated file.
/** Creates the properties associated file. */
public void setFile(File wmffile) throws IOException { stream = new DataInputStream(new BufferedInputStream(new FileInputStream(wmffile))); read(stream); stream.close(); }
Resets the internal storage and viewport coordinates.
/** * Resets the internal storage and viewport coordinates. */
public void reset() { left = 0; right = 0; top = 1000; bottom = 1000; inch = 84; _bleft = -1; _bright = -1; _btop = -1; _bbottom = -1; _ileft = -1; _iright = -1; _itop = -1; _ibottom = -1; _bwidth = -1; _bheight= -1; vpW = -1; vpH = -1; vpX = 0; vpY = 0; startX = 0; startY = 0; scaleXY = 1f; firstEffectivePaint = true; }
Get the associated stream.
/** Get the associated stream. */
public DataInputStream getStream() { return stream; } protected boolean readRecords(DataInputStream is) throws IOException { // effective reading of the rest of the file short functionId = 1; int recSize = 0; int gdiIndex; // the last Object index int brushObject = -1; // the last brush int penObject = -1; // the last pen int fontObject = -1; // the last font GdiObject gdiObj; 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; switch ( functionId ) { case WMFConstants.META_SETMAPMODE: { int mapmode = readShort( is ); // change isotropic if mode is anisotropic if (mapmode == WMFConstants.MM_ANISOTROPIC) isotropic = false; } break; case WMFConstants.META_SETWINDOWORG: { vpY = readShort( is ); vpX = readShort( is ); } break; case WMFConstants.META_SETWINDOWEXT: { vpH = readShort( is ); vpW = readShort( is ); if (! isotropic) scaleXY = (float)vpW / vpH; vpW = (int)(vpW * scaleXY); } break; case WMFConstants.META_CREATEPENINDIRECT: { int objIndex = 0; int penStyle = readShort( is ); readInt( is ); // width // color definition int colorref = readInt( is ); int red = colorref & 0xff; int green = ( colorref & 0xff00 ) >> 8; int blue = ( colorref & 0xff0000 ) >> 16; Color color = new Color( red, green, blue); if (recSize == 6) readShort(is); // if size greater than 5 if ( penStyle == WMFConstants.META_PS_NULL ) { objIndex = addObjectAt( NULL_PEN, color, objIndex ); } else { objIndex = addObjectAt( PEN, color, objIndex ); } } break; case WMFConstants.META_CREATEBRUSHINDIRECT: { int objIndex = 0; int brushStyle = readShort( is ); // color definition int colorref = readInt( is ); int red = colorref & 0xff; int green = ( colorref & 0xff00 ) >> 8; int blue = ( colorref & 0xff0000 ) >> 16; Color color = new Color( red, green, blue); readShort( is ); // hatch if ( brushStyle == WMFConstants.META_PS_NULL ) { objIndex = addObjectAt( NULL_BRUSH, color, objIndex); } else objIndex = addObjectAt(BRUSH, color, objIndex ); } break; case WMFConstants.META_SETTEXTALIGN: 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 ); currentHorizAlign = WMFUtilities.getHorizontalAlignment(align); currentVertAlign = WMFUtilities.getVerticalAlignment(align); break; case WMFConstants.META_EXTTEXTOUT: { int y = readShort( is ); int x = (int)(readShort( is ) * scaleXY); 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 = (int)(readShort( is ) * scaleXY); y1 = readShort( is ); x2 = (int)(readShort( is ) * scaleXY); y2 = readShort( is ); read += 4; clipped = true; } byte[] bstr = new byte[ lenText ]; int i = 0; for ( ; i < lenText; i++ ) { bstr[ i ] = is.readByte(); } String sr = WMFUtilities.decodeString(wf, bstr); 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 ); TextLayout layout = new TextLayout( sr, wf.font, fontCtx ); int lfWidth = (int)layout.getBounds().getWidth(); x = (int)layout.getBounds().getX(); int lfHeight = (int)getVerticalAlignmentValue(layout, currentVertAlign); resizeBounds(x, y); resizeBounds(x+lfWidth, y+lfHeight); firstEffectivePaint = false; } break; case WMFConstants.META_DRAWTEXT: case WMFConstants.META_TEXTOUT: { int len = readShort( is ); int read = 1; // used to track the actual size really read byte[] bstr = new byte[ len ]; for ( int i = 0; i < len; i++ ) { bstr[ i ] = is.readByte(); } String sr = WMFUtilities.decodeString(wf, bstr); /* 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 y = readShort( is ); int x = (int)(readShort( is ) * scaleXY); read += 2; // if the record was not completely read, finish reading if (read < recSize) for (int j = read; j < recSize; j++) readShort( is ); TextLayout layout = new TextLayout( sr, wf.font, fontCtx ); int lfWidth = (int)layout.getBounds().getWidth(); x = (int)layout.getBounds().getX(); int lfHeight = (int)getVerticalAlignmentValue(layout, currentVertAlign); resizeBounds(x, y); resizeBounds(x+lfWidth, y+lfHeight); } break; case WMFConstants.META_CREATEFONTINDIRECT: { int lfHeight = readShort( is ); float size = (int)(scaleY * lfHeight); int lfWidth = readShort( is ); int escape = readShort( is ); int orient = readShort( is ); int weight = readShort( is ); int italic = is.readByte(); int underline = is.readByte(); int strikeOut = is.readByte(); int charset = is.readByte() & 0x00ff; int lfOutPrecision = is.readByte(); int lfClipPrecision = is.readByte(); int lfQuality = is.readByte(); int lfPitchAndFamily = is.readByte(); int style = italic > 0 ? Font.ITALIC : Font.PLAIN; style |= (weight > 400) ? Font.BOLD : Font.PLAIN; // 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 ]; byte ch; for ( int i = 0; i < len; i++ ) lfFaceName[ i ] = is.readByte(); String face = new String( lfFaceName ); // FIXED : management of font names int d = 0; while ((d < face.length()) && ((Character.isLetterOrDigit(face.charAt(d))) || (Character.isWhitespace(face.charAt(d))))) d++; if (d > 0) face = face.substring(0,d); else face = "System"; if ( size < 0 ) size = -size /* * -1.3 */; int objIndex = 0; Font f = new Font(face, style, (int)size); f = f.deriveFont(size); WMFFont wf = new WMFFont(f, charset, underline, strikeOut, italic, weight, orient, escape); objIndex = addObjectAt( FONT, wf , objIndex ); } break; case WMFConstants.META_CREATEREGION: { int objIndex = 0; for ( int j = 0; j < recSize; j++ ) readShort(is); // read all fields objIndex = addObjectAt( PALETTE, INTEGER_0, 0 ); } break; case WMFConstants.META_CREATEPALETTE: { int objIndex = 0; for ( int j = 0; j < recSize; j++ ) readShort(is); // read all fields objIndex = addObjectAt( OBJ_REGION, INTEGER_0, 0 ); } break; case WMFConstants.META_SELECTOBJECT: gdiIndex = readShort(is); if (( gdiIndex & 0x80000000 ) != 0 ) // Stock Object break; gdiObj = getObject( gdiIndex ); if ( !gdiObj.used ) break; switch( gdiObj.type ) { case PEN: penObject = gdiIndex; break; case BRUSH: brushObject = gdiIndex; break; case FONT: { this.wf = ((WMFFont)gdiObj.obj); fontObject = gdiIndex; } break; case NULL_PEN: penObject = -1; break; case NULL_BRUSH: brushObject = -1; break; } break; case WMFConstants.META_DELETEOBJECT: gdiIndex = readShort(is); gdiObj = getObject( gdiIndex ); if ( gdiIndex == brushObject ) brushObject = -1; else if ( gdiIndex == penObject ) penObject = -1; else if ( gdiIndex == fontObject ) fontObject = -1; gdiObj.clear(); break; case WMFConstants.META_LINETO: { int y = readShort( is ); int x = (int)(readShort( is ) * scaleXY); if (penObject >= 0) { resizeBounds(startX, startY); resizeBounds(x, y); firstEffectivePaint = false; } startX = x; startY = y; } break; case WMFConstants.META_MOVETO: { startY = readShort( is ); startX = (int)(readShort( is ) * scaleXY); } break; case WMFConstants.META_POLYPOLYGON: { int count = readShort( is ); int[] pts = new int[ count ]; int ptCount = 0; for ( int i = 0; i < count; i++ ) { pts[ i ] = readShort( is ); ptCount += pts[ i ]; } int offset = count+1; for ( int i = 0; i < count; i++ ) { for ( int j = 0; j < pts[ i ]; j++ ) { // FIXED 115 : correction preliminary images dimensions int x = (int)(readShort( is ) * scaleXY); int y = readShort( is ); if ((brushObject >= 0) || (penObject >= 0)) resizeBounds(x, y); } } firstEffectivePaint = false; } break; case WMFConstants.META_POLYGON: { int count = readShort( is ); float[] _xpts = new float[ count+1 ]; float[] _ypts = new float[ count+1 ]; for ( int i = 0; i < count; i++ ) { _xpts[i] = readShort( is ) * scaleXY; _ypts[i] = readShort( is ); } _xpts[count] = _xpts[0]; _ypts[count] = _ypts[0]; Polygon2D pol = new Polygon2D(_xpts, _ypts, count); paint(brushObject, penObject, pol); } break; case WMFConstants.META_POLYLINE: { int count = readShort( is ); float[] _xpts = new float[ count ]; float[] _ypts = new float[ count ]; for ( int i = 0; i < count; i++ ) { _xpts[i] = readShort( is ) * scaleXY; _ypts[i] = readShort( is ); } Polyline2D pol = new Polyline2D(_xpts, _ypts, count); paintWithPen(penObject, pol); } break; case WMFConstants.META_ELLIPSE: case WMFConstants.META_INTERSECTCLIPRECT: case WMFConstants.META_RECTANGLE: { int bot = readShort( is ); int right = (int)(readShort( is ) * scaleXY); int top = readShort( is ); int left = (int)(readShort( is ) * scaleXY); Rectangle2D.Float rec = new Rectangle2D.Float(left, top, right-left, bot-top); paint(brushObject, penObject, rec); } break; case WMFConstants.META_ROUNDRECT: { readShort( is ); readShort( is ); int bot = readShort( is ); int right = (int)(readShort( is ) * scaleXY); int top = readShort( is ); int left = (int)(readShort( is ) * scaleXY); Rectangle2D.Float rec = new Rectangle2D.Float(left, top, right-left, bot-top); paint(brushObject, penObject, rec); } break; case WMFConstants.META_ARC: case WMFConstants.META_CHORD: case WMFConstants.META_PIE: { readShort( is ); readShort( is ); readShort( is ); readShort( is ); int bot = readShort( is ); int right = (int)(readShort( is ) * scaleXY); int top = readShort( is ); int left = (int)(readShort( is ) * scaleXY); Rectangle2D.Float rec = new Rectangle2D.Float(left, top, right-left, bot-top); paint(brushObject, penObject, rec); } break; case WMFConstants.META_PATBLT : { readInt( is ); // rop int height = readShort( is ); int width = (int)(readShort( is ) * scaleXY); int left = (int)(readShort( is ) * scaleXY); int top = readShort( is ); if (penObject >= 0) resizeBounds(left, top); if (penObject >= 0) resizeBounds(left+width, top+height); } break; // UPDATED : META_DIBSTRETCHBLT added case WMFConstants.META_DIBSTRETCHBLT: { is.readInt(); // mode readShort( is ); // heightSrc readShort( is ); // widthSrc readShort( is ); // sy readShort( is ); // sx float heightDst = readShort( is ); float widthDst = readShort( is ) * scaleXY; float dy = readShort( is ) * getVpWFactor() * inch / PIXEL_PER_INCH; float dx = readShort( is ) * getVpWFactor() * inch / PIXEL_PER_INCH * scaleXY; widthDst = widthDst * getVpWFactor() * inch / PIXEL_PER_INCH; heightDst = heightDst * getVpHFactor() * inch / PIXEL_PER_INCH; resizeImageBounds((int)dx, (int)dy); resizeImageBounds((int)(dx + widthDst), (int)(dy + heightDst)); int len = 2*recSize - 20; for (int i = 0; i < len; i++) is.readByte(); } break; case WMFConstants.META_STRETCHDIB: { is.readInt(); // mode readShort( is ); // usage readShort( is ); // heightSrc readShort( is ); // widthSrc readShort( is ); // sy readShort( is ); // sx float heightDst = readShort( is ); float widthDst = readShort( is ) * scaleXY; float dy = readShort( is ) * getVpHFactor() * inch / PIXEL_PER_INCH; float dx = readShort( is ) * getVpHFactor() * inch / PIXEL_PER_INCH * scaleXY; widthDst = widthDst * getVpWFactor() * inch / PIXEL_PER_INCH; heightDst = heightDst * getVpHFactor() * inch / PIXEL_PER_INCH; resizeImageBounds((int)dx, (int)dy); resizeImageBounds((int)(dx + widthDst), (int)(dy + heightDst)); int len = 2*recSize - 22; byte[] bitmap = new byte[len]; for (int i = 0; i < len; i++) bitmap[i] = is.readByte(); } break; case WMFConstants.META_DIBBITBLT: { is.readInt(); // mode readShort( is ); //sy readShort( is ); //sx readShort( is ); // hdc float height = readShort( is ) * (float)inch / PIXEL_PER_INCH * getVpHFactor(); float width = readShort( is ) * (float)inch / PIXEL_PER_INCH * getVpWFactor() * scaleXY; float dy = inch / PIXEL_PER_INCH * getVpHFactor() * readShort( is ); float dx = inch / PIXEL_PER_INCH * getVpWFactor() * readShort( is ) * scaleXY; resizeImageBounds((int)dx, (int)dy); resizeImageBounds((int)(dx + width), (int)(dy + height)); } break; default: for ( int j = 0; j < recSize; j++ ) readShort(is); break; } } // sets the width, height, etc of the image if the file does not have an APM (in this case it is retrieved // from the viewport) if (! isAldus) { width = vpW; height = vpH; right = vpX; left = vpX + vpW; top = vpY; bottom = vpY + vpH; } resetBounds(); return true; }
Returns:the width of the Rectangle bounding the figures enclosed in the Metafile, in pixels
/** @return the width of the Rectangle bounding the figures enclosed in * the Metafile, in pixels */
public int getWidthBoundsPixels() { return _bwidth; }
Returns:the height of the Rectangle bounding the figures enclosed in the Metafile, in pixels.
/** @return the height of the Rectangle bounding the figures enclosed in * the Metafile, in pixels. */
public int getHeightBoundsPixels() { return _bheight; }
Returns:the width of the Rectangle bounding the figures enclosed in the Metafile, in Metafile Units.
/** @return the width of the Rectangle bounding the figures enclosed in * the Metafile, in Metafile Units. */
public int getWidthBoundsUnits() { return (int)((float)inch * _bwidth / PIXEL_PER_INCH); }
Returns:the height of the Rectangle bounding the figures enclosed in the Metafile in Metafile Units.
/** @return the height of the Rectangle bounding the figures enclosed in * the Metafile in Metafile Units. */
public int getHeightBoundsUnits() { return (int)((float)inch * _bheight / PIXEL_PER_INCH); }
Returns:the X offset of the Rectangle bounding the figures enclosed in the Metafile.
/** @return the X offset of the Rectangle bounding the figures enclosed in * the Metafile. */
public int getXOffset() { return _bleft; }
Returns:the Y offset of the Rectangle bounding the figures enclosed in the Metafile.
/** @return the Y offset of the Rectangle bounding the figures enclosed in * the Metafile. */
public int getYOffset() { return _btop; } private void resetBounds() { // calculate geometry size scale = (float)getWidthPixels() / vpW; if (_bright != -1) { _bright = (int)(scale * (vpX +_bright)); _bleft = (int)(scale * (vpX +_bleft)); _bbottom = (int)(scale * (vpY +_bbottom)); _btop = (int)(scale * (vpY +_btop)); } // calculate image size if (_iright != -1) { _iright = (int)((float)_iright * getWidthPixels() / width); _ileft = (int)((float)_ileft * getWidthPixels() / width); _ibottom = (int)((float)_ibottom * getWidthPixels() / width); _itop = (int)((float)_itop * getWidthPixels() / width); // merge image and geometry size if ((_bright == -1) || (_iright > _bright)) _bright = _iright; if ((_bleft == -1) || (_ileft < _bleft)) _bleft = _ileft; if ((_btop == -1) || (_itop < _btop)) _btop = _itop; if ((_bbottom == -1) || (_ibottom > _bbottom)) _bbottom = _ibottom; } if ((_bleft != -1) && (_bright != -1)) _bwidth = _bright - _bleft; if ((_btop != -1) && (_bbottom != -1)) _bheight = _bbottom - _btop; }
resize Bounds for each primitive encountered. Only elements that are in the overall width and height of the Metafile are kept.
/** resize Bounds for each primitive encountered. Only elements that are in the overall * width and height of the Metafile are kept. */
private void resizeBounds(int x, int y) { if (_bleft == -1) _bleft = x; else if (x < _bleft) _bleft = x; if (_bright == -1) _bright = x; else if (x > _bright) _bright = x; if (_btop == -1) _btop = y; else if (y < _btop) _btop = y; if (_bbottom == -1) _bbottom = y; else if (y > _bbottom) _bbottom = y; }
resize Bounds for each image primitive encountered. Only elements that are in the overall width and height of the Metafile are kept.
/** resize Bounds for each image primitive encountered. Only elements that are in the overall * width and height of the Metafile are kept. */
private void resizeImageBounds(int x, int y) { if (_ileft == -1) _ileft = x; else if (x < _ileft) _ileft = x; if (_iright == -1) _iright = x; else if (x > _iright) _iright = x; if (_itop == -1) _itop = y; else if (y < _itop) _itop = y; if (_ibottom == -1) _ibottom = y; else if (y > _ibottom) _ibottom = y; }
get the Color corresponding with the Object (pen or brush object).
/** get the Color corresponding with the Object (pen or brush object). */
private Color getColorFromObject(int brushObject) { Color color = null; if ( brushObject >= 0 ) { GdiObject gdiObj = getObject( brushObject ); return (Color)gdiObj.obj; } else return null; }
Resize the bounds of the WMF image according with the bounds of the geometric Shape. There will be no resizing if one of the following properties is true :
  • the brush and the pen objects are < 0 (null objects)
  • the color of the geometric Shape is white, and no other Shapes has occured
/** Resize the bounds of the WMF image according with the bounds of the geometric * Shape. * There will be no resizing if one of the following properties is true : * <ul> * <li>the brush and the pen objects are < 0 (null objects)</li> * <li>the color of the geometric Shape is white, and no other Shapes has occured</li> * </ul> */
private void paint(int brushObject, int penObject, Shape shape) { if (( brushObject >= 0 ) || (penObject >= 0)) { Color col; if (brushObject >= 0) col = getColorFromObject(brushObject); else col = getColorFromObject(penObject); if (!(firstEffectivePaint && (col.equals(Color.white)))) { Rectangle rec = shape.getBounds(); resizeBounds((int)rec.getMinX(), (int)rec.getMinY()); resizeBounds((int)rec.getMaxX(), (int)rec.getMaxY()); firstEffectivePaint = false; } } }
Resize the bounds of the WMF image according with the bounds of the geometric Shape. There will be no resizing if one of the following properties is true :
  • the pen objects is < 0 (null object)
  • the color of the geometric Shape is white, and no other Shapes has occured
/** Resize the bounds of the WMF image according with the bounds of the geometric * Shape. * There will be no resizing if one of the following properties is true : * <ul> * <li>the pen objects is < 0 (null object)</li> * <li>the color of the geometric Shape is white, and no other Shapes has occured</li> * </ul> */
private void paintWithPen(int penObject, Shape shape) { if (penObject >= 0) { Color col = getColorFromObject(penObject); if (!(firstEffectivePaint && (col.equals(Color.white)))) { Rectangle rec = shape.getBounds(); resizeBounds((int)rec.getMinX(), (int)rec.getMinY()); resizeBounds((int)rec.getMaxX(), (int)rec.getMaxY()); firstEffectivePaint = false; } } }
get the vertical Alignment value for the text.
/** get the vertical Alignment value for the text. */
private float getVerticalAlignmentValue(TextLayout layout, int vertAlign) { if (vertAlign == WMFConstants.TA_BASELINE) return -layout.getAscent(); else if (vertAlign == WMFConstants.TA_TOP) return (layout.getAscent() + layout.getDescent()); else return 0; } }