From 3605e902731f70a51863af9024e31818b5c356c1 Mon Sep 17 00:00:00 2001 From: pizzi80 Date: Wed, 28 Aug 2024 21:39:03 +0200 Subject: [PATCH 1/9] Version 3.0.0 Signed-off-by: pizzi80 --- html2image/pom.xml | 80 +++++++++---- .../main/java/gui/ava/html/Html2Image.java | 20 ++-- .../ava/html/exception/RenderException.java | 2 + .../ava/html/image/HtmlImageGenerator.java | 78 ++++++------ .../html/image/SynchronousHTMLEditorKit.java | 37 ++++++ .../ava/html/image/util/FormatNameUtil.java | 37 ------ .../image/util/SynchronousHTMLEditorKit.java | 33 ------ .../gui/ava/html/imagemap/ElementBox.java | 38 +++--- .../gui/ava/html/imagemap/HtmlImageMap.java | 6 +- .../ava/html/imagemap/HtmlImageMapImpl.java | 76 ++++++------ .../main/java/gui/ava/html/link/LinkHarvester | 77 ------------ .../main/java/gui/ava/html/link/LinkInfo.java | 46 +++---- .../java/gui/ava/html/parser/HtmlParser.java | 8 +- .../gui/ava/html/parser/HtmlParserImpl.java | 79 ++++++------ .../gui/ava/html/pdf/PdfRendererImpl.java | 15 ++- .../gui/ava/html/renderer/FormatNameUtil.java | 41 ------- .../gui/ava/html/renderer/ImageRenderer.java | 1 + .../ava/html/renderer/ImageRendererImpl.java | 74 ++++++++---- .../gui/ava/html/util/FormatNameUtil.java | 41 +++++++ .../src/test/java/gui/ava/html/BaseTest.java | 112 +++++++++++++++++- .../src/test/java/gui/ava/html/Example.java | 32 ++++- .../java/gui/ava/html/WordWrapExample.java | 53 +++++---- .../html/imagemap/HtmlImageMapImplTest.java | 29 ++--- .../ava/html/parser/HtmlParserImplTest.java | 29 +++-- .../gui/ava/html/pdf/PdfRendererImplTest.java | 8 +- .../ava/html/renderer/FormatNameUtilTest.java | 19 +-- .../html/renderer/ImageRendererImplTest.java | 22 ++-- 27 files changed, 601 insertions(+), 492 deletions(-) create mode 100644 html2image/src/main/java/gui/ava/html/image/SynchronousHTMLEditorKit.java delete mode 100644 html2image/src/main/java/gui/ava/html/image/util/FormatNameUtil.java delete mode 100644 html2image/src/main/java/gui/ava/html/image/util/SynchronousHTMLEditorKit.java delete mode 100644 html2image/src/main/java/gui/ava/html/link/LinkHarvester delete mode 100644 html2image/src/main/java/gui/ava/html/renderer/FormatNameUtil.java create mode 100644 html2image/src/main/java/gui/ava/html/util/FormatNameUtil.java diff --git a/html2image/pom.xml b/html2image/pom.xml index 5cb9118..e278fa5 100644 --- a/html2image/pom.xml +++ b/html2image/pom.xml @@ -1,63 +1,95 @@ - + + + 4.0.0 + gui.ava html2image jar - 2.0-SNAPSHOT + 3.0.0-SNAPSHOT html2image - http://maven.apache.org + https://maven.apache.org + + + + 17 + UTF-8 + ${java.version} + ${java.version} + ${java.version} + 9.9.1 + + + + + + - org.xhtmlrenderer - core-renderer - R8 + org.htmlunit + neko-htmlunit + 4.4.0 + + + - net.sourceforge.nekohtml - nekohtml - 1.9.14 + org.xhtmlrenderer + flying-saucer-core + ${flying-saucer.version} - commons-lang - commons-lang - 2.5 + org.xhtmlrenderer + flying-saucer-pdf + ${flying-saucer.version} + + + - junit - junit - 4.8.1 - test + org.apache.commons + commons-lang3 + 3.16.0 + + + - org.springframework - spring-core - 3.0.3.RELEASE + org.junit.jupiter + junit-jupiter-engine + 5.11.0 test + + jfrog-third-party-releases-local - http://repo.jfrog.org/artifactory/third-party-releases-local + https://repo.jfrog.org/artifactory/third-party-releases-local + org.apache.maven.plugins maven-compiler-plugin + 3.13.0 - 1.8 - 1.8 - utf-8 + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + diff --git a/html2image/src/main/java/gui/ava/html/Html2Image.java b/html2image/src/main/java/gui/ava/html/Html2Image.java index c8a7505..b5c47c9 100644 --- a/html2image/src/main/java/gui/ava/html/Html2Image.java +++ b/html2image/src/main/java/gui/ava/html/Html2Image.java @@ -1,5 +1,13 @@ package gui.ava.html; +import java.io.File; +import java.io.InputStream; +import java.io.Reader; +import java.net.URI; +import java.net.URL; + +import org.w3c.dom.Document; + import gui.ava.html.imagemap.HtmlImageMap; import gui.ava.html.imagemap.HtmlImageMapImpl; import gui.ava.html.parser.HtmlParser; @@ -8,19 +16,14 @@ import gui.ava.html.pdf.PdfRendererImpl; import gui.ava.html.renderer.ImageRenderer; import gui.ava.html.renderer.ImageRendererImpl; -import org.w3c.dom.Document; - -import java.io.File; -import java.io.InputStream; -import java.io.Reader; -import java.net.URI; -import java.net.URL; /** * @author Yoav Aharoni */ public class Html2Image { - private HtmlParser parser = new HtmlParserImpl(); + + private final HtmlParser parser = new HtmlParserImpl(); + private HtmlImageMap htmlImageMap; private ImageRenderer imageRenderer; private PdfRenderer pdfRenderer; @@ -91,4 +94,5 @@ public static Html2Image fromInputStream(InputStream inputStream) { html2Image.getParser().load(inputStream); return html2Image; } + } diff --git a/html2image/src/main/java/gui/ava/html/exception/RenderException.java b/html2image/src/main/java/gui/ava/html/exception/RenderException.java index 395a0ea..2c499ce 100644 --- a/html2image/src/main/java/gui/ava/html/exception/RenderException.java +++ b/html2image/src/main/java/gui/ava/html/exception/RenderException.java @@ -4,7 +4,9 @@ * @author Yoav Aharoni */ public class RenderException extends RuntimeException { + public RenderException(String message, Throwable cause) { super(message, cause); } + } diff --git a/html2image/src/main/java/gui/ava/html/image/HtmlImageGenerator.java b/html2image/src/main/java/gui/ava/html/image/HtmlImageGenerator.java index aaebabb..0db755d 100644 --- a/html2image/src/main/java/gui/ava/html/image/HtmlImageGenerator.java +++ b/html2image/src/main/java/gui/ava/html/image/HtmlImageGenerator.java @@ -1,29 +1,29 @@ package gui.ava.html.image; -import gui.ava.html.image.util.FormatNameUtil; -import gui.ava.html.image.util.SynchronousHTMLEditorKit; -import gui.ava.html.link.LinkInfo; - -import javax.imageio.ImageIO; -import javax.swing.*; import java.awt.*; +import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.net.URL; -import java.util.Collection; import java.util.Collections; import java.util.List; +import javax.imageio.ImageIO; +import javax.swing.*; + +import gui.ava.html.link.LinkInfo; +import gui.ava.html.util.FormatNameUtil; + /** * @author Yoav Aharoni */ public class HtmlImageGenerator { - private JEditorPane editorPane; - static final Dimension DEFAULT_SIZE = new Dimension(800, 800); + + private static final Dimension DEFAULT_SIZE = new Dimension(800, 800); + + private final JEditorPane editorPane; public HtmlImageGenerator() { editorPane = createJEditorPane(); @@ -72,15 +72,15 @@ public String getLinksMapMarkup(String mapName) { final StringBuilder markup = new StringBuilder(); markup.append("\n"); for (LinkInfo link : getLinks()) { - final List bounds = link.getBounds(); - for (Rectangle bound : bounds) { + final List bounds = link.getBounds(); + for (Rectangle2D bound : bounds) { final int x1 = (int) bound.getX(); final int y1 = (int) bound.getY(); final int x2 = (int) (x1 + bound.getWidth()); final int y2 = (int) (y1 + bound.getHeight()); markup.append(String.format("\n"); @@ -101,9 +101,7 @@ public void saveAsHtmlWithMap(String file, String imageUrl) { } public void saveAsHtmlWithMap(File file, String imageUrl) { - FileWriter writer = null; - try { - writer = new FileWriter(file); + try (FileWriter writer = new FileWriter(file)) { writer.append("\n"); writer.append("\n\n"); writer.append("\n"); @@ -115,15 +113,7 @@ public void saveAsHtmlWithMap(File file, String imageUrl) { writer.append("\n"); } catch (IOException e) { throw new RuntimeException(String.format("Exception while saving '%s' html file", file), e); - } finally { - if (writer != null) { - try { - writer.close(); - } catch (IOException ignore) { - } - } } - } public void saveAsImage(String file) { @@ -179,25 +169,35 @@ protected JEditorPane createJEditorPane() { final SynchronousHTMLEditorKit kit = new SynchronousHTMLEditorKit(); editorPane.setEditorKitForContentType("text/html", kit); editorPane.setContentType("text/html"); - editorPane.addPropertyChangeListener(new PropertyChangeListener() { - public void propertyChange(PropertyChangeEvent evt) { - if (evt.getPropertyName().equals("page")) { - onDocumentLoad(); - } + editorPane.addPropertyChangeListener(event -> { + if (event.getPropertyName().equals("page")) { + onDocumentLoad(); } }); return editorPane; } public void show() { - JFrame.setDefaultLookAndFeelDecorated(true); - JFrame frame = new JFrame(); - frame.setTitle("My First Swing Application"); - frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); - JLabel label = new JLabel("Welcome"); - frame.add(label); - frame.add(editorPane); - frame.pack(); - frame.setVisible(true); + // the main window + final JFrame view = new JFrame(); + + // create the view + view.setTitle("HtmlImageGenerator"); + view.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + JLabel label = new JLabel("Label"); + view.add(label); + view.add(editorPane); + view.pack(); + + // set the system look & feel + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + SwingUtilities.updateComponentTreeUI(view); + } catch (Exception ignored) {} + + // show the view + view.setLocationByPlatform(true); + view.setVisible(true); } + } diff --git a/html2image/src/main/java/gui/ava/html/image/SynchronousHTMLEditorKit.java b/html2image/src/main/java/gui/ava/html/image/SynchronousHTMLEditorKit.java new file mode 100644 index 0000000..bf76118 --- /dev/null +++ b/html2image/src/main/java/gui/ava/html/image/SynchronousHTMLEditorKit.java @@ -0,0 +1,37 @@ +package gui.ava.html.image; + +import javax.swing.text.Document; +import javax.swing.text.Element; +import javax.swing.text.View; +import javax.swing.text.ViewFactory; +import javax.swing.text.html.HTMLDocument; +import javax.swing.text.html.HTMLEditorKit; +import javax.swing.text.html.ImageView; + +/** + * @author Yoav Aharoni + */ +public class SynchronousHTMLEditorKit extends HTMLEditorKit { + + @Override + public Document createDefaultDocument() { + HTMLDocument doc = (HTMLDocument) super.createDefaultDocument(); + doc.setAsynchronousLoadPriority(-1); + return doc; + } + + @Override + public ViewFactory getViewFactory() { + return new HTMLFactory() { + @Override + public View create(Element elem) { + View view = super.create(elem); + if (view instanceof ImageView imageView) { + imageView.setLoadsSynchronously(true); + } + return view; + } + }; + } + +} diff --git a/html2image/src/main/java/gui/ava/html/image/util/FormatNameUtil.java b/html2image/src/main/java/gui/ava/html/image/util/FormatNameUtil.java deleted file mode 100644 index ca644a5..0000000 --- a/html2image/src/main/java/gui/ava/html/image/util/FormatNameUtil.java +++ /dev/null @@ -1,37 +0,0 @@ - -package gui.ava.html.image.util; - -import java.util.HashMap; -import java.util.Map; - -/** - * @author Yoav Aharoni - */ -public class FormatNameUtil { - public static Map types = new HashMap(); - private static final String DEFAULT_FORMAT = "png"; - - static { - types.put("gif", "gif"); - types.put("jpg", "jpg"); - types.put("jpeg", "jpg"); - types.put("png", "png"); - } - - public static String formatForExtension(String extension) { - final String type = types.get(extension); - if (type == null) { - return DEFAULT_FORMAT; - } - return type; - } - - public static String formatForFilename(String fileName) { - final int dotIndex = fileName.lastIndexOf('.'); - if (dotIndex < 0) { - return DEFAULT_FORMAT; - } - final String ext = fileName.substring(dotIndex + 1); - return formatForExtension(ext); - } -} diff --git a/html2image/src/main/java/gui/ava/html/image/util/SynchronousHTMLEditorKit.java b/html2image/src/main/java/gui/ava/html/image/util/SynchronousHTMLEditorKit.java deleted file mode 100644 index f9f96f9..0000000 --- a/html2image/src/main/java/gui/ava/html/image/util/SynchronousHTMLEditorKit.java +++ /dev/null @@ -1,33 +0,0 @@ -package gui.ava.html.image.util; - -import javax.swing.text.Document; -import javax.swing.text.Element; -import javax.swing.text.View; -import javax.swing.text.ViewFactory; -import javax.swing.text.html.HTMLDocument; -import javax.swing.text.html.HTMLEditorKit; -import javax.swing.text.html.ImageView; - -/** - * @author Yoav Aharoni - */ -public class SynchronousHTMLEditorKit extends HTMLEditorKit { - - public Document createDefaultDocument() { - HTMLDocument doc = (HTMLDocument) super.createDefaultDocument(); - doc.setAsynchronousLoadPriority(-1); - return doc; - } - - public ViewFactory getViewFactory() { - return new HTMLFactory() { - public View create(Element elem) { - View view = super.create(elem); - if (view instanceof ImageView) { - ((ImageView) view).setLoadsSynchronously(true); - } - return view; - } - }; - } -} diff --git a/html2image/src/main/java/gui/ava/html/imagemap/ElementBox.java b/html2image/src/main/java/gui/ava/html/imagemap/ElementBox.java index 094ae9a..01f2655 100644 --- a/html2image/src/main/java/gui/ava/html/imagemap/ElementBox.java +++ b/html2image/src/main/java/gui/ava/html/imagemap/ElementBox.java @@ -1,18 +1,19 @@ package gui.ava.html.imagemap; -import org.w3c.dom.Element; - import java.util.Collection; +import org.w3c.dom.Element; + /** * @author Yoav Aharoni */ public class ElementBox { - private Element element; - private int left; - private int top; - private int width; - private int height; + + private final Element element; + private final int left; + private final int top; + private final int width; + private final int height; public ElementBox(Element element, int left, int top, int width, int height) { this.element = element; @@ -54,17 +55,22 @@ public boolean isEmpty() { return width <= 0 || height <= 0; } + public boolean containedIn(ElementBox box) { + return containedIn(this, box); + } + public boolean containedIn(Collection elementBoxes) { - for (ElementBox box : elementBoxes) { - if (containedIn(box)) { - return true; - } - } - return false; + return elementBoxes.stream().anyMatch(this::containedIn); } - public boolean containedIn(ElementBox box) { - return getTop() >= box.getTop() && getLeft() >= box.getTop() - && getBottom() <= box.getBottom() && getRight() <= box.getRight(); + /** + * @param box box to test + * @param other the other {@link ElementBox} + * @return true if box is contained inside the other {@link ElementBox} + */ + public static boolean containedIn(ElementBox box, ElementBox other) { + return box.getTop() >= other.getTop() && box.getLeft() >= other.getTop() + && box.getBottom() <= other.getBottom() && box.getRight() <= other.getRight(); } + } diff --git a/html2image/src/main/java/gui/ava/html/imagemap/HtmlImageMap.java b/html2image/src/main/java/gui/ava/html/imagemap/HtmlImageMap.java index 6b38f89..50b2047 100644 --- a/html2image/src/main/java/gui/ava/html/imagemap/HtmlImageMap.java +++ b/html2image/src/main/java/gui/ava/html/imagemap/HtmlImageMap.java @@ -1,16 +1,17 @@ package gui.ava.html.imagemap; -import org.w3c.dom.Element; - import java.io.File; import java.io.Writer; import java.util.Collection; import java.util.Map; +import org.w3c.dom.Element; + /** * @author Yoav Aharoni */ public interface HtmlImageMap { + Map> getClickableBoxes(); String getImageMap(String mapName, String imageURL); @@ -24,4 +25,5 @@ public interface HtmlImageMap { void saveImageMapDocument(File file, String imageURL); void saveImageMapDocument(Writer writer, String imageURL, boolean closeWriter); + } diff --git a/html2image/src/main/java/gui/ava/html/imagemap/HtmlImageMapImpl.java b/html2image/src/main/java/gui/ava/html/imagemap/HtmlImageMapImpl.java index ae7ece9..c1de04b 100644 --- a/html2image/src/main/java/gui/ava/html/imagemap/HtmlImageMapImpl.java +++ b/html2image/src/main/java/gui/ava/html/imagemap/HtmlImageMapImpl.java @@ -1,34 +1,46 @@ package gui.ava.html.imagemap; -import gui.ava.html.exception.RenderException; -import gui.ava.html.renderer.LayoutHolder; -import org.apache.commons.lang.StringUtils; +import static java.lang.String.format; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; + +import gui.ava.html.exception.RenderException; +import gui.ava.html.renderer.LayoutHolder; import org.xhtmlrenderer.layout.Styleable; import org.xhtmlrenderer.render.BlockBox; import org.xhtmlrenderer.render.Box; import org.xhtmlrenderer.render.InlineLayoutBox; import org.xhtmlrenderer.render.LineBox; -import java.io.*; -import java.util.*; - -import static java.lang.String.format; - /** * @author Yoav Aharoni */ public class HtmlImageMapImpl implements HtmlImageMap { - private static Set searchedAttributes = stringSet("href", "onclick", "ondblclick", "onmousedown", "onmouseup"); - private static Set allowedAttributes = stringSet( + + private static final Set searchedAttributes = Set.of("href", "onclick", "ondblclick", "onmousedown", "onmouseup"); + + private static final Set allowedAttributes = Set.of( "href", "target", "title", "class", "tabindex", "dir", "lang", "accesskey", "onblur", "onclick", "ondblclick", "onfocus", "onmousedown", "onmousemove", "onmouseout", "onmouseover", "onmouseup", "onkeydown", "onkeypress", "onkeyup"); - private LayoutHolder layoutHolder; + private final LayoutHolder layoutHolder; public HtmlImageMapImpl(LayoutHolder layoutHolder) { this.layoutHolder = layoutHolder; @@ -120,12 +132,11 @@ public void saveImageMapDocument(String filename, String imageURL) { @Override public Map> getClickableBoxes() { final Box rootBox = layoutHolder.getRootBox(); - final HashMap> boxes = new HashMap>(); - addClickableElements(rootBox, boxes, new HashSet()); + final HashMap> boxes = new HashMap<>(); + addClickableElements(rootBox, boxes, new HashSet<>()); return boxes; } - @SuppressWarnings({"unchecked"}) private void addClickableElements(Styleable styleable, HashMap> boxes, Set visited) { if (styleable == null || visited.contains(styleable)) { return; @@ -134,26 +145,26 @@ private void addClickableElements(Styleable styleable, HashMap) ((Box) styleable).getChildren()) { + if (styleable instanceof Box box) { + for (Styleable child : box.getChildren()) { addClickableElements(child, boxes, visited); } } - if (styleable instanceof InlineLayoutBox) { - for (Object child : (List) ((InlineLayoutBox) styleable).getInlineChildren()) { - if (child instanceof Styleable) { - addClickableElements((Styleable) child, boxes, visited); + if (styleable instanceof InlineLayoutBox inlineLayoutBox) { + for (Object child : inlineLayoutBox.getInlineChildren()) { + if (child instanceof Styleable styleableChild) { + addClickableElements(styleableChild, boxes, visited); } } - } else if (styleable instanceof BlockBox) { - final List content = (List) ((BlockBox) styleable).getInlineContent(); + } else if (styleable instanceof BlockBox blockBox) { + final List content = blockBox.getInlineContent(); if (content != null) { for (Styleable child : content) { addClickableElements(child, boxes, visited); } } - } else if (styleable instanceof LineBox) { - for (Styleable child : (List) ((LineBox) styleable).getNonFlowContent()) { + } else if (styleable instanceof LineBox lineBox) { + for (Styleable child : lineBox.getNonFlowContent()) { addClickableElements(child, boxes, visited); } } @@ -170,7 +181,7 @@ private void addIfClickable(Styleable styleable, HashMap elementBoxes = boxes.get(clickable); if (elementBoxes == null) { - elementBoxes = new ArrayList(); + elementBoxes = new ArrayList<>(); boxes.put(clickable, elementBoxes); elementBoxes.add(elementBox); return; @@ -181,14 +192,12 @@ private void addIfClickable(Styleable styleable, HashMap stringSet(String... items) { - return new HashSet(Arrays.asList(items)); - } } diff --git a/html2image/src/main/java/gui/ava/html/link/LinkHarvester b/html2image/src/main/java/gui/ava/html/link/LinkHarvester deleted file mode 100644 index 0323f3b..0000000 --- a/html2image/src/main/java/gui/ava/html/link/LinkHarvester +++ /dev/null @@ -1,77 +0,0 @@ -package gui.ava.html.link; - -import javax.swing.*; -import javax.swing.text.*; -import javax.swing.text.html.HTML; -import java.awt.*; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.List; - -/** - * @author Yoav Aharoni - */ -public class LinkHarvester { - private final JTextComponent textComponent; - private final List links = new ArrayList(); - - public LinkHarvester(JEditorPane textComponent) { - this.textComponent = textComponent; - harvestElement(textComponent.getDocument().getDefaultRootElement()); - } - - public List getLinks() { - return links; - } - - private void harvestElement(Element element) { - if (element == null) { - return; - } - - final AttributeSet attributes = element.getAttributes(); - final Enumeration attributeNames = attributes.getAttributeNames(); - while (attributeNames.hasMoreElements()) { - final Object key = attributeNames.nextElement(); - if (HTML.Tag.A.equals(key)) { - final Object value = attributes.getAttribute(key); - if (value instanceof SimpleAttributeSet) { - final SimpleAttributeSet attributeSet = (SimpleAttributeSet) value; - final String href = (String) attributeSet.getAttribute(HTML.Attribute.HREF); - final String title = (String) attributeSet.getAttribute(HTML.Attribute.TITLE); - final List bounds = elementBounds(element); - links.add(new LinkInfo(href, title, bounds)); - } - } - } - - for (int i = 0; i < element.getElementCount(); i++) { - final Element child = element.getElement(i); - harvestElement(child); - } - } - - private List elementBounds(Element element) { - final List bounds = new ArrayList(); - try { - final int startOffset = element.getStartOffset(); - final int endOffset = element.getEndOffset(); - Rectangle rectangle = textComponent.modelToView(startOffset); - for (int i = startOffset + 1; i <= endOffset; i++) { - final Rectangle temp = textComponent.modelToView(i); - if (temp.getY() == rectangle.getY()) { - rectangle = rectangle.union(temp); - } else { - bounds.add(rectangle); - rectangle = null; - } - } - if (rectangle != null) { - bounds.add(rectangle); - } - return bounds; - } catch (BadLocationException e) { - throw new RuntimeException("Got BadLocationException", e); - } - } -} diff --git a/html2image/src/main/java/gui/ava/html/link/LinkInfo.java b/html2image/src/main/java/gui/ava/html/link/LinkInfo.java index abbbb36..72aeed1 100644 --- a/html2image/src/main/java/gui/ava/html/link/LinkInfo.java +++ b/html2image/src/main/java/gui/ava/html/link/LinkInfo.java @@ -1,31 +1,33 @@ package gui.ava.html.link; -import java.awt.*; +import java.awt.geom.Rectangle2D; import java.util.List; /** * @author Yoav Aharoni */ public class LinkInfo { - private String href; - private String title; - private List bounds; - - public LinkInfo(String href, String title, List bounds) { - this.href = href; - this.title = title; - this.bounds = bounds; - } - - public String getHref() { - return href; - } - - public String getTitle() { - return title; - } - - public List getBounds() { - return bounds; - } + + private final String href; + private final String title; + private final List bounds; + + public LinkInfo(String href, String title, List bounds) { + this.href = href; + this.title = title; + this.bounds = bounds; + } + + public String getHref() { + return href; + } + + public String getTitle() { + return title; + } + + public List getBounds() { + return bounds; + } + } diff --git a/html2image/src/main/java/gui/ava/html/parser/HtmlParser.java b/html2image/src/main/java/gui/ava/html/parser/HtmlParser.java index 6066793..8bab1cb 100644 --- a/html2image/src/main/java/gui/ava/html/parser/HtmlParser.java +++ b/html2image/src/main/java/gui/ava/html/parser/HtmlParser.java @@ -1,14 +1,15 @@ package gui.ava.html.parser; -import org.apache.xerces.parsers.DOMParser; -import org.w3c.dom.Document; - import java.io.File; import java.io.InputStream; import java.io.Reader; import java.net.URI; import java.net.URL; +import org.w3c.dom.Document; + +import org.htmlunit.cyberneko.parsers.DOMParser; + /** * @author Yoav Aharoni */ @@ -33,4 +34,5 @@ public interface HtmlParser extends DocumentHolder { void loadHtml(String html); void loadURI(String uri); + } diff --git a/html2image/src/main/java/gui/ava/html/parser/HtmlParserImpl.java b/html2image/src/main/java/gui/ava/html/parser/HtmlParserImpl.java index 2c57e60..6bb5679 100644 --- a/html2image/src/main/java/gui/ava/html/parser/HtmlParserImpl.java +++ b/html2image/src/main/java/gui/ava/html/parser/HtmlParserImpl.java @@ -1,36 +1,41 @@ package gui.ava.html.parser; -import org.apache.xerces.parsers.DOMParser; -import org.cyberneko.html.HTMLConfiguration; +import static java.lang.String.format; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.net.URI; +import java.net.URL; + import org.w3c.dom.Document; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXNotRecognizedException; import org.xml.sax.SAXNotSupportedException; -import java.io.*; -import java.net.URI; -import java.net.URL; - -import static java.lang.String.format; +import org.htmlunit.cyberneko.html.dom.HTMLDocumentImpl; +import org.htmlunit.cyberneko.parsers.DOMParser; /** * @author Yoav Aharoni */ public class HtmlParserImpl implements HtmlParser { + private DOMParser domParser; private Document document; public HtmlParserImpl() { - domParser = new DOMParser(new HTMLConfiguration()); try { + domParser = new DOMParser(HTMLDocumentImpl.class); domParser.setProperty("http://cyberneko.org/html/properties/names/elems", "lower"); - } catch (SAXNotRecognizedException e) { - throw new ParseException("Can't create HtmlParserImpl", e); - } catch (SAXNotSupportedException e) { + } + catch (SAXNotRecognizedException | SAXNotSupportedException e) { throw new ParseException("Can't create HtmlParserImpl", e); } - } + } @Override public DOMParser getDomParser() { @@ -54,37 +59,26 @@ public void setDocument(Document document) { @Override public void load(Reader reader) { - try { - domParser.parse(new InputSource(reader)); - document = domParser.getDocument(); - } catch (SAXException e) { - throw new ParseException("SAXException while parsing HTML.", e); - } catch (IOException e) { - throw new ParseException("IOException while parsing HTML.", e); - } finally { - try { - reader.close(); - } catch (IOException ignore) { - } - } + try (reader) { + domParser.parse(new InputSource(reader)); + document = domParser.getDocument(); + } catch (SAXException e) { + throw new ParseException("SAXException while parsing HTML.", e); + } catch (IOException e) { + throw new ParseException("IOException while parsing HTML.", e); + } } @Override public void load(InputStream inputStream) { - try { - domParser.parse(new InputSource(inputStream)); - document = domParser.getDocument(); - } catch (SAXException e) { - throw new ParseException("SAXException while parsing HTML.", e); - } catch (IOException e) { - throw new ParseException("IOException while parsing HTML.", e); - } - finally { - try { - inputStream.close(); - } catch (IOException ignore) { - } - } + try (inputStream) { + domParser.parse(new InputSource(inputStream)); + document = domParser.getDocument(); + } catch (SAXException e) { + throw new ParseException("SAXException while parsing HTML.", e); + } catch (IOException e) { + throw new ParseException("IOException while parsing HTML.", e); + } } @Override @@ -92,12 +86,10 @@ public void loadURI(String uri) { try { domParser.parse(new InputSource(uri)); document = domParser.getDocument(); - } catch (SAXException e) { - throw new ParseException(format("SAXException while parsing HTML from \"%s\".", uri), e); - } catch (IOException e) { + } catch (SAXException | IOException e) { throw new ParseException(format("SAXException while parsing HTML from \"%s\".", uri), e); } - } + } @Override public void load(File file) { @@ -118,4 +110,5 @@ public void load(URI uri) { public void loadHtml(String html) { load(new StringReader(html)); } + } diff --git a/html2image/src/main/java/gui/ava/html/pdf/PdfRendererImpl.java b/html2image/src/main/java/gui/ava/html/pdf/PdfRendererImpl.java index 94221e2..38b01cb 100644 --- a/html2image/src/main/java/gui/ava/html/pdf/PdfRendererImpl.java +++ b/html2image/src/main/java/gui/ava/html/pdf/PdfRendererImpl.java @@ -1,18 +1,24 @@ package gui.ava.html.pdf; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import org.w3c.dom.Document; + import com.lowagie.text.DocumentException; import gui.ava.html.exception.RenderException; import gui.ava.html.parser.DocumentHolder; -import org.w3c.dom.Document; import org.xhtmlrenderer.pdf.ITextRenderer; -import java.io.*; - /** * @author Yoav Aharoni */ public class PdfRendererImpl implements PdfRenderer { - private DocumentHolder documentHolder; + + private final DocumentHolder documentHolder; public PdfRendererImpl(DocumentHolder documentHolder) { this.documentHolder = documentHolder; @@ -51,4 +57,5 @@ public void saveToPDF(File file) { public void saveToPDF(String file) { saveToPDF(new File(file)); } + } diff --git a/html2image/src/main/java/gui/ava/html/renderer/FormatNameUtil.java b/html2image/src/main/java/gui/ava/html/renderer/FormatNameUtil.java deleted file mode 100644 index 6bd9d3b..0000000 --- a/html2image/src/main/java/gui/ava/html/renderer/FormatNameUtil.java +++ /dev/null @@ -1,41 +0,0 @@ -package gui.ava.html.renderer; - -import java.util.HashMap; -import java.util.Map; - -/** - * @author Yoav Aharoni - */ -public class FormatNameUtil { - public static Map types = new HashMap(); - private static final String DEFAULT_FORMAT = "png"; - - static { - types.put("gif", "gif"); - types.put("jpg", "jpg"); - types.put("jpeg", "jpg"); - types.put("png", "png"); - types.put("bmp", "bmp"); - } - - public static String formatForExtension(String extension) { - final String type = types.get(extension); - if (type == null) { - return DEFAULT_FORMAT; - } - return type; - } - - public static String getDefaultFormat() { - return DEFAULT_FORMAT; - } - - public static String formatForFilename(String fileName) { - final int dotIndex = fileName.lastIndexOf('.'); - if (dotIndex < 0) { - return DEFAULT_FORMAT; - } - final String ext = fileName.substring(dotIndex + 1); - return formatForExtension(ext); - } -} diff --git a/html2image/src/main/java/gui/ava/html/renderer/ImageRenderer.java b/html2image/src/main/java/gui/ava/html/renderer/ImageRenderer.java index e12ce31..7127f50 100644 --- a/html2image/src/main/java/gui/ava/html/renderer/ImageRenderer.java +++ b/html2image/src/main/java/gui/ava/html/renderer/ImageRenderer.java @@ -8,6 +8,7 @@ * @author Yoav Aharoni */ public interface ImageRenderer extends LayoutHolder { + int getWidth(); ImageRenderer setWidth(int width); diff --git a/html2image/src/main/java/gui/ava/html/renderer/ImageRendererImpl.java b/html2image/src/main/java/gui/ava/html/renderer/ImageRendererImpl.java index cc7c3ad..3245167 100644 --- a/html2image/src/main/java/gui/ava/html/renderer/ImageRendererImpl.java +++ b/html2image/src/main/java/gui/ava/html/renderer/ImageRendererImpl.java @@ -1,25 +1,37 @@ package gui.ava.html.renderer; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Set; + +import javax.imageio.ImageIO; +import javax.imageio.ImageWriteParam; + +import org.w3c.dom.Document; + import gui.ava.html.exception.RenderException; import gui.ava.html.parser.DocumentHolder; -import org.w3c.dom.Document; +import gui.ava.html.util.FormatNameUtil; import org.xhtmlrenderer.render.Box; import org.xhtmlrenderer.simple.Graphics2DRenderer; import org.xhtmlrenderer.util.FSImageWriter; -import javax.imageio.ImageWriteParam; -import java.awt.*; -import java.awt.image.BufferedImage; -import java.io.*; - /** * @author Yoav Aharoni */ public class ImageRendererImpl implements ImageRenderer { + + private static final Set IMAGE_FORMAT_WITH_ALPHA = Set.of("gif","png"); + public static final int DEFAULT_WIDTH = 1024; public static final int DEFAULT_HEIGHT = 768; - private DocumentHolder documentHolder; + private final DocumentHolder documentHolder; private int width = DEFAULT_WIDTH; private int height = DEFAULT_HEIGHT; @@ -190,31 +202,34 @@ public void saveImage(String filename) { private void save(OutputStream outputStream, String filename, boolean closeStream) { try { - final String imageFormat = getImageFormat(filename); - final FSImageWriter imageWriter = getImageWriter(imageFormat); - final boolean isBMP = "bmp".equalsIgnoreCase(imageFormat); - final BufferedImage bufferedImage = getBufferedImage(isBMP ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB); - imageWriter.write(bufferedImage, outputStream); - } catch (IOException e) { + final String imageFormat = FormatNameUtil.formatForFilename(filename); + final boolean hasAlpha = IMAGE_FORMAT_WITH_ALPHA.contains(imageFormat); + final BufferedImage bufferedImage = getBufferedImage(hasAlpha ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB); + + // note: if you use the FSImageWriter the output stream will always get closed! + // final FSImageWriter imageWriter = getImageWriter(imageFormat); + // imageWriter.write(bufferedImage, outputStream); + + ImageIO.write(bufferedImage, imageFormat, outputStream); + } + catch (IOException e) { throw new RenderException("IOException while rendering image", e); - } finally { + } + finally { if (closeStream) { try { outputStream.close(); - } catch (IOException ignore) { } + catch (IOException ignore) {} } } } - private FSImageWriter getImageWriter(String imageFormat) { - FSImageWriter imageWriter = new FSImageWriter(imageFormat); - imageWriter.setWriteCompressionMode(writeCompressionMode); - imageWriter.setWriteCompressionQuality(writeCompressionQuality); - imageWriter.setWriteCompressionType(writeCompressionType); - return imageWriter; - } - + /** + * note: do not cache the format, otherwise multiple calls will return the same value! :) + * @deprecated use {@link FormatNameUtil#formatForFilename(String)} + */ + @Deprecated private String getImageFormat(String filename) { if (this.imageFormat != null) { return imageFormat; @@ -222,6 +237,17 @@ private String getImageFormat(String filename) { if (filename != null) { return FormatNameUtil.formatForFilename(filename); } - return FormatNameUtil.getDefaultFormat(); + return FormatNameUtil.DEFAULT_FORMAT; } + + private FSImageWriter getImageWriter(String imageFormat) { + // todo: the new version can't be customized ... + // try to subclass it and override the getImageWriteParameters method + FSImageWriter imageWriter = new FSImageWriter(imageFormat); +// imageWriter.setWriteCompressionMode(writeCompressionMode); +// imageWriter.setWriteCompressionQuality(writeCompressionQuality); +// imageWriter.setWriteCompressionType(writeCompressionType); + return imageWriter; + } + } \ No newline at end of file diff --git a/html2image/src/main/java/gui/ava/html/util/FormatNameUtil.java b/html2image/src/main/java/gui/ava/html/util/FormatNameUtil.java new file mode 100644 index 0000000..cb1fe57 --- /dev/null +++ b/html2image/src/main/java/gui/ava/html/util/FormatNameUtil.java @@ -0,0 +1,41 @@ + +package gui.ava.html.util; + +import java.util.Map; +import java.util.Objects; + +/** + * @author Yoav Aharoni + */ +public enum FormatNameUtil { ; + + public static final String DEFAULT_FORMAT = "png"; + + public static final Map types = Map.of( + "gif", "gif", + "jpg", "jpg", + "jpeg", "jpg", + "png", "png", + "bmp", "bmp" + ); + + public static String formatForExtension(String extension) { + Objects.requireNonNull(extension); + final String type = types.get(extension); + if (type == null) { + return DEFAULT_FORMAT; + } + return type; + } + + public static String formatForFilename(String fileName) { + if (fileName == null) return DEFAULT_FORMAT; + final int dotIndex = fileName.lastIndexOf('.'); + if (dotIndex < 0) { + return DEFAULT_FORMAT; + } + final String ext = fileName.substring(dotIndex + 1); + return formatForExtension(ext); + } + +} diff --git a/html2image/src/test/java/gui/ava/html/BaseTest.java b/html2image/src/test/java/gui/ava/html/BaseTest.java index 8d7dcd4..4053951 100644 --- a/html2image/src/test/java/gui/ava/html/BaseTest.java +++ b/html2image/src/test/java/gui/ava/html/BaseTest.java @@ -1,21 +1,125 @@ package gui.ava.html; -import org.springframework.util.ResourceUtils; - +import java.io.File; import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.net.URISyntaxException; import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.logging.Logger; /** * @author Yoav Aharoni */ public class BaseTest { - public static final String TEST1_PATH = "classpath:test1.html"; + + private static final String PROJECT_NAME = "html2image"; + private static final String TEST_OUTPUT_PATH = "./output"; + + static { + try { + Files.createDirectories(getPath(TEST_OUTPUT_PATH)); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + public static File getTestOutputFile(String filename) { + return new File( getFile(TEST_OUTPUT_PATH), filename ); + } + + private static final String TEST1_PATH = "test1.html"; public static URL getTest1Url() { try { - return ResourceUtils.getURL(TEST1_PATH); + return getURL(TEST1_PATH); } catch (FileNotFoundException e) { throw new RuntimeException(e); } } + + public static URL getURL(String filename) throws FileNotFoundException { + URL url = Thread.currentThread().getContextClassLoader().getResource(filename); + if (url == null) throw new FileNotFoundException(filename+" not found"); + return url; + } + + public static File getTest1File() throws FileNotFoundException { + return getTestResourceFile(TEST1_PATH); + } + + public static File getTestResourceFile(String filename) throws FileNotFoundException { + try { + File input = getResourceFile(filename); + if (input.exists()) return input; + } + catch (URISyntaxException e) { + throw new IllegalArgumentException(e); + } + throw new FileNotFoundException(filename); + } + + + public static String getBasePath() { + try { + String sourcePath = getURL(".").getPath(); + String basePath = sourcePath.substring(0, sourcePath.lastIndexOf(PROJECT_NAME)-1); + return basePath; + } + catch (FileNotFoundException e) { + Logger.getLogger(BaseTest.class.getName()).warning("base path not found, fallback to 'user.dir' system property"); + return getUserProjectRootDirectory().toString(); + } + } + + public static Path getUserProjectRootDirectory() { + String envRootDir = System.getProperty("user.dir"); + Path rootDir = Paths.get(".").normalize().toAbsolutePath(); + if ( rootDir.startsWith(envRootDir) ) { + return rootDir; + } else { + throw new RuntimeException("Root dir not found in user directory."); + } + } + + + private static File getResourceFile(String filename) throws FileNotFoundException, URISyntaxException { + return new File(getURL(filename).toURI()); + } + + private static File getFile(String filename) { + return new File(getBasePath(), filename); + } + + private static Path getPath(String filename) { + return Path.of(getBasePath(), filename); + } + + + +// public static void main(String[] args) throws FileNotFoundException { +//// final File sourceFile = new File(BaseTest.class.getProtectionDomain().getCodeSource().getLocation().toString()); +//// System.out.println(sourceFile); +//// System.out.println(BaseTest.class.getClassLoader().getResource(TEST1_PATH)); +//// System.out.println(Thread.currentThread().getContextClassLoader().getResource(TEST1_PATH)); +// +//// final String packagePath = BaseTest.class.getPackageName().replace('.', '/'); +//// final String fullPath = BaseTest.class.getResource("./").toString(); +//// final String basePath = fullPath.substring(0, (fullPath.length()-packagePath.length()-1) ); +//// System.out.println(packagePath); +//// System.out.println(fullPath); +//// System.out.println(basePath); +// +//// String sourcePath = getURL(".").getPath(); +//// String basePath = sourcePath.substring(0, sourcePath.lastIndexOf(PROJECT_NAME)); +//// System.out.println(sourcePath); +//// System.out.println(basePath); +// +// System.out.println(getBasePath()); +// System.out.println(getUsersProjectRootDirectory()); +// } + } diff --git a/html2image/src/test/java/gui/ava/html/Example.java b/html2image/src/test/java/gui/ava/html/Example.java index 169a00a..287f8ff 100644 --- a/html2image/src/test/java/gui/ava/html/Example.java +++ b/html2image/src/test/java/gui/ava/html/Example.java @@ -1,17 +1,37 @@ package gui.ava.html; -import gui.ava.html.image.*; +import gui.ava.html.image.HtmlImageGenerator; /** * Created by hki on 07-01-2016. */ -public class Example { +public class Example extends BaseTest { + + private static final String html = """ + + + + + +

