mirror of https://github.com/apache/poi.git
#64867 - Provide PDF rendering with PPTX2PNG
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1883074 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
dae47d5ced
commit
b20318b58c
18
build.xml
18
build.xml
|
@ -294,10 +294,13 @@ under the License.
|
|||
<!-- only used for signing the release - not used with the ooxml signatures -->
|
||||
<dependency prefix="dsig.bouncycastle-bcpg" artifact="org.bouncycastle:bcpg-jdk15on:1.66" usage="util"/>
|
||||
|
||||
<!-- svg/batik libs - not part of the distribution -->
|
||||
<!-- svg/batik/pdf libs - not part of the distribution -->
|
||||
<dependency prefix="svg.xml-apis-ext" artifact="xml-apis:xml-apis-ext:1.3.04" usage="ooxml-provided"/>
|
||||
<dependency prefix="svg.xmlgraphics-commons" artifact="org.apache.xmlgraphics:xmlgraphics-commons:2.4" usage="ooxml-provided"/>
|
||||
<dependency prefix="svg.batik-all" artifact="org.apache.xmlgraphics:batik-all:1.13" usage="ooxml-provided"/>
|
||||
<dependency prefix="pdf.pdfbox" artifact="org.apache.pdfbox:pdfbox:2.0.19" usage="ooxml-provided"/>
|
||||
<dependency prefix="pdf.fontbox" artifact="org.apache.pdfbox:fontbox:2.0.19" usage="ooxml-provided"/>
|
||||
<dependency prefix="pdf.graphics2d" artifact="de.rototor.pdfbox:graphics2d:0.27" usage="ooxml-provided"/>
|
||||
|
||||
<!-- jars in the ooxml-lib directory, see the fetch-ooxml-jars target-->
|
||||
<dependency prefix="ooxml.curvesapi" artifact="com.github.virtuald:curvesapi:1.06" usage="ooxml"/>
|
||||
|
@ -450,10 +453,17 @@ under the License.
|
|||
<pathelement location="${svg.xmlgraphics-commons.jar}"/>
|
||||
</path>
|
||||
|
||||
<path id="pdfbox.classpath">
|
||||
<pathelement location="${pdf.pdfbox.jar}"/>
|
||||
<pathelement location="${pdf.fontbox.jar}"/>
|
||||
<pathelement location="${pdf.graphics2d.jar}"/>
|
||||
</path>
|
||||
|
||||
<path id="ooxml.classpath">
|
||||
<pathelement location="${ooxml.xsds.jar}"/>
|
||||
<path refid="ooxml.base.classpath"/>
|
||||
<path refid="batik.classpath"/>
|
||||
<path refid="pdfbox.classpath"/>
|
||||
</path>
|
||||
|
||||
<path id="ooxml.lite.verify.classpath">
|
||||
|
@ -712,6 +722,9 @@ under the License.
|
|||
<available file="${svg.xml-apis-ext.jar}"/>
|
||||
<available file="${svg.batik-all.jar}"/>
|
||||
<available file="${svg.xmlgraphics-commons.jar}"/>
|
||||
<available file="${pdf.pdfbox.jar}"/>
|
||||
<available file="${pdf.fontbox.jar}"/>
|
||||
<available file="${pdf.graphics2d.jar}"/>
|
||||
</and>
|
||||
<isset property="disconnected"/>
|
||||
</or>
|
||||
|
@ -730,6 +743,9 @@ under the License.
|
|||
<downloadfile src="${svg.batik-all.url}" dest="${svg.batik-all.jar}"/>
|
||||
<downloadfile src="${svg.xml-apis-ext.url}" dest="${svg.xml-apis-ext.jar}"/>
|
||||
<downloadfile src="${svg.xmlgraphics-commons.url}" dest="${svg.xmlgraphics-commons.jar}"/>
|
||||
<downloadfile src="${pdf.pdfbox.url}" dest="${pdf.pdfbox.jar}"/>
|
||||
<downloadfile src="${pdf.fontbox.url}" dest="${pdf.fontbox.jar}"/>
|
||||
<downloadfile src="${pdf.graphics2d.url}" dest="${pdf.graphics2d.jar}"/>
|
||||
</target>
|
||||
|
||||
<target name="check-svn-jars">
|
||||
|
|
|
@ -17,7 +17,9 @@
|
|||
|
||||
package org.apache.poi.sl.draw;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.font.TextAttribute;
|
||||
import java.awt.font.TextLayout;
|
||||
import java.text.AttributedCharacterIterator;
|
||||
import java.text.AttributedString;
|
||||
|
@ -50,7 +52,22 @@ public class DrawTextFragment implements Drawable {
|
|||
if(textMode != null && textMode == Drawable.TEXT_AS_SHAPES){
|
||||
layout.draw(graphics, (float)x, (float)yBaseline);
|
||||
} else {
|
||||
graphics.drawString(str.getIterator(), (float)x, (float)yBaseline );
|
||||
try {
|
||||
graphics.drawString(str.getIterator(), (float) x, (float) yBaseline);
|
||||
} catch (ClassCastException e) {
|
||||
// workaround: batik issue, which expects only Color as forground color
|
||||
replaceForgroundPaintWithBlack(str);
|
||||
graphics.drawString(str.getIterator(), (float) x, (float) yBaseline);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void replaceForgroundPaintWithBlack(AttributedString as) {
|
||||
AttributedCharacterIterator iter = as.getIterator(new TextAttribute[]{TextAttribute.FOREGROUND});
|
||||
for (char ch = iter.first();
|
||||
ch != CharacterIterator.DONE;
|
||||
ch = iter.next()) {
|
||||
as.addAttribute(TextAttribute.FOREGROUND, Color.BLACK, iter.getBeginIndex(), iter.getEndIndex());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* ====================================================================
|
||||
* 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.xslf.util;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import org.apache.poi.sl.draw.Drawable;
|
||||
import org.apache.poi.util.Internal;
|
||||
|
||||
@Internal
|
||||
public class BitmapFormat implements OutputFormat {
|
||||
private final String format;
|
||||
private BufferedImage img;
|
||||
private Graphics2D graphics;
|
||||
|
||||
public BitmapFormat(String format) {
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Graphics2D addSlide(double width, double height) {
|
||||
img = new BufferedImage((int)width, (int)height, BufferedImage.TYPE_INT_ARGB);
|
||||
graphics = img.createGraphics();
|
||||
graphics.setRenderingHint(Drawable.BUFFERED_IMAGE, new WeakReference<>(img));
|
||||
return graphics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSlide(MFProxy proxy, File outFile) throws IOException {
|
||||
if (!"null".equals(format)) {
|
||||
ImageIO.write(img, format, outFile);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (graphics != null) {
|
||||
graphics.dispose();
|
||||
img.flush();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,23 +19,12 @@
|
|||
|
||||
package org.apache.poi.xslf.util;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import org.apache.batik.dom.GenericDOMImplementation;
|
||||
import org.apache.batik.svggen.SVGGraphics2D;
|
||||
import org.apache.poi.sl.draw.Drawable;
|
||||
import org.apache.poi.util.Internal;
|
||||
import org.apache.poi.xslf.draw.SVGPOIGraphics2D;
|
||||
import org.w3c.dom.DOMImplementation;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
/**
|
||||
* Output formats for PPTX2PNG
|
||||
|
@ -43,72 +32,12 @@ import org.w3c.dom.Document;
|
|||
@Internal
|
||||
interface OutputFormat extends Closeable {
|
||||
|
||||
Graphics2D getGraphics2D(double width, double height);
|
||||
Graphics2D addSlide(double width, double height) throws IOException;
|
||||
|
||||
void writeOut(MFProxy proxy, File outFile) throws IOException;
|
||||
void writeSlide(MFProxy proxy, File outFile) throws IOException;
|
||||
|
||||
class SVGFormat implements OutputFormat {
|
||||
static final String svgNS = "http://www.w3.org/2000/svg";
|
||||
private SVGGraphics2D svgGenerator;
|
||||
private final boolean textAsShapes;
|
||||
default void writeDocument(MFProxy proxy, File outFile) throws IOException {};
|
||||
|
||||
SVGFormat(boolean textAsShapes) {
|
||||
this.textAsShapes = textAsShapes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Graphics2D getGraphics2D(double width, double height) {
|
||||
// Get a DOMImplementation.
|
||||
DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation();
|
||||
|
||||
// Create an instance of org.w3c.dom.Document.
|
||||
Document document = domImpl.createDocument(svgNS, "svg", null);
|
||||
svgGenerator = new SVGPOIGraphics2D(document, textAsShapes);
|
||||
svgGenerator.setSVGCanvasSize(new Dimension((int)width, (int)height));
|
||||
return svgGenerator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeOut(MFProxy proxy, File outFile) throws IOException {
|
||||
svgGenerator.stream(outFile.getCanonicalPath(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
svgGenerator.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
class BitmapFormat implements OutputFormat {
|
||||
private final String format;
|
||||
private BufferedImage img;
|
||||
private Graphics2D graphics;
|
||||
|
||||
BitmapFormat(String format) {
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Graphics2D getGraphics2D(double width, double height) {
|
||||
img = new BufferedImage((int)width, (int)height, BufferedImage.TYPE_INT_ARGB);
|
||||
graphics = img.createGraphics();
|
||||
graphics.setRenderingHint(Drawable.BUFFERED_IMAGE, new WeakReference<>(img));
|
||||
return graphics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeOut(MFProxy proxy, File outFile) throws IOException {
|
||||
if (!"null".equals(format)) {
|
||||
ImageIO.write(img, format, outFile);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (graphics != null) {
|
||||
graphics.dispose();
|
||||
img.flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* ====================================================================
|
||||
* 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.xslf.util;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import de.rototor.pdfbox.graphics2d.PdfBoxGraphics2D;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
|
||||
import org.apache.poi.util.Internal;
|
||||
|
||||
@Internal
|
||||
public class PDFFormat implements OutputFormat {
|
||||
private final PDDocument document;
|
||||
private PDPageContentStream contentStream;
|
||||
private PdfBoxGraphics2D pdfBoxGraphics2D;
|
||||
|
||||
public PDFFormat() {
|
||||
document = new PDDocument();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Graphics2D addSlide(double width, double height) throws IOException {
|
||||
PDPage page = new PDPage(new PDRectangle((float) width, (float) height));
|
||||
document.addPage(page);
|
||||
contentStream = new PDPageContentStream(document, page);
|
||||
pdfBoxGraphics2D = new PdfBoxGraphics2D(document, (float)width, (float)height);
|
||||
return pdfBoxGraphics2D;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSlide(MFProxy proxy, File outFile) throws IOException {
|
||||
pdfBoxGraphics2D.dispose();
|
||||
|
||||
PDFormXObject appearanceStream = pdfBoxGraphics2D.getXFormObject();
|
||||
contentStream.drawForm(appearanceStream);
|
||||
contentStream.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDocument(MFProxy proxy, File outFile) throws IOException {
|
||||
document.save(new File(outFile.getCanonicalPath()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
document.close();
|
||||
}
|
||||
}
|
|
@ -39,8 +39,6 @@ import org.apache.poi.sl.draw.EmbeddedExtractor.EmbeddedPart;
|
|||
import org.apache.poi.util.Dimension2DDouble;
|
||||
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.SVGFormat;
|
||||
|
||||
/**
|
||||
* An utility to convert slides of a .pptx slide show to a PNG image
|
||||
|
@ -62,7 +60,7 @@ public final class PPTX2PNG {
|
|||
" -scale <float> scale factor\n" +
|
||||
" -fixSide <side> specify side (long,short,width,height) to fix - use <scale> as amount of pixels\n" +
|
||||
" -slide <integer> 1-based index of a slide to render\n" +
|
||||
" -format <type> png,gif,jpg,svg (,null for testing)\n" +
|
||||
" -format <type> png,gif,jpg,svg,pdf (,null for testing)\n" +
|
||||
" -outdir <dir> output directory, defaults to origin of the ppt/pptx file\n" +
|
||||
" -outfile <file> output filename, defaults to '"+OUTPUT_PAT_REGEX+"'\n" +
|
||||
" -outpat <pattern> output filename pattern, defaults to '"+OUTPUT_PAT_REGEX+"'\n" +
|
||||
|
@ -207,7 +205,7 @@ public final class PPTX2PNG {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (format == null || !format.matches("^(png|gif|jpg|null|svg)$")) {
|
||||
if (format == null || !format.matches("^(png|gif|jpg|null|svg|pdf)$")) {
|
||||
usage("Invalid format given");
|
||||
return false;
|
||||
}
|
||||
|
@ -262,19 +260,19 @@ public final class PPTX2PNG {
|
|||
final int width = Math.max((int)Math.rint(dim.getWidth()),1);
|
||||
final int height = Math.max((int)Math.rint(dim.getHeight()),1);
|
||||
|
||||
for (int slideNo : slidenum) {
|
||||
proxy.setSlideNo(slideNo);
|
||||
if (!quiet) {
|
||||
String title = proxy.getTitle();
|
||||
System.out.println("Rendering slide " + slideNo + (title == null ? "" : ": " + title.trim()));
|
||||
}
|
||||
try (OutputFormat outputFormat = getOutput()) {
|
||||
for (int slideNo : slidenum) {
|
||||
proxy.setSlideNo(slideNo);
|
||||
if (!quiet) {
|
||||
String title = proxy.getTitle();
|
||||
System.out.println("Rendering slide " + slideNo + (title == null ? "" : ": " + title.trim()));
|
||||
}
|
||||
|
||||
dumpRecords(proxy);
|
||||
dumpRecords(proxy);
|
||||
|
||||
extractEmbedded(proxy, slideNo);
|
||||
extractEmbedded(proxy, slideNo);
|
||||
|
||||
try (OutputFormat outputFormat = ("svg".equals(format)) ? new SVGFormat(textAsShapes) : new BitmapFormat(format)) {
|
||||
Graphics2D graphics = outputFormat.getGraphics2D(width, height);
|
||||
Graphics2D graphics = outputFormat.addSlide(width, height);
|
||||
|
||||
// default rendering options
|
||||
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
@ -294,9 +292,12 @@ public final class PPTX2PNG {
|
|||
// draw stuff
|
||||
proxy.draw(graphics);
|
||||
|
||||
outputFormat.writeOut(proxy, new File(outdir, calcOutFile(proxy, slideNo)));
|
||||
outputFormat.writeSlide(proxy, new File(outdir, calcOutFile(proxy, slideNo)));
|
||||
}
|
||||
|
||||
outputFormat.writeDocument(proxy, new File(outdir, calcOutFile(proxy, 0)));
|
||||
}
|
||||
|
||||
} catch (NoScratchpadException e) {
|
||||
usage("'"+file.getName()+"': Format not supported - try to include poi-scratchpad.jar into the CLASSPATH.");
|
||||
return;
|
||||
|
@ -307,6 +308,17 @@ public final class PPTX2PNG {
|
|||
}
|
||||
}
|
||||
|
||||
private OutputFormat getOutput() {
|
||||
switch (format) {
|
||||
case "svg":
|
||||
return new SVGFormat(textAsShapes);
|
||||
case "pdf":
|
||||
return new PDFFormat();
|
||||
default:
|
||||
return new BitmapFormat(format);
|
||||
}
|
||||
}
|
||||
|
||||
private double getDimensions(MFProxy proxy, Dimension2D dim) {
|
||||
final Dimension2D pgsize = proxy.getSize();
|
||||
|
||||
|
@ -413,7 +425,7 @@ public final class PPTX2PNG {
|
|||
return outfile;
|
||||
}
|
||||
String inname = String.format(Locale.ROOT, "%04d|%s|%s", slideNo, format, file.getName());
|
||||
String outpat = (proxy.getSlideCount() > 1 ? outPattern : outPattern.replaceAll("-?\\$\\{slideno}", ""));
|
||||
String outpat = (proxy.getSlideCount() > 1 && slideNo > 0 ? outPattern : outPattern.replaceAll("-?\\$\\{slideno}", ""));
|
||||
return INPUT_PATTERN.matcher(inname).replaceAll(outpat);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* ====================================================================
|
||||
* 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.xslf.util;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.batik.dom.GenericDOMImplementation;
|
||||
import org.apache.batik.svggen.SVGGraphics2D;
|
||||
import org.apache.poi.util.Internal;
|
||||
import org.apache.poi.xslf.draw.SVGPOIGraphics2D;
|
||||
import org.w3c.dom.DOMImplementation;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
@Internal
|
||||
public class SVGFormat implements OutputFormat {
|
||||
static final String svgNS = "http://www.w3.org/2000/svg";
|
||||
private SVGGraphics2D svgGenerator;
|
||||
private final boolean textAsShapes;
|
||||
|
||||
public SVGFormat(boolean textAsShapes) {
|
||||
this.textAsShapes = textAsShapes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Graphics2D addSlide(double width, double height) {
|
||||
// Get a DOMImplementation.
|
||||
DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation();
|
||||
|
||||
// Create an instance of org.w3c.dom.Document.
|
||||
Document document = domImpl.createDocument(svgNS, "svg", null);
|
||||
svgGenerator = new SVGPOIGraphics2D(document, textAsShapes);
|
||||
svgGenerator.setSVGCanvasSize(new Dimension((int)width, (int)height));
|
||||
return svgGenerator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSlide(MFProxy proxy, File outFile) throws IOException {
|
||||
svgGenerator.stream(outFile.getCanonicalPath(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
svgGenerator.dispose();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue