View Javadoc
1   /*
2    * Copyright (C) 2003-2008 eXo Platform SAS.
3    *
4    * This program is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Affero General Public License
6    * as published by the Free Software Foundation; either version 3
7    * of the License, or (at your option) any later version.
8    *
9    * This program is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU General Public License for more details.
13   *
14   * You should have received a copy of the GNU General Public License
15   * along with this program; if not, see<http://www.gnu.org/licenses/>.
16   */
17  package org.exoplatform.ecm.webui.viewer;
18  
19  import org.exoplatform.download.DownloadService;
20  import org.exoplatform.download.InputStreamDownloadResource;
21  import org.exoplatform.portal.webui.util.Util;
22  import org.exoplatform.portal.webui.workspace.UIPortalApplication;
23  import org.exoplatform.services.cms.mimetype.DMSMimeTypeResolver;
24  import org.exoplatform.services.log.ExoLogger;
25  import org.exoplatform.services.log.Log;
26  import org.exoplatform.services.pdfviewer.PDFViewerService;
27  import org.exoplatform.services.resources.ResourceBundleService;
28  import org.exoplatform.services.wcm.utils.WCMCoreUtils;
29  import org.exoplatform.web.application.RequireJS;
30  import org.exoplatform.webui.application.portlet.PortletRequestContext;
31  import org.exoplatform.webui.config.annotation.ComponentConfig;
32  import org.exoplatform.webui.config.annotation.EventConfig;
33  import org.exoplatform.webui.core.UIComponent;
34  import org.exoplatform.webui.core.lifecycle.UIFormLifecycle;
35  import org.exoplatform.webui.core.model.SelectItemOption;
36  import org.exoplatform.webui.event.Event;
37  import org.exoplatform.webui.event.Event.Phase;
38  import org.exoplatform.webui.event.EventListener;
39  import org.exoplatform.webui.form.UIForm;
40  import org.exoplatform.webui.form.UIFormSelectBox;
41  import org.exoplatform.webui.form.UIFormStringInput;
42  import org.icepdf.core.pobjects.Document;
43  import org.icepdf.core.pobjects.PInfo;
44  
45  import javax.jcr.Node;
46  import javax.jcr.NodeIterator;
47  import javax.jcr.RepositoryException;
48  import javax.portlet.PortletRequest;
49  import java.io.BufferedInputStream;
50  import java.io.File;
51  import java.io.FileInputStream;
52  import java.lang.reflect.InvocationTargetException;
53  import java.lang.reflect.Method;
54  import java.util.ArrayList;
55  import java.util.HashMap;
56  import java.util.List;
57  import java.util.Locale;
58  import java.util.Map;
59  import java.util.MissingResourceException;
60  import java.util.ResourceBundle;
61  
62  /**
63   * Created by The eXo Platform SARL
64   * Author : Dang Van Minh
65   *          minh.dang@exoplatform.com
66   * Aug 18, 2009
67   * 3:49:41 AM
68   */
69  @ComponentConfig(
70      lifecycle = UIFormLifecycle.class,
71      template = "classpath:resources/templates/PDFJSViewer.gtmpl",
72      events = {
73          @EventConfig(listeners = PDFViewer.NextPageActionListener.class, phase = Phase.DECODE),
74          @EventConfig(listeners = PDFViewer.PreviousPageActionListener.class, phase = Phase.DECODE),
75          @EventConfig(listeners = PDFViewer.GotoPageActionListener.class, phase = Phase.DECODE),
76          @EventConfig(listeners = PDFViewer.RotateRightPageActionListener.class, phase = Phase.DECODE),
77          @EventConfig(listeners = PDFViewer.RotateLeftPageActionListener.class, phase = Phase.DECODE),
78          @EventConfig(listeners = PDFViewer.ScalePageActionListener.class, phase = Phase.DECODE),
79          @EventConfig(listeners = PDFViewer.DownloadFileActionListener.class, phase = Phase.DECODE),
80          @EventConfig(listeners = PDFViewer.ZoomInPageActionListener.class, phase = Phase.DECODE),
81          @EventConfig(listeners = PDFViewer.ZoomOutPageActionListener.class, phase = Phase.DECODE)
82      }
83  )
84  /**
85   * PDF Viewer component which will be used to display PDF file on web browser
86   */
87  public class PDFViewer extends UIForm {
88  
89    private static final Log LOG = ExoLogger.getExoLogger(PDFViewer.class);
90  
91    final static private String PAGE_NUMBER = "pageNumber";
92    final static private String SCALE_PAGE = "scalePage";
93    final private String localeFile = "locale.portlet.viewer.PDFViewer";
94    private String sharedResourcesBundleNames[];
95    private ResourceBundle sharedResourceBundle=null;
96    private static final int MIN_IE_SUPPORTED_BROWSER_VERSION = 9;
97    private static final int MIN_FF_SUPPORTED_BROWSER_VERSION = 20;
98    private static final int MIN_CHROME_SUPPORTED_BROWSER_VERSION = 20;
99  
100   private int currentPageNumber_ = 1;
101   private int maximumOfPage_ = 0;
102   private float currentRotation_ = 0.0f;
103   private float currentScale_ = 1.0f;
104   private Map<String, String> metadatas = new HashMap<String, String>();
105 
106   public PDFViewer() throws Exception {
107     addUIFormInput(new UIFormStringInput(PAGE_NUMBER, PAGE_NUMBER, "1"));
108     UIFormSelectBox uiScaleBox = new UIFormSelectBox(SCALE_PAGE, SCALE_PAGE, initScaleOptions());
109     uiScaleBox.setOnChange("ScalePage");
110     addUIFormInput(uiScaleBox);
111     uiScaleBox.setValue("1.0f");
112   }
113 
114   public Method getMethod(UIComponent uiComponent, String name) throws NoSuchMethodException {
115     return uiComponent.getClass().getMethod(name, new Class[0]);
116   }
117 
118   public void initDatas() throws Exception {
119     UIComponent uiParent = getParent();
120 
121     Method method = getMethod(uiParent, "getOriginalNode");
122     Node originalNode = null;
123     if(method != null) originalNode = (Node) method.invoke(uiParent, (Object[]) null);
124 
125     if(originalNode != null) {
126       Document document = getDocument(originalNode);
127       if (document != null) {
128         maximumOfPage_ = document.getNumberOfPages();
129         metadatas.clear();
130         putDocumentInfo(document.getInfo());
131         document.dispose();
132       } else maximumOfPage_ = -1;
133     }
134   }
135 
136   public Map getMetadataExtraction() { return metadatas; }
137 
138   public int getMaximumOfPage() throws Exception {
139     if(maximumOfPage_ == 0) {
140       initDatas();
141     }
142     return maximumOfPage_;
143   }
144 
145   public float getCurrentRotation() { return currentRotation_; }
146 
147   public void setRotation(float rotation) { currentRotation_ = rotation; }
148 
149   public float getCurrentScale() { return currentScale_; }
150 
151   public void setScale(float scale) { currentScale_ = scale; }
152 
153   public int getPageNumber() { return currentPageNumber_; }
154 
155   public void setPageNumber(int pageNum) { currentPageNumber_ = pageNum; };
156 
157   public String getResourceBundle(String key) {
158     try {
159       Locale locale = Util.getUIPortal().getAncestorOfType(UIPortalApplication.class).getLocale();
160       ResourceBundleService resourceBundleService = WCMCoreUtils.getService(ResourceBundleService.class);
161       ResourceBundle resourceBundle = resourceBundleService.getResourceBundle(localeFile, locale, this.getClass().getClassLoader());
162 
163       return resourceBundle.getString(key);
164     } catch (MissingResourceException e) {
165       return key;
166     }
167   }
168 
169   public String getResource(String key) {
170     try {
171       Locale locale = Util.getUIPortal().getAncestorOfType(UIPortalApplication.class).getLocale();
172       ResourceBundleService resourceBundleService = WCMCoreUtils.getService(ResourceBundleService.class);
173       sharedResourcesBundleNames = resourceBundleService.getSharedResourceBundleNames();
174       sharedResourceBundle = resourceBundleService.getResourceBundle(sharedResourcesBundleNames, locale);
175 
176       return sharedResourceBundle.getString(key);
177     } catch (MissingResourceException e) {
178       return key;
179     }
180   }
181 
182   private Document getDocument(Node node) throws RepositoryException, Exception {
183     PDFViewerService pdfViewerService = getApplicationComponent(PDFViewerService.class);
184     String repository = (String) getMethod(this.getParent(), "getRepository").invoke(this.getParent(), (Object[]) null);
185     return pdfViewerService.initDocument(node, repository);
186   }
187 
188   private void putDocumentInfo(PInfo documentInfo) {
189     if (documentInfo != null) {
190       if(documentInfo.getTitle() != null && documentInfo.getTitle().length() > 0) {
191         metadatas.put("title", documentInfo.getTitle());
192       }
193       if(documentInfo.getAuthor() != null && documentInfo.getAuthor().length() > 0) {
194         metadatas.put("author", documentInfo.getAuthor());
195       }
196       if(documentInfo.getSubject() != null && documentInfo.getSubject().length() > 0) {
197         metadatas.put("subject", documentInfo.getSubject());
198       }
199       if(documentInfo.getKeywords() != null && documentInfo.getKeywords().length() > 0) {
200         metadatas.put("keyWords", documentInfo.getKeywords());
201       }
202       if(documentInfo.getCreator() != null && documentInfo.getCreator().length() > 0) {
203         metadatas.put("creator", documentInfo.getCreator());
204       }
205       if(documentInfo.getProducer() != null && documentInfo.getProducer().length() > 0) {
206         metadatas.put("producer", documentInfo.getProducer());
207       }
208       try {
209         if(documentInfo.getCreationDate() != null) {
210           metadatas.put("creationDate", documentInfo.getCreationDate().toString());
211         }
212       } catch (Exception e) {
213         LOG.debug("Error when getting creation date.", e);
214       }
215       try {
216         if(documentInfo.getModDate() != null) {
217           metadatas.put("modDate", documentInfo.getModDate().toString());
218         }
219       } catch (Exception e) {
220         LOG.debug("Exception when getting modification date.", e);
221       }
222     }
223   }
224 
225   private List<SelectItemOption<String>> initScaleOptions() {
226     List<SelectItemOption<String>> scaleOptions = new ArrayList<SelectItemOption<String>>();
227     scaleOptions.add(new SelectItemOption<String>("5%",  "0.05f"));
228     scaleOptions.add(new SelectItemOption<String>("10%",  "0.1f"));
229     scaleOptions.add(new SelectItemOption<String>("25%",  "0.25f"));
230     scaleOptions.add(new SelectItemOption<String>("50%",  "0.5f"));
231     scaleOptions.add(new SelectItemOption<String>("75%",  "0.75f"));
232     scaleOptions.add(new SelectItemOption<String>("100%",  "1.0f"));
233     scaleOptions.add(new SelectItemOption<String>("125%",  "1.25f"));
234     scaleOptions.add(new SelectItemOption<String>("150%",  "1.5f"));
235     scaleOptions.add(new SelectItemOption<String>("200%",  "2.0f"));
236     scaleOptions.add(new SelectItemOption<String>("300%",  "3.0f"));
237     return scaleOptions;
238   }
239 
240 
241   /**
242    * Check if client has modern browser (IE9+, FF20+, Chrome 20+).
243    */
244   private boolean isNotModernBrowser() {
245     PortletRequestContext requestContext = PortletRequestContext.getCurrentInstance();
246     PortletRequest portletRequest = requestContext.getRequest();
247     String userAgent = portletRequest.getProperty("user-agent");
248     boolean isChrome = (userAgent.indexOf("Chrome/") != -1);
249     boolean isMSIE = (userAgent.indexOf("MSIE") != -1);
250     boolean isFirefox = (userAgent.indexOf("Firefox/") != -1);
251     String version = "1";
252     if (isFirefox) {
253       // Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:13.0) Gecko/20100101 Firefox/13.0
254       version = userAgent.replaceAll("^.*?Firefox/", "").replaceAll("\\.\\d+", "");
255     } else if (isChrome) {
256       // Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.52 Safari/536.5
257       version = userAgent.replaceAll("^.*?Chrome/(\\d+)\\..*$", "$1");
258     } else if (isMSIE) {
259       // Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET4.0C; .NET4.0E)
260       version = userAgent.replaceAll("^.*?MSIE\\s+(\\d+).*$", "$1");
261     }
262 
263     boolean unsupportedBrowser = (isFirefox && Integer.parseInt(version) < MIN_FF_SUPPORTED_BROWSER_VERSION)
264             || (isChrome && Integer.parseInt(version) < MIN_CHROME_SUPPORTED_BROWSER_VERSION)
265             || (isMSIE && Integer.parseInt(version) < MIN_IE_SUPPORTED_BROWSER_VERSION);
266     return unsupportedBrowser;
267   }
268 
269   static public class PreviousPageActionListener extends EventListener<PDFViewer> {
270     public void execute(Event<PDFViewer> event) throws Exception {
271       PDFViewer pdfViewer = event.getSource();
272       if(pdfViewer.currentPageNumber_ == 1) {
273         pdfViewer.getUIStringInput(PAGE_NUMBER).setValue(
274             Integer.toString((pdfViewer.currentPageNumber_)));
275       } else {
276         pdfViewer.getUIStringInput(PAGE_NUMBER).setValue(
277             Integer.toString((pdfViewer.currentPageNumber_ -1)));
278         pdfViewer.setPageNumber(pdfViewer.currentPageNumber_ - 1);
279       }
280       event.getRequestContext().addUIComponentToUpdateByAjax(pdfViewer);
281     }
282   }
283 
284   static public class NextPageActionListener extends EventListener<PDFViewer> {
285     public void execute(Event<PDFViewer> event) throws Exception {
286       PDFViewer pdfViewer = event.getSource();
287       if(pdfViewer.currentPageNumber_ == pdfViewer.maximumOfPage_) {
288         pdfViewer.getUIStringInput(PAGE_NUMBER).setValue(
289             Integer.toString((pdfViewer.currentPageNumber_)));
290       } else {
291         pdfViewer.getUIStringInput(PAGE_NUMBER).setValue(
292             Integer.toString((pdfViewer.currentPageNumber_ + 1)));
293         pdfViewer.setPageNumber(pdfViewer.currentPageNumber_ + 1);
294       }
295       event.getRequestContext().addUIComponentToUpdateByAjax(pdfViewer);
296     }
297   }
298 
299   static public class GotoPageActionListener extends EventListener<PDFViewer> {
300     public void execute(Event<PDFViewer> event) throws Exception {
301       PDFViewer pdfViewer = event.getSource();
302       String pageStr = pdfViewer.getUIStringInput(PAGE_NUMBER).getValue();
303       int pageNumber = 1;
304       try {
305         pageNumber = Integer.parseInt(pageStr);
306       } catch(NumberFormatException e) {
307         pageNumber = pdfViewer.currentPageNumber_;
308       }
309       if(pageNumber >= pdfViewer.maximumOfPage_) pageNumber = pdfViewer.maximumOfPage_;
310       else if(pageNumber < 1) pageNumber = 1;
311       pdfViewer.getUIStringInput(PAGE_NUMBER).setValue(Integer.toString((pageNumber)));
312       pdfViewer.setPageNumber(pageNumber);
313       event.getRequestContext().addUIComponentToUpdateByAjax(pdfViewer);
314     }
315   }
316 
317   static public class RotateRightPageActionListener extends EventListener<PDFViewer> {
318     public void execute(Event<PDFViewer> event) throws Exception {
319       PDFViewer pdfViewer = event.getSource();
320       pdfViewer.setRotation(pdfViewer.currentRotation_ + 270.0f);
321       event.getRequestContext().addUIComponentToUpdateByAjax(pdfViewer);
322     }
323   }
324 
325   static public class RotateLeftPageActionListener extends EventListener<PDFViewer> {
326     public void execute(Event<PDFViewer> event) throws Exception {
327       PDFViewer pdfViewer = event.getSource();
328       pdfViewer.setRotation(pdfViewer.currentRotation_ + 90.0f);
329       event.getRequestContext().addUIComponentToUpdateByAjax(pdfViewer);
330     }
331   }
332 
333   static public class ScalePageActionListener extends EventListener<PDFViewer> {
334     public void execute(Event<PDFViewer> event) throws Exception {
335       PDFViewer pdfViewer = event.getSource();
336       String scale = pdfViewer.getUIFormSelectBox(SCALE_PAGE).getValue();
337       pdfViewer.setScale(Float.parseFloat(scale));
338       event.getRequestContext().addUIComponentToUpdateByAjax(pdfViewer);
339     }
340   }
341 
342   static public class DownloadFileActionListener extends EventListener<PDFViewer> {
343     public void execute(Event<PDFViewer> event) throws Exception {
344       PDFViewer pdfViewer = event.getSource();
345       UIComponent uiParent = pdfViewer.getParent();
346       Method methodGetNode = pdfViewer.getMethod(uiParent, "getNode");
347       Node node = (Node)methodGetNode.invoke(uiParent, (Object[]) null);
348       node = getFileLangNode(node);
349       String repository = (String) pdfViewer.getMethod(uiParent, "getRepository").invoke(uiParent, (Object[]) null);
350       PDFViewerService pdfViewerService = pdfViewer.getApplicationComponent(PDFViewerService.class);
351       File file = pdfViewerService.getPDFDocumentFile(node, repository);
352       String fileName = node.getName();
353       int index = fileName.lastIndexOf('.');
354       if (index < 0) {
355         fileName += ".pdf";
356       } else if (index == fileName.length() - 1) {
357         fileName += "pdf";
358       } else {
359         String extension = fileName.substring(index + 1);
360         fileName = fileName.replace(extension, "pdf");
361       }
362       DownloadService dservice = pdfViewer.getApplicationComponent(DownloadService.class) ;
363       InputStreamDownloadResource dresource = new InputStreamDownloadResource(
364           new BufferedInputStream(new FileInputStream(file)), DMSMimeTypeResolver.getInstance().getMimeType(".pdf"));
365       dresource.setDownloadName(fileName) ;
366       String downloadLink = dservice.getDownloadLink(dservice.addDownloadResource(dresource)) ;
367 
368       RequireJS requireJS = event.getRequestContext().getJavascriptManager().getRequireJS();
369       requireJS.require("SHARED/ecm-utils", "ecmutil").addScripts("ecmutil.ECMUtils.ajaxRedirect('" + downloadLink + "');");
370       event.getRequestContext().addUIComponentToUpdateByAjax(pdfViewer);
371     }
372   }
373 
374   static public class ZoomInPageActionListener extends EventListener<PDFViewer> {
375     public void execute(Event<PDFViewer> event) throws Exception {
376       PDFViewer pdfViewer = event.getSource();
377       String[] arrValue = {"0.05f", "0.1f", "0.25f", "0.5f", "0.75f", "1.0f",
378           "1.25f", "1.5f", "2.0f", "3.0f"};
379       String scale = pdfViewer.getUIFormSelectBox(SCALE_PAGE).getValue();
380       if(scale.equals(arrValue[arrValue.length - 1])) return;
381       for(int i = 0; i < arrValue.length - 1; i++) {
382         if(scale.equals(arrValue[i])) {
383           pdfViewer.setScale(Float.parseFloat(arrValue[i + 1]));
384           pdfViewer.getUIFormSelectBox(SCALE_PAGE).setValue(arrValue[i + 1]);
385           break;
386         }
387       }
388       event.getRequestContext().addUIComponentToUpdateByAjax(pdfViewer);
389     }
390   }
391 
392   static public class ZoomOutPageActionListener extends EventListener<PDFViewer> {
393     public void execute(Event<PDFViewer> event) throws Exception {
394       PDFViewer pdfViewer = event.getSource();
395       String scale = pdfViewer.getUIFormSelectBox(SCALE_PAGE).getValue();
396       String[] arrValue = {"0.05f", "0.1f", "0.25f", "0.5f", "0.75f", "1.0f",
397           "1.25f", "1.5f", "2.0f", "3.0f"};
398       if(scale.equals(arrValue[0])) return;
399       for(int i = 0; i < arrValue.length - 1; i++) {
400         if(scale.equals(arrValue[i])) {
401           pdfViewer.setScale(Float.parseFloat(arrValue[i - 1]));
402           pdfViewer.getUIFormSelectBox(SCALE_PAGE).setValue(arrValue[i - 1]);
403           break;
404         }
405       }
406       event.getRequestContext().addUIComponentToUpdateByAjax(pdfViewer);
407     }
408   }
409   static public Node getFileLangNode(Node currentNode) throws Exception {
410     if(currentNode.isNodeType("nt:unstructured")) {
411       if(currentNode.getNodes().getSize() > 0) {
412         NodeIterator nodeIter = currentNode.getNodes() ;
413         while(nodeIter.hasNext()) {
414           Node ntFile = nodeIter.nextNode() ;
415           if(ntFile.isNodeType("nt:file")) {
416             return ntFile ;
417           }
418         }
419         return currentNode ;
420       }
421     }
422     return currentNode ;
423   }
424   
425   public String getActionOpenDocInDesktop() {
426     UIComponent uiParent = getParent();
427     String ret = "";
428     try {
429       Method method = getMethod(uiParent, "getActionOpenDocInDesktop");
430       if(method != null) ret = (String) method.invoke(uiParent, (Object[]) null);
431     } catch (NoSuchMethodException| IllegalArgumentException| IllegalAccessException| InvocationTargetException e) {
432       ret = "";
433     }
434     return ret;
435   }
436 }