From 6667986f0bc67dfb2ed48c65299385f1fdc7f7d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillaume=20T=C3=A2che?= Date: Tue, 25 Jul 2023 12:48:15 +0200 Subject: [PATCH 1/2] GH-216 Adds empty pages filter --- core/core-awt/pom.xml | 8 + .../icepdf/core/util/PropertyConstants.java | 7 +- licenses/filter_pages_icon.txt | 1 + licenses/mail_icon.txt | 1 + .../org/icepdf/ri/common/SwingController.java | 117 ++++++++++++-- .../icepdf/ri/common/SwingViewBuilder.java | 14 +- .../filters/AbstractDocumentFilter.java | 75 +++++++++ .../filters/AbstractImageDocumentFilter.java | 50 ++++++ .../ri/common/filters/DocumentFilter.java | 67 ++++++++ .../filters/DocumentFilterListener.java | 27 ++++ .../ri/common/filters/EmptyPagesFilter.java | 153 ++++++++++++++++++ .../search/DocumentSearchControllerImpl.java | 4 +- .../icepdf/ri/common/tools/TextSelection.java | 2 +- .../tools/TextSelectionViewHandler.java | 2 +- .../ri/common/tools/ZoomInViewHandler.java | 4 +- .../ri/common/utility/layers/LayersPanel.java | 2 +- .../ri/common/views/AbstractDocumentView.java | 5 + .../views/AbstractDocumentViewModel.java | 29 +++- .../views/AbstractPageViewComponent.java | 2 +- .../common/views/CollectionDocumentView.java | 5 + .../views/DocumentViewControllerImpl.java | 113 +++++++++++-- .../ri/common/views/DocumentViewModel.java | 48 +++++- .../common/views/DocumentViewModelImpl.java | 5 +- .../ri/common/views/OneColumnPageView.java | 11 +- .../icepdf/ri/common/views/OnePageView.java | 9 +- .../common/views/PageComponentSelector.java | 6 +- .../ri/common/views/TwoColumnPageView.java | 11 +- .../icepdf/ri/common/views/TwoPageView.java | 11 +- .../summary/AnnotationSummaryBox.java | 2 +- .../annotations/summary/ColorLabelPanel.java | 2 +- .../icepdf/ri/images/filter_pages_a_24.png | Bin 0 -> 773 bytes .../icepdf/ri/images/filter_pages_a_32.png | Bin 0 -> 1133 bytes .../icepdf/ri/images/filter_pages_i_24.png | Bin 0 -> 576 bytes .../icepdf/ri/images/filter_pages_i_32.png | Bin 0 -> 842 bytes .../icepdf/ri/images/filter_pages_r_24.png | Bin 0 -> 535 bytes .../icepdf/ri/images/filter_pages_r_32.png | Bin 0 -> 756 bytes .../ri/resources/MessageBundle.properties | 3 + 37 files changed, 733 insertions(+), 63 deletions(-) create mode 100644 licenses/filter_pages_icon.txt create mode 100644 licenses/mail_icon.txt create mode 100644 viewer/viewer-awt/src/main/java/org/icepdf/ri/common/filters/AbstractDocumentFilter.java create mode 100644 viewer/viewer-awt/src/main/java/org/icepdf/ri/common/filters/AbstractImageDocumentFilter.java create mode 100644 viewer/viewer-awt/src/main/java/org/icepdf/ri/common/filters/DocumentFilter.java create mode 100644 viewer/viewer-awt/src/main/java/org/icepdf/ri/common/filters/DocumentFilterListener.java create mode 100644 viewer/viewer-awt/src/main/java/org/icepdf/ri/common/filters/EmptyPagesFilter.java create mode 100644 viewer/viewer-awt/src/main/resources/org/icepdf/ri/images/filter_pages_a_24.png create mode 100644 viewer/viewer-awt/src/main/resources/org/icepdf/ri/images/filter_pages_a_32.png create mode 100644 viewer/viewer-awt/src/main/resources/org/icepdf/ri/images/filter_pages_i_24.png create mode 100644 viewer/viewer-awt/src/main/resources/org/icepdf/ri/images/filter_pages_i_32.png create mode 100644 viewer/viewer-awt/src/main/resources/org/icepdf/ri/images/filter_pages_r_24.png create mode 100644 viewer/viewer-awt/src/main/resources/org/icepdf/ri/images/filter_pages_r_32.png diff --git a/core/core-awt/pom.xml b/core/core-awt/pom.xml index bd07d4344..fed133be4 100644 --- a/core/core-awt/pom.xml +++ b/core/core-awt/pom.xml @@ -34,6 +34,14 @@ maven-surefire-plugin 3.1.0 + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + + diff --git a/core/core-awt/src/main/java/org/icepdf/core/util/PropertyConstants.java b/core/core-awt/src/main/java/org/icepdf/core/util/PropertyConstants.java index c7925e87f..bb5d46e9c 100644 --- a/core/core-awt/src/main/java/org/icepdf/core/util/PropertyConstants.java +++ b/core/core-awt/src/main/java/org/icepdf/core/util/PropertyConstants.java @@ -19,17 +19,18 @@ public class PropertyConstants { // property change event names - public final static String + public static final String DOCUMENT_CURRENT_PAGE = "documentCurrentPage", DOCUMENT_VIEW_ZOOM_CHANGE = "documentViewZoomChange", DOCUMENT_VIEW_ROTATION_CHANGE = "documentViewRotationChange", DOCUMENT_VIEW_REFRESH_CHANGE = "documentViewRefreshChange", DOCUMENT_VIEW_TYPE_CHANGE = "documentViewTypeChange", + DOCUMENT_VIEW_PAGES_CHANGE = "documentViewPagesChange", DOCUMENT_TOOL_PAN = "documentToolRotation", - DOCUMENT_TOOL_ZOOM_IN = "documentToolZoomIn", - DOCUMENT_TOOL_ZOOM_OUT = "documentToolZoomOut", + DOCUMENT_TOOL_ZOOM_IN = "documentToolZoomIn", + DOCUMENT_TOOL_ZOOM_OUT = "documentToolZoomOut", // DOCUMENT_TOOL_DYNAMIC_ROTATION = "documentToolDynamicRotation", // DOCUMENT_TOOL_DYNAMIC_ZOOM = "documentToolDynamicZoom", diff --git a/licenses/filter_pages_icon.txt b/licenses/filter_pages_icon.txt new file mode 100644 index 000000000..7acdcd374 --- /dev/null +++ b/licenses/filter_pages_icon.txt @@ -0,0 +1 @@ +https://commons.wikimedia.org/wiki/File:OOjs_UI_icon_eye.svg diff --git a/licenses/mail_icon.txt b/licenses/mail_icon.txt new file mode 100644 index 000000000..94bab907a --- /dev/null +++ b/licenses/mail_icon.txt @@ -0,0 +1 @@ +https://iconarchive.com/show/web-blog-icons-by-semlabs/mail2-send-icon.html diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/SwingController.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/SwingController.java index 566e175c6..f08e8ce2b 100644 --- a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/SwingController.java +++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/SwingController.java @@ -29,6 +29,9 @@ import org.icepdf.core.search.DocumentSearchController; import org.icepdf.core.util.*; import org.icepdf.core.util.updater.WriteMode; +import org.icepdf.ri.common.filters.DocumentFilter; +import org.icepdf.ri.common.filters.DocumentFilterListener; +import org.icepdf.ri.common.filters.EmptyPagesFilter; import org.icepdf.ri.common.preferences.PreferencesDialog; import org.icepdf.ri.common.print.PrintHelper; import org.icepdf.ri.common.print.PrintHelperFactory; @@ -120,16 +123,13 @@ public class SwingController extends ComponentAdapter implements org.icepdf.ri.common.views.Controller, ActionListener, FocusListener, ItemListener, TreeSelectionListener, WindowListener, DropTargetListener, - PropertyChangeListener { + PropertyChangeListener, DocumentFilterListener { protected static final Logger logger = Logger.getLogger(SwingController.class.toString()); - private static final boolean USE_JFILECHOOSER; - - static { - USE_JFILECHOOSER = Defs.booleanProperty("org.icepdf.ri.viewer.jfilechooser", false); - } + private static final boolean USE_JFILECHOOSER = Defs.booleanProperty("org.icepdf.ri.viewer.jfilechooser", false); + private static final boolean IS_PREFILTER = Defs.booleanProperty("org.icepdf.ri.viewer.filters.prefilter", false); private static final boolean IS_READONLY = Defs.booleanProperty("org.icepdf.ri.viewer.readonly", false); @@ -143,6 +143,9 @@ public class SwingController extends ComponentAdapter protected static final int MAX_SELECT_ALL_PAGE_COUNT = 250; + private static final DocumentFilter EMPTY_PAGES_FILTER = getEmptyPagesFilter(); + private final Map filters; + private JMenuItem openFileMenuItem; private JMenu recentFilesSubMenu; private JMenuItem openURLMenuItem; @@ -222,6 +225,7 @@ public class SwingController extends ComponentAdapter private JToggleButton textSelectToolButton; private JToggleButton zoomInToolButton; private JToggleButton zoomDynamicToolButton; + private JToggleButton filterPagesButton; private JToggleButton selectToolButton; // main annotation toolbar private AnnotationColorToggleButton highlightAnnotationToolButton; @@ -320,6 +324,9 @@ public SwingController(ResourceBundle currentMessageBundle) { SwingController.messageBundle = ResourceBundle.getBundle( ViewerPropertiesManager.DEFAULT_MESSAGE_BUNDLE); } + filters = new HashMap<>(); + filters.put(EMPTY_PAGES_FILTER, true); + filters.keySet().forEach(df -> df.addListener(this)); } /** @@ -1393,6 +1400,39 @@ public void setZoomDynamicToolButton(JToggleButton btn) { btn.addItemListener(this); } + /** + * Called by SwingViewerBuilder, so that Controller can setup event handling + * + * @param btn button to assign + */ + public void setFilterPagesButton(JToggleButton btn) { + filterPagesButton = btn; + btn.addItemListener(this); + final JPopupMenu popupMenu = new JPopupMenu(); + final JCheckBoxMenuItem emptyPagesMenuItem = getFilterMenuItem(messageBundle.getString( + "viewer.toolbar.tool.filterEmptyPages.label"), EMPTY_PAGES_FILTER); + emptyPagesMenuItem.setSelected(true); + popupMenu.add(emptyPagesMenuItem); + btn.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(final MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON3 && document != null && !isPdfCollection()) { + popupMenu.show(btn, e.getX(), e.getY()); + } + } + }); + } + + private JCheckBoxMenuItem getFilterMenuItem(final String label, final DocumentFilter filter) { + final JCheckBoxMenuItem menuItem = new PersistentJCheckBoxMenuItem(label); + menuItem.addItemListener(e -> { + filters.put(filter, menuItem.isSelected()); + setEnabled(filterPagesButton, document != null && !isPdfCollection() && areFiltersReady()); + }); + return menuItem; + } + + /** * Called by SwingViewerBuilder, so that Controller can setup event handling * @@ -1558,7 +1598,7 @@ public boolean isPdfCollection() { if (filePairs != null) { Library library = catalog.getLibrary(); // check to see if at least one file is a PDF. - for (int i = 0, max = filePairs.size(); i < max; i += 2) { + for (int i = 0, max = filePairs.size(); i < max; i = 2) { // get the name and document for // file name and file specification pairs. String fileName = Utils.convertStringObject(library, (StringObject) filePairs.get(i)); @@ -1696,6 +1736,7 @@ protected void reflectStateInComponents() { setEnabled(panToolButton, opened && !pdfCollection); setEnabled(zoomInToolButton, opened && !pdfCollection); setEnabled(zoomDynamicToolButton, opened && !pdfCollection); + setEnabled(filterPagesButton, opened && !pdfCollection && areFiltersReady()); setEnabled(textSelectToolButton, opened && canExtract && !pdfCollection); setEnabled(selectToolButton, opened && canModify && !pdfCollection); setEnabled(highlightAnnotationToolButton, opened && canModify && !pdfCollection && !IS_READONLY); @@ -1741,6 +1782,10 @@ protected void reflectStateInComponents() { } } + private boolean areFiltersReady() { + return !IS_PREFILTER || getEnabledFilters().stream().allMatch(df -> df.isReady(document)); + } + private boolean hasForms() { return document != null && !(document.getCatalog().getInteractiveForm() == null || @@ -2901,7 +2946,6 @@ public void openDocument(byte[] data, int offset, int length, String description } public void commonNewDocumentHandling(String fileDescription) { - // utility pane visibility boolean showUtilityPane = false; @@ -3148,6 +3192,9 @@ public void commonNewDocumentHandling(String fileDescription) { // set the go to page combo box in the mainToolbar reflectStateInComponents(); updateDocumentView(); + if (IS_PREFILTER) { + new Thread(() -> filters.keySet().forEach(f -> f.filterPages(document))).start(); + } } /** @@ -3198,6 +3245,7 @@ public void closeDocument() { // free the document if (document != null) { + filters.forEach((f, b) -> f.interrupt(document)); document.dispose(); document = null; } @@ -3337,6 +3385,7 @@ public void dispose() { panToolButton = null; zoomInToolButton = null; zoomDynamicToolButton = null; + filterPagesButton = null; textSelectToolButton = null; selectToolButton = null; highlightAnnotationToolButton = null; @@ -3422,6 +3471,7 @@ public void dispose() { viewModel = null; windowManagementCallback = null; + filters.keySet().forEach(df -> df.removeListener(this)); } /** @@ -5100,6 +5150,13 @@ else if (source == panToolButton) { tool = DocumentViewModelImpl.DISPLAY_TOOL_ZOOM_DYNAMIC; setDocumentToolMode(DocumentViewModelImpl.DISPLAY_TOOL_ZOOM_DYNAMIC); } + } else if (source == filterPagesButton) { + if (e.getStateChange() == ItemEvent.SELECTED) { + filterDocumentPages(); + } else { + documentViewController.filterPageComponents(pvc -> true); + } + reflectSelectionInButton(filterPagesButton, filterPagesButton.isSelected()); } else if (source == textSelectToolButton) { if (e.getStateChange() == ItemEvent.SELECTED) { tool = DocumentViewModelImpl.DISPLAY_TOOL_TEXT_SELECTION; @@ -5213,6 +5270,23 @@ else if (source == facingPageViewNonContinuousButton) { } } + protected void filterDocumentPages() { + final Set filteredIndexes = documentViewController.getDocumentViewModel().getAllPageComponents() + .stream().map(AbstractPageViewComponent::getPageIndex).collect(Collectors.toSet()); + logger.info("Filtering with " + getEnabledFilters().stream().map(Object::toString).collect(Collectors.joining(";"))); + filters.forEach((df, enabled) -> { + if (enabled) { + final Set keptIndexes = df.filterPages(document); + filteredIndexes.removeIf(i -> !keptIndexes.contains(i)); + } + }); + documentViewController.filterPageComponents(pvc -> filteredIndexes.contains(pvc.getPageIndex())); + } + + protected Set getEnabledFilters() { + return filters.entrySet().stream().filter(Map.Entry::getValue).map(Map.Entry::getKey).collect(Collectors.toSet()); + } + private static boolean checkAnnotationButton(final Object source, final AnnotationColorToggleButton button, final JToggleButton propertiesButton) { return source == button || (button != null && source == button.getColorButton()) || source == propertiesButton; @@ -5513,6 +5587,22 @@ protected final void addKeyAction(final JComponent component, final int keyCode, actionMap.put(key, action); } + @Override + public void documentFilterStarted(final DocumentFilter filter, final Document document) { + if (this.document == document) { + setEnabled(filterPagesButton, false); + } + } + + @Override + public void documentFilterCompleted(final DocumentFilter filter, final Document document, + final Set filteredPages) { + if (this.document == document) { + setEnabled(filterPagesButton, !isPdfCollection() && document != null && areFiltersReady()); + } + } + + @FunctionalInterface protected interface ActionMethod { void doAction(); @@ -5552,6 +5642,7 @@ public void keyTyped(KeyEvent e) { currentPageNumberTextField.setText(modelValue); } } + } /** @@ -5745,7 +5836,7 @@ public void changeAnnotationsVisibility(final AnnotationFilter filter, final boo pa.setOpen(false); final int idx = pa.getPageIndex(); final AbstractAnnotationComponent comp = (AbstractAnnotationComponent) ((PageViewComponentImpl) - documentViewController.getDocumentViewModel().getPageComponents().get(idx)).getComponentFor(pa); + documentViewController.getDocumentViewModel().getAllPageComponents().get(idx)).getComponentFor(pa); if (comp != null) { comp.setVisible(false); } @@ -5778,7 +5869,7 @@ public void changeAnnotationsPrivacy(final AnnotationFilter filter, final boolea pa.setModifiedDate(PDate.formatDateTime(new Date())); } final PageViewComponentImpl pvc = (PageViewComponentImpl) - documentViewController.getDocumentViewModel().getPageComponents().get(ma.getPageIndex()); + documentViewController.getDocumentViewModel().getAllPageComponents().get(ma.getPageIndex()); final MarkupAnnotationComponent comp = (MarkupAnnotationComponent) pvc.getComponentFor(ma); if (comp != null) { if (comp.getPopupAnnotationComponent() != null) { @@ -5802,4 +5893,10 @@ private void callOnFilteredAnnotations(final AnnotationFilter filter, final Cons } } } + + private static EmptyPagesFilter getEmptyPagesFilter() { + final double minRatio = Defs.doubleProperty("org.icepdf.ri.viewer.filters.empty.ratio", 0.005); + final int whiteFloor = Defs.intProperty("org.icepdf.ri.viewer.filters.empty.white.floor", 240); + return new EmptyPagesFilter(minRatio, whiteFloor); + } } diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/SwingViewBuilder.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/SwingViewBuilder.java index 6b0ac2b02..c23eefe98 100644 --- a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/SwingViewBuilder.java +++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/SwingViewBuilder.java @@ -1224,7 +1224,7 @@ public JToolBar buildCompleteToolBar(boolean embeddableComponent) { if (propertiesManager.checkAndStoreBooleanProperty(ViewerPropertiesManager.PROPERTY_SHOW_TOOLBAR_TOOL)) addToToolBar(toolbar, buildToolToolBar()); if (propertiesManager.checkAndStoreBooleanProperty(ViewerPropertiesManager.PROPERTY_SHOW_TOOLBAR_ANNOTATION)) - addToToolBar(toolbar, buildAnnotationlToolBar()); + addToToolBar(toolbar, buildAnnotationToolBar()); if (propertiesManager.checkAndStoreBooleanProperty(ViewerPropertiesManager.PROPERTY_SHOW_TOOLBAR_FORMS)) addToToolBar(toolbar, buildFormsToolBar()); if (propertiesManager.checkAndStoreBooleanProperty(ViewerPropertiesManager.PROPERTY_SHOW_TOOLBAR_SEARCH)) @@ -1264,6 +1264,7 @@ public JToolBar buildUtilityToolBar(boolean embeddableComponent, ViewerPropertie if (propertiesManager.checkAndStoreBooleanProperty(ViewerPropertiesManager.PROPERTY_SHOW_UTILITY_UPANE)) addToToolBar(toolbar, buildShowHideUtilityPaneButton()); + addToToolBar(toolbar, buildFilterPagesButton()); // Don't bother with this toolbar if we don't have any visible buttons if (toolbar.getComponentCount() == 0) { return null; @@ -1587,7 +1588,7 @@ public JToolBar buildToolToolBar() { return toolbar; } - public JToolBar buildAnnotationlToolBar() { + public JToolBar buildAnnotationToolBar() { JToolBar toolbar = new JToolBar(); commonToolBarSetup(toolbar, false); if (propertiesManager.checkAndStoreBooleanProperty( @@ -2041,6 +2042,15 @@ public JToggleButton buildZoomOutToolButton() { return btn; } + public JToggleButton buildFilterPagesButton() { + JToggleButton btn = makeToolbarToggleButton( + messageBundle.getString("viewer.toolbar.tool.filterPages.label"), + messageBundle.getString("viewer.toolbar.tool.filterPages.tooltip"), + "filter_pages", iconSize, buttonFont); + if (viewerController != null && btn != null) + viewerController.setFilterPagesButton(btn); + return btn; + } public JSplitPane buildUtilityAndDocumentSplitPane(boolean embeddableComponent) { JSplitPane splitpane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/filters/AbstractDocumentFilter.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/filters/AbstractDocumentFilter.java new file mode 100644 index 000000000..309abc5f1 --- /dev/null +++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/filters/AbstractDocumentFilter.java @@ -0,0 +1,75 @@ +package org.icepdf.ri.common.filters; + +import org.icepdf.core.pobjects.Document; + +import java.util.*; + +/** + * Abstract implementation of a DocumentFilter to handle caching + */ +public abstract class AbstractDocumentFilter implements DocumentFilter { + + private final Set listeners; + private final Map> cache; + private final Map parameters; + + /** + * Instantiates the document filter + */ + protected AbstractDocumentFilter(final Map parameters) { + this.cache = new WeakHashMap<>(); + this.listeners = new HashSet<>(); + this.parameters = Collections.unmodifiableMap(new HashMap<>(parameters)); + } + + @Override + public Set filterPages(final Document document) { + if (cache.containsKey(document)) { + return cache.get(document); + } else { + listeners.forEach(l -> l.documentFilterStarted(this, document)); + final Set filtered = filterPagesUncached(document); + cache.put(document, filtered); + listeners.forEach(l -> l.documentFilterCompleted(this, document, filtered)); + return filtered; + } + } + + /** + * Actual implementation of the filter when there is a cache miss + * + * @param document The document + * @return The set of page indexes that passed the filter + */ + protected abstract Set filterPagesUncached(final Document document); + + @Override + public boolean isReady(final Document document) { + return cache.containsKey(document); + } + + @Override + public Map getParameters() { + return parameters; + } + + @Override + public void addListener(final DocumentFilterListener listener) { + listeners.add(listener); + } + + @Override + public void removeListener(final DocumentFilterListener listener) { + listeners.remove(listener); + } + + @Override + public void removeAllListeners() { + listeners.clear(); + } + + @Override + public void precache(final Document document, final Set filtered) { + cache.put(document, filtered); + } +} diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/filters/AbstractImageDocumentFilter.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/filters/AbstractImageDocumentFilter.java new file mode 100644 index 000000000..e51374c69 --- /dev/null +++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/filters/AbstractImageDocumentFilter.java @@ -0,0 +1,50 @@ +package org.icepdf.ri.common.filters; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.function.Supplier; + +/** + * Filter using images of the document pages + */ +public abstract class AbstractImageDocumentFilter extends AbstractDocumentFilter { + //Only use one thread and service because multithreaded access to images has bad performance + private static final ExecutorService SERVICE = Executors.newSingleThreadExecutor(r -> { + final Thread thread = new Thread(r); + thread.setName("image-pages-filter-thread"); + thread.setDaemon(true); + return thread; + }); + + /** + * Instantiates the document filter + * + * @param parameters The filter parameters + */ + protected AbstractImageDocumentFilter(final Map parameters) { + super(parameters); + } + + /** + * Runs the given task + * + * @param supplier The task + * @param The type of the supplier + * @return A completable future for the task + */ + protected CompletableFuture supply(final Supplier supplier) { + return CompletableFuture.supplyAsync(supplier, SERVICE); + } + + /** + * Runs the given task + * + * @param runnable The task + * @return A completable future for the task + */ + protected CompletableFuture run(final Runnable runnable) { + return CompletableFuture.runAsync(runnable, SERVICE); + } +} diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/filters/DocumentFilter.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/filters/DocumentFilter.java new file mode 100644 index 000000000..0dd860a5a --- /dev/null +++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/filters/DocumentFilter.java @@ -0,0 +1,67 @@ +package org.icepdf.ri.common.filters; + +import org.icepdf.core.pobjects.Document; + +import java.util.Map; +import java.util.Set; + +/** + * Represents an object which will filter the pages of a given document + */ +public interface DocumentFilter { + + /** + * Filters the pages of the given document + * + * @param document The document + * @return The set of page indexes that passed the filter + */ + Set filterPages(final Document document); + + /** + * Returns whether the filter is ready for the given document + * + * @param document The document + * @return Whether the filter has the results + */ + boolean isReady(final Document document); + + /** + * @return The filter parameters + */ + Map getParameters(); + + /** + * Adds a listener to this filter + * + * @param listener The listener + */ + void addListener(final DocumentFilterListener listener); + + /** + * Removes a listener from this filter + * + * @param listener The listener + */ + void removeListener(final DocumentFilterListener listener); + + /** + * Removes all listeners from this filter + */ + void removeAllListeners(); + + /** + * Precaches a result (in case there is heavy computation required and the result is already saved somewhere) + * + * @param document The document + * @param filtered The pre-computed set of pages that passed the filter + */ + void precache(final Document document, final Set filtered); + + /** + * Stops the filtering (e.g. document was closed) + * + * @param document The document + */ + void interrupt(final Document document); +} diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/filters/DocumentFilterListener.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/filters/DocumentFilterListener.java new file mode 100644 index 000000000..28c16ac31 --- /dev/null +++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/filters/DocumentFilterListener.java @@ -0,0 +1,27 @@ +package org.icepdf.ri.common.filters; + +import org.icepdf.core.pobjects.Document; + +import java.util.Set; + +/** + * Represents a listener of DocumentFilter + */ +public interface DocumentFilterListener { + /** + * Sent when a filtering has started + * + * @param filter The filter + * @param document The document the filter has started filtering + */ + void documentFilterStarted(final DocumentFilter filter, final Document document); + + /** + * Sent when a filtering has ended + * + * @param filter The filter + * @param document The document the filter has finished filtering + * @param filteredPages The indexes of the pages that passed the filter + */ + void documentFilterCompleted(final DocumentFilter filter, final Document document, final Set filteredPages); +} diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/filters/EmptyPagesFilter.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/filters/EmptyPagesFilter.java new file mode 100644 index 000000000..c3992cb05 --- /dev/null +++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/filters/EmptyPagesFilter.java @@ -0,0 +1,153 @@ +package org.icepdf.ri.common.filters; + + +import org.icepdf.core.pobjects.Document; +import org.icepdf.core.pobjects.Page; +import org.icepdf.core.pobjects.PageTree; + +import java.awt.*; +import java.awt.image.BufferedImage; +import java.util.*; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static org.icepdf.core.util.GraphicsRenderingHints.SCREEN; + +/** + * Implementation of a filter filtering empty pages from a document + */ +public class EmptyPagesFilter extends AbstractImageDocumentFilter { + + private static final Logger logger = Logger.getLogger(EmptyPagesFilter.class.getName()); + + private final double minColoredPixelsFraction; + private final int maxRedValue; + private final int maxGreenValue; + private final int maxBlueValue; + + + private final Map cancellations; + + private final Map> futures; + + /** + * Instantiates the filter + * + * @param minColoredPixelsFraction The minimum fraction of pixels passing the color test to consider that a page is not empty + * @param maxColorValue The maximum red, green or blue value that a pixel can have to be considered non-white (white = 255) + */ + public EmptyPagesFilter(final double minColoredPixelsFraction, final int maxColorValue) { + this(minColoredPixelsFraction, maxColorValue, maxColorValue, maxColorValue); + } + + /** + * Instantiates the filter + * + * @param minColoredPixelsFraction The minimum fraction of pixels passing the color test to consider that a page is not empty + * @param maxRedValue The maximum red value that a pixel can have to be considered non-white (white = 255) + * @param maxGreenValue The maximum green value that a pixel can have to be considered non-white (white = 255) + * @param maxBlueValue The maximum blue value that a pixel can have to be considered non-white (white = 255) + */ + public EmptyPagesFilter(final double minColoredPixelsFraction, final int maxRedValue, final int maxGreenValue, final int maxBlueValue) { + super(prepareParameters(minColoredPixelsFraction, maxRedValue, maxGreenValue, maxBlueValue)); + this.minColoredPixelsFraction = minColoredPixelsFraction; + this.maxRedValue = maxRedValue; + this.maxGreenValue = maxGreenValue; + this.maxBlueValue = maxBlueValue; + this.futures = new ConcurrentHashMap<>(); + this.cancellations = new ConcurrentHashMap<>(); + } + + private static Map prepareParameters(final double minColoredPixelsFraction, final int maxRedValue, + final int maxGreenValue, final int maxBlueValue) { + final Map parameters = new HashMap<>(5); + parameters.put("minColoredPixelsFraction", minColoredPixelsFraction); + parameters.put("maxRedValue", maxRedValue); + parameters.put("maxGreenValue", maxGreenValue); + parameters.put("maxBlueValue", maxBlueValue); + return parameters; + } + + @Override + protected Set filterPagesUncached(final Document document) { + if (document != null) { + cancellations.put(document, new AtomicBoolean(false)); + final int numberOfPages = document.getNumberOfPages(); + final Set filtered = new HashSet<>(numberOfPages); + final PageTree pt = document.getPageTree(); + if (pt != null) { + final Set> allFutures = new HashSet<>(); + for (int i = 0; i < pt.getNumberOfPages(); ++i) { + final int index = i; + final CompletableFuture future = supply(() -> filterPage(document, index)); + future.whenComplete((ret, t) -> { + if (ret) { + filtered.add(index); + } + }); + allFutures.add(future); + } + futures.put(document, CompletableFuture.allOf(allFutures.toArray(new CompletableFuture[0]))); + try { + futures.get(document).get(); + logger.info("Filtering done for " + document.getDocumentOrigin() + " (" + numberOfPages + " pages)"); + } catch (final ExecutionException e) { + logger.log(Level.SEVERE, "Filtering failed for " + document.getDocumentOrigin(), e); + } catch (final InterruptedException | CancellationException e) { + logger.log(Level.INFO, "Filtering cancelled for " + document.getDocumentOrigin()); + } finally { + futures.remove(document); + cancellations.remove(document); + } + } + return filtered; + } else { + return Collections.emptySet(); + } + } + + private boolean filterPage(final Document document, final int index) { + if (!cancellations.computeIfAbsent(document, d -> new AtomicBoolean(true)).get()) { + try { + final BufferedImage image = (BufferedImage) document.getPageImage(index, SCREEN, Page.BOUNDARY_CROPBOX, 0, 0.5f); + final int total = image.getWidth() * image.getHeight(); + int pixelCount = 0; + for (int x = 0; x < image.getWidth(); ++x) { + for (int y = 0; y < image.getHeight(); ++y) { + final Color color = new Color(image.getRGB(x, y), false); + if (color.getRed() < maxRedValue || color.getGreen() < maxGreenValue || color.getBlue() < maxBlueValue) { + pixelCount++; + } + } + } + final double ratio = (double) pixelCount / total; + return ratio > minColoredPixelsFraction; + } catch (final InterruptedException e) { + Thread.currentThread().interrupt(); + return true; + } + } else { + return true; + } + } + + @Override + public String toString() { + return "EmptyPagesFilter"; + } + + @Override + public void interrupt(final Document document) { + final CompletableFuture future = futures.getOrDefault(document, null); + if (future != null && !future.isDone() && !future.isCancelled()) { + future.cancel(true); + } + final AtomicBoolean cancelled = cancellations.computeIfAbsent(document, d -> new AtomicBoolean(true)); + cancelled.set(true); + } +} diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/search/DocumentSearchControllerImpl.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/search/DocumentSearchControllerImpl.java index 5fb502eb7..6b96066b8 100644 --- a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/search/DocumentSearchControllerImpl.java +++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/search/DocumentSearchControllerImpl.java @@ -905,7 +905,7 @@ public void clearSearchHighlight(int pageIndex) { // clear cache and terms list searchModel.clearSearchResults(pageIndex); PageViewComponentImpl pvc = (PageViewComponentImpl) viewerController.getDocumentViewController().getDocumentViewModel() - .getPageComponents().get(pageIndex); + .getAllPageComponents().get(pageIndex); pvc.clearSearchHighlights(); } @@ -918,7 +918,7 @@ public void clearAllSearchHighlight() { searchModel.clearSearchResults(); pageToComponents.forEach((key, shc) -> { PageViewComponentImpl pvc = (PageViewComponentImpl) viewerController.getDocumentViewController().getDocumentViewModel() - .getPageComponents().get(key); + .getAllPageComponents().get(key); pvc.clearSearchHighlights(); }); pageToComponents.clear(); diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/tools/TextSelection.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/tools/TextSelection.java index 6d3751fd3..c2e576022 100644 --- a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/tools/TextSelection.java +++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/tools/TextSelection.java @@ -209,7 +209,7 @@ public void clearSelection() { } public void clearSelectionState() { - java.util.List pages = documentViewController.getDocumentViewModel().getPageComponents(); + java.util.List pages = documentViewController.getDocumentViewModel().getAllPageComponents(); for (AbstractPageViewComponent page : pages) { ((PageViewComponentImpl) page).getTextSelectionPageHandler().clearSelection(); } diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/tools/TextSelectionViewHandler.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/tools/TextSelectionViewHandler.java index c4780c0a5..c22d138a6 100644 --- a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/tools/TextSelectionViewHandler.java +++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/tools/TextSelectionViewHandler.java @@ -239,7 +239,7 @@ public void mouseDragged(MouseEvent e) { // add selection box to child pages java.util.List pages = - documentViewModel.getPageComponents(); + documentViewModel.getFilteredPageComponents(); for (AbstractPageViewComponent page : pages) { Rectangle tmp = SwingUtilities.convertRectangle( parentComponent, getRectToDraw(), page); diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/tools/ZoomInViewHandler.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/tools/ZoomInViewHandler.java index 318fc8d7f..2e854f417 100644 --- a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/tools/ZoomInViewHandler.java +++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/tools/ZoomInViewHandler.java @@ -62,7 +62,7 @@ public void mouseDragged(MouseEvent e) { DocumentViewModel documentViewModel = documentViewController.getDocumentViewModel(); if (documentViewModel != null) { java.util.List pages = - documentViewModel.getPageComponents(); + documentViewModel.getFilteredPageComponents(); for (AbstractPageViewComponent page : pages) { Rectangle tmp = SwingUtilities.convertRectangle( parentComponent, getRectToDraw(), page); @@ -129,7 +129,7 @@ public void mouseReleased(MouseEvent e) { // deselect rectangles on other selected pages. // consider only repainting visible pages. List selectedPages = - documentViewController.getDocumentViewModel().getPageComponents(); + documentViewController.getDocumentViewModel().getAllPageComponents(); if (selectedPages != null && selectedPages.size() > 0) { for (AbstractPageViewComponent pageComp : selectedPages) { diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/utility/layers/LayersPanel.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/utility/layers/LayersPanel.java index 1765bc896..8114edb9f 100644 --- a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/utility/layers/LayersPanel.java +++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/utility/layers/LayersPanel.java @@ -159,7 +159,7 @@ public void mouseClicked(MouseEvent e) { // the current page and repaint DocumentViewController documentViewController = controller.getDocumentViewController(); DocumentViewModel documentViewModel = documentViewController.getDocumentViewModel(); - List pages = documentViewModel.getPageComponents(); + List pages = documentViewModel.getAllPageComponents(); AbstractPageViewComponent page = pages.get(documentViewModel.getViewCurrentPageIndex()); // resort page text as layer visibility will have changed. try { diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/AbstractDocumentView.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/AbstractDocumentView.java index 58b9aa846..738a973cc 100644 --- a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/AbstractDocumentView.java +++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/AbstractDocumentView.java @@ -225,6 +225,11 @@ public void dispose() { */ public abstract void updateDocumentView(); + /** + * Received when the list of displayed pages has changed + */ + public abstract void pagesListChanged(); + public ToolHandler uninstallCurrentTool() { if (currentTool != null) { currentTool.uninstallTool(); diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/AbstractDocumentViewModel.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/AbstractDocumentViewModel.java index 237eafd45..895c9e69d 100644 --- a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/AbstractDocumentViewModel.java +++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/AbstractDocumentViewModel.java @@ -29,7 +29,9 @@ import java.util.HashMap; import java.util.List; import java.util.Set; +import java.util.function.Predicate; import java.util.logging.Logger; +import java.util.stream.Collectors; /** *

The AbstractDocumentViewModel is responsible for keeping the state of the @@ -55,8 +57,10 @@ public abstract class AbstractDocumentViewModel implements DocumentViewModel { private HashMap selectedPageText; // select all state flag, optimization for painting select all state lazily private boolean selectAll; - protected List pageComponents; + protected List allComponents; + protected List filteredComponents; protected HashMap> documentViewAnnotationComponents; + // scroll pane used to contain the view protected JScrollPane documentViewScrollPane; // annotation memento caretaker @@ -90,8 +94,16 @@ public Document getDocument() { return currentDocument; } - public List getPageComponents() { - return pageComponents; + public List getFilteredPageComponents() { + return filteredComponents; + } + + public List getAllPageComponents() { + return allComponents; + } + + public void filterPageComponents(final Predicate filter) { + filteredComponents = allComponents.stream().filter(filter).collect(Collectors.toList()); } @@ -296,8 +308,8 @@ public int getPageBoundary() { } public Rectangle getPageBounds(int pageIndex) { - if (pageComponents != null && pageIndex < pageComponents.size()) { - Component pageViewComponentImpl = pageComponents.get(pageIndex); + if (allComponents != null && pageIndex < allComponents.size()) { + Component pageViewComponentImpl = allComponents.get(pageIndex); if (pageViewComponentImpl != null && pageViewComponentImpl.getParent() != null) { Rectangle pageBounds = pageViewComponentImpl.getParent().getBounds(); return pageBounds; @@ -308,13 +320,14 @@ public Rectangle getPageBounds(int pageIndex) { public void dispose() { - if (pageComponents != null) { - for (AbstractPageViewComponent pageComponent : pageComponents) { + if (allComponents != null) { + for (AbstractPageViewComponent pageComponent : allComponents) { if (pageComponent != null) { pageComponent.dispose(); } } - pageComponents.clear(); + allComponents.clear(); + filteredComponents.clear(); } if (documentViewAnnotationComponents != null) { documentViewAnnotationComponents.clear(); diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/AbstractPageViewComponent.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/AbstractPageViewComponent.java index 3cead268a..8c0354e10 100644 --- a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/AbstractPageViewComponent.java +++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/AbstractPageViewComponent.java @@ -225,7 +225,7 @@ public void updateView(String propertyConstant, Object oldValue, Object newValue * @throws NullPointerException if the parent scrollPane is null. */ private boolean isPageIntersectViewport() { - Rectangle pageBounds = (documentViewModel != null && documentViewModel.getPageComponents() != null) ? + Rectangle pageBounds = (documentViewModel != null && documentViewModel.getFilteredPageComponents() != null) ? documentViewModel.getPageBounds(pageIndex) : getBounds(); JScrollPane parentScrollPane = documentViewModel.getDocumentViewScrollPane(); return pageBounds != null && this.isShowing() && diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/CollectionDocumentView.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/CollectionDocumentView.java index b8c9fc6ba..291775328 100644 --- a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/CollectionDocumentView.java +++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/CollectionDocumentView.java @@ -129,6 +129,11 @@ public void updateDocumentView() { } + @Override + public void pagesListChanged() { + + } + public int getNextPageIncrement() { return 0; } diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/DocumentViewControllerImpl.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/DocumentViewControllerImpl.java index 2f0a65f65..e97c144f8 100644 --- a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/DocumentViewControllerImpl.java +++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/DocumentViewControllerImpl.java @@ -35,9 +35,11 @@ import java.beans.PropertyChangeSupport; import java.util.ArrayList; import java.util.List; +import java.util.function.Predicate; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Pattern; +import java.util.stream.Collectors; /** *

The DocumentViewControllerImpl is responsible for controlling the four @@ -116,6 +118,8 @@ public class DocumentViewControllerImpl protected final PropertyChangeSupport changes = new PropertyChangeSupport(this); + private Predicate pageViewComponentFilter = pvc -> true; + public DocumentViewControllerImpl(final SwingController viewerController) { this.viewerController = viewerController; @@ -207,6 +211,7 @@ public void closeDocument() { setZoom(1); setRotation(0); setViewCursor(DocumentViewControllerImpl.CURSOR_DEFAULT); + pageViewComponentFilter = pvc -> true; } public Adjustable getHorizontalScrollBar() { @@ -626,7 +631,7 @@ public void propertyChange(PropertyChangeEvent evt) { if (PropertyConstants.DOCUMENT_VIEW_REFRESH_CHANGE.equals(prop) || PropertyConstants.DOCUMENT_VIEW_ZOOM_CHANGE.equals(prop) || PropertyConstants.DOCUMENT_VIEW_ROTATION_CHANGE.equals(prop)) { - List pageComponents = documentViewModel.getPageComponents(); + List pageComponents = documentViewModel.getAllPageComponents(); for (AbstractPageViewComponent pageViewComponent : pageComponents) { // pass in zoom, rotation etc. or get form model.... pageViewComponent.updateView(prop, oldValue, newValue); @@ -721,6 +726,13 @@ public boolean setCurrentPageIndex(int pageIndex) { pageIndex = document.getNumberOfPages() - 1; } int oldPageIndex = documentViewModel.getViewCurrentPageIndex(); + if (documentViewModel.isFiltered()) { + final List filteredIndexes = documentViewModel.getFilteredPageComponents().stream().map(pvc -> pvc.pageIndex).collect(Collectors.toList()); + final boolean forward = pageIndex > oldPageIndex; + if (!filteredIndexes.contains(pageIndex)) { + pageIndex = getClosestAvailablePage(forward, pageIndex); + } + } changed = documentViewModel.setViewCurrentPageIndex(pageIndex); if (documentView != null) { @@ -758,12 +770,8 @@ public int setCurrentPageNext() { int increment = 0; if (documentViewModel != null) { increment = documentView.getNextPageIncrement(); - int current = documentViewModel.getViewCurrentPageIndex(); - if ((current + increment) < document.getNumberOfPages()) { - documentViewModel.setViewCurrentPageIndex(current + increment); - } else { - documentViewModel.setViewCurrentPageIndex(document.getNumberOfPages() - 1); - } + final int current = documentViewModel.getFilteredCurrentPageIndex(); + documentViewModel.setViewCurrentPageIndex(Math.min((current + increment), documentViewModel.getUpperBoundFilteredPages())); } return increment; } @@ -772,7 +780,7 @@ public int setCurrentPagePrevious() { int decrement = 0; if (documentViewModel != null) { decrement = documentView.getPreviousPageIncrement(); - int current = documentViewModel.getViewCurrentPageIndex(); + int current = documentViewModel.getFilteredCurrentPageIndex(); documentViewModel.setViewCurrentPageIndex(Math.max((current - decrement), 0)); } return decrement; @@ -895,7 +903,7 @@ public boolean setToolMode(final int viewToolMode) { // notify the page components of the tool change. List pageComponents = - documentViewModel.getPageComponents(); + documentViewModel.getAllPageComponents(); for (AbstractPageViewComponent page : pageComponents) { ((PageViewComponentImpl) page).setToolMode(viewToolMode); } @@ -1395,7 +1403,7 @@ public void addNewDestination(Destination destination) { Page page = (Page) library.getObject(destination.getPageReference()); int pageIndex = page.getPageIndex(); PageViewComponentImpl pageViewComponent = (PageViewComponentImpl) - documentViewModel.getPageComponents().get(pageIndex); + documentViewModel.getAllPageComponents().get(pageIndex); pageViewComponent.refreshDestinationComponents(pageViewComponent.getPage(), false); } } @@ -1409,7 +1417,7 @@ public void updateDestination(Destination oldDestination, Destination destinatio // page is the same then we just do the update if (oldDestination.getPageReference().equals(destination.getPageReference())) { PageViewComponentImpl pageViewComponent = (PageViewComponentImpl) - documentViewModel.getPageComponents().get(pageIndex); + documentViewModel.getAllPageComponents().get(pageIndex); List destinationComponents = pageViewComponent.getDestinationComponents(); for (DestinationComponent destinationComponent : destinationComponents) { if (destinationComponent.getDestination().getNamedDestination() @@ -1425,7 +1433,7 @@ public void updateDestination(Destination oldDestination, Destination destinatio Page oldPage = (Page) library.getObject(oldDestination.getPageReference()); int oldPagIndex = oldPage.getPageIndex(); PageViewComponentImpl pageViewComponent = (PageViewComponentImpl) - documentViewModel.getPageComponents().get(oldPagIndex); + documentViewModel.getAllPageComponents().get(oldPagIndex); List destinationComponents = pageViewComponent.getDestinationComponents(); for (DestinationComponent destinationComponent : destinationComponents) { if (destinationComponent.getDestination().getNamedDestination() @@ -1451,7 +1459,7 @@ public void deleteDestination(Destination destination) { Page page = (Page) library.getObject(destination.getPageReference()); int pageIndex = page.getPageIndex(); PageViewComponentImpl pageViewComponent = (PageViewComponentImpl) - documentViewModel.getPageComponents().get(pageIndex); + documentViewModel.getAllPageComponents().get(pageIndex); List destinationComponents = pageViewComponent.getDestinationComponents(); for (DestinationComponent destinationComponent : destinationComponents) { if (destinationComponent.getDestination().getNamedDestination().equals(destination.getNamedDestination())) { @@ -1479,4 +1487,83 @@ public void redo() { public void removePropertyChangeListener(PropertyChangeListener l) { changes.removePropertyChangeListener(l); } + + public void filterPageComponents(final Predicate filter) { + this.pageViewComponentFilter = filter; + final List oldComponents = new ArrayList<>(documentViewModel.getFilteredPageComponents()); + documentViewModel.filterPageComponents(pageViewComponentFilter); + final List filteredComponents = documentViewModel.getFilteredPageComponents(); + if (!filteredComponents.equals(oldComponents)) { + final int currentIndex = documentViewModel.getViewCurrentPageIndex(); + if (!filteredComponents.contains(documentViewModel.getAllPageComponents().get(currentIndex))) { + fixCurrentViewPageAfterFilter(); + } + ((AbstractDocumentView) documentView).pagesListChanged(); + ((JComponent) documentView).invalidate(); + ((JComponent) documentView).firePropertyChange(PropertyConstants.DOCUMENT_VIEW_PAGES_CHANGE, viewType, viewType); + ((JComponent) documentView).revalidate(); + documentView.repaint(); + } + } + + private int getClosestAvailablePage(final boolean forward, final int currentIndex) { + final List allComponents = documentViewModel.getAllPageComponents(); + final List filteredComponents = documentViewModel.getFilteredPageComponents(); + final int upperBound = documentViewModel.getUpperBoundFilteredPages(); + final int lowerBound = documentViewModel.getLowerBoundFilteredPages(); + int newIdx = -1; + if (forward) { + for (int i = currentIndex; i <= upperBound && newIdx == -1; ++i) { + if (filteredComponents.contains(allComponents.get(i))) { + newIdx = i; + } + } + if (newIdx == -1 && !filteredComponents.isEmpty()) { + newIdx = upperBound; + } + } else { + for (int i = currentIndex; i >= lowerBound && newIdx == -1; --i) { + if (filteredComponents.contains(allComponents.get(i))) { + newIdx = i; + } + } + if (newIdx == -1 && !filteredComponents.isEmpty()) { + newIdx = lowerBound; + } + } + if (newIdx == -1 && !filteredComponents.isEmpty()) { + newIdx = filteredComponents.get(0).getPageIndex(); + } + return newIdx; + } + + private void fixCurrentViewPageAfterFilter() { + final List allComponents = documentViewModel.getAllPageComponents(); + final List filteredComponents = documentViewModel.getFilteredPageComponents(); + final int currentIndex = documentViewModel.getViewCurrentPageIndex(); + final int newIdx; + final int upperBound = documentViewModel.getUpperBoundFilteredPages(); + final int lowerBound = documentViewModel.getLowerBoundFilteredPages(); + if (upperBound > currentIndex) { + int tmpIdx = -1; + for (int i = currentIndex; i <= upperBound && tmpIdx == -1; ++i) { + if (filteredComponents.contains(allComponents.get(i))) { + tmpIdx = i; + } + } + newIdx = tmpIdx; + } else if (lowerBound < currentIndex) { + int tmpIdx = -1; + for (int i = currentIndex; i >= lowerBound && tmpIdx == -1; --i) { + if (filteredComponents.contains(allComponents.get(i))) { + tmpIdx = i; + } + } + newIdx = tmpIdx; + } else { + newIdx = -1; + } + documentViewModel.setViewCurrentPageIndex(newIdx); + } + } diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/DocumentViewModel.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/DocumentViewModel.java index eef703a2b..78df07afc 100644 --- a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/DocumentViewModel.java +++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/DocumentViewModel.java @@ -24,6 +24,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.function.Predicate; /** * The DocumentViewModel interface contains common accessors and modifiers needed @@ -172,11 +173,26 @@ public interface DocumentViewModel { void clearSelectedPageText(); /** - * Gets the page components associated with this view model. + * Gets the filtered page components associated with this view model. * * @return vector of page components. */ - List getPageComponents(); + List getFilteredPageComponents(); + + /** + * Gets all the page components associated with this view model. + * + * @return vector of page components. + */ + List getAllPageComponents(); + + /** + * Filters the page components using the given predicate + * + * @param filter The predicate to use + */ + void filterPageComponents(final Predicate filter); + HashMap> getDocumentViewAnnotationComponents(); @@ -214,6 +230,34 @@ public interface DocumentViewModel { */ int getViewCurrentPageIndex(); + /** + * @return The index of the current page in the filtered page components + */ + default int getFilteredCurrentPageIndex() { + return isFiltered() ? getFilteredPageComponents().indexOf(getAllPageComponents().get(getViewCurrentPageIndex())) : getViewCurrentPageIndex(); + } + + /** + * @return The lower bound of the pages index in the filtered page components + */ + default int getLowerBoundFilteredPages() { + return getFilteredPageComponents().isEmpty() ? -1 : getFilteredPageComponents().get(0).getPageIndex(); + } + + /** + * @return The upper bound of the pages index in the filtered page components + */ + default int getUpperBoundFilteredPages() { + return getFilteredPageComponents().isEmpty() ? -1 : getFilteredPageComponents().get(getFilteredPageComponents().size() - 1).getPageIndex(); + } + + /** + * @return Whether the page components are currently filtered (i.e. filteredPageComponents != allPageComponents) + */ + default boolean isFiltered() { + return getFilteredPageComponents().size() != getAllPageComponents().size(); + } + /** * Sets the models zoom level. * diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/DocumentViewModelImpl.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/DocumentViewModelImpl.java index 8f0567b59..84eac4c12 100644 --- a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/DocumentViewModelImpl.java +++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/DocumentViewModelImpl.java @@ -46,7 +46,7 @@ public DocumentViewModelImpl(Document document) { int avgPageHeight = 0; // add components for every page in the document - pageComponents = new ArrayList<>(numberOfPages); + allComponents = new ArrayList<>(numberOfPages); for (int i = 0; i < numberOfPages; i++) { // also a way to pass in an average document size. if (i < MAX_PAGE_SIZE_READ_AHEAD) { @@ -66,8 +66,9 @@ public DocumentViewModelImpl(Document document) { pageViewComponent = buildPageViewComponent(this, pageTree, i, avgPageWidth, avgPageHeight); } - pageComponents.add(pageViewComponent); + allComponents.add(pageViewComponent); } + filteredComponents = new ArrayList<>(allComponents); } } diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/OneColumnPageView.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/OneColumnPageView.java index 131939b5e..0439ad047 100644 --- a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/OneColumnPageView.java +++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/OneColumnPageView.java @@ -55,7 +55,7 @@ public OneColumnPageView(DocumentViewController documentDocumentViewController, // add the first of many tools need for this views and others like it. currentPageChanger = new CurrentPageChanger(documentScrollpane, this, - documentViewModel.getPageComponents()); + documentViewModel.getAllPageComponents()); // add page changing key listeners keyListenerPageChanger = @@ -69,7 +69,7 @@ private void buildGUI() { this.setBorder(new EmptyBorder(layoutInserts, layoutInserts, layoutInserts, layoutInserts)); List pageComponents = - documentViewController.getDocumentViewModel().getPageComponents(); + documentViewController.getDocumentViewModel().getFilteredPageComponents(); if (pageComponents != null) { for (AbstractPageViewComponent pageViewComponent : pageComponents) { @@ -92,6 +92,13 @@ private void buildGUI() { public void updateDocumentView() { } + @Override + public void pagesListChanged() { + dispose(); + disposing = false; + buildGUI(); + } + /** * Returns a next page increment of one. */ diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/OnePageView.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/OnePageView.java index f426bd1ff..85461a622 100644 --- a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/OnePageView.java +++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/OnePageView.java @@ -72,9 +72,9 @@ private void buildGUI() { public void updateDocumentView() { DocumentViewModel documentViewModel = documentViewController.getDocumentViewModel(); - java.util.List pageComponents = documentViewModel.getPageComponents(); + java.util.List pageComponents = documentViewModel.getFilteredPageComponents(); if (pageComponents != null) { - AbstractPageViewComponent pageViewComponent = pageComponents.get(documentViewModel.getViewCurrentPageIndex()); + AbstractPageViewComponent pageViewComponent = pageComponents.get(documentViewModel.getFilteredCurrentPageIndex()); if (pageViewComponent != null) { // remove old component this.removeAll(); @@ -98,6 +98,11 @@ public void updateDocumentView() { } } + @Override + public void pagesListChanged() { + updateDocumentView(); + } + protected JComponent buildPageDecoration(AbstractPageViewComponent pageViewComponent) { return new PageViewDecorator(pageViewComponent); } diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/PageComponentSelector.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/PageComponentSelector.java index 38e6f2606..394551c67 100644 --- a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/PageComponentSelector.java +++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/PageComponentSelector.java @@ -71,7 +71,7 @@ public static AnnotationComponent SelectAnnotationComponent(Controller controlle // so we need to do a deep search for the annotation. Document document = controller.getDocument(); java.util.List pageViewComponentList = - controller.getDocumentViewController().getDocumentViewModel().getPageComponents(); + controller.getDocumentViewController().getDocumentViewModel().getAllPageComponents(); int pages = controller.getDocument().getPageTree().getNumberOfPages(); boolean found = false; int pageIndex; @@ -126,7 +126,7 @@ public static DestinationComponent SelectDestinationComponent(Controller control // so we need to do a deep search for the annotation. Document document = controller.getDocument(); java.util.List pageViewComponentList = - controller.getDocumentViewController().getDocumentViewModel().getPageComponents(); + controller.getDocumentViewController().getDocumentViewModel().getAllPageComponents(); Reference pageReference = destination.getPageReference(); if (pageReference != null) { Library library = document.getCatalog().getLibrary(); @@ -155,8 +155,6 @@ public static DestinationComponent SelectDestinationComponent(Controller control public static int AssignAnnotationPage(Controller controller, Annotation widgetAnnotation) { Document document = controller.getDocument(); - java.util.List pageViewComponentList = - controller.getDocumentViewController().getDocumentViewModel().getPageComponents(); int pages = controller.getDocument().getPageTree().getNumberOfPages(); int pageIndex; for (pageIndex = 0; pageIndex < pages; pageIndex++) { diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/TwoColumnPageView.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/TwoColumnPageView.java index 036769b4b..eb6205e2a 100644 --- a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/TwoColumnPageView.java +++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/TwoColumnPageView.java @@ -59,7 +59,7 @@ public TwoColumnPageView(DocumentViewController documentDocumentViewController, // add the first of many tools needed for this view and others like it. currentPageChanger = new CurrentPageChanger(documentScrollpane, this, - documentViewModel.getPageComponents()); + documentViewModel.getAllPageComponents()); // add page changing key listeners keyListenerPageChanger = @@ -78,7 +78,7 @@ private void buildGUI() { // finally add all the components // add components for every page in the document java.util.List pageComponents = - documentViewController.getDocumentViewModel().getPageComponents(); + documentViewController.getDocumentViewModel().getFilteredPageComponents(); if (pageComponents != null) { AbstractPageViewComponent pageViewComponent; @@ -102,6 +102,13 @@ private void buildGUI() { public void updateDocumentView() { } + @Override + public void pagesListChanged() { + dispose(); + disposing=false; + buildGUI(); + } + /** * Returns a next page increment of two. */ diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/TwoPageView.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/TwoPageView.java index 9021c79fa..7fef3fe16 100644 --- a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/TwoPageView.java +++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/TwoPageView.java @@ -71,7 +71,7 @@ public TwoPageView(DocumentViewController documentDocumentViewController, // add the first of many tools need for this views and others like it. currentPageChanger = new CurrentPageChanger(documentScrollpane, this, - documentViewModel.getPageComponents(), + documentViewModel.getAllPageComponents(), false); } @@ -89,7 +89,7 @@ private void buildGUI() { public void updateDocumentView() { DocumentViewModel documentViewModel = documentViewController.getDocumentViewModel(); - java.util.List pageComponents = documentViewModel.getPageComponents(); + java.util.List pageComponents = documentViewModel.getFilteredPageComponents(); if (pageComponents != null) { // remove old component @@ -97,7 +97,7 @@ public void updateDocumentView() { AbstractPageViewComponent pageViewComponent; int count = 0; - int index = documentViewModel.getViewCurrentPageIndex(); + int index = documentViewModel.getFilteredCurrentPageIndex(); int docLength = pageComponents.size(); // adjust for 2 up view, so we don't page again to the 3 pages... @@ -136,6 +136,11 @@ public void updateDocumentView() { } } + @Override + public void pagesListChanged() { + updateDocumentView(); + } + /** * Returns a next page increment of two. */ diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/annotations/summary/AnnotationSummaryBox.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/annotations/summary/AnnotationSummaryBox.java index 96449d97b..482f5e2f6 100644 --- a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/annotations/summary/AnnotationSummaryBox.java +++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/annotations/summary/AnnotationSummaryBox.java @@ -122,7 +122,7 @@ public JPopupMenu getContextMenu(Frame frame, DraggableAnnotationPanel.MouseHand int pageIndex = annotation.getParent().getPageIndex(); PageViewComponentImpl pageViewComponent = (PageViewComponentImpl) documentViewController.getParentController().getDocumentViewController() - .getDocumentViewModel().getPageComponents().get(pageIndex); + .getDocumentViewModel().getAllPageComponents().get(pageIndex); pageViewComponent.refreshAnnotationComponents(pageViewComponent.getPage(), false); ArrayList comps = pageViewComponent.getAnnotationComponents(); for (AbstractAnnotationComponent abstractComp : comps) { diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/annotations/summary/ColorLabelPanel.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/annotations/summary/ColorLabelPanel.java index 65cd40c3e..f399def6e 100644 --- a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/annotations/summary/ColorLabelPanel.java +++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/annotations/summary/ColorLabelPanel.java @@ -59,7 +59,7 @@ public void addAnnotation(MarkupAnnotation markupAnnotation) { PopupAnnotation popupAnnotation = markupAnnotation.getPopupAnnotation(); if (popupAnnotation != null) { List pageComponents = - controller.getDocumentViewController().getDocumentViewModel().getPageComponents(); + controller.getDocumentViewController().getDocumentViewModel().getAllPageComponents(); int pageIndex = markupAnnotation.getPageIndex(); if (pageIndex >= 0) { AnnotationSummaryBox popupAnnotationComponent = diff --git a/viewer/viewer-awt/src/main/resources/org/icepdf/ri/images/filter_pages_a_24.png b/viewer/viewer-awt/src/main/resources/org/icepdf/ri/images/filter_pages_a_24.png new file mode 100644 index 0000000000000000000000000000000000000000..4092324871e0a4533465f7ebd5d5d7508b3b2a0e GIT binary patch literal 773 zcmV+g1N!`lP))N6%92r^cesE0+LBY zK~zY`?Ul<*R8btqKjYj{iGuR|h(cQw8KsLz(gce%A83R`5NeXPmkBoEDCQrg!E#>wa(-=l<^betxfe4*a(S zW;1g*oz9ea{xv26sj8~93~)M~DL;SxHj0ajOjYOR=J78qbmiscya1K}eKUw{6LA}$ zWMpQAZ}r|Z1q=?oWa8}vzS-ICob1dez#?G$3rR8ApOUC103DqjR8&^>E%+C^fK0#< z4KSgQEv~qfNC`7D-zYy`MOJqHweJi5PGBdXSdP?8Tu3M>DIpLD@b$|_%FBqZNZ z3#6{iLa{0y42AG{y(|Soq^6})RJ@P<2M%HgiEc>T+K2<#1*ArsZXGbHX$%jKYDrtFO2!#y?MQLfCSFe z*2LC6uaDBwG5`)8F2n2f#e7GORwY-Wi>+-aiX8x#tB2aUM(#d%#HpHk?%(NMnXa&) zw&}d7h7vCWAtaCcpQ33RV;+s$eK*#9Q&HVO#mPFBBVy|Sq(lgbAtd#U&77z{jo0hr zPhbf}Nh2dWA8B{691(<;_2e>G{fQ!mKyjq8dv7t3h#;&BLJ=b-9t;~vG+7c8*9}Qn z7pCIz0A?A;&!0ZLztnnRyA2#xE*`txJ`3o8umTYIdATjX9$<%sLs0@jU>=BAy3v4i zU>hqLSTQ3C39729$L)ctssacBKcZ1tdHg^i3X0iZ%bqcp){!<{00000NkvXXu0mjf Dz}Zxm literal 0 HcmV?d00001 diff --git a/viewer/viewer-awt/src/main/resources/org/icepdf/ri/images/filter_pages_a_32.png b/viewer/viewer-awt/src/main/resources/org/icepdf/ri/images/filter_pages_a_32.png new file mode 100644 index 0000000000000000000000000000000000000000..cea9946f069fd75851607fb030945c4e3c00b106 GIT binary patch literal 1133 zcmV-z1d{uSP))O6EfFH!219I1Nuos zK~z}7?U!w6TU8jxfA=PBlO}Dd)vhm6yJ@=)1sSZct!OJ{FVa@YhST{$5Qo<}L{O|0 zhTE)_6+ehC6e9>CiiOU0%oJO+lW9>b2%;_N;+Ir&>!i>nv#CpR&pCc*h|5?>n#NCh zKHSUUKKJ~8&wZYA?(@L^%!NxQHBHk~wpn4EPRBbp zRd|*F0f=l6S{BcBbK#Qp*x^TW^7zZi^DHhd1}ZAb50yHdCJ+NGpU)@PddHq+O*_Eh z907iwnZfOHZ41qZ&f0YS@sg5~AaKPnj5Q(jENkky##^N>M~*y6d;9%&EM1O%6ptsG z0S{m|3`5UHK+9cRp#UKj#?fQ!YQ1ms(&fm9@pxh@P@aO&^7b&Rcq-KssL#BQJru&p-w%DD+~+_5C{YSsIRZb>pehCZ5?6L zWOU>VW;o24{~WmT6hW6yE?)M<)} zos5s2P1|g{dpp~AwiPA>pz)OP3|pI<0Wia1PMtW;Twsn{s;Zd_1bF?mm$~%22|&Y^ zCLTQW1WBQ?CSgw7P%3aZoxIr7%jn3bjDJ3cX@+_4-M2Hn(QUN0@8#Z(Zlq=-Y00c# zAV&a&QVP4hhyxElLVL$PzW?r9e*JBpE72&fvT~|x>bdKl796fJ5|$)sNfOBd0Vt(1 zj#qb?hqjLW2&qU4iDfA)AxR3EA)c^AVa|bT#f4N@Lggf$uq09zAPMsok0)i;ZJ1LC zW@ly*QX%Ckj3rh>VhM>Q6+-+2loSF>D3X>WDHQRmE9pOXJ^)J7wCU6Ruei{(5-pGV zL4hoDyP~NoS*;g9*4At~2s8k<0>!zOY1h;&31AWU3y|vtuz)BK0{*yBL6J)erNpDD z_14#zq!QJhy64j+sg&myAg^y!{?*njmsag>K7u9I$IVAG00000NkvXXu0mjfwRQiW literal 0 HcmV?d00001 diff --git a/viewer/viewer-awt/src/main/resources/org/icepdf/ri/images/filter_pages_i_24.png b/viewer/viewer-awt/src/main/resources/org/icepdf/ri/images/filter_pages_i_24.png new file mode 100644 index 0000000000000000000000000000000000000000..ac832b7b25b11c316fd6d66f71ae258bf4a7f74c GIT binary patch literal 576 zcmV-G0>Ax)O02Yd+Ww-zU0nJH7 zK~zY`?Um0<8$lGuzuB(lQVQZq(?l!O7BsdH*o$86Ey05Q2h>ykrkvt!FQJic{($}g zfi4kO56vMPL?Z2>Y)p1%X6Jdag8?_VbLqu*nR)YOzMuK=W(Ih+|82RnK(pB-%d%1j zhGB>>43F#e`iU0VbzPcg-ENm!trj&JjiY%wh7O$W%bnd_Y`p)_oCh(q#EC#exVia_ z(%TKZe6^GcF?7Tw5elyh;4H%5Ki7D-z6t<`kBu>(Dn5-O41)lZ=@j|JMXZ)eSXr@< zNQop#&^tSWHsD*kjW~*C+C0yLVHl`Zt4M^b88!e!Qjz;nE;i1PFzmT3Q=DaN;De?8-0PWLL5D|Re zhwu9{GtV!W;4ID#_?IP#l&IBexE+r%2m(yTW4tg;Sj!fW%Ym~PObbAyE>R)`Ow+{r z+8P+=;EW??Oo=nbG_nX#;z9@r&Qrx%%(AawG#dSI94E_D)O6${=L?{xqG0@q1I zK~z}7?Uv0>6Hyq3pD8n7Ww1XaU=T1ufy#>7g(-#_u7=z265P(cKs2zjiG(P`(i9dz z1w%)gj{KDK=R%xVqhXYZZg_X!$(Qp!dCzyw4BYBg|5?^`4W8${_`^M4E-fv!;sh|o zVlmQYwOWlJ2sVJls1SCPrl?Va;||CQ%?ksuOrouvUVhEl1-)YSAG@XGVN z^gjXkZVsR{Je^gOA`zFlF^1XMSq9vGoP@#c zgW1P<`uqFmfVmqD;mX&_g1t5fAZEcNavk5>dl_HH1%~D=iV8C?&I5|1t z-TU`Qsjl;nmO#B;r_pGHbv~b`)9J9hyo?Y6$8mUGE;E_Sv9YlM!13`h`}=zsVo0D? zt98|`>*6Y&{wlg;8d4gikhkh*t|b`W&+ z-5(yt=okPdCMFme842qT)hc#R56{bG3WWmC=I602i{0JbuqTsFlgVT-M#u9M7mGy( z2M50;5Cp8QuF~{2xk}9Ghe^Njjau zvOo%n5SK_P<4Hgp)AhVMw*4()rBPBKr9w)HkfO_6N)gX3_|aS|g;FY#xeyYgV;I7} zH5Wp}a|DM+N9d~*w9;t(9Ymp|LMn-p@@h?nVDf71rU0*3R#tBM-_qh%xB88~0(EEX U+!KRfrvLx|07*qoM6N<$f^<8B@Bjb+ literal 0 HcmV?d00001 diff --git a/viewer/viewer-awt/src/main/resources/org/icepdf/ri/images/filter_pages_r_24.png b/viewer/viewer-awt/src/main/resources/org/icepdf/ri/images/filter_pages_r_24.png new file mode 100644 index 0000000000000000000000000000000000000000..946b009d7410b1bbb1f4cd0c6b23f145edfd2ac1 GIT binary patch literal 535 zcmV+y0_gpTP))O4Pcd-2%&4<9+ap5_Bs zmNhr;^?CfmCP|v#Ew4?$f6IR6U1Zs<_zk-lHxMX+AL`hA8t6nDBki*gnJY~MN)U#!&^!&0ID-|Va8NC;W?8oO!(TL) Z{s6Z?RESumXTtyh002ovPDHLkV1gm1=End4 literal 0 HcmV?d00001 diff --git a/viewer/viewer-awt/src/main/resources/org/icepdf/ri/images/filter_pages_r_32.png b/viewer/viewer-awt/src/main/resources/org/icepdf/ri/images/filter_pages_r_32.png new file mode 100644 index 0000000000000000000000000000000000000000..9aa12227e66eb63fc79e6208e154dd38c08e49ba GIT binary patch literal 756 zcmV)O843lYknaEh0)a_H zK~z}7?Ur3@6G0S)pPAjY&4+F6jY^{^2nwQ9Q1DX!nLok*;+0p5sI?aHBSDKO(#DpS zL~NTj+3d`j<3%tSO4-;f2-5dzcK7VQ&%S%kuyCRi{bQND0#$wRm(P4?jM*uSQ2$Sg+LBS0g#~dG9k^vBJd;+!WLz&I%L2YNHal#=0eya%_S%b zB$1^u9pOa@s*Av4p#YsuhnGuB?Cv{u4?KHa&(f_r<*A4`NmyT7XLjxk&GofI=HE7d;zVk6Ntxo|DcMhVr@iZl#T75eEQhn_MN-Kn&&TG zQoD4Oc&aos&s@lzg0T`vGa&>;R1q2M`Cb$Wij#o%ikEEAz~sJ~den z9M?Sa`3Z~#a9s1g3%O%BRu33suvSsb;07~bw literal 0 HcmV?d00001 diff --git a/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle.properties b/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle.properties index a78244083..0dce1129c 100644 --- a/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle.properties +++ b/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle.properties @@ -141,6 +141,9 @@ viewer.toolbar.tool.zoomDynamic.label= viewer.toolbar.tool.zoomDynamic.tooltip=Zoom Dynamic Tool viewer.toolbar.tool.zoomOut.label= viewer.toolbar.tool.zoomOut.tooltip=Zoom Out Tool +viewer.toolbar.tool.filterPages.label= +viewer.toolbar.tool.filterPages.tooltip=Filter pages +viewer.toolbar.tool.filterEmptyPages.label=Filter empty pages viewer.toolbar.tool.annotationPreview.label= viewer.toolbar.tool.annotationPreview.tooltip=Annotation Summary Window viewer.toolbar.tool.annotationUtility.label= From 47660583794aba2c5c04ff19297334872355c97d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillaume=20T=C3=A2che?= Date: Fri, 18 Aug 2023 12:14:17 +0200 Subject: [PATCH 2/2] GH-216 Adds translations --- .../org/icepdf/ri/resources/MessageBundle_de.properties | 4 +++- .../org/icepdf/ri/resources/MessageBundle_fr.properties | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_de.properties b/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_de.properties index 5959be0ff..167cda707 100644 --- a/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_de.properties +++ b/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_de.properties @@ -517,4 +517,6 @@ viewer.annotation.signature.properties.dialog.revocation.failure=- Revokations\u viewer.annotation.signature.properties.dialog.certificateExpired.failure=- Signaturzertifikat ist abgelaufen. viewer.annotation.signature.properties.dialog.showCertificates.label=Signaturzertifikat... viewer.annotation.signature.properties.dialog.validity.title=G\u00FCltigkeitszusammenfassung -viewer.annotation.signature.properties.dialog.signerInfo.title=Unterzeichnerinformationen \ No newline at end of file +viewer.annotation.signature.properties.dialog.signerInfo.title=Unterzeichnerinformationen +viewer.toolbar.tool.filterPages.tooltip=Seiten filtern +viewer.toolbar.tool.filterEmptyPages.label=Wei\u00DFe Seiten ausblenden \ No newline at end of file diff --git a/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_fr.properties b/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_fr.properties index 024636356..93b3603e1 100644 --- a/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_fr.properties +++ b/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_fr.properties @@ -508,4 +508,6 @@ viewer.annotation.signature.properties.dialog.revocation.failure=- Aucune v\u00E viewer.annotation.signature.properties.dialog.certificateExpired.failure=- Le certificat du signataire est \u00E9chu. viewer.annotation.signature.properties.dialog.showCertificates.label=Certificat du signataire... viewer.annotation.signature.properties.dialog.validity.title=R\u00E9sum\u00E9 de la validation -viewer.annotation.signature.properties.dialog.signerInfo.title=Informations sur le signataire \ No newline at end of file +viewer.annotation.signature.properties.dialog.signerInfo.title=Informations sur le signataire +viewer.toolbar.tool.filterPages.tooltip=Filtrer les pages +viewer.toolbar.tool.filterEmptyPages.label=Cacher les pages blanches \ No newline at end of file