/* * Copyright (C) 2003-2008 eXo Platform SAS. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see<http://www.gnu.org/licenses/>. */ package org.exoplatform.ecm.webui.viewer; import org.exoplatform.download.DownloadService; import org.exoplatform.download.InputStreamDownloadResource; import org.exoplatform.portal.webui.util.Util; import org.exoplatform.portal.webui.workspace.UIPortalApplication; import org.exoplatform.services.cms.mimetype.DMSMimeTypeResolver; import org.exoplatform.services.log.ExoLogger; import org.exoplatform.services.log.Log; import org.exoplatform.services.pdfviewer.PDFViewerService; import org.exoplatform.services.resources.ResourceBundleService; import org.exoplatform.services.wcm.utils.WCMCoreUtils; import org.exoplatform.web.application.RequireJS; import org.exoplatform.webui.application.portlet.PortletRequestContext; import org.exoplatform.webui.config.annotation.ComponentConfig; import org.exoplatform.webui.config.annotation.EventConfig; import org.exoplatform.webui.core.UIComponent; import org.exoplatform.webui.core.lifecycle.UIFormLifecycle; import org.exoplatform.webui.core.model.SelectItemOption; import org.exoplatform.webui.event.Event; import org.exoplatform.webui.event.Event.Phase; import org.exoplatform.webui.event.EventListener; import org.exoplatform.webui.form.UIForm; import org.exoplatform.webui.form.UIFormSelectBox; import org.exoplatform.webui.form.UIFormStringInput; import org.icepdf.core.pobjects.Document; import org.icepdf.core.pobjects.PInfo; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.RepositoryException; import javax.portlet.PortletRequest; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.MissingResourceException; import java.util.ResourceBundle; /** * Created by The eXo Platform SARL * Author : Dang Van Minh * minh.dang@exoplatform.com * Aug 18, 2009 * 3:49:41 AM */ @ComponentConfig( lifecycle = UIFormLifecycle.class, template = "classpath:resources/templates/PDFJSViewer.gtmpl", events = { @EventConfig(listeners = PDFViewer.NextPageActionListener.class, phase = Phase.DECODE), @EventConfig(listeners = PDFViewer.PreviousPageActionListener.class, phase = Phase.DECODE), @EventConfig(listeners = PDFViewer.GotoPageActionListener.class, phase = Phase.DECODE), @EventConfig(listeners = PDFViewer.RotateRightPageActionListener.class, phase = Phase.DECODE), @EventConfig(listeners = PDFViewer.RotateLeftPageActionListener.class, phase = Phase.DECODE), @EventConfig(listeners = PDFViewer.ScalePageActionListener.class, phase = Phase.DECODE), @EventConfig(listeners = PDFViewer.DownloadFileActionListener.class, phase = Phase.DECODE), @EventConfig(listeners = PDFViewer.ZoomInPageActionListener.class, phase = Phase.DECODE), @EventConfig(listeners = PDFViewer.ZoomOutPageActionListener.class, phase = Phase.DECODE) } ) /** * PDF Viewer component which will be used to display PDF file on web browser */ public class PDFViewer extends UIForm { private static final Log LOG = ExoLogger.getExoLogger(PDFViewer.class); final static private String PAGE_NUMBER = "pageNumber"; final static private String SCALE_PAGE = "scalePage"; final private String localeFile = "locale.portlet.viewer.PDFViewer"; private static final int MIN_IE_SUPPORTED_BROWSER_VERSION = 9; private static final int MIN_FF_SUPPORTED_BROWSER_VERSION = 20; private static final int MIN_CHROME_SUPPORTED_BROWSER_VERSION = 20; private int currentPageNumber_ = 1; private int maximumOfPage_ = 0; private float currentRotation_ = 0.0f; private float currentScale_ = 1.0f; private Map<String, String> metadatas = new HashMap<String, String>(); public PDFViewer() throws Exception { addUIFormInput(new UIFormStringInput(PAGE_NUMBER, PAGE_NUMBER, "1")); UIFormSelectBox uiScaleBox = new UIFormSelectBox(SCALE_PAGE, SCALE_PAGE, initScaleOptions()); uiScaleBox.setOnChange("ScalePage"); addUIFormInput(uiScaleBox); uiScaleBox.setValue("1.0f"); } public Method getMethod(UIComponent uiComponent, String name) throws NoSuchMethodException { return uiComponent.getClass().getMethod(name, new Class[0]); } public void initDatas() throws Exception { UIComponent uiParent = getParent(); Method method = getMethod(uiParent, "getOriginalNode"); Node originalNode = null; if(method != null) originalNode = (Node) method.invoke(uiParent, (Object[]) null); if(originalNode != null) { Document document = getDocument(originalNode); if (document != null) { maximumOfPage_ = document.getNumberOfPages(); metadatas.clear(); putDocumentInfo(document.getInfo()); document.dispose(); } else maximumOfPage_ = -1; } } public Map getMetadataExtraction() { return metadatas; } public int getMaximumOfPage() throws Exception { if(maximumOfPage_ == 0) { initDatas(); } return maximumOfPage_; } public float getCurrentRotation() { return currentRotation_; } public void setRotation(float rotation) { currentRotation_ = rotation; } public float getCurrentScale() { return currentScale_; } public void setScale(float scale) { currentScale_ = scale; } public int getPageNumber() { return currentPageNumber_; } public void setPageNumber(int pageNum) { currentPageNumber_ = pageNum; }; public String getResourceBundle(String key) { try { Locale locale = Util.getUIPortal().getAncestorOfType(UIPortalApplication.class).getLocale(); ResourceBundleService resourceBundleService = WCMCoreUtils.getService(ResourceBundleService.class); ResourceBundle resourceBundle = resourceBundleService.getResourceBundle(localeFile, locale, this.getClass().getClassLoader()); return resourceBundle.getString(key); } catch (MissingResourceException e) { return key; } } private Document getDocument(Node node) throws RepositoryException, Exception { PDFViewerService pdfViewerService = getApplicationComponent(PDFViewerService.class); String repository = (String) getMethod(this.getParent(), "getRepository").invoke(this.getParent(), (Object[]) null); return pdfViewerService.initDocument(node, repository); } private void putDocumentInfo(PInfo documentInfo) { if (documentInfo != null) { if(documentInfo.getTitle() != null && documentInfo.getTitle().length() > 0) { metadatas.put("title", documentInfo.getTitle()); } if(documentInfo.getAuthor() != null && documentInfo.getAuthor().length() > 0) { metadatas.put("author", documentInfo.getAuthor()); } if(documentInfo.getSubject() != null && documentInfo.getSubject().length() > 0) { metadatas.put("subject", documentInfo.getSubject()); } if(documentInfo.getKeywords() != null && documentInfo.getKeywords().length() > 0) { metadatas.put("keyWords", documentInfo.getKeywords()); } if(documentInfo.getCreator() != null && documentInfo.getCreator().length() > 0) { metadatas.put("creator", documentInfo.getCreator()); } if(documentInfo.getProducer() != null && documentInfo.getProducer().length() > 0) { metadatas.put("producer", documentInfo.getProducer()); } try { if(documentInfo.getCreationDate() != null) { metadatas.put("creationDate", documentInfo.getCreationDate().toString()); } } catch (Exception e) { LOG.debug("Error when getting creation date.", e); } try { if(documentInfo.getModDate() != null) { metadatas.put("modDate", documentInfo.getModDate().toString()); } } catch (Exception e) { LOG.debug("Exception when getting modification date.", e); } } } private List<SelectItemOption<String>> initScaleOptions() { List<SelectItemOption<String>> scaleOptions = new ArrayList<SelectItemOption<String>>(); scaleOptions.add(new SelectItemOption<String>("5%", "0.05f")); scaleOptions.add(new SelectItemOption<String>("10%", "0.1f")); scaleOptions.add(new SelectItemOption<String>("25%", "0.25f")); scaleOptions.add(new SelectItemOption<String>("50%", "0.5f")); scaleOptions.add(new SelectItemOption<String>("75%", "0.75f")); scaleOptions.add(new SelectItemOption<String>("100%", "1.0f")); scaleOptions.add(new SelectItemOption<String>("125%", "1.25f")); scaleOptions.add(new SelectItemOption<String>("150%", "1.5f")); scaleOptions.add(new SelectItemOption<String>("200%", "2.0f")); scaleOptions.add(new SelectItemOption<String>("300%", "3.0f")); return scaleOptions; } /** * Check if client has modern browser (IE9+, FF20+, Chrome 20+). */ private boolean isNotModernBrowser() { PortletRequestContext requestContext = PortletRequestContext.getCurrentInstance(); PortletRequest portletRequest = requestContext.getRequest(); String userAgent = portletRequest.getProperty("user-agent"); boolean isChrome = (userAgent.indexOf("Chrome/") != -1); boolean isMSIE = (userAgent.indexOf("MSIE") != -1); boolean isFirefox = (userAgent.indexOf("Firefox/") != -1); String version = "1"; if (isFirefox) { // Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:13.0) Gecko/20100101 Firefox/13.0 version = userAgent.replaceAll("^.*?Firefox/", "").replaceAll("\\.\\d+", ""); } else if (isChrome) { // Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.52 Safari/536.5 version = userAgent.replaceAll("^.*?Chrome/(\\d+)\\..*$", "$1"); } else if (isMSIE) { // 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) version = userAgent.replaceAll("^.*?MSIE\\s+(\\d+).*$", "$1"); } boolean unsupportedBrowser = (isFirefox && Integer.parseInt(version) < MIN_FF_SUPPORTED_BROWSER_VERSION) || (isChrome && Integer.parseInt(version) < MIN_CHROME_SUPPORTED_BROWSER_VERSION) || (isMSIE && Integer.parseInt(version) < MIN_IE_SUPPORTED_BROWSER_VERSION); return unsupportedBrowser; } static public class PreviousPageActionListener extends EventListener<PDFViewer> { public void execute(Event<PDFViewer> event) throws Exception { PDFViewer pdfViewer = event.getSource(); if(pdfViewer.currentPageNumber_ == 1) { pdfViewer.getUIStringInput(PAGE_NUMBER).setValue( Integer.toString((pdfViewer.currentPageNumber_))); } else { pdfViewer.getUIStringInput(PAGE_NUMBER).setValue( Integer.toString((pdfViewer.currentPageNumber_ -1))); pdfViewer.setPageNumber(pdfViewer.currentPageNumber_ - 1); } event.getRequestContext().addUIComponentToUpdateByAjax(pdfViewer); } } static public class NextPageActionListener extends EventListener<PDFViewer> { public void execute(Event<PDFViewer> event) throws Exception { PDFViewer pdfViewer = event.getSource(); if(pdfViewer.currentPageNumber_ == pdfViewer.maximumOfPage_) { pdfViewer.getUIStringInput(PAGE_NUMBER).setValue( Integer.toString((pdfViewer.currentPageNumber_))); } else { pdfViewer.getUIStringInput(PAGE_NUMBER).setValue( Integer.toString((pdfViewer.currentPageNumber_ + 1))); pdfViewer.setPageNumber(pdfViewer.currentPageNumber_ + 1); } event.getRequestContext().addUIComponentToUpdateByAjax(pdfViewer); } } static public class GotoPageActionListener extends EventListener<PDFViewer> { public void execute(Event<PDFViewer> event) throws Exception { PDFViewer pdfViewer = event.getSource(); String pageStr = pdfViewer.getUIStringInput(PAGE_NUMBER).getValue(); int pageNumber = 1; try { pageNumber = Integer.parseInt(pageStr); } catch(NumberFormatException e) { pageNumber = pdfViewer.currentPageNumber_; } if(pageNumber >= pdfViewer.maximumOfPage_) pageNumber = pdfViewer.maximumOfPage_; else if(pageNumber < 1) pageNumber = 1; pdfViewer.getUIStringInput(PAGE_NUMBER).setValue(Integer.toString((pageNumber))); pdfViewer.setPageNumber(pageNumber); event.getRequestContext().addUIComponentToUpdateByAjax(pdfViewer); } } static public class RotateRightPageActionListener extends EventListener<PDFViewer> { public void execute(Event<PDFViewer> event) throws Exception { PDFViewer pdfViewer = event.getSource(); pdfViewer.setRotation(pdfViewer.currentRotation_ + 270.0f); event.getRequestContext().addUIComponentToUpdateByAjax(pdfViewer); } } static public class RotateLeftPageActionListener extends EventListener<PDFViewer> { public void execute(Event<PDFViewer> event) throws Exception { PDFViewer pdfViewer = event.getSource(); pdfViewer.setRotation(pdfViewer.currentRotation_ + 90.0f); event.getRequestContext().addUIComponentToUpdateByAjax(pdfViewer); } } static public class ScalePageActionListener extends EventListener<PDFViewer> { public void execute(Event<PDFViewer> event) throws Exception { PDFViewer pdfViewer = event.getSource(); String scale = pdfViewer.getUIFormSelectBox(SCALE_PAGE).getValue(); pdfViewer.setScale(Float.parseFloat(scale)); event.getRequestContext().addUIComponentToUpdateByAjax(pdfViewer); } } static public class DownloadFileActionListener extends EventListener<PDFViewer> { public void execute(Event<PDFViewer> event) throws Exception { PDFViewer pdfViewer = event.getSource(); UIComponent uiParent = pdfViewer.getParent(); Method methodGetNode = pdfViewer.getMethod(uiParent, "getNode"); Node node = (Node)methodGetNode.invoke(uiParent, (Object[]) null); node = getFileLangNode(node); String repository = (String) pdfViewer.getMethod(uiParent, "getRepository").invoke(uiParent, (Object[]) null); PDFViewerService pdfViewerService = pdfViewer.getApplicationComponent(PDFViewerService.class); File file = pdfViewerService.getPDFDocumentFile(node, repository); String fileName = node.getName(); int index = fileName.lastIndexOf('.'); if (index < 0) { fileName += ".pdf"; } else if (index == fileName.length() - 1) { fileName += "pdf"; } else { String extension = fileName.substring(index + 1); fileName = fileName.replace(extension, "pdf"); } DownloadService dservice = pdfViewer.getApplicationComponent(DownloadService.class) ; InputStreamDownloadResource dresource = new InputStreamDownloadResource( new BufferedInputStream(new FileInputStream(file)), DMSMimeTypeResolver.getInstance().getMimeType(".pdf")); dresource.setDownloadName(fileName) ; String downloadLink = dservice.getDownloadLink(dservice.addDownloadResource(dresource)) ; RequireJS requireJS = event.getRequestContext().getJavascriptManager().getRequireJS(); requireJS.require("SHARED/ecm-utils", "ecmutil").addScripts("ecmutil.ECMUtils.ajaxRedirect('" + downloadLink + "');"); event.getRequestContext().addUIComponentToUpdateByAjax(pdfViewer); } } static public class ZoomInPageActionListener extends EventListener<PDFViewer> { public void execute(Event<PDFViewer> event) throws Exception { PDFViewer pdfViewer = event.getSource(); String[] arrValue = {"0.05f", "0.1f", "0.25f", "0.5f", "0.75f", "1.0f", "1.25f", "1.5f", "2.0f", "3.0f"}; String scale = pdfViewer.getUIFormSelectBox(SCALE_PAGE).getValue(); if(scale.equals(arrValue[arrValue.length - 1])) return; for(int i = 0; i < arrValue.length - 1; i++) { if(scale.equals(arrValue[i])) { pdfViewer.setScale(Float.parseFloat(arrValue[i + 1])); pdfViewer.getUIFormSelectBox(SCALE_PAGE).setValue(arrValue[i + 1]); break; } } event.getRequestContext().addUIComponentToUpdateByAjax(pdfViewer); } } static public class ZoomOutPageActionListener extends EventListener<PDFViewer> { public void execute(Event<PDFViewer> event) throws Exception { PDFViewer pdfViewer = event.getSource(); String scale = pdfViewer.getUIFormSelectBox(SCALE_PAGE).getValue(); String[] arrValue = {"0.05f", "0.1f", "0.25f", "0.5f", "0.75f", "1.0f", "1.25f", "1.5f", "2.0f", "3.0f"}; if(scale.equals(arrValue[0])) return; for(int i = 0; i < arrValue.length - 1; i++) { if(scale.equals(arrValue[i])) { pdfViewer.setScale(Float.parseFloat(arrValue[i - 1])); pdfViewer.getUIFormSelectBox(SCALE_PAGE).setValue(arrValue[i - 1]); break; } } event.getRequestContext().addUIComponentToUpdateByAjax(pdfViewer); } } static public Node getFileLangNode(Node currentNode) throws Exception { if(currentNode.isNodeType("nt:unstructured")) { if(currentNode.getNodes().getSize() > 0) { NodeIterator nodeIter = currentNode.getNodes() ; while(nodeIter.hasNext()) { Node ntFile = nodeIter.nextNode() ; if(ntFile.isNodeType("nt:file")) { return ntFile ; } } return currentNode ; } } return currentNode ; } public String getActionOpenDocInDesktop() { UIComponent uiParent = getParent(); String ret = ""; try { Method method = getMethod(uiParent, "getActionOpenDocInDesktop"); if(method != null) ret = (String) method.invoke(uiParent, (Object[]) null); } catch (NoSuchMethodException| IllegalArgumentException| IllegalAccessException| InvocationTargetException e) { ret = ""; } return ret; } }