1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.mycore.iview2.services;
20
21 import java.awt.Graphics;
22 import java.awt.image.BufferedImage;
23 import java.io.IOException;
24 import java.io.UncheckedIOException;
25 import java.net.URI;
26 import java.nio.channels.SeekableByteChannel;
27 import java.nio.file.FileStore;
28 import java.nio.file.FileSystem;
29 import java.nio.file.FileSystemAlreadyExistsException;
30 import java.nio.file.FileSystemNotFoundException;
31 import java.nio.file.FileSystems;
32 import java.nio.file.Files;
33 import java.nio.file.NoSuchFileException;
34 import java.nio.file.Path;
35 import java.nio.file.Paths;
36 import java.text.MessageFormat;
37 import java.util.Collections;
38 import java.util.Locale;
39 import java.util.Optional;
40 import java.util.stream.Collectors;
41
42 import javax.imageio.ImageIO;
43 import javax.imageio.ImageReader;
44 import javax.imageio.stream.ImageInputStream;
45
46 import org.apache.logging.log4j.LogManager;
47 import org.apache.logging.log4j.Logger;
48 import org.jdom2.JDOMException;
49 import org.mycore.backend.jpa.MCREntityManagerProvider;
50 import org.mycore.common.MCRClassTools;
51 import org.mycore.common.config.MCRConfiguration2;
52 import org.mycore.datamodel.metadata.MCRDerivate;
53 import org.mycore.datamodel.metadata.MCRMetadataManager;
54 import org.mycore.datamodel.metadata.MCRObjectID;
55 import org.mycore.datamodel.niofs.MCRAbstractFileStore;
56 import org.mycore.datamodel.niofs.MCRContentTypes;
57 import org.mycore.datamodel.niofs.MCRPath;
58 import org.mycore.imagetiler.MCRImage;
59 import org.mycore.imagetiler.MCRTiledPictureProps;
60
61 import jakarta.activation.FileTypeMap;
62 import jakarta.persistence.EntityManager;
63 import jakarta.persistence.TypedQuery;
64
65
66
67
68
69
70 public class MCRIView2Tools {
71
72 public static final String CONFIG_PREFIX = "MCR.Module-iview2.";
73
74 private static String SUPPORTED_CONTENT_TYPE = MCRConfiguration2.getString(CONFIG_PREFIX + "SupportedContentTypes")
75 .orElse("");
76
77 private static Path TILE_DIR = Paths.get(MCRIView2Tools.getIView2Property("DirectoryForTiles"));
78
79 private static Logger LOGGER = LogManager.getLogger(MCRIView2Tools.class);
80
81
82
83
84 public static Path getTileDir() {
85 return TILE_DIR;
86 }
87
88
89
90
91
92
93 public static String getSupportedMainFile(String derivateID) {
94 try {
95 MCRDerivate deriv = MCRMetadataManager.retrieveMCRDerivate(MCRObjectID.getInstance(derivateID));
96 String nameOfMainFile = deriv.getDerivate().getInternals().getMainDoc();
97
98 if (nameOfMainFile != null && !nameOfMainFile.equals("")) {
99 MCRPath mainFile = MCRPath.getPath(derivateID, '/' + nameOfMainFile);
100 if (isFileSupported(mainFile)) {
101 return mainFile.getRoot().relativize(mainFile).toString();
102 }
103 }
104 } catch (Exception e) {
105 LOGGER.warn("Could not get main file of derivate.", e);
106 }
107 return "";
108 }
109
110
111
112
113
114
115 public static boolean isDerivateSupported(String derivateID) {
116 if (derivateID == null || derivateID.trim().length() == 0) {
117 return false;
118 }
119
120 return getSupportedMainFile(derivateID).length() > 0;
121 }
122
123
124
125
126
127
128
129 public static boolean isFileSupported(Path file) throws IOException {
130 try {
131 return Optional.ofNullable(file)
132 .map(path -> {
133 try {
134 return MCRContentTypes.probeContentType(path);
135 } catch (IOException e) {
136 throw new UncheckedIOException(e);
137 }
138 })
139 .or(() -> Optional.of("application/octet-stream"))
140 .map(SUPPORTED_CONTENT_TYPE::contains)
141 .orElse(Boolean.FALSE);
142 } catch (UncheckedIOException e) {
143 throw e.getCause();
144 }
145 }
146
147
148
149
150 public static boolean isFileSupported(String filename) {
151 return SUPPORTED_CONTENT_TYPE.contains(FileTypeMap.getDefaultFileTypeMap().getContentType(
152 filename.toLowerCase(Locale.ROOT)));
153 }
154
155
156
157
158
159
160 public static boolean isCompletelyTiled(String derivateId) {
161 if (!MCRMetadataManager.exists(MCRObjectID.getInstance(derivateId))) {
162 return false;
163 }
164 EntityManager em = MCREntityManagerProvider.getCurrentEntityManager();
165
166 TypedQuery<Number> namedQuery = em
167 .createNamedQuery("MCRTileJob.countByStateListByDerivate", Number.class)
168 .setParameter("derivateId", derivateId)
169 .setParameter("states", MCRJobState.notCompleteStates().stream().map(MCRJobState::toChar).collect(
170 Collectors.toSet()));
171
172 return namedQuery.getSingleResult().intValue() == 0;
173 }
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188 public static BufferedImage getZoomLevel(Path iviewFile, int zoomLevel) throws IOException, JDOMException {
189 ImageReader reader = getTileImageReader();
190 try (FileSystem zipFileSystem = getFileSystem(iviewFile)) {
191 Path iviewFileRoot = zipFileSystem.getRootDirectories().iterator().next();
192 MCRTiledPictureProps imageProps = MCRTiledPictureProps.getInstanceFromDirectory(iviewFileRoot);
193 if (zoomLevel < 0 || zoomLevel > imageProps.getZoomlevel()) {
194 throw new IndexOutOfBoundsException(
195 "Zoom level " + zoomLevel + " is not in range 0 - " + imageProps.getZoomlevel());
196 }
197 return getZoomLevel(iviewFileRoot, imageProps, reader, zoomLevel);
198 } finally {
199 reader.dispose();
200 }
201 }
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218 public static BufferedImage getZoomLevel(final Path iviewFileRoot, final MCRTiledPictureProps imageProperties,
219 final ImageReader reader, final int zoomLevel) throws IOException, JDOMException {
220 if (zoomLevel == 0) {
221 return readTile(iviewFileRoot, reader, 0, 0, 0);
222 }
223 MCRTiledPictureProps imageProps = imageProperties == null
224 ? MCRTiledPictureProps.getInstanceFromDirectory(iviewFileRoot)
225 : imageProperties;
226 double zoomFactor = Math.pow(2, (imageProps.getZoomlevel() - zoomLevel));
227 int maxX = (int) Math.ceil((imageProps.getWidth() / zoomFactor) / MCRImage.getTileSize());
228 int maxY = (int) Math.ceil((imageProps.getHeight() / zoomFactor) / MCRImage.getTileSize());
229 LOGGER.debug("Image size:{}x{}, tiles:{}x{}", imageProps.getWidth(), imageProps.getHeight(), maxX, maxY);
230 int imageType = getImageType(iviewFileRoot, reader, zoomLevel, 0, 0);
231 int xDim = ((maxX - 1) * MCRImage.getTileSize()
232 + readTile(iviewFileRoot, reader, zoomLevel, maxX - 1, 0).getWidth());
233 int yDim = ((maxY - 1) * MCRImage.getTileSize()
234 + readTile(iviewFileRoot, reader, zoomLevel, 0, maxY - 1).getHeight());
235 BufferedImage resultImage = new BufferedImage(xDim, yDim, imageType);
236 Graphics graphics = resultImage.getGraphics();
237 try {
238 for (int x = 0; x < maxX; x++) {
239 for (int y = 0; y < maxY; y++) {
240 BufferedImage tile = readTile(iviewFileRoot, reader, zoomLevel, x, y);
241 graphics.drawImage(tile, x * MCRImage.getTileSize(), y * MCRImage.getTileSize(), null);
242 }
243 }
244 return resultImage;
245 } finally {
246 graphics.dispose();
247 }
248 }
249
250 public static FileSystem getFileSystem(Path iviewFile) throws IOException {
251 URI uri = URI.create("jar:" + iviewFile.toUri());
252 try {
253 return FileSystems.newFileSystem(uri, Collections.emptyMap(), MCRClassTools.getClassLoader());
254 } catch (FileSystemAlreadyExistsException exc) {
255
256 try {
257 FileSystem fileSystem = FileSystems.getFileSystem(uri);
258 while (fileSystem.isOpen()) {
259 try {
260 Thread.sleep(10);
261 } catch (InterruptedException ie) {
262
263 throw new IOException(ie);
264 }
265 }
266 } catch (FileSystemNotFoundException fsnfe) {
267
268 LOGGER.debug("Filesystem not found", fsnfe);
269 }
270 return getFileSystem(iviewFile);
271 }
272 }
273
274 public static ImageReader getTileImageReader() {
275 return ImageIO.getImageReadersByMIMEType("image/jpeg").next();
276 }
277
278 public static BufferedImage readTile(Path iviewFileRoot, ImageReader imageReader, int zoomLevel, int x, int y)
279 throws IOException {
280 String tileName = new MessageFormat("{0}/{1}/{2}.jpg", Locale.ROOT).format(new Object[] { zoomLevel, y, x });
281 Path tile = iviewFileRoot.resolve(tileName);
282 if (Files.exists(tile)) {
283 try (SeekableByteChannel fileChannel = Files.newByteChannel(tile)) {
284 ImageInputStream iis = ImageIO.createImageInputStream(fileChannel);
285 if (iis == null) {
286 throw new IOException("Could not acquire ImageInputStream from SeekableByteChannel: " + tile);
287 }
288 imageReader.setInput(iis, true);
289 BufferedImage image = imageReader.read(0);
290 imageReader.reset();
291 iis.close();
292 return image;
293 }
294 } else {
295 throw new NoSuchFileException(iviewFileRoot.toString(), tileName, null);
296 }
297 }
298
299 public static int getImageType(Path iviewFileRoot, ImageReader imageReader, int zoomLevel, int x, int y)
300 throws IOException {
301 String tileName = new MessageFormat("{0}/{1}/{2}.jpg", Locale.ROOT).format(new Object[] { zoomLevel, y, x });
302 Path tile = iviewFileRoot.resolve(tileName);
303 if (Files.exists(tile)) {
304 try (SeekableByteChannel fileChannel = Files.newByteChannel(tile)) {
305 ImageInputStream iis = ImageIO.createImageInputStream(fileChannel);
306 if (iis == null) {
307 throw new IOException("Could not acquire ImageInputStream from SeekableByteChannel: " + tile);
308 }
309 imageReader.setInput(iis, true);
310 int imageType = MCRImage.getImageType(imageReader);
311 imageReader.reset();
312 iis.close();
313 return imageType;
314 }
315 } else {
316 throw new NoSuchFileException(iviewFileRoot.toString(), tileName, null);
317 }
318 }
319
320
321
322
323 public static String getIView2Property(String propName) {
324 return getIView2Property(propName, null);
325 }
326
327
328
329
330
331
332
333
334 public static String getIView2Property(String propName, String defaultProp) {
335 return MCRConfiguration2.getString(CONFIG_PREFIX + propName).orElse(defaultProp);
336 }
337
338 public static String getFilePath(String derID, String derPath) throws IOException {
339 MCRPath mcrPath = MCRPath.getPath(derID, derPath);
340 Path physicalPath = mcrPath.toPhysicalPath();
341 for (FileStore fs : mcrPath.getFileSystem().getFileStores()) {
342 if (fs instanceof MCRAbstractFileStore) {
343 Path basePath = ((MCRAbstractFileStore) fs).getBaseDirectory();
344 if (physicalPath.startsWith(basePath)) {
345 return basePath.relativize(physicalPath).toString();
346 }
347 }
348 }
349 return physicalPath.toString();
350 }
351 }