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

Implement book cover feature #12198

Open
wants to merge 46 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
bf9388b
Add method to check if file is image in LinkedFile.java
damgam0288 Nov 11, 2024
6aaa9af
Add method to retrieve cover image in BibEntry.java
damgam0288 Nov 11, 2024
aa64848
Add HTML image for book cover to setPreviewText
damgam0288 Nov 11, 2024
b74e022
Remove unnecessary TODO
damgam0288 Nov 11, 2024
2100210
Check EntryType is a book or related item in BibEntry
damgam0288-anu Nov 12, 2024
c89f3c8
Fix book cover size too large in PreviewViewer
damgam0288-anu Nov 12, 2024
d7c559b
Refactor locations of methods in BibEntry
damgam0288-anu Nov 12, 2024
7c82123
Remove todo comments. Add comments.
damgam0288-anu Nov 12, 2024
fd3750a
Add todo comments for testing new methods
damgam0288-anu Nov 13, 2024
0e04bbf
Refactor BibEntry to return cover image file instead of path
damgam0288 Nov 13, 2024
5e9d764
Refactor PreviewViewer to receive cover file from BibEntry and conver…
damgam0288 Nov 13, 2024
0f0d2fa
Add tests for getCoverImageFile method in BibEntry
damgam0288 Nov 14, 2024
ab2d3ab
Change cover string to a static field
damgam0288 Nov 14, 2024
da906f7
Align book cover to right side in PreviewViewer setPreviewText
damgam0288 Nov 15, 2024
eb06002
Fix checkstyle issues
damgam0288 Nov 15, 2024
23c04d5
Make getCoverImageFile only return image with "cover" in the descript…
damgam0288 Nov 16, 2024
c902323
Fix poor cover image resizing
damgam0288 Nov 16, 2024
6b789f5
Add test for getCoverImage in BibEntryTest.java
damgam0288 Nov 17, 2024
34647c8
Fix cover image not being displayed when its title has spaces
damgam0288 Nov 17, 2024
42ac2c9
Refactor getBookCoverURI in PreviewViewer
damgam0288 Nov 17, 2024
c561303
Merge branch 'JabRef:main' into fix-10120
damgam0288 Nov 17, 2024
a9c5dbd
Update Changelog for book cover feature issue 10120
damgam0288 Nov 17, 2024
aacffed
Merge branch 'main' into fix-10120
damgam0288 Nov 17, 2024
4119b7d
Fix CI tests failure: change getBookCoverURI
damgam0288 Nov 18, 2024
8b1a961
Merge remote-tracking branch 'origin/fix-10120' into fix-10120
damgam0288 Nov 18, 2024
871fcaa
Fix CI tests failure: handle entry field null value
damgam0288 Nov 18, 2024
6d2e3b1
Merge branch 'main' into fix-10120
damgam0288 Nov 18, 2024
a7e9b10
Revise getCoverImage tests to use parameterization.
damgam0288 Nov 21, 2024
2cf00f3
Revise Image_Extensions to use HashSet of StandardExternalFileTypes enum
damgam0288 Nov 21, 2024
28a762d
Reword CHANGELOG.md. Remove wrong import BibEntry.java
damgam0288 Nov 21, 2024
23a691f
Revise LinkedFile.isImage to check for keyword "image" instead of che…
damgam0288 Nov 21, 2024
b1b39d7
Revise BibEntryTest.java with correct "filetypes" for getCoverImage t…
damgam0288 Nov 21, 2024
edd101e
Fix cover image scales too wide in PreviewViewer.java
damgam0288 Nov 23, 2024
015a14c
Revise isCoverable in BibEntry.java to use HashSet instead of List
damgam0288 Nov 23, 2024
7198ce8
Merge branch 'main' into fix-10120
damgam0288 Nov 23, 2024
75ad6ee
Merge branch 'main' into fix-10120
damgam0288 Nov 29, 2024
8684dbd
Attempt fix OpenRewrite error in BibEntryTest.java
damgam0288 Nov 29, 2024
a94dac7
Rewrite getCoverImage tests in BibEntryTest.java
damgam0288 Nov 29, 2024
eb0a1b7
Fix getCoverImage to check files is null or empty
damgam0288 Nov 29, 2024
d0ffcc1
Remove all getCoverImage tests that expect Optional.empty as a result
damgam0288 Nov 29, 2024
c7b71d4
Add BibEntryTest - getCoverImageUpdatesWithChangeToDescription
damgam0288 Nov 29, 2024
f377939
Add OpenRewrite changes to multiple classes
damgam0288 Nov 30, 2024
4f3df63
Fix missing Junit imports for tests
damgam0288 Nov 30, 2024
87cc072
Remove unused import in BibEntry
damgam0288 Nov 30, 2024
80cdf05
Merge branch 'main' into fix-10120
damgam0288 Nov 30, 2024
b85aab9
Add tests for getCoverImage in BibEntryTest
damgam0288 Nov 30, 2024
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
- We added a search bar for filtering keyboard shortcuts. [#11686](https://github.com/JabRef/jabref/issues/11686)
- By double clicking on a local citation in the Citation Relations Tab you can now jump the linked entry. [#11955](https://github.com/JabRef/jabref/pull/11955)
- We use the menu icon for background tasks as a progress indicator to visualise an import's progress when dragging and dropping several PDF files into the main table. [#12072](https://github.com/JabRef/jabref/pull/12072)
- We added the ability for users to display a cover image in the preview panel of the entry editor for book, in-book and booklet citations [#10120](https://github.com/JabRef/jabref/issues/10120)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- We added the ability for users to display a cover image in the preview panel of the entry editor for book, in-book and booklet citations [#10120](https://github.com/JabRef/jabref/issues/10120)
- We added the ability for users to display a cover image in the preview panel of the entry editor for book, in-book and booklet citations. [#10120](https://github.com/JabRef/jabref/issues/10120)


### Changed

Expand Down
18 changes: 16 additions & 2 deletions src/main/java/org/jabref/gui/preview/PreviewViewer.java
Original file line number Diff line number Diff line change
Expand Up @@ -218,14 +218,28 @@ private void setPreviewText(String text) {
layoutText = """
<html>
<body id="previewBody">
<div id="content"> %s </div>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. See general review comments.

<div style="display: flex;">
<div id="content" style="flex: 1;">
%s
</div>
<div id="bookCover" style="flex: 1;">
<img src="%s" style="width: auto; height: 100vh;" align="right">
</div>
</div>
</body>
</html>
""".formatted(text);
""".formatted(text, getBookCoverURI());
highlightLayoutText();
this.setHvalue(0);
}

private String getBookCoverURI() {
return entry
.flatMap(e -> e.getCoverImageFile()
.map(file -> "file:///" + file.getLink()))
.orElse("");
}

private void highlightLayoutText() {
if (layoutText == null) {
return;
Expand Down
35 changes: 34 additions & 1 deletion src/main/java/org/jabref/model/entry/BibEntry.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import scala.annotation.meta.field;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really needed?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like a wrong import

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong import sorry, will remove


/**
* Represents a Bib(La)TeX entry, which can be BibTeX or BibLaTeX.
Expand Down Expand Up @@ -99,6 +100,10 @@
public class BibEntry implements Cloneable {

public static final EntryType DEFAULT_TYPE = StandardEntryType.Misc;

private static final List<EntryType> COVERABLE_TYPES = List.of(StandardEntryType.Book, StandardEntryType.InBook, StandardEntryType.Booklet);
private static final String COVER_TAG = "cover";

private static final Logger LOGGER = LoggerFactory.getLogger(BibEntry.class);
private final SharedBibEntryData sharedBibEntryData;

Expand Down Expand Up @@ -1124,6 +1129,26 @@ public Optional<FieldChange> addFiles(List<LinkedFile> filesToAdd) {
currentFiles.addAll(filesToAdd);
return setFiles(currentFiles);
}

/**
* @return <code>LinkedFile</code> that contains the cover image
* if the <code>BibEntry</code> is a Book or other <code>COVERABLE_TYPES</code>
*/
public Optional<LinkedFile> getCoverImageFile() {
if (!isCoverable()) {
return Optional.empty();
}

for (LinkedFile file : getFiles()) {
if (file.getDescription().equalsIgnoreCase(COVER_TAG)) {
if (file.isImage()) {
return Optional.of(file);
}
}
}

return Optional.empty();
}
// endregion

/**
Expand Down Expand Up @@ -1156,7 +1181,8 @@ public Optional<Month> getMonth() {
return getFieldOrAlias(StandardField.MONTH).flatMap(Month::parse);
}

public OptionalBinding<String> getFieldBinding(Field field) {
public OptionalBinding<String> getFieldBinding(Field
field) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revert newline

if ((field == InternalField.TYPE_HEADER) || (field == InternalField.OBSOLETE_TYPE_HEADER)) {
return EasyBind.wrapNullable(type).mapOpt(EntryType::getDisplayName);
}
Expand Down Expand Up @@ -1253,4 +1279,11 @@ public boolean isEmpty() {
}
return StandardField.AUTOMATIC_FIELDS.containsAll(this.getFields());
}

/**
* @return <code>true</code> if this entry's <code>type</code> is a Book or one of <code>COVERABLE_TYPES</code>
*/
private boolean isCoverable() {
return COVERABLE_TYPES.contains(this.type.get());
}
}
7 changes: 7 additions & 0 deletions src/main/java/org/jabref/model/entry/LinkedFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
Expand Down Expand Up @@ -40,6 +41,8 @@ public class LinkedFile implements Serializable {

private static final LinkedFile NULL_OBJECT = new LinkedFile("", Path.of(""), "");

private static final List<String> IMAGE_EXTENSIONS = new ArrayList<>(List.of("jpeg", "jpg", "png", "gif"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use a hashset for more efficient lookup and simpler code (next review comment)


// We have to mark these properties as transient because they can't be serialized directly
private transient StringProperty description = new SimpleStringProperty();
private transient StringProperty link = new SimpleStringProperty();
Expand Down Expand Up @@ -220,6 +223,10 @@ public boolean isOnlineLink() {
return isOnlineLink(link.get());
}

public boolean isImage() {
return IMAGE_EXTENSIONS.stream().anyMatch(getFileType().toLowerCase()::contains);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you use a hashset, can be simplified to:

return IMAGE_EXTENSIONS.contains(getFileType().toLowerCase());

Copy link
Contributor Author

@damgam0288 damgam0288 Nov 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed this to check for the string "image" in the fileType because I could not find a way to check equivalence between fileType, which is a StringProperty and the actual JPG, PNG etc... file types from inside the LinkedFile class.

}

public Optional<Path> findIn(BibDatabaseContext databaseContext, FilePreferences filePreferences) {
List<Path> dirs = databaseContext.getFileDirectories(filePreferences);
return findIn(dirs);
Expand Down
95 changes: 95 additions & 0 deletions src/test/java/org/jabref/model/entry/BibEntryTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
Expand Down Expand Up @@ -813,6 +814,100 @@ void mergeEntriesWithOverlapAndPriorityGivenToOverlappingField() {
assertEquals(expected.getFields(), copyEntry.getFields());
}

@Test
void getCoverImageReturnsEmptyIfNoFiles() {
entry = new BibEntry(StandardEntryType.Book).withField(StandardField.AUTHOR, "value");
assertEquals(Optional.empty(), entry.getCoverImageFile());
}

@Test
void getCoverImageReturnsEmptyIfNoImageFiles() {
LinkedFile pdf = new LinkedFile("", Paths.get("Baldoni2002.pdf").toAbsolutePath().toString(), "pdf");
LinkedFile markdown = new LinkedFile("", "readme.md", "md");
entry = new BibEntry(StandardEntryType.Book).withField(StandardField.AUTHOR, "value");

entry.addFile(markdown);
entry.addFile(pdf);

assertEquals(Optional.empty(), entry.getCoverImageFile());
}

@Test
void getCoverImageReturnsEmptyIfEntryIsNotCoverable() {
entry = new BibEntry(StandardEntryType.Proceedings).withField(StandardField.AUTHOR, "value");
assertEquals(Optional.empty(), entry.getCoverImageFile());

entry = new BibEntry(StandardEntryType.Dataset).withField(StandardField.AUTHOR, "value");
assertEquals(Optional.empty(), entry.getCoverImageFile());

entry = new BibEntry(StandardEntryType.Software).withField(StandardField.AUTHOR, "value");
assertEquals(Optional.empty(), entry.getCoverImageFile());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can convert to parameterized test to avoid repetition

}

@Test
void getCoverImageDoesNotReturnImagesWithoutCoverDescription() {
LinkedFile cover1 = new LinkedFile("", Paths.get("JabRef-icon-128.png"), "png");
LinkedFile cover2 = new LinkedFile("", Paths.get("JabRef-icon-64.png"), "png");
LinkedFile cover3 = new LinkedFile("", Paths.get("JabRef-icon-32.png"), "png");
entry = new BibEntry(StandardEntryType.Book).withField(StandardField.AUTHOR, "value");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can keep this here and parameterize the covers


entry.addFile(cover1);
entry.addFile(cover2);
entry.addFile(cover3);

assertNotEquals(Optional.of(cover1), entry.getCoverImageFile());
assertNotEquals(Optional.of(cover2), entry.getCoverImageFile());
assertNotEquals(Optional.of(cover3), entry.getCoverImageFile());
Copy link
Member

@subhramit subhramit Nov 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it does not return images, can this be converted to assertEquals with Optional.empty() for a stricter check? If not, what is it expected to be returned? (Determinism in tests is desirable).

}

@Test
void getCoverImageDoesNotReturnDocumentsWithCoverDescription() {
LinkedFile pdf = new LinkedFile("cover", Paths.get("Baldoni2002.pdf"), "pdf");
LinkedFile md = new LinkedFile("cover", Paths.get("readme.md"), "md");
entry = new BibEntry(StandardEntryType.Book).withField(StandardField.AUTHOR, "value");

entry.addFile(pdf);
entry.addFile(md);

assertNotEquals(Optional.of(md), entry.getCoverImageFile());
assertNotEquals(Optional.of(pdf), entry.getCoverImageFile());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar comments as above

}

@Test
void getCoverImageReturnsCorrectImage() {
LinkedFile cover1 = new LinkedFile("", Paths.get("JabRef-icon-128.png"), "png");
LinkedFile cover2 = new LinkedFile("", Paths.get("JabRef-icon-64.png"), "png");
LinkedFile cover3 = new LinkedFile("cover", Paths.get("JabRef-icon-32.png"), "png");
BibEntry entry = new BibEntry(StandardEntryType.Book).withField(StandardField.AUTHOR, "value");

entry.addFile(cover1);
entry.addFile(cover2);
entry.addFile(cover3);

assertEquals(Optional.of(cover3), entry.getCoverImageFile());
}

@Test
void getCoverImageUpdatesWithChangeToDescription() {
List<LinkedFile> files = new ArrayList<>();

files.add(new LinkedFile("cover", Paths.get("JabRef-icon-128.png"), "png"));
files.add(new LinkedFile("", Paths.get("JabRef-icon-64.png"), "png"));
LinkedFile cover1 = files.get(0);
LinkedFile cover2 = files.get(1);

BibEntry entry = new BibEntry(StandardEntryType.Book).withField(StandardField.AUTHOR, "value");
entry.setFiles(files);

assertEquals(Optional.of(cover1), entry.getCoverImageFile());

cover1.setDescription("");
cover2.setDescription("cover");
entry.setFiles(files);

assertEquals(Optional.of(cover2), entry.getCoverImageFile());
}

public static Stream<BibEntry> isEmpty() {
return Stream.of(
new BibEntry(),
Expand Down
Loading