飞天AI数字人展会页面
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

399 lines
15 KiB

6 months ago
  1. /**
  2. * PDFObject v2.3.0
  3. * https://github.com/pipwerks/PDFObject
  4. * @license
  5. * Copyright (c) 2008-2024 Philip Hutchison
  6. * MIT-style license: http://pipwerks.mit-license.org/
  7. * UMD module pattern from https://github.com/umdjs/umd/blob/master/templates/returnExports.js
  8. */
  9. (function (root, factory) {
  10. if (typeof define === "function" && define.amd) {
  11. // AMD. Register as an anonymous module.
  12. define([], factory);
  13. } else if (typeof module === "object" && module.exports) {
  14. // Node. Does not work with strict CommonJS, but
  15. // only CommonJS-like environments that support module.exports,
  16. // like Node.
  17. module.exports = factory();
  18. } else {
  19. // Browser globals (root is window)
  20. root.PDFObject = factory();
  21. }
  22. }(this, function () {
  23. "use strict";
  24. //PDFObject is designed for client-side (browsers), not server-side (node)
  25. //Will choke on undefined navigator and window vars when run on server
  26. //Return boolean false and exit function when running server-side
  27. if(typeof window === "undefined" || window.navigator === undefined || window.navigator.userAgent === undefined){ return false; }
  28. let pdfobjectversion = "2.3.0";
  29. let win = window;
  30. let nav = win.navigator;
  31. let ua = nav.userAgent;
  32. let suppressConsole = false;
  33. //Fallback validation when navigator.pdfViewerEnabled is not supported
  34. let isModernBrowser = function (){
  35. /*
  36. userAgent sniffing is not the ideal path, but most browsers revoked the ability to check navigator.mimeTypes
  37. for security purposes. As of 2023, browsers have begun implementing navigator.pdfViewerEnabled, but older versions
  38. do not have navigator.pdfViewerEnabled or the ability to check navigator.mimeTypes. We're left with basic browser
  39. sniffing and assumptions of PDF support based on browser vendor.
  40. */
  41. //Chromium has provided native PDF support since 2011.
  42. //Most modern browsers use Chromium under the hood: Google Chrome, Microsoft Edge, Opera, Brave, Vivaldi, Arc, and more.
  43. //Chromium uses the PDFium rendering engine, which is based on Foxit's PDF rendering engine.
  44. //Note that MS Edge opts to use a different PDF rendering engine. As of 2024, Edge uses a version of Adobe's Reader
  45. let isChromium = (win.chrome !== undefined);
  46. //Safari on macOS has provided native PDF support since 2009.
  47. //This code snippet also detects the DuckDuckGo browser, which uses Safari/Webkit under the hood.
  48. let isSafari = (win.safari !== undefined || (nav.vendor !== undefined && /Apple/.test(nav.vendor) && /Safari/.test(ua)));
  49. //Firefox has provided PDF support via PDFJS since 2013.
  50. let isFirefox = (win.Mozilla !== undefined || /irefox/.test(ua));
  51. return isChromium || isSafari || isFirefox;
  52. };
  53. /*
  54. Special handling for Internet Explorer 11.
  55. Check for ActiveX support, then whether "AcroPDF.PDF" or "PDF.PdfCtrl" are valid.
  56. IE11 uses ActiveX for Adobe Reader and other PDF plugins, but window.ActiveXObject will evaluate to false.
  57. ("ActiveXObject" in window) evaluates to true.
  58. MS Edge does not support ActiveX so this test will evaluate false for MS Edge.
  59. */
  60. let validateAX = function (type){
  61. var ax = null;
  62. try {
  63. ax = new ActiveXObject(type);
  64. } catch (e) {
  65. //ensure ax remains null when ActiveXObject attempt fails
  66. ax = null;
  67. }
  68. return !!ax; //convert resulting object to boolean
  69. };
  70. let hasActiveXPDFPlugin = function (){ return ("ActiveXObject" in win) && (validateAX("AcroPDF.PDF") || validateAX("PDF.PdfCtrl")) };
  71. let checkSupport = function (){
  72. //Safari on iPadOS doesn't report as 'mobile' when requesting desktop site, yet still fails to embed PDFs
  73. let isSafariIOSDesktopMode = (nav.platform !== undefined && nav.platform === "MacIntel" && nav.maxTouchPoints !== undefined && nav.maxTouchPoints > 1);
  74. let isMobileDevice = (isSafariIOSDesktopMode || /Mobi|Tablet|Android|iPad|iPhone/.test(ua));
  75. //As of June 2023, no mobile browsers properly support inline PDFs. If mobile, just say no.
  76. if(isMobileDevice){ return false; }
  77. //Modern browsers began supporting navigator.pdfViewerEnabled in late 2022 and early 2023.
  78. let supportsPDFVE = (typeof nav.pdfViewerEnabled === "boolean");
  79. //If browser supports nav.pdfViewerEnabled and is explicitly saying PDFs are NOT supported (e.g. PDFJS disabled by user in Firefox), respect it.
  80. if(supportsPDFVE && !nav.pdfViewerEnabled){ return false; }
  81. return (supportsPDFVE && nav.pdfViewerEnabled) || isModernBrowser() || hasActiveXPDFPlugin();
  82. };
  83. //Determines whether PDF support is available
  84. let supportsPDFs = checkSupport();
  85. //Create a fragment identifier for using PDF Open parameters when embedding PDF
  86. let buildURLFragmentString = function(pdfParams){
  87. let string = "";
  88. let prop;
  89. let paramArray = [];
  90. let fdf = "";
  91. //The comment, viewrect, and highlight parameters require page to be set first.
  92. //Check to ensure page is used if comment, viewrect, or highlight are specified
  93. if(pdfParams.comment || pdfParams.viewrect || pdfParams.highlight){
  94. if(!pdfParams.page){
  95. //If page is not set, use the first page
  96. pdfParams.page = 1;
  97. //Inform user that page needs to be set properly
  98. embedError("The comment, viewrect, and highlight parameters require a page parameter, but none was specified. Defaulting to page 1.");
  99. }
  100. }
  101. //Let's go ahead and ensure page is always the first parameter.
  102. if(pdfParams.page){
  103. paramArray.push("page=" + encodeURIComponent(pdfParams.page));
  104. delete pdfParams.page;
  105. }
  106. //FDF needs to be the last parameter in the string
  107. if(pdfParams.fdf){
  108. fdf = pdfParams.fdf;
  109. delete pdfParams.fdf;
  110. }
  111. //Add all other parameters, as needed
  112. if(pdfParams){
  113. for (prop in pdfParams) {
  114. if (pdfParams.hasOwnProperty(prop)) {
  115. paramArray.push(encodeURIComponent(prop) + "=" + encodeURIComponent(pdfParams[prop]));
  116. }
  117. }
  118. //Add fdf as the last parameter, if needed
  119. if(fdf){
  120. paramArray.push("fdf=" + encodeURIComponent(fdf));
  121. }
  122. //Join all parameters in the array into a string
  123. string = paramArray.join("&");
  124. //The string will be empty if no PDF Parameters were provided
  125. //Only prepend the hash if the string is not empty
  126. if(string){
  127. string = "#" + string;
  128. }
  129. }
  130. return string;
  131. };
  132. let embedError = function (msg){
  133. if(!suppressConsole){
  134. console.log("[PDFObject]", msg);
  135. }
  136. return false;
  137. };
  138. let emptyNodeContents = function (node){
  139. while(node.firstChild){
  140. node.removeChild(node.firstChild);
  141. }
  142. };
  143. let getTargetElement = function (targetSelector){
  144. //Default to body for full-browser PDF
  145. let targetNode = document.body;
  146. //If a targetSelector is specified, check to see whether
  147. //it's passing a selector, jQuery object, or an HTML element
  148. if(typeof targetSelector === "string"){
  149. //Is CSS selector
  150. targetNode = document.querySelector(targetSelector);
  151. } else if (win.jQuery !== undefined && targetSelector instanceof jQuery && targetSelector.length) {
  152. //Is jQuery element. Extract HTML node
  153. targetNode = targetSelector.get(0);
  154. } else if (targetSelector.nodeType !== undefined && targetSelector.nodeType === 1){
  155. //Is HTML element
  156. targetNode = targetSelector;
  157. }
  158. return targetNode;
  159. };
  160. let convertBase64ToDownloadableLink = function (b64, filename, targetNode, fallbackHTML) {
  161. //IE-11 safe version. More verbose than modern fetch()
  162. if (window.Blob && window.URL && window.URL.createObjectURL) {
  163. var xhr = new XMLHttpRequest();
  164. xhr.open('GET', b64, true);
  165. xhr.responseType = 'blob';
  166. xhr.onload = function() {
  167. if (xhr.status === 200) {
  168. var blob = xhr.response;
  169. var link = document.createElement('a');
  170. link.innerText = "Download PDF";
  171. link.href = URL.createObjectURL(blob);
  172. link.setAttribute('download', filename);
  173. targetNode.innerHTML = fallbackHTML.replace(/\[pdflink\]/g, link.outerHTML);
  174. }
  175. };
  176. xhr.send();
  177. }
  178. };
  179. let generatePDFObjectMarkup = function (embedType, targetNode, url, pdfOpenFragment, width, height, id, title, omitInlineStyles, customAttribute, PDFJS_URL){
  180. //Ensure target element is empty first
  181. emptyNodeContents(targetNode);
  182. let source = url;
  183. if(embedType === "pdfjs"){
  184. //If PDFJS_URL already contains a ?, assume querystring is in place, and use an ampersand to append PDFJS's file parameter
  185. let connector = (PDFJS_URL.indexOf("?") !== -1) ? "&" : "?";
  186. source = PDFJS_URL + connector + "file=" + encodeURIComponent(url) + pdfOpenFragment;
  187. } else {
  188. source += pdfOpenFragment;
  189. }
  190. let el = document.createElement("iframe");
  191. el.className = "pdfobject";
  192. el.type = "application/pdf";
  193. el.title = title;
  194. el.src = source;
  195. el.allow = "fullscreen";
  196. el.frameborder = "0";
  197. if(id){ el.id = id; }
  198. if(!omitInlineStyles){
  199. let style = "border: none;";
  200. if(targetNode !== document.body){
  201. //assign width and height to target node
  202. style += "width: " + width + "; height: " + height + ";";
  203. } else {
  204. //this is a full-page embed, use CSS to fill the viewport
  205. style += "position: absolute; top: 0; right: 0; bottom: 0; left: 0; width: 100%; height: 100%;";
  206. }
  207. el.style.cssText = style;
  208. }
  209. //Allow developer to insert custom attribute on iframe element, but ensure it does not conflict with attributes used by PDFObject
  210. let reservedTokens = ["className", "type", "title", "src", "style", "id", "allow", "frameborder"];
  211. if(customAttribute && customAttribute.key && reservedTokens.indexOf(customAttribute.key) === -1){
  212. el.setAttribute(customAttribute.key, (typeof customAttribute.value !== "undefined") ? customAttribute.value : "");
  213. }
  214. targetNode.classList.add("pdfobject-container");
  215. targetNode.appendChild(el);
  216. return targetNode.getElementsByTagName("iframe")[0];
  217. };
  218. let embed = function(url, targetSelector, options){
  219. //If targetSelector is not defined, convert to boolean
  220. let selector = targetSelector || false;
  221. //Ensure options object is not undefined -- enables easier error checking below
  222. let opt = options || {};
  223. //Get passed options, or set reasonable defaults
  224. suppressConsole = (typeof opt.suppressConsole === "boolean") ? opt.suppressConsole : false;
  225. let id = (typeof opt.id === "string") ? opt.id : "";
  226. let page = opt.page || false;
  227. let pdfOpenParams = opt.pdfOpenParams || {};
  228. let fallbackLink = (typeof opt.fallbackLink === "string" || typeof opt.fallbackLink === "boolean") ? opt.fallbackLink : true;
  229. let width = opt.width || "100%";
  230. let height = opt.height || "100%";
  231. let title = opt.title || "Embedded PDF";
  232. let forcePDFJS = (typeof opt.forcePDFJS === "boolean") ? opt.forcePDFJS : false;
  233. let omitInlineStyles = (typeof opt.omitInlineStyles === "boolean") ? opt.omitInlineStyles : false;
  234. let PDFJS_URL = opt.PDFJS_URL || false;
  235. let targetNode = getTargetElement(selector);
  236. let pdfOpenFragment = "";
  237. let customAttribute = opt.customAttribute || {};
  238. let fallbackHTML_default = "<p>This browser does not support inline PDFs. Please download the PDF to view it: [pdflink]</p>";
  239. //Ensure URL is available. If not, exit now.
  240. if(typeof url !== "string"){ return embedError("URL is not valid"); }
  241. //If target element is specified but is not valid, exit without doing anything
  242. if(!targetNode){ return embedError("Target element cannot be determined"); }
  243. //page option overrides pdfOpenParams, if found
  244. if(page){ pdfOpenParams.page = page; }
  245. //Stringify optional Adobe params for opening document (as fragment identifier)
  246. pdfOpenFragment = buildURLFragmentString(pdfOpenParams);
  247. // --== Do the dance: Embed attempt #1 ==--
  248. //If the forcePDFJS option is invoked, skip everything else and embed as directed
  249. if(forcePDFJS && PDFJS_URL){
  250. return generatePDFObjectMarkup("pdfjs", targetNode, url, pdfOpenFragment, width, height, id, title, omitInlineStyles, customAttribute, PDFJS_URL);
  251. }
  252. // --== Embed attempt #2 ==--
  253. //Embed PDF if support is detected, or if this is a relatively modern browser
  254. if(supportsPDFs){
  255. return generatePDFObjectMarkup("iframe", targetNode, url, pdfOpenFragment, width, height, id, title, omitInlineStyles, customAttribute);
  256. }
  257. // --== Embed attempt #3 ==--
  258. //If everything else has failed and a PDFJS fallback is provided, try to use it
  259. if(PDFJS_URL){
  260. return generatePDFObjectMarkup("pdfjs", targetNode, url, pdfOpenFragment, width, height, id, title, omitInlineStyles, customAttribute, PDFJS_URL);
  261. }
  262. // --== PDF embed not supported! Use fallback ==--
  263. //Display the fallback link if available
  264. if(fallbackLink){
  265. //If a custom fallback has been provided, handle it now
  266. if(typeof fallbackLink === "string"){
  267. //Ensure [url] is set in custom fallback
  268. targetNode.innerHTML = fallbackLink.replace(/\[url\]/g, url);
  269. } else {
  270. //If the PDF is a base64 string, convert it to a downloadable link
  271. if(url.indexOf("data:application/pdf;base64") !== -1){
  272. //Asynchronously append the link to the targetNode
  273. convertBase64ToDownloadableLink(url, "file.pdf", targetNode, fallbackHTML_default);
  274. } else {
  275. //Use default fallback link
  276. let link = "<a href='" + url + "'>Download PDF</a>";
  277. targetNode.innerHTML = fallbackHTML_default.replace(/\[pdflink\]/g, link);
  278. }
  279. }
  280. }
  281. return embedError("This browser does not support embedded PDFs");
  282. };
  283. return {
  284. embed: function (a,b,c){ return embed(a,b,c); },
  285. pdfobjectversion: (function () { return pdfobjectversion; })(),
  286. supportsPDFs: (function (){ return supportsPDFs; })()
  287. };
  288. }));