Explorar el Código

#60656 - Support export file that contains emf and render it correctly

git-svn-id: https://svn.apache.org/repos/asf/poi/branches/hemf@1842056 13f79535-47bb-0310-9956-ffa450edef68
pull/131/head
Andreas Beeker hace 5 años
padre
commit
cadf567528

+ 1
- 1
src/scratchpad/src/org/apache/poi/hemf/draw/HemfGraphics.java Ver fichero

@@ -104,7 +104,7 @@ public class HemfGraphics extends HwmfGraphics {
prop.setLocation(path.getCurrentPoint());
if (!useBracket) {
// TODO: when to use draw vs. fill?
graphicsCtx.draw(path);
super.draw(path);
}

}

+ 1
- 1
src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java Ver fichero

@@ -217,7 +217,7 @@ public class HemfComment {

@Override
public String toString() {
return "\""+new String(privateData, LocaleUtil.CHARSET_1252)+"\"";
return "\""+new String(privateData, LocaleUtil.CHARSET_1252).replaceAll("\\p{Cntrl}", ".")+"\"";
}
}


+ 21
- 0
src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfMisc.java Ver fichero

@@ -272,6 +272,15 @@ public class HemfMisc {
ctx.addObjectTableEntry(this, brushIdx);
}


@Override
public String toString() {
return
"{ brushIndex: "+brushIdx+
", brushStyle: '"+brushStyle+"'"+
", colorRef: "+colorRef+
", brushHatch: '"+brushHatch+"' }";
}
}

/**
@@ -329,6 +338,11 @@ public class HemfMisc {
public void draw(HemfGraphics ctx) {
ctx.addObjectTableEntry(this, penIndex);
}

@Override
public String toString() {
return super.toString().replaceFirst("\\{", "{ penIndex: "+penIndex+", ");
}
}

public static class EmfExtCreatePen extends EmfCreatePen {
@@ -421,6 +435,13 @@ public class HemfMisc {

return size;
}

@Override
public String toString() {
// TODO: add style entries + bmp
return super.toString().replaceFirst("\\{",
"{ brushStyle: '"+brushStyle+"', hatchStyle: '"+hatchStyle+"', ");
}
}

/**

+ 85
- 100
src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfText.java Ver fichero

@@ -18,18 +18,19 @@
package org.apache.poi.hemf.record.emf;

import static java.nio.charset.StandardCharsets.UTF_16LE;
import static org.apache.poi.hemf.record.emf.HemfDraw.readRectL;
import static org.apache.poi.hemf.record.emf.HemfDraw.readDimensionFloat;
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.Dimension2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.codec.Charsets;
import org.apache.poi.hemf.draw.HemfGraphics;
import org.apache.poi.hwmf.draw.HwmfGraphics;
import org.apache.poi.hwmf.record.HwmfText;
import org.apache.poi.hwmf.record.HwmfText.WmfSetTextAlign;
import org.apache.poi.util.Dimension2DDouble;
@@ -37,6 +38,7 @@ import org.apache.poi.util.IOUtils;
import org.apache.poi.util.Internal;
import org.apache.poi.util.LittleEndianConsts;
import org.apache.poi.util.LittleEndianInputStream;
import org.apache.poi.util.LocaleUtil;
import org.apache.poi.util.RecordFormatException;

/**
@@ -53,9 +55,7 @@ public class HemfText {
GM_COMPATIBLE, GM_ADVANCED
}

public static class EmfExtTextOutA implements HemfRecord {

protected final Rectangle2D bounds = new Rectangle2D.Double();
public static class EmfExtTextOutA extends HwmfText.WmfExtTextOut implements HemfRecord {

protected EmfGraphicsMode graphicsMode;

@@ -65,14 +65,8 @@ public class HemfText {
*/
protected final Dimension2D scale = new Dimension2DDouble();

protected final EmrTextObject textObject;

public EmfExtTextOutA() {
this(false);
}

protected EmfExtTextOutA(boolean isUnicode) {
textObject = new EmrTextObject(isUnicode);
super(new EmfExtTextOutOptions());
}

