Skip to content

Commit

Permalink
GH-216 Adds empty pages filter
Browse files Browse the repository at this point in the history
  • Loading branch information
Guillaume Tâche committed Jul 25, 2023
1 parent d1cbfb2 commit 6667986
Show file tree
Hide file tree
Showing 37 changed files with 733 additions and 63 deletions.
8 changes: 8 additions & 0 deletions core/core-awt/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@
<artifactId>maven-surefire-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
</plugins>
</build>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",

Expand Down
1 change: 1 addition & 0 deletions licenses/filter_pages_icon.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
https://commons.wikimedia.org/wiki/File:OOjs_UI_icon_eye.svg
1 change: 1 addition & 0 deletions licenses/mail_icon.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
https://iconarchive.com/show/web-blog-icons-by-semlabs/mail2-send-icon.html
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand All @@ -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<DocumentFilter, Boolean> filters;

private JMenuItem openFileMenuItem;
private JMenu recentFilesSubMenu;
private JMenuItem openURLMenuItem;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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));
}

/**
Expand Down Expand Up @@ -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
*
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 ||
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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();
}
}

/**
Expand Down Expand Up @@ -3198,6 +3245,7 @@ public void closeDocument() {

// free the document
if (document != null) {
filters.forEach((f, b) -> f.interrupt(document));
document.dispose();
document = null;
}
Expand Down Expand Up @@ -3337,6 +3385,7 @@ public void dispose() {
panToolButton = null;
zoomInToolButton = null;
zoomDynamicToolButton = null;
filterPagesButton = null;
textSelectToolButton = null;
selectToolButton = null;
highlightAnnotationToolButton = null;
Expand Down Expand Up @@ -3422,6 +3471,7 @@ public void dispose() {
viewModel = null;

windowManagementCallback = null;
filters.keySet().forEach(df -> df.removeListener(this));
}

/**
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -5213,6 +5270,23 @@ else if (source == facingPageViewNonContinuousButton) {
}
}

protected void filterDocumentPages() {
final Set<Integer> 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<Integer> keptIndexes = df.filterPages(document);
filteredIndexes.removeIf(i -> !keptIndexes.contains(i));
}
});
documentViewController.filterPageComponents(pvc -> filteredIndexes.contains(pvc.getPageIndex()));
}

protected Set<DocumentFilter> 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;
Expand Down Expand Up @@ -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<Integer> filteredPages) {
if (this.document == document) {
setEnabled(filterPagesButton, !isPdfCollection() && document != null && areFiltersReady());
}
}


@FunctionalInterface
protected interface ActionMethod {
void doAction();
Expand Down Expand Up @@ -5552,6 +5642,7 @@ public void keyTyped(KeyEvent e) {
currentPageNumberTextField.setText(modelValue);
}
}

}

/**
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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) {
Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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);
Expand Down
Loading

0 comments on commit 6667986

Please sign in to comment.