|
26 | 26 |
|
27 | 27 | import { jsPDF } from "../jspdf.js"; |
28 | 28 |
|
29 | | -/** |
30 | | - * @name xmp_metadata |
31 | | - * @module |
32 | | - */ |
33 | | -(function(jsPDFAPI) { |
34 | | - "use strict"; |
| 29 | +function postPutResources() { |
| 30 | + const metadata = this.internal.__metadata__.metadata; |
| 31 | + const utf8Metadata = unescape(encodeURIComponent(metadata)); |
35 | 32 |
|
36 | | - var postPutResources = function() { |
37 | | - var xmpmeta_beginning = '<x:xmpmeta xmlns:x="adobe:ns:meta/">'; |
38 | | - var rdf_beginning = |
| 33 | + const rawXml = this.internal.__metadata__.rawXml; |
| 34 | + let content; |
| 35 | + if (rawXml) { |
| 36 | + content = utf8Metadata; |
| 37 | + } else { |
| 38 | + const xmpmetaBeginning = '<x:xmpmeta xmlns:x="adobe:ns:meta/">'; |
| 39 | + const rdfBeginning = |
39 | 40 | '<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about="" xmlns:jspdf="' + |
40 | | - this.internal.__metadata__.namespaceuri + |
| 41 | + this.internal.__metadata__.namespaceUri + |
41 | 42 | '"><jspdf:metadata>'; |
42 | | - var rdf_ending = "</jspdf:metadata></rdf:Description></rdf:RDF>"; |
43 | | - var xmpmeta_ending = "</x:xmpmeta>"; |
44 | | - var utf8_xmpmeta_beginning = unescape( |
45 | | - encodeURIComponent(xmpmeta_beginning) |
46 | | - ); |
47 | | - var utf8_rdf_beginning = unescape(encodeURIComponent(rdf_beginning)); |
48 | | - var utf8_metadata = unescape( |
49 | | - encodeURIComponent(this.internal.__metadata__.metadata) |
50 | | - ); |
51 | | - var utf8_rdf_ending = unescape(encodeURIComponent(rdf_ending)); |
52 | | - var utf8_xmpmeta_ending = unescape(encodeURIComponent(xmpmeta_ending)); |
| 43 | + const rdfEnding = "</jspdf:metadata></rdf:Description></rdf:RDF>"; |
| 44 | + const xmpmetaEnding = "</x:xmpmeta>"; |
53 | 45 |
|
54 | | - var total_len = |
55 | | - utf8_rdf_beginning.length + |
56 | | - utf8_metadata.length + |
57 | | - utf8_rdf_ending.length + |
58 | | - utf8_xmpmeta_beginning.length + |
59 | | - utf8_xmpmeta_ending.length; |
| 46 | + content = |
| 47 | + xmpmetaBeginning + |
| 48 | + rdfBeginning + |
| 49 | + escapeXml(utf8Metadata) + |
| 50 | + rdfEnding + |
| 51 | + xmpmetaEnding; |
| 52 | + } |
60 | 53 |
|
61 | | - this.internal.__metadata__.metadata_object_number = this.internal.newObject(); |
62 | | - this.internal.write( |
63 | | - "<< /Type /Metadata /Subtype /XML /Length " + total_len + " >>" |
64 | | - ); |
65 | | - this.internal.write("stream"); |
| 54 | + this.internal.__metadata__.metadataObjectNumber = this.internal.newObject(); |
| 55 | + this.internal.write( |
| 56 | + "<< /Type /Metadata /Subtype /XML /Length " + content.length + " >>" |
| 57 | + ); |
| 58 | + this.internal.write("stream"); |
| 59 | + this.internal.write(content); |
| 60 | + this.internal.write("endstream"); |
| 61 | + this.internal.write("endobj"); |
| 62 | +} |
| 63 | + |
| 64 | +function putCatalog() { |
| 65 | + if (this.internal.__metadata__.metadataObjectNumber) { |
66 | 66 | this.internal.write( |
67 | | - utf8_xmpmeta_beginning + |
68 | | - utf8_rdf_beginning + |
69 | | - utf8_metadata + |
70 | | - utf8_rdf_ending + |
71 | | - utf8_xmpmeta_ending |
| 67 | + "/Metadata " + this.internal.__metadata__.metadataObjectNumber + " 0 R" |
72 | 68 | ); |
73 | | - this.internal.write("endstream"); |
74 | | - this.internal.write("endobj"); |
75 | | - }; |
| 69 | + } |
| 70 | +} |
76 | 71 |
|
77 | | - var putCatalog = function() { |
78 | | - if (this.internal.__metadata__.metadata_object_number) { |
79 | | - this.internal.write( |
80 | | - "/Metadata " + |
81 | | - this.internal.__metadata__.metadata_object_number + |
82 | | - " 0 R" |
83 | | - ); |
84 | | - } |
85 | | - }; |
| 72 | +function escapeXml(str) { |
| 73 | + return str |
| 74 | + .replace(/&/g, "&") |
| 75 | + .replace(/</g, "<") |
| 76 | + .replace(/>/g, ">") |
| 77 | + .replace(/"/g, """) |
| 78 | + .replace(/'/g, "'"); |
| 79 | +} |
86 | 80 |
|
87 | | - /** |
88 | | - * Adds XMP formatted metadata to PDF |
89 | | - * |
90 | | - * @name addMetadata |
91 | | - * @function |
92 | | - * @param {String} metadata The actual metadata to be added. The metadata shall be stored as XMP simple value. Note that if the metadata string contains XML markup characters "<", ">" or "&", those characters should be written using XML entities. |
93 | | - * @param {String} namespaceuri Sets the namespace URI for the metadata. Last character should be slash or hash. |
94 | | - * @returns {jsPDF} jsPDF-instance |
95 | | - */ |
96 | | - jsPDFAPI.addMetadata = function(metadata, namespaceuri) { |
97 | | - if (typeof this.internal.__metadata__ === "undefined") { |
98 | | - this.internal.__metadata__ = { |
99 | | - metadata: metadata, |
100 | | - namespaceuri: namespaceuri || "http://jspdf.default.namespaceuri/" |
101 | | - }; |
102 | | - this.internal.events.subscribe("putCatalog", putCatalog); |
| 81 | +/** |
| 82 | + * Adds XMP formatted metadata to PDF. |
| 83 | + * |
| 84 | + * WARNING: Passing raw XML is potentially insecure! Always sanitize user input before passing it to this function! |
| 85 | + * @name addMetadata |
| 86 | + * @function |
| 87 | + * @param {string} metadata The actual metadata to be added. The interpretation of this parameter depends on the |
| 88 | + * second parameter. |
| 89 | + * @param {boolean|string|undefined} rawXmlOrNamespaceUri If a string is passed it sets the namespace URI for the |
| 90 | + * metadata and the metadata shall be stored as XMP simple value. The last character should be a slash or hash. |
| 91 | + * |
| 92 | + * If this argument is omitted, a string is passed, or `false` is passed, the `metadata` argument will be |
| 93 | + * XML-escaped before including it in the PDF. |
| 94 | + * |
| 95 | + * If `true` is passed, the `metadata` argument will be interpreted as raw XMP and will be included verbatim |
| 96 | + * in the PDF. The passed metadata must be complete (including surrounding `xmpmeta` and `RDF` tags). |
| 97 | + * @returns {jsPDF} jsPDF-instance |
| 98 | + */ |
| 99 | +jsPDF.API.addMetadata = function(metadata, rawXmlOrNamespaceUri) { |
| 100 | + if (typeof this.internal.__metadata__ === "undefined") { |
| 101 | + this.internal.__metadata__ = { |
| 102 | + metadata: metadata, |
| 103 | + namespaceUri: |
| 104 | + rawXmlOrNamespaceUri ?? "http://jspdf.default.namespaceuri/", |
| 105 | + rawXml: |
| 106 | + typeof rawXmlOrNamespaceUri === "boolean" ? rawXmlOrNamespaceUri : false |
| 107 | + }; |
| 108 | + this.internal.events.subscribe("putCatalog", putCatalog); |
103 | 109 |
|
104 | | - this.internal.events.subscribe("postPutResources", postPutResources); |
105 | | - } |
106 | | - return this; |
107 | | - }; |
108 | | -})(jsPDF.API); |
| 110 | + this.internal.events.subscribe("postPutResources", postPutResources); |
| 111 | + } |
| 112 | + return this; |
| 113 | +}; |
0 commit comments