@Override
@@ -95,12 +89,70 @@ public class HemfText {

size += readDimensionFloat(leis, scale);

// guarantee to read the rest of the EMRTextObjectRecord
size += textObject.init(leis, recordSize, (int)size);
// A WMF PointL object that specifies the coordinates of the reference point used to position the string.
// The reference point is defined by the last EMR_SETTEXTALIGN record.
// If no such record has been set, the default alignment is TA_LEFT,TA_TOP.
size += readPointL(leis, reference);
// A 32-bit unsigned integer that specifies the number of characters in the string.
stringLength = (int)leis.readUInt();
// A 32-bit unsigned integer that specifies the offset to the output string, in bytes,
// from the start of the record in which this object is contained.
// This value MUST be 8- or 16-bit aligned, according to the character format.
int offString = (int)leis.readUInt();
size += 2*LittleEndianConsts.INT_SIZE;

size += options.init(leis);
// An optional WMF RectL object that defines a clipping and/or opaquing rectangle in logical units.
// This rectangle is applied to the text output performed by the containing record.
if (options.isClipped() || options.isOpaque()) {
size += readRectL(leis, bounds);
}

// A 32-bit unsigned integer that specifies the offset to an intercharacter spacing array, in bytes,
// from the start of the record in which this object is contained. This value MUST be 32-bit aligned.
int offDx = (int)leis.readUInt();
size += LittleEndianConsts.INT_SIZE;

int undefinedSpace1 = (int)(offString - size - HEADER_SIZE);
assert (undefinedSpace1 >= 0);
leis.skipFully(undefinedSpace1);
size += undefinedSpace1;

rawTextBytes = IOUtils.safelyAllocate(stringLength*(isUnicode()?2:1), MAX_RECORD_LENGTH);
leis.readFully(rawTextBytes);
size += rawTextBytes.length;

dx.clear();
if (offDx > 0) {
int undefinedSpace2 = (int) (offDx - size - HEADER_SIZE);
assert (undefinedSpace2 >= 0);
leis.skipFully(undefinedSpace2);
size += undefinedSpace2;

// An array of 32-bit unsigned integers that specify the output spacing between the origins of adjacent
// character cells in logical units. The location of this field is specified by the value of offDx
// in bytes from the start of this record. If spacing is defined, this field contains the same number
// of values as characters in the output string.
//
// If the Options field of the EmrText object contains the ETO_PDY flag, then this buffer
// contains twice as many values as there are characters in the output string, one
// horizontal and one vertical offset for each, in that order.
//
// If ETO_RTLREADING is specified, characters are laid right to left instead of left to right.
// No other options affect the interpretation of this field.
while (size < recordSize) {
dx.add((int) leis.readUInt());
size += LittleEndianConsts.INT_SIZE;
}
}

return size;
}

protected boolean isUnicode() {
return false;
}

/**
*
* To be implemented! We need to get the current character set
@@ -114,15 +166,7 @@ public class HemfText {
* @throws IOException
*/
public String getText(Charset charset) throws IOException {
return textObject.getText(charset);
}

/**
*
* @return the x offset for the EmrTextObject
*/
public EmrTextObject getTextObject() {
return textObject;
return super.getText(charset);
}

public EmfGraphicsMode getGraphicsMode() {
@@ -133,8 +177,20 @@ public class HemfText {
return scale;
}

@Override
public void draw(HwmfGraphics ctx) {
Rectangle2D bounds = new Rectangle2D.Double(reference.getX(), reference.getY(), 0, 0);
ctx.drawString(rawTextBytes, bounds, dx, isUnicode());
}

@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()+
@@ -142,17 +198,13 @@ public class HemfText {
", h: "+bounds.getHeight()+
"}, graphicsMode: '"+graphicsMode+"'"+
", scale: { w: "+scale.getWidth()+", h: "+scale.getHeight()+" }"+
", textObject: "+textObject+
", text: '"+text.replaceAll("\\p{Cntrl}",".")+"'"+
"}";
}
}

