package org.apache.poi.hemf.draw;
+import static org.apache.poi.hwmf.record.HwmfBrushStyle.BS_NULL;
+import static org.apache.poi.hwmf.record.HwmfBrushStyle.BS_SOLID;
+
+import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import org.apache.poi.hemf.record.emf.HemfBounded;
import org.apache.poi.hemf.record.emf.HemfRecord;
import org.apache.poi.hwmf.draw.HwmfGraphics;
+import org.apache.poi.hwmf.record.HwmfColorRef;
import org.apache.poi.hwmf.record.HwmfObjectTableEntry;
+import org.apache.poi.hwmf.record.HwmfPenStyle;
import org.apache.poi.util.Internal;
public class HemfGraphics extends HwmfGraphics {
+ private static final HwmfColorRef WHITE = new HwmfColorRef(Color.WHITE);
+ private static final HwmfColorRef LTGRAY = new HwmfColorRef(new Color(0x00C0C0C0));
+ private static final HwmfColorRef GRAY = new HwmfColorRef(new Color(0x00808080));
+ private static final HwmfColorRef DKGRAY = new HwmfColorRef(new Color(0x00404040));
+ private static final HwmfColorRef BLACK = new HwmfColorRef(Color.BLACK);
+
+
private final Deque<AffineTransform> transforms = new ArrayDeque<>();
public HemfGraphics(Graphics2D graphicsCtx, Rectangle2D bbox) {
@Override
public void saveProperties() {
- assert(prop != null);
- propStack.add(prop);
+ propStack.add(getProperties());
prop = new HemfDrawProperties((HemfDrawProperties)prop);
}
}
}
+ @Override
+ public void applyObjectTableEntry(int index) {
+ if ((index & 0x80000000) != 0) {
+ selectStockObject(index);
+ } else {
+ super.applyObjectTableEntry(index);
+ }
+ }
+
+ private void selectStockObject(int objectIndex) {
+ final HemfDrawProperties prop = getProperties();
+ switch (objectIndex) {
+ case 0x80000000:
+ // WHITE_BRUSH - A white, solid-color brush
+ // BrushStyle: BS_SOLID
+ // Color: 0x00FFFFFF
+ prop.setBrushColor(WHITE);
+ prop.setBrushStyle(BS_SOLID);
+ break;
+ case 0x80000001:
+ // LTGRAY_BRUSH - A light gray, solid-color brush
+ // BrushStyle: BS_SOLID
+ // Color: 0x00C0C0C0
+ prop.setBrushColor(LTGRAY);
+ prop.setBrushStyle(BS_SOLID);
+ break;
+ case 0x80000002:
+ // GRAY_BRUSH - A gray, solid-color brush
+ // BrushStyle: BS_SOLID
+ // Color: 0x00808080
+ prop.setBrushColor(GRAY);
+ prop.setBrushStyle(BS_SOLID);
+ break;
+ case 0x80000003:
+ // DKGRAY_BRUSH - A dark gray, solid color brush
+ // BrushStyle: BS_SOLID
+ // Color: 0x00404040
+ prop.setBrushColor(DKGRAY);
+ prop.setBrushStyle(BS_SOLID);
+ break;
+ case 0x80000004:
+ // BLACK_BRUSH - A black, solid color brush
+ // BrushStyle: BS_SOLID
+ // Color: 0x00000000
+ prop.setBrushColor(BLACK);
+ prop.setBrushStyle(BS_SOLID);
+ break;
+ case 0x80000005:
+ // NULL_BRUSH - A null brush
+ // BrushStyle: BS_NULL
+ prop.setBrushStyle(BS_NULL);
+ break;
+ case 0x80000006:
+ // WHITE_PEN - A white, solid-color pen
+ // PenStyle: PS_COSMETIC + PS_SOLID
+ // ColorRef: 0x00FFFFFF
+ prop.setPenStyle(HwmfPenStyle.valueOf(0));
+ prop.setPenWidth(1);
+ prop.setPenColor(WHITE);
+ break;
+ case 0x80000007:
+ // BLACK_PEN - A black, solid-color pen
+ // PenStyle: PS_COSMETIC + PS_SOLID
+ // ColorRef: 0x00000000
+ prop.setPenStyle(HwmfPenStyle.valueOf(0));
+ prop.setPenWidth(1);
+ prop.setPenColor(BLACK);
+ break;
+ case 0x80000008:
+ // NULL_PEN - A null pen
+ // PenStyle: PS_NULL
+ prop.setPenStyle(HwmfPenStyle.valueOf(HwmfPenStyle.HwmfLineDash.NULL.wmfFlag));
+ break;
+ case 0x8000000A:
+ // OEM_FIXED_FONT - A fixed-width, OEM character set
+ // Charset: OEM_CHARSET
+ // PitchAndFamily: FF_DONTCARE + FIXED_PITCH
+ break;
+ case 0x8000000B:
+ // ANSI_FIXED_FONT - A fixed-width font
+ // Charset: ANSI_CHARSET
+ // PitchAndFamily: FF_DONTCARE + FIXED_PITCH
+ break;
+ case 0x8000000C:
+ // ANSI_VAR_FONT - A variable-width font
+ // Charset: ANSI_CHARSET
+ // PitchAndFamily: FF_DONTCARE + VARIABLE_PITCH
+ break;
+ case 0x8000000D:
+ // SYSTEM_FONT - A font that is guaranteed to be available in the operating system
+ break;
+ case 0x8000000E:
+ // DEVICE_DEFAULT_FONT
+ // The default font that is provided by the graphics device driver for the current output device
+ break;
+ case 0x8000000F:
+ // DEFAULT_PALETTE
+ // The default palette that is defined for the current output device.
+ break;
+ case 0x80000010:
+ // SYSTEM_FIXED_FONT
+ // A fixed-width font that is guaranteed to be available in the operating system.
+ break;
+ case 0x80000011:
+ // DEFAULT_GUI_FONT
+ // The default font that is used for user interface objects such as menus and dialog boxes.
+ break;
+ case 0x80000012:
+ // DC_BRUSH
+ // The solid-color brush that is currently selected in the playback device context.
+ break;
+ case 0x80000013:
+ // DC_PEN
+ // The solid-color pen that is currently selected in the playback device context.
+ break;
+ }
+ }
+
+
/** saves the current affine transform on the stack */
private void saveTransform() {
*/
@Internal
public class HemfComment {
- private static final int MAX_RECORD_LENGTH = 1_000_000;
+ private static final int MAX_RECORD_LENGTH = 2_000_000;
public enum HemfCommentRecordType {
emfGeneric(-1, EmfCommentDataGeneric::new, false),
*/
public static class EmfSelectObject extends WmfSelectObject implements HemfRecord {
- private static final HwmfColorRef WHITE = new HwmfColorRef(Color.WHITE);
- private static final HwmfColorRef LTGRAY = new HwmfColorRef(new Color(0x00C0C0C0));
- private static final HwmfColorRef GRAY = new HwmfColorRef(new Color(0x00808080));
- private static final HwmfColorRef DKGRAY = new HwmfColorRef(new Color(0x00404040));
- private static final HwmfColorRef BLACK = new HwmfColorRef(Color.BLACK);
-
private static final String[] STOCK_IDS = {
"0x80000000 /* WHITE_BRUSH */",
"0x80000001 /* LTGRAY_BRUSH */",
return LittleEndianConsts.INT_SIZE;
}
- @Override
- public void draw(HemfGraphics ctx) {
- if ((objectIndex & 0x80000000) != 0) {
- selectStockObject(ctx);
- } else {
- super.draw(ctx);
- }
- }
-
- private void selectStockObject(HemfGraphics ctx) {
- final HemfDrawProperties prop = ctx.getProperties();
- switch (objectIndex) {
- case 0x80000000:
- // WHITE_BRUSH - A white, solid-color brush
- // BrushStyle: BS_SOLID
- // Color: 0x00FFFFFF
- prop.setBrushColor(WHITE);
- prop.setBrushStyle(BS_SOLID);
- break;
- case 0x80000001:
- // LTGRAY_BRUSH - A light gray, solid-color brush
- // BrushStyle: BS_SOLID
- // Color: 0x00C0C0C0
- prop.setBrushColor(LTGRAY);
- prop.setBrushStyle(BS_SOLID);
- break;
- case 0x80000002:
- // GRAY_BRUSH - A gray, solid-color brush
- // BrushStyle: BS_SOLID
- // Color: 0x00808080
- prop.setBrushColor(GRAY);
- prop.setBrushStyle(BS_SOLID);
- break;
- case 0x80000003:
- // DKGRAY_BRUSH - A dark gray, solid color brush
- // BrushStyle: BS_SOLID
- // Color: 0x00404040
- prop.setBrushColor(DKGRAY);
- prop.setBrushStyle(BS_SOLID);
- break;
- case 0x80000004:
- // BLACK_BRUSH - A black, solid color brush
- // BrushStyle: BS_SOLID
- // Color: 0x00000000
- prop.setBrushColor(BLACK);
- prop.setBrushStyle(BS_SOLID);
- break;
- case 0x80000005:
- // NULL_BRUSH - A null brush
- // BrushStyle: BS_NULL
- prop.setBrushStyle(BS_NULL);
- break;
- case 0x80000006:
- // WHITE_PEN - A white, solid-color pen
- // PenStyle: PS_COSMETIC + PS_SOLID
- // ColorRef: 0x00FFFFFF
- prop.setPenStyle(HwmfPenStyle.valueOf(0));
- prop.setPenWidth(1);
- prop.setPenColor(WHITE);
- break;
- case 0x80000007:
- // BLACK_PEN - A black, solid-color pen
- // PenStyle: PS_COSMETIC + PS_SOLID
- // ColorRef: 0x00000000
- prop.setPenStyle(HwmfPenStyle.valueOf(0));
- prop.setPenWidth(1);
- prop.setPenColor(BLACK);
- break;
- case 0x80000008:
- // NULL_PEN - A null pen
- // PenStyle: PS_NULL
- prop.setPenStyle(HwmfPenStyle.valueOf(HwmfPenStyle.HwmfLineDash.NULL.wmfFlag));
- break;
- case 0x8000000A:
- // OEM_FIXED_FONT - A fixed-width, OEM character set
- // Charset: OEM_CHARSET
- // PitchAndFamily: FF_DONTCARE + FIXED_PITCH
- break;
- case 0x8000000B:
- // ANSI_FIXED_FONT - A fixed-width font
- // Charset: ANSI_CHARSET
- // PitchAndFamily: FF_DONTCARE + FIXED_PITCH
- break;
- case 0x8000000C:
- // ANSI_VAR_FONT - A variable-width font
- // Charset: ANSI_CHARSET
- // PitchAndFamily: FF_DONTCARE + VARIABLE_PITCH
- break;
- case 0x8000000D:
- // SYSTEM_FONT - A font that is guaranteed to be available in the operating system
- break;
- case 0x8000000E:
- // DEVICE_DEFAULT_FONT
- // The default font that is provided by the graphics device driver for the current output device
- break;
- case 0x8000000F:
- // DEFAULT_PALETTE
- // The default palette that is defined for the current output device.
- break;
- case 0x80000010:
- // SYSTEM_FIXED_FONT
- // A fixed-width font that is guaranteed to be available in the operating system.
- break;
- case 0x80000011:
- // DEFAULT_GUI_FONT
- // The default font that is used for user interface objects such as menus and dialog boxes.
- break;
- case 0x80000012:
- // DC_BRUSH
- // The solid-color brush that is currently selected in the playback device context.
- break;
- case 0x80000013:
- // DC_PEN
- // The solid-color pen that is currently selected in the playback device context.
- break;
- }
- }
-
@Override
public String toString() {
return "{ index: "+
private static void polyTo(final HemfGraphics ctx, final Path2D poly) {
final PathIterator pi = poly.getPathIterator(null);
+ if (pi.isDone()) {
+ // ignore empty polys
+ return;
+ }
+
// ignore dummy start point (moveTo)
pi.next();
assert (!pi.isDone());
import static org.apache.poi.hemf.record.emf.HemfDraw.readPointL;
import static org.apache.poi.hemf.record.emf.HemfDraw.readRectL;
+import static org.apache.poi.hemf.record.emf.HemfRecordIterator.HEADER_SIZE;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
@Override
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
long size = readPointL(leis, start);
- size = colorRef.init(leis);
+ size += colorRef.init(leis);
// A 32-bit unsigned integer that specifies how to use the Color value to determine the area for
// the flood fill operation. The value MUST be in the FloodFill enumeration
mode = (int)leis.readUInt();
protected final Rectangle2D bounds = new Rectangle2D.Double();
/** An XForm object that specifies a world-space to page-space transform to apply to the source bitmap. */
- protected final byte[] xformSrc = new byte[24];
+ protected final AffineTransform xFormSrc = new AffineTransform();
/** A WMF ColorRef object that specifies the background color of the source bitmap. */
protected final HwmfColorRef bkColorSrc = new HwmfColorRef();
final Point2D srcPnt = new Point2D.Double();
size += readPointL(leis, srcPnt);
- leis.readFully(xformSrc);
- size += 24;
+ size += readXForm(leis, xFormSrc);
size += bkColorSrc.init(leis);
// A 32-bit unsigned integer that specifies the size, in bytes, of the source bitmap header.
final int cbBmiSrc = (int)leis.readUInt();
+ size += 3*LittleEndianConsts.INT_SIZE;
+ if (size <= recordSize) {
+ return size;
+ }
// A 32-bit unsigned integer that specifies the offset, in bytes, from the
// start of this record to the source bitmap bits in the BitmapBuffer field.
// A 32-bit unsigned integer that specifies the size, in bytes, of the source bitmap bits.
final int cbBitsSrc = (int)leis.readUInt();
- size += 5*LittleEndianConsts.INT_SIZE;
+ size += 2*LittleEndianConsts.INT_SIZE;
if (srcEqualsDstDimension()) {
srcBounds.setRect(srcPnt.getX(), srcPnt.getY(), dstBounds.getWidth(), dstBounds.getHeight());
protected boolean srcEqualsDstDimension() {
return false;
}
+
+ @Override
+ public String toString() {
+ return
+ "{ bounds: { x: "+bounds.getX()+", y: "+bounds.getY()+", w: "+bounds.getWidth()+", h: "+bounds.getHeight()+"}"+
+ ", xFormSrc: { scaleX: "+xFormSrc.getScaleX()+", shearX: "+xFormSrc.getShearX()+", transX: "+xFormSrc.getTranslateX()+", scaleY: "+xFormSrc.getScaleY()+", shearY: "+xFormSrc.getShearY()+", transY: "+xFormSrc.getTranslateY()+" }"+
+ ", bkColorSrc: "+bkColorSrc+
+ ", usageSrc: "+usageSrc+", "
+ + super.toString().substring(1);
+ }
}
/**
}
static long readBitmap(final LittleEndianInputStream leis, final HwmfBitmapDib bitmap,
- final int startIdx, final int offBmiSrc, final int cbBmiSrc, final int offBitsSrc, int cbBitsSrc)
+ final int startIdx, final int offBmi, final int cbBmi, final int offBits, int cbBits)
throws IOException {
- final int offCurr = leis.getReadIndex()-startIdx;
- final int undefinedSpace1 = offBmiSrc-offCurr;
- assert(undefinedSpace1 >= 0);
+ if (offBmi == 0) {
+ return 0;
+ }
+
+ final int offCurr = leis.getReadIndex()-(startIdx-HEADER_SIZE);
+ final int undefinedSpace1 = offBmi-offCurr;
+ if (undefinedSpace1 < 0) {
+ return 0;
+ }
- final int undefinedSpace2 = offBitsSrc-offCurr-cbBmiSrc-undefinedSpace1;
+ final int undefinedSpace2 = offBits-offCurr-cbBmi-undefinedSpace1;
assert(undefinedSpace2 >= 0);
leis.skipFully(undefinedSpace1);
- if (cbBmiSrc == 0 || cbBitsSrc == 0) {
+ if (cbBmi == 0 || cbBits == 0) {
return undefinedSpace1;
}
if (undefinedSpace2 == 0) {
leisDib = leis;
} else {
- final ByteArrayOutputStream bos = new ByteArrayOutputStream(cbBmiSrc+cbBitsSrc);
- final long cbBmiSrcAct = IOUtils.copy(leis, bos, cbBmiSrc);
- assert (cbBmiSrcAct == cbBmiSrc);
+ final ByteArrayOutputStream bos = new ByteArrayOutputStream(cbBmi+cbBits);
+ final long cbBmiSrcAct = IOUtils.copy(leis, bos, cbBmi);
+ assert (cbBmiSrcAct == cbBmi);
leis.skipFully(undefinedSpace2);
- final long cbBitsSrcAct = IOUtils.copy(leis, bos, cbBitsSrc);
- assert (cbBitsSrcAct == cbBitsSrc);
+ final long cbBitsSrcAct = IOUtils.copy(leis, bos, cbBits);
+ assert (cbBitsSrcAct == cbBits);
leisDib = new LittleEndianInputStream(new ByteArrayInputStream(bos.toByteArray()));
}
- final int dibSize = cbBmiSrc+cbBitsSrc;
+ final int dibSize = cbBmi+cbBits;
final int dibSizeAct = bitmap.init(leisDib, dibSize);
assert (dibSizeAct <= dibSize);
- return undefinedSpace1 + cbBmiSrc + undefinedSpace2 + cbBitsSrc;
+ return undefinedSpace1 + cbBmi + undefinedSpace2 + cbBits;
}
import org.apache.poi.hwmf.record.HwmfBitmapDib;
import org.apache.poi.hwmf.record.HwmfBrushStyle;
import org.apache.poi.hwmf.record.HwmfColorRef;
+import org.apache.poi.hwmf.record.HwmfFill;
import org.apache.poi.hwmf.record.HwmfHatchStyle;
import org.apache.poi.hwmf.record.HwmfMapMode;
import org.apache.poi.hwmf.record.HwmfMisc;
int size = 2 * LittleEndianConsts.INT_SIZE;
- if (offPalEntries > 0) {
- int undefinedSpace1 = (int) (offPalEntries - size - HEADER_SIZE);
+ if (nPalEntries > 0 && offPalEntries > 0) {
+ int undefinedSpace1 = (int) (offPalEntries - (size + HEADER_SIZE));
assert (undefinedSpace1 >= 0);
leis.skipFully(undefinedSpace1);
size += undefinedSpace1;
}
}
+ /**
+ * The EMR_CREATEDIBPATTERNBRUSHPT record defines a pattern brush for graphics operations.
+ * The pattern is specified by a DIB.
+ */
+ public static class EmfCreateDibPatternBrushPt extends HwmfMisc.WmfDibCreatePatternBrush implements HemfRecord {
+ protected int brushIdx;
+
+ @Override
+ public HemfRecordType getEmfRecordType() {
+ return HemfRecordType.createDibPatternBrushPt;
+ }
+
+ @Override
+ public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
+ final int startIdx = leis.getReadIndex();
+
+ style = HwmfBrushStyle.BS_DIBPATTERNPT;
+
+ // A 32-bit unsigned integer that specifies the index of the pattern brush
+ // object in the EMF Object Table
+ brushIdx = (int)leis.readUInt();
+
+ // A 32-bit unsigned integer that specifies how to interpret values in the color
+ // table in the DIB header. This value MUST be in the DIBColors enumeration
+ colorUsage = HwmfFill.ColorUsage.valueOf((int)leis.readUInt());
+
+ // A 32-bit unsigned integer that specifies the offset from the start of this
+ // record to the DIB header.
+ final int offBmi = leis.readInt();
+
+ // A 32-bit unsigned integer that specifies the size of the DIB header.
+ final int cbBmi = leis.readInt();
+
+ // A 32-bit unsigned integer that specifies the offset from the start of this record to the DIB bits.
+ final int offBits = leis.readInt();
+
+ // A 32-bit unsigned integer that specifies the size of the DIB bits.
+ final int cbBits = leis.readInt();
+
+ int size = 6*LittleEndianConsts.INT_SIZE;
+
+ patternDib = new HwmfBitmapDib();
+ size += readBitmap(leis, patternDib, startIdx, offBmi, cbBmi, offBits, cbBits);
+ return size;
+ }
+
+ @Override
+ public void draw(HemfGraphics ctx) {
+ ctx.addObjectTableEntry(this, brushIdx);
+ }
+
+ }
+
/**
* The EMR_DELETEOBJECT record deletes a graphics object, which is specified by its index
* in the EMF Object Table
return size + LittleEndianConsts.INT_SIZE;
}
+
+ @Override
+ public String toString() {
+ return
+ "{ xForm: { scaleX: "+xForm.getScaleX()+", shearX: "+xForm.getShearX()+", transX: "+xForm.getTranslateX()+", scaleY: "+xForm.getScaleY()+", shearY: "+xForm.getShearY()+", transY: "+xForm.getTranslateY()+" }"+
+ ", modifyWorldTransformMode: "+modifyWorldTransformMode+" }";
+ }
}
}
polyPolygon16(0x0000005B, HemfDraw.EmfPolyPolygon16::new),
polyDraw16(0x0000005C, HemfDraw.EmfPolyDraw16::new),
createmonobrush16(0x0000005D, UnimplementedHemfRecord::new),
- createdibpatternbrushpt(0x0000005E, UnimplementedHemfRecord::new),
+ createDibPatternBrushPt(0x0000005E, HemfMisc.EmfCreateDibPatternBrushPt::new),
extCreatePen(0x0000005F, HemfMisc.EmfExtCreatePen::new),
polytextouta(0x00000060, HemfText.PolyTextOutA::new),
polytextoutw(0x00000061, HemfText.PolyTextOutW::new),
int offDx = (int)leis.readUInt();
size += LittleEndianConsts.INT_SIZE;
- int undefinedSpace1 = (int)(offString - size - HEADER_SIZE);
+ int undefinedSpace1 = (int)(offString - (size + HEADER_SIZE));
assert (undefinedSpace1 >= 0);
leis.skipFully(undefinedSpace1);
size += undefinedSpace1;
dx.clear();
if (offDx > 0) {
- int undefinedSpace2 = (int) (offDx - size - HEADER_SIZE);
+ int undefinedSpace2 = (int) (offDx - (size + HEADER_SIZE));
assert (undefinedSpace2 >= 0);
leis.skipFully(undefinedSpace2);
size += undefinedSpace2;
return size;
}
- protected boolean isUnicode() {
- return false;
- }
-
/**
*
* To be implemented! We need to get the current character set
@Override
public String toString() {
- String text = "";
- try {
- text = getText(isUnicode() ? Charsets.UTF_16LE : LocaleUtil.CHARSET_1252);
- } catch (IOException ignored) {
- }
-
return
- "{ bounds: { x: "+bounds.getX()+
- ", y: "+bounds.getY()+
- ", w: "+bounds.getWidth()+
- ", h: "+bounds.getHeight()+
- "}, graphicsMode: '"+graphicsMode+"'"+
- ", scale: { w: "+scale.getWidth()+", h: "+scale.getHeight()+" }"+
- ", text: '"+text.replaceAll("\\p{Cntrl}",".")+"'"+
- "}";
+ "{ graphicsMode: '"+graphicsMode+"'"+
+ ", scale: { w: "+scale.getWidth()+", h: "+scale.getHeight()+" },"+
+ super.toString().substring(1);
}
}
ctx.scale(graphicsBounds.getWidth()/emfBounds.getWidth(), graphicsBounds.getHeight()/emfBounds.getHeight());
ctx.translate(-emfBounds.getCenterX(), -emfBounds.getCenterY());
-
-
+ int idx = 0;
HemfGraphics g = new HemfGraphics(ctx, emfBounds);
for (HemfRecord r : getRecords()) {
g.draw(r);
+ idx++;
}
} finally {
ctx.setTransform(at);
}
public void drawString(byte[] text, Rectangle2D bounds) {
- drawString(text, bounds, null);
- }
-
- public void drawString(byte[] text, Rectangle2D bounds, List<Integer> dx) {
- drawString(text, bounds, dx, false);
+ drawString(text, bounds, null, false);
}
public void drawString(byte[] text, Rectangle2D bounds, List<Integer> dx, boolean isUnicode) {
}
String textString = new String(text, charset).trim();
+ if (textString.isEmpty()) {
+ return;
+ }
AttributedString as = new AttributedString(textString);
- if (dx == null || dx.isEmpty()) {
- addAttributes(as, font);
- } else {
+ addAttributes(as, font);
+ if (dx != null && !dx.isEmpty()) {
//for multi-byte encodings (e.g. Shift_JIS), the byte length
//might not equal the string length().
//The x information is stored in dx[], an array parallel to the
//dxNormed[0] = 13 textString.get(0) = U+30D7
//dxNormed[1] = 14 textString.get(1) = U+30ED
- final List<Integer> dxNormed;
- if (textString.length() == text.length) {
- dxNormed = new ArrayList<>(dx);
- } else {
- dxNormed = new ArrayList<>(dx.size());
- int dxPosition = 0;
- int[] chars = {0};
- for (int offset = 0; offset < textString.length(); ) {
- dxNormed.add(dx.get(dxPosition));
- chars[0] = textString.codePointAt(offset);
- //now figure out how many bytes it takes to encode that
- //code point in the charset
- int byteLength = new String(chars, 0, chars.length).getBytes(charset).length;
- dxPosition += byteLength;
- offset += Character.charCount(chars[0]);
+ final int cps = textString.codePointCount(0, textString.length());
+ final int unicodeSteps = Math.max(dx.size()/cps, 1);
+ int dxPosition = 0;
+ int beginIndex = 0;
+ int[] chars = {0};
+ while (beginIndex < textString.length() && dxPosition < dx.size()) {
+ int endIndex = textString.offsetByCodePoints(beginIndex, 1);
+ if (beginIndex > 0) {
+ // Tracking works as a prefix/advance space on characters whereas
+ // dx[...] is the complete width of the current char
+ // therefore we need to add the additional/suffix width to the next char
+ as.addAttribute(TextAttribute.TRACKING, (float)((dx.get(dxPosition) - fontW) / fontH), beginIndex, endIndex);
}
- }
-
- int cps = textString.codePointCount(0, textString.length());
- for (int i = 0; i < Math.min(dxNormed.size(),cps-1); i++) {
- addAttributes(as, font);
- // Tracking works as a prefix/advance space on characters whereas
- // dx[...] is the complete width of the current char
- // therefore we need to add the additional/suffix width to the next char
- as.addAttribute(TextAttribute.TRACKING, (dxNormed.get(i) - fontW) / fontH, i + 1, i + 2);
+ dxPosition += (isUnicode) ? unicodeSteps : (endIndex-beginIndex);
+ beginIndex = endIndex;
}
}
graphicsCtx.fill(new Rectangle2D.Double(0, 0, bounds.getWidth(), bounds.getHeight()));
}
graphicsCtx.setColor(getProperties().getTextColor().getColor());
- graphicsCtx.drawString(as.getIterator(), 0, 0); // (float)bounds.getX(), (float)bounds.getY());
+ graphicsCtx.drawString(as.getIterator(), 0, 0);
} finally {
graphicsCtx.setTransform(at);
}
as.addAttribute(TextAttribute.FAMILY, fontInfo.getTypeface());
as.addAttribute(TextAttribute.SIZE, getFontHeight(font));
- as.addAttribute(TextAttribute.STRIKETHROUGH, font.isStrikeOut());
+ if (font.isStrikeOut()) {
+ as.addAttribute(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON);
+ }
if (font.isUnderline()) {
as.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
}
public void draw(HwmfGraphics ctx) {
}
+
+ @Override
+ public String toString() {
+ return
+ "{ rasterOperation: '"+rasterOperation+"'"+
+ ", srcBounds: { x: "+srcBounds.getX()+", y: "+srcBounds.getY()+", w: "+srcBounds.getWidth()+", h: "+srcBounds.getHeight()+" }"+
+ ", dstBounds: { x: "+dstBounds.getX()+", y: "+dstBounds.getY()+", w: "+dstBounds.getWidth()+", h: "+dstBounds.getHeight()+" }"+
+ "}";
+ }
}
/**
*/
public static class WmfDibCreatePatternBrush implements HwmfRecord, HwmfImageRecord, HwmfObjectTableEntry {
- private HwmfBrushStyle style;
+ protected HwmfBrushStyle style;
/**
* A 16-bit unsigned integer that defines whether the Colors field of a DIB
*
* If the Style field specified anything but BS_PATTERN, this field MUST be one of the ColorUsage values.
*/
- private ColorUsage colorUsage;
+ protected ColorUsage colorUsage;
- private HwmfBitmapDib patternDib;
+ protected HwmfBitmapDib patternDib;
private HwmfBitmap16 pattern16;
@Override
import java.util.ArrayList;
import java.util.List;
+import org.apache.commons.codec.Charsets;
import org.apache.poi.hwmf.draw.HwmfDrawProperties;
import org.apache.poi.hwmf.draw.HwmfGraphics;
import org.apache.poi.hwmf.record.HwmfMisc.WmfSetMapMode;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.LittleEndianConsts;
import org.apache.poi.util.LittleEndianInputStream;
+import org.apache.poi.util.LocaleUtil;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
import org.apache.poi.util.RecordFormatException;
public boolean isClipped() {
return ETO_CLIPPED.isSet(flag);
}
+
+ public boolean isYDisplaced() {
+ return ETO_PDY.isSet(flag);
+ }
}
/**
@Override
public void draw(HwmfGraphics ctx) {
Rectangle2D bounds = new Rectangle2D.Double(reference.getX(), reference.getY(), 0, 0);
- ctx.drawString(rawTextBytes, bounds, dx);
+ ctx.drawString(rawTextBytes, bounds, dx, false);
}
-
public String getText(Charset charset) throws IOException {
- StringBuilder sb = new StringBuilder();
- try (Reader r = new InputStreamReader(new ByteArrayInputStream(rawTextBytes), charset)) {
- for (int i = 0; i < stringLength; i++) {
- sb.appendCodePoint(readCodePoint(r));
- }
- }
- return sb.toString();
- }
-
- //TODO: move this to IOUtils?
- private int readCodePoint(Reader r) throws IOException {
- int c1 = r.read();
- if (c1 == -1) {
- throw new EOFException("Tried to read beyond byte array");
- }
- if (!Character.isHighSurrogate((char)c1)) {
- return c1;
- }
- int c2 = r.read();
- if (c2 == -1) {
- throw new EOFException("Tried to read beyond byte array");
- }
- if (!Character.isLowSurrogate((char)c2)) {
- throw new RecordFormatException("Expected low surrogate after high surrogate");
- }
- return Character.toCodePoint((char)c1, (char)c2);
+ return new String(rawTextBytes, charset);
}
public Point2D getReference() {
public Rectangle2D getBounds() {
return bounds;
}
+
+ protected boolean isUnicode() {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ String text = "";
+ try {
+ text = getText(isUnicode() ? Charsets.UTF_16LE : LocaleUtil.CHARSET_1252);
+ } catch (IOException ignored) {
+ }
+
+ return
+ "{ reference: { x: "+reference.getX()+", y: "+reference.getY()+" }"+
+ ", bounds: { x: "+bounds.getX()+", y: "+bounds.getY()+", w: "+bounds.getWidth()+", h: "+bounds.getHeight()+"}"+
+ ", text: '"+text.replaceAll("\\p{Cntrl}",".")+"'"+
+ "}";
+ }
}
public enum HwmfTextAlignment {
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
import javax.imageio.ImageIO;
@Test
@Ignore("Only for manual tests")
public void paint() throws IOException {
- File f = new File("picture_14.emf"); // sl_samples.getFile("wrench.emf");
- try (FileInputStream fis = new FileInputStream(f)) {
- HemfPicture emf = new HemfPicture(fis);
-
- Dimension2D dim = emf.getSize();
- int width = Units.pointsToPixel(dim.getWidth());
- // keep aspect ratio for height
- int height = Units.pointsToPixel(dim.getHeight());
- double max = Math.max(width, height);
- if (max > 1500) {
- width *= 1500 / max;
- height *= 1500 / max;
- }
+// File f = new File("picture_14.emf"); // sl_samples.getFile("wrench.emf");
+// try (FileInputStream fis = new FileInputStream(f)) {
+
+ try (ZipInputStream zis = new ZipInputStream(new FileInputStream("tmp/emf.zip"))) {
+ for (;;) {
+ ZipEntry ze = zis.getNextEntry();
+ if (ze == null) {
+ break;
+ }
+ final File pngName = new File("build/tmp",ze.getName().replaceFirst( ".*/","").replace(".emf", ".png"));
+ if (pngName.exists()) {
+ continue;
+ }
- BufferedImage bufImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
- Graphics2D g = bufImg.createGraphics();
- g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
- g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
- g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
- g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
-
- FileWriter fw = new FileWriter("record-list.txt");
- int i=0;
- for (HemfRecord r : emf.getRecords()) {
- if (r.getEmfRecordType() != HemfRecordType.comment) {
- fw.write(i + " " + r.getEmfRecordType() + " " + r.toString() + "\n");
+
+ // 263/263282_000.emf
+// if (!ze.getName().contains("298/298837_000.emf")) continue;
+ HemfPicture emf = new HemfPicture(zis);
+ System.out.println(ze.getName());
+
+ Dimension2D dim = emf.getSize();
+ int width = Units.pointsToPixel(dim.getWidth());
+ // keep aspect ratio for height
+ int height = Units.pointsToPixel(dim.getHeight());
+ double max = Math.max(width, height);
+ if (max > 1500) {
+ width *= 1500 / max;
+ height *= 1500 / max;
}
- i++;
- }
- fw.close();
- emf.draw(g, new Rectangle2D.Double(0,0,width,height));
+ BufferedImage bufImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+ Graphics2D g = bufImg.createGraphics();
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
+ g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
+ g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
+
+ FileWriter fw = new FileWriter("record-list.txt");
+ int i = 0;
+ for (HemfRecord r : emf.getRecords()) {
+ if (r.getEmfRecordType() != HemfRecordType.comment) {
+ fw.write(i + " " + r.getEmfRecordType() + " " + r.toString() + "\n");
+ }
+ i++;
+ }
+ fw.close();
+
+ emf.draw(g, new Rectangle2D.Double(0, 0, width, height));
- g.dispose();
+ g.dispose();
- ImageIO.write(bufImg, "PNG", new File("bla.png"));
+ ImageIO.write(bufImg, "PNG", pngName);
+
+// break;
+ }
}
}