001/* 002 * (C) Copyright 2006-2016 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Nuxeo - initial API and implementation 018 */ 019package org.nuxeo.ecm.platform.picture.api.adapters; 020 021import static org.nuxeo.ecm.platform.picture.api.ImagingConvertConstants.OPERATION_CROP; 022import static org.nuxeo.ecm.platform.picture.api.ImagingConvertConstants.OPTION_CROP_X; 023import static org.nuxeo.ecm.platform.picture.api.ImagingConvertConstants.OPTION_CROP_Y; 024import static org.nuxeo.ecm.platform.picture.api.ImagingConvertConstants.OPTION_RESIZE_HEIGHT; 025import static org.nuxeo.ecm.platform.picture.api.ImagingConvertConstants.OPTION_RESIZE_WIDTH; 026import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_BY_LINE; 027import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_BY_LINE_TITLE; 028import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_CAPTION; 029import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_CATEGORY; 030import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_CITY; 031import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_COLORSPACE; 032import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_COMMENT; 033import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_COPYRIGHT; 034import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_COPYRIGHT_NOTICE; 035import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_COUNTRY_OR_PRIMARY_LOCATION; 036import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_CREDIT; 037import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_DATE_CREATED; 038import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_DESCRIPTION; 039import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_EQUIPMENT; 040import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_EXPOSURE; 041import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_FNUMBER; 042import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_FOCALLENGTH; 043import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_HEADLINE; 044import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_HEIGHT; 045import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_HRESOLUTION; 046import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_ICCPROFILE; 047import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_ISOSPEED; 048import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_KEYWORDS; 049import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_LANGUAGE; 050import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_OBJECT_NAME; 051import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_ORIENTATION; 052import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_ORIGINALDATE; 053import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_ORIGINAL_TRANSMISSION_REFERENCE; 054import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_ORIGINATING_PROGRAM; 055import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_PIXEL_XDIMENSION; 056import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_PIXEL_YDIMENSION; 057import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_PROVINCE_OR_STATE; 058import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_RECORD_VERSION; 059import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_RELEASE_DATE; 060import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_RELEASE_TIME; 061import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_SOURCE; 062import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_SPECIAL_INSTRUCTIONS; 063import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_SUPPLEMENTAL_CATEGORIES; 064import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_TIME_CREATED; 065import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_URGENCY; 066import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_VRESOLUTION; 067import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_WHITEBALANCE; 068import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_WIDTH; 069import static org.nuxeo.ecm.platform.picture.api.MetadataConstants.META_WRITER; 070 071import java.awt.color.ICC_Profile; 072import java.io.IOException; 073import java.io.Serializable; 074import java.util.ArrayList; 075import java.util.Calendar; 076import java.util.Date; 077import java.util.GregorianCalendar; 078import java.util.HashMap; 079import java.util.List; 080import java.util.Map; 081 082import org.nuxeo.ecm.core.api.Blob; 083import org.nuxeo.ecm.core.api.Blobs; 084import org.nuxeo.ecm.core.api.CoreSession; 085import org.nuxeo.ecm.core.api.DocumentModel; 086import org.nuxeo.ecm.core.api.NuxeoException; 087import org.nuxeo.ecm.core.api.blobholder.BlobHolder; 088import org.nuxeo.ecm.core.api.blobholder.SimpleBlobHolder; 089import org.nuxeo.ecm.core.convert.api.ConversionService; 090import org.nuxeo.ecm.platform.picture.api.ImageInfo; 091import org.nuxeo.ecm.platform.picture.api.ImagingService; 092import org.nuxeo.ecm.platform.picture.api.PictureConversion; 093import org.nuxeo.ecm.platform.picture.api.PictureView; 094import org.nuxeo.runtime.api.Framework; 095 096public abstract class AbstractPictureAdapter implements PictureResourceAdapter { 097 098 public static final String VIEWS_PROPERTY = "picture:views"; 099 100 public static final String CONTENT_XPATH = "picture:views/view[%d]/content"; 101 102 public static final String FIELD_HEADLINE = "headline"; 103 104 public static final String FIELD_SUBHEADLINE = "subheadline"; 105 106 public static final String FIELD_BYLINE = "byline"; 107 108 public static final String FIELD_DATELINE = "dateline"; 109 110 public static final String FIELD_SLUGLINE = "slugline"; 111 112 public static final String FIELD_CREDIT = "credit"; 113 114 public static final String FIELD_LANGUAGE = "language"; 115 116 public static final String FIELD_SOURCE = "source"; 117 118 public static final String FIELD_ORIGIN = "origin"; 119 120 public static final String FIELD_GENRE = "genre"; 121 122 public static final String FIELD_CAPTION = "caption"; 123 124 public static final String FIELD_TYPAGE = "typage"; 125 126 public static final String SCHEMA_NAME = "picture"; 127 128 public static final int MEDIUM_SIZE = 1200; 129 130 public static final int SMALL_SIZE = 350; 131 132 public static final int THUMB_SIZE = 150; 133 134 protected DocumentModel doc; 135 136 protected Integer width; 137 138 protected Integer height; 139 140 protected Integer depth; 141 142 protected String description; 143 144 protected String type; 145 146 protected Blob fileContent; 147 148 /** @since 5.9.5 */ 149 protected ImageInfo imageInfo; 150 151 private CoreSession session; 152 153 private ConversionService converionService; 154 155 @Override 156 public void setDocumentModel(DocumentModel doc) { 157 this.doc = doc; 158 } 159 160 protected ImagingService getImagingService() { 161 return Framework.getService(ImagingService.class); 162 } 163 164 protected ConversionService getConversionService() { 165 if (converionService == null) { 166 converionService = Framework.getService(ConversionService.class); 167 } 168 return converionService; 169 } 170 171 protected CoreSession getSession() { 172 if (session == null) { 173 if (doc == null) { 174 return null; 175 } 176 session = doc.getCoreSession(); 177 } 178 return session; 179 } 180 181 protected void setMetadata() throws IOException { 182 boolean imageInfoUsed = false; 183 ImageInfo imageInfo = getImageInfo(); 184 if (imageInfo != null) { 185 width = imageInfo.getWidth(); 186 height = imageInfo.getHeight(); 187 depth = imageInfo.getDepth(); 188 imageInfoUsed = true; 189 } 190 Map<String, Object> metadata = getImagingService().getImageMetadata(fileContent); 191 description = (String) metadata.get(META_DESCRIPTION); 192 if (!imageInfoUsed) { 193 width = (Integer) metadata.get(META_WIDTH); 194 height = (Integer) metadata.get(META_HEIGHT); 195 } 196 doc.setPropertyValue("picture:" + FIELD_BYLINE, (String) metadata.get(META_BY_LINE)); 197 doc.setPropertyValue("picture:" + FIELD_CAPTION, (String) metadata.get(META_CAPTION)); 198 doc.setPropertyValue("picture:" + FIELD_CREDIT, (String) metadata.get(META_CREDIT)); 199 if (metadata.containsKey(META_DATE_CREATED)) { 200 doc.setPropertyValue("picture:" + FIELD_DATELINE, metadata.get(META_DATE_CREATED).toString()); 201 } 202 doc.setPropertyValue("picture:" + FIELD_HEADLINE, (String) metadata.get(META_HEADLINE)); 203 doc.setPropertyValue("picture:" + FIELD_LANGUAGE, (String) metadata.get(META_LANGUAGE)); 204 doc.setPropertyValue("picture:" + FIELD_ORIGIN, (String) metadata.get(META_OBJECT_NAME)); 205 doc.setPropertyValue("picture:" + FIELD_SOURCE, (String) metadata.get(META_SOURCE)); 206 207 // Set EXIF info 208 doc.setPropertyValue("imd:image_description", (String) metadata.get(META_DESCRIPTION)); 209 doc.setPropertyValue("imd:user_comment", (String) metadata.get(META_COMMENT)); 210 doc.setPropertyValue("imd:equipment", (String) metadata.get(META_EQUIPMENT)); 211 Date dateTimeOriginal = (Date) metadata.get(META_ORIGINALDATE); 212 if (dateTimeOriginal != null) { 213 Calendar calendar = new GregorianCalendar(); 214 calendar.setTime(dateTimeOriginal); 215 doc.setPropertyValue("imd:date_time_original", calendar); 216 } 217 doc.setPropertyValue("imd:xresolution", (Integer) metadata.get(META_HRESOLUTION)); 218 doc.setPropertyValue("imd:yresolution", (Integer) metadata.get(META_VRESOLUTION)); 219 doc.setPropertyValue("imd:pixel_xdimension", (Integer) metadata.get(META_PIXEL_XDIMENSION)); 220 doc.setPropertyValue("imd:pixel_ydimension", (Integer) metadata.get(META_PIXEL_YDIMENSION)); 221 doc.setPropertyValue("imd:copyright", (String) metadata.get(META_COPYRIGHT)); 222 doc.setPropertyValue("imd:exposure_time", (String) metadata.get(META_EXPOSURE)); 223 doc.setPropertyValue("imd:iso_speed_ratings", (String) metadata.get(META_ISOSPEED)); 224 doc.setPropertyValue("imd:focal_length", (Double) metadata.get(META_FOCALLENGTH)); 225 doc.setPropertyValue("imd:color_space", (String) metadata.get(META_COLORSPACE)); 226 doc.setPropertyValue("imd:white_balance", (String) metadata.get(META_WHITEBALANCE)); 227 ICC_Profile iccProfile = (ICC_Profile) metadata.get(META_ICCPROFILE); 228 if (iccProfile != null) { 229 doc.setPropertyValue("imd:icc_profile", iccProfile.toString()); 230 } 231 doc.setPropertyValue("imd:orientation", (String) metadata.get(META_ORIENTATION)); 232 doc.setPropertyValue("imd:fnumber", (Double) metadata.get(META_FNUMBER)); 233 234 // Set IPTC info 235 doc.setPropertyValue("iptc:by_line", (String) metadata.get(META_BY_LINE)); 236 doc.setPropertyValue("iptc:by_line_title", (String) metadata.get(META_BY_LINE_TITLE)); 237 doc.setPropertyValue("iptc:caption", (String) metadata.get(META_CAPTION)); 238 doc.setPropertyValue("iptc:category", (String) metadata.get(META_CATEGORY)); 239 doc.setPropertyValue("iptc:city", (String) metadata.get(META_CITY)); 240 doc.setPropertyValue("iptc:copyright_notice", (String) metadata.get(META_COPYRIGHT_NOTICE)); 241 doc.setPropertyValue("iptc:country_or_primary_location", 242 (String) metadata.get(META_COUNTRY_OR_PRIMARY_LOCATION)); 243 doc.setPropertyValue("iptc:credit", (String) metadata.get(META_CREDIT)); 244 Date dateCreated = (Date) metadata.get(META_DATE_CREATED); 245 if (dateCreated != null) { 246 Calendar calendar = new GregorianCalendar(); 247 calendar.setTime(dateCreated); 248 doc.setPropertyValue("iptc:date_created", calendar); 249 } 250 doc.setPropertyValue("iptc:headline", (String) metadata.get(META_HEADLINE)); 251 doc.setPropertyValue("iptc:keywords", (String) metadata.get(META_KEYWORDS)); 252 doc.setPropertyValue("iptc:language", (String) metadata.get(META_LANGUAGE)); 253 doc.setPropertyValue("iptc:object_name", (String) metadata.get(META_OBJECT_NAME)); 254 doc.setPropertyValue("iptc:original_transmission_ref", 255 (String) metadata.get(META_ORIGINAL_TRANSMISSION_REFERENCE)); 256 doc.setPropertyValue("iptc:originating_program", (String) metadata.get(META_ORIGINATING_PROGRAM)); 257 doc.setPropertyValue("iptc:province_or_state", (String) metadata.get(META_PROVINCE_OR_STATE)); 258 doc.setPropertyValue("iptc:record_version", (String) metadata.get(META_RECORD_VERSION)); 259 Date releaseDate = (Date) metadata.get(META_RELEASE_DATE); 260 if (releaseDate != null) { 261 Calendar calendar = new GregorianCalendar(); 262 calendar.setTime(releaseDate); 263 doc.setPropertyValue("iptc:release_date", calendar); 264 } 265 doc.setPropertyValue("iptc:release_time", (String) metadata.get(META_RELEASE_TIME)); 266 doc.setPropertyValue("iptc:source", (String) metadata.get(META_SOURCE)); 267 doc.setPropertyValue("iptc:special_instructions", (String) metadata.get(META_SPECIAL_INSTRUCTIONS)); 268 doc.setPropertyValue("iptc:supplemental_categories", (String) metadata.get(META_SUPPLEMENTAL_CATEGORIES)); 269 doc.setPropertyValue("iptc:time_created", (String) metadata.get(META_TIME_CREATED)); 270 doc.setPropertyValue("iptc:urgency", (String) metadata.get(META_URGENCY)); 271 doc.setPropertyValue("iptc:writer", (String) metadata.get(META_WRITER)); 272 } 273 274 protected void clearViews() { 275 List<Map<String, Object>> viewsList = new ArrayList<>(); 276 doc.getProperty(VIEWS_PROPERTY).setValue(viewsList); 277 } 278 279 protected void addViews(List<Map<String, Object>> pictureConversions, String filename, String title) 280 throws IOException { 281 doc.setProperty("dublincore", "title", title); 282 if (pictureConversions != null) { 283 // Use PictureBook Properties 284 for (Map<String, Object> view : pictureConversions) { 285 Integer maxsize; 286 if (view.get("maxsize") == null) { 287 maxsize = MEDIUM_SIZE; 288 } else { 289 maxsize = ((Long) view.get("maxsize")).intValue(); 290 } 291 createPictureimpl((String) view.get("description"), (String) view.get("tag"), 292 (String) view.get("title"), maxsize, filename, width, height, depth, fileContent); 293 } 294 } else { 295 List<PictureView> pictureViews = getImagingService().computeViewsFor(doc, fileContent, getImageInfo(), 296 true); 297 addPictureViews(pictureViews, true); 298 } 299 } 300 301 public void createPictureimpl(String description, String tag, String title, Integer maxsize, String filename, 302 Integer width, Integer height, Integer depth, Blob fileContent) throws IOException { 303 if (fileContent.getFilename() == null) { 304 fileContent.setFilename(filename); 305 } 306 307 if (maxsize == null) { 308 maxsize = 0; 309 } 310 311 PictureConversion pictureConversion = new PictureConversion(title, description, tag, maxsize); 312 pictureConversion.setChainId("Image.Blob.Resize"); 313 314 PictureView view = getImagingService().computeViewFor(fileContent, pictureConversion, getImageInfo(), true); 315 316 addPictureView(view); 317 } 318 319 /** 320 * Attach new picture views with the document 321 * 322 * @since 7.1 323 */ 324 protected void addPictureViews(List<PictureView> pictureViews, boolean clearPictureViews) { 325 if (clearPictureViews) { 326 clearViews(); 327 } 328 329 List<Map<String, Serializable>> views = getPictureViews(); 330 331 for (PictureView pictureView : pictureViews) { 332 views.add(pictureView.asMap()); 333 } 334 335 doc.setPropertyValue(VIEWS_PROPERTY, (Serializable) views); 336 } 337 338 @Override 339 public boolean fillPictureViews(Blob blob, String filename, String title) throws IOException { 340 return fillPictureViews(blob, filename, title, null); 341 } 342 343 /** 344 * Returns the picture views attached to the document if present, an empty list otherwise. 345 * 346 * @since 7.1 347 */ 348 @SuppressWarnings("unchecked") 349 protected List<Map<String, Serializable>> getPictureViews() { 350 List<Map<String, Serializable>> views = (List<Map<String, Serializable>>) doc.getPropertyValue(VIEWS_PROPERTY); 351 if (views == null) { 352 views = new ArrayList<>(); 353 } 354 return views; 355 } 356 357 /** 358 * Add a pictureView to the existing picture views attached with the document 359 * 360 * @since 7.1 361 */ 362 protected void addPictureView(PictureView view) { 363 List<Map<String, Serializable>> views = getPictureViews(); 364 views.add(view.asMap()); 365 doc.setPropertyValue(VIEWS_PROPERTY, (Serializable) views); 366 } 367 368 /** 369 * Returns the {@link ImageInfo} for the main Blob ({@code fileContent}). 370 * 371 * @since 5.9.5. 372 */ 373 protected ImageInfo getImageInfo() { 374 if (imageInfo == null) { 375 imageInfo = getImagingService().getImageInfo(fileContent); 376 } 377 return imageInfo; 378 } 379 380 protected Blob getContentFromViews(Integer i) { 381 return (Blob) doc.getPropertyValue(String.format(CONTENT_XPATH, i)); 382 } 383 384 protected Blob crop(Blob blob, Map<String, Serializable> coords) { 385 try { 386 BlobHolder bh = new SimpleBlobHolder(blob); 387 String type = blob.getMimeType(); 388 389 Map<String, Serializable> options = new HashMap<>(); 390 options.put(OPTION_CROP_X, coords.get("x")); 391 options.put(OPTION_CROP_Y, coords.get("y")); 392 options.put(OPTION_RESIZE_HEIGHT, coords.get("h")); 393 options.put(OPTION_RESIZE_WIDTH, coords.get("w")); 394 395 if (!"image/png".equals(type)) { 396 bh = getConversionService().convert(OPERATION_CROP, bh, options); 397 return Blobs.createBlob(bh.getBlob().getStream(), type); 398 } 399 } catch (IOException e) { 400 throw new NuxeoException("Crop failed", e); 401 } 402 return null; 403 } 404 405}