How to replace a PDAnnotationLink's default action with PDActionJavaScript one? - java

I need to find all PDAnnotationLink in a document and replace their default actions with PDActionJavaScript ones.
The final goal is to make the link open in a new tab but for now I just want alert to be displayed on click.
Here is the test example:
import java.nio.file.Path;
import java.nio.file.Paths;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.interactive.action.PDAction;
import org.apache.pdfbox.pdmodel.interactive.action.PDActionJavaScript;
import org.apache.pdfbox.pdmodel.interactive.action.PDActionURI;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationLink;
import org.junit.Test;
/**
*
*/
public class PdfBoxTest {
#Test
public void testReplaceLinks() throws Exception {
String pathPrefix = ...path to pdf file with links...;
Path originalPdf = Paths.get(pathPrefix + "links_test.pdf");
PDDocument doc = PDDocument.load(originalPdf.toFile());
doc.getDocumentCatalog().getPages().forEach(page -> {
try {
page.getAnnotations().forEach(pdAnnotation -> {
if (pdAnnotation instanceof PDAnnotationLink) {
PDAnnotationLink link = (PDAnnotationLink) pdAnnotation;
PDAction action = link.getAction();
if (action instanceof PDActionURI) {
PDActionURI uri = (PDActionURI) action;
PDActionJavaScript javaScript =
new PDActionJavaScript("app.alert(\"" + uri.getURI() + "\");");
// actual goal of replacing the links is to open them in a new tab/window
// PDActionJavaScript javaScript =
// new PDActionJavaScript("app.launchURL(\"" + uri.getURI() + "\", true);");
link.setAction(javaScript);
}
}
});
} catch (Throwable t) {
throw new RuntimeException(t);
}
});
Path modifiedPdf = Paths.get(pathPrefix + "links_test_mod.pdf");
doc.save(modifiedPdf.toFile());
}
}
but the document just renders the link label which is not clickable (no alert is shown).
I'm sure I'm missing something here.
Here are the links to the PDF files.
Original:
https://www.dropbox.com/s/notwf6yde5okh7g/links_test_orig.pdf?dl=0
Modified:
https://www.dropbox.com/s/bejj6rawwjrbyyc/links_test_mod.pdf?dl=0

Related

How to debug JavaFX webview in 2022

How to debug a JavaFX webview?
I find two ways,
one is to attach external chrome debugger to JavaFX webview.
Another is to inject firebug javascript.
Method#1 seems to use a bit of hacks like set access to private fields etc.
Method#2 depends on firebuglite which seems to be obsolete now.
How should this be done in 2022. Are there any other methods, or better methods?
I am trying firebug lite approach as of now, and have made the javascript part of the maven project and trying to inject it as a string versus an external URL, because firebug seems to be properly public hosted nowhere now.
Refering to https://stackoverflow.com/a/18396900/2448015
This is where I am right now :
package xyz.jphil.internal_browser;
import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpServer;
import java.net.InetSocketAddress;
import javafx.scene.web.WebEngine;
import netscape.javascript.JSObject;
/**
*
* #author Ivan
*/
public class WebkitDebugUtils {
private static int resourcesPort = 0;
public static void createHttpServerForLocalFiles() {
int portStart = 64321, endPort = 65534;
resourcesPort = portStart + (int) ((endPort * 1d - portStart * 1d) * Math.random());
System.out.println("Starting internal server on " + resourcesPort);
try {
HttpServer server = HttpServer.create(new InetSocketAddress(resourcesPort), 0);
HttpContext context = server.createContext("/");
context.setHandler((req) -> {
try (req) {
var p = req.getRequestURI();
System.out.println("Serving # localhost:"+resourcesPort+" "+p);
var s = WebkitDebugUtils.class.getResourceAsStream(p.toString()).readAllBytes();
req.sendResponseHeaders(200, s.length);
req.getResponseBody().write(s);
}catch(Exception a){
a.printStackTrace();
}
});
server.start();
} catch (Exception e) {
e.printStackTrace();
}
}
public static class JavaBridge {
public void log(String text) {
System.out.println(text);
}
}
// Maintain a strong reference to prevent garbage collection:
// https://bugs.openjdk.java.net/browse/JDK-8154127
private final static JavaBridge bridge = new JavaBridge();
public static void enableDebug(final WebEngine e) {
JSObject window = (JSObject) e.executeScript("window");
window.setMember("java", bridge);
e.executeScript("""
console.log = function(message)
{
java.log(message);
};""");
e.executeScript("console.log('console.log enabled');");
}
public static void enableFirebug(final WebEngine e) {
var firebugurl = "http://localhost:"+resourcesPort+"/firebug-lite/firebug-lite.js";
//https://raw.githubusercontent.com/stefanXO/firebug-lite/master/firebug-lite.js
//https://lupatec.eu/getfirebug/firebug-lite-compressed.js
var script= """
console.log("Inside script block");
if (!document.getElementById("FirebugLite")) {
E = document["createElement" + "NS"] && document.documentElement.namespaceURI;
E = E ?
document["createElement" + "NS"](E, "script") :
document["createElement"]("script");
E["setAttribute"]("id", "FirebugLite");
E["setAttribute"]("src", "{$firebugurl}#startOpened");
E["setAttribute"]("FirebugLite", "4");
(
document["getElementsByTagName"]("head")[0]
||
document["getElementsByTagName"]("body")[0]
).appendChild(E);
E = new Image();
E["setAttribute"]("src", "{$firebugurl}#startOpened");
}
console.log("executed script block");
""".replace("{$firebugurl}", firebugurl);
System.out.println("+++firebug thing+++");
System.out.println(script);
System.out.println("---firebug thing---");
e.executeScript(script);
}
}
Main method of the application
//...
public static void main(String[]args){
WebkitDebugUtils.createHttpServerForLocalFiles();
launch(args);
}
//...
In the JavaFx Stage initializing code section
#Override
public void start(final Stage primaryStage) throws Exception {
// ....
wv = new WebView();
// ....
javafx.scene.web.WebEngine e = wv.getEngine();
// ....
e.getLoadWorker().stateProperty().addListener((ov, t, t1) -> WebkitDebugUtils.enableDebug(e));
e.documentProperty().addListener((ov, t, t1) -> WebkitDebugUtils.enableFirebug(e));
// ....
}
I am serving the files for firebug from the resources folder of the jar
Snippter of the custom firebug.js file
console.log("inside firebug-lite");
(function(){
//...
//firebug lite 1.4.0 code
//..
})();
console.log("outside firebug-lite");
Output
Starting internal server on 65148
console.log enabled
console.log enabled
// some application specific output
+++firebug thing+++
if (!document.getElementById("FirebugLite")) {
E = document["createElement" + "NS"] && document.documentElement.namespaceURI;
E = E ?
document["createElement" + "NS"](E, "script") :
document["createElement"]("script");
E["setAttribute"]("id", "FirebugLite");
E["setAttribute"]("src", "http://localhost:65148/firebug-lite/firebug-lite.js#startOpened");
E["setAttribute"]("FirebugLite", "4");
(
document["getElementsByTagName"]("head")[0]
||
document["getElementsByTagName"]("body")[0]
).appendChild(E);
E = new Image();
E["setAttribute"]("src", "http://localhost:65148/firebug-lite/firebug-lite.js#startOpened");
}
---firebug thing---
Inside script block
executed script block
Serving # localhost:65148 /firebug-lite/firebug-lite.js
// some application specific output
console.log enabled
Summary:
nothing is happening, nothing is showing up in the console, I don't know the error, firebug is not even opening.
Update2:
I think I am using the wrong version of firebug 1.5 or something
and the old version I don't see any HTML file and thus it is dependent on the default website which is down at the moment.
You can simply inject firebug-lite after loading your webpage.
I've tested on windows 10 with correto 18.0.2 JDK.
Here's an example: (Page can take some time to load)
package com.example.demo;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import javafx.scene.web.WebEngine;
import javafx.concurrent.Worker.State;
public class HelloApplication extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage myStage) {
System.out.println("Demo for firebugLite");
WebView myWebView = new WebView();
myWebView.minHeight(1050);
myWebView.prefWidth(1950);
myWebView.prefHeight(1070);
myWebView.minWidth(1050);
final WebEngine myWebEngine = myWebView.getEngine();
myWebEngine.getLoadWorker().stateProperty()
.addListener((obs, oldValue, newValue) -> {
if (newValue == State.SUCCEEDED) {
System.out.println("finished loading");
myWebEngine.executeScript("var firebug=document.createElement('script');firebug.setAttribute('src','https://lupatec.eu/getfirebug/firebug-lite-compressed.js');document.body.appendChild(firebug);(function(){if(window.firebug.version){firebug.init();}else{setTimeout(arguments.callee);}})();void(firebug);");
}
});
myWebEngine.load("https://lupatec.eu/getfirebug/");
VBox myBox = new VBox(myWebView);
Scene myScene = new Scene(myBox, 1050, 600);
myStage.setScene(myScene);
myStage.show();
}
}
Result:

Amazon Rekognition label detection problem

Problem:
I'm starting to use Amazon Rekognition label detection, the problem is that I don't know how to pass a url to the DetectLabelsRequest () object. That url contains an image which is the one I need to analyze.
Code:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
instantiateRekognition()
}
private fun instantiateRekognition(){
val rekognitionClient: AmazonRekognition =
AmazonRekognitionClient(BasicAWSCredentials("", ""))
val sourceStream: InputStream = FileInputStream(
"http://placehold.it/120x120&text=image1")
var souImage: Image = Image()
val byteBuffer = ByteBuffer.allocate(sourceStream.toString().length)
souImage.withBytes(byteBuffer)
val request = DetectLabelsRequest().withImage(souImage)
.withMaxLabels(10)
.withMinConfidence(75f)
try {
val result = rekognitionClient.detectLabels(request)
val labels = result.labels
for (label in labels) {
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
URL of image to analyze:
http://placehold.it/120x120&text=image1
Here is your solution using the Amazon Rekognition Java V2 API.
If you are not familiar with V2, please refer to this AWS Java Dev Guide topic:
Get started with the AWS SDK for Java 2.x
Code:
package com.example.rekognition;
// snippet-start:[rekognition.java2.detect_labels.import]
import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.rekognition.RekognitionClient;
import software.amazon.awssdk.services.rekognition.model.Image;
import software.amazon.awssdk.services.rekognition.model.DetectLabelsRequest;
import software.amazon.awssdk.services.rekognition.model.DetectLabelsResponse;
import software.amazon.awssdk.services.rekognition.model.Label;
import software.amazon.awssdk.services.rekognition.model.RekognitionException;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
// snippet-end:[rekognition.java2.detect_labels.import]
/**
* To run this Java V2 code example, ensure that you have setup your development environment, including your credentials.
*
* For information, see this documentation topic:
*
* https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
*/
public class DetectLabels {
public static void main(String[] args) {
final String USAGE = "\n" +
"Usage: " +
"DetectLabels <sourceImage>\n\n" +
"Where:\n" +
"sourceImage - the path to the image (for example, C:\\AWS\\pic1.png). \n\n";
// if (args.length != 1) {
// System.out.println(USAGE);
// System.exit(1);
// }
String sourceImage = "C:\\Users\\scmacdon\\lake.png" ; // args[0] ;
Region region = Region.US_EAST_1;
RekognitionClient rekClient = RekognitionClient.builder()
.region(region)
.build();
detectImageLabels(rekClient, sourceImage );
rekClient.close();
}
// snippet-start:[rekognition.java2.detect_labels.main]
public static void detectImageLabels(RekognitionClient rekClient, String sourceImage) {
try {
InputStream sourceStream = new URL("http://placehold.it/120x120&text=image1").openStream();
// InputStream sourceStream = new FileInputStream(sourceImage);
SdkBytes sourceBytes = SdkBytes.fromInputStream(sourceStream);
// Create an Image object for the source image.
Image souImage = Image.builder()
.bytes(sourceBytes)
.build();
DetectLabelsRequest detectLabelsRequest = DetectLabelsRequest.builder()
.image(souImage)
.maxLabels(10)
.build();
DetectLabelsResponse labelsResponse = rekClient.detectLabels(detectLabelsRequest);
List<Label> labels = labelsResponse.labels();
System.out.println("Detected labels for the given photo");
for (Label label: labels) {
System.out.println(label.name() + ": " + label.confidence().toString());
}
} catch (RekognitionException | FileNotFoundException | MalformedURLException e) {
System.out.println(e.getMessage());
System.exit(1);
} catch (IOException e) {
e.printStackTrace();
}
}
// snippet-end:[rekognition.java2.detect_labels.main]
}
Results given your image at the given URL:
I tested with the URL TOO
https://images.unsplash.com/photo-1557456170-0cf4f4d0d362?ixid=MnwxMjA3fDB8MHxzZWFyY2h8MXx8bGFrZXxlbnwwfHwwfHw%3D&ixlib=rb-1.2.1&w=1000&q=80
This produced:
You can find many other Java V2 Amazon Rekognition examples here:
https://github.com/awsdocs/aws-doc-sdk-examples/tree/master/javav2/example_code/rekognition

Run cucumber and generate html report without maven/gradle plugins

I am currently using cucumber and the masterthought plugin to generate html reports on test executions. I want to enable future flexibility to configure the cucumber options when running my tests from a gradle script as well as running the cucumber from java code without the need of using plugins on build script.
I have previously used the surefire plugin to run the cucumber and the masterthought plugin to generate the cucumber report
package com.my.domain;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.junit.runner.RunWith;
import org.junit.runners.model.InitializationError;
import cucumber.api.CucumberOptions;
import cucumber.api.junit.Cucumber;
import cucumber.runtime.RuntimeOptions;
import cucumber.runtime.RuntimeOptionsFactory;
import net.masterthought.cucumber.Configuration;
import net.masterthought.cucumber.ReportBuilder;
import net.masterthought.cucumber.Reportable;
#RunWith(Cucumber.class)
#CucumberOptions(
plugin = {
"json:build/path/cucumber.json" },
features = {
"src/main/resources/features" },
glue = {
"com.my.domain.stepdefinitions" },
dryRun = false,
tags = {"#tag"}
)
public class CucumberJunitRunner {
// TODO: Make Tags, Features, Glue, plugin runtime, and path configurable
private static final String JSON_PLUGIN_PREFIX = "json:";
private static final String PRECONFIGURED_REPORT_OUTPUT_FOLDER_NAME = "cucumber-html-reports";
private static final Optional<String> REPORT_OUTPUT_FOLDER_LOCATION = Optional.ofNullable(null);
private static final boolean SKIP_REPORT_GENERATION = false;
public static void main(String[] args) throws InitializationError {
RuntimeOptions cucumberRuntimeOptions = null;
Optional<String> jsonPluginOutputLocation = Optional.empty();
if(args.length > 0) {
//TODO: USE ARGUMENTS TO INITIALIZE cucumberRuntimeOptions AND jsonPluginOutputLocation
} else {
RuntimeOptionsFactory cucumberRuntimeOptionsFactory = new RuntimeOptionsFactory(CucumberJunitRunner.class);
cucumberRuntimeOptions = cucumberRuntimeOptionsFactory.create();
jsonPluginOutputLocation = cucumberRuntimeOptions.getPluginFormatterNames().stream()
.filter(s -> s.startsWith(JSON_PLUGIN_PREFIX))
.map(s -> s.substring(JSON_PLUGIN_PREFIX.length())).findFirst();
if( ! jsonPluginOutputLocation.isPresent() ) {
throw new RuntimeException(String.format(
"Could not find jsonPluginOutputLocation in plugins from cucumber options: %s",
cucumberRuntimeOptions.getPluginFormatterNames()));
}
}
deletePreviousData(jsonPluginOutputLocation);
runCucumber(cucumberRuntimeOptions, Thread.currentThread().getContextClassLoader());
if (SKIP_REPORT_GENERATION) {
System.out.println("Report generation skipped. No HTML report was built.");
} else {
if (cucumberRuntimeOptions.isDryRun()) {
System.out.println("Cucumber DryRun executed. No HTML report was built.");
} else {
if (jsonPluginOutputLocation.isPresent()) {
Path jsonPluginOutputPath = Paths.get(jsonPluginOutputLocation.get());
if (Files.exists(jsonPluginOutputPath)) {
generateCucumberReport(
REPORT_OUTPUT_FOLDER_LOCATION.orElse(
FilenameUtils.getFullPathNoEndSeparator(jsonPluginOutputLocation.get())),
jsonPluginOutputLocation.get(), "1", "My Project");
} else {
System.out.println("Cucumber JSON report was missing. No HTML report was built.");
}
} else {
System.out.println("Cucumber JSON plugin was missing. No HTML report was built.");
}
}
}
}
private static void deletePreviousData(Optional<String> jsonPluginOutputLocation) {
Path jsonPluginOutputPath = Paths.get(jsonPluginOutputLocation.get());
if (Files.exists(jsonPluginOutputPath)) {
try {
Files.delete(jsonPluginOutputPath);
System.out.println("Cucumber JSON file was deleted: " +
jsonPluginOutputPath.toAbsolutePath().toString());
} catch (IOException e) {
e.printStackTrace();
}
} else {
System.out.println("Cucumber JSON file from previous execution was not detected: "
+ jsonPluginOutputPath.toAbsolutePath().toString());
}
Path cucumberReportsFolder = jsonPluginOutputPath.resolveSibling(PRECONFIGURED_REPORT_OUTPUT_FOLDER_NAME);
if (Files.exists(cucumberReportsFolder)) {
try {
FileUtils.deleteDirectory(cucumberReportsFolder.toFile());
System.out.println("Cucumber JSON report was deleted: " +
cucumberReportsFolder.toAbsolutePath().toString());
} catch (IOException e) {
e.printStackTrace();
}
} else {
System.out.println("Cucumber JSON report from previous execution was not detected: "
+ cucumberReportsFolder.toAbsolutePath().toString());
}
}
/**
* Launches the Cucumber-JVM command line.
*
* #param argv runtime options. See details in the
* {#code cucumber.api.cli.Usage.txt} resource.
* #param classLoader classloader used to load the runtime
* #return 0 if execution was successful, 1 if it was not (test failures)
*/
public static byte runCucumber(RuntimeOptions cucumberRuntimeOptions, ClassLoader classLoader) {
final cucumber.runtime.Runtime runtime = cucumber.runtime.Runtime.builder()
.withRuntimeOptions(cucumberRuntimeOptions).withClassLoader(classLoader).build();
runtime.run();
return runtime.exitStatus();
}
private static void generateCucumberReport(String reportOutputDirectoryLocation, String cucumberJsonFile,
String buildNumber, String projectName) {
File reportOutputDirectory = new File(reportOutputDirectoryLocation);
List<String> jsonFiles = new ArrayList<>();
jsonFiles.add(cucumberJsonFile);
// jsonFiles.add("cucumber-report-2.json");
// String buildNumber = "1";
// String projectName = "cucumberProject";
boolean runWithJenkins = false;
Configuration configuration = new Configuration(reportOutputDirectory, projectName);
// optional configuration - check javadoc
configuration.setRunWithJenkins(runWithJenkins);
configuration.setBuildNumber(buildNumber);
// addidtional metadata presented on main page
// configuration.addClassifications("Platform", "Windows");
// configuration.addClassifications("Browser", "Firefox");
// configuration.addClassifications("Branch", "release/1.0");
// optionally add metadata presented on main page via properties file
//List<String> classificationFiles = new ArrayList<>();
//classificationFiles.add("properties-1.properties");
//classificationFiles.add("properties-2.properties");
// configuration.addClassificationFiles(classificationFiles);
ReportBuilder reportBuilder = new ReportBuilder(jsonFiles, configuration);
Reportable result = reportBuilder.generateReports();
// and here validate 'result' to decide what to do if report has failed
if (result == null) {
System.out.println("There was an isssue while building the report");
System.exit(1);
}
System.out.println(result);
}
}

How to populate JavaFX TreeView using JarEntries (packages - files)

Baically, I'm looping through JarEntry classes from a JarFile and trying to add each package as a node on the TreeView<String>.
Annoyingly, the packages are returned being split up by slashes. Meaning: I have to split each package name into an array and then check if each part (package) already exists in the tree.
Here's an example of what I'm working with:
org/something/commons
org/something/commons/more
I need to somehow work with each of these strings to create this kind of structure:
root
org
something
commons
more
After that I'll need to add each class file from non directory entries to each pre-existing directory nodes.
This is honestly the more confusing thing I've ever tried to achieve. I just can't think up a good algorithm for doing this except creating some form of extending treeitem class that acts as an entry wrapper or something like that.
Any guidance would be much appreciated. My current code is like:
private void populateTree(Enumeration<String> jarEntries) {
jarFile.stream().forEach(entry -> {
String entryName = entry.getName();
if (entry.isDirectory()) {
String[] packages = entryName.split("/");
for(String packageName : packages) {
// check if already exists in root node
if(root.getChildren().contains(root.getChildren().indexOf(packageName))) {
TreeItem<String> packageNode = root.getChildren().get(root.getChildren().indexOf(packageName));
packageNode.getChildren().add(new TreeItem<String>(packageName));
} else {
root.getChildren().add(new TreeItem<String>(packageName));
}
}
} else {
// it's a file
String fileName = entryName.substring(entryName.lastIndexOf("/"), entryName.length());
String[] packages = entryName.substring(0, entryName.lastIndexOf("/")).split("/");
// somehow loop through each child of the root node and eventually, using some form of traversal algo, get to the package node to add new item to
}
});
root.setExpanded(true);
}
This produces the incorrect output of:
I would create a TreeView<JarEntry>, so the data wrapped by the TreeItems are the JarEntry objects. Then use a cellFactory to instruct the cells to display only the last part of the path.
Actually populating the tree is kind of tricky, because a jar file doesn't need to have entries for its directories. So you may end up having to create new entries as you build the structure. I'm not sure I follow the method you posted: aren't you adding all packages and their subpackages directly to the root (instead of subpackages to packages)?
Here's a SSCCE. You might be able to find a cleaner implementation of populating the tree...
import java.io.File;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Collectors;
import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.layout.BorderPane;
import javafx.stage.FileChooser;
import javafx.stage.FileChooser.ExtensionFilter;
import javafx.stage.Stage;
public class JarFileTreeView extends Application {
#Override
public void start(Stage primaryStage) {
TreeView<JarEntry> tree = new TreeView<>();
tree.setShowRoot(false);
TreeItem<JarEntry> root = new TreeItem<>();
tree.setRoot(root);
// only display last portion of the path in the cells:
tree.setCellFactory(tv -> new TreeCell<JarEntry>() {
#Override
public void updateItem(JarEntry item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
} else {
String[] pathElements = item.getName().split("/");
setText(pathElements[pathElements.length - 1]);
}
}
});
ObjectProperty<JarFile> jarFile = new SimpleObjectProperty<>();
jarFile.addListener((obs, oldFile, newFile) -> {
if (newFile == null) {
root.getChildren().clear();
} else {
populateTree(root, newFile);
}
});
FileChooser chooser = new FileChooser();
chooser.getExtensionFilters().add(new ExtensionFilter("Jar Files", "*.jar"));
Button loadButton = new Button("Load...");
loadButton.setOnAction(e -> {
File file = chooser.showOpenDialog(primaryStage);
if (file != null) {
try {
jarFile.set(new JarFile(file));
} catch (Exception e1) {
e1.printStackTrace();
}
}
});
BorderPane uiRoot = new BorderPane(tree, null, null, loadButton, null);
BorderPane.setMargin(loadButton, new Insets(10));
BorderPane.setAlignment(loadButton, Pos.CENTER);
Scene scene = new Scene(uiRoot, 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
private void populateTree(TreeItem<JarEntry> root, JarFile file) {
root.getChildren().clear();
List<JarEntry> entries = file.stream().collect(Collectors.toList());
// sort by length of path (i.e. number of components separated by "/"), then by name:
entries.sort(Comparator
.comparing((JarEntry entry) -> entry.getName().split("/").length)
.thenComparing(entry -> {
String[] pathElements = entry.getName().split("/");
return pathElements[pathElements.length - 1];
}));
for (JarEntry entry : entries) {
// need to find correct parent for entry. That parent (or any of the ancestors)
// might not exist yet, so we create it if necessary as we search.
// Split name up into folder, subfolder, etc:
List<String> pathElements = Arrays.asList(entry.getName().split("/"));
// Search for parent. Start at root:
TreeItem<JarEntry> parent = root;
// Iterate through all elements except the last, traversing tree:
for (int i = 0; i < pathElements.size() - 1 ; i++) {
// name of ancestor entry:
String matchingName = String.join("/", pathElements.subList(0, i+1));
final TreeItem<JarEntry> current = parent ;
// update parent with current parent's descendant, matching appropriate name:
parent = current.getChildren().stream()
.filter(child -> child.getValue().getName().equals(matchingName))
.findFirst()
// it's possible this ancestor didn't yet exist, so we create it,
// and add it to the correct parent:
.orElseGet(() -> {
JarEntry newEntry = new JarEntry(matchingName);
TreeItem<JarEntry> newItem = new TreeItem<>(newEntry);
current.getChildren().add(newItem);
return newItem ;
});
}
// after all that, we have a valid parent:
parent.getChildren().add(new TreeItem<>(entry));
}
}
public static void main(String[] args) {
launch(args);
}
}

Convert DOCX to HTML incliding IMAGES

I am using DOCX4J to convert the DOCX to HTML .I have successfully done the conversion and got the html format.I will be using the html format to embed it as EMAIL body to send an email.But I have some issues which are listed below....
Unable to display images in email body
Losing the spaces and bullets
Please find the code which I have written,
WordprocessingMLPackage wordMLPackage;
wordMLPackage = Docx4J.load(new java.io.File(resourcePath2));
HTMLSettings htmlSettings = Docx4J.createHTMLSettings();
htmlSettings.setImageDirPath(imageFolder + resourcePath2 + "_files");
htmlSettings.setImageTargetUri(imageFolder +resourcePath2.substring(resourcePath2.lastIndexOf("/")+1) + "_files");
htmlSettings.setWmlPackage(wordMLPackage);
OutputStream os;
os = new ByteArrayOutputStream();
Docx4jProperties.setProperty("docx4j.Convert.Out.HTML.OutputMethodXML", true);
Docx4J.toHTML(htmlSettings, os, Docx4J.FLAG_SAVE_FLAT_XML);
DOCX = ((ByteArrayOutputStream)os).toString();
You may add like this in your code
package tcg.doc.web.managedBeans;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.poi.xwpf.converter.core.FileImageExtractor;
import org.apache.poi.xwpf.converter.core.FileURIResolver;
import org.apache.poi.xwpf.converter.xhtml.XHTMLConverter;
import org.apache.poi.xwpf.converter.xhtml.XHTMLOptions;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
#Component
#Scope("session")
#Qualifier("ConvertWord")
public class ConvertWord {
private static final String docName = "TestDocx.docx";
private static final String outputlFolderPath = "d:/";
String htmlNamePath = "docHtml.html";
String zipName="_tmp.zip";
File docFile = new File(outputlFolderPath+docName);
File zipFile = new File(zipName);
public void ConvertWordToHtml() {
try {
// 1) Load DOCX into XWPFDocument
InputStream doc = new FileInputStream(new File(outputlFolderPath+docName));
System.out.println("InputStream"+doc);
XWPFDocument document = new XWPFDocument(doc);
// 2) Prepare XHTML options (here we set the IURIResolver to load images from a "word/media" folder)
XHTMLOptions options = XHTMLOptions.create(); //.URIResolver(new FileURIResolver(new File("word/media")));;
// Extract image
String root = "target";
File imageFolder = new File( root + "/images/" + doc );
options.setExtractor( new FileImageExtractor( imageFolder ) );
// URI resolver
options.URIResolver( new FileURIResolver( imageFolder ) );
OutputStream out = new FileOutputStream(new File(htmlPath()));
XHTMLConverter.getInstance().convert(document, out, options);
System.out.println("OutputStream "+out.toString());
} catch (FileNotFoundException ex) {
} catch (IOException ex) {
}
}
public static void main(String[] args) {
ConvertWord cwoWord=new ConvertWord();
cwoWord.ConvertWordToHtml();
System.out.println();
}
public String htmlPath(){
// d:/docHtml.html
return outputlFolderPath+htmlNamePath;
}
public String zipPath(){
// d:/_tmp.zip
return outputlFolderPath+zipName;
}
}
For maven Dependency on pom.xml
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>org.apache.poi.xwpf.converter.xhtml</artifactId>
<version>1.0.4</version>
</dependency>
or download it from Here
For images to work in an email body, I guess you need to use either a data URI or publish them to a web-reachable location.
In either case, you'll need to write an implementation of:
public interface ConversionImageHandler {
/**
* #param picture
* #param relationship of the image
* #param part of the image, if it is an internal image, otherwise null
* #return uri for the image we've saved, or null
* #throws Docx4JException this exception will be logged, but not propagated
*/
public String handleImage(AbstractWordXmlPicture picture, Relationship relationship, BinaryPart part) throws Docx4JException;
}
and configure docx4j to use it with htmlSettings.setImageHandler.
You can look at some of the existing implementations in the docx4j source code, and take advantage of the helper methods in AbstractConversionImageHandler (eg createEncodedImage if you want data URIs).

Categories