2 * Copyright (C) 2020 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
9 package eu
.etaxonomy
.cdm
.remote
.controller
.iiif
;
11 import java
.io
.IOException
;
13 import java
.net
.URISyntaxException
;
14 import java
.util
.ArrayList
;
15 import java
.util
.HashMap
;
16 import java
.util
.List
;
18 import java
.util
.stream
.Collectors
;
20 import org
.apache
.http
.HttpException
;
21 import org
.apache
.log4j
.Logger
;
23 import de
.digitalcollections
.iiif
.model
.ImageContent
;
24 import de
.digitalcollections
.iiif
.model
.MetadataEntry
;
25 import de
.digitalcollections
.iiif
.model
.MimeType
;
26 import de
.digitalcollections
.iiif
.model
.PropertyValue
;
27 import de
.digitalcollections
.iiif
.model
.enums
.ViewingDirection
;
28 import de
.digitalcollections
.iiif
.model
.sharedcanvas
.Canvas
;
29 import de
.digitalcollections
.iiif
.model
.sharedcanvas
.Manifest
;
30 import de
.digitalcollections
.iiif
.model
.sharedcanvas
.Resource
;
31 import de
.digitalcollections
.iiif
.model
.sharedcanvas
.Sequence
;
32 import eu
.etaxonomy
.cdm
.api
.service
.IMediaService
;
33 import eu
.etaxonomy
.cdm
.api
.service
.MediaServiceImpl
;
34 import eu
.etaxonomy
.cdm
.api
.service
.l10n
.LocaleContext
;
35 import eu
.etaxonomy
.cdm
.common
.media
.CdmImageInfo
;
36 import eu
.etaxonomy
.cdm
.model
.common
.Credit
;
37 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableEntity
;
38 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableSource
;
39 import eu
.etaxonomy
.cdm
.model
.common
.Identifier
;
40 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
41 import eu
.etaxonomy
.cdm
.model
.common
.LanguageString
;
42 import eu
.etaxonomy
.cdm
.model
.media
.ExternalLink
;
43 import eu
.etaxonomy
.cdm
.model
.media
.ImageFile
;
44 import eu
.etaxonomy
.cdm
.model
.media
.Media
;
45 import eu
.etaxonomy
.cdm
.model
.media
.MediaRepresentation
;
46 import eu
.etaxonomy
.cdm
.model
.media
.MediaRepresentationPart
;
47 import eu
.etaxonomy
.cdm
.model
.media
.MediaUtils
;
48 import eu
.etaxonomy
.cdm
.model
.media
.Rights
;
49 import eu
.etaxonomy
.cdm
.model
.media
.RightsType
;
50 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
51 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
52 import eu
.etaxonomy
.cdm
.remote
.controller
.TaxonPortalController
.EntityMediaContext
;
53 import eu
.etaxonomy
.cdm
.remote
.controller
.util
.IMediaToolbox
;
54 import eu
.etaxonomy
.cdm
.strategy
.cache
.TaggedCacheHelper
;
55 import eu
.etaxonomy
.cdm
.strategy
.cache
.reference
.ReferenceDefaultCacheStrategy
;
58 * Factory class for creating iiif manifests.
60 * This class is not state less therefore it is not a spring bean.
62 * @author a.kohlbecker
65 public class ManifestComposer
{
67 public static final Logger logger
= Logger
.getLogger(ManifestComposer
.class);
69 private IMediaToolbox mediaTools
;
71 private IMediaService mediaService
;
73 private String iiifIdPrefix
;
75 private String
[] thumbnailMimetypes
= new String
[] {"image/.*", ".*"};
78 private boolean doJoinAttributions
= false;
80 private boolean useThumbnailDimensionsForCanvas
= false;
83 public String
getIiifIdPrefix() {
87 public void setIiifIdPrefix(String iiifIdPrefix
) {
88 this.iiifIdPrefix
= iiifIdPrefix
;
92 public String
[] getThumbnailMimetypes() {
93 return thumbnailMimetypes
;
96 public void setThumbnailMimetypes(String
[] thumbnailMimetypes
) {
97 this.thumbnailMimetypes
= thumbnailMimetypes
;
100 public boolean isDoJoinAttributions() {
101 return doJoinAttributions
;
105 * Universal viewer only shows one attribution value in the popup panel
106 * Therefore it makes sense to join all of them.
108 public void setDoJoinAttributions(boolean doJoinAttributions
) {
109 this.doJoinAttributions
= doJoinAttributions
;
112 public boolean isUseThumbnailDimensionsForCanvas() {
113 return useThumbnailDimensionsForCanvas
;
117 * Width and height of the thumbnail image will be used for the canvas size when this is true.
118 * Normally the canvas dimensions conform to the image dimension.
119 * This trick is necessary to achieve a pleasant presentation of the thumbnails in universal viewer,
120 * see {@linkplain https://dev.e-taxonomy.eu/redmine/issues/9132#note-21} and
121 * {@linkplain https://github.com/UniversalViewer/universalviewer/issues/743}
124 public void setUseThumbnailDimensionsForCanvas(boolean useThumbnailDimensionsForCanvas
) {
125 this.useThumbnailDimensionsForCanvas
= useThumbnailDimensionsForCanvas
;
129 public ManifestComposer(String iiifIdPrefix
, IMediaToolbox mediaTools
, IMediaService mediaService
) {
130 this.mediaTools
= mediaTools
;
131 this.iiifIdPrefix
= iiifIdPrefix
;
132 this.mediaService
= mediaService
;
135 <T
extends IdentifiableEntity
> Manifest
manifestFor(EntityMediaContext
<T
> entityMediaContext
, String onEntitiyType
, String onEntityUuid
) throws IOException
{
137 List
<Canvas
> canvases
= new ArrayList
<>(entityMediaContext
.getMedia().size());
139 // Logger.getLogger(MediaUtils.class).setLevel(Level.DEBUG);
140 // logger.setLevel(Level.DEBUG);
143 for(Media media
: entityMediaContext
.getMedia()){
145 MediaRepresentation thumbnailRepresentation
= mediaTools
.processAndFindBestMatchingRepresentation(media
, null, null, 100, 100, thumbnailMimetypes
, MediaUtils
.MissingValueStrategy
.MAX
);
146 MediaRepresentation fullSizeRepresentation
= mediaTools
.processAndFindBestMatchingRepresentation(media
, null, null, Integer
.MAX_VALUE
, Integer
.MAX_VALUE
, null, MediaUtils
.MissingValueStrategy
.MAX
);
147 // MediaRepresentation fullSizeRepresentation = MediaUtils.findBestMatchingRepresentation(media, null, null, Integer.MAX_VALUE, Integer.MAX_VALUE, null, MediaUtils.MissingValueStrategy.MAX);
148 // MediaRepresentation thumbnailRepresentation = MediaUtils.findBestMatchingRepresentation(media, null, null, 100, 100, tumbnailMimetypes, MediaUtils.MissingValueStrategy.MAX);
149 if(logger
.isDebugEnabled()){
150 logger
.debug("fullSizeRepresentation: " + fullSizeRepresentation
.getParts().get(0).getUri());
151 logger
.debug("thumbnailRepresentation: " + thumbnailRepresentation
.getParts().get(0).getUri());
154 // FIXME the below only makes sense if the media is an Image!!!!!
155 List
<ImageContent
> fullSizeImageContents
= representationPartsToImageContent(fullSizeRepresentation
);
157 List
<ImageContent
> thumbnailImageContents
;
158 if(fullSizeRepresentation
.equals(thumbnailRepresentation
)){
159 thumbnailImageContents
= fullSizeImageContents
;
161 thumbnailImageContents
= representationPartsToImageContent(thumbnailRepresentation
);
164 Canvas canvas
= new Canvas(iiifID(onEntitiyType
, onEntityUuid
, Canvas
.class, mediaID
++));
165 for(Language lang
: media
.getAllTitles().keySet()){
166 LanguageString titleLocalized
= media
.getAllTitles().get(lang
);
167 canvas
.addLabel(titleLocalized
.getText());
169 canvas
.setLabel(new PropertyValue(media
.getTitleCache()));
170 canvas
.setThumbnails(thumbnailImageContents
);
171 for(ImageContent image
: fullSizeImageContents
){
172 canvas
.addImage(image
);
174 // TODO if there is only one image canvas.addImage() internally sets the canvas width and height
175 // to the height of the image, for multiple images it is required to follow the specification:
177 // IIIF Presentation API 2.1.1:
178 // It is recommended that if there is (at the time of implementation) a single image that depicts the page,
179 // then the dimensions of the image are used as the dimensions of the canvas for simplicity. If there are
180 // multiple full images, then the dimensions of the largest image should be used. If the largest image’s
181 // dimensions are less than 1200 pixels on either edge, then the canvas’s dimensions should be double those
184 // apply hack for accurate thumbnail container aspect ratios see setUseThumbnailDimensionsForCanvas() for an
186 if(useThumbnailDimensionsForCanvas
&& !thumbnailImageContents
.isEmpty()) {
187 if(thumbnailImageContents
.get(0).getHeight() != null && thumbnailImageContents
.get(0).getHeight() > 0 && thumbnailImageContents
.get(0).getWidth() != null && thumbnailImageContents
.get(0).getWidth() > 0) {
188 canvas
.setHeight(thumbnailImageContents
.get(0).getHeight());
189 canvas
.setWidth(thumbnailImageContents
.get(0).getWidth());
193 List
<MetadataEntry
> mediaMetadata
= mediaMetaData(media
);
194 List
<MetadataEntry
> representationMetadata
;
196 representationMetadata
= mediaService
.readResourceMetadataFiltered(fullSizeRepresentation
)
199 .map(e
-> new MetadataEntry(e
.getKey(), e
.getValue())).collect(Collectors
.toList());
200 mediaMetadata
.addAll(representationMetadata
);
201 } catch (IOException e
) {
202 logger
.error("Error reading media metadata", e
);
203 } catch (HttpException e
) {
204 logger
.error("Error accessing remote media resource", e
);
207 // extractAndAddDesciptions(canvas, mediaMetadata);
208 mediaMetadata
= deduplicateMetadata(mediaMetadata
);
209 canvas
= addAttributionAndLicense(media
, canvas
, mediaMetadata
);
210 orderMedatadaItems(canvas
);
211 canvas
.addMetadata(mediaMetadata
.toArray(new MetadataEntry
[mediaMetadata
.size()]));
212 canvases
.add(canvas
);
215 Sequence sequence
= null;
216 if(canvases
.size() > 0) {
217 sequence
= new Sequence(iiifID(onEntitiyType
, onEntityUuid
, Sequence
.class, "default"));
218 sequence
.setViewingDirection(ViewingDirection
.LEFT_TO_RIGHT
);
219 sequence
.setCanvases(canvases
);
220 sequence
.setStartCanvas(canvases
.get(0).getIdentifier());
223 Manifest manifest
= new Manifest(iiifID(onEntitiyType
, onEntityUuid
, Manifest
.class, null));
224 if(sequence
!= null){
225 // manifest.setLabel(new PropertyValue("Media for " + onEntitiyType + "[" + onEntityUuid + "]")); // TODO better label!!
226 manifest
.addSequence(sequence
);
228 manifest
.setLabel(new PropertyValue("No media found for " + onEntitiyType
+ "[" + onEntityUuid
+ "]")); // TODO better label!!
230 List
<MetadataEntry
> entityMetadata
= entityMetadata(entityMediaContext
.getEntity());
231 manifest
.addMetadata(entityMetadata
.toArray(new MetadataEntry
[entityMetadata
.size()]));
232 copyAttributionAndLicenseToManifest(manifest
);
238 * Due to limitations in universal viewer it seems not to be able
239 * to show attribution and licenses, therefore we copy this data to
240 * also to the metadata
242 * <b>NOTE:</b> This method expects that the canvas attributions and
243 * licenses are not localized!!!!
247 private void copyAttributionAndLicenseToManifest(Manifest manifest
) {
249 PropertyValue attributions
= new PropertyValue();
250 List
<URI
> licenses
= new ArrayList
<>();
251 String firstAttributionString
= null;
252 boolean hasAttributions
= false;
253 boolean hasLicenses
= false;
254 boolean hasDiversAttributions
= false;
255 boolean hasDiversLicenses
= false;
256 String firstLicensesString
= null;
258 if(manifest
.getSequences() == null){
259 // nothing to do, skip!
263 for (Sequence sequence
: manifest
.getSequences()) {
264 for (Canvas canvas
: sequence
.getCanvases()) {
265 if (canvas
.getAttribution() != null) {
266 canvas
.getAttribution().getValues().stream().forEachOrdered(val
-> attributions
.addValue(val
));
267 String thisAttributionString
= canvas
.getAttribution().getValues()
270 .collect(Collectors
.joining());
271 if(firstAttributionString
== null){
272 firstAttributionString
= thisAttributionString
;
273 hasAttributions
= true;
275 hasDiversAttributions
|= !firstAttributionString
.equals(thisAttributionString
);
278 if (canvas
.getLicenses() != null && canvas
.getLicenses().size() > 0) {
279 licenses
.addAll(canvas
.getLicenses());
280 String thisLicensesString
= canvas
.getLicenses()
284 .collect(Collectors
.joining());
285 if(firstLicensesString
== null){
286 firstLicensesString
= thisLicensesString
;
289 hasDiversLicenses
|= !firstLicensesString
.equals(thisLicensesString
);
294 String diversityInfo
= "";
296 if(hasAttributions
|| hasLicenses
){
298 if(hasAttributions
&& hasLicenses
) {
299 dataTypes
= "attributions and licenses";
300 } else if(hasAttributions
){
301 dataTypes
= "attributions";
303 dataTypes
= "licenses";
305 if(hasDiversAttributions
|| hasDiversLicenses
){
306 diversityInfo
= "Individual " + dataTypes
+ " per Item:";
308 diversityInfo
= "Same " + dataTypes
+ " for any Item:";
311 List
<String
> attrs
= new ArrayList
<>(attributions
.getValues());
312 attrs
= attrs
.stream().sorted().distinct().collect(Collectors
.toList());
313 if(doJoinAttributions
){
314 attrs
.add(0, diversityInfo
+ "<br/>" + attrs
.get(0));
316 manifest
.addAttribution(attrs
.stream()
319 .collect(Collectors
.joining("; ")));
321 manifest
.addAttribution(diversityInfo
, attrs
.toArray(
322 new String
[attributions
.getValues().size()]
330 .forEachOrdered(l
-> manifest
.addLicense(l
));
334 private void orderMedatadaItems(Canvas canvas
) {
335 // TODO Auto-generated method stub
336 // order by label name, Title, description, author, license, attribution should come first.
339 private List
<MetadataEntry
> deduplicateMetadata(List
<MetadataEntry
> mediaMetadata
) {
340 Map
<String
, MetadataEntry
> dedupMap
= new HashMap
<>();
341 mediaMetadata
.stream().forEach(mde
-> {
342 String dedupKey
= mde
.getLabelString() + ":" + mde
.getValueString();
343 dedupMap
.put(dedupKey
, mde
);
346 return new ArrayList
<>(dedupMap
.values());
349 private void extractAndAddDesciptions(Resource resource
, List
<MetadataEntry
> mediaMetadata
) {
350 List
<MetadataEntry
> descriptions
= mediaMetadata
.stream()
351 .filter(mde
-> mde
.getLabelString().toLowerCase().matches(".*description.*|.*caption.*"))
352 .collect(Collectors
.toList());
353 mediaMetadata
.removeAll(descriptions
);
354 // FIXME deduplicate mde.getValueString()
355 // descriptions.sream ...
356 descriptions
.stream().forEach(mde
-> resource
.addDescription(mde
.getValueString()));
359 private <T
extends IdentifiableEntity
> List
<MetadataEntry
> entityMetadata(T entity
) {
361 List
<MetadataEntry
> metadata
= new ArrayList
<>();
362 if(entity
instanceof TaxonBase
){
363 List taggedTitle
= ((TaxonBase
)entity
).getTaggedTitle();
364 if(taggedTitle
!= null){
365 //FIXME taggedTitel to HTML!!!!
366 metadata
.add(new MetadataEntry(entity
.getClass().getSimpleName(), TaggedCacheHelper
.createString(taggedTitle
)));
369 String titleCache
= entity
.getTitleCache();
370 if(titleCache
!= null){
371 metadata
.add(new MetadataEntry(entity
.getClass().getSimpleName(), titleCache
));
379 * @deprecated unused as media metadata is now read via the mediaService, see
382 private List
<MetadataEntry
> mediaRepresentationMetaData(MediaRepresentation representation
) {
384 List
<MetadataEntry
> metadata
= new ArrayList
<>();
385 boolean needsPrefix
= representation
.getParts().size() > 1;
388 for (MediaRepresentationPart part
: representation
.getParts()) {
391 prefix
= "Part" + partIndex
+ " ";
393 if (part
.getUri() != null) {
395 CdmImageInfo cdmImageInfo
= CdmImageInfo
.NewInstanceWithMetaData(part
.getUri(), MediaServiceImpl
.IMAGE_READ_TIMEOUT
);
396 Map
<String
, String
> result
= cdmImageInfo
.getMetaData();
398 for (String key
: result
.keySet()) {
399 metadata
.add(new MetadataEntry(key
, result
.get(key
)));
402 } catch (IOException
| HttpException e
) {
403 logger
.error("Problem while loading image metadata", e
);
404 metadata
.add(new MetadataEntry(prefix
+ " Error:", "Problem while loading image metadata <br/><small>(" + e
.getLocalizedMessage() + ")</small>"));
412 private List
<MetadataEntry
> mediaMetaData(Media media
) {
413 List
<MetadataEntry
> metadata
= new ArrayList
<>();
414 List
<Language
> languages
= LocaleContext
.getLanguages();
417 if(media
.getTitle() != null){
418 // TODO get localized titleCache
419 metadata
.add(new MetadataEntry("Title", media
.getTitleCache()));
421 if(media
.getArtist() != null){
422 metadata
.add(new MetadataEntry("Artist", media
.getArtist().getTitleCache()));
424 if(media
.getAllDescriptions().size() > 0){
425 // TODO get localized description
426 PropertyValue descriptionValues
= new PropertyValue();
427 for(LanguageString description
: media
.getAllDescriptions().values()){
428 descriptionValues
.addValue(description
.getText());
430 metadata
.add(new MetadataEntry(new PropertyValue("Description"), descriptionValues
));
432 if(media
.getMediaCreated() != null){
433 metadata
.add(new MetadataEntry("Created on", media
.getMediaCreated().toString())); // TODO is this correct to string conversion?
436 if(!media
.getIdentifiers().isEmpty()) {
437 PropertyValue identifierValues
= new PropertyValue();
438 for(Identifier identifier
: media
.getIdentifiers()){
439 if(identifier
.getIdentifier() != null) {
440 identifierValues
.addValue(identifier
.getIdentifier());
443 metadata
.add(new MetadataEntry(new PropertyValue("Identifiers"), identifierValues
));
446 if(!media
.getSources().isEmpty()) {
447 PropertyValue descriptionValues
= new PropertyValue();
448 for(IdentifiableSource source
: media
.getSources()){
449 descriptionValues
.addValue(sourceAsHtml(source
));
451 metadata
.add(new MetadataEntry(new PropertyValue("Sources"), descriptionValues
));
456 private String
sourceAsHtml(IdentifiableSource source
) {
458 StringBuilder html
= new StringBuilder();
459 Reference citation
= source
.getCitation();
460 if(citation
!= null) {
461 ReferenceDefaultCacheStrategy strategy
= ((ReferenceDefaultCacheStrategy
)citation
.getCacheStrategy());
462 html
.append(strategy
.createShortCitation(citation
, source
.getCitationMicroReference(), false)).append(" ");
463 if(citation
.getDoi() != null) {
465 html
.append(" ").append(htmlLink(new URI("http://doi.org/" + citation
.getDoiString()), citation
.getDoiString()));
466 } catch (URISyntaxException e
) {
467 // IGNORE, should never happen
471 if(source
.getIdNamespace() != null && source
.getIdInSource() != null) {
472 html
.append(source
.getIdNamespace()).append("/").append(source
.getIdInSource()).append(" ");
475 String linkhtml
= null;
476 for(ExternalLink extLink
: source
.getLinks()) {
477 if(extLink
.getUri() != null) {
478 if(linkhtml
!= null) {
481 linkhtml
= htmlLink(extLink
.getUri().getJavaUri(), extLink
.getUri().toString());
482 html
.append(linkhtml
);
485 return html
.toString();
488 private <T
extends Resource
<T
>> T
addAttributionAndLicense(IdentifiableEntity
<?
> entity
, T resource
, List
<MetadataEntry
> metadata
) {
490 List
<Language
> languages
= LocaleContext
.getLanguages();
492 List
<String
> rightsTexts
= new ArrayList
<>();
493 List
<String
> creditTexts
= new ArrayList
<>();
494 List
<URI
> license
= new ArrayList
<>();
496 if(entity
.getRights() != null && entity
.getRights().size() > 0){
497 for(Rights right
: entity
.getRights()){
498 String rightText
= "";
499 // TODO get localized texts below
500 // --- LICENSE or NULL
501 if(right
.getType() == null || right
.getType().equals(RightsType
.LICENSE())){
502 String licenseText
= "";
503 String licenseAbbrev
= "";
504 if(right
.getText() != null){
505 licenseText
= right
.getText();
507 if(right
.getAbbreviatedText() != null){
508 licenseAbbrev
= right
.getAbbreviatedText().trim();
510 if(right
.getUri() != null){
511 if(!licenseAbbrev
.isEmpty()) {
512 licenseAbbrev
= htmlLink(right
.getUri().getJavaUri(), licenseAbbrev
);
513 } else if(!licenseText
.isEmpty()) {
514 licenseText
= htmlLink(right
.getUri().getJavaUri(), licenseText
);
516 licenseText
= htmlLink(right
.getUri().getJavaUri(), right
.getUri().toString());
518 license
.add(right
.getUri().getJavaUri());
520 rightText
= licenseAbbrev
+ (licenseText
.isEmpty() ?
"" : " ") + licenseText
;
523 else if(right
.getType().equals(RightsType
.COPYRIGHT())){
524 // titleCache + agent
525 String copyRightText
= "";
526 if(right
.getText() != null){
527 copyRightText
= right
.getText();
528 // sanitize potential '(c)' away
529 copyRightText
= copyRightText
.replace("(c)", "").trim();
531 if(right
.getAgent() != null){
532 // may only apply to RightsType.accessRights
533 copyRightText
+= " " + right
.getAgent().getTitleCache();
535 if(!copyRightText
.isEmpty()){
536 copyRightText
= "© " + copyRightText
;
538 rightText
= copyRightText
;
540 if(right
.getType().equals(RightsType
.ACCESS_RIGHTS())){
541 // titleCache + agent
542 String accessRights
= right
.getText();
543 if(right
.getAgent() != null){
544 // may only apply to RightsType.accessRights
545 accessRights
= " " + right
.getAgent().getTitleCache();
547 rightText
= accessRights
;
549 if(!rightText
.isEmpty()){
550 rightsTexts
.add(rightText
);
554 if(entity
.getCredits() != null && entity
.getCredits().size() > 0){
555 for(Credit credit
: entity
.getCredits()){
556 String creditText
= "";
557 if(credit
.getText() != null){
558 creditText
+= credit
.getText();
560 if(creditText
.isEmpty() && credit
.getAbbreviatedText() != null){
561 creditText
+= credit
.getAbbreviatedText();
563 if(credit
.getAgent() != null){
564 // may only apply to RightsType.accessRights
565 creditText
+= " " + credit
.getAgent().getTitleCache();
567 creditTexts
.add(creditText
);
571 if(rightsTexts
.size() > 0){
572 String joinedRights
= rightsTexts
.stream().collect(Collectors
.joining(", "));
573 resource
.addAttribution(joinedRights
);
574 if(metadata
!= null){
575 metadata
.add(new MetadataEntry(new PropertyValue("Copyright"), new PropertyValue(joinedRights
)));
578 if(creditTexts
.size() > 0){
579 String joinedCredits
= creditTexts
.stream().collect(Collectors
.joining(", "));
580 resource
.addAttribution(joinedCredits
);
581 if(metadata
!= null){
582 metadata
.add(new MetadataEntry(new PropertyValue("Credit"), new PropertyValue(joinedCredits
)));
585 resource
.setLicenses(license
);
589 private String
htmlLink(URI uri
, String text
) {
590 return String
.format(" <a href=\"%s\">%s</a>", uri
, text
);
593 private List
<ImageContent
> representationPartsToImageContent(MediaRepresentation representation
) {
594 List
<ImageContent
> imageContents
= new ArrayList
<>();
595 for(MediaRepresentationPart part
: representation
.getParts()){
596 if(part
.getUri() != null){
597 ImageContent ic
= new ImageContent(part
.getUri().toString());
598 if(part
instanceof ImageFile
){
599 ImageFile image
= (ImageFile
)part
;
600 if(image
.getWidth() != null && image
.getWidth() > 0){
601 ic
.setWidth(image
.getWidth());
603 if(image
.getHeight() != null && image
.getHeight() > 0){
604 ic
.setHeight(image
.getHeight());
606 if(representation
.getMimeType() != null){
607 ic
.setFormat(MimeType
.fromTypename(representation
.getMimeType()));
609 ic
.setFormat(MimeType
.MIME_IMAGE
);
612 imageContents
.add(ic
);
615 return imageContents
;
618 private String
iiifID(String onEntitiyType
, String onEntityUuid
, Class
<?
extends Resource
> iiifType
, Object index
) {
619 String indexPart
= "";
621 indexPart
= "/" + index
.toString();
623 return this.iiifIdPrefix
+ onEntitiyType
+ "/" + onEntityUuid
+ "/" + iiifType
.getSimpleName().toLowerCase() + indexPart
;