001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.pdftools; 029 030import org.opencms.file.CmsFile; 031import org.opencms.file.CmsObject; 032import org.opencms.loader.CmsImageScaler; 033import org.opencms.main.CmsException; 034import org.opencms.main.CmsLog; 035import org.opencms.main.OpenCms; 036 037import java.io.ByteArrayInputStream; 038import java.net.URI; 039import java.util.regex.Matcher; 040import java.util.regex.Pattern; 041 042import org.apache.commons.logging.Log; 043 044import org.xhtmlrenderer.extend.FSImage; 045import org.xhtmlrenderer.layout.SharedContext; 046import org.xhtmlrenderer.pdf.ITextFSImage; 047import org.xhtmlrenderer.resource.CSSResource; 048import org.xhtmlrenderer.resource.ImageResource; 049import org.xhtmlrenderer.swing.NaiveUserAgent; 050 051import com.lowagie.text.Image; 052 053/** 054 * This class is responsible for loading external resources while generating PDF from XHTML. 055 * 056 * Resources will be loaded from the VFS. Additionally, if there are image scaler parameters in an image 057 * URI, the scaled image data will be returned. Please note that this class just reads the data from the linked 058 * resources; it will not go through OpenCms's resource loaders, so you can't e.g. use a JSP as a dynamic stylesheet. 059 */ 060public class CmsPdfUserAgent extends NaiveUserAgent { 061 062 /** The regex to match image scaler parameters. */ 063 public static final Pattern SCALE_PARAMS_PATTERN = Pattern.compile("__scale=(.*?)(?:&|$)"); 064 065 /** The image cache capacity. */ 066 private static final int IMAGE_CACHE_CAPACITY = 64; 067 068 /** The logger instance for this class. */ 069 private static final Log LOG = CmsLog.getLog(CmsPdfUserAgent.class); 070 071 /** The CMS context to use for loading the resources. */ 072 private CmsObject m_cms; 073 074 /** The CMS context to use, with the site root set to the root site. */ 075 private CmsObject m_rootCms; 076 077 /** The shared context. */ 078 private SharedContext m_sharedContext; 079 080 /** 081 * Creates a new instance.<p> 082 * 083 * @param cms the CMS context 084 * 085 * @throws CmsException if something goes wrong 086 */ 087 public CmsPdfUserAgent(CmsObject cms) 088 throws CmsException { 089 090 super(IMAGE_CACHE_CAPACITY); 091 092 m_cms = cms; 093 m_rootCms = OpenCms.initCmsObject(cms); 094 m_rootCms.getRequestContext().setSiteRoot(""); 095 } 096 097 /** 098 * @see org.xhtmlrenderer.swing.NaiveUserAgent#getBinaryResource(java.lang.String) 099 */ 100 @Override 101 public byte[] getBinaryResource(String uri) { 102 103 return readFile(uri); 104 } 105 106 /** 107 * @see org.xhtmlrenderer.swing.NaiveUserAgent#getCSSResource(java.lang.String) 108 */ 109 @Override 110 public CSSResource getCSSResource(String uri) { 111 112 return new CSSResource(getStream(readFile(uri))); 113 } 114 115 /** 116 * @see org.xhtmlrenderer.swing.NaiveUserAgent#getImageResource(java.lang.String) 117 */ 118 @SuppressWarnings("unchecked") 119 @Override 120 public ImageResource getImageResource(String uri) { 121 122 ImageResource resource = null; 123 resource = (ImageResource)_imageCache.get(uri); 124 if (resource == null) { 125 byte[] imageData = readImage(uri); 126 if (imageData != null) { 127 try { 128 Image image = Image.getInstance(imageData); 129 scaleToOutputResolution(image); 130 resource = new ImageResource(uri, new ITextFSImage(image)); 131 _imageCache.put(uri, resource); 132 } catch (Exception e) { 133 LOG.error("Problem with getting image resource " + uri, e); 134 } 135 } 136 } 137 138 if (resource != null) { 139 resource = new ImageResource(resource.getImageUri(), (FSImage)((ITextFSImage)resource.getImage()).clone()); 140 } else { 141 resource = new ImageResource(uri, null); 142 } 143 return resource; 144 } 145 146 /** 147 * Gets the shared context.<p> 148 * 149 * @return the shared context 150 */ 151 public SharedContext getSharedContext() { 152 153 return m_sharedContext; 154 } 155 156 /** 157 * @see org.xhtmlrenderer.swing.NaiveUserAgent#resolveURI(java.lang.String) 158 */ 159 @Override 160 public String resolveURI(String uri) { 161 162 // we want to pass the uri unchanged to the get... methods 163 return uri; 164 } 165 166 /** 167 * Sets the shared context.<p> 168 * 169 * @param sharedContext the shared context 170 */ 171 public void setSharedContext(SharedContext sharedContext) { 172 173 m_sharedContext = sharedContext; 174 } 175 176 /** 177 * Converts a byte array to an input stream, but returns null if the byte array is null.<p> 178 * 179 * @param data the data 180 * @return the input stream for the data, or null 181 */ 182 ByteArrayInputStream getStream(byte[] data) { 183 184 if (data == null) { 185 return null; 186 } else { 187 return new ByteArrayInputStream(data); 188 } 189 } 190 191 /** 192 * Reads a file from the VFS.<p> 193 * 194 * @param uriWithParams the 195 * @return the file data 196 */ 197 private byte[] readFile(String uriWithParams) { 198 199 try { 200 String pathAndQuery = OpenCms.getLinkManager().getRootPath(m_cms, uriWithParams); 201 URI uri = new URI(pathAndQuery); 202 String path = uri.getPath(); 203 CmsFile file = m_rootCms.readFile(path); 204 return file.getContents(); 205 } catch (Exception e) { 206 LOG.error("Problem with reading CSS " + uriWithParams + ": " + e.getLocalizedMessage(), e); 207 return null; 208 } 209 } 210 211 /** 212 * Reads an image from the VFS, scaling it if necessary.<p> 213 * 214 * @param uriWithParams the image uri, possible with scaling parameter 215 * 216 * @return the image data 217 */ 218 private byte[] readImage(String uriWithParams) { 219 220 try { 221 String pathAndQuery = OpenCms.getLinkManager().getRootPath(m_cms, uriWithParams); 222 URI uri = new URI(pathAndQuery); 223 String path = uri.getPath(); 224 String query = uri.getQuery(); 225 String scaleParams = null; 226 if (query != null) { 227 Matcher matcher = SCALE_PARAMS_PATTERN.matcher(query); 228 if (matcher.find()) { 229 scaleParams = matcher.group(1); 230 } 231 } 232 CmsFile imageFile = m_rootCms.readFile(path); 233 byte[] result = imageFile.getContents(); 234 if (scaleParams != null) { 235 CmsImageScaler scaler = new CmsImageScaler(scaleParams); 236 result = scaler.scaleImage(imageFile); 237 } 238 return result; 239 } catch (Exception e) { 240 LOG.error("Problem with reading image " + uriWithParams + ": " + e.getLocalizedMessage(), e); 241 return null; 242 } 243 } 244 245 /** 246 * Scales the image to output resolution.<p> 247 * 248 * @param image the image to scale 249 */ 250 private void scaleToOutputResolution(Image image) { 251 252 float factor = m_sharedContext.getDotsPerPixel(); 253 if (factor != 1.0f) { 254 image.scaleAbsolute(image.getPlainWidth() * factor, image.getPlainHeight() * factor); 255 } 256 } 257 258}