diff --git a/core/core-awt/src/main/java/org/icepdf/core/pobjects/fonts/zfont/cmap/CMap.java b/core/core-awt/src/main/java/org/icepdf/core/pobjects/fonts/zfont/cmap/CMap.java
index 354cb791f..004d46ea5 100644
--- a/core/core-awt/src/main/java/org/icepdf/core/pobjects/fonts/zfont/cmap/CMap.java
+++ b/core/core-awt/src/main/java/org/icepdf/core/pobjects/fonts/zfont/cmap/CMap.java
@@ -512,7 +512,7 @@ public synchronized void init() {
try {
cMapInputStream.close();
} catch (IOException e) {
- logger.log(Level.FINE, "Error clossing cmap stream", e);
+ logger.log(Level.FINE, "Error closing cmap stream", e);
}
}
}
diff --git a/pom.xml b/pom.xml
index e2efd64f3..5a0597cf4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -28,6 +28,7 @@
3.0.4
2.0.27
5.9.3
+ 1.2
@@ -167,6 +168,10 @@
org.apache.maven.plugins
maven-compiler-plugin
3.7.0
+
+
+ 11
+
org.codehaus.mojo
diff --git a/viewer/viewer-awt/build.gradle b/viewer/viewer-awt/build.gradle
index f63835401..7bcf6ed97 100644
--- a/viewer/viewer-awt/build.gradle
+++ b/viewer/viewer-awt/build.gradle
@@ -22,6 +22,10 @@ dependencies {
implementation 'org.bouncycastle:bcprov-jdk15on:' + "${BOUNCY_VERSION}"
implementation 'org.bouncycastle:bcprov-ext-jdk15on:' + "${BOUNCY_VERSION}"
implementation 'org.bouncycastle:bcpkix-jdk15on:' + "${BOUNCY_VERSION}"
+ compile 'com.github.lookfirst:sardine:5.12'
+ compile 'org.apache.tika:tika-core:1.23'
+ compile 'jakarta.xml.bind:jakarta.xml.bind-api:2.3.3'
+ compile 'org.glassfish.jaxb:jaxb-runtime:2.3.3'
}
// generatePomFileForViewerJarPublication
@@ -101,4 +105,4 @@ task javadocJar(type: Jar, dependsOn: 'javadoc') {
artifacts {
archives sourcesJar
archives javadocJar
-}
+}
\ No newline at end of file
diff --git a/viewer/viewer-awt/pom.xml b/viewer/viewer-awt/pom.xml
index bfefcaa23..4d5a46a59 100644
--- a/viewer/viewer-awt/pom.xml
+++ b/viewer/viewer-awt/pom.xml
@@ -1,56 +1,78 @@
-
-
- 4.0.0
-
- com.github.pcorless.icepdf
- viewer
- 7.2.0-SNAPSHOT
-
- icepdf-viewer
- jar
- ICEpdf :: Viewer : Swing/AWT Viewer RI
-
- ICEpdf Java Swing/AWT reference implementation.
-
-
-
-
- com.github.pcorless.icepdf
- icepdf-core
-
-
-
-
-
- assembly
-
-
-
- maven-assembly-plugin
-
-
-
- org.icepdf.ri.viewer.Launcher
-
-
-
- jar-with-dependencies
-
-
-
-
- make-assembly
-
- package
-
-
- single
-
-
-
-
-
-
-
-
-
+
+
+ 4.0.0
+
+ com.github.pcorless.icepdf
+ viewer
+ 7.2.0-SNAPSHOT
+
+ icepdf-viewer
+ jar
+ ICEpdf :: Viewer : Swing/AWT Viewer RI
+
+ ICEpdf Java Swing/AWT reference implementation.
+
+
+
+
+ com.github.pcorless.icepdf
+ icepdf-core
+
+
+ com.github.lookfirst
+ sardine
+ 5.12
+
+
+ org.apache.tika
+ tika-core
+ 1.23
+
+
+ jakarta.xml.bind
+ jakarta.xml.bind-api
+ 2.3.3
+
+
+ org.glassfish.jaxb
+ jaxb-runtime
+ 2.3.3
+
+
+
+
+
+ assembly
+
+
+
+ maven-assembly-plugin
+
+
+
+ org.icepdf.ri.viewer.Launcher
+
+
+
+ jar-with-dependencies
+
+
+
+
+ make-assembly
+
+ package
+
+
+ single
+
+
+
+
+
+
+
+
+
+
+
diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/KeyEventConstants.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/KeyEventConstants.java
index 6b06cef90..4e4c884ca 100644
--- a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/KeyEventConstants.java
+++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/KeyEventConstants.java
@@ -33,6 +33,8 @@ public class KeyEventConstants {
public static final int MODIFIER_OPEN_FILE = MENU_SHORTCUT_KEY_MASK;
public static final int KEY_CODE_OPEN_URL = KeyEvent.VK_U;
public static final int MODIFIER_OPEN_URL = MENU_SHORTCUT_KEY_MASK;
+ public static final int KEY_CODE_OPEN_DAV = KEY_CODE_OPEN_URL;
+ public static final int MODIFIER_OPEN_DAV = MENU_SHORTCUT_KEY_MASK | InputEvent.ALT_MASK;
public static final int KEY_CODE_CLOSE = KeyEvent.VK_W;
public static final int MODIFIER_CLOSE = MENU_SHORTCUT_KEY_MASK;
public static final int KEY_CODE_SAVE = KeyEvent.VK_S;
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..3734ead8a 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
@@ -15,7 +15,9 @@
*/
package org.icepdf.ri.common;
+import com.github.sardine.impl.SardineException;
import org.icepdf.core.SecurityCallback;
+import org.icepdf.core.exceptions.PDFException;
import org.icepdf.core.exceptions.PDFSecurityException;
import org.icepdf.core.io.SizeInputStream;
import org.icepdf.core.pobjects.*;
@@ -82,6 +84,7 @@
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -146,6 +149,7 @@ public class SwingController extends ComponentAdapter
private JMenuItem openFileMenuItem;
private JMenu recentFilesSubMenu;
private JMenuItem openURLMenuItem;
+ private JMenuItem openDavMenuItem;
private JMenuItem closeMenuItem;
private JMenuItem saveFileMenuItem;
private JMenuItem saveAsFileMenuItem;
@@ -280,7 +284,7 @@ public class SwingController extends ComponentAdapter
// sub controller for document text searching.
protected DocumentSearchController documentSearchController;
-
+ private DavFileClient pdfClient;
protected Document document;
protected boolean disposed;
@@ -432,6 +436,16 @@ public void setOpenURLMenuItem(JMenuItem mi) {
mi.addActionListener(this);
}
+ /**
+ * Called by SwingViewerBuilder, so that Controller can setup event handling
+ *
+ * @param mi menu item to assign
+ */
+ public void setOpenDavMenuItem(JMenuItem mi) {
+ openDavMenuItem = mi;
+ mi.addActionListener(this);
+ }
+
/**
* Called by SwingViewerBuilder, so that Controller can setup event handling
*
@@ -2570,7 +2584,8 @@ public void openDocument(String pathname) {
private static String getTempSaveFileName(final String originalFilePath) {
final String separator = File.separator;
- final String[] pathSplit = originalFilePath.split(Pattern.quote(separator));
+ // Also forward slash for url (webdav)
+ final String[] pathSplit = originalFilePath.split(Pattern.quote(separator) + "|/");
final String name = pathSplit[pathSplit.length - 1];
final String[] dotSplit = name.split("\\.");
final String basename = getBasename(dotSplit);
@@ -2750,6 +2765,138 @@ protected Object doInBackground() throws Exception {
}
}
+ /**
+ * Utility method for opening a WebDav URL. Shows a dialog for the user to type
+ * what URL to open
+ */
+ public void openDav() {
+ String davLocation = ((ViewModel.getDefaultDav() != null) ? ViewModel.getDefaultDav() : "");
+ // display webdav url input dialog
+ String url = (String) JOptionPane.showInputDialog(
+ viewer,
+ "WebDav URL:",
+ "Open WebDav URL",
+ JOptionPane.QUESTION_MESSAGE,
+ null,
+ null,
+ davLocation);
+ if (url != null) {
+ if (viewer != null) {
+ viewer.toFront();
+ viewer.requestFocus();
+ }
+ openDavInSomeViewer(url);
+ ViewModel.setDefaultDav(url);
+ }
+ }
+
+ private void openDavInSomeViewer(final String url) {
+ final DavFileClient client = new DavFileClient(UriUtils.encodePath(url, StandardCharsets.UTF_8));
+ if (document == null) {
+ openDocument(client);
+ } else if (windowManagementCallback != null) {
+ int oldTool = SwingController.this.getDocumentViewToolMode();
+ setDisplayTool(DocumentViewModelImpl.DISPLAY_TOOL_WAIT);
+ try {
+ windowManagementCallback.newWindow(client);
+ } finally {
+ setDisplayTool(oldTool);
+ }
+ }
+ }
+
+
+ /**
+ * Opens a document specified by the DavFileClient. Asks the user their password if needed
+ *
+ * @param pdfDavClient The client
+ */
+ public void openDocument(final DavFileClient pdfDavClient) {
+ pdfClient = pdfDavClient;
+ if (pdfClient.username() == null || pdfClient.username().isEmpty()) {
+ pdfClient.setUsername(System.getProperty("user.name"));
+ }
+ if (pdfClient.password() == null) {
+ JPanel panel = new JPanel();
+ panel.setLayout(new GridBagLayout());
+ GridBagConstraints constraints = new GridBagConstraints(0, 0, 2, 1, 1, 1,
+ GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0);
+ JLabel mainLabel = new JLabel(MessageFormat.format(messageBundle.getString("viewer.dialog.dav.credentials" +
+ ".label"), pdfClient.name()));
+ JLabel userLabel = new JLabel(messageBundle.getString("viewer.dialog.dav.user.label"));
+ JTextField userField = new JTextField(pdfClient.username());
+ JLabel passwordLabel = new JLabel(messageBundle.getString("viewer.dialog.dav.password.label"));
+ JPasswordField passwordField = new JPasswordField();
+ panel.add(mainLabel, constraints);
+ constraints.gridy = 1;
+ constraints.gridwidth = 1;
+ panel.add(userLabel, constraints);
+ constraints.gridx = 1;
+ panel.add(userField, constraints);
+ constraints.gridx = 0;
+ constraints.gridy = 2;
+ panel.add(passwordLabel, constraints);
+ constraints.gridx = 1;
+ panel.add(passwordField, constraints);
+ String[] options = {messageBundle.getString("viewer.dialog.dav.credentials.button.ok"),
+ messageBundle.getString("viewer.dialog.dav.credentials.button.cancel")};
+ int option = JOptionPane.showOptionDialog(getViewerFrame(), panel, messageBundle.getString("viewer.dialog.dav.credentials.title"),
+ JOptionPane.YES_NO_OPTION, JOptionPane.PLAIN_MESSAGE,
+ null, options, options[0]);
+ if (option == JOptionPane.OK_OPTION) {
+ pdfClient.setUsername(userField.getText());
+ pdfClient.setPassword(new String(passwordField.getPassword()));
+ } else if (option == JOptionPane.NO_OPTION) {
+ return;
+ }
+ }
+ try {
+ openDavDocument();
+ } catch (SardineException e) {
+ SwingUtilities.invokeLater(() -> {
+ org.icepdf.ri.util.Resources.showMessageDialog(
+ viewer,
+ JOptionPane.INFORMATION_MESSAGE,
+ messageBundle,
+ "viewer.dialog.sardine.exception.title",
+ "viewer.dialog.sardine.exception.msg",
+ e.getMessage() != null ? e.getMessage() : e.toString());
+ //401 is probably wrong password
+ if (e.getStatusCode() == 401) {
+ pdfClient.setPassword(null);
+ openDocument(pdfClient);
+ }
+ });
+ } catch (IOException | PDFException | PDFSecurityException e) {
+ SwingUtilities.invokeLater(() -> org.icepdf.ri.util.Resources.showMessageDialog(
+ viewer,
+ JOptionPane.INFORMATION_MESSAGE,
+ messageBundle,
+ "viewer.dialog.dav.exception.title",
+ "viewer.dialog.dav.exception.msg",
+ e.getMessage() != null ? e.getMessage() : e.toString()));
+ } finally {
+ setDisplayTool(DocumentViewModelImpl.DISPLAY_TOOL_PAN);
+ }
+ }
+
+ protected void openDavDocument() throws IOException, PDFException, PDFSecurityException {
+ if (pdfClient.exists()) {
+ final InputStream stream = pdfClient.getContent();
+ openDocument(new BufferedInputStream(stream), pdfClient.url(), pdfClient.url());
+ } else {
+ org.icepdf.ri.util.Resources.showMessageDialog(
+ viewer,
+ JOptionPane.INFORMATION_MESSAGE,
+ messageBundle,
+ "viewer.dialog.dav.notfound.title",
+ "viewer.dialog.dav.notfound.msg",
+ pdfClient.url()
+ );
+ }
+ }
+
+
/**
* Opens a Document via the specified InputStream. This method is a convenience method provided for
* backwards compatibility.
@@ -3133,7 +3280,9 @@ public void commonNewDocumentHandling(String fileDescription) {
title = document.getInfo().getTitle();
}
String filename = f.exists() ? f.getName() : fileDescription;
- Object[] messageArguments = title == null ? new String[]{filename} : new String[]{title, filename};
+ final String fileTitle = pdfClient == null ? filename : pdfClient.name();
+
+ Object[] messageArguments = title == null ? new String[]{fileTitle} : new String[]{title, fileTitle};
String titleResource = title == null ? "notitle" : "default";
MessageFormat formatter =
new MessageFormat(messageBundle.getString("viewer.window.title.open." + titleResource));
@@ -3258,6 +3407,7 @@ public void dispose() {
openFileMenuItem = null;
openURLMenuItem = null;
+ openDavMenuItem = null;
closeMenuItem = null;
saveAsFileMenuItem = null;
sendMailMenuItem = null;
@@ -3431,29 +3581,46 @@ public void dispose() {
* when the window is closed.
*/
public void saveFile() {
- if (IS_READONLY) return;
- if (document.getStateManager().isChange() &&
- saveFilePath != null &&
- !saveFilePath.isEmpty()) {
- File out = new File(saveFilePath);
- if (out.getParentFile() != null) {
- if (Files.isWritable(out.getParentFile().toPath())) {
- try (OutputStream stream = new BufferedOutputStream(new FileOutputStream(out))) {
- document.saveToOutputStream(stream);
- stream.flush();
- document.getStateManager().setChangesSnapshot();
- } catch (IOException e) {
- logger.log(Level.FINE, "IO Exception ", e);
+ if (!IS_READONLY && document.getStateManager().isChange()) {
+ if (pdfClient != null) {
+ try {
+ final PipedInputStream in = new PipedInputStream();
+ new Thread(() -> {
+ try (final PipedOutputStream out = new PipedOutputStream(in)) {
+ document.saveToOutputStream(out);
+ } catch (final IOException e) {
+ logger.log(Level.WARNING, e, () -> "Error writing to output");
+ }
+ }).start();
+ pdfClient.save(in);
+ document.getStateManager().setChangesSnapshot();
+ } catch (IOException e) {
+ logger.log(Level.FINE, "IOException while saving dav", e);
+ saveFileAs();
+ }
+ } else if (saveFilePath != null &&
+ !saveFilePath.isEmpty()) {
+ File out = new File(saveFilePath);
+ if (out.getParentFile() != null) {
+ if (Files.isWritable(out.getParentFile().toPath())) {
+ try (OutputStream stream = new BufferedOutputStream(new FileOutputStream(out))) {
+ document.saveToOutputStream(stream);
+ stream.flush();
+ document.getStateManager().setChangesSnapshot();
+ } catch (IOException e) {
+ logger.log(Level.FINE, "IO Exception ", e);
+ }
}
+ } else {
+ //Probably got loaded from an InputStream, can't simply save
+ saveFileAs(SaveMode.SAVE);
}
} else {
- //Probably got loaded from an InputStream, can't simply save
+ // show saveAs dialog as this was legacy behaviour for the save button on the toolbar
saveFileAs(SaveMode.SAVE);
}
- } else {
- // show saveAs dialog as this was legacy behaviour for the save button on the toolbar
- saveFileAs(SaveMode.SAVE);
}
+
}
/**
@@ -4831,6 +4998,9 @@ public void actionPerformed(ActionEvent event) {
} else if (source == openURLMenuItem) {
cancelSetFocus = true;
openURL();
+ } else if (source == openDavMenuItem) {
+ cancelSetFocus = true;
+ openDav();
} else if (source == closeMenuItem) {
boolean isCanceled = saveChangesDialog();
if (!isCanceled) {
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..9c7a4c63c 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
@@ -574,13 +574,15 @@ public JMenu buildFileMenu() {
fileMenu.setMnemonic(buildMnemonic(messageBundle.getString("viewer.menu.file.mnemonic").charAt(0)));
JMenuItem openFileMenuItem = buildOpenFileMenuItem();
JMenuItem openURLMenuItem = buildOpenURLMenuItem();
- if (openFileMenuItem != null && openURLMenuItem != null) {
+ JMenuItem openDavMenuItem = buildOpenDavMenuItem();
+ if (openFileMenuItem != null && openURLMenuItem != null && openDavMenuItem != null) {
JMenu openSubMenu = new JMenu(messageBundle.getString("viewer.menu.open.label"));
openSubMenu.setIcon(new ImageIcon(Images.get("open_a_24.png")));
openSubMenu.setDisabledIcon(new ImageIcon(Images.get("open_i_24.png")));
openSubMenu.setRolloverIcon(new ImageIcon(Images.get("open_r_24.png")));
addToMenu(openSubMenu, openFileMenuItem);
addToMenu(openSubMenu, openURLMenuItem);
+ addToMenu(openSubMenu, openDavMenuItem);
addToMenu(fileMenu, openSubMenu);
} else if (openFileMenuItem != null || openURLMenuItem != null) {
addToMenu(fileMenu, openFileMenuItem);
@@ -650,6 +652,15 @@ public JMenuItem buildOpenURLMenuItem() {
return mi;
}
+ public JMenuItem buildOpenDavMenuItem() {
+ JMenuItem mi = makeMenuItem(messageBundle.getString("viewer.menu.open.dav.label"),
+ buildKeyStroke(KeyEventConstants.KEY_CODE_OPEN_DAV, KeyEventConstants.MODIFIER_OPEN_DAV));
+ if (viewerController != null && mi != null) {
+ viewerController.setOpenDavMenuItem(mi);
+ }
+ return mi;
+ }
+
public JMenuItem buildCloseMenuItem() {
JMenuItem mi = makeMenuItem(
messageBundle.getString("viewer.menu.close.label"), null, null,
diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/ViewModel.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/ViewModel.java
index 9d0a1cd22..502df1414 100644
--- a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/ViewModel.java
+++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/ViewModel.java
@@ -36,6 +36,9 @@ public class ViewModel {
// Store current URL path
private static String defaultURL = null;
+ // Store current DAV path
+ private static String defaultDav = null;
+
// store for shrink to fit setting for Controller prints.
private boolean isShrinkToPrintableArea = true;
@@ -63,6 +66,10 @@ public static String getDefaultURL() {
return defaultURL;
}
+ public static String getDefaultDav() {
+ return defaultDav;
+ }
+
public static void setDefaultFile(File f) {
defaultFile = f;
}
@@ -81,6 +88,14 @@ public static void setDefaultURL(String defURL) {
defaultURL = defURL;
}
+ public static void setDefaultDav(String defDav) {
+ if (defDav == null || defDav.isEmpty()) {
+ defaultDav = null;
+ } else {
+ defaultDav = defDav;
+ }
+ }
+
public PrintHelper getPrintHelper() {
return printHelper;
}
diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/WindowManagementCallback.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/WindowManagementCallback.java
index e60079a6b..9afedfa24 100644
--- a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/WindowManagementCallback.java
+++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/WindowManagementCallback.java
@@ -16,6 +16,7 @@
package org.icepdf.ri.common;
import org.icepdf.ri.common.views.Controller;
+import org.icepdf.ri.util.DavFileClient;
import org.icepdf.ri.util.ViewerPropertiesManager;
import java.awt.*;
@@ -38,6 +39,8 @@ public interface WindowManagementCallback {
void newWindow(URL url);
+ void newWindow(DavFileClient fileClient);
+
void disposeWindow(Controller controller, Frame viewer, Preferences preferences);
void minimiseAllWindows();
diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/Controller.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/Controller.java
index d98809dca..3bc1740c8 100644
--- a/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/Controller.java
+++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/common/views/Controller.java
@@ -22,6 +22,7 @@
import org.icepdf.ri.common.WindowManagementCallback;
import org.icepdf.ri.common.print.PrintHelperFactory;
import org.icepdf.ri.common.utility.outline.OutlineItemTreeNode;
+import org.icepdf.ri.util.DavFileClient;
import org.icepdf.ri.util.ViewerPropertiesManager;
import java.awt.*;
@@ -271,6 +272,13 @@ enum SaveMode {
*/
void openDocument(final URL location);
+ /**
+ * Opens a document specified by the given DavFileClient
+ *
+ * @param davFileClient The dav file client
+ */
+ void openDocument(DavFileClient davFileClient);
+
/**
* Load the specified file in a new Viewer RI window.
*
diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/util/DavFileClient.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/util/DavFileClient.java
new file mode 100644
index 000000000..38e2ee452
--- /dev/null
+++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/util/DavFileClient.java
@@ -0,0 +1,431 @@
+package org.icepdf.ri.util;
+
+import com.github.sardine.DavResource;
+import com.github.sardine.Sardine;
+import com.github.sardine.SardineFactory;
+import org.apache.tika.Tika;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.function.Function;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+/**
+ * Represents a WebDav connection to a file
+ */
+public class DavFileClient implements AutoCloseable {
+ private static final Logger logger = Logger.getLogger(DavFileClient.class.getName());
+ private static final Tika TIKA = new Tika();
+ private final Sardine sardine;
+ private final String url;
+ private final String folderUrl;
+ private final String name;
+ private final String extension;
+ private final boolean readOnly;
+ private String username;
+ private String password;
+ private int revision;
+ private File file;
+ private InputStream stream;
+ private String mimeType;
+ private String lock;
+
+ /**
+ * Instantiates a client with the given url and no credentials
+ *
+ * @param url The url
+ */
+ public DavFileClient(final String url) {
+ this(url, null, null);
+ }
+
+ /**
+ * Instantiates a client with the given url and credentials
+ *
+ * @param url The url
+ * @param username The username
+ * @param password The password
+ */
+ public DavFileClient(final String url, final String username, final String password) {
+ this(url, username, password, false);
+ }
+
+ /**
+ * Instantiates a client with the given url and credentials
+ *
+ * @param url The url
+ * @param username The username
+ * @param password The password
+ * @param readOnly Whether the client is readonly or not
+ */
+ public DavFileClient(final String url, final String username, final String password, final boolean readOnly) {
+ this.url = url;
+ this.username = username;
+ this.password = password;
+ this.sardine = username == null || password == null ? SardineFactory.begin() : SardineFactory.begin(username, password);
+ final String[] split = url.split("/");
+ folderUrl = Arrays.stream(split).limit(split.length - 1L).collect(Collectors.joining("/"));
+ final String[] names = split[split.length - 1].split("\\.");
+ name = names[0];
+ if (names.length == 1) {
+ extension = "";
+ } else {
+ extension = names[1];
+ }
+ this.readOnly = readOnly;
+ setPreemptiveAuthenticationEnabled(true);
+ }
+
+ public void setUsername(final String username) {
+ this.username = username;
+ }
+
+ public void setPassword(final String password) {
+ this.password = password;
+ }
+
+ /**
+ * Sets the preemptive authentication value
+ * Must be set to true if an action causes a NonRepeatableRequestException
+ *
+ * @param enabled true or false
+ */
+
+ public void setPreemptiveAuthenticationEnabled(final boolean enabled) {
+ if (enabled) {
+ sardine.enablePreemptiveAuthentication(url);
+ } else {
+ sardine.disablePreemptiveAuthentication();
+ }
+ }
+
+ /**
+ * @return The contents of the parent folder (including the current file)
+ * @throws IOException
+ */
+ public List getParentFolderResources() throws IOException {
+ final String[] split = folderUrl.split("/");
+ final String folderName = split[split.length - 1].isEmpty() ? split[split.length - 2] : split[split.length - 1];
+ return sardine.list(folderUrl).stream().filter(dr -> !dr.getName().equals(folderName)).collect(Collectors.toList());
+ }
+
+
+ public List getParentFolderContents() throws IOException {
+ final String[] split = folderUrl.split("/");
+ final String folderName = split[split.length - 1].isEmpty() ? split[split.length - 2] : split[split.length - 1];
+ final String slashedFolder = folderName.endsWith("/") ? folderName : folderName + "/";
+ return getParentFolderResources().stream().map(dr -> slashedFolder + dr.getName()).collect(Collectors.toList());
+
+ }
+
+ /**
+ * @return A temporary copy file for this connection
+ * @throws IOException
+ */
+ public File getFile() throws IOException {
+ if (file == null) {
+ file = File.createTempFile(name, extension.isEmpty() ? "" : "." + extension);
+ try (final InputStream in = sardine.get(url)) {
+ Files.copy(in, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
+ mimeType = new Tika().detect(file);
+ }
+ }
+ return file;
+ }
+
+ /**
+ * @return The content stream of this connection
+ * @throws IOException
+ */
+ public InputStream getContent() throws IOException {
+ resetStream();
+ return stream;
+ }
+
+ /**
+ * Resets the stream of this connection
+ *
+ * @throws IOException
+ */
+ public void resetStream() throws IOException {
+ close();
+ if (exists()) {
+ stream = sardine.get(url);
+ }
+ }
+
+ /**
+ * Saves the given inputstream to the remote url
+ *
+ * @param inputStream The data to save
+ * @throws IOException
+ */
+ public void save(final InputStream inputStream) throws IOException {
+ if (!readOnly) {
+ if (inputStream.markSupported() && inputStream.available() > 0) {
+ mimeType = TIKA.detect(inputStream);
+ }
+ if (mimeType == null || mimeType.equals("application/octet-stream")) {
+ mimeType = TIKA.detect(name + (extension.isEmpty() ? "" : "." + extension));
+ }
+ createDirectoryHierarchy(folderUrl);
+ sardine.put(url, inputStream, mimeType);
+ revision += 1;
+ }
+ }
+
+
+ private void createDirectoryHierarchy(final String url) throws IOException {
+ if (!sardine.exists(url)) {
+ final boolean isHttps = url.startsWith("https");
+ final String startUrl = isHttps ? "https://" : "http://";
+ final List folderUrls = Arrays.asList(url.substring(isHttps ? 8 : 7).split("/"));
+ int startIdx = folderUrls.size();
+ while (startIdx > 0) {
+ final String parentFolder = startUrl + String.join("/", folderUrls.subList(0, startIdx));
+ if (sardine.exists(parentFolder)) {
+ break;
+ }
+ startIdx--;
+ }
+ if (startIdx < folderUrls.size()) {
+ for (int idx = startIdx + 1; idx <= folderUrls.size(); ++idx) {
+ final String toCreate = startUrl + String.join("/", folderUrls.subList(0, idx));
+ sardine.createDirectory(toCreate);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void close() {
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (final IOException e) {
+ logger.log(Level.WARNING, "Error closing stream", e);
+ }
+ }
+ }
+
+ /**
+ * Deletes the resource
+ *
+ * @throws IOException
+ */
+ public void delete() throws IOException {
+ if (!readOnly && exists()) {
+ sardine.delete(url);
+ }
+ }
+
+ /**
+ * @return Whether the remote file exists or not
+ * @throws IOException
+ */
+ public boolean exists() throws IOException {
+ return sardine.exists(url);
+ }
+
+ /**
+ * Moves the remote resource to the given url. No-op if the file doesn't exist.
+ *
+ * @param destinationUrl The new url
+ * @param overwrite Whether to overwrite the resource or not
+ * @return The client managing the resource under the new url
+ * @throws IOException
+ */
+ public DavFileClient move(final String destinationUrl, final boolean overwrite) throws IOException {
+ if (!readOnly && exists()) {
+ final String[] split = destinationUrl.split("/");
+ createDirectoryHierarchy(Arrays.stream(split).limit(split.length - 1L).collect(Collectors.joining("/")));
+ sardine.move(url, destinationUrl, overwrite);
+ return new DavFileClient(destinationUrl, username, password);
+ } else {
+ return this;
+ }
+ }
+
+
+ /**
+ * Copies the resource to the given url. No-op if the file doesn't exist.
+ *
+ * @param destinationUrl The new url
+ * @param overwrite Whether to overwrite or not
+ * @return The client managing the new resource
+ * @throws IOException
+ */
+ public DavFileClient copy(final String destinationUrl, final boolean overwrite) throws IOException {
+ if (exists()) {
+ final String[] split = destinationUrl.split("/");
+ createDirectoryHierarchy(Arrays.stream(split).limit(split.length - 1L).collect(Collectors.joining("/")));
+ sardine.copy(url, destinationUrl, overwrite);
+ return new DavFileClient(destinationUrl, username, password);
+ } else {
+ return this;
+ }
+ }
+
+ /**
+ * Creates the directory
+ *
+ * @throws IOException
+ */
+ public void createDirectory() throws IOException {
+ if (!readOnly) {
+ createDirectoryHierarchy(url);
+ }
+ }
+
+
+ public boolean isFolder() throws IOException {
+ final String slashedFolder = folderUrl.endsWith("/") ? folderUrl : folderUrl + "/";
+ for (final DavResource dr : getParentFolderResources()) {
+ final String url = slashedFolder + dr.getName();
+ if (url.equals(this.url)) {
+ return dr.isDirectory();
+ }
+ }
+ return false;
+ }
+
+
+ public boolean isFile() throws IOException {
+ final String slashedFolder = folderUrl.endsWith("/") ? folderUrl : folderUrl + "/";
+ for (final DavResource dr : getParentFolderResources()) {
+ final String url = slashedFolder + dr.getName();
+ if (url.equals(this.url)) {
+ return !dr.isDirectory();
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @return The name of the file
+ */
+ public String name() {
+ return name;
+ }
+
+ /**
+ * @return The extension of the file
+ */
+ public String extension() {
+ return extension;
+ }
+
+ /**
+ * @return The revision of the file
+ */
+ public int revision() {
+ return revision;
+ }
+
+ /**
+ * @return The mimetype of the file
+ */
+ public String mimetype() {
+ return mimeType;
+ }
+
+ /**
+ * @return The parent folder url
+ */
+ public String folderUrl() {
+ return folderUrl;
+ }
+
+ /**
+ * @return The url of the resource
+ */
+ public String url() {
+ return url;
+ }
+
+ /**
+ * @return The username for the connection
+ */
+ public String username() {
+ return username;
+ }
+
+ /**
+ * @return The password for the connection
+ */
+ public String password() {
+ return password;
+ }
+
+
+ /**
+ * @return Whether the connection is readonly or not
+ */
+ public boolean isReadOnly() {
+ return readOnly;
+ }
+
+ /**
+ * Locks the resource
+ */
+ public void lock() {
+ try {
+ this.lock = sardine.lock(url);
+ } catch (final IOException e) {
+ logger.log(Level.WARNING, "Error locking " + url, e);
+ }
+ }
+
+ /**
+ * Unlocks the resource
+ */
+ public void unlock() {
+ if (lock != null) {
+ try {
+ sardine.unlock(url, lock);
+ } catch (final IOException e) {
+ logger.log(Level.WARNING, "Error unlocking " + url, e);
+ }
+ lock = null;
+ }
+ }
+
+ /**
+ * @return The modification time
+ */
+ public Instant getModificationTime() throws IOException {
+ return getInstant(DavResource::getModified);
+ }
+
+ /**
+ * @return The creation time
+ */
+ public Instant getCreationTime() throws IOException {
+ return getInstant(DavResource::getCreation);
+ }
+
+ private Instant getInstant(final Function resourceToDate) throws IOException {
+ final String slashedFolder = folderUrl.endsWith("/") ? folderUrl : folderUrl + "/";
+ final List resources = sardine.list(folderUrl);
+ return resources.stream().filter(dr -> (slashedFolder + dr.getName()).equals(url)).findFirst()
+ .map(dr -> resourceToInstant(dr, resourceToDate)).orElse(null);
+ }
+
+ private static Instant resourceToInstant(final DavResource resource, final Function resourceToDate) {
+ final Date date = resourceToDate.apply(resource);
+ if (date == null) {
+ return null;
+ } else {
+ return Instant.ofEpochMilli(date.getTime());
+ }
+ }
+}
\ No newline at end of file
diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/util/UriUtils.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/util/UriUtils.java
new file mode 100644
index 000000000..85f5b2145
--- /dev/null
+++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/util/UriUtils.java
@@ -0,0 +1,86 @@
+package org.icepdf.ri.util;
+
+import java.io.ByteArrayOutputStream;
+import java.nio.charset.Charset;
+import java.util.regex.Pattern;
+
+public final class UriUtils {
+
+ private static final Pattern URL_ESCAPE_PATTERN = Pattern.compile("%[0-9A-Fa-f]{2}");
+
+ private UriUtils() {
+ }
+
+ public static String decode(final String source, final Charset charset) {
+ final int length = source.length();
+ if (length == 0) {
+ return source;
+ }
+
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream(length);
+ boolean changed = false;
+ for (int i = 0; i < length; i++) {
+ final int ch = source.charAt(i);
+ if (ch == '%') {
+ if (i + 2 < length) {
+ final char hex1 = source.charAt(i + 1);
+ final char hex2 = source.charAt(i + 2);
+ final int u = Character.digit(hex1, 16);
+ final int l = Character.digit(hex2, 16);
+ if (u == -1 || l == -1) {
+ throw new IllegalArgumentException("Invalid encoded sequence \"" + source.substring(i) + "\"");
+ }
+ baos.write((char) ((u << 4) + l));
+ i += 2;
+ changed = true;
+ } else {
+ throw new IllegalArgumentException("Invalid encoded sequence \"" + source.substring(i) + "\"");
+ }
+ } else {
+ baos.write(ch);
+ }
+ }
+ return (changed ? new String(baos.toByteArray(), charset) : source);
+ }
+
+ public static String encodePath(final String source, final Charset charset) {
+ //Avoid double encoding
+ if (source == null || source.isEmpty() || URL_ESCAPE_PATTERN.matcher(source).find()) {
+ return source;
+ }
+
+ final byte[] bytes = source.getBytes(charset);
+ boolean original = true;
+ for (final byte b : bytes) {
+ if (!isAllowedPath(b)) {
+ original = false;
+ break;
+ }
+ }
+ if (original) {
+ return source;
+ }
+
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream(bytes.length);
+ for (final byte b : bytes) {
+ if (isAllowedPath(b)) {
+ baos.write(b);
+ } else {
+ baos.write('%');
+ final char hex1 = Character.toUpperCase(Character.forDigit((b >> 4) & 0xF, 16));
+ final char hex2 = Character.toUpperCase(Character.forDigit(b & 0xF, 16));
+ baos.write(hex1);
+ baos.write(hex2);
+ }
+ }
+ return new String(baos.toByteArray(), charset);
+ }
+
+ private static boolean isAllowedPath(final int c) {
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') ||
+ c == '/' || c == '@' || c == ':' || c == '.' || c == '-' || c == '_' || c == '~' ||
+ c == '!' || c == '$' || c == '&' || c == '\'' || c == '(' || c == ')' || c == '*' ||
+ c == '+' || c == ',' || c == ';' || c == '=';
+ }
+
+}
diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/viewer/Launcher.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/viewer/Launcher.java
index f7cd973ca..6ba97b99f 100644
--- a/viewer/viewer-awt/src/main/java/org/icepdf/ri/viewer/Launcher.java
+++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/viewer/Launcher.java
@@ -18,11 +18,10 @@
import org.icepdf.core.util.Defs;
import org.icepdf.core.util.SystemProperties;
import org.icepdf.ri.common.ViewModel;
-import org.icepdf.ri.util.FontPropertiesManager;
-import org.icepdf.ri.util.URLAccess;
-import org.icepdf.ri.util.ViewerPropertiesManager;
+import org.icepdf.ri.util.*;
import javax.swing.*;
+import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.ResourceBundle;
import java.util.logging.Level;
@@ -67,6 +66,9 @@ public static void main(String[] argv) {
String contentURL = "";
String contentFile = "";
+ String password = null;
+ String contentDav = "";
+ String user = SystemProperties.USER_NAME;
String printer = null;
// parse command line arguments
for (int i = 0; i < argv.length; i++) {
@@ -82,6 +84,15 @@ public static void main(String[] argv) {
case "-loadurl":
contentURL = argv[++i].trim();
break;
+ case "-loaddav":
+ contentDav = argv[++i].trim();
+ break;
+ case "-user":
+ user = argv[++i].trim();
+ break;
+ case "-password":
+ password = argv[++i].trim();
+ break;
case "-print":
printer = argv[++i].trim();
break;
@@ -101,7 +112,7 @@ public static void main(String[] argv) {
System.exit(1);
}
// start the viewer
- run(contentFile, contentURL, printer, messageBundle);
+ run(contentFile, contentURL, contentDav, user, password, printer, messageBundle);
}
/**
@@ -111,11 +122,17 @@ public static void main(String[] argv) {
* null.
* @param contentURL URL of a file which will be loaded at runtime, can be
* null.
+ * @param contentDav URL of a file which will be loaded at runtime, can be null
+ * @param user The dav username
+ * @param password the dav password
* @param printer The name of the printer to use, can be null
* @param messageBundle messageBundle to pull strings from
*/
private static void run(String contentFile,
String contentURL,
+ String contentDav,
+ String user,
+ String password,
String printer,
ResourceBundle messageBundle) {
@@ -172,10 +189,19 @@ private static void run(String contentFile,
ViewModel.setDefaultURL(urlAccess.urlLocation);
urlAccess.dispose();
}
+ if (contentDav != null && !contentDav.isEmpty()) {
+ if (printer != null) {
+ windowManager.newWindow(new DavFileClient(UriUtils.encodePath(contentDav, StandardCharsets.UTF_8), user, password), printer);
+ } else {
+ windowManager.newWindow(new DavFileClient(UriUtils.encodePath(contentDav, StandardCharsets.UTF_8), user, password));
+ }
+ }
+
// Start an empy viewer if there was no command line parameters
if (((contentFile == null || contentFile.isEmpty()) &&
- (contentURL == null || contentURL.isEmpty()))
+ (contentURL == null || contentURL.isEmpty()) &&
+ (contentDav == null || contentDav.isEmpty()))
|| (windowManager.getNumberOfWindows() == 0)
) {
windowManager.newWindow("");
diff --git a/viewer/viewer-awt/src/main/java/org/icepdf/ri/viewer/WindowManager.java b/viewer/viewer-awt/src/main/java/org/icepdf/ri/viewer/WindowManager.java
index 8d7a90252..029ffdf2a 100644
--- a/viewer/viewer-awt/src/main/java/org/icepdf/ri/viewer/WindowManager.java
+++ b/viewer/viewer-awt/src/main/java/org/icepdf/ri/viewer/WindowManager.java
@@ -22,6 +22,7 @@
import org.icepdf.ri.common.views.Controller;
import org.icepdf.ri.common.views.DocumentViewController;
import org.icepdf.ri.common.views.DocumentViewControllerImpl;
+import org.icepdf.ri.util.DavFileClient;
import org.icepdf.ri.util.ViewerPropertiesManager;
import javax.swing.*;
@@ -141,6 +142,15 @@ private void print(Controller controller, String printer) {
quit(controller, controller.getViewerFrame(), controller.getPropertiesManager().getPreferences());
}
+ public void newWindow(DavFileClient davFileClient) {
+ commonWindowCreation().openDocument(davFileClient);
+ }
+ public void newWindow(DavFileClient davFileClient, String printer) {
+ Controller controller = commonWindowCreation(false);
+ controller.openDocument(davFileClient);
+ print(controller, printer);
+ }
+
protected Controller commonWindowCreation() {
return commonWindowCreation(true);
}
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..831e0eef0 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
@@ -176,6 +176,7 @@ viewer.menu.open.label=Open
viewer.menu.open.recentFiles.label=Open Recent Files
viewer.menu.open.file.label=File...
viewer.menu.open.URL.label=URL...
+viewer.menu.open.dav.label=WebDav URL...
viewer.menu.close.label=Close
viewer.menu.save.label=Save
viewer.menu.saveAs.label=Save As...
@@ -395,6 +396,19 @@ viewer.dialog.security.cancelButton.label=Cancel
viewer.dialog.security.cancelButton.mnemonic=C
## Open URL Dialog
viewer.dialog.openURL.title=Open URL
+## Open Webdav Dialog
+viewer.dialog.dav.credentials.title=Enter your credentials
+viewer.dialog.dav.credentials.label=Enter your credentials to open {0}
+viewer.dialog.dav.user.label=Username
+viewer.dialog.dav.password.label=Password
+viewer.dialog.dav.credentials.button.ok=Ok
+viewer.dialog.dav.credentials.button.cancel=Cancel
+viewer.dialog.sardine.exception.title=Communication error
+viewer.dialog.sardine.exception.msg=Couldn't open document due to {0}
+viewer.dialog.dav.exception.title=Webdav error
+viewer.dialog.dav.exception.msg=Couldn't open document due to {0}
+viewer.dialog.dav.notfound.title=File not found
+viewer.dialog.dav.notfound.msg=Couldn't open file: File not found at\n{0}
### Save Dialogs
viewer.dialog.saveAs.title=Save As
viewer.dialog.saveAs.extensionError.title=ICEpdf - Save Error
diff --git a/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_da.properties b/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_da.properties
index 636dc7195..33f424936 100644
--- a/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_da.properties
+++ b/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_da.properties
@@ -19,6 +19,7 @@
#
## Window toolbar Title
viewer.window.title.default=ICEpdf-fremviser
+viewer.window.title.open.default=ICEpdf-fremviser - [{0}]
#status bar
viewer.statusbar.currentPage=Side {0} / {1}
## Top Page Control Toolbar
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..2af5b6505 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
@@ -19,6 +19,7 @@
#
## Window toolbar Title
viewer.window.title.default=ICEpdf Viewer
+viewer.window.title.open.default=ICEpdf Viewer - [{0}]
#status bar
viewer.statusbar.currentPage=Seite {0} / {1}
## Top Page Control Toolbar
@@ -213,6 +214,19 @@ viewer.dialog.security.cancelButton.label=Abbrechen
viewer.dialog.security.cancelButton.mnemonic=C
## Open URL Dialog
viewer.dialog.openURL.title=URL \u00F6ffnen
+## Open Webdav Dialog
+viewer.dialog.dav.credentials.title=Geben Sie Ihre Anmeldedaten ein
+viewer.dialog.dav.credentials.label=Geben Sie Ihre Anmeldedaten ein, um {0} zu \u00F6ffnen
+viewer.dialog.dav.user.label=Benutzername
+viewer.dialog.dav.password.label=Passwort
+viewer.dialog.dav.credentials.button.ok=Ok
+viewer.dialog.dav.credentials.button.cancel=Abbrechen
+viewer.dialog.sardine.exception.title=Kommunikationsfehler
+viewer.dialog.sardine.exception.msg=Das Dokument konnte aufgrund von {0} nicht ge\u00F6ffnet werden.
+viewer.dialog.dav.exception.title=Webdav-Fehler
+viewer.dialog.dav.exception.msg=Konnte Dokument nicht \u00F6ffnen wegen {0}
+viewer.dialog.dav.notfound.title=Dokument nicht gefunden
+viewer.dialog.dav.notfound.msg=Konnte Dokument nicht \u00F6ffnen: Dokument nicht gefunden am\n{0}
### Save a Copy Dialog
viewer.dialog.saveAs.title=Speichern unter
viewer.dialog.saveAs.extensionError.title=ICEpdf - Fehler beim Speichern
diff --git a/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_es.properties b/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_es.properties
index 1ba1b1b7a..98185b053 100644
--- a/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_es.properties
+++ b/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_es.properties
@@ -19,6 +19,7 @@
#
## Window toolbar Title
viewer.window.title.default=ICEpdf Viewer
+viewer.window.title.open.default=ICEpdf Viewer - [{0}]
#status bar
viewer.statusbar.currentPage=P\u00E1gina {0} / {1}
## Top Page Control Toolbar
diff --git a/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_fi.properties b/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_fi.properties
index 5b313d0c1..940903ac6 100644
--- a/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_fi.properties
+++ b/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_fi.properties
@@ -19,6 +19,7 @@
#
## Window toolbar Title
viewer.window.title.default=ICEpdf-katselusovellus
+viewer.window.title.open.default=ICEpdf-katselusovellus - [{0}]
#status bar
viewer.statusbar.currentPage=Sivu {0} / {1}
## Top Page Control Toolbar
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..66438ad13 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
@@ -19,6 +19,7 @@
#
## Window toolbar Title
viewer.window.title.default=ICEpdf Viewer
+viewer.window.title.open.default=ICEpdf Viewer - [{0}]
#status bar
viewer.statusbar.currentPage=Page {0} / {1}
## Top Page Control Toolbar
@@ -213,6 +214,19 @@ viewer.dialog.security.cancelButton.label=Annuler
viewer.dialog.security.cancelButton.mnemonic=C
## Open URL Dialog
viewer.dialog.openURL.title=Ouvrir l'URL
+## Open Webdav Dialog
+viewer.dialog.dav.credentials.title=Entrez vos identifiants
+viewer.dialog.dav.credentials.label=Entrez vos identifiants pour ouvrir {0}
+viewer.dialog.dav.user.label=Nom d'utilisateur
+viewer.dialog.dav.password.label=Mot de passe
+viewer.dialog.dav.credentials.button.ok=Ok
+viewer.dialog.dav.credentials.button.cancel=Annuler
+viewer.dialog.sardine.exception.title=Erreur de communication
+viewer.dialog.sardine.exception.msg=Impossible d'ouvrir le document en raison de {0}
+viewer.dialog.dav.exception.title=Erreur Webdav
+viewer.dialog.dav.exception.msg=Impossible d'ouvrir le document en raison de {0}
+viewer.dialog.dav.notfound.title=Document introuvable
+viewer.dialog.dav.notfound.msg=Impossible d'ouvrir le fichier: Fichier introuvable \u00E0 l'adresse\n{0}
### Save a Copy Dialog
viewer.dialog.saveAs.title=Enregistrer sous
viewer.dialog.saveAs.extensionError.title=ICEpdf - Erreur d'enregistrement
diff --git a/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_it.properties b/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_it.properties
index c0292e941..68a8e0e5a 100644
--- a/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_it.properties
+++ b/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_it.properties
@@ -19,6 +19,7 @@
#
## Window toolbar Title
viewer.window.title.default=ICEpdf Viewer
+viewer.window.title.open.default=ICEpdf Viewer - [{0}]
#status bar
viewer.statusbar.currentPage=Pag. {0} / {1}
## Top Page Control Toolbar
diff --git a/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_nl.properties b/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_nl.properties
index eafef48df..c89ae1084 100644
--- a/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_nl.properties
+++ b/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_nl.properties
@@ -19,6 +19,7 @@
#
## Window toolbar Title
viewer.window.title.default=ICEpdf Viewer
+viewer.window.title.open.default=ICEpdf Viewer - [{0}]
#status bar
viewer.statusbar.currentPage=Pag. {0} / {1}
## Top Page Control Toolbar
diff --git a/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_no.properties b/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_no.properties
index 82b1a13f6..ce04ae305 100644
--- a/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_no.properties
+++ b/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_no.properties
@@ -19,6 +19,7 @@
#
## Window toolbar Title
viewer.window.title.default=ICEpdf Oversikt
+viewer.window.title.open.default=ICEpdf Oversikt - [{0}]
#status bar
viewer.statusbar.currentPage=Side {0} / {1}
## Top Page Control Toolbar
diff --git a/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_pt.properties b/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_pt.properties
index 2ec2eb9cb..b3721d3b2 100644
--- a/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_pt.properties
+++ b/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_pt.properties
@@ -19,6 +19,7 @@
#
## Window toolbar Title
viewer.window.title.default=Visualizador CEpdf
+viewer.window.title.open.default=Visualizador ICEpdf - [{0}]
#status bar
viewer.statusbar.currentPage=P\u00E1gina {0} / {1}
## Top Page Control Toolbar
diff --git a/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_sv.properties b/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_sv.properties
index c45d9c94e..bdca58656 100644
--- a/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_sv.properties
+++ b/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_sv.properties
@@ -19,6 +19,7 @@
#
## Window toolbar Title
viewer.window.title.default=ICEpdf Viewer
+viewer.window.title.open.default=ICEpdf Viewer - [{0}]
#status bar
viewer.statusbar.currentPage=Sida {0} / {1}
## Top Page Control Toolbar
diff --git a/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_zh_CN.properties b/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_zh_CN.properties
index 23d0cefec..c2eb3bd2b 100644
--- a/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_zh_CN.properties
+++ b/viewer/viewer-awt/src/main/resources/org/icepdf/ri/resources/MessageBundle_zh_CN.properties
@@ -19,6 +19,7 @@
#
## Window toolbar Title
viewer.window.title.default=ICEpdf Viewer
+viewer.window.title.open.default=ICEpdf Viewer - [{0}]
#status bar
viewer.statusbar.currentPage=\u9875 {0} / {1}
## Top Page Control Toolbar