001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: // Aug 21, 2000:
019: // Added ability to omit DOCTYPE declaration.
020: // Reported by Lars Martin <lars@smb-tec.com>
021: // Aug 25, 2000:
022: // Added ability to omit comments.
023: // Contributed by Anupam Bagchi <abagchi@jtcsv.com>
024:
025: package org.apache.xml.serialize;
026:
027: import java.io.UnsupportedEncodingException;
028:
029: import org.w3c.dom.Document;
030: import org.w3c.dom.DocumentType;
031: import org.w3c.dom.Node;
032: import org.w3c.dom.html.HTMLDocument;
033:
034: /**
035: * Specifies an output format to control the serializer. Based on the
036: * XSLT specification for output format, plus additional parameters.
037: * Used to select the suitable serializer and determine how the
038: * document should be formatted on output.
039: * <p>
040: * The two interesting constructors are:
041: * <ul>
042: * <li>{@link #OutputFormat(String,String,boolean)} creates a format
043: * for the specified method (XML, HTML, Text, etc), encoding and indentation
044: * <li>{@link #OutputFormat(Document,String,boolean)} creates a format
045: * compatible with the document type (XML, HTML, Text, etc), encoding and
046: * indentation
047: * </ul>
048: *
049: *
050: * @deprecated This class was deprecated in Xerces 2.9.0. It is recommended
051: * that new applications use the DOM Level 3 LSSerializer or JAXP's Transformation
052: * API for XML (TrAX) for serializing XML and HTML. See the Xerces documentation for
053: * more information.
054: * @version $Revision: 476048 $ $Date: 2006-11-16 23:32:47 -0500 (Thu, 16 Nov 2006) $
055: * @author <a href="mailto:arkin@intalio.com">Assaf Arkin</a>
056: * <a href="mailto:visco@intalio.com">Keith Visco</a>
057: * @see Serializer
058: * @see Method
059: * @see LineSeparator
060: */
061: public class OutputFormat {
062: /**
063: * @deprecated This class was deprecated in Xerces 2.9.0. It is recommended
064: * that new applications use the DOM Level 3 LSSerializer or JAXP's Transformation
065: * API for XML (TrAX) for serializing XML and HTML. See the Xerces documentation for
066: * more information.
067: */
068: public static class DTD {
069:
070: /**
071: * Public identifier for HTML 4.01 (Strict) document type.
072: */
073: public static final String HTMLPublicId = "-//W3C//DTD HTML 4.01//EN";
074:
075: /**
076: * System identifier for HTML 4.01 (Strict) document type.
077: */
078: public static final String HTMLSystemId = "http://www.w3.org/TR/html4/strict.dtd";
079:
080: /**
081: * Public identifier for XHTML 1.0 (Strict) document type.
082: */
083: public static final String XHTMLPublicId = "-//W3C//DTD XHTML 1.0 Strict//EN";
084:
085: /**
086: * System identifier for XHTML 1.0 (Strict) document type.
087: */
088: public static final String XHTMLSystemId = "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";
089:
090: }
091:
092: /**
093: * @deprecated This class was deprecated in Xerces 2.9.0. It is recommended
094: * that new applications use the DOM Level 3 LSSerializer or JAXP's Transformation
095: * API for XML (TrAX) for serializing XML and HTML. See the Xerces documentation for
096: * more information.
097: */
098: public static class Defaults {
099:
100: /**
101: * If indentation is turned on, the default identation
102: * level is 4.
103: *
104: * @see #setIndenting(boolean)
105: */
106: public static final int Indent = 4;
107:
108: /**
109: * The default encoding for Web documents it UTF-8.
110: *
111: * @see #getEncoding()
112: */
113: public static final String Encoding = "UTF-8";
114:
115: /**
116: * The default line width at which to break long lines
117: * when identing. This is set to 72.
118: */
119: public static final int LineWidth = 72;
120:
121: }
122:
123: /**
124: * Holds the output method specified for this document,
125: * or null if no method was specified.
126: */
127: private String _method;
128:
129: /**
130: * Specifies the version of the output method.
131: */
132: private String _version;
133:
134: /**
135: * The indentation level, or zero if no indentation
136: * was requested.
137: */
138: private int _indent = 0;
139:
140: /**
141: * The encoding to use, if an input stream is used.
142: * The default is always UTF-8.
143: */
144: private String _encoding = Defaults.Encoding;
145:
146: /**
147: * The EncodingInfo instance for _encoding.
148: */
149: private EncodingInfo _encodingInfo = null;
150:
151: // whether java names for encodings are permitted
152: private boolean _allowJavaNames = false;
153:
154: /**
155: * The specified media type or null.
156: */
157: private String _mediaType;
158:
159: /**
160: * The specified document type system identifier, or null.
161: */
162: private String _doctypeSystem;
163:
164: /**
165: * The specified document type public identifier, or null.
166: */
167: private String _doctypePublic;
168:
169: /**
170: * True if the XML declaration should be ommited;
171: */
172: private boolean _omitXmlDeclaration = false;
173:
174: /**
175: * True if the DOCTYPE declaration should be ommited;
176: */
177: private boolean _omitDoctype = false;
178:
179: /**
180: * True if comments should be ommited;
181: */
182: private boolean _omitComments = false;
183:
184: /**
185: * True if the document type should be marked as standalone.
186: */
187: private boolean _standalone = false;
188:
189: /**
190: * List of element tag names whose text node children must
191: * be output as CDATA.
192: */
193: private String[] _cdataElements;
194:
195: /**
196: * List of element tag names whose text node children must
197: * be output unescaped.
198: */
199: private String[] _nonEscapingElements;
200:
201: /**
202: * The selected line separator.
203: */
204: private String _lineSeparator = LineSeparator.Web;
205:
206: /**
207: * The line width at which to wrap long lines when indenting.
208: */
209: private int _lineWidth = Defaults.LineWidth;
210:
211: /**
212: * True if spaces should be preserved in elements that do not
213: * specify otherwise, or specify the default behavior.
214: */
215: private boolean _preserve = false;
216: /** If true, an empty string valued attribute is output as "". If false and
217: * and we are using the HTMLSerializer, then only the attribute name is
218: * serialized. Defaults to false for backwards compatibility.
219: */
220: private boolean _preserveEmptyAttributes = false;
221:
222: /**
223: * Constructs a new output format with the default values.
224: */
225: public OutputFormat() {
226: }
227:
228: /**
229: * Constructs a new output format with the default values for
230: * the specified method and encoding. If <tt>indent</tt>
231: * is true, the document will be pretty printed with the default
232: * indentation level and default line wrapping.
233: *
234: * @param method The specified output method
235: * @param encoding The specified encoding
236: * @param indenting True for pretty printing
237: * @see #setEncoding
238: * @see #setIndenting
239: * @see #setMethod
240: */
241: public OutputFormat(String method, String encoding,
242: boolean indenting) {
243: setMethod(method);
244: setEncoding(encoding);
245: setIndenting(indenting);
246: }
247:
248: /**
249: * Constructs a new output format with the proper method,
250: * document type identifiers and media type for the specified
251: * document.
252: *
253: * @param doc The document to output
254: * @see #whichMethod
255: */
256: public OutputFormat(Document doc) {
257: setMethod(whichMethod(doc));
258: setDoctype(whichDoctypePublic(doc), whichDoctypeSystem(doc));
259: setMediaType(whichMediaType(getMethod()));
260: }
261:
262: /**
263: * Constructs a new output format with the proper method,
264: * document type identifiers and media type for the specified
265: * document, and with the specified encoding. If <tt>indent</tt>
266: * is true, the document will be pretty printed with the default
267: * indentation level and default line wrapping.
268: *
269: * @param doc The document to output
270: * @param encoding The specified encoding
271: * @param indenting True for pretty printing
272: * @see #setEncoding
273: * @see #setIndenting
274: * @see #whichMethod
275: */
276: public OutputFormat(Document doc, String encoding, boolean indenting) {
277: this (doc);
278: setEncoding(encoding);
279: setIndenting(indenting);
280: }
281:
282: /**
283: * Returns the method specified for this output format.
284: * Typically the method will be <tt>xml</tt>, <tt>html</tt>
285: * or <tt>text</tt>, but it might be other values.
286: * If no method was specified, null will be returned
287: * and the most suitable method will be determined for
288: * the document by calling {@link #whichMethod}.
289: *
290: * @return The specified output method, or null
291: */
292: public String getMethod() {
293: return _method;
294: }
295:
296: /**
297: * Sets the method for this output format.
298: *
299: * @see #getMethod
300: * @param method The output method, or null
301: */
302: public void setMethod(String method) {
303: _method = method;
304: }
305:
306: /**
307: * Returns the version for this output method.
308: * If no version was specified, will return null
309: * and the default version number will be used.
310: * If the serializer does not support that particular
311: * version, it should default to a supported version.
312: *
313: * @return The specified method version, or null
314: */
315: public String getVersion() {
316: return _version;
317: }
318:
319: /**
320: * Sets the version for this output method.
321: * For XML the value would be "1.0", for HTML
322: * it would be "4.0".
323: *
324: * @see #getVersion
325: * @param version The output method version, or null
326: */
327: public void setVersion(String version) {
328: _version = version;
329: }
330:
331: /**
332: * Returns the indentation specified. If no indentation
333: * was specified, zero is returned and the document
334: * should not be indented.
335: *
336: * @return The indentation or zero
337: * @see #setIndenting
338: */
339: public int getIndent() {
340: return _indent;
341: }
342:
343: /**
344: * Returns true if indentation was specified.
345: */
346: public boolean getIndenting() {
347: return (_indent > 0);
348: }
349:
350: /**
351: * Sets the indentation. The document will not be
352: * indented if the indentation is set to zero.
353: * Calling {@link #setIndenting} will reset this
354: * value to zero (off) or the default (on).
355: *
356: * @param indent The indentation, or zero
357: */
358: public void setIndent(int indent) {
359: if (indent < 0)
360: _indent = 0;
361: else
362: _indent = indent;
363: }
364:
365: /**
366: * Sets the indentation on and off. When set on, the default
367: * indentation level and default line wrapping is used
368: * (see {@link Defaults#Indent} and {@link Defaults#LineWidth}).
369: * To specify a different indentation level or line wrapping,
370: * use {@link #setIndent} and {@link #setLineWidth}.
371: *
372: * @param on True if indentation should be on
373: */
374: public void setIndenting(boolean on) {
375: if (on) {
376: _indent = Defaults.Indent;
377: _lineWidth = Defaults.LineWidth;
378: } else {
379: _indent = 0;
380: _lineWidth = 0;
381: }
382: }
383:
384: /**
385: * Returns the specified encoding. If no encoding was
386: * specified, the default is always "UTF-8".
387: *
388: * @return The encoding
389: */
390: public String getEncoding() {
391: return _encoding;
392: }
393:
394: /**
395: * Sets the encoding for this output method. If no
396: * encoding was specified, the default is always "UTF-8".
397: * Make sure the encoding is compatible with the one
398: * used by the {@link java.io.Writer}.
399: *
400: * @see #getEncoding
401: * @param encoding The encoding, or null
402: */
403: public void setEncoding(String encoding) {
404: _encoding = encoding;
405: _encodingInfo = null;
406: }
407:
408: /**
409: * Sets the encoding for this output method with an <code>EncodingInfo</code>
410: * instance.
411: */
412: public void setEncoding(EncodingInfo encInfo) {
413: _encoding = encInfo.getIANAName();
414: _encodingInfo = encInfo;
415: }
416:
417: /**
418: * Returns an <code>EncodingInfo</code> instance for the encoding.
419: *
420: * @see #setEncoding
421: */
422: public EncodingInfo getEncodingInfo()
423: throws UnsupportedEncodingException {
424: if (_encodingInfo == null)
425: _encodingInfo = Encodings.getEncodingInfo(_encoding,
426: _allowJavaNames);
427: return _encodingInfo;
428: }
429:
430: /**
431: * Sets whether java encoding names are permitted
432: */
433: public void setAllowJavaNames(boolean allow) {
434: _allowJavaNames = allow;
435: }
436:
437: /**
438: * Returns whether java encoding names are permitted
439: */
440: public boolean setAllowJavaNames() {
441: return _allowJavaNames;
442: }
443:
444: /**
445: * Returns the specified media type, or null.
446: * To determine the media type based on the
447: * document type, use {@link #whichMediaType}.
448: *
449: * @return The specified media type, or null
450: */
451: public String getMediaType() {
452: return _mediaType;
453: }
454:
455: /**
456: * Sets the media type.
457: *
458: * @see #getMediaType
459: * @param mediaType The specified media type
460: */
461: public void setMediaType(String mediaType) {
462: _mediaType = mediaType;
463: }
464:
465: /**
466: * Sets the document type public and system identifiers.
467: * Required only if the DOM Document or SAX events do not
468: * specify the document type, and one must be present in
469: * the serialized document. Any document type specified
470: * by the DOM Document or SAX events will override these
471: * values.
472: *
473: * @param publicId The public identifier, or null
474: * @param systemId The system identifier, or null
475: */
476: public void setDoctype(String publicId, String systemId) {
477: _doctypePublic = publicId;
478: _doctypeSystem = systemId;
479: }
480:
481: /**
482: * Returns the specified document type public identifier,
483: * or null.
484: */
485: public String getDoctypePublic() {
486: return _doctypePublic;
487: }
488:
489: /**
490: * Returns the specified document type system identifier,
491: * or null.
492: */
493: public String getDoctypeSystem() {
494: return _doctypeSystem;
495: }
496:
497: /**
498: * Returns true if comments should be ommited.
499: * The default is false.
500: */
501: public boolean getOmitComments() {
502: return _omitComments;
503: }
504:
505: /**
506: * Sets comment omitting on and off.
507: *
508: * @param omit True if comments should be ommited
509: */
510: public void setOmitComments(boolean omit) {
511: _omitComments = omit;
512: }
513:
514: /**
515: * Returns true if the DOCTYPE declaration should
516: * be ommited. The default is false.
517: */
518: public boolean getOmitDocumentType() {
519: return _omitDoctype;
520: }
521:
522: /**
523: * Sets DOCTYPE declaration omitting on and off.
524: *
525: * @param omit True if DOCTYPE declaration should be ommited
526: */
527: public void setOmitDocumentType(boolean omit) {
528: _omitDoctype = omit;
529: }
530:
531: /**
532: * Returns true if the XML document declaration should
533: * be ommited. The default is false.
534: */
535: public boolean getOmitXMLDeclaration() {
536: return _omitXmlDeclaration;
537: }
538:
539: /**
540: * Sets XML declaration omitting on and off.
541: *
542: * @param omit True if XML declaration should be ommited
543: */
544: public void setOmitXMLDeclaration(boolean omit) {
545: _omitXmlDeclaration = omit;
546: }
547:
548: /**
549: * Returns true if the document type is standalone.
550: * The default is false.
551: */
552: public boolean getStandalone() {
553: return _standalone;
554: }
555:
556: /**
557: * Sets document DTD standalone. The public and system
558: * identifiers must be null for the document to be
559: * serialized as standalone.
560: *
561: * @param standalone True if document DTD is standalone
562: */
563: public void setStandalone(boolean standalone) {
564: _standalone = standalone;
565: }
566:
567: /**
568: * Returns a list of all the elements whose text node children
569: * should be output as CDATA, or null if no such elements were
570: * specified.
571: */
572: public String[] getCDataElements() {
573: return _cdataElements;
574: }
575:
576: /**
577: * Returns true if the text node children of the given elements
578: * should be output as CDATA.
579: *
580: * @param tagName The element's tag name
581: * @return True if should serialize as CDATA
582: */
583: public boolean isCDataElement(String tagName) {
584: int i;
585:
586: if (_cdataElements == null)
587: return false;
588: for (i = 0; i < _cdataElements.length; ++i)
589: if (_cdataElements[i].equals(tagName))
590: return true;
591: return false;
592: }
593:
594: /**
595: * Sets the list of elements for which text node children
596: * should be output as CDATA.
597: *
598: * @param cdataElements List of CDATA element tag names
599: */
600: public void setCDataElements(String[] cdataElements) {
601: _cdataElements = cdataElements;
602: }
603:
604: /**
605: * Returns a list of all the elements whose text node children
606: * should be output unescaped (no character references), or null
607: * if no such elements were specified.
608: */
609: public String[] getNonEscapingElements() {
610: return _nonEscapingElements;
611: }
612:
613: /**
614: * Returns true if the text node children of the given elements
615: * should be output unescaped.
616: *
617: * @param tagName The element's tag name
618: * @return True if should serialize unescaped
619: */
620: public boolean isNonEscapingElement(String tagName) {
621: int i;
622:
623: if (_nonEscapingElements == null) {
624: return false;
625: }
626: for (i = 0; i < _nonEscapingElements.length; ++i)
627: if (_nonEscapingElements[i].equals(tagName))
628: return true;
629: return false;
630: }
631:
632: /**
633: * Sets the list of elements for which text node children
634: * should be output unescaped (no character references).
635: *
636: * @param nonEscapingElements List of unescaped element tag names
637: */
638: public void setNonEscapingElements(String[] nonEscapingElements) {
639: _nonEscapingElements = nonEscapingElements;
640: }
641:
642: /**
643: * Returns a specific line separator to use. The default is the
644: * Web line separator (<tt>\n</tt>). A string is returned to
645: * support double codes (CR + LF).
646: *
647: * @return The specified line separator
648: */
649: public String getLineSeparator() {
650: return _lineSeparator;
651: }
652:
653: /**
654: * Sets the line separator. The default is the Web line separator
655: * (<tt>\n</tt>). The machine's line separator can be obtained
656: * from the system property <tt>line.separator</tt>, but is only
657: * useful if the document is edited on machines of the same type.
658: * For general documents, use the Web line separator.
659: *
660: * @param lineSeparator The specified line separator
661: */
662: public void setLineSeparator(String lineSeparator) {
663: if (lineSeparator == null)
664: _lineSeparator = LineSeparator.Web;
665: else
666: _lineSeparator = lineSeparator;
667: }
668:
669: /**
670: * Returns true if the default behavior for this format is to
671: * preserve spaces. All elements that do not specify otherwise
672: * or specify the default behavior will be formatted based on
673: * this rule. All elements that specify space preserving will
674: * always preserve space.
675: */
676: public boolean getPreserveSpace() {
677: return _preserve;
678: }
679:
680: /**
681: * Sets space preserving as the default behavior. The default is
682: * space stripping and all elements that do not specify otherwise
683: * or use the default value will not preserve spaces.
684: *
685: * @param preserve True if spaces should be preserved
686: */
687: public void setPreserveSpace(boolean preserve) {
688: _preserve = preserve;
689: }
690:
691: /**
692: * Return the selected line width for breaking up long lines.
693: * When indenting, and only when indenting, long lines will be
694: * broken at space boundaries based on this line width.
695: * No line wrapping occurs if this value is zero.
696: */
697: public int getLineWidth() {
698: return _lineWidth;
699: }
700:
701: /**
702: * Sets the line width. If zero then no line wrapping will
703: * occur. Calling {@link #setIndenting} will reset this
704: * value to zero (off) or the default (on).
705: *
706: * @param lineWidth The line width to use, zero for default
707: * @see #getLineWidth
708: * @see #setIndenting
709: */
710: public void setLineWidth(int lineWidth) {
711: if (lineWidth <= 0)
712: _lineWidth = 0;
713: else
714: _lineWidth = lineWidth;
715: }
716:
717: /**
718: * Returns the preserveEmptyAttribute flag. If flag is false, then'
719: * attributes with empty string values are output as the attribute
720: * name only (in HTML mode).
721: * @return preserve the preserve flag
722: */
723: public boolean getPreserveEmptyAttributes() {
724: return _preserveEmptyAttributes;
725: }
726:
727: /**
728: * Sets the preserveEmptyAttribute flag. If flag is false, then'
729: * attributes with empty string values are output as the attribute
730: * name only (in HTML mode).
731: * @param preserve the preserve flag
732: */
733: public void setPreserveEmptyAttributes(boolean preserve) {
734: _preserveEmptyAttributes = preserve;
735: }
736:
737: /**
738: * Returns the last printable character based on the selected
739: * encoding. Control characters and non-printable characters
740: * are always printed as character references.
741: */
742: public char getLastPrintable() {
743: if (getEncoding() != null
744: && (getEncoding().equalsIgnoreCase("ASCII"))) {
745: return 0xFF;
746: }
747: return 0xFFFF;
748: }
749:
750: /**
751: * Determine the output method for the specified document.
752: * If the document is an instance of {@link org.w3c.dom.html.HTMLDocument}
753: * then the method is said to be <tt>html</tt>. If the root
754: * element is 'html' and all text nodes preceding the root
755: * element are all whitespace, then the method is said to be
756: * <tt>html</tt>. Otherwise the method is <tt>xml</tt>.
757: *
758: * @param doc The document to check
759: * @return The suitable method
760: */
761: public static String whichMethod(Document doc) {
762: Node node;
763: String value;
764: int i;
765:
766: // If document is derived from HTMLDocument then the default
767: // method is html.
768: if (doc instanceof HTMLDocument)
769: return Method.HTML;
770:
771: // Lookup the root element and the text nodes preceding it.
772: // If root element is html and all text nodes contain whitespace
773: // only, the method is html.
774:
775: // FIXME (SM) should we care about namespaces here?
776:
777: node = doc.getFirstChild();
778: while (node != null) {
779: // If the root element is html, the method is html.
780: if (node.getNodeType() == Node.ELEMENT_NODE) {
781: if (node.getNodeName().equalsIgnoreCase("html")) {
782: return Method.HTML;
783: } else if (node.getNodeName().equalsIgnoreCase("root")) {
784: return Method.FOP;
785: } else {
786: return Method.XML;
787: }
788: } else if (node.getNodeType() == Node.TEXT_NODE) {
789: // If a text node preceding the root element contains
790: // only whitespace, this might be html, otherwise it's
791: // definitely xml.
792: value = node.getNodeValue();
793: for (i = 0; i < value.length(); ++i)
794: if (value.charAt(i) != 0x20
795: && value.charAt(i) != 0x0A
796: && value.charAt(i) != 0x09
797: && value.charAt(i) != 0x0D)
798: return Method.XML;
799: }
800: node = node.getNextSibling();
801: }
802: // Anything else, the method is xml.
803: return Method.XML;
804: }
805:
806: /**
807: * Returns the document type public identifier
808: * specified for this document, or null.
809: */
810: public static String whichDoctypePublic(Document doc) {
811: DocumentType doctype;
812:
813: /* DOM Level 2 was introduced into the code base*/
814: doctype = doc.getDoctype();
815: if (doctype != null) {
816: // Note on catch: DOM Level 1 does not specify this method
817: // and the code will throw a NoSuchMethodError
818: try {
819: return doctype.getPublicId();
820: } catch (Error except) {
821: }
822: }
823:
824: if (doc instanceof HTMLDocument)
825: return DTD.XHTMLPublicId;
826: return null;
827: }
828:
829: /**
830: * Returns the document type system identifier
831: * specified for this document, or null.
832: */
833: public static String whichDoctypeSystem(Document doc) {
834: DocumentType doctype;
835:
836: /* DOM Level 2 was introduced into the code base*/
837: doctype = doc.getDoctype();
838: if (doctype != null) {
839: // Note on catch: DOM Level 1 does not specify this method
840: // and the code will throw a NoSuchMethodError
841: try {
842: return doctype.getSystemId();
843: } catch (Error except) {
844: }
845: }
846:
847: if (doc instanceof HTMLDocument)
848: return DTD.XHTMLSystemId;
849: return null;
850: }
851:
852: /**
853: * Returns the suitable media format for a document
854: * output with the specified method.
855: */
856: public static String whichMediaType(String method) {
857: if (method.equalsIgnoreCase(Method.XML))
858: return "text/xml";
859: if (method.equalsIgnoreCase(Method.HTML))
860: return "text/html";
861: if (method.equalsIgnoreCase(Method.XHTML))
862: return "text/html";
863: if (method.equalsIgnoreCase(Method.TEXT))
864: return "text/plain";
865: if (method.equalsIgnoreCase(Method.FOP))
866: return "application/pdf";
867: return null;
868: }
869:
870: }
|