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