Hello World!

+
+ Please goto Google. +
+ + + """; public static void main(String[] args) { HtmlImageGenerator imageGenerator = new HtmlImageGenerator(); - imageGenerator.loadHtml("Hello World! Please goto Google."); - imageGenerator.saveAsImage("hello-world.png"); - imageGenerator.saveAsImage("hello-world.jpeg"); - //imageGenerator.saveAsHtmlWithMap("hello-world.html", "hello-world.png"); + imageGenerator.loadHtml(html); + imageGenerator.saveAsImage( getTestOutputFile("hello-world.jpg") ); +// imageGenerator.saveAsImage("hello-world.png"); +// imageGenerator.saveAsImage("hello-world.jpg"); +// imageGenerator.saveAsHtmlWithMap("hello-world.html", "hello-world.png"); } + } diff --git a/html2image/src/test/java/gui/ava/html/WordWrapExample.java b/html2image/src/test/java/gui/ava/html/WordWrapExample.java index c2a4a72..7eae61d 100644 --- a/html2image/src/test/java/gui/ava/html/WordWrapExample.java +++ b/html2image/src/test/java/gui/ava/html/WordWrapExample.java @@ -2,37 +2,38 @@ import gui.ava.html.image.HtmlImageGenerator; -import java.awt.*; - /** * Created by hki on 12-01-2016. */ -public class WordWrapExample { - public static void main(String[] args) { - HtmlImageGenerator imageGenerator = new HtmlImageGenerator(); - imageGenerator.loadHtml("\n" + - "\n" + - "Hello World! Please goto Google.\n" + - "
" + - "word wrap all " + - "" + - "llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll" + - "" + - "
" + +public class WordWrapExample extends BaseTest { - "
" + - "word wrap word " + - "" + - "wwwwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwww wwwwwwwwwwwwwww w wwwwwwwwwwwwwwwwwwww w wwwwwwwwwwwwwwww w wwwwwwwwwwwwwwwwwwwwww w w w wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwww wwwwwwwwwwwwwww w wwwwwwwwwwwwwwwwwwww w wwwwwwwwwwwwwwww w wwwwwwwwwwwwwwwwwwwwww w w w wwwwwwwwwwwwwwwwwwwwwww" + - "" + - "
" + + private static final String html = """ + + + Hello World! Please goto Google. +
+ word wrap all + + llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll + +
+
+ word wrap word + + wwwwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwww wwwwwwwwwwwwwww w wwwwwwwwwwwwwwwwwwww w wwwwwwwwwwwwwwww w wwwwwwwwwwwwwwwwwwwwww w w w wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwww wwwwwwwwwwwwwww w wwwwwwwwwwwwwwwwwwww w wwwwwwwwwwwwwwww w wwwwwwwwwwwwwwwwwwwwww w w w wwwwwwwwwwwwwwwwwwwwwww + +
+ + + """; - "\n" + - "\n" + - ""); - //imageGenerator.saveAsImage("hello-form.png"); - //imageGenerator.setSize(new Dimension(150, 600)); - //imageGenerator.saveAsImage("word-wrap.jpeg"); + public static void main(String[] args) { + HtmlImageGenerator imageGenerator = new HtmlImageGenerator(); + imageGenerator.loadHtml(html); + // imageGenerator.saveAsImage("hello-form.png"); + // imageGenerator.setSize(new Dimension(150, 600)); + imageGenerator.saveAsImage(getTestOutputFile("word-wrap.jpg")); imageGenerator.show(); } + } diff --git a/html2image/src/test/java/gui/ava/html/imagemap/HtmlImageMapImplTest.java b/html2image/src/test/java/gui/ava/html/imagemap/HtmlImageMapImplTest.java index 3be3c82..184e65b 100644 --- a/html2image/src/test/java/gui/ava/html/imagemap/HtmlImageMapImplTest.java +++ b/html2image/src/test/java/gui/ava/html/imagemap/HtmlImageMapImplTest.java @@ -1,41 +1,42 @@ package gui.ava.html.imagemap; +import java.net.URL; + import gui.ava.html.BaseTest; import gui.ava.html.Html2Image; -import org.junit.Test; - -import java.net.URL; +import org.junit.jupiter.api.Test; /** * @author Yoav Aharoni */ public class HtmlImageMapImplTest extends BaseTest { + @Test public void test1ImageMapDocument() throws Exception { final Html2Image html2Image = Html2Image.fromURL(getTest1Url()); - html2Image.getImageRenderer().saveImage("test1.png"); - html2Image.getHtmlImageMap().saveImageMapDocument("test1.html", "test1.png"); + html2Image.getImageRenderer().saveImage(getTestOutputFile("test1.png")); + html2Image.getHtmlImageMap().saveImageMapDocument(getTestOutputFile("test1.html"), "test1.png"); } - @Test public void googleImageMapDocument() throws Exception { - final Html2Image html2Image = Html2Image.fromURL(new URL("http://www.google.com")); - html2Image.getImageRenderer().saveImage("google.png"); - html2Image.getHtmlImageMap().saveImageMapDocument("google.html", "google.png"); + final Html2Image html2Image = Html2Image.fromURL(new URL("https://www.google.com")); + html2Image.getImageRenderer().saveImage(getTestOutputFile("google.png")); + html2Image.getHtmlImageMap().saveImageMapDocument(getTestOutputFile("google.html"), "google.png"); } @Test public void hebImageMapDocument() throws Exception { final Html2Image html2Image = Html2Image.fromHtml(""); - html2Image.getImageRenderer().saveImage("heb.png"); - html2Image.getHtmlImageMap().saveImageMapDocument("heb.html", "heb.png"); + html2Image.getImageRenderer().saveImage(getTestOutputFile("heb.png")); + html2Image.getHtmlImageMap().saveImageMapDocument(getTestOutputFile("heb.html"), "heb.png"); } @Test public void imageImageMapDocument() throws Exception { - final Html2Image html2Image = Html2Image.fromHtml("
HELLO!
"); - html2Image.getImageRenderer().saveImage("image.png"); - html2Image.getHtmlImageMap().saveImageMapDocument("image.html", "heb.png"); + final Html2Image html2Image = Html2Image.fromHtml("
HELLO!
"); + html2Image.getImageRenderer().saveImage(getTestOutputFile("image.png")); + html2Image.getHtmlImageMap().saveImageMapDocument(getTestOutputFile("image.html"), "heb.png"); } + } diff --git a/html2image/src/test/java/gui/ava/html/parser/HtmlParserImplTest.java b/html2image/src/test/java/gui/ava/html/parser/HtmlParserImplTest.java index 3ce778e..2750a4e 100644 --- a/html2image/src/test/java/gui/ava/html/parser/HtmlParserImplTest.java +++ b/html2image/src/test/java/gui/ava/html/parser/HtmlParserImplTest.java @@ -1,25 +1,27 @@ package gui.ava.html.parser; -import gui.ava.html.BaseTest; -import org.junit.Before; -import org.junit.Test; -import org.springframework.util.ResourceUtils; -import org.w3c.dom.Document; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.FileInputStream; import java.io.FileReader; -import java.net.URL; +import java.net.URI; + +import org.w3c.dom.Document; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import gui.ava.html.BaseTest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** * @author Yoav Aharoni */ public class HtmlParserImplTest extends BaseTest { + private HtmlParserImpl parser; - @Before + @BeforeEach public void createParser() { parser = new HtmlParserImpl(); } @@ -32,25 +34,25 @@ public void testLoadURI() throws Exception { @Test public void testLoadExternalURL() throws Exception { - parser.load(new URL("http://www.google.co.il")); + parser.load(new URI("https://www.google.co.il").toURL()); assertTrue(getDocument().getElementsByTagName("div").getLength() > 0); } @Test public void testLoadFile() throws Exception { - parser.load(ResourceUtils.getFile(TEST1_PATH)); + parser.load(getTest1Url()); assertTest1(); } @Test public void testLoadReader() throws Exception { - parser.load(new FileReader(ResourceUtils.getFile(TEST1_PATH))); + parser.load(new FileReader(getTest1File())); assertTest1(); } @Test public void testLoadInputStream() throws Exception { - parser.load(new FileInputStream(ResourceUtils.getFile(TEST1_PATH))); + parser.load(new FileInputStream(getTest1File())); assertTest1(); } @@ -67,4 +69,5 @@ private void assertTest1() { private Document getDocument() { return parser.getDocument(); } + } diff --git a/html2image/src/test/java/gui/ava/html/pdf/PdfRendererImplTest.java b/html2image/src/test/java/gui/ava/html/pdf/PdfRendererImplTest.java index dc90408..65fda78 100644 --- a/html2image/src/test/java/gui/ava/html/pdf/PdfRendererImplTest.java +++ b/html2image/src/test/java/gui/ava/html/pdf/PdfRendererImplTest.java @@ -2,14 +2,16 @@ import gui.ava.html.BaseTest; import gui.ava.html.Html2Image; -import org.junit.Test; +import org.junit.jupiter.api.Test; /** * @author Yoav Aharoni */ public class PdfRendererImplTest extends BaseTest { + @Test - public void testSaveToPDF() throws Exception { - Html2Image.fromURL(getTest1Url()).getPdfRenderer().saveToPDF("test.pdf"); + public void testSaveToPDF() { + Html2Image.fromURL(getTest1Url()).getPdfRenderer().saveToPDF(getTestOutputFile("test.pdf")); } + } diff --git a/html2image/src/test/java/gui/ava/html/renderer/FormatNameUtilTest.java b/html2image/src/test/java/gui/ava/html/renderer/FormatNameUtilTest.java index 3705c10..5cd1e27 100644 --- a/html2image/src/test/java/gui/ava/html/renderer/FormatNameUtilTest.java +++ b/html2image/src/test/java/gui/ava/html/renderer/FormatNameUtilTest.java @@ -1,45 +1,48 @@ package gui.ava.html.renderer; -import org.junit.Assert; -import org.junit.Test; +import gui.ava.html.util.FormatNameUtil; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; /** * @author Yoav Aharoni */ public class FormatNameUtilTest { + @Test public void testGif() { final String format = FormatNameUtil.formatForFilename("test.file.gif"); - Assert.assertEquals("gif", format); + Assertions.assertEquals("gif", format); } @Test public void testPng() { final String format = FormatNameUtil.formatForFilename("test.file.png"); - Assert.assertEquals("png", format); + Assertions.assertEquals("png", format); } @Test public void testJpg() { final String format = FormatNameUtil.formatForFilename("test.file.jpg"); - Assert.assertEquals("jpg", format); + Assertions.assertEquals("jpg", format); } @Test public void testNoName() { final String format = FormatNameUtil.formatForFilename(".gif"); - Assert.assertEquals("gif", format); + Assertions.assertEquals("gif", format); } @Test public void testNoExtension() { final String format = FormatNameUtil.formatForFilename("name."); - Assert.assertEquals("png", format); + Assertions.assertEquals("png", format); } @Test public void testEmptyFilename() { final String format = FormatNameUtil.formatForFilename(""); - Assert.assertEquals("png", format); + Assertions.assertEquals("png", format); } + } diff --git a/html2image/src/test/java/gui/ava/html/renderer/ImageRendererImplTest.java b/html2image/src/test/java/gui/ava/html/renderer/ImageRendererImplTest.java index 4f41ddd..b6a9178 100644 --- a/html2image/src/test/java/gui/ava/html/renderer/ImageRendererImplTest.java +++ b/html2image/src/test/java/gui/ava/html/renderer/ImageRendererImplTest.java @@ -1,20 +1,21 @@ package gui.ava.html.renderer; +import java.io.FileOutputStream; + import gui.ava.html.BaseTest; import gui.ava.html.parser.HtmlParserImpl; -import org.junit.Before; -import org.junit.Test; - -import java.io.FileOutputStream; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** * @author Yoav Aharoni */ public class ImageRendererImplTest extends BaseTest { + private HtmlParserImpl parser; private ImageRendererImpl renderer; - @Before + @BeforeEach public void createParser() { parser = new HtmlParserImpl(); renderer = new ImageRendererImpl(parser); @@ -23,15 +24,16 @@ public void createParser() { @Test public void testSaveStream() throws Exception { parser.load(getTest1Url()); - renderer.saveImage(new FileOutputStream("file1.png"), true); + renderer.saveImage(new FileOutputStream(getTestOutputFile("file1.png")), true); } @Test public void testSaveFile() throws Exception { parser.load(getTest1Url()); - renderer.saveImage("test.gif"); - renderer.saveImage("test.png"); - renderer.saveImage("test.jpg"); - renderer.saveImage("test.bmp"); + renderer.saveImage(getTestOutputFile("test.gif")); + renderer.saveImage(getTestOutputFile("test.png")); + renderer.saveImage(getTestOutputFile("test.jpg")); + renderer.saveImage(getTestOutputFile("test.bmp")); } + } From e02d703f00c219343997f933c49f1fccda40754c Mon Sep 17 00:00:00 2001 From: pizzi80 Date: Wed, 28 Aug 2024 22:08:31 +0200 Subject: [PATCH 2/9] do not define maven compiler plugin version otherwise jitpack.io do not compile Signed-off-by: pizzi80 --- html2image/pom.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/html2image/pom.xml b/html2image/pom.xml index e278fa5..a329f50 100644 --- a/html2image/pom.xml +++ b/html2image/pom.xml @@ -76,7 +76,7 @@ - +
From 931960232fd5894e0e67f29b042468cd8bf4571b Mon Sep 17 00:00:00 2001 From: pizzi80 Date: Wed, 28 Aug 2024 22:29:51 +0200 Subject: [PATCH 3/9] jitpack.yml Signed-off-by: pizzi80 --- html2image/jitpack.yml | 7 +++++++ html2image/pom.xml | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 html2image/jitpack.yml diff --git a/html2image/jitpack.yml b/html2image/jitpack.yml new file mode 100644 index 0000000..57aaa34 --- /dev/null +++ b/html2image/jitpack.yml @@ -0,0 +1,7 @@ +jdk: + - openjdk17 +before_install: + - sdk install java 17.0.12-tem + - sdk use java 17.0.12-tem + - sdk install maven + - mvn -v diff --git a/html2image/pom.xml b/html2image/pom.xml index a329f50..d99c0e4 100644 --- a/html2image/pom.xml +++ b/html2image/pom.xml @@ -76,7 +76,7 @@ - @@ -91,6 +91,6 @@ - --> + From b77092c95ce530a61a9d768c559599f02b1e021c Mon Sep 17 00:00:00 2001 From: pizzi80 Date: Wed, 28 Aug 2024 22:36:29 +0200 Subject: [PATCH 4/9] jitpack.yml Signed-off-by: pizzi80 --- html2image/jitpack.yml | 6 +----- html2image/pom.xml | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/html2image/jitpack.yml b/html2image/jitpack.yml index 57aaa34..bbaedec 100644 --- a/html2image/jitpack.yml +++ b/html2image/jitpack.yml @@ -1,7 +1,3 @@ jdk: - openjdk17 -before_install: - - sdk install java 17.0.12-tem - - sdk use java 17.0.12-tem - - sdk install maven - - mvn -v + diff --git a/html2image/pom.xml b/html2image/pom.xml index d99c0e4..a329f50 100644 --- a/html2image/pom.xml +++ b/html2image/pom.xml @@ -76,7 +76,7 @@ - + From 33c062f8d0adbe433e973138b649eaa91ea957fc Mon Sep 17 00:00:00 2001 From: pizzi80 Date: Wed, 28 Aug 2024 22:41:33 +0200 Subject: [PATCH 5/9] jitpack.yml Signed-off-by: pizzi80 --- html2image/jitpack.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/html2image/jitpack.yml b/html2image/jitpack.yml index bbaedec..6d5ed3c 100644 --- a/html2image/jitpack.yml +++ b/html2image/jitpack.yml @@ -1,3 +1,6 @@ jdk: - openjdk17 - +before_install: + - sdk list java + - sdk install java 17.0.1-open + - sdk use java 17.0.1-open From 2612b8e785f5a972a68d7988fa111a402c7213c0 Mon Sep 17 00:00:00 2001 From: pizzi80 Date: Wed, 28 Aug 2024 22:53:03 +0200 Subject: [PATCH 6/9] jitpack.yml Signed-off-by: pizzi80 --- html2image/jitpack.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/html2image/jitpack.yml b/html2image/jitpack.yml index 6d5ed3c..3263cc2 100644 --- a/html2image/jitpack.yml +++ b/html2image/jitpack.yml @@ -2,5 +2,5 @@ jdk: - openjdk17 before_install: - sdk list java - - sdk install java 17.0.1-open - - sdk use java 17.0.1-open + - sdk install java 22-open + - sdk use java 22-open From 49daaee5d0f9488fe411f8586d3f00979f159bc5 Mon Sep 17 00:00:00 2001 From: pizzi80 Date: Wed, 28 Aug 2024 22:56:03 +0200 Subject: [PATCH 7/9] jitpack.yml Signed-off-by: pizzi80 --- html2image/jitpack.yml => jitpack.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename html2image/jitpack.yml => jitpack.yml (100%) diff --git a/html2image/jitpack.yml b/jitpack.yml similarity index 100% rename from html2image/jitpack.yml rename to jitpack.yml From 44216e545b7ffb48708024aab407493a1603b0c5 Mon Sep 17 00:00:00 2001 From: pizzi80 Date: Thu, 29 Aug 2024 19:44:24 +0200 Subject: [PATCH 8/9] - Revert to NekoHtml 1.9.x - Upgrade to NekoHtml 1.9.22 - CustomizableFSImageWriter extends the default FS Image writer - Added "html5" test Signed-off-by: pizzi80 --- html2image/pom.xml | 21 ++++- .../ava/html/image/HtmlImageGenerator.java | 5 +- .../java/gui/ava/html/link/LinkHarvester.java | 83 +++++++++++++++++ .../java/gui/ava/html/parser/HtmlParser.java | 2 +- .../gui/ava/html/parser/HtmlParserImpl.java | 7 +- .../renderer/CustomizableFSImageWriter.java | 93 +++++++++++++++++++ .../ava/html/renderer/ImageRendererImpl.java | 20 +--- .../src/test/java/gui/ava/html/Example.java | 2 +- .../html/renderer/ImageRendererImplTest.java | 23 +++++ html2image/src/test/resources/html5.html | 12 +++ 10 files changed, 241 insertions(+), 27 deletions(-) create mode 100644 html2image/src/main/java/gui/ava/html/link/LinkHarvester.java create mode 100644 html2image/src/main/java/gui/ava/html/renderer/CustomizableFSImageWriter.java create mode 100644 html2image/src/test/resources/html5.html diff --git a/html2image/pom.xml b/html2image/pom.xml index a329f50..f00bfc3 100644 --- a/html2image/pom.xml +++ b/html2image/pom.xml @@ -28,12 +28,27 @@ + - org.htmlunit - neko-htmlunit - 4.4.0 + net.sourceforge.nekohtml + nekohtml + 1.9.22 + + + + + + + + + + + + + + diff --git a/html2image/src/main/java/gui/ava/html/image/HtmlImageGenerator.java b/html2image/src/main/java/gui/ava/html/image/HtmlImageGenerator.java index 0db755d..9620966 100644 --- a/html2image/src/main/java/gui/ava/html/image/HtmlImageGenerator.java +++ b/html2image/src/main/java/gui/ava/html/image/HtmlImageGenerator.java @@ -63,8 +63,8 @@ public void loadUrl(String url) { public void loadHtml(String html) { editorPane.setEditable(false); - editorPane.setText(html); editorPane.setContentType("text/html"); + editorPane.setText(html); onDocumentLoad(); } @@ -136,8 +136,7 @@ public void saveAsImage(File file) { } } - protected void onDocumentLoad() { - } + protected void onDocumentLoad() {} public Dimension getDefaultSize() { return DEFAULT_SIZE; diff --git a/html2image/src/main/java/gui/ava/html/link/LinkHarvester.java b/html2image/src/main/java/gui/ava/html/link/LinkHarvester.java new file mode 100644 index 0000000..7d65ba9 --- /dev/null +++ b/html2image/src/main/java/gui/ava/html/link/LinkHarvester.java @@ -0,0 +1,83 @@ +package gui.ava.html.link; + +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; + +import javax.swing.*; +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.Element; +import javax.swing.text.JTextComponent; +import javax.swing.text.SimpleAttributeSet; +import javax.swing.text.html.HTML; + +/** + * @author Yoav Aharoni + */ +public class LinkHarvester { + + private final JTextComponent textComponent; + private final List links = new ArrayList<>(); + + public LinkHarvester(JEditorPane textComponent) { + this.textComponent = textComponent; + harvestElement(textComponent.getDocument().getDefaultRootElement()); + } + + public List getLinks() { + return links; + } + + private void harvestElement(Element element) { + if (element == null) { + return; + } + + final AttributeSet attributes = element.getAttributes(); + final Enumeration attributeNames = attributes.getAttributeNames(); + while (attributeNames.hasMoreElements()) { + final Object key = attributeNames.nextElement(); + if (HTML.Tag.A.equals(key)) { + final Object value = attributes.getAttribute(key); + if (value instanceof SimpleAttributeSet attributeSet) { + final String href = (String) attributeSet.getAttribute(HTML.Attribute.HREF); + final String title = (String) attributeSet.getAttribute(HTML.Attribute.TITLE); + final List bounds = elementBounds(element); + links.add(new LinkInfo(href, title, bounds)); + } + } + } + + for (int i = 0; i < element.getElementCount(); i++) { + final Element child = element.getElement(i); + harvestElement(child); + } + } + + private List elementBounds(Element element) { + final List bounds = new ArrayList<>(); + try { + final int startOffset = element.getStartOffset(); + final int endOffset = element.getEndOffset(); + Rectangle2D rectangle = textComponent.modelToView2D(startOffset); + for (int i = startOffset + 1; i <= endOffset; i++) { + final Rectangle2D temp = textComponent.modelToView2D(i); + if (temp.getY() == rectangle.getY()) { + Rectangle2D.union(rectangle, temp, rectangle); + } else { + bounds.add(rectangle); + rectangle = null; + } + } + if (rectangle != null) { + bounds.add(rectangle); + } + return bounds; + } catch (BadLocationException e) { + throw new RuntimeException("Got BadLocationException", e); + } + } + +} diff --git a/html2image/src/main/java/gui/ava/html/parser/HtmlParser.java b/html2image/src/main/java/gui/ava/html/parser/HtmlParser.java index 8bab1cb..c929245 100644 --- a/html2image/src/main/java/gui/ava/html/parser/HtmlParser.java +++ b/html2image/src/main/java/gui/ava/html/parser/HtmlParser.java @@ -8,7 +8,7 @@ import org.w3c.dom.Document; -import org.htmlunit.cyberneko.parsers.DOMParser; +import org.apache.xerces.parsers.DOMParser; /** * @author Yoav Aharoni diff --git a/html2image/src/main/java/gui/ava/html/parser/HtmlParserImpl.java b/html2image/src/main/java/gui/ava/html/parser/HtmlParserImpl.java index 6bb5679..13c2102 100644 --- a/html2image/src/main/java/gui/ava/html/parser/HtmlParserImpl.java +++ b/html2image/src/main/java/gui/ava/html/parser/HtmlParserImpl.java @@ -16,8 +16,8 @@ import org.xml.sax.SAXNotRecognizedException; import org.xml.sax.SAXNotSupportedException; -import org.htmlunit.cyberneko.html.dom.HTMLDocumentImpl; -import org.htmlunit.cyberneko.parsers.DOMParser; +import org.apache.xerces.parsers.DOMParser; +import org.cyberneko.html.HTMLConfiguration; /** * @author Yoav Aharoni @@ -29,7 +29,8 @@ public class HtmlParserImpl implements HtmlParser { public HtmlParserImpl() { try { - domParser = new DOMParser(HTMLDocumentImpl.class); + domParser = new DOMParser(new HTMLConfiguration()); // HtmlUnit 1.9.x + // domParser = new DOMParser(HTMLDocumentImpl.class); // HtmlUnit 4.x domParser.setProperty("http://cyberneko.org/html/properties/names/elems", "lower"); } catch (SAXNotRecognizedException | SAXNotSupportedException e) { diff --git a/html2image/src/main/java/gui/ava/html/renderer/CustomizableFSImageWriter.java b/html2image/src/main/java/gui/ava/html/renderer/CustomizableFSImageWriter.java new file mode 100644 index 0000000..68ffbcc --- /dev/null +++ b/html2image/src/main/java/gui/ava/html/renderer/CustomizableFSImageWriter.java @@ -0,0 +1,93 @@ +package gui.ava.html.renderer; + +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Iterator; + +import javax.imageio.IIOImage; +import javax.imageio.ImageIO; +import javax.imageio.ImageWriteParam; +import javax.imageio.ImageWriter; +import javax.imageio.stream.ImageOutputStream; + +import org.xhtmlrenderer.util.FSImageWriter; + +/** + * Flying Saucer 9.x forgot to create a constructor to set the compression parameters! + */ +public class CustomizableFSImageWriter extends FSImageWriter { + + private final String imageFormat; + private final int writeCompressionMode; + private final float writeCompressionQuality; + private final String writeCompressionType; + + public CustomizableFSImageWriter(String imageFormat, int writeCompressionMode, float writeCompressionQuality, String writeCompressionType) { + super(imageFormat); + this.imageFormat = imageFormat; + this.writeCompressionMode = writeCompressionMode; + this.writeCompressionQuality = writeCompressionQuality; + this.writeCompressionType = writeCompressionType; + } + + /** + * Write the passed image to the passed OutputStream. + * Close the output stream only if the closeStream param is true. + */ + public void write(BufferedImage image, OutputStream os, boolean closeStream) throws IOException { + ImageWriter writer = lookupImageWriterForFormat(this.imageFormat); + + try { + ImageOutputStream ios = ImageIO.createImageOutputStream(os); + + try { + writer.setOutput(ios); + ImageWriteParam parameters = getImageWriteParameters(writer); + writer.write(null, new IIOImage(image, null, null), parameters); + ios.flush(); + } + catch (Throwable t) { + if (closeStream && ios != null) { + try { + ios.close(); + } catch (Throwable t2) { + t.addSuppressed(t2); + } + } + + throw t; + } + + if (closeStream && ios != null) { + ios.close(); + } + } finally { + writer.dispose(); + } + } + + @Override + protected ImageWriteParam getImageWriteParameters(ImageWriter writer) { + ImageWriteParam param = writer.getDefaultWriteParam(); + if (param.canWriteCompressed() && this.writeCompressionMode != 3) { + param.setCompressionMode(this.writeCompressionMode); + if (this.writeCompressionMode == 2) { + param.setCompressionType(this.writeCompressionType); + param.setCompressionQuality(this.writeCompressionQuality); + } + } + + return param; + } + + private static ImageWriter lookupImageWriterForFormat(String imageFormat) { + Iterator iter = ImageIO.getImageWritersByFormatName(imageFormat); + if (iter.hasNext()) { + return iter.next(); + } else { + throw new IllegalArgumentException("Image writer not found for format " + imageFormat); + } + } + +} diff --git a/html2image/src/main/java/gui/ava/html/renderer/ImageRendererImpl.java b/html2image/src/main/java/gui/ava/html/renderer/ImageRendererImpl.java index 3245167..40b1fb1 100644 --- a/html2image/src/main/java/gui/ava/html/renderer/ImageRendererImpl.java +++ b/html2image/src/main/java/gui/ava/html/renderer/ImageRendererImpl.java @@ -9,7 +9,6 @@ import java.io.OutputStream; import java.util.Set; -import javax.imageio.ImageIO; import javax.imageio.ImageWriteParam; import org.w3c.dom.Document; @@ -19,7 +18,6 @@ import gui.ava.html.util.FormatNameUtil; import org.xhtmlrenderer.render.Box; import org.xhtmlrenderer.simple.Graphics2DRenderer; -import org.xhtmlrenderer.util.FSImageWriter; /** * @author Yoav Aharoni @@ -205,12 +203,8 @@ private void save(OutputStream outputStream, String filename, boolean closeStrea final String imageFormat = FormatNameUtil.formatForFilename(filename); final boolean hasAlpha = IMAGE_FORMAT_WITH_ALPHA.contains(imageFormat); final BufferedImage bufferedImage = getBufferedImage(hasAlpha ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB); - - // note: if you use the FSImageWriter the output stream will always get closed! - // final FSImageWriter imageWriter = getImageWriter(imageFormat); - // imageWriter.write(bufferedImage, outputStream); - - ImageIO.write(bufferedImage, imageFormat, outputStream); + final CustomizableFSImageWriter imageWriter = getImageWriter(imageFormat); + imageWriter.write(bufferedImage, outputStream, closeStream); } catch (IOException e) { throw new RenderException("IOException while rendering image", e); @@ -240,14 +234,8 @@ private String getImageFormat(String filename) { return FormatNameUtil.DEFAULT_FORMAT; } - private FSImageWriter getImageWriter(String imageFormat) { - // todo: the new version can't be customized ... - // try to subclass it and override the getImageWriteParameters method - FSImageWriter imageWriter = new FSImageWriter(imageFormat); -// imageWriter.setWriteCompressionMode(writeCompressionMode); -// imageWriter.setWriteCompressionQuality(writeCompressionQuality); -// imageWriter.setWriteCompressionType(writeCompressionType); - return imageWriter; + private CustomizableFSImageWriter getImageWriter(String imageFormat) { + return new CustomizableFSImageWriter(imageFormat, writeCompressionMode, writeCompressionQuality, writeCompressionType); } } \ No newline at end of file diff --git a/html2image/src/test/java/gui/ava/html/Example.java b/html2image/src/test/java/gui/ava/html/Example.java index 287f8ff..d47ec76 100644 --- a/html2image/src/test/java/gui/ava/html/Example.java +++ b/html2image/src/test/java/gui/ava/html/Example.java @@ -8,13 +8,13 @@ public class Example extends BaseTest { private static final String html = """ +

