Added added service for image formats not supported by ImageIO (jp2)

This commit is contained in:
Michael Spalti
2021-12-10 17:14:17 -08:00
parent a009a24474
commit e07f562469
11 changed files with 230 additions and 29 deletions

View File

@@ -8,18 +8,30 @@
package org.dspace.app.canvasdimension;
import java.util.Arrays;
import java.util.UUID;
import org.apache.commons.cli.*;
import org.dspace.app.canvasdimension.factory.IIIFCanvasDimensionServiceFactory;
import org.dspace.app.canvasdimension.service.IIIFCanvasDimensionService;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.factory.EPersonServiceFactory;
import org.dspace.eperson.service.EPersonService;
import org.dspace.handle.factory.HandleServiceFactory;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
public class CanvasDimensionCLI {
private static final EPersonService epersonService = EPersonServiceFactory.getInstance().getEPersonService();
private static final ConfigurationService configurationService = DSpaceServicesFactory.getInstance()
.getConfigurationService();
private CanvasDimensionCLI() {}
public static void main(String[] argv) throws Exception {
@@ -27,16 +39,26 @@ public class CanvasDimensionCLI {
boolean force = false;
boolean isQuiet = false;
String identifier = null;
String eperson = null;
int max2Process = Integer.MAX_VALUE;
boolean iiifEnabled = configurationService.getBooleanProperty("iiif.enabled");
if (!iiifEnabled) {
System.out.println("IIIF is not enabled on this DSpace server.");
return;
}
Context context = new Context();
IIIFCanvasDimensionProcessor canvasProcessor = new IIIFCanvasDimensionProcessor();
IIIFCanvasDimensionService canvasProcessor = IIIFCanvasDimensionServiceFactory.getInstance()
.getIiifCanvasDimensionService();
CommandLineParser parser = new DefaultParser();
Options options = new Options();
options.addOption("i", "identifier", true,
"process IIIF canvas dimensions for images belonging to identifier");
options.addOption("e", "eperson", true,
"email of eperson setting canvas dimensions");
options.addOption("f", "force", false,
"force update of all IIIF canvas height and width dimensions");
options.addOption("q", "quiet", false,
@@ -74,6 +96,9 @@ public class CanvasDimensionCLI {
if (line.hasOption('q')) {
isQuiet = true;
}
if (line.hasOption('e')) {
eperson = line.getOptionValue('e');
}
if (line.hasOption('i')) {
identifier = line.getOptionValue('i');
} else {
@@ -111,6 +136,21 @@ public class CanvasDimensionCLI {
+ identifier + " to a DSpace object");
}
EPerson user;
if (eperson.indexOf('@') != -1) {
// @ sign, must be an email
user = epersonService.findByEmail(context, eperson);
} else {
user = epersonService.find(context, UUID.fromString(eperson));
}
if (user == null) {
System.out.println("Error, eperson cannot be found: " + eperson);
System.exit(1);
}
context.setCurrentUser(user);
canvasProcessor.setForceProcessing(force);
canvasProcessor.setMax2Process(max2Process);

View File

@@ -0,0 +1,65 @@
package org.dspace.app.canvasdimension;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.dspace.app.canvasdimension.service.IIIFApiQueryService;
import org.dspace.content.Bitstream;
import org.dspace.services.ConfigurationService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
public class IIIFApiQueryServiceImpl implements IIIFApiQueryService, InitializingBean {
@Autowired(required = true)
protected ConfigurationService configurationService;
String iiifImageServer;
@Override
public void afterPropertiesSet() throws Exception {
iiifImageServer = configurationService.getProperty("iiif.image.server");
}
public int[] getImageDimensions(Bitstream bitstream) {
return getIiifImageDimensions(bitstream);
}
/**
* Retrieves image dimensions from the image server (IIIF Image API v.2.1.1).
* @param bitstream the bitstream DSO
* @return image dimensions
*/
private int[] getIiifImageDimensions(Bitstream bitstream) {
int[] arr = new int[2];
String path = iiifImageServer + bitstream.getID() + "/info.json";
URL url;
try {
url = new URL(path);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
BufferedReader in = new BufferedReader(
new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuilder response = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
JsonNode parent = new ObjectMapper().readTree(response.toString());
arr[0] = parent.get("width").asInt();
arr[1] = parent.get("height").asInt();
return arr;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}

View File

@@ -16,11 +16,14 @@ import java.util.Optional;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.dspace.app.canvasdimension.service.IIIFApiQueryService;
import org.dspace.app.canvasdimension.service.IIIFCanvasDimensionService;
import org.dspace.content.Bitstream;
import org.dspace.content.Bundle;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.Item;
import org.dspace.content.MetadataValue;
import org.dspace.content.service.BitstreamService;
import org.dspace.content.service.CommunityService;
import org.dspace.content.service.DSpaceObjectService;
@@ -28,19 +31,20 @@ import org.dspace.content.service.ItemService;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.license.CreativeCommonsServiceImpl;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
public class IIIFCanvasDimensionProcessor implements InitializingBean {
public class IIIFCanvasDimensionServiceImpl implements IIIFCanvasDimensionService {
@Autowired
@Autowired(required = true)
ItemService itemService;
@Autowired
@Autowired(required = true)
CommunityService communityService;
@Autowired
@Autowired(required = true)
BitstreamService bitstreamService;
@Autowired
@Autowired(required = true)
DSpaceObjectService<Bitstream> dSpaceObjectService;
@Autowired(required = true)
IIIFApiQueryService iiifApiQuery;
// metadata used to enable the iiif features on the item
public static final String METADATA_IIIF_ENABLED = "dspace.iiif.enabled";
@@ -55,20 +59,17 @@ public class IIIFCanvasDimensionProcessor implements InitializingBean {
private int processed = 0;
protected Item currentItem = null; // TODO needed?
@Override
public void afterPropertiesSet() throws Exception {
}
/**
* Set the force processing property. If true, existing canvas
* metadata will be replaced.
* @param force
*/
@Override
public void setForceProcessing(boolean force) {
forceProcessing = force;
}
@Override
public void setIsQuiet(boolean quiet) {
isQuiet = quiet;
}
@@ -77,6 +78,7 @@ public class IIIFCanvasDimensionProcessor implements InitializingBean {
* Set the maximum number of items to process.
* @param max2Process
*/
@Override
public void setMax2Process(int max2Process) {
this.max2Process = max2Process;
}
@@ -85,6 +87,7 @@ public class IIIFCanvasDimensionProcessor implements InitializingBean {
* Set dso identifiers to skip.
* @param skipList
*/
@Override
public void setSkipList(List<String> skipList) {
this.skipList = skipList;
}
@@ -94,6 +97,7 @@ public class IIIFCanvasDimensionProcessor implements InitializingBean {
* @param context
* @throws Exception
*/
@Override
public void processSite(Context context) throws Exception {
if (skipList != null) {
//if a skip-list exists, we need to filter community-by-community
@@ -119,6 +123,7 @@ public class IIIFCanvasDimensionProcessor implements InitializingBean {
* @param community
* @throws Exception
*/
@Override
public void processCommunity(Context context, Community community) throws Exception {
if (!inSkipList(community.getHandle())) {
List<Community> subcommunities = community.getSubcommunities();
@@ -138,6 +143,7 @@ public class IIIFCanvasDimensionProcessor implements InitializingBean {
* @param collection
* @throws Exception
*/
@Override
public void processCollection(Context context, Collection collection) throws Exception {
if (!inSkipList(collection.getHandle())) {
Iterator<Item> itemIterator = itemService.findAllByCollection(context, collection);
@@ -153,6 +159,7 @@ public class IIIFCanvasDimensionProcessor implements InitializingBean {
* @param item
* @throws Exception
*/
@Override
public void processItem(Context context, Item item) throws Exception {
if (!inSkipList(item.getHandle())) {
boolean isIIIFItem = item.getMetadata().stream().filter(m -> m.getMetadataField().toString('.')
@@ -160,8 +167,10 @@ public class IIIFCanvasDimensionProcessor implements InitializingBean {
.anyMatch(m -> m.getValue().equalsIgnoreCase("true") ||
m.getValue().equalsIgnoreCase("yes"));
if (isIIIFItem) {
if (processBundles(context, item)) {
if (processItemBundles(context, item)) {
++processed;
// commit changes
context.commit();
}
}
}
@@ -174,15 +183,16 @@ public class IIIFCanvasDimensionProcessor implements InitializingBean {
* @return
* @throws Exception
*/
private boolean processBundles(Context context, Item item) throws Exception {
private boolean processItemBundles(Context context, Item item) throws Exception {
List<Bundle> bundles = getIIIFBundles(item);
boolean done = false;
for (Bundle bundle : bundles) {
List<Bitstream> myBitstreams = bundle.getBitstreams();
for (Bitstream myBitstream : myBitstreams) {
done |= processBitstream(context, myBitstream);
List<Bitstream> bitstreams = bundle.getBitstreams();
for (Bitstream bit : bitstreams) {
done |= processBitstream(context, bit);
}
}
itemService.update(context, item);
return done;
}
@@ -197,15 +207,23 @@ public class IIIFCanvasDimensionProcessor implements InitializingBean {
*/
private boolean processBitstream(Context context, Bitstream bitstream) throws Exception {
boolean processed = false;
boolean isUnsupported = bitstream.getFormat(context).getMIMEType().contains("image/jp2");
boolean isImage = bitstream.getFormat(context).getMIMEType().contains("image/");
if (isImage) {
Optional op = bitstream.getMetadata().stream().filter(m -> m.getMetadataField().toString('.')
.contentEquals(IIIF_WIDTH_METADATA)).findFirst();
Optional<MetadataValue> op = bitstream.getMetadata().stream()
.filter(m -> m.getMetadataField().toString('.')
.contentEquals(IIIF_WIDTH_METADATA)).findFirst();
if (op.isEmpty() || forceProcessing) {
InputStream srcStream = bitstreamService.retrieve(context, bitstream);
int[] dims = ImageDimensionReader.getImageDimensions(srcStream);
int[] dims;
if (isUnsupported) {
dims = iiifApiQuery.getImageDimensions(bitstream);
} else {
InputStream stream = bitstreamService.retrieve(context, bitstream);
dims = ImageDimensionReader.getImageDimensions(stream);
}
if (dims != null) {
processed = setBitstreamMetadata(context, bitstream, dims);
bitstreamService.update(context, bitstream);
}
}
}
@@ -216,14 +234,12 @@ public class IIIFCanvasDimensionProcessor implements InitializingBean {
try {
dSpaceObjectService.clearMetadata(context, bitstream, "iiif",
"image", "width", Item.ANY);
dSpaceObjectService.addAndShiftRightMetadata(context, bitstream, "iiif",
"image", "width", null,
String.valueOf(dims[0]), null, -1, -1);
dSpaceObjectService.setMetadataSingleValue(context, bitstream, "iiif",
"image", "width", Item.ANY, String.valueOf(dims[0]));
dSpaceObjectService.clearMetadata(context, bitstream, "iiif",
"image", "height", Item.ANY);
dSpaceObjectService.addAndShiftRightMetadata(context, bitstream, "iiif",
"image", "height", null,
String.valueOf(dims[1]), null, -1, -1);
dSpaceObjectService.setMetadataSingleValue(context, bitstream, "iiif",
"image", "height", Item.ANY, String.valueOf(dims[1]));
return true;
} catch (SQLException e) {
System.out.println("Unable to update metadata: " + e.getMessage());

View File

@@ -18,8 +18,8 @@ public class ImageDimensionReader {
public static int[] getImageDimensions(InputStream image) throws Exception {
int[] dims = new int[2];
BufferedImage buf = ImageIO.read(image);
int width = buf.getWidth();
int height = buf.getHeight();
int width = buf.getWidth(null);
int height = buf.getHeight(null);
if (width > 0 && height > 0) {
dims[0] = width;
dims[1] = height;

View File

@@ -0,0 +1,15 @@
package org.dspace.app.canvasdimension.factory;
import org.dspace.app.canvasdimension.service.IIIFCanvasDimensionService;
import org.dspace.services.factory.DSpaceServicesFactory;
public abstract class IIIFCanvasDimensionServiceFactory {
public static IIIFCanvasDimensionServiceFactory getInstance() {
return DSpaceServicesFactory.getInstance().getServiceManager()
.getServiceByName("iiifCanvasDimensionServiceFactory",
IIIFCanvasDimensionServiceFactory.class);
}
public abstract IIIFCanvasDimensionService getIiifCanvasDimensionService();
}

View File

@@ -0,0 +1,15 @@
package org.dspace.app.canvasdimension.factory;
import org.dspace.app.canvasdimension.service.IIIFCanvasDimensionService;
import org.springframework.beans.factory.annotation.Autowired;
public class IIIFCanvasDimensionServiceFactoryImpl extends IIIFCanvasDimensionServiceFactory {
@Autowired(required = true)
private IIIFCanvasDimensionService iiifCanvasDimensionService;
@Override
public IIIFCanvasDimensionService getIiifCanvasDimensionService() {
return iiifCanvasDimensionService;
}
}

View File

@@ -0,0 +1,9 @@
package org.dspace.app.canvasdimension.service;
import org.dspace.content.Bitstream;
public interface IIIFApiQueryService {
public int[] getImageDimensions(Bitstream bitstream);
}

View File

@@ -0,0 +1,30 @@
package org.dspace.app.canvasdimension.service;
import java.util.List;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.Item;
import org.dspace.core.Context;
public interface IIIFCanvasDimensionService {
public void processSite(Context context) throws Exception;
public void processCommunity(Context context, Community community) throws Exception;
public void processCollection(Context context, Collection collection) throws Exception;
public void processItem(Context context, Item item) throws Exception;
public void setForceProcessing(boolean force);
public void setIsQuiet(boolean quiet);
public void setMax2Process(int max2Process);
public void setSkipList(List<String> skipList);
}

View File

@@ -366,4 +366,11 @@
<class>org.dspace.statistics.AnonymizeStatistics</class>
</step>
</command>
<command>
<name>canvas-dimensions</name>
<description>Add canvas width and height metadata for IIIF enabled items.</description>
<step>
<class>org.dspace.app.canvasdimension.CanvasDimensionCLI</class>
</step>
</command>
</commands>

View File

@@ -10,6 +10,7 @@
<bean id="requestItemServiceFactory" class="org.dspace.app.requestitem.factory.RequestItemServiceFactoryImpl"/>
<bean id="itemExportServiceFactory" class="org.dspace.app.itemexport.factory.ItemExportServiceFactoryImpl"/>
<bean id="itemImportServiceFactory" class="org.dspace.app.itemimport.factory.ItemImportServiceFactoryImpl"/>
<bean id="iiifCanvasDimensionServiceFactory" class="org.dspace.app.canvasdimension.factory.IIIFCanvasDimensionServiceFactoryImpl"/>
<bean id="mediaFilterServiceFactory" class="org.dspace.app.mediafilter.factory.MediaFilterServiceFactoryImpl"/>
<bean id="sfxServiceFactory" class="org.dspace.app.sfx.factory.SfxServiceFactoryImpl"/>
<bean id="appUtilServiceFactory" class="org.dspace.app.util.factory.UtilServiceFactoryImpl"/>

View File

@@ -17,6 +17,9 @@
<!--Ensure that bean remains prototype ! -->
<bean class="org.dspace.app.mediafilter.MediaFilterServiceImpl" scope="prototype"/>
<bean class="org.dspace.app.canvasdimension.IIIFCanvasDimensionServiceImpl" scope="prototype"/>
<bean class="org.dspace.app.canvasdimension.IIIFApiQueryServiceImpl" scope="prototype"/>
<bean class="org.dspace.app.sfx.SFXFileReaderServiceImpl" scope="prototype"/>
<bean class="org.dspace.app.util.MetadataExposureServiceImpl"/>