public static class EmfExtTextOutW extends EmfExtTextOutA {

public EmfExtTextOutW() {
super(true);
}

@Override
public HemfRecordType getEmfRecordType() {
return HemfRecordType.exttextoutw;
@@ -161,6 +213,10 @@ public class HemfText {
public String getText() throws IOException {
return getText(UTF_16LE);
}

protected boolean isUnicode() {
return true;
}
}

/**
@@ -200,77 +256,6 @@ public class HemfText {
}
}

public static class EmrTextObject extends HwmfText.WmfExtTextOut {
protected final boolean isUnicode;
protected final List<Integer> outputDx = new ArrayList<>();

public EmrTextObject(boolean isUnicode) {
super(new EmfExtTextOutOptions());
this.isUnicode = isUnicode;
}

@Override
public int init(LittleEndianInputStream leis, final long recordSize, final int offset) throws IOException {
// A WMF PointL object that specifies the coordinates of the reference point used to position the string.
// The reference point is defined by the last EMR_SETTEXTALIGN record.
// If no such record has been set, the default alignment is TA_LEFT,TA_TOP.
long size = readPointL(leis, reference);
// A 32-bit unsigned integer that specifies the number of characters in the string.
stringLength = (int)leis.readUInt();
// A 32-bit unsigned integer that specifies the offset to the output string, in bytes,
// from the start of the record in which this object is contained.
// This value MUST be 8- or 16-bit aligned, according to the character format.
int offString = (int)leis.readUInt();
size += 2*LittleEndianConsts.INT_SIZE;

size += options.init(leis);
// An optional WMF RectL object that defines a clipping and/or opaquing rectangle in logical units.
// This rectangle is applied to the text output performed by the containing record.
if (options.isClipped() || options.isOpaque()) {
size += readRectL(leis, bounds);
}

// A 32-bit unsigned integer that specifies the offset to an intercharacter spacing array, in bytes,
// from the start of the record in which this object is contained. This value MUST be 32-bit aligned.
int offDx = (int)leis.readUInt();
size += LittleEndianConsts.INT_SIZE;

int undefinedSpace1 = (int)(offString-offset-size-2*LittleEndianConsts.INT_SIZE);
assert (undefinedSpace1 >= 0);
leis.skipFully(undefinedSpace1);
size += undefinedSpace1;

rawTextBytes = IOUtils.safelyAllocate(stringLength*(isUnicode?2:1), MAX_RECORD_LENGTH);
leis.readFully(rawTextBytes);
size += rawTextBytes.length;

outputDx.clear();
if (offDx > 0) {
int undefinedSpace2 = (int) (offDx - offset - size - 2 * LittleEndianConsts.INT_SIZE);
assert (undefinedSpace2 >= 0);
leis.skipFully(undefinedSpace2);
size += undefinedSpace2;

// An array of 32-bit unsigned integers that specify the output spacing between the origins of adjacent
// character cells in logical units. The location of this field is specified by the value of offDx
// in bytes from the start of this record. If spacing is defined, this field contains the same number
// of values as characters in the output string.
//
// If the Options field of the EmrText object contains the ETO_PDY flag, then this buffer
// contains twice as many values as there are characters in the output string, one
// horizontal and one vertical offset for each, in that order.
//
// If ETO_RTLREADING is specified, characters are laid right to left instead of left to right.
// No other options affect the interpretation of this field.
while (size < recordSize) {
outputDx.add((int) leis.readUInt());
size += LittleEndianConsts.INT_SIZE;
}
}

return (int)size;
}
}


public static class ExtCreateFontIndirectW extends HwmfText.WmfCreateFontIndirect

+ 33
- 20
src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java Ver fichero

@@ -37,6 +37,7 @@ import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;

import org.apache.commons.codec.Charsets;
import org.apache.poi.common.usermodel.fonts.FontInfo;
import org.apache.poi.hwmf.record.HwmfBrushStyle;
import org.apache.poi.hwmf.record.HwmfFont;
@@ -325,7 +326,12 @@ public class HwmfGraphics {
drawString(text, bounds, null);
}

public void drawString(byte[] text, Rectangle2D bounds, int dx[]) {
public void drawString(byte[] text, Rectangle2D bounds, List<Integer> dx) {
drawString(text, bounds, dx, false);
}

public void drawString(byte[] text, Rectangle2D bounds, List<Integer> dx, boolean isUnicode) {

HwmfFont font = getProperties().getFont();
if (font == null || text == null || text.length == 0) {
return;
@@ -335,14 +341,21 @@ public class HwmfGraphics {
// TODO: another approx. ...
double fontW = fontH/1.8;
Charset charset = (font.getCharset().getCharset() == null)?
DEFAULT_CHARSET : font.getCharset().getCharset();
Charset charset;
if (isUnicode) {
charset = Charsets.UTF_16LE;
} else {
charset = font.getCharset().getCharset();
if (charset == null) {
charset = DEFAULT_CHARSET;
}
}

String textString = new String(text, charset);
AttributedString as = new AttributedString(textString);
if (dx == null || dx.length == 0) {
if (dx == null || dx.isEmpty()) {
addAttributes(as, font);
} else {
int[] dxNormed = dx;
//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
@@ -357,41 +370,41 @@ public class HwmfGraphics {
// needs to be remapped as:
//dxNormed[0] = 13 textString.get(0) = U+30D7
//dxNormed[1] = 14 textString.get(1) = U+30ED
if (textString.length() != text.length) {
int codePoints = textString.codePointCount(0, textString.length());
dxNormed = new int[codePoints];

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[offset] = dx[dxPosition];
int[] chars = new int[1];
int cp = textString.codePointAt(offset);
chars[0] = cp;
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(cp);
offset += Character.charCount(chars[0]);
}
}
for (int i = 0; i < dxNormed.length; i++) {
for (int i = 0; i < dxNormed.size(); 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
if (i < dxNormed.length - 1) {
as.addAttribute(TextAttribute.TRACKING, (dxNormed[i] - fontW) / fontH, i + 1, i + 2);
}
as.addAttribute(TextAttribute.TRACKING, (dxNormed.get(i) - fontW) / fontH, i + 1, i + 2);
}
}
double angle = Math.toRadians(-font.getEscapement()/10.);

final AffineTransform at = graphicsCtx.getTransform();
try {
graphicsCtx.translate(bounds.getX(), bounds.getY()+fontH);
graphicsCtx.translate(bounds.getX(), bounds.getY());
graphicsCtx.rotate(angle);
graphicsCtx.translate(0, fontH);
if (getProperties().getBkMode() == HwmfBkMode.OPAQUE) {
// TODO: validate bounds
graphicsCtx.setBackground(getProperties().getBackgroundColor().getColor());

+ 2
- 1
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfColorRef.java Ver fichero

@@ -19,6 +19,7 @@ package org.apache.poi.hwmf.record;

import java.awt.Color;
import java.io.IOException;
import java.util.Locale;

import org.apache.poi.util.LittleEndianConsts;
import org.apache.poi.util.LittleEndianInputStream;
@@ -72,6 +73,6 @@ public class HwmfColorRef implements Cloneable {

@Override
public String toString() {
return String.format("%#8X", colorRef.getRGB());
return String.format(Locale.ROOT, "%#08X", colorRef.getRGB()&0xFFFFFF);
}
}

+ 16
- 0
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMisc.java Ver fichero

@@ -554,6 +554,14 @@ public class HwmfMisc {
p.setPenColor(colorRef);
p.setPenWidth(dimension.getWidth());
}

@Override
public String toString() {
return
"{ penStyle: "+penStyle+
", dimension: { width: "+dimension.getWidth()+", height: "+dimension.getHeight()+" }"+
", colorRef: "+colorRef+"}";
}
}

/**
@@ -634,5 +642,13 @@ public class HwmfMisc {
p.setBrushColor(colorRef);
p.setBrushHatch(brushHatch);
}

@Override
public String toString() {
return
"{ brushStyle: '"+brushStyle+"'"+
", colorRef: "+colorRef+
", brushHatch: '"+brushHatch+"' }";
}
}
}

+ 11
- 0
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPenStyle.java Ver fichero

@@ -195,4 +195,15 @@ public class HwmfPenStyle implements Cloneable {
throw new InternalError();
}
}

@Override
public String toString() {
return
"{ lineCap: '"+getLineCap()+"'"+
", lineDash: '"+getLineDash()+"'"+
", lineJoin: '"+getLineJoin()+"'"+
(isAlternateDash()?", alternateDash: true ":"")+
(isGeometric()?", geometric: true ":"")+
"}";
}
}

+ 5
- 16
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfText.java Ver fichero

@@ -28,6 +28,8 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

import org.apache.poi.hwmf.draw.HwmfDrawProperties;
import org.apache.poi.hwmf.draw.HwmfGraphics;
@@ -334,7 +336,7 @@ public class HwmfText {
* character cell i and character cell i + 1. If this field is present, there MUST be the same
* number of values as there are characters in the string.
*/
private int dx[];
protected final List<Integer> dx = new ArrayList<>();

public WmfExtTextOut() {
this(new WmfExtTextOutOptions());
@@ -380,9 +382,8 @@ public class HwmfText {
logger.log(POILogger.WARN, "META_EXTTEXTOUT tracking info doesn't cover all characters");
}

dx = new int[stringLength];
for (int i=0; i<dxLen; i++) {
dx[i] = leis.readShort();
dx.add((int)leis.readShort());
size += LittleEndianConsts.SHORT_SIZE;
}
@@ -392,7 +393,7 @@ public class HwmfText {
@Override
public void draw(HwmfGraphics ctx) {
Rectangle2D bounds = new Rectangle2D.Double(reference.getX(), reference.getY(), 0, 0);
ctx.drawString(getTextBytes(), bounds, dx);
ctx.drawString(rawTextBytes, bounds, dx);
}


@@ -432,18 +433,6 @@ public class HwmfText {
public Rectangle2D getBounds() {
return bounds;
}

/**
*
* @return a copy of a trimmed byte array of rawTextBytes bytes.
* This includes only the bytes from 0..stringLength.
* This does not include the extra optional padding on the byte array.
*/
private byte[] getTextBytes() {
byte[] ret = IOUtils.safelyAllocate(stringLength, MAX_RECORD_LENGTH);
System.arraycopy(rawTextBytes, 0, ret, 0, stringLength);
return ret;
}
}
public enum HwmfTextAlignment {

+ 5
- 3
src/scratchpad/testcases/org/apache/poi/hemf/usermodel/HemfPictureTest.java Ver fichero

@@ -88,7 +88,9 @@ public class HemfPictureTest {
FileWriter fw = new FileWriter("record-list.txt");
int i=0;
for (HemfRecord r : emf.getRecords()) {
fw.write(i + " "+r.getEmfRecordType()+" "+r.toString()+"\n");
if (r.getEmfRecordType() != HemfRecordType.comment) {
fw.write(i + " " + r.getEmfRecordType() + " " + r.toString() + "\n");
}
i++;
}
fw.close();
@@ -170,7 +172,7 @@ public class HemfPictureTest {
for (HemfRecord record : pic) {
if (record.getEmfRecordType().equals(HemfRecordType.exttextoutw)) {
HemfText.EmfExtTextOutW extTextOutW = (HemfText.EmfExtTextOutW) record;
Point2D reference = extTextOutW.getTextObject().getReference();
Point2D reference = extTextOutW.getReference();
if (lastY > -1 && lastY != reference.getY()) {
sb.append("\n");
lastX = -1;
@@ -204,7 +206,7 @@ public class HemfPictureTest {
for (HemfRecord record : pic) {
if (record.getEmfRecordType().equals(HemfRecordType.exttextoutw)) {
HemfText.EmfExtTextOutW extTextOutW = (HemfText.EmfExtTextOutW) record;
Point2D reference = extTextOutW.getTextObject().getReference();
Point2D reference = extTextOutW.getReference();
if (lastY > -1 && lastY != reference.getY()) {
sb.append("\n");
lastX = -1;

Cargando…
Cancelar
Guardar