git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1881322 13f79535-47bb-0310-9956-ffa450edef68tags/before_ooxml_3rd_edition
protected DrawableHint(int id) { | protected DrawableHint(int id) { | ||||
super(id); | super(id); | ||||
} | } | ||||
public boolean isCompatibleValue(Object val) { | public boolean isCompatibleValue(Object val) { | ||||
return true; | return true; | ||||
} | } | ||||
public String toString() { | public String toString() { | ||||
switch (intKey()) { | switch (intKey()) { | ||||
case 1: return "DRAW_FACTORY"; | case 1: return "DRAW_FACTORY"; | ||||
case 11: return "GRESTORE"; | case 11: return "GRESTORE"; | ||||
case 12: return "CURRENT_SLIDE"; | case 12: return "CURRENT_SLIDE"; | ||||
case 13: return "BUFFERED_IMAGE"; | case 13: return "BUFFERED_IMAGE"; | ||||
case 14: return "DEFAULT_CHARSET"; | |||||
default: return "UNKNOWN_ID "+intKey(); | default: return "UNKNOWN_ID "+intKey(); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* {@link DrawFactory} which will be used to draw objects into this graphics context | * {@link DrawFactory} which will be used to draw objects into this graphics context | ||||
*/ | */ | ||||
* Internal key for caching the preset geometries | * Internal key for caching the preset geometries | ||||
*/ | */ | ||||
DrawableHint PRESET_GEOMETRY_CACHE = new DrawableHint(6); | DrawableHint PRESET_GEOMETRY_CACHE = new DrawableHint(6); | ||||
/** | /** | ||||
* draw text via {@link java.awt.Graphics2D#drawString(java.text.AttributedCharacterIterator, float, float)} | * draw text via {@link java.awt.Graphics2D#drawString(java.text.AttributedCharacterIterator, float, float)} | ||||
*/ | */ | ||||
/** | /** | ||||
* Use this object to resolve unknown / missing fonts when rendering slides. | * Use this object to resolve unknown / missing fonts when rendering slides. | ||||
* The font handler must be of type {@link DrawFontManager}.<p> | * The font handler must be of type {@link DrawFontManager}.<p> | ||||
* | |||||
* In case a {@code FONT_HANDLER} is register, {@code FONT_FALLBACK} and {@code FONT_MAP} are ignored | |||||
* | |||||
* In case a {@code FONT_HANDLER} is register, {@code FONT_FALLBACK} and {@code FONT_MAP} are ignored | |||||
*/ | */ | ||||
DrawableHint FONT_HANDLER = new DrawableHint(7); | DrawableHint FONT_HANDLER = new DrawableHint(7); | ||||
/** | /** | ||||
* Key for a font fallback map of type {@code Map<String,String>} which maps | * Key for a font fallback map of type {@code Map<String,String>} which maps | ||||
* the original font family (key) to the fallback font family (value). | * the original font family (key) to the fallback font family (value). | ||||
* the original font family (key) to the mapped font family (value) | * the original font family (key) to the mapped font family (value) | ||||
*/ | */ | ||||
DrawableHint FONT_MAP = new DrawableHint(9); | DrawableHint FONT_MAP = new DrawableHint(9); | ||||
DrawableHint GSAVE = new DrawableHint(10); | DrawableHint GSAVE = new DrawableHint(10); | ||||
DrawableHint GRESTORE = new DrawableHint(11); | DrawableHint GRESTORE = new DrawableHint(11); | ||||
/** | /** | ||||
* The Common SL Draw API works sometimes cascading, but there are places | * The Common SL Draw API works sometimes cascading, but there are places | ||||
* where the current slide context need to be evaluated, e.g. when slide numbers | * where the current slide context need to be evaluated, e.g. when slide numbers | ||||
*/ | */ | ||||
DrawableHint BUFFERED_IMAGE = new DrawableHint(13); | DrawableHint BUFFERED_IMAGE = new DrawableHint(13); | ||||
/** | |||||
* Sets the default charset to render text elements. | |||||
* Opposed to other windows libraries in POI this simply defaults to Windows-1252. | |||||
* The rendering value is of type {@link java.nio.charset.Charset} | |||||
*/ | |||||
DrawableHint DEFAULT_CHARSET = new DrawableHint(14); | |||||
/** | /** | ||||
* Apply 2-D transforms before drawing this shape. This includes rotation and flipping. | * Apply 2-D transforms before drawing this shape. This includes rotation and flipping. | ||||
* | * | ||||
* @param graphics the graphics whos transform matrix will be modified | * @param graphics the graphics whos transform matrix will be modified | ||||
*/ | */ | ||||
void applyTransform(Graphics2D graphics); | void applyTransform(Graphics2D graphics); | ||||
/** | /** | ||||
* Draw this shape into the supplied canvas | * Draw this shape into the supplied canvas | ||||
* | * | ||||
* @param graphics the graphics to draw into | * @param graphics the graphics to draw into | ||||
*/ | */ | ||||
void draw(Graphics2D graphics); | void draw(Graphics2D graphics); | ||||
/** | /** | ||||
* draw any content within this shape (image, text, etc.). | * draw any content within this shape (image, text, etc.). | ||||
* | * | ||||
* @param graphics the graphics to draw into | * @param graphics the graphics to draw into | ||||
*/ | */ | ||||
void drawContent(Graphics2D graphics); | |||||
void drawContent(Graphics2D graphics); | |||||
} | } |
import java.awt.image.BufferedImage; | import java.awt.image.BufferedImage; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.InputStream; | import java.io.InputStream; | ||||
import java.nio.charset.Charset; | |||||
import org.apache.poi.common.usermodel.GenericRecord; | import org.apache.poi.common.usermodel.GenericRecord; | ||||
import org.apache.poi.util.Dimension2DDouble; | import org.apache.poi.util.Dimension2DDouble; | ||||
default GenericRecord getGenericRecord() { return null; } | default GenericRecord getGenericRecord() { return null; } | ||||
/** | |||||
* Sets the default charset to render text elements. | |||||
* Opposed to other windows libraries in POI this simply defaults to Windows-1252. | |||||
* | |||||
* @param defaultCharset the default charset | |||||
*/ | |||||
default void setDefaultCharset(Charset defaultCharset) {} | |||||
} | } |
import java.awt.geom.PathIterator; | import java.awt.geom.PathIterator; | ||||
import java.awt.geom.Point2D; | import java.awt.geom.Point2D; | ||||
import java.awt.geom.Rectangle2D; | import java.awt.geom.Rectangle2D; | ||||
import java.awt.image.BufferedImage; | |||||
import java.awt.image.ColorModel; | |||||
import java.awt.image.ComponentColorModel; | |||||
import java.awt.image.DirectColorModel; | |||||
import java.awt.image.IndexColorModel; | |||||
import java.awt.image.PackedColorModel; | |||||
import java.io.Closeable; | import java.io.Closeable; | ||||
import java.io.File; | import java.io.File; | ||||
import java.io.FileOutputStream; | import java.io.FileOutputStream; | ||||
private static final Pattern ESC_CHARS = Pattern.compile("[\"\\p{Cntrl}\\\\]"); | private static final Pattern ESC_CHARS = Pattern.compile("[\"\\p{Cntrl}\\\\]"); | ||||
private static final String NL = System.getProperty("line.separator"); | private static final String NL = System.getProperty("line.separator"); | ||||
@FunctionalInterface | @FunctionalInterface | ||||
protected interface GenericRecordHandler { | protected interface GenericRecordHandler { | ||||
/** | /** | ||||
handler(Path2D.class, GenericRecordJsonWriter::printPath); | handler(Path2D.class, GenericRecordJsonWriter::printPath); | ||||
handler(AffineTransform.class, GenericRecordJsonWriter::printAffineTransform); | handler(AffineTransform.class, GenericRecordJsonWriter::printAffineTransform); | ||||
handler(Color.class, GenericRecordJsonWriter::printColor); | handler(Color.class, GenericRecordJsonWriter::printColor); | ||||
handler(BufferedImage.class, GenericRecordJsonWriter::printImage); | |||||
handler(Array.class, GenericRecordJsonWriter::printArray); | handler(Array.class, GenericRecordJsonWriter::printArray); | ||||
handler(Object.class, GenericRecordJsonWriter::printObject); | handler(Object.class, GenericRecordJsonWriter::printObject); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
protected boolean printImage(String name, Object o) { | |||||
BufferedImage img = (BufferedImage)o; | |||||
final String[] COLOR_SPACES = { | |||||
"XYZ","Lab","Luv","YCbCr","Yxy","RGB","GRAY","HSV","HLS","CMYK","Unknown","CMY","Unknown" | |||||
}; | |||||
final String[] IMAGE_TYPES = { | |||||
"CUSTOM","INT_RGB","INT_ARGB","INT_ARGB_PRE","INT_BGR","3BYTE_BGR","4BYTE_ABGR","4BYTE_ABGR_PRE", | |||||
"USHORT_565_RGB","USHORT_555_RGB","BYTE_GRAY","USHORT_GRAY","BYTE_BINARY","BYTE_INDEXED" | |||||
}; | |||||
printName(name); | |||||
ColorModel cm = img.getColorModel(); | |||||
String colorType = | |||||
(cm instanceof IndexColorModel) ? "indexed" : | |||||
(cm instanceof ComponentColorModel) ? "component" : | |||||
(cm instanceof DirectColorModel) ? "direct" : | |||||
(cm instanceof PackedColorModel) ? "packed" : "unknown"; | |||||
fw.write( | |||||
"{ \"width\": "+img.getWidth()+ | |||||
", \"height\": "+img.getHeight()+ | |||||
", \"type\": \""+IMAGE_TYPES[img.getType()]+"\""+ | |||||
", \"colormodel\": \""+colorType+"\""+ | |||||
", \"pixelBits\": "+cm.getPixelSize()+ | |||||
", \"numComponents\": "+cm.getNumComponents()+ | |||||
", \"colorSpace\": \""+COLOR_SPACES[Math.min(cm.getColorSpace().getType(),12)]+"\""+ | |||||
", \"transparency\": "+cm.getTransparency()+ | |||||
", \"alpha\": "+cm.hasAlpha()+ | |||||
"}" | |||||
); | |||||
return true; | |||||
} | |||||
static String trimHex(final long l, final int size) { | static String trimHex(final long l, final int size) { | ||||
final String b = Long.toHexString(l); | final String b = Long.toHexString(l); | ||||
int len = b.length(); | int len = b.length(); |
// SYMBOL is missing | // SYMBOL is missing | ||||
if (font == null || !font.isSetTypeface() || "".equals(font.getTypeface())) { | if (font == null || !font.isSetTypeface() || "".equals(font.getTypeface())) { | ||||
font = coll.getLatin(); | |||||
// don't fallback to latin but bubble up in the style hierarchy (slide -> layout -> master -> theme) | |||||
return null; | |||||
// font = coll.getLatin(); | |||||
} | } | ||||
} | } | ||||
import java.io.File; | import java.io.File; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.InputStream; | import java.io.InputStream; | ||||
import java.nio.charset.Charset; | |||||
import java.util.Collections; | import java.util.Collections; | ||||
import org.apache.poi.common.usermodel.GenericRecord; | import org.apache.poi.common.usermodel.GenericRecord; | ||||
? ((EmbeddedExtractor) imgr).getEmbeddings() | ? ((EmbeddedExtractor) imgr).getEmbeddings() | ||||
: Collections.emptyList(); | : Collections.emptyList(); | ||||
} | } | ||||
@Override | |||||
void setDefaultCharset(Charset charset) { | |||||
imgr.setDefaultCharset(charset); | |||||
} | |||||
} | } |
import java.io.File; | import java.io.File; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.InputStream; | import java.io.InputStream; | ||||
import java.nio.charset.Charset; | |||||
import java.util.Collections; | import java.util.Collections; | ||||
import java.util.Set; | import java.util.Set; | ||||
abstract GenericRecord getRoot(); | abstract GenericRecord getRoot(); | ||||
abstract Iterable<EmbeddedPart> getEmbeddings(int slideNo); | abstract Iterable<EmbeddedPart> getEmbeddings(int slideNo); | ||||
abstract void setDefaultCharset(Charset charset); | |||||
} | } |
import java.io.File; | import java.io.File; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.InputStream; | import java.io.InputStream; | ||||
import java.nio.charset.Charset; | |||||
import java.util.Set; | import java.util.Set; | ||||
import java.util.Spliterator; | import java.util.Spliterator; | ||||
import java.util.Spliterators; | import java.util.Spliterators; | ||||
import org.apache.poi.sl.usermodel.SlideShowFactory; | import org.apache.poi.sl.usermodel.SlideShowFactory; | ||||
import org.apache.poi.util.IOUtils; | import org.apache.poi.util.IOUtils; | ||||
import org.apache.poi.util.Internal; | import org.apache.poi.util.Internal; | ||||
import org.apache.poi.util.LocaleUtil; | |||||
/** Handler for ppt and pptx files */ | /** Handler for ppt and pptx files */ | ||||
@Internal | @Internal | ||||
class PPTHandler extends MFProxy { | class PPTHandler extends MFProxy { | ||||
private SlideShow<?,?> ppt; | private SlideShow<?,?> ppt; | ||||
private Slide<?,?> slide; | private Slide<?,?> slide; | ||||
private Charset defaultCharset = LocaleUtil.CHARSET_1252; | |||||
@Override | @Override | ||||
public void parse(File file) throws IOException { | public void parse(File file) throws IOException { | ||||
throw e; | throw e; | ||||
} | } | ||||
} | } | ||||
if (ppt == null) { | |||||
throw new IOException("Unknown file format or missing poi-scratchpad.jar / poi-ooxml.jar"); | |||||
} | |||||
slide = ppt.getSlides().get(0); | slide = ppt.getSlides().get(0); | ||||
} | } | ||||
throw e; | throw e; | ||||
} | } | ||||
} | } | ||||
if (ppt == null) { | |||||
throw new IOException("Unknown file format or missing poi-scratchpad.jar / poi-ooxml.jar"); | |||||
} | |||||
slide = ppt.getSlides().get(0); | slide = ppt.getSlides().get(0); | ||||
} | } | ||||
; | ; | ||||
} | } | ||||
private static EmbeddedPart fromObjectShape(Shape s) { | |||||
final ObjectShape os = (ObjectShape)s; | |||||
private static EmbeddedPart fromObjectShape(Shape<?,?> s) { | |||||
final ObjectShape<?,?> os = (ObjectShape<?,?>)s; | |||||
final ObjectData od = os.getObjectData(); | final ObjectData od = os.getObjectData(); | ||||
EmbeddedPart embed = new EmbeddedPart(); | EmbeddedPart embed = new EmbeddedPart(); | ||||
embed.setName(od.getFileName()); | embed.setName(od.getFileName()); | ||||
}); | }); | ||||
return embed; | return embed; | ||||
} | } | ||||
@Override | |||||
void setDefaultCharset(Charset charset) { | |||||
} | |||||
} | } |
import java.io.FileOutputStream; | import java.io.FileOutputStream; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.InputStream; | import java.io.InputStream; | ||||
import java.nio.charset.Charset; | |||||
import java.util.Locale; | import java.util.Locale; | ||||
import java.util.Set; | import java.util.Set; | ||||
import java.util.regex.Pattern; | import java.util.regex.Pattern; | ||||
import org.apache.poi.common.usermodel.GenericRecord; | import org.apache.poi.common.usermodel.GenericRecord; | ||||
import org.apache.poi.poifs.filesystem.FileMagic; | import org.apache.poi.poifs.filesystem.FileMagic; | ||||
import org.apache.poi.sl.draw.Drawable; | |||||
import org.apache.poi.sl.draw.EmbeddedExtractor.EmbeddedPart; | import org.apache.poi.sl.draw.EmbeddedExtractor.EmbeddedPart; | ||||
import org.apache.poi.util.Dimension2DDouble; | import org.apache.poi.util.Dimension2DDouble; | ||||
import org.apache.poi.util.GenericRecordJsonWriter; | import org.apache.poi.util.GenericRecordJsonWriter; | ||||
import org.apache.poi.util.LocaleUtil; | |||||
import org.apache.poi.xslf.util.OutputFormat.BitmapFormat; | import org.apache.poi.xslf.util.OutputFormat.BitmapFormat; | ||||
import org.apache.poi.xslf.util.OutputFormat.SVGFormat; | import org.apache.poi.xslf.util.OutputFormat.SVGFormat; | ||||
" -inputType <type> default input file type (OLE2,WMF,EMF), default is OLE2 = Powerpoint\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" + | " 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" + | " -textAsShapes text elements are saved as shapes in SVG, necessary for variable spacing\n" + | ||||
" often found in math formulas"; | |||||
" often found in math formulas\n" + | |||||
" -charset sets the default charset to be used, defaults to Windows-1252"; | |||||
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! | ||||
private boolean extractEmbedded = false; | private boolean extractEmbedded = false; | ||||
private FileMagic defaultFileType = FileMagic.OLE2; | private FileMagic defaultFileType = FileMagic.OLE2; | ||||
private boolean textAsShapes = false; | private boolean textAsShapes = false; | ||||
private Charset charset = LocaleUtil.CHARSET_1252; | |||||
private PPTX2PNG() { | private PPTX2PNG() { | ||||
} | } | ||||
case "-extractEmbedded": | case "-extractEmbedded": | ||||
extractEmbedded = true; | extractEmbedded = true; | ||||
break; | break; | ||||
case "-charset": | |||||
if (opt != null) { | |||||
charset = Charset.forName(opt); | |||||
i++; | |||||
} else { | |||||
charset = LocaleUtil.CHARSET_1252; | |||||
} | |||||
break; | |||||
default: | default: | ||||
file = new File(args[i]); | file = new File(args[i]); | ||||
break; | break; | ||||
graphics.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED); | graphics.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED); | ||||
graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); | graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); | ||||
graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); | graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); | ||||
graphics.setRenderingHint(Drawable.DEFAULT_CHARSET, getDefaultCharset()); | |||||
graphics.scale(scale / lenSide, scale / lenSide); | graphics.scale(scale / lenSide, scale / lenSide); | ||||
} | } | ||||
private void dumpRecords(MFProxy proxy) throws IOException { | private void dumpRecords(MFProxy proxy) throws IOException { | ||||
if (dumpfile == null) { | |||||
if (dumpfile == null || "null".equals(dumpfile.getPath())) { | |||||
return; | return; | ||||
} | } | ||||
GenericRecord gr = proxy.getRoot(); | GenericRecord gr = proxy.getRoot(); | ||||
proxy.setQuite(quiet); | proxy.setQuite(quiet); | ||||
proxy.parse(file); | proxy.parse(file); | ||||
} | } | ||||
proxy.setDefaultCharset(charset); | |||||
return proxy; | return proxy; | ||||
} | } | ||||
return INPUT_PATTERN.matcher(inname).replaceAll(outpat); | return INPUT_PATTERN.matcher(inname).replaceAll(outpat); | ||||
} | } | ||||
private Charset getDefaultCharset() { | |||||
return charset; | |||||
} | |||||
static class NoScratchpadException extends IOException { | static class NoScratchpadException extends IOException { | ||||
NoScratchpadException() { | NoScratchpadException() { | ||||
} | } |
package org.apache.poi.xslf.usermodel; | package org.apache.poi.xslf.usermodel; | ||||
import static java.util.Arrays.asList; | |||||
import static org.junit.Assume.assumeFalse; | import static org.junit.Assume.assumeFalse; | ||||
import java.io.File; | import java.io.File; | ||||
import java.util.ArrayList; | |||||
import java.util.Collection; | import java.util.Collection; | ||||
import java.util.List; | |||||
import java.util.function.Function; | import java.util.function.Function; | ||||
import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||
import java.util.stream.Stream; | import java.util.stream.Stream; | ||||
/** | /** | ||||
* Test class for testing PPTX2PNG utility which renders .ppt and .pptx slideshows | * Test class for testing PPTX2PNG utility which renders .ppt and .pptx slideshows | ||||
*/ | */ | ||||
@SuppressWarnings("ConstantConditions") | |||||
@RunWith(Parameterized.class) | @RunWith(Parameterized.class) | ||||
public class TestPPTX2PNG { | public class TestPPTX2PNG { | ||||
private static boolean xslfOnly; | private static boolean xslfOnly; | ||||
private static final POIDataSamples samples = POIDataSamples.getSlideShowInstance(); | private static final POIDataSamples samples = POIDataSamples.getSlideShowInstance(); | ||||
private static final File basedir = null; | private static final File basedir = null; | ||||
private static final String files = | private static final String files = | ||||
"53446.ppt, alterman_security.ppt, alterman_security.pptx, KEY02.pptx, themes.pptx, " + | |||||
"bug64693.pptx, 53446.ppt, alterman_security.ppt, alterman_security.pptx, KEY02.pptx, themes.pptx, " + | |||||
"backgrounds.pptx, layouts.pptx, sample.pptx, shapes.pptx, 54880_chinese.ppt, keyframes.pptx," + | "backgrounds.pptx, layouts.pptx, sample.pptx, shapes.pptx, 54880_chinese.ppt, keyframes.pptx," + | ||||
"customGeo.pptx, customGeo.ppt, wrench.emf, santa.wmf, missing-moveto.ppt"; | "customGeo.pptx, customGeo.ppt, wrench.emf, santa.wmf, missing-moveto.ppt"; | ||||
@Parameter | @Parameter | ||||
public String pptFile; | public String pptFile; | ||||
@SuppressWarnings("ConstantConditions") | |||||
@Parameters(name="{0}") | @Parameters(name="{0}") | ||||
public static Collection<String> data() { | public static Collection<String> data() { | ||||
Function<String, Stream<String>> fun = (basedir == null) ? Stream::of : | Function<String, Stream<String>> fun = (basedir == null) ? Stream::of : | ||||
public void render() throws Exception { | public void render() throws Exception { | ||||
assumeFalse("ignore HSLF (.ppt) / HEMF (.emf) / HWMF (.wmf) files in no-scratchpad run", xslfOnly && pptFile.matches(".*\\.(ppt|emf|wmf)$")); | assumeFalse("ignore HSLF (.ppt) / HEMF (.emf) / HWMF (.wmf) files in no-scratchpad run", xslfOnly && pptFile.matches(".*\\.(ppt|emf|wmf)$")); | ||||
String[] args = { | |||||
// bug64693.pptx | |||||
final List<String> args = new ArrayList<>(asList( | |||||
"-format", "null", // png,gif,jpg,svg or null for test | "-format", "null", // png,gif,jpg,svg or null for test | ||||
"-slide", "-1", // -1 for all | "-slide", "-1", // -1 for all | ||||
"-outdir", new File("build/tmp/").getCanonicalPath(), | "-outdir", new File("build/tmp/").getCanonicalPath(), | ||||
"-dump", "null", | "-dump", "null", | ||||
"-quiet", | "-quiet", | ||||
"-fixside", "long", | "-fixside", "long", | ||||
"-scale", "800", | |||||
// "-scale", "1.333333333", | |||||
(basedir == null ? samples.getFile(pptFile) : new File(basedir, pptFile)).getAbsolutePath() | |||||
}; | |||||
PPTX2PNG.main(args); | |||||
"-scale", "800" | |||||
)); | |||||
if ("bug64693.pptx".equals(pptFile)) { | |||||
args.addAll(asList( | |||||
"-charset", "GBK" | |||||
)); | |||||
} | |||||
args.add((basedir == null ? samples.getFile(pptFile) : new File(basedir, pptFile)).getAbsolutePath()); | |||||
PPTX2PNG.main(args.toArray(new String[0])); | |||||
} | } | ||||
} | } |
javax.xml.bind.DatatypeConverter | javax.xml.bind.DatatypeConverter | ||||
@defaultMessage don't rely on the threads ContextClassLoader - provide the classloader via load(Class, Classloader) | @defaultMessage don't rely on the threads ContextClassLoader - provide the classloader via load(Class, Classloader) | ||||
java.util.ServiceLoader#load(java.lang.Class) | |||||
java.util.ServiceLoader#load(java.lang.Class) | |||||
@defaultMessage use java.nio.charset.StandardCharsets instead | |||||
org.apache.commons.codec.Charsets |
import java.io.ByteArrayInputStream; | import java.io.ByteArrayInputStream; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.InputStream; | import java.io.InputStream; | ||||
import java.nio.charset.Charset; | |||||
import org.apache.poi.common.usermodel.GenericRecord; | import org.apache.poi.common.usermodel.GenericRecord; | ||||
import org.apache.poi.hemf.usermodel.HemfPicture; | import org.apache.poi.hemf.usermodel.HemfPicture; | ||||
import org.apache.poi.hwmf.draw.HwmfGraphicsState; | import org.apache.poi.hwmf.draw.HwmfGraphicsState; | ||||
import org.apache.poi.hwmf.draw.HwmfImageRenderer; | import org.apache.poi.hwmf.draw.HwmfImageRenderer; | ||||
import org.apache.poi.sl.draw.BitmapImageRenderer; | import org.apache.poi.sl.draw.BitmapImageRenderer; | ||||
import org.apache.poi.sl.draw.Drawable; | |||||
import org.apache.poi.sl.draw.EmbeddedExtractor; | import org.apache.poi.sl.draw.EmbeddedExtractor; | ||||
import org.apache.poi.sl.draw.ImageRenderer; | import org.apache.poi.sl.draw.ImageRenderer; | ||||
import org.apache.poi.sl.usermodel.PictureData; | import org.apache.poi.sl.usermodel.PictureData; | ||||
public class HemfImageRenderer implements ImageRenderer, EmbeddedExtractor { | public class HemfImageRenderer implements ImageRenderer, EmbeddedExtractor { | ||||
HemfPicture image; | HemfPicture image; | ||||
double alpha; | double alpha; | ||||
boolean charsetInitialized = false; | |||||
@Override | @Override | ||||
public boolean canRender(String contentType) { | public boolean canRender(String contentType) { | ||||
return false; | return false; | ||||
} | } | ||||
Charset cs = (Charset)graphics.getRenderingHint(Drawable.DEFAULT_CHARSET); | |||||
if (cs != null && !charsetInitialized) { | |||||
setDefaultCharset(cs); | |||||
} | |||||
HwmfGraphicsState graphicsState = new HwmfGraphicsState(); | HwmfGraphicsState graphicsState = new HwmfGraphicsState(); | ||||
graphicsState.backup(graphics); | graphicsState.backup(graphics); | ||||
public Rectangle2D getBounds() { | public Rectangle2D getBounds() { | ||||
return Units.pointsToPixel(image == null ? new Rectangle2D.Double() : image.getBoundsInPoints()); | return Units.pointsToPixel(image == null ? new Rectangle2D.Double() : image.getBoundsInPoints()); | ||||
} | } | ||||
@Override | |||||
public void setDefaultCharset(Charset defaultCharset) { | |||||
image.setDefaultCharset(defaultCharset); | |||||
charsetInitialized = true; | |||||
} | |||||
} | } |
import java.awt.geom.Rectangle2D; | import java.awt.geom.Rectangle2D; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.nio.charset.Charset; | |||||
import java.nio.charset.StandardCharsets; | import java.nio.charset.StandardCharsets; | ||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.Collections; | import java.util.Collections; | ||||
import org.apache.poi.hemf.draw.HemfGraphics.EmfRenderState; | import org.apache.poi.hemf.draw.HemfGraphics.EmfRenderState; | ||||
import org.apache.poi.hemf.record.emfplus.HemfPlusRecord; | import org.apache.poi.hemf.record.emfplus.HemfPlusRecord; | ||||
import org.apache.poi.hemf.record.emfplus.HemfPlusRecordIterator; | import org.apache.poi.hemf.record.emfplus.HemfPlusRecordIterator; | ||||
import org.apache.poi.hwmf.usermodel.HwmfCharsetAware; | |||||
import org.apache.poi.hwmf.usermodel.HwmfPicture; | import org.apache.poi.hwmf.usermodel.HwmfPicture; | ||||
import org.apache.poi.util.GenericRecordJsonWriter; | import org.apache.poi.util.GenericRecordJsonWriter; | ||||
import org.apache.poi.util.GenericRecordUtil; | import org.apache.poi.util.GenericRecordUtil; | ||||
} | } | ||||
} | } | ||||
public static class EmfComment implements HemfRecord { | |||||
public static class EmfComment implements HemfRecord, HwmfCharsetAware { | |||||
private EmfCommentData data; | private EmfCommentData data; | ||||
@Override | @Override | ||||
} | } | ||||
assert(commentIdentifier == commentType.id); | assert(commentIdentifier == commentType.id); | ||||
} | } | ||||
@Override | |||||
public void setCharsetProvider(Supplier<Charset> provider) { | |||||
if (data instanceof HwmfCharsetAware) { | |||||
((HwmfCharsetAware)data).setCharsetProvider(provider); | |||||
} | |||||
} | |||||
} | } | ||||
public static class EmfCommentDataIterator implements Iterator<EmfCommentData> { | public static class EmfCommentDataIterator implements Iterator<EmfCommentData> { | ||||
* Private data is unknown to EMF; it is meaningful only to applications that know the format of the | * Private data is unknown to EMF; it is meaningful only to applications that know the format of the | ||||
* data and how to use it. EMR_COMMENT private data records MAY be ignored. | * data and how to use it. EMR_COMMENT private data records MAY be ignored. | ||||
*/ | */ | ||||
public static class EmfCommentDataGeneric implements EmfCommentData { | |||||
public static class EmfCommentDataGeneric implements EmfCommentData, HwmfCharsetAware { | |||||
private byte[] privateData; | private byte[] privateData; | ||||
private Supplier<Charset> charsetProvider = () -> LocaleUtil.CHARSET_1252; | |||||
@Override | @Override | ||||
public HemfCommentRecordType getCommentRecordType() { | public HemfCommentRecordType getCommentRecordType() { | ||||
} | } | ||||
public String getPrivateDataAsString() { | public String getPrivateDataAsString() { | ||||
return new String(privateData, LocaleUtil.CHARSET_1252); | |||||
return new String(privateData, charsetProvider.get()); | |||||
} | } | ||||
@Override | @Override | ||||
"privateDataAsString", this::getPrivateDataAsString | "privateDataAsString", this::getPrivateDataAsString | ||||
); | ); | ||||
} | } | ||||
@Override | |||||
public void setCharsetProvider(Supplier<Charset> provider) { | |||||
charsetProvider = provider; | |||||
} | |||||
} | } | ||||
/** The EMR_COMMENT_EMFPLUS record contains embedded EMF+ records. */ | /** The EMR_COMMENT_EMFPLUS record contains embedded EMF+ records. */ |
// the axis to convert from page space units to .01mm units. | // the axis to convert from page space units to .01mm units. | ||||
// This SHOULD be used only if the graphics mode specified by iGraphicsMode is GM_COMPATIBLE. | // This SHOULD be used only if the graphics mode specified by iGraphicsMode is GM_COMPATIBLE. | ||||
Dimension2D scl = graphicsMode == EmfGraphicsMode.GM_COMPATIBLE ? scale : null; | Dimension2D scl = graphicsMode == EmfGraphicsMode.GM_COMPATIBLE ? scale : null; | ||||
ctx.setCharsetProvider(charsetProvider); | |||||
ctx.drawString(rawTextBytes, stringLength, reference, scl, bounds, options, dx, isUnicode()); | ctx.drawString(rawTextBytes, stringLength, reference, scl, bounds, options, dx, isUnicode()); | ||||
} | } | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.math.BigDecimal; | import java.math.BigDecimal; | ||||
import java.math.RoundingMode; | import java.math.RoundingMode; | ||||
import java.nio.charset.StandardCharsets; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.Collections; | import java.util.Collections; | ||||
import java.util.LinkedHashMap; | import java.util.LinkedHashMap; | ||||
import java.util.function.BiFunction; | import java.util.function.BiFunction; | ||||
import java.util.function.Supplier; | import java.util.function.Supplier; | ||||
import org.apache.commons.codec.Charsets; | |||||
import org.apache.commons.math3.linear.LUDecomposition; | import org.apache.commons.math3.linear.LUDecomposition; | ||||
import org.apache.commons.math3.linear.MatrixUtils; | import org.apache.commons.math3.linear.MatrixUtils; | ||||
import org.apache.commons.math3.linear.RealMatrix; | import org.apache.commons.math3.linear.RealMatrix; | ||||
} | } | ||||
if (REALIZED_ADVANCE.isSet(optionsFlags)) { | if (REALIZED_ADVANCE.isSet(optionsFlags)) { | ||||
byte[] buf = glyphs.getBytes(Charsets.UTF_16LE); | |||||
byte[] buf = glyphs.getBytes(StandardCharsets.UTF_16LE); | |||||
ctx.drawString(buf, buf.length, glyphPos.get(0), null, null, null, null, true); | ctx.drawString(buf, buf.length, glyphPos.get(0), null, null, null, null, true); | ||||
} else { | } else { | ||||
final OfInt glyphIter = glyphs.codePoints().iterator(); | final OfInt glyphIter = glyphs.codePoints().iterator(); | ||||
glyphPos.forEach(p -> { | glyphPos.forEach(p -> { | ||||
byte[] buf = new String(new int[]{glyphIter.next()}, 0, 1).getBytes(Charsets.UTF_16LE); | |||||
byte[] buf = new String(new int[]{glyphIter.next()}, 0, 1).getBytes(StandardCharsets.UTF_16LE); | |||||
ctx.drawString(buf, buf.length, p, null, null, null, null, true); | ctx.drawString(buf, buf.length, p, null, null, null, null, true); | ||||
}); | }); | ||||
} | } |
import java.awt.geom.Dimension2D; | import java.awt.geom.Dimension2D; | ||||
import java.awt.geom.Rectangle2D; | import java.awt.geom.Rectangle2D; | ||||
import java.io.InputStream; | import java.io.InputStream; | ||||
import java.nio.charset.Charset; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.Iterator; | import java.util.Iterator; | ||||
import java.util.List; | import java.util.List; | ||||
import org.apache.poi.hemf.record.emf.HemfRecord; | import org.apache.poi.hemf.record.emf.HemfRecord; | ||||
import org.apache.poi.hemf.record.emf.HemfRecordIterator; | import org.apache.poi.hemf.record.emf.HemfRecordIterator; | ||||
import org.apache.poi.hemf.record.emf.HemfWindowing; | import org.apache.poi.hemf.record.emf.HemfWindowing; | ||||
import org.apache.poi.hwmf.usermodel.HwmfCharsetAware; | |||||
import org.apache.poi.hwmf.usermodel.HwmfEmbedded; | import org.apache.poi.hwmf.usermodel.HwmfEmbedded; | ||||
import org.apache.poi.util.Dimension2DDouble; | import org.apache.poi.util.Dimension2DDouble; | ||||
import org.apache.poi.util.Internal; | import org.apache.poi.util.Internal; | ||||
import org.apache.poi.util.LittleEndianInputStream; | import org.apache.poi.util.LittleEndianInputStream; | ||||
import org.apache.poi.util.LocaleUtil; | |||||
import org.apache.poi.util.Units; | import org.apache.poi.util.Units; | ||||
/** | /** | ||||
private final LittleEndianInputStream stream; | private final LittleEndianInputStream stream; | ||||
private final List<HemfRecord> records = new ArrayList<>(); | private final List<HemfRecord> records = new ArrayList<>(); | ||||
private boolean isParsed = false; | private boolean isParsed = false; | ||||
private Charset defaultCharset = LocaleUtil.CHARSET_1252; | |||||
public HemfPicture(InputStream is) { | public HemfPicture(InputStream is) { | ||||
this(new LittleEndianInputStream(is)); | this(new LittleEndianInputStream(is)); | ||||
header[0] = (HemfHeader) r; | header[0] = (HemfHeader) r; | ||||
} | } | ||||
r.setHeader(header[0]); | r.setHeader(header[0]); | ||||
if (r instanceof HwmfCharsetAware) { | |||||
((HwmfCharsetAware)r).setCharsetProvider(this::getDefaultCharset); | |||||
} | |||||
records.add(r); | records.add(r); | ||||
}); | }); | ||||
} | } | ||||
public Map<String, Supplier<?>> getGenericProperties() { | public Map<String, Supplier<?>> getGenericProperties() { | ||||
return null; | return null; | ||||
} | } | ||||
public void setDefaultCharset(Charset defaultCharset) { | |||||
this.defaultCharset = defaultCharset; | |||||
} | |||||
public Charset getDefaultCharset() { | |||||
return defaultCharset; | |||||
} | |||||
} | } |
import java.awt.geom.Rectangle2D; | 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.nio.charset.StandardCharsets; | |||||
import java.text.AttributedString; | import java.text.AttributedString; | ||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.BitSet; | import java.util.BitSet; | ||||
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 java.util.function.Supplier; | |||||
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; | ||||
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.WmfExtTextOutOptions; | import org.apache.poi.hwmf.record.HwmfText.WmfExtTextOutOptions; | ||||
import org.apache.poi.hwmf.usermodel.HwmfCharsetAware; | |||||
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.util.Internal; | import org.apache.poi.util.Internal; | ||||
import org.apache.poi.util.LocaleUtil; | import org.apache.poi.util.LocaleUtil; | ||||
public class HwmfGraphics { | |||||
public class HwmfGraphics implements HwmfCharsetAware { | |||||
public enum FillDrawStyle { | public enum FillDrawStyle { | ||||
NONE(FillDrawStyle::fillNone), | NONE(FillDrawStyle::fillNone), | ||||
private final AffineTransform initialAT = new AffineTransform(); | private final AffineTransform initialAT = new AffineTransform(); | ||||
private static final Charset DEFAULT_CHARSET = LocaleUtil.CHARSET_1252; | |||||
/** Bounding box from the placeable header */ | /** Bounding box from the placeable header */ | ||||
private final Rectangle2D bbox; | private final Rectangle2D bbox; | ||||
private Supplier<Charset> charsetProvider = () -> LocaleUtil.CHARSET_1252; | |||||
/** | /** | ||||
* Initialize a graphics context for wmf rendering | * Initialize a graphics context for wmf rendering | ||||
} | } | ||||
} | } | ||||
private static Charset getCharset(HwmfFont font, boolean isUnicode) { | |||||
private Charset getCharset(HwmfFont font, boolean isUnicode) { | |||||
if (isUnicode) { | if (isUnicode) { | ||||
return Charsets.UTF_16LE; | |||||
return StandardCharsets.UTF_16LE; | |||||
} | } | ||||
Charset charset = font.getCharset().getCharset(); | |||||
return (charset == null) ? DEFAULT_CHARSET : charset; | |||||
FontCharset fc = font.getCharset(); | |||||
if (fc == FontCharset.DEFAULT) { | |||||
return charsetProvider.get(); | |||||
} | |||||
Charset charset = fc.getCharset(); | |||||
return (charset == null) ? charsetProvider.get() : charset; | |||||
} | } | ||||
private static String trimText(HwmfFont font, boolean isUnicode, byte[] text, int length) { | |||||
@Override | |||||
public void setCharsetProvider(Supplier<Charset> provider) { | |||||
charsetProvider = provider; | |||||
} | |||||
private String trimText(HwmfFont font, boolean isUnicode, byte[] text, int length) { | |||||
final Charset charset = getCharset(font, isUnicode); | final Charset charset = getCharset(font, isUnicode); | ||||
int trimLen; | int trimLen; | ||||
graphicsCtx.fill(dstBounds); | graphicsCtx.fill(dstBounds); | ||||
break; | break; | ||||
default: | default: | ||||
case SRCAND: | |||||
case SRCCOPY: | case SRCCOPY: | ||||
case SRCINVERT: | |||||
if (img == null) { | if (img == null) { | ||||
return; | return; | ||||
} | } | ||||
// the difference is, that clippings are 0-based, whereas the srcBounds are absolute in the user-space | // the difference is, that clippings are 0-based, whereas the srcBounds are absolute in the user-space | ||||
// of the referenced image and can be also negative | // of the referenced image and can be also negative | ||||
Composite old = graphicsCtx.getComposite(); | Composite old = graphicsCtx.getComposite(); | ||||
graphicsCtx.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); | |||||
int newComp; | |||||
switch (prop.getRasterOp()) { | |||||
default: | |||||
case SRCCOPY: | |||||
newComp = AlphaComposite.SRC_OVER; | |||||
break; | |||||
case SRCINVERT: | |||||
newComp = AlphaComposite.SRC_IN; | |||||
break; | |||||
case SRCAND: | |||||
newComp = AlphaComposite.SRC; | |||||
break; | |||||
} | |||||
graphicsCtx.setComposite(AlphaComposite.getInstance(newComp)); | |||||
boolean useDeviceBounds = (img instanceof HwmfImageRenderer); | boolean useDeviceBounds = (img instanceof HwmfImageRenderer); | ||||
import java.io.ByteArrayInputStream; | import java.io.ByteArrayInputStream; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.InputStream; | import java.io.InputStream; | ||||
import java.nio.charset.Charset; | |||||
import java.util.Iterator; | import java.util.Iterator; | ||||
import org.apache.poi.common.usermodel.GenericRecord; | import org.apache.poi.common.usermodel.GenericRecord; | ||||
import org.apache.poi.hwmf.usermodel.HwmfPicture; | import org.apache.poi.hwmf.usermodel.HwmfPicture; | ||||
import org.apache.poi.sl.draw.BitmapImageRenderer; | import org.apache.poi.sl.draw.BitmapImageRenderer; | ||||
import org.apache.poi.sl.draw.DrawPictureShape; | import org.apache.poi.sl.draw.DrawPictureShape; | ||||
import org.apache.poi.sl.draw.Drawable; | |||||
import org.apache.poi.sl.draw.EmbeddedExtractor; | import org.apache.poi.sl.draw.EmbeddedExtractor; | ||||
import org.apache.poi.sl.draw.ImageRenderer; | import org.apache.poi.sl.draw.ImageRenderer; | ||||
import org.apache.poi.sl.usermodel.PictureData.PictureType; | import org.apache.poi.sl.usermodel.PictureData.PictureType; | ||||
public class HwmfImageRenderer implements ImageRenderer, EmbeddedExtractor { | public class HwmfImageRenderer implements ImageRenderer, EmbeddedExtractor { | ||||
HwmfPicture image; | HwmfPicture image; | ||||
double alpha; | double alpha; | ||||
boolean charsetInitialized = false; | |||||
@Override | @Override | ||||
public boolean canRender(String contentType) { | public boolean canRender(String contentType) { | ||||
@Override | @Override | ||||
public BufferedImage getImage(Dimension2D dim) { | public BufferedImage getImage(Dimension2D dim) { | ||||
if (image == null) { | if (image == null) { | ||||
return new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); | |||||
return new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); | |||||
} | } | ||||
BufferedImage bufImg = new BufferedImage((int)dim.getWidth(), (int)dim.getHeight(), BufferedImage.TYPE_INT_ARGB); | BufferedImage bufImg = new BufferedImage((int)dim.getWidth(), (int)dim.getHeight(), BufferedImage.TYPE_INT_ARGB); | ||||
Graphics2D g = bufImg.createGraphics(); | Graphics2D g = bufImg.createGraphics(); | ||||
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); | g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); | ||||
return BitmapImageRenderer.setAlpha(bufImg, alpha); | return BitmapImageRenderer.setAlpha(bufImg, alpha); | ||||
} | } | ||||
@Override | @Override | ||||
public boolean drawImage(Graphics2D graphics, Rectangle2D anchor) { | public boolean drawImage(Graphics2D graphics, Rectangle2D anchor) { | ||||
return drawImage(graphics, anchor, null); | return drawImage(graphics, anchor, null); | ||||
return false; | return false; | ||||
} | } | ||||
Charset cs = (Charset)graphics.getRenderingHint(Drawable.DEFAULT_CHARSET); | |||||
if (cs != null && !charsetInitialized) { | |||||
setDefaultCharset(cs); | |||||
} | |||||
HwmfGraphicsState graphicsState = new HwmfGraphicsState(); | HwmfGraphicsState graphicsState = new HwmfGraphicsState(); | ||||
graphicsState.backup(graphics); | graphicsState.backup(graphics); | ||||
public Rectangle2D getBounds() { | public Rectangle2D getBounds() { | ||||
return Units.pointsToPixel(image == null ? new Rectangle2D.Double() : image.getBoundsInPoints()); | return Units.pointsToPixel(image == null ? new Rectangle2D.Double() : image.getBoundsInPoints()); | ||||
} | } | ||||
@Override | |||||
public void setDefaultCharset(Charset defaultCharset) { | |||||
image.setDefaultCharset(defaultCharset); | |||||
charsetInitialized = true; | |||||
} | |||||
} | } |
if (foreground != null && background != null && headerBitCount == HwmfBitmapDib.BitCount.BI_BITCOUNT_1) { | if (foreground != null && background != null && headerBitCount == HwmfBitmapDib.BitCount.BI_BITCOUNT_1) { | ||||
IndexColorModel cmOld = (IndexColorModel)bi.getColorModel(); | IndexColorModel cmOld = (IndexColorModel)bi.getColorModel(); | ||||
int transPixel = hasAlpha ? (((cmOld.getRGB(0) & 0xFFFFFF) == 0) ? 0 : 1) : -1; | |||||
int fg = foreground.getRGB(); | |||||
int bg = background.getRGB() & (hasAlpha ? 0xFFFFFF : 0xFFFFFFFF); | |||||
boolean ordered = (cmOld.getRGB(0) & 0xFFFFFF) == (bg & 0xFFFFFF); | |||||
int transPixel = ordered ? 0 : 1; | |||||
int[] cmap = ordered ? new int[]{ bg, fg } : new int[]{ fg, bg }; | |||||
int transferType = bi.getData().getTransferType(); | int transferType = bi.getData().getTransferType(); | ||||
int fg = foreground.getRGB(), bg = background.getRGB(); | |||||
int[] cmap = { (transPixel == 0 ? bg : fg), (transPixel == 1 ? bg : fg) }; | |||||
IndexColorModel cmNew = new IndexColorModel(1, cmap.length, cmap, 0, hasAlpha, transPixel, transferType); | |||||
IndexColorModel cmNew = new IndexColorModel(1, 2, cmap, 0, hasAlpha, transPixel, transferType); | |||||
bi = new BufferedImage(cmNew, bi.getRaster(), false, null); | bi = new BufferedImage(cmNew, bi.getRaster(), false, null); | ||||
} | } | ||||
import org.apache.poi.hwmf.draw.HwmfDrawProperties; | import org.apache.poi.hwmf.draw.HwmfDrawProperties; | ||||
import org.apache.poi.hwmf.draw.HwmfGraphics; | import org.apache.poi.hwmf.draw.HwmfGraphics; | ||||
import org.apache.poi.hwmf.record.HwmfMisc.WmfSetMapMode; | import org.apache.poi.hwmf.record.HwmfMisc.WmfSetMapMode; | ||||
import org.apache.poi.hwmf.usermodel.HwmfCharsetAware; | |||||
import org.apache.poi.util.BitField; | import org.apache.poi.util.BitField; | ||||
import org.apache.poi.util.BitFieldFactory; | import org.apache.poi.util.BitFieldFactory; | ||||
import org.apache.poi.util.GenericRecordJsonWriter; | import org.apache.poi.util.GenericRecordJsonWriter; | ||||
import org.apache.poi.util.IOUtils; | import org.apache.poi.util.IOUtils; | ||||
import org.apache.poi.util.LittleEndianConsts; | import org.apache.poi.util.LittleEndianConsts; | ||||
import org.apache.poi.util.LittleEndianInputStream; | import org.apache.poi.util.LittleEndianInputStream; | ||||
import org.apache.poi.util.LocaleUtil; | |||||
import org.apache.poi.util.POILogFactory; | import org.apache.poi.util.POILogFactory; | ||||
import org.apache.poi.util.POILogger; | import org.apache.poi.util.POILogger; | ||||
* The META_TEXTOUT record outputs a character string at the specified location by using the font, | * The META_TEXTOUT record outputs a character string at the specified location by using the font, | ||||
* background color, and text color that are defined in the playback device context. | * background color, and text color that are defined in the playback device context. | ||||
*/ | */ | ||||
public static class WmfTextOut implements HwmfRecord { | |||||
public static class WmfTextOut implements HwmfRecord, HwmfCharsetAware { | |||||
/** | /** | ||||
* A 16-bit signed integer that defines the length of the string, in bytes, pointed to by String. | * A 16-bit signed integer that defines the length of the string, in bytes, pointed to by String. | ||||
*/ | */ | ||||
protected Point2D reference = new Point2D.Double(); | protected Point2D reference = new Point2D.Double(); | ||||
protected Supplier<Charset> charsetProvider = () -> LocaleUtil.CHARSET_1252; | |||||
@Override | @Override | ||||
public HwmfRecordType getWmfRecordType() { | public HwmfRecordType getWmfRecordType() { | ||||
return HwmfRecordType.textOut; | return HwmfRecordType.textOut; | ||||
@Override | @Override | ||||
public void draw(HwmfGraphics ctx) { | public void draw(HwmfGraphics ctx) { | ||||
ctx.setCharsetProvider(charsetProvider); | |||||
ctx.drawString(getTextBytes(), stringLength, reference); | ctx.drawString(getTextBytes(), stringLength, reference); | ||||
} | } | ||||
"reference", () -> reference | "reference", () -> reference | ||||
); | ); | ||||
} | } | ||||
@Override | |||||
public void setCharsetProvider(Supplier<Charset> provider) { | |||||
charsetProvider = provider; | |||||
} | |||||
} | } | ||||
@SuppressWarnings("unused") | @SuppressWarnings("unused") | ||||
* are defined in the playback device context. Optionally, dimensions can be provided for clipping, | * are defined in the playback device context. Optionally, dimensions can be provided for clipping, | ||||
* opaquing, or both. | * opaquing, or both. | ||||
*/ | */ | ||||
public static class WmfExtTextOut implements HwmfRecord { | |||||
public static class WmfExtTextOut implements HwmfRecord, HwmfCharsetAware { | |||||
/** | /** | ||||
* The location, in logical units, where the text string is to be placed. | * The location, in logical units, where the text string is to be placed. | ||||
*/ | */ | ||||
*/ | */ | ||||
protected final List<Integer> dx = new ArrayList<>(); | protected final List<Integer> dx = new ArrayList<>(); | ||||
protected Supplier<Charset> charsetProvider = () -> LocaleUtil.CHARSET_1252; | |||||
public WmfExtTextOut() { | public WmfExtTextOut() { | ||||
this(new WmfExtTextOutOptions()); | this(new WmfExtTextOutOptions()); | ||||
} | } | ||||
@Override | @Override | ||||
public void draw(HwmfGraphics ctx) { | public void draw(HwmfGraphics ctx) { | ||||
ctx.setCharsetProvider(charsetProvider); | |||||
ctx.drawString(rawTextBytes, stringLength, reference, null, bounds, options, dx, false); | ctx.drawString(rawTextBytes, stringLength, reference, null, bounds, options, dx, false); | ||||
} | } | ||||
return ""; | return ""; | ||||
} | } | ||||
String ret = new String(rawTextBytes, charset); | String ret = new String(rawTextBytes, charset); | ||||
return ret.substring(0, | |||||
Math.min(ret.length(), stringLength)); | |||||
return ret.substring(0, Math.min(ret.length(), stringLength)); | |||||
} | } | ||||
public Point2D getReference() { | public Point2D getReference() { | ||||
private String getGenericText() { | private String getGenericText() { | ||||
try { | try { | ||||
return getText(isUnicode() ? StandardCharsets.UTF_16LE : StandardCharsets.US_ASCII); | |||||
return getText(isUnicode() ? StandardCharsets.UTF_16LE : charsetProvider.get()); | |||||
} catch (IOException e) { | } catch (IOException e) { | ||||
return ""; | return ""; | ||||
} | } | ||||
"dx", () -> dx | "dx", () -> dx | ||||
); | ); | ||||
} | } | ||||
@Override | |||||
public void setCharsetProvider(Supplier<Charset> provider) { | |||||
charsetProvider = provider; | |||||
} | |||||
} | } | ||||
public enum HwmfTextAlignment { | public enum HwmfTextAlignment { |
/* | |||||
* ==================================================================== | |||||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||||
* contributor license agreements. See the NOTICE file distributed with | |||||
* this work for additional information regarding copyright ownership. | |||||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||||
* (the "License"); you may not use this file except in compliance with | |||||
* the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
* ==================================================================== | |||||
*/ | |||||
package org.apache.poi.hwmf.usermodel; | |||||
import java.nio.charset.Charset; | |||||
import java.util.function.Supplier; | |||||
/** | |||||
* Helper interface to provide a default charset to records which would depend on a system charset | |||||
*/ | |||||
public interface HwmfCharsetAware { | |||||
void setCharsetProvider(Supplier<Charset> provider); | |||||
} |
import java.awt.geom.Rectangle2D; | import java.awt.geom.Rectangle2D; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.InputStream; | import java.io.InputStream; | ||||
import java.nio.charset.Charset; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.Collections; | import java.util.Collections; | ||||
import java.util.Iterator; | import java.util.Iterator; | ||||
import org.apache.poi.util.Dimension2DDouble; | import org.apache.poi.util.Dimension2DDouble; | ||||
import org.apache.poi.util.IOUtils; | import org.apache.poi.util.IOUtils; | ||||
import org.apache.poi.util.LittleEndianInputStream; | import org.apache.poi.util.LittleEndianInputStream; | ||||
import org.apache.poi.util.LocaleUtil; | |||||
import org.apache.poi.util.POILogFactory; | import org.apache.poi.util.POILogFactory; | ||||
import org.apache.poi.util.POILogger; | import org.apache.poi.util.POILogger; | ||||
import org.apache.poi.util.RecordFormatException; | import org.apache.poi.util.RecordFormatException; | ||||
public static final int MAX_RECORD_LENGTH = 50_000_000; | public static final int MAX_RECORD_LENGTH = 50_000_000; | ||||
private static final POILogger logger = POILogFactory.getLogger(HwmfPicture.class); | private static final POILogger logger = POILogFactory.getLogger(HwmfPicture.class); | ||||
final List<HwmfRecord> records = new ArrayList<>(); | final List<HwmfRecord> records = new ArrayList<>(); | ||||
final HwmfPlaceableHeader placeableHeader; | final HwmfPlaceableHeader placeableHeader; | ||||
final HwmfHeader header; | final HwmfHeader header; | ||||
/** The default charset */ | |||||
private Charset defaultCharset = LocaleUtil.CHARSET_1252; | |||||
public HwmfPicture(InputStream inputStream) throws IOException { | public HwmfPicture(InputStream inputStream) throws IOException { | ||||
try (LittleEndianInputStream leis = new LittleEndianInputStream(inputStream)) { | try (LittleEndianInputStream leis = new LittleEndianInputStream(inputStream)) { | ||||
throw new RecordFormatException("Tried to skip "+remainingSize + " but skipped: "+skipped); | throw new RecordFormatException("Tried to skip "+remainingSize + " but skipped: "+skipped); | ||||
} | } | ||||
} | } | ||||
if (wr instanceof HwmfCharsetAware) { | |||||
((HwmfCharsetAware)wr).setCharsetProvider(this::getDefaultCharset); | |||||
} | |||||
} | } | ||||
} | } | ||||
} | } | ||||
Rectangle2D bounds = new Rectangle2D.Double(0,0,width,height); | Rectangle2D bounds = new Rectangle2D.Double(0,0,width,height); | ||||
draw(ctx, bounds); | draw(ctx, bounds); | ||||
} | } | ||||
public void draw(Graphics2D ctx, Rectangle2D graphicsBounds) { | public void draw(Graphics2D ctx, Rectangle2D graphicsBounds) { | ||||
HwmfGraphicsState state = new HwmfGraphicsState(); | HwmfGraphicsState state = new HwmfGraphicsState(); | ||||
state.backup(ctx); | state.backup(ctx); | ||||
} | } | ||||
if (wOrg != null && wExt != null) { | if (wOrg != null && wExt != null) { | ||||
return new Rectangle2D.Double(wOrg.getX(), wOrg.getY(), wExt.getSize().getWidth(), wExt.getSize().getHeight()); | return new Rectangle2D.Double(wOrg.getX(), wOrg.getY(), wExt.getSize().getWidth(), wExt.getSize().getHeight()); | ||||
} | |||||
} | |||||
} | } | ||||
return null; | return null; | ||||
} | } | ||||
public List<? extends GenericRecord> getGenericChildren() { | public List<? extends GenericRecord> getGenericChildren() { | ||||
return getRecords(); | return getRecords(); | ||||
} | } | ||||
public void setDefaultCharset(Charset defaultCharset) { | |||||
this.defaultCharset = defaultCharset; | |||||
} | |||||
public Charset getDefaultCharset() { | |||||
return defaultCharset; | |||||
} | |||||
} | } |
package org.apache.poi.poifs.filesystem; | package org.apache.poi.poifs.filesystem; | ||||
import org.apache.commons.codec.Charsets; | |||||
import org.apache.poi.POIDataSamples; | |||||
import org.apache.poi.util.TempFile; | |||||
import org.junit.Test; | |||||
import static org.junit.Assert.assertEquals; | |||||
import static org.junit.Assert.assertFalse; | |||||
import static org.junit.Assert.assertNotNull; | |||||
import static org.junit.Assert.assertNotSame; | |||||
import static org.junit.Assert.assertSame; | |||||
import static org.junit.Assert.assertTrue; | |||||
import static org.junit.Assert.fail; | |||||
import java.io.BufferedInputStream; | import java.io.BufferedInputStream; | ||||
import java.io.File; | import java.io.File; | ||||
import java.io.FileOutputStream; | import java.io.FileOutputStream; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.InputStream; | import java.io.InputStream; | ||||
import java.nio.charset.StandardCharsets; | |||||
import java.util.Arrays; | import java.util.Arrays; | ||||
import java.util.Random; | import java.util.Random; | ||||
import static org.junit.Assert.*; | |||||
import org.apache.poi.POIDataSamples; | |||||
import org.apache.poi.util.TempFile; | |||||
import org.junit.Test; | |||||
public class TestFileMagic { | public class TestFileMagic { | ||||
@Test | @Test | ||||
public void testFileMagic() { | public void testFileMagic() { | ||||
assertEquals(FileMagic.XML, FileMagic.valueOf("XML")); | assertEquals(FileMagic.XML, FileMagic.valueOf("XML")); | ||||
assertEquals(FileMagic.XML, FileMagic.valueOf("<?xml".getBytes(Charsets.UTF_8))); | |||||
assertEquals(FileMagic.XML, FileMagic.valueOf("<?xml".getBytes(StandardCharsets.UTF_8))); | |||||
assertEquals(FileMagic.HTML, FileMagic.valueOf("HTML")); | assertEquals(FileMagic.HTML, FileMagic.valueOf("HTML")); | ||||
assertEquals(FileMagic.HTML, FileMagic.valueOf("<!DOCTYP".getBytes(Charsets.UTF_8))); | |||||
assertEquals(FileMagic.HTML, FileMagic.valueOf("<!DOCTYPE".getBytes(Charsets.UTF_8))); | |||||
assertEquals(FileMagic.HTML, FileMagic.valueOf("<html".getBytes(Charsets.UTF_8))); | |||||
assertEquals(FileMagic.HTML, FileMagic.valueOf("\n\r<html".getBytes(Charsets.UTF_8))); | |||||
assertEquals(FileMagic.HTML, FileMagic.valueOf("\n<html".getBytes(Charsets.UTF_8))); | |||||
assertEquals(FileMagic.HTML, FileMagic.valueOf("\r\n<html".getBytes(Charsets.UTF_8))); | |||||
assertEquals(FileMagic.HTML, FileMagic.valueOf("\r<html".getBytes(Charsets.UTF_8))); | |||||
assertEquals(FileMagic.HTML, FileMagic.valueOf("<!DOCTYP".getBytes(StandardCharsets.UTF_8))); | |||||
assertEquals(FileMagic.HTML, FileMagic.valueOf("<!DOCTYPE".getBytes(StandardCharsets.UTF_8))); | |||||
assertEquals(FileMagic.HTML, FileMagic.valueOf("<html".getBytes(StandardCharsets.UTF_8))); | |||||
assertEquals(FileMagic.HTML, FileMagic.valueOf("\n\r<html".getBytes(StandardCharsets.UTF_8))); | |||||
assertEquals(FileMagic.HTML, FileMagic.valueOf("\n<html".getBytes(StandardCharsets.UTF_8))); | |||||
assertEquals(FileMagic.HTML, FileMagic.valueOf("\r\n<html".getBytes(StandardCharsets.UTF_8))); | |||||
assertEquals(FileMagic.HTML, FileMagic.valueOf("\r<html".getBytes(StandardCharsets.UTF_8))); | |||||
assertEquals(FileMagic.JPEG, FileMagic.valueOf(new byte[]{ (byte)0xFF, (byte)0xD8, (byte)0xFF, (byte)0xDB })); | assertEquals(FileMagic.JPEG, FileMagic.valueOf(new byte[]{ (byte)0xFF, (byte)0xD8, (byte)0xFF, (byte)0xDB })); | ||||
assertEquals(FileMagic.JPEG, FileMagic.valueOf(new byte[]{ (byte)0xFF, (byte)0xD8, (byte)0xFF, (byte)0xE0, 'a', 'b', 'J', 'F', 'I', 'F', 0x00, 0x01 })); | assertEquals(FileMagic.JPEG, FileMagic.valueOf(new byte[]{ (byte)0xFF, (byte)0xD8, (byte)0xFF, (byte)0xE0, 'a', 'b', 'J', 'F', 'I', 'F', 0x00, 0x01 })); | ||||
assertEquals(FileMagic.JPEG, FileMagic.valueOf(new byte[]{ (byte)0xFF, (byte)0xD8, (byte)0xFF, (byte)0xEE })); | assertEquals(FileMagic.JPEG, FileMagic.valueOf(new byte[]{ (byte)0xFF, (byte)0xD8, (byte)0xFF, (byte)0xEE })); | ||||
assertEquals(FileMagic.JPEG, FileMagic.valueOf(new byte[]{ (byte)0xFF, (byte)0xD8, (byte)0xFF, (byte)0xE1, 'd', 'c', 'E', 'x', 'i', 'f', 0x00, 0x00 })); | assertEquals(FileMagic.JPEG, FileMagic.valueOf(new byte[]{ (byte)0xFF, (byte)0xD8, (byte)0xFF, (byte)0xE1, 'd', 'c', 'E', 'x', 'i', 'f', 0x00, 0x00 })); | ||||
assertEquals(FileMagic.UNKNOWN, FileMagic.valueOf("something".getBytes(Charsets.UTF_8))); | |||||
assertEquals(FileMagic.UNKNOWN, FileMagic.valueOf("something".getBytes(StandardCharsets.UTF_8))); | |||||
assertEquals(FileMagic.UNKNOWN, FileMagic.valueOf(new byte[0])); | assertEquals(FileMagic.UNKNOWN, FileMagic.valueOf(new byte[0])); | ||||
try { | try { |