From f51e9db8fb313b0eae0290e8bf86c92855c74443 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 26 Jun 2026 23:18:04 -0400 Subject: [PATCH 1/2] UX: remember last open directory and zoom level, add jbig2/jpeg2000 preview support - Remember chosen directory in File > Open across invocations, persisted to config.properties. - Remember zoom level across document loads, persisted to config.properties. - Add jbig2-imageio and jai-imageio-jpeg2000 dependencies so the PDF preview can render optimized/reduced-size PDFs. --- jsignpdf/pom.xml | 12 ++++ .../fx/view/MainWindowController.java | 66 +++++++++++++++++-- pom.xml | 13 +++- 3 files changed, 83 insertions(+), 8 deletions(-) diff --git a/jsignpdf/pom.xml b/jsignpdf/pom.xml index f162a52b..a6f2a0da 100644 --- a/jsignpdf/pom.xml +++ b/jsignpdf/pom.xml @@ -159,6 +159,18 @@ org.apache.pdfbox pdfbox + + org.apache.pdfbox + jbig2-imageio + + + com.github.jai-imageio + jai-imageio-core + + + com.github.jai-imageio + jai-imageio-jpeg2000 + com.github.kwart.jsign jsign-jpedal diff --git a/jsignpdf/src/main/java/net/sf/jsignpdf/fx/view/MainWindowController.java b/jsignpdf/src/main/java/net/sf/jsignpdf/fx/view/MainWindowController.java index ae3a14d2..8ec0765c 100644 --- a/jsignpdf/src/main/java/net/sf/jsignpdf/fx/view/MainWindowController.java +++ b/jsignpdf/src/main/java/net/sf/jsignpdf/fx/view/MainWindowController.java @@ -67,9 +67,10 @@ import net.sf.jsignpdf.fx.preset.Preset; import net.sf.jsignpdf.fx.preset.PresetManager; import net.sf.jsignpdf.fx.preset.PresetValidation; +import net.sf.jsignpdf.utils.PropertyProvider; +import net.sf.jsignpdf.utils.PropertyStoreFactory; import net.sf.jsignpdf.fx.util.RecentFilesManager; import net.sf.jsignpdf.fx.viewmodel.DocumentViewModel; -import net.sf.jsignpdf.utils.PropertyStoreFactory; import net.sf.jsignpdf.fx.viewmodel.SignaturePlacementViewModel; import net.sf.jsignpdf.fx.viewmodel.SigningOptionsViewModel; import net.sf.jsignpdf.fx.viewmodel.VisibleSignatureCoordinator; @@ -85,6 +86,8 @@ public class MainWindowController { private Stage stage; + private File lastOpenDir; + private double lastZoomLevel = 1.0; private BasicSignerOptions options; private final DocumentViewModel documentVM = new DocumentViewModel(); private final SigningOptionsViewModel signingVM = new SigningOptionsViewModel(); @@ -319,6 +322,7 @@ private void initialize() { progressBar.setVisible(false); updateStatus(RES.get("jfx.gui.status.ready")); + loadPersistedViewState(); cmbZoom.getItems().addAll("50%", "75%", "100%", "125%", "150%", "200%"); cmbZoom.setValue("100%"); @@ -351,12 +355,14 @@ private void initialize() { } }); - // Zoom level changes update combo + // Zoom level changes update combo, remember the last zoom, and persist documentVM.zoomLevelProperty().addListener((obs, oldVal, newVal) -> { + lastZoomLevel = newVal.doubleValue(); String formatted = Math.round(newVal.doubleValue() * 100) + "%"; if (!formatted.equals(cmbZoom.getValue())) { cmbZoom.setValue(formatted); } + saveViewStateToConfig(); }); // Page number text field commit @@ -931,11 +937,16 @@ private void handleKeyPress(KeyEvent event) { @FXML private void onFileOpen() { - File file = new NativeFileChooser() + NativeFileChooser fc = new NativeFileChooser() .setTitle(RES.get("jfx.gui.dialog.openPdf.title")) - .addFilter(ExtensionFilter.of("PDF Files", "*.pdf")) - .showOpenDialog(stage); + .addFilter(ExtensionFilter.of("PDF Files", "*.pdf")); + if (lastOpenDir != null) { + fc.setInitialDirectory(lastOpenDir); + } + File file = fc.showOpenDialog(stage); if (file != null) { + lastOpenDir = file.getParentFile(); + saveLastOpenDir(); openDocument(file); } } @@ -947,6 +958,7 @@ private void onFileClose() { @FXML private void onFileExit() { + saveViewStateToConfig(); stage.close(); } @@ -1184,6 +1196,7 @@ private void onDragDropped(DragEvent event) { // --- Document handling --- private void openDocument(File file) { + lastOpenDir = file.getParentFile(); try { if (options == null) { options = new BasicSignerOptions(); @@ -1218,13 +1231,13 @@ private void openDocument(File file) { documentVM.setDocumentFile(file); documentVM.setPageCount(pages); - documentVM.setZoomLevel(1.0); + documentVM.setZoomLevel(lastZoomLevel); lblDropHint.setVisible(false); setDocumentControlsDisabled(false); lblPageCount.setText("/ " + pages); txtPageNumber.setText("1"); - cmbZoom.setValue("100%"); + cmbZoom.setValue(Math.round(lastZoomLevel * 100) + "%"); stage.setTitle("JSignPdf " + Constants.VERSION + " - " + file.getName()); LOGGER.info("Opened document: " + file.getAbsolutePath()); @@ -1342,6 +1355,45 @@ private void closeDocument() { stage.setTitle("JSignPdf " + Constants.VERSION); } + private static final String KEY_LAST_OPEN_DIR = "last.open.dir"; + private static final String KEY_LAST_ZOOM = "last.zoom"; + + private void saveLastOpenDir() { + try { + PropertyProvider cfg = PropertyStoreFactory.getInstance().mainConfig(); + cfg.setProperty(KEY_LAST_OPEN_DIR, lastOpenDir.getAbsolutePath()); + cfg.save(); + } catch (Exception ignored) { + } + } + + private void loadPersistedViewState() { + try { + PropertyProvider cfg = PropertyStoreFactory.getInstance().mainConfig(); + String dir = cfg.getProperty(KEY_LAST_OPEN_DIR); + if (dir != null && !dir.isEmpty()) { + File f = new File(dir); + if (f.isDirectory()) { + lastOpenDir = f; + } + } + String zoom = cfg.getProperty(KEY_LAST_ZOOM); + if (zoom != null) { + lastZoomLevel = Double.parseDouble(zoom); + } + } catch (Exception ignored) { + } + } + + private void saveViewStateToConfig() { + try { + PropertyProvider cfg = PropertyStoreFactory.getInstance().mainConfig(); + cfg.setProperty(KEY_LAST_ZOOM, String.valueOf(lastZoomLevel)); + cfg.save(); + } catch (Exception ignored) { + } + } + /** * Returns the document view model (used by other controllers). */ diff --git a/pom.xml b/pom.xml index c5baea59..8f2e664c 100644 --- a/pom.xml +++ b/pom.xml @@ -28,6 +28,8 @@ 1.1.0 4.92.13 3.0.7 + 3.0.2 + 1.4.0 2.22.0 1.11.0 3.20.0 @@ -111,6 +113,11 @@ pdfbox ${pdfbox.version} + + org.apache.pdfbox + jbig2-imageio + ${jbig2-imageio.version} + com.github.kwart.jsign jsign-jpedal @@ -126,12 +133,16 @@ junit ${junit.version} - com.github.jai-imageio jai-imageio-core 1.4.0 + + com.github.jai-imageio + jai-imageio-jpeg2000 + ${jai-imageio-jpeg2000.version} + From 27c3779c65c64fc0a1bf0fe2bd1fe0763b046fe9 Mon Sep 17 00:00:00 2001 From: "Josef (kwart) Cacek" Date: Sun, 5 Jul 2026 16:00:17 +0200 Subject: [PATCH 2/2] Review fixes: drop jai-imageio-jpeg2000; persist view state less aggressively - Remove the jai-imageio-jpeg2000 dependency (and its enabler jai-imageio-core runtime dep). Its bundled JJ2000 codec is under a restrictive, GPL-incompatible license with a field-of-use clause and patent warning, and its license isn't in the add-third-party allow list, so it would break the release build's license check. Keep the clean Apache-2.0 jbig2-imageio for scanned (JBIG2) PDFs. - jai-imageio-core reverts to dependencyManagement-only (convergence). - Stop writing config.properties on every zoom change (blocking I/O on the FX thread); track zoom in memory and persist on document open/exit. - Persist the last open directory from openDocument() so drag-drop and recent-files opens also remember it, not just File > Open. - Log persistence failures at FINE instead of swallowing them; tidy imports and constant placement. Co-Authored-By: Claude Opus 4.8 --- jsignpdf/pom.xml | 8 ----- .../fx/view/MainWindowController.java | 34 ++++++++----------- pom.xml | 7 +--- 3 files changed, 15 insertions(+), 34 deletions(-) diff --git a/jsignpdf/pom.xml b/jsignpdf/pom.xml index a6f2a0da..ff719d5a 100644 --- a/jsignpdf/pom.xml +++ b/jsignpdf/pom.xml @@ -163,14 +163,6 @@ org.apache.pdfbox jbig2-imageio - - com.github.jai-imageio - jai-imageio-core - - - com.github.jai-imageio - jai-imageio-jpeg2000 - com.github.kwart.jsign jsign-jpedal diff --git a/jsignpdf/src/main/java/net/sf/jsignpdf/fx/view/MainWindowController.java b/jsignpdf/src/main/java/net/sf/jsignpdf/fx/view/MainWindowController.java index 8ec0765c..35c7a2c9 100644 --- a/jsignpdf/src/main/java/net/sf/jsignpdf/fx/view/MainWindowController.java +++ b/jsignpdf/src/main/java/net/sf/jsignpdf/fx/view/MainWindowController.java @@ -67,8 +67,6 @@ import net.sf.jsignpdf.fx.preset.Preset; import net.sf.jsignpdf.fx.preset.PresetManager; import net.sf.jsignpdf.fx.preset.PresetValidation; -import net.sf.jsignpdf.utils.PropertyProvider; -import net.sf.jsignpdf.utils.PropertyStoreFactory; import net.sf.jsignpdf.fx.util.RecentFilesManager; import net.sf.jsignpdf.fx.viewmodel.DocumentViewModel; import net.sf.jsignpdf.fx.viewmodel.SignaturePlacementViewModel; @@ -76,6 +74,8 @@ import net.sf.jsignpdf.fx.viewmodel.VisibleSignatureCoordinator; import net.sf.jsignpdf.types.PadesLevel; import net.sf.jsignpdf.types.PageInfo; +import net.sf.jsignpdf.utils.PropertyProvider; +import net.sf.jsignpdf.utils.PropertyStoreFactory; import static net.sf.jsignpdf.Constants.LOGGER; import static net.sf.jsignpdf.Constants.RES; @@ -85,6 +85,9 @@ */ public class MainWindowController { + private static final String KEY_LAST_OPEN_DIR = "last.open.dir"; + private static final String KEY_LAST_ZOOM = "last.zoom"; + private Stage stage; private File lastOpenDir; private double lastZoomLevel = 1.0; @@ -355,14 +358,13 @@ private void initialize() { } }); - // Zoom level changes update combo, remember the last zoom, and persist + // Zoom level changes update combo and remember the last zoom (persisted on document open / exit) documentVM.zoomLevelProperty().addListener((obs, oldVal, newVal) -> { lastZoomLevel = newVal.doubleValue(); String formatted = Math.round(newVal.doubleValue() * 100) + "%"; if (!formatted.equals(cmbZoom.getValue())) { cmbZoom.setValue(formatted); } - saveViewStateToConfig(); }); // Page number text field commit @@ -945,8 +947,6 @@ private void onFileOpen() { } File file = fc.showOpenDialog(stage); if (file != null) { - lastOpenDir = file.getParentFile(); - saveLastOpenDir(); openDocument(file); } } @@ -1197,6 +1197,7 @@ private void onDragDropped(DragEvent event) { private void openDocument(File file) { lastOpenDir = file.getParentFile(); + saveViewStateToConfig(); try { if (options == null) { options = new BasicSignerOptions(); @@ -1355,18 +1356,6 @@ private void closeDocument() { stage.setTitle("JSignPdf " + Constants.VERSION); } - private static final String KEY_LAST_OPEN_DIR = "last.open.dir"; - private static final String KEY_LAST_ZOOM = "last.zoom"; - - private void saveLastOpenDir() { - try { - PropertyProvider cfg = PropertyStoreFactory.getInstance().mainConfig(); - cfg.setProperty(KEY_LAST_OPEN_DIR, lastOpenDir.getAbsolutePath()); - cfg.save(); - } catch (Exception ignored) { - } - } - private void loadPersistedViewState() { try { PropertyProvider cfg = PropertyStoreFactory.getInstance().mainConfig(); @@ -1381,16 +1370,21 @@ private void loadPersistedViewState() { if (zoom != null) { lastZoomLevel = Double.parseDouble(zoom); } - } catch (Exception ignored) { + } catch (Exception e) { + LOGGER.log(Level.FINE, "Could not load persisted view state", e); } } private void saveViewStateToConfig() { try { PropertyProvider cfg = PropertyStoreFactory.getInstance().mainConfig(); + if (lastOpenDir != null) { + cfg.setProperty(KEY_LAST_OPEN_DIR, lastOpenDir.getAbsolutePath()); + } cfg.setProperty(KEY_LAST_ZOOM, String.valueOf(lastZoomLevel)); cfg.save(); - } catch (Exception ignored) { + } catch (Exception e) { + LOGGER.log(Level.FINE, "Could not persist view state", e); } } diff --git a/pom.xml b/pom.xml index 8f2e664c..f5846724 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,6 @@ 4.92.13 3.0.7 3.0.2 - 1.4.0 2.22.0 1.11.0 3.20.0 @@ -133,16 +132,12 @@ junit ${junit.version} + com.github.jai-imageio jai-imageio-core 1.4.0 - - com.github.jai-imageio - jai-imageio-jpeg2000 - ${jai-imageio-jpeg2000.version} -