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 @Override 154 public void setDocumentModel(DocumentModel doc) { 155 this.doc = doc; 156 } 157 158 protected ImagingService getImagingService() { 159 return Framework.getService(ImagingService.class); 160 } 161 162 protected ConversionService getConversionService() { 163 return Framework.getService(ConversionService.class); 164 } 165 166 protected CoreSession getSession() { 167 if (session == null) { 168 if (doc == null) { 169 return null; 170 } 171 session = doc.getCoreSession(); 172 } 173 return session; 174 } 175 176 protected void setMetadata() throws IOException { 177 boolean imageInfoUsed = false; 178 ImageInfo imageInfo = getImageInfo(); 179 if (imageInfo != null) { 180 width = imageInfo.getWidth(); 181 height = imageInfo.getHeight(); 182 depth = imageInfo.getDepth(); 183 imageInfoUsed = true; 184 } 185 Map<String, Object> metadata = getImagingService().getImageMetadata(fileContent); 186 description = (String) metadata.get(META_DESCRIPTION); 187 if (!imageInfoUsed) { 188 width = (Integer) metadata.get(META_WIDTH); 189 height = (Integer) metadata.get(META_HEIGHT); 190 } 191 doc.setPropertyValue("picture:" + FIELD_BYLINE, (String) metadata.get(META_BY_LINE)); 192 doc.setPropertyValue("picture:" + FIELD_CAPTION, (String) metadata.get(META_CAPTION)); 193 doc.setPropertyValue("picture:" + FIELD_CREDIT, (String) metadata.get(META_CREDIT)); 194 if (metadata.containsKey(META_DATE_CREATED)) { 195 doc.setPropertyValue("picture:" + FIELD_DATELINE, metadata.get(META_DATE_CREATED).toString()); 196 } 197 doc.setPropertyValue("picture:" + FIELD_HEADLINE, (String) metadata.get(META_HEADLINE)); 198 doc.setPropertyValue("picture:" + FIELD_LANGUAGE, (String) metadata.get(META_LANGUAGE)); 199 doc.setPropertyValue("picture:" + FIELD_ORIGIN, (String) metadata.get(META_OBJECT_NAME)); 200 doc.setPropertyValue("picture:" + FIELD_SOURCE, (String) metadata.get(META_SOURCE)); 201 202 // Set EXIF info 203 doc.setPropertyValue("imd:image_description", (String) metadata.get(META_DESCRIPTION)); 204 doc.setPropertyValue("imd:user_comment", (String) metadata.get(META_COMMENT)); 205 doc.setPropertyValue("imd:equipment", (String) metadata.get(META_EQUIPMENT)); 206 Date dateTimeOriginal = (Date) metadata.get(META_ORIGINALDATE); 207 if (dateTimeOriginal != null) { 208 Calendar calendar = new GregorianCalendar(); 209 calendar.setTime(dateTimeOriginal); 210 doc.setPropertyValue("imd:date_time_original", calendar); 211 } 212 doc.setPropertyValue("imd:xresolution", (Integer) metadata.get(META_HRESOLUTION)); 213 doc.setPropertyValue("imd:yresolution", (Integer) metadata.get(META_VRESOLUTION)); 214 doc.setPropertyValue("imd:pixel_xdimension", (Integer) metadata.get(META_PIXEL_XDIMENSION)); 215 doc.setPropertyValue("imd:pixel_ydimension", (Integer) metadata.get(META_PIXEL_YDIMENSION)); 216 doc.setPropertyValue("imd:copyright", (String) metadata.get(META_COPYRIGHT)); 217 doc.setPropertyValue("imd:exposure_time", (String) metadata.get(META_EXPOSURE)); 218 doc.setPropertyValue("imd:iso_speed_ratings", (String) metadata.get(META_ISOSPEED)); 219 doc.setPropertyValue("imd:focal_length", (Double) metadata.get(META_FOCALLENGTH)); 220 doc.setPropertyValue("imd:color_space", (String) metadata.get(META_COLORSPACE)); 221 doc.setPropertyValue("imd:white_balance", (String) metadata.get(META_WHITEBALANCE)); 222 ICC_Profile iccProfile = (ICC_Profile) metadata.get(META_ICCPROFILE); 223 if (iccProfile != null) { 224 doc.setPropertyValue("imd:icc_profile", iccProfile.toString()); 225 } 226 doc.setPropertyValue("imd:orientation", (String) metadata.get(META_ORIENTATION)); 227 doc.setPropertyValue("imd:fnumber", (Double) metadata.get(META_FNUMBER)); 228 229 // Set IPTC info 230 doc.setPropertyValue("iptc:by_line", (String) metadata.get(META_BY_LINE)); 231 doc.setPropertyValue("iptc:by_line_title", (String) metadata.get(META_BY_LINE_TITLE)); 232 doc.setPropertyValue("iptc:caption", (String) metadata.get(META_CAPTION)); 233 doc.setPropertyValue("iptc:category", (String) metadata.get(META_CATEGORY)); 234 doc.setPropertyValue("iptc:city", (String) metadata.get(META_CITY)); 235 doc.setPropertyValue("iptc:copyright_notice", (String) metadata.get(META_COPYRIGHT_NOTICE)); 236 doc.setPropertyValue("iptc:country_or_primary_location", 237 (String) metadata.get(META_COUNTRY_OR_PRIMARY_LOCATION)); 238 doc.setPropertyValue("iptc:credit", (String) metadata.get(META_CREDIT)); 239 Date dateCreated = (Date) metadata.get(META_DATE_CREATED); 240 if (dateCreated != null) { 241 Calendar calendar = new GregorianCalendar(); 242 calendar.setTime(dateCreated); 243 doc.setPropertyValue("iptc:date_created", calendar); 244 } 245 doc.setPropertyValue("iptc:headline", (String) metadata.get(META_HEADLINE)); 246 doc.setPropertyValue("iptc:keywords", (String) metadata.get(META_KEYWORDS)); 247 doc.setPropertyValue("iptc:language", (String) metadata.get(META_LANGUAGE)); 248 doc.setPropertyValue("iptc:object_name", (String) metadata.get(META_OBJECT_NAME)); 249 doc.setPropertyValue("iptc:original_transmission_ref", 250 (String) metadata.get(META_ORIGINAL_TRANSMISSION_REFERENCE)); 251 doc.setPropertyValue("iptc:originating_program", (String) metadata.get(META_ORIGINATING_PROGRAM)); 252 doc.setPropertyValue("iptc:province_or_state", (String) metadata.get(META_PROVINCE_OR_STATE)); 253 doc.setPropertyValue("iptc:record_version", (String) metadata.get(META_RECORD_VERSION)); 254 Date releaseDate = (Date) metadata.get(META_RELEASE_DATE); 255 if (releaseDate != null) { 256 Calendar calendar = new GregorianCalendar(); 257 calendar.setTime(releaseDate); 258 doc.setPropertyValue("iptc:release_date", calendar); 259 } 260 doc.setPropertyValue("iptc:release_time", (String) metadata.get(META_RELEASE_TIME)); 261 doc.setPropertyValue("iptc:source", (String) metadata.get(META_SOURCE)); 262 doc.setPropertyValue("iptc:special_instructions", (String) metadata.get(META_SPECIAL_INSTRUCTIONS)); 263 doc.setPropertyValue("iptc:supplemental_categories", (String) metadata.get(META_SUPPLEMENTAL_CATEGORIES)); 264 doc.setPropertyValue("iptc:time_created", (String) metadata.get(META_TIME_CREATED)); 265 doc.setPropertyValue("iptc:urgency", (String) metadata.get(META_URGENCY)); 266 doc.setPropertyValue("iptc:writer", (String) metadata.get(META_WRITER)); 267 } 268 269 protected void clearViews() { 270 List<Map<String, Object>> viewsList = new ArrayList<>(); 271 doc.getProperty(VIEWS_PROPERTY).setValue(viewsList); 272 } 273 274 protected void addViews(List<Map<String, Object>> pictureConversions, String filename, String title) 275 throws IOException { 276 doc.setProperty("dublincore", "title", title); 277 if (pictureConversions != null) { 278 // Use PictureBook Properties 279 for (Map<String, Object> view : pictureConversions) { 280 Integer maxsize; 281 if (view.get("maxsize") == null) { 282 maxsize = MEDIUM_SIZE; 283 } else { 284 maxsize = ((Long) view.get("maxsize")).intValue(); 285 } 286 createPictureimpl((String) view.get("description"), (String) view.get("tag"), 287 (String) view.get("title"), maxsize, filename, width, height, depth, fileContent); 288 } 289 } else { 290 List<PictureView> pictureViews = getImagingService().computeViewsFor(doc, fileContent, getImageInfo(), 291 true); 292 addPictureViews(pictureViews, true); 293 } 294 } 295 296 public void createPictureimpl(String description, String tag, String title, Integer maxsize, String filename, 297 Integer width, Integer height, Integer depth, Blob fileContent) throws IOException { 298 if (fileContent.getFilename() == null) { 299 fileContent.setFilename(filename); 300 } 301 302 if (maxsize == null) { 303 maxsize = 0; 304 } 305 306 PictureConversion pictureConversion = new PictureConversion(title, description, tag, maxsize); 307 pictureConversion.setChainId("Image.Blob.Resize"); 308 309 PictureView view = getImagingService().computeViewFor(fileContent, pictureConversion, getImageInfo(), true); 310 311 addPictureView(view); 312 } 313 314 /** 315 * Attach new picture views with the document 316 * 317 * @since 7.1 318 */ 319 protected void addPictureViews(List<PictureView> pictureViews, boolean clearPictureViews) { 320 if (clearPictureViews) { 321 clearViews(); 322 } 323 324 List<Map<String, Serializable>> views = getPictureViews(); 325 326 for (PictureView pictureView : pictureViews) { 327 views.add(pictureView.asMap()); 328 } 329 330 doc.setPropertyValue(VIEWS_PROPERTY, (Serializable) views); 331 } 332 333 @Override 334 public boolean fillPictureViews(Blob blob, String filename, String title) throws IOException { 335 return fillPictureViews(blob, filename, title, null); 336 } 337 338 /** 339 * Returns the picture views attached to the document if present, an empty list otherwise. 340 * 341 * @since 7.1 342 */ 343 @SuppressWarnings("unchecked") 344 protected List<Map<String, Serializable>> getPictureViews() { 345 List<Map<String, Serializable>> views = (List<Map<String, Serializable>>) doc.getPropertyValue(VIEWS_PROPERTY); 346 if (views == null) { 347 views = new ArrayList<>(); 348 } 349 return views; 350 } 351 352 /** 353 * Add a pictureView to the existing picture views attached with the document 354 * 355 * @since 7.1 356 */ 357 protected void addPictureView(PictureView view) { 358 List<Map<String, Serializable>> views = getPictureViews(); 359 views.add(view.asMap()); 360 doc.setPropertyValue(VIEWS_PROPERTY, (Serializable) views); 361 } 362 363 /** 364 * Returns the {@link ImageInfo} for the main Blob ({@code fileContent}). 365 * 366 * @since 5.9.5. 367 */ 368 protected ImageInfo getImageInfo() { 369 if (imageInfo == null) { 370 imageInfo = getImagingService().getImageInfo(fileContent); 371 } 372 return imageInfo; 373 } 374 375 protected Blob getContentFromViews(Integer i) { 376 return (Blob) doc.getPropertyValue(String.format(CONTENT_XPATH, i)); 377 } 378 379 protected Blob crop(Blob blob, Map<String, Serializable> coords) { 380 try { 381 BlobHolder bh = new SimpleBlobHolder(blob); 382 String type = blob.getMimeType(); 383 384 Map<String, Serializable> options = new HashMap<>(); 385 options.put(OPTION_CROP_X, coords.get("x")); 386 options.put(OPTION_CROP_Y, coords.get("y")); 387 options.put(OPTION_RESIZE_HEIGHT, coords.get("h")); 388 options.put(OPTION_RESIZE_WIDTH, coords.get("w")); 389 390 if (!"image/png".equals(type)) { 391 bh = getConversionService().convert(OPERATION_CROP, bh, options); 392 return Blobs.createBlob(bh.getBlob().getStream(), type); 393 } 394 } catch (IOException e) { 395 throw new NuxeoException("Crop failed", e); 396 } 397 return null; 398 } 399 400}