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 all 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 @@ -48,6 +48,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
- 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)
- The PDF content importer now supports importing title from upto the second page of the PDF. [#12139](https://github.com/JabRef/jabref/issues/12139)
- 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
25 changes: 23 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,35 @@ 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: 75vh; height: auto; max-height: 100vh;"
align="right";
>
</div>
</div>
</body>
</html>
""".formatted(text);
""".formatted(text, getBookCoverURI());
highlightLayoutText();
this.setHvalue(0);
}

private String getBookCoverURI() {
if (entry != null) {
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 check really necessary? I would convert it into assert entry != null;

if (entry.getCoverImageFile().isPresent()) {
return "file:///" + entry.getCoverImageFile().get().getLink();
}
}

return "";
}

private void highlightLayoutText() {
if (layoutText == null) {
return;
Expand Down
47 changes: 47 additions & 0 deletions src/main/java/org/jabref/model/entry/BibEntry.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,16 @@
public class BibEntry implements Cloneable {

public static final EntryType DEFAULT_TYPE = StandardEntryType.Misc;

private static final HashSet<EntryType> COVERABLE_TYPES = new HashSet<>();
static {
COVERABLE_TYPES.add(StandardEntryType.Book);
COVERABLE_TYPES.add(StandardEntryType.InBook);
COVERABLE_TYPES.add(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 +1134,36 @@ 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();
}

List<LinkedFile> files = getFiles();

if (files == null) {
return Optional.empty();
}

if (files.isEmpty()) {
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 @@ -1253,4 +1293,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(getType());
}
}
8 changes: 8 additions & 0 deletions src/main/java/org/jabref/model/entry/LinkedFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,14 @@ public boolean isOnlineLink() {
return isOnlineLink(link.get());
}

/**
* @return <code>true</code> if <code>fileType</code> contains the string "image"
*/
public boolean isImage() {
// Cannot compare fileType to StandardExternalFileType enum since it is a StringProperty
return getFileType().toLowerCase().contains("image");
}

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

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,82 @@ void mergeEntriesWithOverlapAndPriorityGivenToOverlappingField() {
assertEquals(expected.getFields(), copyEntry.getFields());
}

@Test
void getCoverImageReturnsCorrectImage() {
LinkedFile cover1 = new LinkedFile("", Paths.get("JabRef-icon-128.png"), "PNG image");
LinkedFile cover2 = new LinkedFile("", Paths.get("JabRef-icon-64.png"), "PNG image");
LinkedFile cover3 = new LinkedFile("cover", Paths.get("wallpaper.jpg"), "JPG image");
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 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());
}

@ParameterizedTest
@MethodSource("nonCoverableEntryTypes")
void getCoverImageReturnsEmptyIfEntryIsNotCoverable(StandardEntryType entryType) {
BibEntry entry = new BibEntry(entryType).withField(StandardField.AUTHOR, "value");
assertEquals(Optional.empty(), entry.getCoverImageFile());
}

static Stream<StandardEntryType> nonCoverableEntryTypes() {
return Stream.of(
StandardEntryType.Proceedings,
StandardEntryType.Dataset,
StandardEntryType.Software
);
}

@ParameterizedTest
@MethodSource("imagesWithoutCoverDescription")
void getCoverImageDoesNotReturnImagesWithoutCoverDescription(LinkedFile image) {
entry = new BibEntry(StandardEntryType.Book).withField(StandardField.AUTHOR, "value");
entry.addFile(image);
assertEquals(Optional.empty(), entry.getCoverImageFile());
}

static Stream<LinkedFile> imagesWithoutCoverDescription() {
return Stream.of(
new LinkedFile("", Paths.get("JabRef-icon-128.png"), "PNG image"),
new LinkedFile("", Paths.get("JabRef-icon-64.png"), "PNG image"),
new LinkedFile("", Paths.get("JabRef-icon-32.png"), "PNG image")
);
}

@ParameterizedTest
@MethodSource("docsWithCoverDescription")
void getCoverImageDoesNotReturnDocumentsWithCoverDescription(LinkedFile file) {
entry = new BibEntry(StandardEntryType.Book).withField(StandardField.AUTHOR, "value");
entry.addFile(file);
assertEquals(Optional.empty(), entry.getCoverImageFile());
}

static Stream<LinkedFile> docsWithCoverDescription() {
return Stream.of(
new LinkedFile("cover", Paths.get("Baldoni2002.pdf"), "pdf"),
new LinkedFile("cover", Paths.get("readme.md"), "md"),
new LinkedFile("cover", Paths.get("BiblioscapeImporterTestArticleST.txt"), "txt"),
new LinkedFile("cover", Paths.get("emptyFile.xml"), "xml")
);
}

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