Hello World!

diff --git a/html2image/src/test/java/gui/ava/html/renderer/ImageRendererImplTest.java b/html2image/src/test/java/gui/ava/html/renderer/ImageRendererImplTest.java index b6a9178..beea8f9 100644 --- a/html2image/src/test/java/gui/ava/html/renderer/ImageRendererImplTest.java +++ b/html2image/src/test/java/gui/ava/html/renderer/ImageRendererImplTest.java @@ -36,4 +36,27 @@ public void testSaveFile() throws Exception { renderer.saveImage(getTestOutputFile("test.bmp")); } + static final String html5 = """ + + + + + + + +

Hello html5 world!

+ + + """; + + + @Test + public void testHtml5() throws Exception { + //parser.loadHtml(html5); + parser.load( getURL("html5.html") ); + renderer.saveImage(getTestOutputFile("html5.jpg")); + } + } diff --git a/html2image/src/test/resources/html5.html b/html2image/src/test/resources/html5.html new file mode 100644 index 0000000..b939f56 --- /dev/null +++ b/html2image/src/test/resources/html5.html @@ -0,0 +1,12 @@ + + + + + + + +

Hello html5 world!

+ + \ No newline at end of file From 8faf292931ef6a3c1153654da78185a8cd095e35 Mon Sep 17 00:00:00 2001 From: pizzi80 Date: Thu, 29 Aug 2024 20:05:30 +0200 Subject: [PATCH 9/9] - Test for div with display inline-block Signed-off-by: pizzi80 --- .../html/renderer/ImageRendererImplTest.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/html2image/src/test/java/gui/ava/html/renderer/ImageRendererImplTest.java b/html2image/src/test/java/gui/ava/html/renderer/ImageRendererImplTest.java index beea8f9..8702957 100644 --- a/html2image/src/test/java/gui/ava/html/renderer/ImageRendererImplTest.java +++ b/html2image/src/test/java/gui/ava/html/renderer/ImageRendererImplTest.java @@ -59,4 +59,40 @@ public void testHtml5() throws Exception { renderer.saveImage(getTestOutputFile("html5.jpg")); } + + static final String html_inline_block = """ + + + + + + +

Inline Block divs

+
+
1
+
2
+
3
+
+ + + """; + + @Test + public void testInlineBlock() throws Exception { + parser.loadHtml(html_inline_block); + renderer.saveImage(getTestOutputFile("inline.png")); + } + }