Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancement to improve the svg-image-handling and the usage of data-url #1386 #1387

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,8 @@ public Image getImage() {
} else if (DesignChoiceConstants.IMAGE_REF_TYPE_FILE.equalsIgnoreCase(imageSource)) {
if (URIUtil.isValidResourcePath(url)) {
return ImageManager.getInstance().getImage(imageHandel.getModuleHandle(), URIUtil.getLocalPath(url));
} else {
return ImageManager.getInstance().getImage(imageHandel.getModuleHandle(), url);
}
return ImageManager.getInstance().getImage(imageHandel.getModuleHandle(), url);

} else if (DesignChoiceConstants.IMAGE_REF_TYPE_URL.equalsIgnoreCase(imageSource)) {
// bugzilla 245641
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,21 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Base64.Decoder;
import java.util.List;

import org.apache.batik.transcoder.TranscoderException;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.JPEGTranscoder;
import org.apache.batik.transcoder.image.PNGTranscoder;
import org.eclipse.birt.report.designer.core.CorePlugin;
import org.eclipse.birt.report.designer.core.DesignerConstants;
import org.eclipse.birt.report.designer.core.model.SessionHandleAdapter;
Expand All @@ -51,7 +55,9 @@ public class ImageManager {

private static final String EMBEDDED_SUFFIX = ".Embedded."; //$NON-NLS-1$

private static final String DATA_PROTOCOL = "data:";
private static final String URL_IMAGE_TYPE_SVG = "image/svg+xml";
private static final String URL_PROTOCOL_TYPE_DATA = "data:";
private static final String URL_PROTOCOL_TYPE_DATA_BASE = ";base64,";

private static final ImageManager instance = new ImageManager();

Expand Down Expand Up @@ -95,7 +101,7 @@ public Image getImage(ModuleHandle handle, String uri, boolean refresh) {
URL url = null;

try {
if (uri.contains(DATA_PROTOCOL)) {
if (uri.contains(URL_PROTOCOL_TYPE_DATA)) {
image = getEmbeddedImageDataURL(uri, refresh);
} else {
url = generateURL(handle, uri);
Expand Down Expand Up @@ -175,15 +181,16 @@ public Image getEmbeddedImage(ModuleHandle handle, String name) {

InputStream in = null;
try {
if (key.toLowerCase().endsWith(".svg")) //$NON-NLS-1$
if (key.toLowerCase().endsWith(".svg") //$NON-NLS-1$
|| embeddedImage.getType(handle.getModule()).equalsIgnoreCase("image/svg+xml"))
{
// convert svg image to JPEG image bytes
JPEGTranscoder transcoder = new JPEGTranscoder();
JPEGTranscoder jpegTranscoder = new JPEGTranscoder();
// set the transcoding hints
transcoder.addTranscodingHint(JPEGTranscoder.KEY_QUALITY, .8f);
jpegTranscoder.addTranscodingHint(JPEGTranscoder.KEY_QUALITY, .8f);
// create the transcoder input
TranscoderInput input = new TranscoderInput(
new ByteArrayInputStream(embeddedImage.getData(handle.getModule())));
ByteArrayInputStream reader = new ByteArrayInputStream(embeddedImage.getData(handle.getModule()));
TranscoderInput input = new TranscoderInput(reader);
// For embedded image we have't a file URI, so set handle
// filename as URI.
// See Bugzilla Bug 167395
Expand All @@ -192,11 +199,22 @@ public Image getEmbeddedImage(ModuleHandle handle, String name) {
ByteArrayOutputStream ostream = new ByteArrayOutputStream();
TranscoderOutput output = new TranscoderOutput(ostream);
try {
transcoder.transcode(input, output);
} catch (TranscoderException e) {
// issue with batik JPEGTranscoder (since version 1.8)
// JPEGTranscoder is not longer part of apache-xmlgraphics
jpegTranscoder.transcode(input, output);
} catch (TranscoderException excJpg) {
try {
// fallback of preview image converting from svg to png
reader = new ByteArrayInputStream(embeddedImage.getData(handle.getModule()));
input = new TranscoderInput(reader);
PNGTranscoder pngConverter = new PNGTranscoder();
pngConverter.transcode(input, output);
} catch (TranscoderException excPng) {
}
}
// flush the stream
ostream.flush();
ostream.close();
// use the outputstream as Image input stream.
in = new ByteArrayInputStream(ostream.toByteArray());
} else {
Expand All @@ -205,10 +223,6 @@ public Image getEmbeddedImage(ModuleHandle handle, String name) {
ImageData[] datas = new ImageLoader().load(in);
if (datas != null && datas.length != 0) {
ImageData cur;
// if (datas.length == 1)
// {
// cur = datas[0];
// }
int index = 0;
for (int i = 0; i < datas.length; i++) {
ImageData temp = datas[i];
Expand Down Expand Up @@ -262,31 +276,14 @@ public Image getEmbeddedImageDataURL(String url, boolean refresh) throws IOExcep
removeCachedImage(key);
}
InputStream in = null;
String[] imageDataArray = key.split(";base64,");
String[] imageDataArray = key.split(URL_PROTOCOL_TYPE_DATA_BASE);
String imageDataBase64 = imageDataArray[1];
Decoder decoder = java.util.Base64.getDecoder();

try {
if (url.toLowerCase().contains("svg+xml")) //$NON-NLS-1$
if (url.toLowerCase().contains(URL_PROTOCOL_TYPE_DATA_BASE)) // $NON-NLS-1$
{
// convert svg image to JPEG image bytes
JPEGTranscoder transcoder = new JPEGTranscoder();
// set the transcoding hints
transcoder.addTranscodingHint(JPEGTranscoder.KEY_QUALITY, .8f);
// create the transcoder input
String svgURI = url;
TranscoderInput input = new TranscoderInput(svgURI);
// create the transcoder output
ByteArrayOutputStream ostream = new ByteArrayOutputStream();
TranscoderOutput output = new TranscoderOutput(ostream);
try {
transcoder.transcode(input, output);
} catch (TranscoderException e) {
}
// flush the stream
ostream.flush();
// use the outputstream as Image input stream.
in = new ByteArrayInputStream(ostream.toByteArray());
in = convertSvgToRasterImage(url.toString());
} else {
in = new ByteArrayInputStream(decoder.decode(imageDataBase64));
}
Expand Down Expand Up @@ -427,6 +424,16 @@ public Image loadImage(URL url, boolean reload) throws IOException {
try {
transcoder.transcode(input, output);
} catch (TranscoderException e) {

PNGTranscoder pngTranscoder = new PNGTranscoder();
input = new TranscoderInput(svgURI);
// create the transcoder output
ostream = new ByteArrayOutputStream();
output = new TranscoderOutput(ostream);
try {
pngTranscoder.transcode(input, output);
} catch (TranscoderException eJpeg) {
}
}
// flush the stream
ostream.flush();
Expand Down Expand Up @@ -463,6 +470,30 @@ public Image loadImage(URL url, boolean reload) throws IOException {
return image;
}

/**
* Converter to create raster image based on svg image
*/
private InputStream convertSvgToRasterImage(String imageSvg) throws IOException {

// convert svg image to JPEG image bytes
PNGTranscoder pngTranscoder = new PNGTranscoder();
// create the transcoder input
StringReader reader = new StringReader(imageSvg);
TranscoderInput input = new TranscoderInput(reader);
// create the transcoder output
ByteArrayOutputStream ostream = new ByteArrayOutputStream();
TranscoderOutput output = new TranscoderOutput(ostream);
try {
pngTranscoder.transcode(input, output);
} catch (TranscoderException eJpeg) {
}
// flush the stream
ostream.flush();
ostream.close();
// use the outputstream as Image input stream.
return new ByteArrayInputStream(ostream.toByteArray());
}

private ImageRegistry getImageRegistry() {
return CorePlugin.getDefault().getImageRegistry();
}
Expand Down Expand Up @@ -568,10 +599,61 @@ public URL createURIURL(String uri) {
*/
// bugzilla 245641
public Image getURIImage(ModuleHandle moduleHandel, String uri) {
URL url = createURIURL(uri);
Image image = null;
URL url = null;
String uriParts[] = null;
try {
image = getImageFromURL(url, false);
// data protocol raster image
if (uri.startsWith(URL_PROTOCOL_TYPE_DATA) && uri.contains(URL_PROTOCOL_TYPE_DATA_BASE)
&& !uri.contains(URL_IMAGE_TYPE_SVG)) {
uriParts = uri.split(URL_PROTOCOL_TYPE_DATA_BASE);
if (uriParts.length >= 2) {
String encodedImg = uriParts[1];
InputStream in = new ByteArrayInputStream(
Base64.getDecoder().decode(encodedImg.getBytes(StandardCharsets.UTF_8)));
ImageData[] datas = new ImageLoader().load(in);
if (datas != null && datas.length != 0) {
image = new Image(null, datas[0]);
in.close();
}
}
// data protocol svg image
} else if (uri.startsWith(URL_PROTOCOL_TYPE_DATA) && uri.contains(URL_IMAGE_TYPE_SVG)) {

String svgSplitter = "svg\\+xml,";
if (uri.contains("svg+xml;utf8,")) {
svgSplitter = "svg\\+xml;utf8,";
} else if (uri.contains("svg+xml;base64,")) {
svgSplitter = "svg\\+xml;base64,";
}
uriParts = uri.split(svgSplitter);
if (uriParts.length >= 2) {
String encodedImg = uriParts[1];
String decodedImg = encodedImg;
if (uri.contains(URL_PROTOCOL_TYPE_DATA_BASE)) { // "svg+xml;base64,"
decodedImg = new String(
Base64.getDecoder().decode(encodedImg.getBytes(StandardCharsets.UTF_8)));
}
decodedImg = java.net.URLDecoder.decode(decodedImg, StandardCharsets.UTF_8);
InputStream in = convertSvgToRasterImage(decodedImg);
ImageData[] datas = new ImageLoader().load(in);
if (datas != null && datas.length != 0) {
ImageData cur;
int index = 0;
for (int i = 0; i < datas.length; i++) {
ImageData temp = datas[i];
if (temp.width * temp.height > datas[index].width * datas[index].height) {
index = i;
}
}
cur = datas[index];
image = new Image(null, cur);
}
}
} else {
url = createURIURL(uri);
image = getImageFromURL(url, false);
}
} catch (Exception e) {
if (url != null && !invalidUrlList.contains(url.toString())) {
invalidUrlList.add(url.toString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
Expand Down Expand Up @@ -362,6 +364,9 @@ public class HTMLReportEmitter extends ContentEmitterAdapter {
*/
private static int DEFAULT_IMAGE_PX_HEIGHT = 200;

private static final String URL_PROTOCOL_TYPE_FILE = "file:";
private static final String URL_PROTOCOL_TYPE_DATA = "data:";

/**
* the constructor
*/
Expand Down Expand Up @@ -2995,10 +3000,24 @@ protected void resetImageDefaultBorders(IImageContent image, StringBuffer styleB
*/
protected String getImageURI(IImageContent image) {
String imgUri = null;
String tmpImgUri = null;
if (imageHandler != null) {
if (image.getImageSource() == IImageContent.IMAGE_URL) {
return image.getURI();
imgUri = image.getURI();

// embedded images w/o URI check
if (image.getImageSource() != IImageContent.IMAGE_NAME) {
tmpImgUri = this.verifyURI(imgUri);
if (imgUri != tmpImgUri) {
imgUri = tmpImgUri;
image.setURI(tmpImgUri);
}
}

// image URI with http/https
if (image.getImageSource() == IImageContent.IMAGE_URL && !imgUri.contains(URL_PROTOCOL_TYPE_FILE)) {
return imgUri;
}

Image img = new Image(image);
img.setRenderOption(renderOption);
img.setReportRunnable(runnable);
Expand Down Expand Up @@ -3559,6 +3578,26 @@ protected void retrieveRtLFlag() {
}
}
}

/**
* Check the URL to be valid and fall back try it like file-URL
*/
private String verifyURI(String uri) {
if (uri != null && !uri.toLowerCase().startsWith(URL_PROTOCOL_TYPE_DATA)) {
try {
new URL(uri).toURI();
} catch (MalformedURLException | URISyntaxException excUrl) {
// invalid URI try it like "file:"
try {
String tmpUrl = URL_PROTOCOL_TYPE_FILE + "///" + uri;
new URL(tmpUrl).toURI();
uri = tmpUrl;
} catch (MalformedURLException | URISyntaxException excFile) {
}
}
}
return uri;
}
}

class IDGenerator {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@

import java.io.IOException;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
Expand Down Expand Up @@ -255,6 +258,10 @@ public enum TextFlag {

protected static final String EMPTY_FOOTER = " ";

private static final String URL_PROTOCOL_TYPE_DATA = "data:";

private static final String URL_PROTOCOL_TYPE_FILE = "file:";

/**
* Initialize of the service
*
Expand Down Expand Up @@ -1005,7 +1012,7 @@ public void endPage(IPageContent page) {
public void startImage(IImageContent image) {
IStyle style = image.getComputedStyle();
InlineFlag inlineFlag = getInlineFlag(style);
String uri = image.getURI();
String uri = this.verifyURI(image.getURI());
String mimeType = image.getMIMEType();
String extension = image.getExtension();
String altText = image.getAltText();
Expand Down Expand Up @@ -1494,4 +1501,25 @@ static class TocInfo {
this.tocLevel = tocLevel;
}
}

/**
* Check the URL to be valid and fall back try it like file-URL
*/
private String verifyURI(String uri) {
if (uri != null && !uri.toLowerCase().startsWith(URL_PROTOCOL_TYPE_DATA)) {
try {
new URL(uri).toURI();
} catch (MalformedURLException | URISyntaxException excUrl) {
// invalid URI try it like "file:"
try {
String tmpUrl = URL_PROTOCOL_TYPE_FILE + "///" + uri;
new URL(tmpUrl).toURI();
uri = tmpUrl;
} catch (MalformedURLException | URISyntaxException excFile) {
}
}
}
return uri;
}

}
Loading
Loading