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;
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;
/**
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;
*/
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
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
* @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() {
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()+
", 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;
public String getText() throws IOException {
return getText(UTF_16LE);
}
+
+ protected boolean isUnicode() {
+ return true;
+ }
}
/**
}
}
- 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
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;
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;
// 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
// 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());