View Javadoc
1   /*
2    * Copyright (c) 2011-2024 PrimeFaces Extensions
3    *
4    *  Permission is hereby granted, free of charge, to any person obtaining a copy
5    *  of this software and associated documentation files (the "Software"), to deal
6    *  in the Software without restriction, including without limitation the rights
7    *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    *  copies of the Software, and to permit persons to whom the Software is
9    *  furnished to do so, subject to the following conditions:
10   *
11   *  The above copyright notice and this permission notice shall be included in
12   *  all copies or substantial portions of the Software.
13   *
14   *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20   *  THE SOFTWARE.
21   */
22  package org.primefaces.extensions.component.documentviewer;
23  
24  import java.io.IOException;
25  import java.net.URLEncoder;
26  import java.nio.charset.StandardCharsets;
27  import java.util.ArrayList;
28  import java.util.List;
29  import java.util.Objects;
30  
31  import javax.faces.application.Resource;
32  import javax.faces.application.ResourceHandler;
33  import javax.faces.component.UIComponent;
34  import javax.faces.context.FacesContext;
35  import javax.faces.context.ResponseWriter;
36  
37  import org.primefaces.context.PrimeRequestContext;
38  import org.primefaces.extensions.util.Attrs;
39  import org.primefaces.extensions.util.ExtLangUtils;
40  import org.primefaces.model.StreamedContent;
41  import org.primefaces.renderkit.CoreRenderer;
42  import org.primefaces.util.Constants;
43  import org.primefaces.util.DynamicContentSrcBuilder;
44  import org.primefaces.util.LangUtils;
45  import org.primefaces.util.Lazy;
46  
47  /**
48   * Renderer for the {@link DocumentViewer} component.
49   *
50   * @author f.strazzullo
51   * @author Melloware mellowaredev@gmail.com
52   * @since 3.0.0
53   */
54  public class DocumentViewerRenderer extends CoreRenderer {
55  
56      @Override
57      public void encodeEnd(final FacesContext context, final UIComponent component) throws IOException {
58          boolean hideResourceVersion = PrimeRequestContext.getCurrentInstance(context).isHideResourceVersion();
59          if (hideResourceVersion) {
60              logDevelopmentWarning(context, "DocumentViewer requires a resource version to work properly and '" +
61                          Constants.ContextParams.HIDE_RESOURCE_VERSION + "' is currently configured.");
62          }
63  
64          final DocumentViewer documentViewer = (DocumentViewer) component;
65          encodeMarkup(context, documentViewer);
66      }
67  
68      private void encodeMarkup(final FacesContext context, final DocumentViewer documentViewer) throws IOException {
69          // Section 508 frame title for assisted technology
70          String title = documentViewer.getTitle() != null ? documentViewer.getTitle() : documentViewer.getName();
71          title = ExtLangUtils.defaultString(title, "Document Viewer");
72  
73          final ResponseWriter writer = context.getResponseWriter();
74          writer.startElement("iframe", documentViewer);
75          writer.writeAttribute("id", documentViewer.getClientId(), null);
76          writer.writeAttribute(Attrs.STYLE, documentViewer.getStyle(), null);
77          writer.writeAttribute("title", title, null);
78          writer.writeAttribute("width", documentViewer.getWidth() != null ? documentViewer.getWidth() : "100%", null);
79          writer.writeAttribute("height", documentViewer.getHeight(), null);
80          writer.writeAttribute("allowfullscreen", Constants.EMPTY_STRING, null);
81          writer.writeAttribute("webkitallowfullscreen", Constants.EMPTY_STRING, null);
82          writer.writeAttribute("src", generateSrc(context, documentViewer), null);
83          writer.endElement("iframe");
84      }
85  
86      private String generateSrc(final FacesContext context, final DocumentViewer documentViewer) throws IOException {
87          final String imageSrc;
88          try {
89              imageSrc = URLEncoder.encode(getDocumentSource(context, documentViewer), StandardCharsets.UTF_8);
90          }
91          catch (final Exception ex) {
92              throw new IOException(ex);
93          }
94  
95          return getResourceURL(context) +
96                      "&file=" +
97                      imageSrc +
98                      generateHashString(documentViewer);
99      }
100 
101     private String generateHashString(final DocumentViewer documentViewer) {
102         final List<String> params = new ArrayList<>(4);
103         params.add("locale=" + documentViewer.calculateLocale().toString().replace('_', '-'));
104 
105         // page: page number. Example: page=2
106         if (documentViewer.getPage() != null) {
107             params.add("page=" + documentViewer.getPage());
108         }
109 
110         // zoom level. Example: zoom=200 (accepted formats: '[zoom],[left],[top]',
111         // 'page-width', 'page-height', 'page-fit', 'auto')
112         if (LangUtils.isNotBlank(documentViewer.getZoom())) {
113             params.add("zoom=" + documentViewer.getZoom());
114         }
115 
116         // nameddest: go to a named destination
117         if (LangUtils.isNotBlank(documentViewer.getNameddest())) {
118             params.add("nameddest=" + documentViewer.getNameddest());
119         }
120 
121         // pagemode: either "thumbs" or "bookmarks". Example: pagemode=thumbs
122         if (LangUtils.isNotBlank(documentViewer.getPagemode())) {
123             params.add("pagemode=" + documentViewer.getPagemode());
124         }
125 
126         params.add("disableFontFace=" + documentViewer.isDisableFontFace());
127 
128         return "#" + String.join("&", params.toArray(new String[0]));
129     }
130 
131     private String getResourceURL(final FacesContext context) {
132         final ResourceHandler handler = context.getApplication().getResourceHandler();
133         return context.getExternalContext().encodeResourceURL(
134                     handler.createResource("documentviewer/pdfviewer.html", "primefaces-extensions").getRequestPath());
135     }
136 
137     protected String getDocumentSource(final FacesContext context, final DocumentViewer documentViewer) {
138         final String name = documentViewer.getName();
139 
140         if (name != null) {
141             final String libName = documentViewer.getLibrary();
142             final ResourceHandler handler = context.getApplication().getResourceHandler();
143             final Resource res = handler.createResource(name, libName);
144 
145             if (res == null) {
146                 return "RES_NOT_FOUND";
147             }
148             else {
149                 final String requestPath = res.getRequestPath();
150                 return context.getExternalContext().encodeResourceURL(requestPath);
151             }
152         }
153         else {
154             final Object value = documentViewer.getValue();
155             String downloadName = documentViewer.getDownload();
156             if (value instanceof StreamedContent) {
157                 final StreamedContent streamedContent = (StreamedContent) value;
158                 downloadName = Objects.toString(streamedContent.getName(), downloadName);
159             }
160             return DynamicContentSrcBuilder.build(context,
161                         documentViewer,
162                         documentViewer.getValueExpression("value"),
163                         new Lazy<>(() -> value),
164                         documentViewer.isCache(),
165                         true) + "&download=" + downloadName;
166         }
167     }
168 }