Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix XMP write #8658

Merged
merged 23 commits into from
Apr 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ dependencies {

implementation group: 'org.apache.commons', name: 'commons-csv', version: '1.9.0'
implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.12.0'
implementation 'com.h2database:h2-mvstore:2.1.212'
implementation 'com.h2database:h2-mvstore:2.1.210'

implementation group: 'org.apache.tika', name: 'tika-core', version: '2.3.0'
implementation 'com.ibm.icu:icu4j-charset:70.1'
Expand Down Expand Up @@ -187,7 +187,7 @@ dependencies {

implementation 'de.undercouch:citeproc-java:3.0.0-alpha.6'

// jakarta.activation is already depdency of glassfish
// jakarta.activation is already dependency of glassfish
implementation group: 'jakarta.xml.bind', name: 'jakarta.xml.bind-api', version: '3.0.1'
implementation group: 'org.glassfish.jaxb', name: 'jaxb-runtime', version: '3.0.2'

Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/jabref/gui/JabRefFrame.java
Original file line number Diff line number Diff line change
Expand Up @@ -826,7 +826,7 @@ private MenuBar createMenu() {

new SeparatorMenuItem(),

factory.createMenuItem(StandardActions.WRITE_METADATA_TO_PDF, new WriteMetadataToPdfAction(stateManager, prefs.getGeneralPreferences().getDefaultBibDatabaseMode(), Globals.entryTypesManager, prefs.getFieldWriterPreferences(), dialogService, taskExecutor, prefs.getFilePreferences(), prefs.getXmpPreferences())),
factory.createMenuItem(StandardActions.WRITE_METADATA_TO_PDF, new WriteMetadataToPdfAction(stateManager, Globals.entryTypesManager, prefs.getFieldWriterPreferences(), dialogService, taskExecutor, prefs.getFilePreferences(), prefs.getXmpPreferences())),
factory.createMenuItem(StandardActions.COPY_LINKED_FILES, new CopyFilesAction(dialogService, prefs, stateManager)),

new SeparatorMenuItem(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
import org.jabref.logic.xmp.XmpUtilWriter;
import org.jabref.model.database.BibDatabase;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.database.BibDatabaseMode;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.BibEntryTypesManager;
import org.jabref.preferences.FilePreferences;
Expand All @@ -44,11 +43,12 @@
public class WriteMetadataToPdfAction extends SimpleCommand {

private final StateManager stateManager;
private final BibEntryTypesManager entryTypesManager;
private final FieldWriterPreferences fieldWriterPreferences;
private final DialogService dialogService;
private final TaskExecutor taskExecutor;
private final FilePreferences filePreferences;
private final XmpPreferences xmpPreferences;
private final EmbeddedBibFilePdfExporter embeddedBibExporter;

private OptionsDialog optionsDialog;

Expand All @@ -60,13 +60,14 @@ public class WriteMetadataToPdfAction extends SimpleCommand {
private int entriesChanged;
private int errors;

public WriteMetadataToPdfAction(StateManager stateManager, BibDatabaseMode databaseMode, BibEntryTypesManager entryTypesManager, FieldWriterPreferences fieldWriterPreferences, DialogService dialogService, TaskExecutor taskExecutor, FilePreferences filePreferences, XmpPreferences xmpPreferences) {
public WriteMetadataToPdfAction(StateManager stateManager, BibEntryTypesManager entryTypesManager, FieldWriterPreferences fieldWriterPreferences, DialogService dialogService, TaskExecutor taskExecutor, FilePreferences filePreferences, XmpPreferences xmpPreferences) {
this.stateManager = stateManager;
this.entryTypesManager = entryTypesManager;
this.fieldWriterPreferences = fieldWriterPreferences;
this.dialogService = dialogService;
this.taskExecutor = taskExecutor;
this.filePreferences = filePreferences;
this.xmpPreferences = xmpPreferences;
this.embeddedBibExporter = new EmbeddedBibFilePdfExporter(databaseMode, entryTypesManager, fieldWriterPreferences);

this.executable.bind(needsDatabase(stateManager));
}
Expand Down Expand Up @@ -189,10 +190,12 @@ private void writeMetadata() {
}

/**
* This writes both XMP data and embeddeds the .bib file
* This writes both XMP data and embeds a corresponding .bib file
*/
private void writeMetadataToFile(Path file, BibEntry entry, BibDatabaseContext databaseContext, BibDatabase database) throws Exception {
synchronized private void writeMetadataToFile(Path file, BibEntry entry, BibDatabaseContext databaseContext, BibDatabase database) throws Exception {
XmpUtilWriter.writeXmp(file, entry, database, xmpPreferences);

EmbeddedBibFilePdfExporter embeddedBibExporter = new EmbeddedBibFilePdfExporter(databaseContext.getMode(), entryTypesManager, fieldWriterPreferences);
embeddedBibExporter.exportToFileByPath(databaseContext, database, filePreferences, file);
}

Expand Down
23 changes: 13 additions & 10 deletions src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -423,16 +423,19 @@ public void writeMetadataToPdf() {
if (file.isEmpty()) {
dialogService.notify(Localization.lang("Failed to write metadata, file %1 not found.", file.map(Path::toString).orElse("")));
} else {
try {
XmpUtilWriter.writeXmp(file.get(), entry, databaseContext.getDatabase(), preferences.getXmpPreferences());

EmbeddedBibFilePdfExporter embeddedBibExporter = new EmbeddedBibFilePdfExporter(preferences.getGeneralPreferences().getDefaultBibDatabaseMode(), Globals.entryTypesManager, preferences.getFieldWriterPreferences());
embeddedBibExporter.exportToFileByPath(databaseContext, databaseContext.getDatabase(), preferences.getFilePreferences(), file.get());

dialogService.notify(Localization.lang("Success! Finished writing metadata."));
} catch (IOException | TransformerException ex) {
dialogService.notify(Localization.lang("Error while writing metadata. See the error log for details."));
LOGGER.error("Error while writing metadata to {}", file.map(Path::toString).orElse(""), ex);
synchronized (linkedFile) {
try {
// Similar code can be found at {@link org.jabref.gui.exporter.WriteMetadataToPdfAction.writeMetadataToFile}
XmpUtilWriter.writeXmp(file.get(), entry, databaseContext.getDatabase(), preferences.getXmpPreferences());

EmbeddedBibFilePdfExporter embeddedBibExporter = new EmbeddedBibFilePdfExporter(databaseContext.getMode(), Globals.entryTypesManager, preferences.getFieldWriterPreferences());
embeddedBibExporter.exportToFileByPath(databaseContext, databaseContext.getDatabase(), preferences.getFilePreferences(), file.get());

dialogService.notify(Localization.lang("Success! Finished writing metadata."));
} catch (IOException | TransformerException ex) {
dialogService.notify(Localization.lang("Error while writing metadata. See the error log for details."));
LOGGER.error("Error while writing metadata to {}", file.map(Path::toString).orElse(""), ex);
}
}
}
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
* This class monitors a set of files for changes. Upon detecting a change it notifies the registered {@link
* FileUpdateListener}s.
* <p>
* Implementation based on https://stackoverflow.com/questions/16251273/can-i-watch-for-single-file-change-with-watchservice-not-the-whole-directory
* Implementation based on <a href="https://stackoverflow.com/questions/16251273/can-i-watch-for-single-file-change-with-watchservice-not-the-whole-directory">https://stackoverflow.com/questions/16251273/can-i-watch-for-single-file-change-with-watchservice-not-the-whole-directory</a>.
*/
public class DefaultFileUpdateMonitor implements Runnable, FileUpdateMonitor {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.jabref.logic.util.OS;
import org.jabref.logic.util.StandardFileType;
import org.jabref.logic.util.io.FileUtil;
import org.jabref.logic.xmp.XmpUtilWriter;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.database.BibDatabaseMode;
import org.jabref.model.entry.BibEntry;
Expand Down Expand Up @@ -64,12 +65,19 @@ public void export(BibDatabaseContext databaseContext, Path file, List<BibEntry>
embedBibTex(bibString, file);
}

private void embedBibTex(String bibTeX, Path file) throws IOException {
if (!Files.exists(file) || !FileUtil.isPDFFile(file)) {
/**
* Similar method: {@link XmpUtilWriter#writeXmp(java.nio.file.Path, java.util.List, org.jabref.model.database.BibDatabase, org.jabref.logic.xmp.XmpPreferences)}
*/
private void embedBibTex(String bibTeX, Path path) throws IOException {
if (!Files.exists(path) || !FileUtil.isPDFFile(path)) {
return;
}

try (PDDocument document = Loader.loadPDF(file.toFile())) {
// Read from another file
// Reason: Apache PDFBox does not support writing while the file is opened
// See https://issues.apache.org/jira/browse/PDFBOX-4028
Path newFile = Files.createTempFile("JabRef", "pdf");
try (PDDocument document = Loader.loadPDF(path.toFile())) {
PDDocumentNameDictionary nameDictionary = document.getDocumentCatalog().getNames();
PDEmbeddedFilesNameTreeNode efTree;
Map<String, PDComplexFileSpecification> names;
Expand Down Expand Up @@ -111,19 +119,19 @@ private void embedBibTex(String bibTeX, Path file) throws IOException {
try {
names.put(EMBEDDED_FILE_NAME, fileSpecification);
} catch (UnsupportedOperationException e) {
throw new IOException(Localization.lang("File '%0' is write protected.", file.toString()));
throw new IOException(Localization.lang("File '%0' is write protected.", path.toString()));
}
}

efTree.setNames(names);
nameDictionary.setEmbeddedFiles(efTree);
document.getDocumentCatalog().setNames(nameDictionary);
}
Path newFile = Files.createTempFile("JabRef", "pdf");
document.save(newFile.toFile());
FileUtil.copyFile(newFile, file, true);
Files.delete(newFile);
FileUtil.copyFile(newFile, path, true);

}
Files.delete(newFile);
}

private String getBibString(List<BibEntry> entries) throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ private void extractAbstract() {
try {
description = dcSchema.getDescription();
} catch (BadFieldValueException e) {
LOGGER.warn("Could not get abstract, e");
LOGGER.warn("Could not get abstract", e);
}
if (!StringUtil.isNullOrEmpty(description)) {
bibEntry.setField(StandardField.ABSTRACT, description);
Expand Down
27 changes: 17 additions & 10 deletions src/main/java/org/jabref/logic/xmp/XmpUtilWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import javax.xml.transform.TransformerException;

import org.jabref.logic.exporter.EmbeddedBibFilePdfExporter;
import org.jabref.logic.util.io.FileUtil;
import org.jabref.model.database.BibDatabase;
import org.jabref.model.entry.BibEntry;
Expand Down Expand Up @@ -60,7 +61,8 @@ public class XmpUtilWriter {
* @throws IOException If the file could not be written to or could not be found.
*/
public static void writeXmp(String fileName, BibEntry entry,
BibDatabase database, XmpPreferences xmpPreferences) throws IOException, TransformerException {
BibDatabase database, XmpPreferences xmpPreferences)
throws IOException, TransformerException {
XmpUtilWriter.writeXmp(Path.of(fileName), entry, database, xmpPreferences);
}

Expand All @@ -84,7 +86,8 @@ public static void writeXmp(String fileName, BibEntry entry,
* @throws IOException If the file could not be written to or could not be found.
*/
public static void writeXmp(Path file, BibEntry entry,
BibDatabase database, XmpPreferences xmpPreferences) throws IOException, TransformerException {
BibDatabase database, XmpPreferences xmpPreferences)
throws IOException, TransformerException {
List<BibEntry> bibEntryList = new ArrayList<>();
bibEntryList.add(entry);
XmpUtilWriter.writeXmp(file, bibEntryList, database, xmpPreferences);
Expand Down Expand Up @@ -132,8 +135,8 @@ private static void writeToDCSchema(DublinCoreSchema dcSchema, BibEntry entry, X
* resolve strings. If the database is null the strings will not be resolved.
*/
private static void writeDublinCore(PDDocument document,
List<BibEntry> entries, BibDatabase database, XmpPreferences xmpPreferences)
throws IOException, TransformerException {
List<BibEntry> entries, BibDatabase database, XmpPreferences xmpPreferences)
throws IOException, TransformerException {

List<BibEntry> resolvedEntries;
if (database == null) {
Expand Down Expand Up @@ -297,6 +300,8 @@ private static void writeDocumentInformation(PDDocument document,
* The method will overwrite existing BibTeX-XMP-data, but keep other
* existing metadata.
*
* The code for using PDFBox is also used at {@link EmbeddedBibFilePdfExporter#embedBibTex(java.lang.String, java.nio.file.Path)}.
*
* @param path The file to write the entries to.
* @param bibtexEntries The entries to write to the file. *
* @param database maybenull An optional database which the given bibtex entries belong to, which will be used
Expand All @@ -307,17 +312,20 @@ private static void writeDocumentInformation(PDDocument document,
*/
public static void writeXmp(Path path,
List<BibEntry> bibtexEntries, BibDatabase database,
XmpPreferences xmpPreferences) throws IOException, TransformerException {

XmpPreferences xmpPreferences)
throws IOException, TransformerException {
List<BibEntry> resolvedEntries;
if (database == null) {
resolvedEntries = bibtexEntries;
} else {
resolvedEntries = database.resolveForStrings(bibtexEntries, false);
}

// Read from another file
// Reason: Apache PDFBox does not support writing while the file is opened
// See https://issues.apache.org/jira/browse/PDFBOX-4028
Path newFile = Files.createTempFile("JabRef", "pdf");
try (PDDocument document = Loader.loadPDF(path.toFile())) {

if (document.isEncrypted()) {
throw new EncryptedPdfsNotSupportedException();
}
Expand All @@ -328,17 +336,16 @@ public static void writeXmp(Path path,
XmpUtilWriter.writeDublinCore(document, resolvedEntries, null, xmpPreferences);
}

// Save
// Save updates to original file
try {
Path newFile = Files.createTempFile("JabRef", "pdf");
document.save(newFile.toFile());
FileUtil.copyFile(newFile, path, true);
Files.delete(newFile);
} catch (IOException e) {
LOGGER.debug("Could not write XMP metadata", e);
throw new TransformerException("Could not write XMP metadata: " + e.getLocalizedMessage(), e);
}
}
Files.delete(newFile);
}

private static BibEntry getDefaultOrDatabaseEntry(BibEntry defaultEntry, BibDatabase database) {
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/jabref/model/util/FileUpdateMonitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public interface FileUpdateMonitor {
void removeListener(Path path, FileUpdateListener listener);

/**
* Indicates whether or not the native system's file monitor has successfully started.
* Indicates whether the native system's file monitor has successfully started.
* @return true is process is running; false otherwise.
*/
boolean isActive();
Expand Down