mirror of https://github.com/apache/poi.git
Bug 60656 - Emf image support in slideshows
- fixed WmfExtTextOut dx handling for variable text spacing - fixed WmfExtTextOut text position for (0,0) references based on the current/last path location - fixed WmfExtTextOut handling of symbol/wingdings charset (move ascii to unicode private area, because Java font loader maps the glyphs there) - and use existing workaround if the fonts aren't installed, i.e. use corresponding unicode characters of the logcial font then - provide option in PPTX2PNG to use given file input type, if the file magic is unknown - provide option in PPTX2PNG to render text as shapes in SVG, as dx handling (above) implemented via TextAttribute.TRACKING is not supported by batik source of the sample.wmf, which I've used: https://stackoverflow.com/questions/58726194/svg-rendering-with-batik-produce-broken-image git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1876136 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
1dc771394b
commit
81400a3ee2
|
@ -54,9 +54,14 @@ public interface DrawFontManager {
|
||||||
FontInfo getFallbackFont(Graphics2D graphics, FontInfo fontInfo);
|
FontInfo getFallbackFont(Graphics2D graphics, FontInfo fontInfo);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map text charset depending on font family.<p>
|
* Map text charset depending on font family.
|
||||||
*
|
* <p>
|
||||||
* Currently this only maps for wingdings font (into unicode private use area)
|
* Currently this only maps for wingdings and symbol font (into unicode private use area)
|
||||||
|
* <p>
|
||||||
|
* Depending if the requested font is installed in the system, tbe mapped string varies:<br>
|
||||||
|
* If the font is registered into the graphics environment the characters are mapped to the
|
||||||
|
* private use area. If the font is missing (and hence a AWT logical font is used), the
|
||||||
|
* characters are mapped to the corresponding unicode characters
|
||||||
*
|
*
|
||||||
* @param graphics the graphics context to request additional rendering hints
|
* @param graphics the graphics context to request additional rendering hints
|
||||||
* @param fontInfo the font info object corresponding to the text run font
|
* @param fontInfo the font info object corresponding to the text run font
|
||||||
|
|
|
@ -21,12 +21,16 @@ package org.apache.poi.sl.draw;
|
||||||
|
|
||||||
import java.awt.Font;
|
import java.awt.Font;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.GraphicsEnvironment;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
import org.apache.poi.common.usermodel.fonts.FontCharset;
|
||||||
import org.apache.poi.common.usermodel.fonts.FontInfo;
|
import org.apache.poi.common.usermodel.fonts.FontInfo;
|
||||||
import org.apache.poi.sl.draw.Drawable.DrawableHint;
|
import org.apache.poi.sl.draw.Drawable.DrawableHint;
|
||||||
|
import org.apache.poi.util.StringUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages fonts when rendering slides.
|
* Manages fonts when rendering slides.
|
||||||
|
@ -56,37 +60,36 @@ public class DrawFontManagerDefault implements DrawFontManager {
|
||||||
return fi;
|
return fi;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String mapFontCharset(Graphics2D graphics, FontInfo fontInfo, String text) {
|
|
||||||
// TODO: find a real charset mapping solution instead of hard coding for Wingdings
|
|
||||||
return (fontInfo != null && knownSymbolFonts.contains(fontInfo.getTypeface()))
|
|
||||||
? mapSymbolChars(text)
|
|
||||||
: text;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Symbol fonts like "Wingdings" or "Symbol" have glyphs mapped to a Unicode private use range via the Java font loader,
|
* Symbol fonts like "Wingdings" or "Symbol" have glyphs mapped to a Unicode private use range via the Java font loader,
|
||||||
* although a system font viewer might show you the glyphs in the ASCII range.
|
* although a system font viewer might show you the glyphs in the ASCII range.
|
||||||
* This helper function maps the chars of the text string to the corresponding private use range chars.
|
* This maps the chars of the text string to the corresponding private use range chars.
|
||||||
|
*
|
||||||
|
* @param graphics the used graphics context
|
||||||
|
* @param fontInfo the font info
|
||||||
|
* @param text the input string
|
||||||
*
|
*
|
||||||
* @param text the input string, typically consists of ASCII chars
|
|
||||||
* @return the mapped string, typically consists of chars in the range of 0xf000 to 0xf0ff
|
* @return the mapped string, typically consists of chars in the range of 0xf000 to 0xf0ff
|
||||||
*
|
*
|
||||||
* @since POI 4.0.0
|
* @since POI 4.0.0
|
||||||
*/
|
*/
|
||||||
public static String mapSymbolChars(String text) {
|
@Override
|
||||||
// wingdings doesn't contain high-surrogates, so chars are ok
|
public String mapFontCharset(Graphics2D graphics, FontInfo fontInfo, String text) {
|
||||||
boolean changed = false;
|
if (fontInfo == null || text == null || text.isEmpty()) {
|
||||||
char[] chrs = text.toCharArray();
|
return text;
|
||||||
for (int i=0; i<chrs.length; i++) {
|
|
||||||
// only change valid chars
|
|
||||||
if ((0x20 <= chrs[i] && chrs[i] <= 0x7f) ||
|
|
||||||
(0xa0 <= chrs[i] && chrs[i] <= 0xff)) {
|
|
||||||
chrs[i] |= 0xf000;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return changed ? new String(chrs) : text;
|
String typeface = fontInfo.getTypeface();
|
||||||
|
if (fontInfo.getCharset() == FontCharset.SYMBOL || knownSymbolFonts.contains(typeface)) {
|
||||||
|
int[] cps = text.codePoints().map(DrawFontManagerDefault::mapSymbolChar).toArray();
|
||||||
|
String ret = new String(cps, 0, cps.length);
|
||||||
|
|
||||||
|
String[] allFonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
|
||||||
|
boolean hasFont = Arrays.asList(allFonts).contains(typeface);
|
||||||
|
return hasFont ? ret : StringUtil.mapMsCodepointString(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -117,4 +120,9 @@ public class DrawFontManagerDefault implements DrawFontManager {
|
||||||
|
|
||||||
return (mappedTypeface != null) ? new DrawFontInfo(mappedTypeface) : fontInfo;
|
return (mappedTypeface != null) ? new DrawFontInfo(mappedTypeface) : fontInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static int mapSymbolChar(int cp) {
|
||||||
|
return ((0x20 <= cp && cp <= 0x7f) || (0xa0 <= cp && cp <= 0xff)) ? cp | 0xf000 : cp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,17 +19,15 @@ package org.apache.poi.util;
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collection of string handling utilities
|
* Collection of string handling utilities
|
||||||
*/
|
*/
|
||||||
@Internal
|
@Internal
|
||||||
public class StringUtil {
|
public final class StringUtil {
|
||||||
protected static final Charset ISO_8859_1 = StandardCharsets.ISO_8859_1;
|
private static final Charset ISO_8859_1 = StandardCharsets.ISO_8859_1;
|
||||||
//arbitrarily selected; may need to increase
|
//arbitrarily selected; may need to increase
|
||||||
private static final int MAX_RECORD_LENGTH = 10000000;
|
private static final int MAX_RECORD_LENGTH = 10000000;
|
||||||
|
|
||||||
|
@ -38,8 +36,6 @@ public class StringUtil {
|
||||||
public static final Charset WIN_1252 = Charset.forName("cp1252");
|
public static final Charset WIN_1252 = Charset.forName("cp1252");
|
||||||
public static final Charset BIG5 = Charset.forName("Big5");
|
public static final Charset BIG5 = Charset.forName("Big5");
|
||||||
|
|
||||||
private static Map<Integer, Integer> msCodepointToUnicode;
|
|
||||||
|
|
||||||
private StringUtil() {
|
private StringUtil() {
|
||||||
// no instances of this class
|
// no instances of this class
|
||||||
}
|
}
|
||||||
|
@ -293,16 +289,6 @@ public class StringUtil {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks to see if a given String needs to be represented as Unicode
|
|
||||||
*
|
|
||||||
* @param value The string to look at.
|
|
||||||
* @return true if string needs Unicode to be represented.
|
|
||||||
*/
|
|
||||||
public static boolean isUnicodeString(final String value) {
|
|
||||||
return !value.equals(new String(value.getBytes(ISO_8859_1), ISO_8859_1));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests if the string starts with the specified prefix, ignoring case consideration.
|
* Tests if the string starts with the specified prefix, ignoring case consideration.
|
||||||
*/
|
*/
|
||||||
|
@ -381,38 +367,18 @@ public class StringUtil {
|
||||||
if (string == null || string.isEmpty()) {
|
if (string == null || string.isEmpty()) {
|
||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
initMsCodepointMap();
|
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
int[] cps = string.codePoints().map(StringUtil::mapMsCodepoint).toArray();
|
||||||
final int length = string.length();
|
return new String(cps, 0, cps.length);
|
||||||
for (int offset = 0; offset < length; ) {
|
|
||||||
int msCodepoint = string.codePointAt(offset);
|
|
||||||
Integer uniCodepoint = msCodepointToUnicode.get(msCodepoint);
|
|
||||||
sb.appendCodePoint(uniCodepoint == null ? msCodepoint : uniCodepoint);
|
|
||||||
offset += Character.charCount(msCodepoint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return sb.toString();
|
private static int mapMsCodepoint(int cp) {
|
||||||
}
|
if (0xf020 <= cp && cp <= 0xf07f) {
|
||||||
|
return symbolMap_f020[cp - 0xf020];
|
||||||
public static synchronized void mapMsCodepoint(int msCodepoint, int unicodeCodepoint) {
|
} else if (0xf0a0 <= cp && cp <= 0xf0ff) {
|
||||||
initMsCodepointMap();
|
return symbolMap_f0a0[cp - 0xf0a0];
|
||||||
msCodepointToUnicode.put(msCodepoint, unicodeCodepoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static synchronized void initMsCodepointMap() {
|
|
||||||
if (msCodepointToUnicode != null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
msCodepointToUnicode = new HashMap<>();
|
|
||||||
int i = 0xF020;
|
|
||||||
for (int ch : symbolMap_f020) {
|
|
||||||
msCodepointToUnicode.put(i++, ch);
|
|
||||||
}
|
|
||||||
i = 0xf0a0;
|
|
||||||
for (int ch : symbolMap_f0a0) {
|
|
||||||
msCodepointToUnicode.put(i++, ch);
|
|
||||||
}
|
}
|
||||||
|
return cp;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final int[] symbolMap_f020 = {
|
private static final int[] symbolMap_f020 = {
|
||||||
|
|
|
@ -37,8 +37,8 @@ public class SVGPOIGraphics2D extends SVGGraphics2D {
|
||||||
|
|
||||||
private final RenderingHints hints;
|
private final RenderingHints hints;
|
||||||
|
|
||||||
public SVGPOIGraphics2D(Document document) {
|
public SVGPOIGraphics2D(Document document, boolean textAsShapes) {
|
||||||
super(getCtx(document), false);
|
super(getCtx(document), textAsShapes);
|
||||||
hints = getGeneratorContext().getGraphicContextDefaults().getRenderingHints();
|
hints = getGeneratorContext().getGraphicContextDefaults().getRenderingHints();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,11 @@ interface OutputFormat extends Closeable {
|
||||||
class SVGFormat implements OutputFormat {
|
class SVGFormat implements OutputFormat {
|
||||||
static final String svgNS = "http://www.w3.org/2000/svg";
|
static final String svgNS = "http://www.w3.org/2000/svg";
|
||||||
private SVGGraphics2D svgGenerator;
|
private SVGGraphics2D svgGenerator;
|
||||||
|
private final boolean textAsShapes;
|
||||||
|
|
||||||
|
SVGFormat(boolean textAsShapes) {
|
||||||
|
this.textAsShapes = textAsShapes;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Graphics2D getGraphics2D(double width, double height) {
|
public Graphics2D getGraphics2D(double width, double height) {
|
||||||
|
@ -58,7 +63,7 @@ interface OutputFormat extends Closeable {
|
||||||
|
|
||||||
// Create an instance of org.w3c.dom.Document.
|
// Create an instance of org.w3c.dom.Document.
|
||||||
Document document = domImpl.createDocument(svgNS, "svg", null);
|
Document document = domImpl.createDocument(svgNS, "svg", null);
|
||||||
svgGenerator = new SVGPOIGraphics2D(document);
|
svgGenerator = new SVGPOIGraphics2D(document, textAsShapes);
|
||||||
svgGenerator.setSVGCanvasSize(new Dimension((int)width, (int)height));
|
svgGenerator.setSVGCanvasSize(new Dimension((int)width, (int)height));
|
||||||
return svgGenerator;
|
return svgGenerator;
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,7 +67,11 @@ public final class PPTX2PNG {
|
||||||
" -dump <file> dump the annotated records to a file\n" +
|
" -dump <file> dump the annotated records to a file\n" +
|
||||||
" -quiet do not write to console (for normal processing)\n" +
|
" -quiet do not write to console (for normal processing)\n" +
|
||||||
" -ignoreParse ignore parsing error and continue with the records read until the error\n" +
|
" -ignoreParse ignore parsing error and continue with the records read until the error\n" +
|
||||||
" -extractEmbedded extract embedded parts";
|
" -extractEmbedded extract embedded parts\n" +
|
||||||
|
" -inputType <type> default input file type (OLE2,WMF,EMF), default is OLE2 = Powerpoint\n" +
|
||||||
|
" some files (usually wmf) don't have a header, i.e. an identifiable file magic\n" +
|
||||||
|
" -textAsShapes text elements are saved as shapes in SVG, necessary for variable spacing\n" +
|
||||||
|
" often found in math formulas";
|
||||||
|
|
||||||
System.out.println(msg);
|
System.out.println(msg);
|
||||||
// no System.exit here, as we also run in junit tests!
|
// no System.exit here, as we also run in junit tests!
|
||||||
|
@ -93,6 +97,8 @@ public final class PPTX2PNG {
|
||||||
private String fixSide = "scale";
|
private String fixSide = "scale";
|
||||||
private boolean ignoreParse = false;
|
private boolean ignoreParse = false;
|
||||||
private boolean extractEmbedded = false;
|
private boolean extractEmbedded = false;
|
||||||
|
private FileMagic defaultFileType = FileMagic.OLE2;
|
||||||
|
private boolean textAsShapes = false;
|
||||||
|
|
||||||
private PPTX2PNG() {
|
private PPTX2PNG() {
|
||||||
}
|
}
|
||||||
|
@ -153,6 +159,17 @@ public final class PPTX2PNG {
|
||||||
fixSide = "long";
|
fixSide = "long";
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "-inputType":
|
||||||
|
if (opt != null) {
|
||||||
|
defaultFileType = FileMagic.valueOf(opt);
|
||||||
|
i++;
|
||||||
|
} else {
|
||||||
|
defaultFileType = FileMagic.OLE2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "-textAsShapes":
|
||||||
|
textAsShapes = true;
|
||||||
|
break;
|
||||||
case "-ignoreParse":
|
case "-ignoreParse":
|
||||||
ignoreParse = true;
|
ignoreParse = true;
|
||||||
break;
|
break;
|
||||||
|
@ -238,7 +255,7 @@ public final class PPTX2PNG {
|
||||||
|
|
||||||
extractEmbedded(proxy, slideNo);
|
extractEmbedded(proxy, slideNo);
|
||||||
|
|
||||||
try (OutputFormat outputFormat = ("svg".equals(format)) ? new SVGFormat() : new BitmapFormat(format)) {
|
try (OutputFormat outputFormat = ("svg".equals(format)) ? new SVGFormat(textAsShapes) : new BitmapFormat(format)) {
|
||||||
Graphics2D graphics = outputFormat.getGraphics2D(width, height);
|
Graphics2D graphics = outputFormat.getGraphics2D(width, height);
|
||||||
|
|
||||||
// default rendering options
|
// default rendering options
|
||||||
|
@ -337,6 +354,9 @@ public final class PPTX2PNG {
|
||||||
if ("stdin".equals(fileName)) {
|
if ("stdin".equals(fileName)) {
|
||||||
InputStream bis = FileMagic.prepareToCheckMagic(System.in);
|
InputStream bis = FileMagic.prepareToCheckMagic(System.in);
|
||||||
FileMagic fm = FileMagic.valueOf(bis);
|
FileMagic fm = FileMagic.valueOf(bis);
|
||||||
|
if (fm == FileMagic.UNKNOWN) {
|
||||||
|
fm = defaultFileType;
|
||||||
|
}
|
||||||
switch (fm) {
|
switch (fm) {
|
||||||
case EMF:
|
case EMF:
|
||||||
proxy = new EMFHandler();
|
proxy = new EMFHandler();
|
||||||
|
|
|
@ -21,6 +21,7 @@ import java.awt.AlphaComposite;
|
||||||
import java.awt.BasicStroke;
|
import java.awt.BasicStroke;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Composite;
|
import java.awt.Composite;
|
||||||
|
import java.awt.Font;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.GraphicsConfiguration;
|
import java.awt.GraphicsConfiguration;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
|
@ -29,6 +30,7 @@ import java.awt.Rectangle;
|
||||||
import java.awt.Shape;
|
import java.awt.Shape;
|
||||||
import java.awt.TexturePaint;
|
import java.awt.TexturePaint;
|
||||||
import java.awt.font.FontRenderContext;
|
import java.awt.font.FontRenderContext;
|
||||||
|
import java.awt.font.GlyphVector;
|
||||||
import java.awt.font.TextAttribute;
|
import java.awt.font.TextAttribute;
|
||||||
import java.awt.font.TextLayout;
|
import java.awt.font.TextLayout;
|
||||||
import java.awt.geom.AffineTransform;
|
import java.awt.geom.AffineTransform;
|
||||||
|
@ -39,16 +41,18 @@ import java.awt.geom.Rectangle2D;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.text.AttributedString;
|
import java.text.AttributedString;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.BitSet;
|
import java.util.BitSet;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
import org.apache.commons.codec.Charsets;
|
import org.apache.commons.codec.Charsets;
|
||||||
import org.apache.poi.common.usermodel.fonts.FontCharset;
|
|
||||||
import org.apache.poi.common.usermodel.fonts.FontInfo;
|
import org.apache.poi.common.usermodel.fonts.FontInfo;
|
||||||
import org.apache.poi.hwmf.record.HwmfBrushStyle;
|
import org.apache.poi.hwmf.record.HwmfBrushStyle;
|
||||||
import org.apache.poi.hwmf.record.HwmfFont;
|
import org.apache.poi.hwmf.record.HwmfFont;
|
||||||
|
@ -59,12 +63,10 @@ import org.apache.poi.hwmf.record.HwmfObjectTableEntry;
|
||||||
import org.apache.poi.hwmf.record.HwmfPenStyle;
|
import org.apache.poi.hwmf.record.HwmfPenStyle;
|
||||||
import org.apache.poi.hwmf.record.HwmfPenStyle.HwmfLineDash;
|
import org.apache.poi.hwmf.record.HwmfPenStyle.HwmfLineDash;
|
||||||
import org.apache.poi.hwmf.record.HwmfRegionMode;
|
import org.apache.poi.hwmf.record.HwmfRegionMode;
|
||||||
import org.apache.poi.hwmf.record.HwmfText;
|
|
||||||
import org.apache.poi.hwmf.record.HwmfText.WmfExtTextOutOptions;
|
import org.apache.poi.hwmf.record.HwmfText.WmfExtTextOutOptions;
|
||||||
import org.apache.poi.sl.draw.BitmapImageRenderer;
|
import org.apache.poi.sl.draw.BitmapImageRenderer;
|
||||||
import org.apache.poi.sl.draw.DrawFactory;
|
import org.apache.poi.sl.draw.DrawFactory;
|
||||||
import org.apache.poi.sl.draw.DrawFontManager;
|
import org.apache.poi.sl.draw.DrawFontManager;
|
||||||
import org.apache.poi.sl.draw.DrawFontManagerDefault;
|
|
||||||
import org.apache.poi.sl.draw.DrawPictureShape;
|
import org.apache.poi.sl.draw.DrawPictureShape;
|
||||||
import org.apache.poi.sl.draw.ImageRenderer;
|
import org.apache.poi.sl.draw.ImageRenderer;
|
||||||
import org.apache.poi.util.Internal;
|
import org.apache.poi.util.Internal;
|
||||||
|
@ -107,6 +109,16 @@ public class HwmfGraphics {
|
||||||
1f, TextAttribute.WEIGHT_EXTRA_LIGHT
|
1f, TextAttribute.WEIGHT_EXTRA_LIGHT
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static class DxLayout {
|
||||||
|
double dx;
|
||||||
|
// Spacing at default tracking value of 0
|
||||||
|
double pos0;
|
||||||
|
// Spacing at second tracking value
|
||||||
|
double pos1;
|
||||||
|
int beginIndex;
|
||||||
|
int endIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private final List<HwmfDrawProperties> propStack = new LinkedList<>();
|
private final List<HwmfDrawProperties> propStack = new LinkedList<>();
|
||||||
protected HwmfDrawProperties prop;
|
protected HwmfDrawProperties prop;
|
||||||
|
@ -189,6 +201,7 @@ public class HwmfGraphics {
|
||||||
draw(shape);
|
draw(shape);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("MagicConstant")
|
||||||
protected BasicStroke getStroke() {
|
protected BasicStroke getStroke() {
|
||||||
HwmfDrawProperties prop = getProperties();
|
HwmfDrawProperties prop = getProperties();
|
||||||
HwmfPenStyle ps = prop.getPenStyle();
|
HwmfPenStyle ps = prop.getPenStyle();
|
||||||
|
@ -285,7 +298,7 @@ public class HwmfGraphics {
|
||||||
* Moreover, each object table index uniquely refers to an object.
|
* Moreover, each object table index uniquely refers to an object.
|
||||||
* Indexes in the WMF Object Table always start at 0.
|
* Indexes in the WMF Object Table always start at 0.
|
||||||
*
|
*
|
||||||
* @param entry
|
* @param entry the object table entry
|
||||||
*/
|
*/
|
||||||
public void addObjectTableEntry(HwmfObjectTableEntry entry) {
|
public void addObjectTableEntry(HwmfObjectTableEntry entry) {
|
||||||
int objIdx = objectIndexes.nextClearBit(0);
|
int objIdx = objectIndexes.nextClearBit(0);
|
||||||
|
@ -433,32 +446,167 @@ public class HwmfGraphics {
|
||||||
final HwmfDrawProperties prop = getProperties();
|
final HwmfDrawProperties prop = getProperties();
|
||||||
|
|
||||||
final AffineTransform at = graphicsCtx.getTransform();
|
final AffineTransform at = graphicsCtx.getTransform();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
at.createInverse();
|
at.createInverse();
|
||||||
} catch (NoninvertibleTransformException e) {
|
} catch (NoninvertibleTransformException e) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
HwmfFont font = prop.getFont();
|
final HwmfFont font = prop.getFont();
|
||||||
if (font == null || text == null || text.length == 0) {
|
if (font == null || text == null || text.length == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
double fontH = getFontHeight(font);
|
String textString = trimText(font, isUnicode, text, length);
|
||||||
// TODO: another approx. ...
|
if (textString.isEmpty()) {
|
||||||
double fontW = fontH/1.8;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Charset charset;
|
final DrawFontManager fontHandler = DrawFactory.getInstance(graphicsCtx).getFontManager(graphicsCtx);
|
||||||
if (isUnicode) {
|
final FontInfo fontInfo = fontHandler.getMappedFont(graphicsCtx, font);
|
||||||
charset = Charsets.UTF_16LE;
|
textString = fontHandler.mapFontCharset(graphicsCtx, fontInfo, textString);
|
||||||
|
|
||||||
|
final AttributedString as = new AttributedString(textString);
|
||||||
|
addAttributes(as::addAttribute, font, fontInfo.getTypeface());
|
||||||
|
final FontRenderContext frc = graphicsCtx.getFontRenderContext();
|
||||||
|
|
||||||
|
calculateDx(textString, dx, font, fontInfo, frc, as);
|
||||||
|
|
||||||
|
final double angle = Math.toRadians(-font.getEscapement()/10.);
|
||||||
|
|
||||||
|
final Point2D dst = getRotatedOffset(angle, frc, as);
|
||||||
|
final Shape clipShape = graphicsCtx.getClip();
|
||||||
|
|
||||||
|
try {
|
||||||
|
updateClipping(graphicsCtx, clip, angle, opts);
|
||||||
|
|
||||||
|
// TODO: Check: certain images don't use the reference of the extTextOut, but rely on a moveto issued beforehand
|
||||||
|
Point2D moveTo = (reference.distance(0,0) == 0) ? prop.getLocation() : reference;
|
||||||
|
graphicsCtx.translate(moveTo.getX(), moveTo.getY());
|
||||||
|
|
||||||
|
graphicsCtx.rotate(angle);
|
||||||
|
if (scale != null) {
|
||||||
|
graphicsCtx.scale(scale.getWidth() < 0 ? -1 : 1, scale.getHeight() < 0 ? -1 : 1);
|
||||||
|
}
|
||||||
|
graphicsCtx.translate(dst.getX(), dst.getY());
|
||||||
|
graphicsCtx.setColor(prop.getTextColor().getColor());
|
||||||
|
graphicsCtx.drawString(as.getIterator(), 0, 0);
|
||||||
|
} finally {
|
||||||
|
graphicsCtx.setTransform(at);
|
||||||
|
graphicsCtx.setClip(clipShape);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The dx array indicate the distance between origins of adjacent character cells.
|
||||||
|
* For example, dx[i] logical units separate the origins of character cell i and character cell i + 1.
|
||||||
|
* So dx{i] is the complete width of the current char + space to the next character
|
||||||
|
*
|
||||||
|
* In AWT we have the {@link TextAttribute#TRACKING} attribute, which works very similar.
|
||||||
|
* As we don't know (yet) the calculation based on the font size/height, we interpolate
|
||||||
|
* between the default tracking and a tracking value of 1
|
||||||
|
*/
|
||||||
|
private void calculateDx(String textString, List<Integer> dx, HwmfFont font, FontInfo fontInfo, FontRenderContext frc, AttributedString as) {
|
||||||
|
if (dx == null || dx.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final List<DxLayout> dxList = new ArrayList<>();
|
||||||
|
|
||||||
|
Map<TextAttribute,Object> fontAtt = new HashMap<>();
|
||||||
|
// Font tracking default (= 0)
|
||||||
|
addAttributes(fontAtt::put, font, fontInfo.getTypeface());
|
||||||
|
final GlyphVector gv0 = new Font(fontAtt).createGlyphVector(frc, textString);
|
||||||
|
// Font tracking = 1
|
||||||
|
fontAtt.put(TextAttribute.TRACKING, 1);
|
||||||
|
final GlyphVector gv1 = new Font(fontAtt).createGlyphVector(frc, textString);
|
||||||
|
|
||||||
|
int beginIndex = 0;
|
||||||
|
for (int offset = 0; offset < dx.size(); offset++) {
|
||||||
|
if (beginIndex >= textString.length()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
DxLayout dxLayout = new DxLayout();
|
||||||
|
dxLayout.dx = dx.get(offset);
|
||||||
|
dxLayout.pos0 = gv0.getGlyphPosition(offset).getX();
|
||||||
|
dxLayout.pos1 = gv1.getGlyphPosition(offset).getX();
|
||||||
|
dxLayout.beginIndex = beginIndex;
|
||||||
|
dxLayout.endIndex = textString.offsetByCodePoints(beginIndex, 1);
|
||||||
|
dxList.add(dxLayout);
|
||||||
|
|
||||||
|
beginIndex = dxLayout.endIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the linear (y ~= Tracking setting / x ~= character spacing / target value)
|
||||||
|
// y = m * x + n
|
||||||
|
// y = ((y2-y1)/(x2-x1))x + ((y1x2-y2x1)/(x2-x1))
|
||||||
|
|
||||||
|
DxLayout dx0 = null;
|
||||||
|
for (DxLayout dx1 : dxList) {
|
||||||
|
if (dx0 != null) {
|
||||||
|
// Default Tracking = 0 (y1)
|
||||||
|
double y1 = 0, x1 = dx1.pos0-dx0.pos0;
|
||||||
|
// Second Tracking = 1 (y2)
|
||||||
|
double y2 = 1, x2 = dx1.pos1-dx0.pos1;
|
||||||
|
double track = ((y2-y1)/(x2-x1))*dx0.dx + ((y1*x2-y2*x1)/(x2-x1));
|
||||||
|
as.addAttribute(TextAttribute.TRACKING, (float)track, dx0.beginIndex, dx0.endIndex);
|
||||||
|
}
|
||||||
|
dx0 = dx1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addAttributes(BiConsumer<TextAttribute,Object> attributes, HwmfFont font, String typeface) {
|
||||||
|
attributes.accept(TextAttribute.FAMILY, typeface);
|
||||||
|
attributes.accept(TextAttribute.SIZE, getFontHeight(font));
|
||||||
|
if (font.isStrikeOut()) {
|
||||||
|
attributes.accept(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON);
|
||||||
|
}
|
||||||
|
if (font.isUnderline()) {
|
||||||
|
attributes.accept(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
|
||||||
|
}
|
||||||
|
if (font.isItalic()) {
|
||||||
|
attributes.accept(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE);
|
||||||
|
}
|
||||||
|
// convert font weight to awt font weight - usually a font weight of 400 is regarded as regular
|
||||||
|
final int fw = font.getWeight();
|
||||||
|
Float awtFW = TextAttribute.WEIGHT_REGULAR;
|
||||||
|
for (int i=0; i<WEIGHT_MAP.length; i+=2) {
|
||||||
|
if (fw >= WEIGHT_MAP[i]) {
|
||||||
|
awtFW = WEIGHT_MAP[i+1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
attributes.accept(TextAttribute.WEIGHT, awtFW);
|
||||||
|
}
|
||||||
|
|
||||||
|
private double getFontHeight(HwmfFont font) {
|
||||||
|
// see HwmfFont#height for details
|
||||||
|
double fontHeight = font.getHeight();
|
||||||
|
if (fontHeight == 0) {
|
||||||
|
return 12;
|
||||||
|
} else if (fontHeight < 0) {
|
||||||
|
return -fontHeight;
|
||||||
} else {
|
} else {
|
||||||
charset = font.getCharset().getCharset();
|
// TODO: fix font height calculation
|
||||||
if (charset == null) {
|
// the height is given as font size + ascent + descent
|
||||||
charset = DEFAULT_CHARSET;
|
// as an approximation we reduce the height by a static factor
|
||||||
|
//
|
||||||
|
// see https://stackoverflow.com/a/26564924/2066598 on to get the font size from the cell height
|
||||||
|
return fontHeight*3/4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Charset getCharset(HwmfFont font, boolean isUnicode) {
|
||||||
|
if (isUnicode) {
|
||||||
|
return Charsets.UTF_16LE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Charset charset = font.getCharset().getCharset();
|
||||||
|
return (charset == null) ? DEFAULT_CHARSET : charset;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String trimText(HwmfFont font, boolean isUnicode, byte[] text, int length) {
|
||||||
|
final Charset charset = getCharset(font, isUnicode);
|
||||||
|
|
||||||
int trimLen;
|
int trimLen;
|
||||||
for (trimLen=0; trimLen<text.length; trimLen+=2) {
|
for (trimLen=0; trimLen<text.length; trimLen+=2) {
|
||||||
if (trimLen == text.length-1) {
|
if (trimLen == text.length-1) {
|
||||||
|
@ -473,83 +621,26 @@ public class HwmfGraphics {
|
||||||
}
|
}
|
||||||
|
|
||||||
String textString = new String(text, 0, trimLen, charset);
|
String textString = new String(text, 0, trimLen, charset);
|
||||||
textString = textString.substring(0, Math.min(textString.length(), length));
|
return textString.substring(0, Math.min(textString.length(), length));
|
||||||
|
|
||||||
if (textString.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawFontManager fontHandler = DrawFactory.getInstance(graphicsCtx).getFontManager(graphicsCtx);
|
private void updateHorizontalAlign(AffineTransform tx, TextLayout layout) {
|
||||||
FontInfo fontInfo = fontHandler.getMappedFont(graphicsCtx, font);
|
switch (prop.getTextAlignLatin()) {
|
||||||
if (fontInfo.getCharset() == FontCharset.SYMBOL) {
|
|
||||||
textString = DrawFontManagerDefault.mapSymbolChars(textString);
|
|
||||||
}
|
|
||||||
|
|
||||||
AttributedString as = new AttributedString(textString);
|
|
||||||
addAttributes(as, font, fontInfo.getTypeface());
|
|
||||||
|
|
||||||
// disabled for the time being, as the results aren't promising
|
|
||||||
/*
|
|
||||||
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
|
|
||||||
//byte array text[]. dx[] stores the x info in the
|
|
||||||
//first byte of a multibyte character, but dx[] stores 0
|
|
||||||
//for the other bytes in that character.
|
|
||||||
//We need to map this information to the String offsets
|
|
||||||
//dx[0] = 13 text[0] = -125
|
|
||||||
//dx[1] = 0 text[1] = 118
|
|
||||||
//dx[2] = 14 text[2] = -125
|
|
||||||
//dx[3] = 0 text[3] = -115
|
|
||||||
// needs to be remapped as:
|
|
||||||
//dxNormed[0] = 13 textString.get(0) = U+30D7
|
|
||||||
//dxNormed[1] = 14 textString.get(1) = U+30ED
|
|
||||||
|
|
||||||
final int cps = textString.codePointCount(0, textString.length());
|
|
||||||
final int unicodeSteps = Math.max(dx.size()/cps, 1);
|
|
||||||
int dxPosition = 0, lastDxPosition = 0;
|
|
||||||
int beginIndex = 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(lastDxPosition) - fontW) / fontH), beginIndex, endIndex);
|
|
||||||
}
|
|
||||||
lastDxPosition = dxPosition;
|
|
||||||
dxPosition += (isUnicode) ? unicodeSteps : (endIndex-beginIndex);
|
|
||||||
beginIndex = endIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
double angle = Math.toRadians(-font.getEscapement()/10.);
|
|
||||||
|
|
||||||
final HwmfText.HwmfTextAlignment align = prop.getTextAlignLatin();
|
|
||||||
final HwmfText.HwmfTextVerticalAlignment valign = prop.getTextVAlignLatin();
|
|
||||||
final FontRenderContext frc = graphicsCtx.getFontRenderContext();
|
|
||||||
final TextLayout layout = new TextLayout(as.getIterator(), frc);
|
|
||||||
|
|
||||||
final Rectangle2D pixelBounds = layout.getBounds();
|
|
||||||
|
|
||||||
AffineTransform tx = new AffineTransform();
|
|
||||||
switch (align) {
|
|
||||||
default:
|
default:
|
||||||
case LEFT:
|
case LEFT:
|
||||||
break;
|
break;
|
||||||
case CENTER:
|
case CENTER:
|
||||||
tx.translate(-pixelBounds.getWidth() / 2., 0);
|
tx.translate(-layout.getBounds().getWidth() / 2., 0);
|
||||||
break;
|
break;
|
||||||
case RIGHT:
|
case RIGHT:
|
||||||
tx.translate(-layout.getAdvance(), 0);
|
tx.translate(-layout.getAdvance(), 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateVerticalAlign(AffineTransform tx, TextLayout layout) {
|
||||||
// TODO: check min/max orientation
|
// TODO: check min/max orientation
|
||||||
switch (valign) {
|
switch (prop.getTextVAlignLatin()) {
|
||||||
case TOP:
|
case TOP:
|
||||||
tx.translate(0, layout.getAscent());
|
tx.translate(0, layout.getAscent());
|
||||||
break;
|
break;
|
||||||
|
@ -557,17 +648,18 @@ public class HwmfGraphics {
|
||||||
case BASELINE:
|
case BASELINE:
|
||||||
break;
|
break;
|
||||||
case BOTTOM:
|
case BOTTOM:
|
||||||
tx.translate(0, -(pixelBounds.getHeight()-layout.getDescent()));
|
tx.translate(0, -(layout.getBounds().getHeight()-layout.getDescent()));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tx.rotate(angle);
|
}
|
||||||
Point2D src = new Point2D.Double();
|
|
||||||
Point2D dst = new Point2D.Double();
|
private void updateClipping(Graphics2D graphicsCtx, Rectangle2D clip, double angle, WmfExtTextOutOptions opts) {
|
||||||
tx.transform(src, dst);
|
if (clip == null || clip.getBounds2D().isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final AffineTransform at = graphicsCtx.getTransform();
|
||||||
|
|
||||||
final Shape clipShape = graphicsCtx.getClip();
|
|
||||||
try {
|
|
||||||
if (clip != null && !clip.getBounds2D().isEmpty()) {
|
|
||||||
graphicsCtx.translate(-clip.getCenterX(), -clip.getCenterY());
|
graphicsCtx.translate(-clip.getCenterX(), -clip.getCenterY());
|
||||||
graphicsCtx.rotate(angle);
|
graphicsCtx.rotate(angle);
|
||||||
graphicsCtx.translate(clip.getCenterX(), clip.getCenterY());
|
graphicsCtx.translate(clip.getCenterX(), clip.getCenterY());
|
||||||
|
@ -578,60 +670,19 @@ public class HwmfGraphics {
|
||||||
if (opts.isClipped()) {
|
if (opts.isClipped()) {
|
||||||
graphicsCtx.setClip(clip);
|
graphicsCtx.setClip(clip);
|
||||||
}
|
}
|
||||||
|
|
||||||
graphicsCtx.setTransform(at);
|
graphicsCtx.setTransform(at);
|
||||||
}
|
}
|
||||||
|
|
||||||
graphicsCtx.translate(reference.getX(), reference.getY());
|
private Point2D getRotatedOffset(double angle, FontRenderContext frc, AttributedString as) {
|
||||||
graphicsCtx.rotate(angle);
|
final TextLayout layout = new TextLayout(as.getIterator(), frc);
|
||||||
if (scale != null) {
|
final AffineTransform tx = new AffineTransform();
|
||||||
graphicsCtx.scale(scale.getWidth() < 0 ? -1 : 1, scale.getHeight() < 0 ? -1 : 1);
|
updateHorizontalAlign(tx, layout);
|
||||||
}
|
updateVerticalAlign(tx, layout);
|
||||||
graphicsCtx.translate(dst.getX(), dst.getY());
|
|
||||||
graphicsCtx.setColor(prop.getTextColor().getColor());
|
|
||||||
graphicsCtx.drawString(as.getIterator(), 0, 0);
|
|
||||||
} finally {
|
|
||||||
graphicsCtx.setTransform(at);
|
|
||||||
graphicsCtx.setClip(clipShape);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addAttributes(AttributedString as, HwmfFont font, String typeface) {
|
tx.rotate(angle);
|
||||||
as.addAttribute(TextAttribute.FAMILY, typeface);
|
Point2D src = new Point2D.Double();
|
||||||
as.addAttribute(TextAttribute.SIZE, getFontHeight(font));
|
return tx.transform(src, null);
|
||||||
if (font.isStrikeOut()) {
|
|
||||||
as.addAttribute(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON);
|
|
||||||
}
|
|
||||||
if (font.isUnderline()) {
|
|
||||||
as.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
|
|
||||||
}
|
|
||||||
if (font.isItalic()) {
|
|
||||||
as.addAttribute(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE);
|
|
||||||
}
|
|
||||||
// convert font weight to awt font weight - usually a font weight of 400 is regarded as regular
|
|
||||||
final int fw = font.getWeight();
|
|
||||||
Float awtFW = TextAttribute.WEIGHT_REGULAR;
|
|
||||||
for (int i=0; i<WEIGHT_MAP.length; i+=2) {
|
|
||||||
if (fw >= WEIGHT_MAP[i]) {
|
|
||||||
awtFW = WEIGHT_MAP[i+1];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
as.addAttribute(TextAttribute.WEIGHT, awtFW);
|
|
||||||
}
|
|
||||||
|
|
||||||
private double getFontHeight(HwmfFont font) {
|
|
||||||
// see HwmfFont#height for details
|
|
||||||
double fontHeight = font.getHeight();
|
|
||||||
if (fontHeight == 0) {
|
|
||||||
return 12;
|
|
||||||
} else if (fontHeight < 0) {
|
|
||||||
return -fontHeight;
|
|
||||||
} else {
|
|
||||||
// TODO: fix font height calculation
|
|
||||||
// the height is given as font size + ascent + descent
|
|
||||||
// as an approximation we reduce the height by a static factor
|
|
||||||
return fontHeight*3/4;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void drawImage(BufferedImage img, Rectangle2D srcBounds, Rectangle2D dstBounds) {
|
public void drawImage(BufferedImage img, Rectangle2D srcBounds, Rectangle2D dstBounds) {
|
||||||
|
|
|
@ -239,6 +239,7 @@ public class HwmfText {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public static class WmfExtTextOutOptions implements GenericRecord {
|
public static class WmfExtTextOutOptions implements GenericRecord {
|
||||||
/**
|
/**
|
||||||
* Indicates that the background color that is defined in the playback device context
|
* Indicates that the background color that is defined in the playback device context
|
||||||
|
@ -480,7 +481,8 @@ public class HwmfText {
|
||||||
return GenericRecordUtil.getGenericProperties(
|
return GenericRecordUtil.getGenericProperties(
|
||||||
"reference", this::getReference,
|
"reference", this::getReference,
|
||||||
"bounds", this::getBounds,
|
"bounds", this::getBounds,
|
||||||
"text", this::getGenericText
|
"text", this::getGenericText,
|
||||||
|
"dx", () -> dx
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,7 @@ import org.apache.poi.util.IOUtils;
|
||||||
import org.apache.poi.util.RecordFormatException;
|
import org.apache.poi.util.RecordFormatException;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@SuppressWarnings("StatementWithEmptyBody")
|
||||||
public class TestHemfPicture {
|
public class TestHemfPicture {
|
||||||
|
|
||||||
private static final POIDataSamples ss_samples = POIDataSamples.getSpreadSheetInstance();
|
private static final POIDataSamples ss_samples = POIDataSamples.getSpreadSheetInstance();
|
||||||
|
@ -77,44 +78,49 @@ public class TestHemfPicture {
|
||||||
PPTX2PNG.main(args);
|
PPTX2PNG.main(args);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@Test
|
@Test
|
||||||
@Ignore("Only for manual tests - need to add org.tukaani:xz:1.8 for this to work")
|
@Ignore("Only for manual tests - need to add org.tukaani:xz:1.8 for this to work")
|
||||||
public void paintMultiple() throws Exception {
|
public void paintMultiple() throws Exception {
|
||||||
final byte buf[] = new byte[50_000_000];
|
Pattern fileExt = Pattern.compile("(?i)^(.+/)*(.+)\\.(emf|wmf)$");
|
||||||
|
final byte[] buf = new byte[50_000_000];
|
||||||
try (SevenZFile sevenZFile = new SevenZFile(new File("tmp/plus_emf.7z"))
|
try (SevenZFile sevenZFile = new SevenZFile(new File("tmp/plus_emf.7z"))
|
||||||
) {
|
) {
|
||||||
SevenZArchiveEntry entry;
|
SevenZArchiveEntry entry;
|
||||||
while ((entry = sevenZFile.getNextEntry()) != null) {
|
while ((entry = sevenZFile.getNextEntry()) != null) {
|
||||||
final String etName = entry.getName();
|
if (entry.isDirectory() || entry.getSize() == 0) continue;
|
||||||
|
Matcher m = fileExt.matcher(entry.getName());
|
||||||
if (entry.isDirectory() || !etName.endsWith(".emf")) continue;
|
if (!m.matches()) continue;
|
||||||
|
|
||||||
int size = sevenZFile.read(buf);
|
int size = sevenZFile.read(buf);
|
||||||
|
|
||||||
ByteArrayInputStream bis = new ByteArrayInputStream(buf, 0, size);
|
ByteArrayInputStream bis = new ByteArrayInputStream(buf, 0, size);
|
||||||
System.setIn(bis);
|
System.setIn(bis);
|
||||||
|
|
||||||
String lastName = etName.replaceFirst(".+/", "");
|
|
||||||
|
|
||||||
String[] args = {
|
String[] args = {
|
||||||
"-format", "png", // png,gif,jpg or null for test
|
"-format", "png", // png,gif,jpg or null for test
|
||||||
"-outdir", new File("build/tmp/").getCanonicalPath(),
|
"-outdir", new File("build/tmp/").getCanonicalPath(),
|
||||||
"-outfile", lastName.replace(".emf", ".png"),
|
"-outfile", m.replaceAll("$2.png"),
|
||||||
"-fixside", "long",
|
"-fixside", "long",
|
||||||
"-scale", "800",
|
"-scale", "800",
|
||||||
"-ignoreParse",
|
"-ignoreParse",
|
||||||
|
"-inputtype", m.replaceAll("$3").toUpperCase(),
|
||||||
// "-dump", new File("build/tmp/", lastName.replace(".emf",".json")).getCanonicalPath(),
|
// "-dump", new File("build/tmp/", lastName.replace(".emf",".json")).getCanonicalPath(),
|
||||||
// "-quiet",
|
"-quiet",
|
||||||
// "-extractEmbedded",
|
// "-extractEmbedded",
|
||||||
"stdin"
|
"stdin"
|
||||||
};
|
};
|
||||||
|
try {
|
||||||
PPTX2PNG.main(args);
|
PPTX2PNG.main(args);
|
||||||
|
System.out.println("Processing "+entry.getName()+" ok");
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println("Processing "+entry.getName()+" failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
}
|
||||||
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testBasicWindows() throws Exception {
|
public void testBasicWindows() throws Exception {
|
||||||
try (InputStream is = ss_samples.openResourceAsStream("SimpleEMF_windows.emf")) {
|
try (InputStream is = ss_samples.openResourceAsStream("SimpleEMF_windows.emf")) {
|
||||||
|
@ -272,7 +278,7 @@ public class TestHemfPicture {
|
||||||
public void testInfiniteLoopOnFile() throws Exception {
|
public void testInfiniteLoopOnFile() throws Exception {
|
||||||
try (InputStream is = ss_samples.openResourceAsStream("61294.emf")) {
|
try (InputStream is = ss_samples.openResourceAsStream("61294.emf")) {
|
||||||
HemfPicture pic = new HemfPicture(is);
|
HemfPicture pic = new HemfPicture(is);
|
||||||
for (HemfRecord record : pic) {
|
for (HemfRecord ignored : pic) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -286,7 +292,7 @@ public class TestHemfPicture {
|
||||||
is.close();
|
is.close();
|
||||||
|
|
||||||
HemfPicture pic = new HemfPicture(new ByteArrayInputStream(bos.toByteArray()));
|
HemfPicture pic = new HemfPicture(new ByteArrayInputStream(bos.toByteArray()));
|
||||||
for (HemfRecord record : pic) {
|
for (HemfRecord ignored : pic) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue