org.mapfish.print.PDFUtils Maven / Gradle / Ivy
/*
* Copyright (C) 2013 Camptocamp
*
* This file is part of MapFish Print
*
* MapFish Print is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MapFish Print is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with MapFish Print. If not, see .
*/
package org.mapfish.print;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import com.itextpdf.awt.PdfGraphics2D;
import com.itextpdf.text.BadElementException;
import com.itextpdf.text.Chunk;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.Image;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfTemplate;
import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.DocumentLoader;
import org.apache.batik.bridge.GVTBuilder;
import org.apache.batik.bridge.UserAgent;
import org.apache.batik.bridge.UserAgentAdapter;
import org.apache.batik.dom.svg.SAXSVGDocumentFactory;
import org.apache.batik.dom.svg.SVGDocumentFactory;
import java.io.ByteArrayOutputStream;
import org.apache.batik.gvt.GraphicsNode;
import java.io.File;
import org.apache.batik.util.XMLResourceDescriptor;
import java.io.IOException;
import org.apache.commons.httpclient.Header;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import org.apache.commons.httpclient.methods.GetMethod;
import java.text.SimpleDateFormat;
import org.apache.log4j.Logger;
import java.util.Date;
import org.mapfish.print.config.layout.Block;
import java.util.HashMap;
import org.mapfish.print.config.layout.HorizontalAlign;
import java.util.List;
import org.mapfish.print.config.layout.MapBlock;
import java.util.Map;
import org.mapfish.print.config.layout.ScalebarBlock;
import java.util.regex.Matcher;
import org.mapfish.print.config.layout.TableConfig;
import java.util.regex.Pattern;
import org.mapfish.print.utils.PJsonObject;
import org.w3c.dom.svg.SVGDocument;
/**
* Some utility functions for iText.
*/
public class PDFUtils {
public static final Logger LOGGER = Logger.getLogger(PDFUtils.class);
private static final Map placeholderCache = new HashMap();
/**
* Gets an iText image with a cache that uses PdfTemplates to re-use the same
* bitmap content multiple times in order to reduce the file size.
*/
public static Image getImage(RenderingContext context, URI uri, float w, float h) throws IOException, DocumentException {
return getImage(context, uri, w, h, 0f);
}
/**
* Gets an iText image with a cache that uses PdfTemplates to re-use the same
* bitmap content multiple times in order to reduce the file size.
*/
public static Image getImage(RenderingContext context, URI uri, float w, float h, float scale) throws IOException, DocumentException {
//Check the image is not already used in the PDF file.
//
//This part is not protected against multi-threads... worst case, a single image can
//be twice in the PDF, if used more than one time. But since only one !map
//block is dealed with at a time, this should not happen
Map cache = context.getTemplateCache();
PdfTemplate template = cache.get(uri);
if (template == null) {
Image content = getImageDirect(context, uri);
content.setAbsolutePosition(0, 0);
final PdfContentByte dc = context.getDirectContent();
synchronized (context.getPdfLock()) { //protect against parallel writing on the PDF file
template = dc.createTemplate(content.getPlainWidth(), content.getPlainHeight());
template.addImage(content);
}
cache.put(uri, template);
}
//fix the size/aspect ratio of the image in function of what is specified by the user
if (w == 0.0f) {
if (h == 0.0f) {
scale = scale == 0f ? 1f : scale;
w = template.getWidth() * scale;
h = template.getHeight() * scale;
} else {
if (scale == 0f) {
w = h / template.getHeight() * template.getWidth();
}
else {
float maxh = h;
w = template.getWidth() * scale;
h = template.getWidth() * scale;
float scaleh = h / maxh;
if (scaleh > 1f) {
w /= scaleh;
h /= scaleh;
}
}
}
} else {
if (h == 0.0f) {
if (scale == 0f) {
h = w / template.getWidth() * template.getHeight();
}
else {
float maxw = w;
w = template.getWidth() * scale;
h = template.getWidth() * scale;
float scalew = w / maxw;
if (scalew > 1f) {
w /= scalew;
h /= scalew;
}
}
}
else {
if (scale == 0f) {
float scalew = template.getWidth() / w;
float scaleh = template.getHeight() / h;
float maxscale = Math.max(scalew, scaleh);
w = template.getWidth() / maxscale;
h = template.getHeight() / maxscale;
}
else {
float maxw = w;
float maxh = h;
w = template.getWidth() * scale;
h = template.getHeight() * scale;
float scalew = w / maxw;
float scaleh = h / maxh;
float maxscale = Math.max(scalew, scaleh);
if (maxscale > 1f) {
w /= maxscale;
h /= maxscale;
}
}
}
}
final Image result = Image.getInstance(template);
result.scaleToFit(w, h);
return result;
}
/**
* Gets an iText image. Avoids doing the query twice.
*/
protected static Image getImageDirect(RenderingContext context, URI uri) throws IOException, DocumentException {
return loadImageFromUrl(context, uri, false);
}
private static Image loadImageFromUrl(final RenderingContext context, final URI uri, final boolean alwaysThrowExceptionOnError)
throws
IOException, DocumentException {
File uriAsFile = null;
try {
uriAsFile = new File(uri.toString());
} catch (Throwable t) {
// ignore;
}
if (uriAsFile != null && uriAsFile.exists()) {
return Image.getInstance(uriAsFile.toURI().toURL());
} else if (!uri.isAbsolute()) {
//Assumption is that the file is on the local file system
return Image.getInstance(uri.toString());
} else if ("file".equalsIgnoreCase(uri.getScheme())) {
String path;
if (uri.getHost() != null && uri.getPath() != null) {
path = uri.getHost() + uri.getPath();
} else if (uri.getHost() == null && uri.getPath() != null) {
path = uri.getPath();
} else {
path = uri.toString().substring("file:".length()).replaceAll("/+", "/");
}
path = path.replace("/", File.separator);
return Image.getInstance(new File(path).toURI().toURL());
} else {
final String contentType;
final int statusCode;
final String statusText;
byte[] data = null;
try {
//read the whole image content in memory, then give that to iText
if ((uri.getScheme().equals("http") || uri.getScheme().equals("https"))
&& context.getConfig().localHostForwardIsFrom(uri.getHost())) {
String scheme = uri.getScheme();
final String host = uri.getHost();
if (uri.getScheme().equals("https")
&& context.getConfig().localHostForwardIsHttps2http()) {
scheme = "http";
}
URL url = new URL(scheme, "localhost", uri.getPort(),
uri.getPath() + "?" + uri.getQuery());
HttpURLConnection connexion = (HttpURLConnection) url.openConnection();
connexion.setRequestProperty("Host", host);
for (Map.Entry entry : context.getHeaders().entrySet()) {
connexion.setRequestProperty(entry.getKey(), entry.getValue());
}
InputStream is = null;
try {
try {
is = connexion.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer)) != -1) {
baos.write(buffer, 0, length);
}
baos.flush();
data = baos.toByteArray();
} catch (IOException e) {
LOGGER.warn(e);
}
statusCode = connexion.getResponseCode();
statusText = connexion.getResponseMessage();
contentType = connexion.getContentType();
} finally {
if (is != null) {
is.close();
}
}
} else {
GetMethod getMethod = null;
MetricRegistry registry = context.getConfig().getMetricRegistry();
final Timer.Context timer = registry.timer("http_" + uri.getAuthority()).time();
try {
getMethod = new GetMethod(uri.toString());
for (Map.Entry entry : context.getHeaders().entrySet()) {
getMethod.setRequestHeader(entry.getKey(), entry.getValue());
}
if (LOGGER.isDebugEnabled()) LOGGER.debug("loading image: " + uri);
context.getConfig().getHttpClient(uri).executeMethod(getMethod);
statusCode = getMethod.getStatusCode();
statusText = getMethod.getStatusText();
Header contentTypeHeader = getMethod.getResponseHeader("Content-Type");
if (contentTypeHeader == null) {
contentType = "";
} else {
contentType = contentTypeHeader.getValue();
}
data = getMethod.getResponseBody();
} finally {
timer.close();
if (getMethod != null) {
getMethod.releaseConnection();
}
}
}
if (statusCode == 204) {
// returns a transparent image
if (LOGGER.isDebugEnabled()) LOGGER.debug("creating a transparent image for: " + uri);
try {
final byte[] maskr = {(byte) 255};
Image mask = Image.getInstance(1, 1, 1, 1, maskr);
mask.makeMask();
data = new byte[1 * 1 * 3];
Image image = Image.getInstance(1, 1, 3, 8, data);
image.setImageMask(mask);
return image;
} catch (DocumentException e) {
LOGGER.warn("Couldn't generate a transparent image");
if (alwaysThrowExceptionOnError) {
throw e;
}
Image image = handleImageLoadError(context, e.getMessage());
LOGGER.warn("The status code was not a valid code, a default image is being returned.");
return image;
}
} else if (statusCode < 200 || statusCode >= 300 || contentType.startsWith("text/") || contentType.equals
("application/vnd" +
".ogc.se_xml")) {
if (LOGGER.isDebugEnabled()) LOGGER.debug("Server returned an error for " + uri + ": " + new String(data));
String errorMessage;
if (statusCode < 200 || statusCode >= 300) {
errorMessage = "Error (status=" + statusCode + ") while reading the image from " + uri + ": " + statusText;
} else {
errorMessage = "Didn't receive an image while reading: " + uri;
}
if (alwaysThrowExceptionOnError) {
throw new IOException(errorMessage);
}
Image image = handleImageLoadError(context, errorMessage);
LOGGER.warn("The status code was not a valid code, a default image is being returned.");
return image;
} else {
if (LOGGER.isDebugEnabled()) LOGGER.debug("loaded image: " + uri);
return Image.getInstance(data);
}
} catch (IOException e) {
LOGGER.error("Server returned an error for " + uri + ": " + e.getMessage());
if (alwaysThrowExceptionOnError) {
throw e;
}
return handleImageLoadError(context, e.getMessage());
}
}
}
/**
* In the case url fails to load an image this method should be called to handle the issue. If the configuration
* has a default image for broken image urls then it will be returned otherwise an error will be thrown.
*
* @param context the context.
* @param errorMessage the message of the error to throw in the case the configuration requires it.
*/
public static Image handleImageLoadError(final RenderingContext context, final String errorMessage) throws IOException,
DocumentException {
String placeholderString = context.getConfig().getBrokenUrlPlaceholder();
if (placeholderString.equalsIgnoreCase(Constants.ImagePlaceHolderConstants.THROW)) {
throw new IOException(errorMessage);
} else {
Image image = placeholderCache.get(placeholderString);
if (image == null) {
try {
if (placeholderString.equalsIgnoreCase(Constants.ImagePlaceHolderConstants.DEFAULT)) {
URL url = PDFUtils.class.getClassLoader().getResource(Constants.ImagePlaceHolderConstants.DEFAULT_ERROR_IMAGE);
image = loadImageFromUrl(context, url.toURI(), true);
} else {
image = loadImageFromUrl(context, new URI(placeholderString), true);
}
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
if (image != null) {
placeholderCache.put(placeholderString, image);
}
}
return image;
}
}
/**
* When we have to do some custom drawing in a block that is layed out by
* iText, we first give an empty table with the good dimensions to iText,
* then iText will call a callback with the actual position. When that
* happens, we use the given drawer to do the actual drawing.
*/
public static PdfPTable createPlaceholderTable(double width, double height, double spacingAfter,
ChunkDrawer drawer, HorizontalAlign align,
PDFCustomBlocks customBlocks) {
PdfPTable placeHolderTable = new PdfPTable(1);
placeHolderTable.setLockedWidth(true);
placeHolderTable.setTotalWidth((float) width);
final PdfPCell placeHolderCell = new PdfPCell();
placeHolderCell.setMinimumHeight((float) height);
placeHolderCell.setPadding(0f);
placeHolderCell.setBorder(PdfPCell.NO_BORDER);
placeHolderTable.addCell(placeHolderCell);
customBlocks.addChunkDrawer(drawer);
placeHolderTable.setTableEvent(drawer);
placeHolderTable.setComplete(true);
final PdfPCell surroundingCell = new PdfPCell(placeHolderTable);
surroundingCell.setPadding(0f);
surroundingCell.setBorder(PdfPCell.NO_BORDER);
if (align != null) {
placeHolderTable.setHorizontalAlignment(align.getCode());
surroundingCell.setHorizontalAlignment(align.getCode());
}
PdfPTable surroundingTable = new PdfPTable(1);
surroundingTable.setSpacingAfter((float) spacingAfter);
surroundingTable.addCell(surroundingCell);
surroundingTable.setComplete(true);
return surroundingTable;
}
private static final Pattern VAR_REGEXP = Pattern.compile("\\$\\{([^}]+)\\}");
public static Phrase renderString(RenderingContext context, PJsonObject params, String val, com.itextpdf.text.Font font, String mapName) throws BadElementException {
Phrase result = new Phrase();
while (true) {
Matcher matcher = VAR_REGEXP.matcher(val);
if (matcher.find()) {
result.add(val.substring(0, matcher.start()));
final String value;
final String varName = matcher.group(1);
if (varName.equals("pageTot")) {
result.add(context.getCustomBlocks().getOrCreateTotalPagesBlock(font));
} else {
value = getContextValue(context, params, varName, mapName);
result.add(value);
}
val = val.substring(matcher.end());
} else {
break;
}
}
result.add(val);
return result;
}
/**
* Evaluates stuff like "toto ${titi}"
*/
public static String evalString(RenderingContext context, PJsonObject params, String val, String mapName) {
if (val == null) {
return null;
}
StringBuilder result = new StringBuilder();
while (true) {
Matcher matcher = VAR_REGEXP.matcher(val);
if (matcher.find()) {
result.append(val.substring(0, matcher.start()));
result.append(getContextValue(context, params, matcher.group(1), mapName));
val = val.substring(matcher.end());
} else {
break;
}
}
result.append(val);
String uri = result.toString();
if(System.getProperty("os.name").toLowerCase().indexOf("win") >= 0){
uri = uri.replace("\\", "/");
if(uri.matches("file://\\w:(/.*)?")) {
return "file:/"+uri.substring(7);
}
}
return uri;
}
private static final Pattern FORMAT_PATTERN = Pattern.compile("^format\\s+(%[-+# 0,(]*\\d*(\\.\\d*)?(d))\\s+(.*)$");
public static String getValueFromString(String val) {
String str = val;
while (true) {
Matcher matcher = VAR_REGEXP.matcher(val);
if (matcher.find()) {
str = val.substring(0, matcher.start());
final String varName = matcher.group(1); // varName == key
str += getDateValue(varName);
val = val.substring(matcher.end());
} else {
break;
}
}
return str;
}
private static String getDateValue(String key) {
String val = "";
if (key.equals("now")) {
val = new Date().toString();
} else if (key.startsWith("now ")) {
val = formatTime(key);
}
return val;
}
private static String getContextValue(RenderingContext context, PJsonObject params, String key, String mapName) {
String result = null;
if (context != null) {
Matcher matcher;
if (key.equals("pageNum")) {
return Integer.toString(context.getWriter().getPageNumber());
} else if (key.equals("now")) {
return new Date().toString();
} else if (key.startsWith("now ")) {
return formatTime(context, key);
} else if ((matcher = FORMAT_PATTERN.matcher(key)) != null && matcher.matches()) {
return format(context, params, matcher, mapName);
} else if (key.equals("configDir")) {
return context.getConfigDir().replace('\\', '/');
} else if (key.equals("scale") || key.startsWith("scale.")) {
if(key.startsWith("scale.")) {
mapName = key.substring(6);
}
return Integer.toString((int)context.getLayout().getMainPage().getMap(mapName).createTransformer(context, params).getScale());
}
result = context.getGlobalParams().optString(key);
}
if (result == null) {
result = params.getString(key);
}
return result;
}
private static String format(RenderingContext context, PJsonObject params, Matcher matcher, String mapName) {
final String valueTxt = getContextValue(context, params, matcher.group(4), mapName);
final Object value;
try {
switch (matcher.group(3).charAt(0)) {
case 'd':
case 'o':
case 'x':
case 'X':
value = Math.round(Double.valueOf(valueTxt));
break;
case 'e':
case 'E':
case 'f':
case 'g':
case 'G':
case 'a':
case 'A':
value = Double.valueOf(valueTxt);
break;
default:
value = valueTxt;
}
} catch (Throwable e) {
context.addError(new RuntimeException("Error converting valueTxt: '" + valueTxt + "' to a number. Pattern: " + matcher.group(3)));
return valueTxt;
}
try {
return String.format(matcher.group(1), value);
} catch (RuntimeException e) {
// gracefuly fallback to the standard format
context.addError(e);
return valueTxt;
}
}
private static String formatTime(RenderingContext context, String key) {
try {
SimpleDateFormat format = new SimpleDateFormat(key.substring(4));
return format.format(new Date());
} catch (IllegalArgumentException e) {
// gracefuly fallback to the standard format
context.addError(e);
return new Date().toString();
}
}
private static String formatTime(String key) throws IllegalArgumentException {
SimpleDateFormat format = new SimpleDateFormat(key.substring(4));
return format.format(new Date());
}
/**
* Creates a PDF table with the given items. Returns null if the table is empty
*/
public static PdfPTable buildTable(List items, PJsonObject params, RenderingContext context, int nbColumns, TableConfig tableConfig) throws DocumentException {
int nbCells = 0;
for (int i = 0; i < items.size(); i++) {
final Block block = items.get(i);
if (block.isVisible(context, params)) {
if (block.isAbsolute()) {
// absolute blocks are rendered directly (special case for
// header/footer containing absolute blocks; it should not
// happen in other usecases).
block.render(params, null, context);
} else {
nbCells++;
}
}
}
if (nbCells == 0) return null;
nbColumns = nbColumns > 0 ? nbColumns : nbCells;
int nbRows = (nbCells + nbColumns - 1) / nbColumns;
final PdfPTable table = new PdfPTable(nbColumns);
table.setWidthPercentage(100f);
int cellNum = 0;
for (int i = 0; i < items.size(); i++) {
final Block block = items.get(i);
if (block.isVisible(context, params) && !block.isAbsolute()) {
final PdfPCell cell = createCell(params, context, block, cellNum / nbColumns, cellNum % nbColumns, nbRows, nbColumns, tableConfig);
table.addCell(cell);
cellNum++;
}
}
table.setComplete(true);
return table;
}
/**
* Create a PDF table cell with support for styling using the {@link org.mapfish.print.config.layout.CellConfig} stuff.
*/
public static PdfPCell createCell(final PJsonObject params, final RenderingContext context, final Block block, final int row,
final int col, final int nbRows, final int nbCols, final TableConfig tableConfig) throws DocumentException {
final PdfPCell[] cell = new PdfPCell[1];
block.render(params, new Block.PdfElement() {
public void add(Element element) throws DocumentException {
if (element instanceof PdfPTable) {
cell[0] = new PdfPCell((PdfPTable) element);
} else {
final Phrase phrase = new Phrase();
phrase.add(element);
cell[0] = new PdfPCell(phrase);
}
cell[0].setBorder(PdfPCell.NO_BORDER);
cell[0].setPadding(0);
if (tableConfig != null) {
tableConfig.apply(cell[0], row, col, nbRows, nbCols, context, params);
}
if (block.getAlign() != null) {
cell[0].setHorizontalAlignment(block.getAlign().getCode());
}
if (block.getVertAlign() != null) {
cell[0].setVerticalAlignment(block.getVertAlign().getCode());
}
if (!(block instanceof MapBlock) && !(block instanceof ScalebarBlock) && block.getBackgroundColorVal(context, params) != null) {
cell[0].setBackgroundColor(block.getBackgroundColorVal(context, params));
}
}
}, context);
return cell[0];
}
public static Chunk createImageChunk(RenderingContext context, double maxWidth, double maxHeight, URI url, float rotation) throws DocumentException {
return createImageChunk(context, maxWidth, maxHeight, 0f, url, rotation);
}
public static Chunk createImageChunk(RenderingContext context, double maxWidth, double maxHeight, float scale, URI url, float rotation) throws DocumentException {
final Image image = createImage(context, maxWidth, maxHeight, scale, url, rotation);
return new Chunk(image, 0f, 0f, true);
}
public static Image createImage(RenderingContext context, double maxWidth, double maxHeight, URI url, float rotation) throws DocumentException {
return createImage(context, maxWidth, maxHeight, 0f, url, rotation);
}
public static Image createImage(RenderingContext context, double maxWidth, double maxHeight, float scale, URI url, float rotation) throws DocumentException {
final Image image;
try {
image = getImage(context, url, (float) maxWidth, (float) maxHeight, scale);
} catch (IOException e) {
throw new InvalidValueException("url", url.toString(), e);
}
if (rotation != 0.0F) {
image.setRotation(rotation);
}
return image;
}
public static BaseFont getBaseFont(String fontFamily, String fontSize,
String fontWeight) {
Font.FontFamily myFontValue;
float myFontSize;
int myFontWeight;
if (fontFamily.toUpperCase().contains("COURIER")) {
myFontValue = Font.FontFamily.COURIER;
} else if (fontFamily.toUpperCase().contains("HELVETICA")) {
myFontValue = Font.FontFamily.HELVETICA;
} else if (fontFamily.toUpperCase().contains("ROMAN")) {
myFontValue = Font.FontFamily.TIMES_ROMAN;
} else {
myFontValue = Font.FontFamily.HELVETICA;
}
myFontSize = (float) Double.parseDouble(fontSize.toLowerCase()
.replaceAll("px", ""));
if (fontWeight.toUpperCase().contains("NORMAL")) {
myFontWeight = Font.NORMAL;
} else if (fontWeight.toUpperCase().contains("BOLD")) {
myFontWeight = Font.BOLD;
} else if (fontWeight.toUpperCase().contains("ITALIC")) {
myFontWeight = Font.ITALIC;
} else {
myFontWeight = Font.NORMAL;
}
Font pdfFont = new Font(myFontValue, myFontSize, myFontWeight);
return pdfFont.getCalculatedBaseFont(false);
}
public static int getHorizontalAlignment(String labelAlign) {
/* Valid values for horizontal alignment: "l"=left, "c"=center, "r"=right. */
int myAlignment = PdfContentByte.ALIGN_LEFT;
if (labelAlign.toUpperCase().contains("L")) {
myAlignment = PdfContentByte.ALIGN_LEFT;
}
if (labelAlign.toUpperCase().contains("C")) {
myAlignment = PdfContentByte.ALIGN_CENTER;
}
if (labelAlign.toUpperCase().contains("R")) {
myAlignment = PdfContentByte.ALIGN_RIGHT;
}
return myAlignment;
}
public static float getVerticalOffset(String labelAlign, float fontHeight) {
/* Valid values for vertical alignment: "t"=top, "m"=middle, "b"=bottom. */
float myOffset = (float) 0.0;
if (labelAlign.toUpperCase().contains("T")) {
myOffset = fontHeight;
}
if (labelAlign.toUpperCase().contains("M")) {
myOffset = fontHeight/2;
}
if (labelAlign.toUpperCase().contains("B")) {
myOffset = (float) 0.0;
}
return myOffset;
}
public static Chunk createImageChunkFromSVG(RenderingContext context,
String iconItem,
double maxIconWidth,
double maxIconHeight,
double scale) throws IOException {
return new Chunk(PDFUtils.createImageFromSVG(context, iconItem,
maxIconWidth, maxIconHeight, scale), 0f, 0f, true);
}
public static Image createImageFromSVG(RenderingContext context,
String iconItem, double maxIconWidth, double maxIconHeight,
double scale) throws IOException {
Image image = null;
try {
PdfContentByte dc = context.getDirectContent();
URI uri = URI.create(iconItem);
URL url = uri.toURL();
SVGDocumentFactory factory = new SAXSVGDocumentFactory(XMLResourceDescriptor.getXMLParserClassName());
UserAgent userAgent = new UserAgentAdapter();
DocumentLoader loader = new DocumentLoader(userAgent);
BridgeContext ctx = new BridgeContext(userAgent, loader);
ctx.setDynamicState(BridgeContext.DYNAMIC);
SVGDocument svgDoc = factory.createSVGDocument(null, url.openStream());
GVTBuilder builder = new GVTBuilder();
GraphicsNode graphics = builder.build(ctx, svgDoc);
String svgWidthString = svgDoc.getDocumentElement().getAttribute("width");
String svgHeightString = svgDoc.getDocumentElement().getAttribute("height");
float svgWidth = Float.valueOf(svgWidthString.substring(0, svgWidthString.length() - 2));
float svgHeight = Float.valueOf(svgHeightString.substring(0, svgHeightString.length() - 2));
/**
* svgFactor needs to be calculated depending on the screen DPI by the PDF DPI
* This is 96 / 72 = 4 / 3 ~= 1.3333333 on Windows, but might be different on *nix.
*/
final float svgFactor = 25.4f / userAgent.getPixelUnitToMillimeter() / 72f; // 25.4 mm = 1 inch TODO: Might need to get 72 from somewhere else?
//float svgFactor = (float) Toolkit.getDefaultToolkit().getScreenResolution() / 72f; // this only works with AWT, i.e. when a window environment is running
PdfTemplate map = dc.createTemplate(svgWidth * svgFactor, svgHeight * svgFactor);
PdfGraphics2D g2d = new PdfGraphics2D(map, svgWidth * svgFactor, svgHeight * svgFactor);
graphics.paint(g2d);
g2d.dispose();
image = Image.getInstance(map);
image.scalePercent((float) (scale * 100));
if (image.getWidth()*scale > maxIconWidth || image.getHeight()*scale > maxIconHeight) {
image.scaleToFit((float) maxIconWidth, (float) maxIconHeight);
}
} catch (BadElementException bee) {
LOGGER.warn("Bad Element " + iconItem + " with " + bee.getMessage());
} catch (MalformedURLException mue) {
LOGGER.warn("Malformed URL " + iconItem + " with " + mue.getMessage());
} catch (IOException ioe) {
throw ioe;
}
return image;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy