package eu.svjatoslav.meviz.htmlindexer;
+import eu.svjatoslav.commons.file.FilePathParser;
import eu.svjatoslav.meviz.htmlindexer.layouts.Layout;
import eu.svjatoslav.meviz.htmlindexer.layouts.MixedLayout;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
+import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
if (file.equals(lastLoadedFile))
return lastLoadedBufferedImage;
- lastLoadedBufferedImage = createBufferedImage(readMBF(file));
+ String extension = FilePathParser.getFileExtension(file.getName()).toLowerCase();
+ if ("jxl".equals(extension)) {
+ lastLoadedBufferedImage = loadJxlImage(file);
+ } else {
+ lastLoadedBufferedImage = createBufferedImage(readMBF(file));
+ }
lastLoadedFile = file;
if (lastLoadedBufferedImage == null) {
return lastLoadedBufferedImage;
}
+ /**
+ * Load a JPEG XL (.jxl) image by converting it to PNG using the djxl decoder
+ * from libjxl-tools, since Java's ImageIO and OpenIMAJ do not natively
+ * support JPEG XL. Requires libjxl-tools (djxl) to be installed.
+ */
+ private static BufferedImage loadJxlImage(final File jxlFile)
+ throws ImageFormatError, IOException {
+ File tempPng = null;
+ try {
+ tempPng = File.createTempFile("meviz_jxl_", ".png");
+ tempPng.deleteOnExit();
+
+ System.out.println("Decoding JXL: \"" + jxlFile.getName() + "\"");
+
+ final Runtime runtime = Runtime.getRuntime();
+ final Process process = runtime.exec(new String[]{
+ "djxl", jxlFile.getAbsolutePath(), tempPng.getAbsolutePath()});
+
+ // Read stderr in a separate thread to prevent blocking
+ final StringBuilder stderrBuilder = new StringBuilder();
+ final Thread stderrThread = new Thread(() -> {
+ try (java.io.InputStream is = process.getErrorStream();
+ java.io.InputStreamReader isr = new java.io.InputStreamReader(is);
+ BufferedReader br = new BufferedReader(isr)) {
+ String line;
+ while ((line = br.readLine()) != null) {
+ stderrBuilder.append(line).append("\n");
+ }
+ } catch (IOException e) {
+ stderrBuilder.append("Error reading stderr: ").append(e.getMessage());
+ }
+ });
+ stderrThread.start();
+
+ final int exitCode;
+ try {
+ exitCode = process.waitFor();
+ stderrThread.join();
+ } catch (InterruptedException e) {
+ throw new IOException("JXL decoding interrupted for: " + jxlFile, e);
+ }
+
+ final String stderr = stderrBuilder.toString().trim();
+ if (!stderr.isEmpty()) {
+ System.out.println(" djxl stderr: " + stderr);
+ }
+
+ if (exitCode != 0) {
+ throw new ImageFormatError(
+ "djxl failed to decode JXL file: " + jxlFile
+ + " (exit code " + exitCode + "). "
+ + "stderr: " + stderr + ". "
+ + "Ensure libjxl-tools (djxl) is installed.");
+ }
+
+ System.out.println(" JXL decoded, temp PNG size: " + tempPng.length() + " bytes");
+
+ final BufferedImage image = ImageIO.read(tempPng);
+ if (image == null) {
+ throw new ImageFormatError(
+ "Failed to read converted PNG from JXL file: " + jxlFile);
+ }
+
+ System.out.println(" JXL image loaded: "
+ + image.getWidth() + "x" + image.getHeight());
+
+ return image;
+ } finally {
+ if (tempPng != null && tempPng.exists()) {
+ tempPng.delete();
+ }
+ }
+ }
+
public static File getLayoutIndexFile(final Layout layout,
final File directoryToIndex) {