export.js 29 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247
  1. var mxIsElectron = navigator.userAgent != null &&
  2. navigator.userAgent.toLowerCase().indexOf(' electron/') > -1 &&
  3. navigator.userAgent.indexOf(' draw.io/') > -1;
  4. var GOOGLE_APPS_MAX_AREA = 25000000;
  5. var GOOGLE_SHEET_MAX_AREA = 1000000; // The maximum number of pixels is 1 million.
  6. /**
  7. * Adds meta tag to the page.
  8. */
  9. function mxmeta(content, httpEquiv)
  10. {
  11. try
  12. {
  13. var s = document.createElement('meta');
  14. s.setAttribute('content', content);
  15. s.setAttribute('http-equiv', httpEquiv);
  16. var t = document.getElementsByTagName('meta')[0];
  17. t.parentNode.insertBefore(s, t);
  18. }
  19. catch (e)
  20. {
  21. // ignore
  22. }
  23. };
  24. function mxscript(src, onLoad)
  25. {
  26. var s = document.createElement('script');
  27. s.setAttribute('type', 'text/javascript');
  28. s.setAttribute('src', src);
  29. if (onLoad != null)
  30. {
  31. var r = false;
  32. s.onload = s.onreadystatechange = function()
  33. {
  34. if (!r && (!this.readyState || this.readyState == 'complete'))
  35. {
  36. r = true;
  37. onLoad();
  38. }
  39. };
  40. }
  41. var t = document.getElementsByTagName('script')[0];
  42. if (t != null)
  43. {
  44. t.parentNode.insertBefore(s, t);
  45. }
  46. };
  47. if (mxIsElectron)
  48. {
  49. mxmeta('default-src \'self\'; script-src \'self\'; connect-src \'self\' https://*.draw.io https://*.diagrams.net https://fonts.googleapis.com https://fonts.gstatic.com; img-src * data:; media-src *; font-src *; frame-src \'none\'; style-src \'self\' \'unsafe-inline\' https://fonts.googleapis.com; base-uri \'none\';child-src \'self\';object-src \'none\';', 'Content-Security-Policy');
  50. // We can't use eval in Electron because of CSP, so load all shapes and disable eval
  51. mxscript('js/stencils.min.js', function()
  52. {
  53. mxscript('js/shapes-14-6-5.min.js', function()
  54. {
  55. if (window.pendingRequest != null)
  56. {
  57. render(window.pendingRequest);
  58. }
  59. window.shapesLoaded = true;
  60. });
  61. });
  62. // Disables eval for JS (uses shapes-14-6-5.min.js)
  63. mxStencilRegistry.allowEval = false;
  64. }
  65. // TODO Add support for loading math from a local folder
  66. Editor.initMath((remoteMath? 'https://app.diagrams.net/' : '') + 'math/es5/startup.js');
  67. // Marks individual font and CSS URLs as preloaded
  68. var fontPreload = {};
  69. var cssPreload = {};
  70. function render(data)
  71. {
  72. if (data.csv != null)
  73. {
  74. // CSV loads orgChart asynchronously and needs mxscript
  75. window.mxscript = function (src, onLoad, id)
  76. {
  77. var s = document.createElement('script');
  78. s.setAttribute('type', 'text/javascript');
  79. s.setAttribute('defer', 'true');
  80. s.setAttribute('src', src);
  81. if (id != null)
  82. {
  83. s.setAttribute('id', id);
  84. }
  85. if (onLoad != null)
  86. {
  87. var r = false;
  88. s.onload = s.onreadystatechange = function()
  89. {
  90. if (!r && (!this.readyState || this.readyState == 'complete'))
  91. {
  92. r = true;
  93. onLoad();
  94. }
  95. };
  96. }
  97. var t = document.getElementsByTagName('script')[0];
  98. if (t != null)
  99. {
  100. t.parentNode.insertBefore(s, t);
  101. }
  102. };
  103. var editorUi = new HeadlessEditorUi();
  104. editorUi.importCsv(data.csv, function()
  105. {
  106. data.xml = mxUtils.getXml(editorUi.editor.getGraphXml());
  107. delete data.csv;
  108. render(data);
  109. });
  110. return;
  111. }
  112. var autoScale = false;
  113. if (data.scale == 'auto')
  114. {
  115. autoScale = true;
  116. data.scale = 1;
  117. }
  118. document.body.innerText = '';
  119. var container = document.createElement('div');
  120. container.id = 'graph';
  121. container.style.width = '100%';
  122. container.style.height = '100%';
  123. document.body.appendChild(container);
  124. var graph = new Graph(container);
  125. graph.enableFlowAnimation = true;
  126. data.border = parseInt(data.border) || 0;
  127. data.w = parseFloat(data.w) || 0;
  128. data.h = parseFloat(data.h) || 0;
  129. data.scale = parseFloat(data.scale) || 1;
  130. var extras = null;
  131. try
  132. {
  133. extras = JSON.parse(data.extras);
  134. }
  135. catch (e)
  136. {
  137. try
  138. {
  139. extras = JSON.parse(decodeURIComponent(data.extras));
  140. }
  141. catch (e)
  142. {
  143. // ignore
  144. }
  145. }
  146. var gridColor = null;
  147. if (extras != null && extras.grid != null)
  148. {
  149. graph.gridSize = extras.grid.size;
  150. graph.view.gridSteps = extras.grid.steps;
  151. gridColor = extras.grid.color;
  152. }
  153. if (extras != null && extras.diagramLanguage != null)
  154. {
  155. Graph.diagramLanguage = extras.diagramLanguage;
  156. Graph.translateDiagram = true;
  157. }
  158. if (extras != null && extras.globalVars != null && extras.globalVars.filename != null)
  159. {
  160. document.title = extras.globalVars.filename + '.pdf';
  161. }
  162. // Overrides graph bounds to include background images
  163. var graphGetGraphBounds = graph.getGraphBounds;
  164. graph.getGraphBounds = function()
  165. {
  166. var bounds = graphGetGraphBounds.apply(this, arguments);
  167. var img = this.backgroundImage;
  168. if (img != null && img.width != null && img.height != null)
  169. {
  170. var t = this.view.translate;
  171. var s = this.view.scale;
  172. bounds = mxRectangle.fromRectangle(bounds);
  173. bounds.add(new mxRectangle(
  174. (t.x + img.x) * s, (t.y + img.y) * s,
  175. img.width * s, img.height * s));
  176. }
  177. return bounds;
  178. };
  179. //PNG+XML format
  180. if (data.xml.substring(0, 5) == 'iVBOR' || (extras != null && extras.isPng))
  181. {
  182. data.xml = Editor.extractGraphModelFromPng('data:image/png;base64,' + data.xml);
  183. }
  184. //IE11 sends incorrect xml
  185. if (data.xml.substring(0, 11) == '<#document>')
  186. {
  187. data.xml = data.xml.substring(11, data.xml.length - 12);
  188. }
  189. // Parses XML
  190. var doc = mxUtils.parseXml(data.xml);
  191. var node = Editor.extractGraphModel(doc.documentElement, true);
  192. if (node == null)
  193. {
  194. //Electron pdf export
  195. try
  196. {
  197. electron.sendMessage('render-finished', null);
  198. }
  199. catch(e)
  200. {
  201. console.log(e);
  202. }
  203. return graph;
  204. }
  205. var xmlDoc = node.ownerDocument;
  206. var origXmlDoc = xmlDoc;
  207. var diagrams = null;
  208. var from = 0;
  209. function getFileXml(uncompressed)
  210. {
  211. var xml = mxUtils.getXml(origXmlDoc);
  212. var editorUi = new HeadlessEditorUi();
  213. var tmpFile = new LocalFile(editorUi, xml);
  214. editorUi.setCurrentFile(tmpFile);
  215. editorUi.setFileData(xml);
  216. return editorUi.createFileData(editorUi.getXmlFileData(null, null, uncompressed));
  217. };
  218. if (mxIsElectron && data.format == 'xml')
  219. {
  220. try
  221. {
  222. electron.sendMessage('xml-data', getFileXml(data.uncompressed));
  223. }
  224. catch(e)
  225. {
  226. electron.sendMessage('xml-data-error');
  227. }
  228. return;
  229. }
  230. // Handles mxfile
  231. if (xmlDoc.documentElement.nodeName == 'mxfile')
  232. {
  233. diagrams = xmlDoc.documentElement.getElementsByTagName('diagram');
  234. }
  235. //Add global variables to graph
  236. if (extras != null && extras.globalVars != null)
  237. {
  238. graph.globalVars = extras.globalVars;
  239. }
  240. /**
  241. * Disables custom links but allows page links.
  242. */
  243. function isLinkIgnored(graph, link)
  244. {
  245. return link == null || (graph.isCustomLink(link) && !Graph.isPageLink(link));
  246. };
  247. /**
  248. * Disables custom links on shapes.
  249. */
  250. var graphGetLinkForCell = graph.getLinkForCell;
  251. graph.getLinkForCell = function(cell)
  252. {
  253. var link = graphGetLinkForCell.apply(this, arguments);
  254. if (isLinkIgnored(this, link))
  255. {
  256. link = null;
  257. }
  258. return link;
  259. };
  260. /**
  261. * Disables custom links in labels.
  262. */
  263. var cellRendererRedrawLabelShape = graph.cellRenderer.redrawLabelShape;
  264. graph.cellRenderer.redrawLabelShape = function(shape)
  265. {
  266. cellRendererRedrawLabelShape.apply(this, arguments);
  267. if (shape.node != null)
  268. {
  269. var links = shape.node.getElementsByTagName('a');
  270. for (var i = 0; i < links.length; i++)
  271. {
  272. var href = links[i].getAttribute('href');
  273. if (isLinkIgnored(graph, href))
  274. {
  275. links[i].setAttribute('href', '#');
  276. }
  277. }
  278. }
  279. };
  280. var preview = null;
  281. var waitCounter = 1;
  282. var bounds;
  283. var pageId;
  284. var expScale;
  285. // Waits for all images to finish loading
  286. var cache = new Object();
  287. var math = false;
  288. // Decrements waitCounter and invokes callback when finished
  289. function decrementWaitCounter()
  290. {
  291. if (--waitCounter < 1)
  292. {
  293. //Note: This code targets Chrome as it is the browser used by export server
  294. //Ensure that all fonts have been loaded, this promise is never rejected
  295. document.fonts.ready.then(function()
  296. {
  297. // Rewrite page links
  298. Graph.rewritePageLinks(document);
  299. var doneDiv = document.createElement("div");
  300. var pageCount = diagrams != null? diagrams.length : 1;
  301. doneDiv.id = 'LoadingComplete';
  302. doneDiv.style.display = 'none';
  303. doneDiv.setAttribute('bounds', JSON.stringify(bounds));
  304. doneDiv.setAttribute('page-id', pageId);
  305. doneDiv.setAttribute('scale', expScale);
  306. doneDiv.setAttribute('pageCount', pageCount);
  307. document.body.appendChild(doneDiv);
  308. //Electron pdf export
  309. if (mxIsElectron)
  310. {
  311. try
  312. {
  313. electron.registerMsgListener('get-svg-data', (arg) =>
  314. {
  315. graph.mathEnabled = math; //Enable math such that getSvg works as expected
  316. // Returns the exported SVG for the given graph (see EditorUi.exportSvg)
  317. var bg = graph.background;
  318. if (bg == mxConstants.NONE)
  319. {
  320. bg = null;
  321. }
  322. if (data.theme == 'dark')
  323. {
  324. // TODO Support enableCssDarkMode?
  325. graph.shapeForegroundColor = Editor.lightColor;
  326. graph.shapeBackgroundColor = Editor.darkColor;
  327. graph.stylesheet = graph.getDefaultStylesheet();
  328. graph.refresh();
  329. }
  330. var linkTarget = null;
  331. if (data.linkTarget == 'same-win')
  332. {
  333. linkTarget = '_top';
  334. }
  335. else if (data.linkTarget == 'new-win')
  336. {
  337. linkTarget = '_blank';
  338. }
  339. var svgRoot = graph.getSvg(bg, expScale, 0, false, null, true, null, null, linkTarget);
  340. if (graph.shadowVisible)
  341. {
  342. graph.addSvgShadow(svgRoot);
  343. }
  344. // TODO addFontCss cannot be used as it requires this
  345. // Adds CSS
  346. //Editor.prototype.addFontCss(svgRoot);
  347. if (math)
  348. {
  349. Editor.prototype.addMathCss(svgRoot);
  350. }
  351. function doSend()
  352. {
  353. var editable = data.embedXml == '1';
  354. if (editable)
  355. {
  356. svgRoot.setAttribute('content', getFileXml());
  357. }
  358. electron.sendMessage('svg-data', Graph.xmlDeclaration + '\n' + ((editable) ? Graph.svgFileComment + '\n' : '') +
  359. Graph.svgDoctype + '\n' + mxUtils.getXml(svgRoot));
  360. };
  361. if (data.embedImages == '1')
  362. {
  363. var tmpEditor = new Editor();
  364. tmpEditor.convertImages(svgRoot, doSend);
  365. }
  366. else
  367. {
  368. doSend();
  369. }
  370. });
  371. //For some reason, Electron 9 doesn't send this object as is without stringifying. Usually when variable is external to function own scope
  372. electron.sendMessage('render-finished', {bounds: JSON.stringify(bounds), pageCount: pageCount});
  373. }
  374. catch(e)
  375. {
  376. console.log(e);
  377. }
  378. }
  379. });
  380. }
  381. };
  382. function waitForImages(tagName, attributeName)
  383. {
  384. var imgs = document.body.getElementsByTagName(tagName);
  385. waitCounter += imgs.length;
  386. for (var i = 0; i < imgs.length; i++)
  387. {
  388. // No load events for image elements in Phantom using indirection instead
  389. var src = imgs[i].getAttribute(attributeName);
  390. if (src != null && src.length > 0 && cache[src] == null)
  391. {
  392. cache[src] = new Image();
  393. cache[src].onload = decrementWaitCounter;
  394. cache[src].onerror = decrementWaitCounter;
  395. cache[src].src = src;
  396. }
  397. else
  398. {
  399. decrementWaitCounter();
  400. }
  401. }
  402. };
  403. // Waits for MathJax autoloading and rendering
  404. var editorOnMathJaxDone = Editor.onMathJaxDone;
  405. Editor.onMathJaxDone = function()
  406. {
  407. editorOnMathJaxDone.apply(this, arguments);
  408. decrementWaitCounter();
  409. };
  410. // Adds MathJax rendering task
  411. function renderMath(elt)
  412. {
  413. if (Editor.MathJaxRender != null)
  414. {
  415. waitCounter++;
  416. Editor.MathJaxRender(elt);
  417. }
  418. };
  419. // Waits for the given font
  420. function waitForFont(url)
  421. {
  422. try
  423. {
  424. if (url != null && fontPreload[url] == null)
  425. {
  426. waitCounter++;
  427. fontPreload[url] = true;
  428. mxUtils.get(url, decrementWaitCounter,
  429. decrementWaitCounter, false, 10000,
  430. decrementWaitCounter);
  431. }
  432. }
  433. catch (e)
  434. {
  435. // ignore font
  436. }
  437. };
  438. // Waits for the fonts in the given CSS
  439. function waitForFonts(fontCss)
  440. {
  441. var parts = fontCss.split('url(');
  442. for (var i = 1; i < parts.length; i++)
  443. {
  444. try
  445. {
  446. var idx = parts[i].indexOf(')');
  447. var url = Editor.trimCssUrl(parts[i].substring(0, idx));
  448. waitForFont(url);
  449. }
  450. catch (e)
  451. {
  452. // ignore font css
  453. }
  454. }
  455. };
  456. // Loads and processes the fonts in the given Google Font URL
  457. function processGoogleFontCss(url)
  458. {
  459. try
  460. {
  461. if (Graph.isGoogleFontUrl(url) && cssPreload[url] == null)
  462. {
  463. cssPreload[url] = true;
  464. var link = document.createElement('link');
  465. link.setAttribute('rel', 'preload');
  466. link.setAttribute('as', 'style');
  467. link.setAttribute('href', url);
  468. document.getElementsByTagName('head')[0].appendChild(link);
  469. // Loads the stylesheet to wait for fonts
  470. waitCounter++;
  471. mxUtils.get(url, mxUtils.bind(this, function(req)
  472. {
  473. try
  474. {
  475. if (req.getStatus() >= 200 && req.getStatus() <= 299)
  476. {
  477. waitForFonts(req.getText());
  478. }
  479. decrementWaitCounter();
  480. }
  481. catch(e)
  482. {
  483. decrementWaitCounter();
  484. }
  485. }), decrementWaitCounter, false, 10000, decrementWaitCounter);
  486. }
  487. }
  488. catch (e)
  489. {
  490. // ignore stylesheet
  491. }
  492. };
  493. // Waits for Google Font CSS imports in the given element
  494. function waitForGoogleFontImports(elt)
  495. {
  496. var style = elt.getElementsByTagName('style');
  497. for (var i = 0; i < style.length; i++)
  498. {
  499. var parts = style[i].innerHTML.split('@import url(');
  500. for (var i = 1; i < parts.length; i++)
  501. {
  502. try
  503. {
  504. var idx = parts[i].indexOf(')');
  505. var url = Editor.trimCssUrl(parts[i].substring(0, idx));
  506. processGoogleFontCss(url);
  507. }
  508. catch (e)
  509. {
  510. // ignore import
  511. }
  512. }
  513. }
  514. };
  515. // Intercepts loading of Google Fonts CSS
  516. var origCreateFontElement = Graph.createFontElement;
  517. Graph.createFontElement = function(name, url)
  518. {
  519. var elt = origCreateFontElement.apply(this, arguments);
  520. try
  521. {
  522. if (elt != null && elt.nodeName.toLowerCase() == 'link' &&
  523. elt.getAttribute('type') == 'text/css' &&
  524. elt.getAttribute('rel') == 'stylesheet')
  525. {
  526. processGoogleFontCss(elt.getAttribute('href'));
  527. }
  528. }
  529. catch (e)
  530. {
  531. // ignore stylesheet
  532. }
  533. return elt;
  534. };
  535. // Adds wait counter for loading fonts
  536. var origAddFont = Graph.addFont;
  537. Graph.addFont = function(name, url)
  538. {
  539. waitCounter++;
  540. return origAddFont.call(this, name, url, decrementWaitCounter);
  541. };
  542. /**
  543. * Renders the given page or all pages.
  544. */
  545. function renderPage(currentPageId)
  546. {
  547. // Configures math typesetting
  548. graph.mathEnabled = xmlDoc.documentElement.getAttribute('math') == '1';
  549. // Sets grid size
  550. var gs = xmlDoc.documentElement.getAttribute('gridSize');
  551. if (gs != null)
  552. {
  553. graph.gridSize = parseInt(gs);
  554. }
  555. else
  556. {
  557. graph.gridSize = mxGraph.prototype.gridSize;
  558. }
  559. // Configure graph
  560. graph.foldingEnabled = false;
  561. graph.setEnabled(false);
  562. // Sets background image
  563. var bgImg = xmlDoc.documentElement.getAttribute('backgroundImage');
  564. if (bgImg != null)
  565. {
  566. bgImg = JSON.parse(bgImg);
  567. graph.setBackgroundImage(new mxImage(bgImg.src, bgImg.width,
  568. bgImg.height, bgImg.x, bgImg.y));
  569. }
  570. else
  571. {
  572. graph.setBackgroundImage(null);
  573. }
  574. // Parses XML into graph
  575. var codec = new mxCodec(xmlDoc);
  576. var model = graph.getModel();
  577. codec.decode(xmlDoc.documentElement, model);
  578. var bg;
  579. if (data.format == 'pdf')
  580. {
  581. if (data.bg == 'none' || bg == '')
  582. {
  583. bg = null;
  584. }
  585. else
  586. {
  587. bg = xmlDoc.documentElement.getAttribute('background');
  588. if (bg == 'none' || !bg)
  589. {
  590. bg = '#ffffff';
  591. }
  592. }
  593. }
  594. else
  595. {
  596. // Loads background color
  597. bg = xmlDoc.documentElement.getAttribute('background');
  598. // Normalizes values for transparent backgrounds
  599. if (bg == 'none' || bg == '')
  600. {
  601. bg = null;
  602. }
  603. // Checks if export format supports transparent backgrounds
  604. if (bg == null && data.format != 'gif' && data.format != 'png' && data.format != 'svg')
  605. {
  606. bg = '#ffffff';
  607. }
  608. // Sets background color on page
  609. if (bg != null)
  610. {
  611. document.body.style.backgroundColor = bg;
  612. }
  613. }
  614. //handle layers
  615. if (extras != null && ((extras.layers != null && extras.layers.length > 0) ||
  616. (extras.layerIds != null && extras.layerIds.length > 0)))
  617. {
  618. var childCount = model.getChildCount(model.root);
  619. // Hides all layers
  620. for (var i = 0; i < childCount; i++)
  621. {
  622. model.setVisible(model.getChildAt(model.root, i), false);
  623. }
  624. if (extras.layerIds != null)
  625. {
  626. for (var i = 0; i < extras.layerIds.length; i++)
  627. {
  628. model.setVisible(model.getCell(extras.layerIds[i]), true);
  629. }
  630. }
  631. else
  632. {
  633. for (var i = 0; i < extras.layers.length; i++)
  634. {
  635. var layer = model.getChildAt(model.root, extras.layers[i]);
  636. if (layer != null)
  637. {
  638. model.setVisible(layer, true);
  639. }
  640. }
  641. }
  642. }
  643. // Sets initial value for PDF page background
  644. var gb = graph.getGraphBounds();
  645. graph.pdfPageVisible = false;
  646. // Handles PDF output where the output should match the page format if the page is visible
  647. if (data.print || data.format == 'pdf')
  648. {
  649. var printScale = 1;
  650. var pw = data.pageWidth || xmlDoc.documentElement.getAttribute('pageWidth');
  651. var ph = data.pageHeight || xmlDoc.documentElement.getAttribute('pageHeight');
  652. graph.pdfPageVisible = true;
  653. if (pw != null && ph != null)
  654. {
  655. graph.pageFormat = new mxRectangle(0, 0, parseFloat(pw), parseFloat(ph));
  656. }
  657. var ps = data.pageScale || xmlDoc.documentElement.getAttribute('pageScale');
  658. if (ps != null)
  659. {
  660. graph.pageScale = ps;
  661. }
  662. if (data.fileTitle != null)
  663. {
  664. document.title = data.fileTitle;
  665. }
  666. var pf = graph.pageFormat;
  667. var temp = data.scale;
  668. pf.width = Math.ceil(pf.width * graph.pageScale);
  669. pf.height = Math.ceil(pf.height * graph.pageScale);
  670. var scale = 1;
  671. if (data.fit == '1' && data.sheetsAcross != null && data.sheetsDown != null)
  672. {
  673. var h = data.sheetsAcross;
  674. var v = data.sheetsDown;
  675. if (!isNaN(temp))
  676. {
  677. pf.width = Math.ceil(pf.width * temp);
  678. pf.height = Math.ceil(pf.height * temp);
  679. }
  680. scale = Math.min((pf.height * v) / (gb.height / graph.view.scale),
  681. (pf.width * h) / (gb.width / graph.view.scale));
  682. }
  683. else
  684. {
  685. scale = !isNaN(temp) ? temp : 1;
  686. }
  687. // Applies print scale
  688. data.scale = scale * printScale;
  689. graph.getPageSize = function()
  690. {
  691. return new mxRectangle(0, 0, this.pageFormat.width * this.pageScale,
  692. this.pageFormat.height * this.pageScale);
  693. };
  694. graph.getPageLayout = function()
  695. {
  696. var size = this.getPageSize();
  697. var bounds = this.getGraphBounds();
  698. if (bounds.width == 0 || bounds.height == 0)
  699. {
  700. return new mxRectangle(0, 0, 1, 1);
  701. }
  702. else
  703. {
  704. // Computes untransformed graph bounds
  705. var x = Math.ceil(bounds.x / this.view.scale - this.view.translate.x);
  706. var y = Math.ceil(bounds.y / this.view.scale - this.view.translate.y);
  707. var w = Math.floor(bounds.width / this.view.scale);
  708. var h = Math.floor(bounds.height / this.view.scale);
  709. var x0 = Math.floor(x / size.width);
  710. var y0 = Math.floor(y / size.height);
  711. var w0 = Math.ceil((x + w) / size.width) - x0;
  712. var h0 = Math.ceil((y + h) / size.height) - y0;
  713. return new mxRectangle(x0, y0, w0, h0);
  714. }
  715. };
  716. // Fits the number of background pages to the graph
  717. graph.view.getBackgroundPageBounds = function()
  718. {
  719. var layout = this.graph.getPageLayout();
  720. var page = this.graph.getPageSize();
  721. return new mxRectangle(
  722. this.scale * (this.translate.x + layout.x * page.width),
  723. this.scale * (this.translate.y + layout.y * page.height),
  724. this.scale * layout.width * page.width,
  725. this.scale * layout.height * page.height);
  726. };
  727. }
  728. if (!graph.pdfPageVisible)
  729. {
  730. var b = graph.getGraphBounds();
  731. // Floor is needed to keep rendering crisp
  732. if (data.w > 0 || data.h > 0)
  733. {
  734. var s = 1;
  735. if (data.w > 0 && data.h > 0)
  736. {
  737. s = Math.min(data.w / b.width, data.h / b.height);
  738. }
  739. else if (data.w > 0)
  740. {
  741. s = data.w / b.width;
  742. }
  743. else
  744. {
  745. s = data.h / b.height;
  746. }
  747. graph.view.scaleAndTranslate(s,
  748. Math.floor(data.border / s - Math.floor(b.x)),
  749. Math.floor(data.border / s - Math.floor(b.y)));
  750. }
  751. else
  752. {
  753. var s = data.scale;
  754. if (autoScale)
  755. {
  756. var pageWidth = (extras != null && extras.pageWidth != null) ? extras.pageWidth : 800;
  757. if (b.width < pageWidth & b.height < 1.5 * pageWidth)
  758. {
  759. s = 4;
  760. }
  761. else if (b.width < 2 * pageWidth & b.height < 3 * pageWidth)
  762. {
  763. s = 3;
  764. }
  765. else if (b.width < 4 * pageWidth && b.height < 6 * pageWidth)
  766. {
  767. s = 2;
  768. }
  769. if (extras != null && extras.isGoogleSheet != null)
  770. {
  771. GOOGLE_APPS_MAX_AREA = GOOGLE_SHEET_MAX_AREA;
  772. }
  773. //The image cannot exceed 25 MP to be included in Google Apps
  774. if (b.width * s * b.height * s > GOOGLE_APPS_MAX_AREA)
  775. {
  776. //Subtracting 0.01 to prevent any other rounding that can make slightly over 25 MP
  777. s = Math.sqrt(GOOGLE_APPS_MAX_AREA / (b.width * b.height)) - 0.01;
  778. }
  779. }
  780. graph.view.scaleAndTranslate(s,
  781. Math.floor(data.border - Math.floor(b.x)),
  782. Math.floor(data.border - Math.floor(b.y)));
  783. }
  784. }
  785. else
  786. {
  787. // Disables border for PDF page export
  788. data.border = 0;
  789. // Moves to first page in page layout
  790. var layout = graph.getPageLayout();
  791. var page = graph.getPageSize();
  792. var dx = layout.x * page.width;
  793. var dy = layout.y * page.height;
  794. if (dx != 0 || dy != 0)
  795. {
  796. graph.view.setTranslate(Math.floor(-dx), Math.floor(-dy));
  797. }
  798. }
  799. // Gets the diagram bounds and sets the document size
  800. bounds = (graph.pdfPageVisible) ? graph.view.getBackgroundPageBounds() : graph.getGraphBounds();
  801. bounds.width = Math.ceil(bounds.width + data.border) + 1; //The 1 extra pixels to prevent cutting the cells on the edges when crop is enabled
  802. bounds.height = Math.ceil(bounds.height + data.border) + 1; //The 1 extra pixels to prevent starting a new page. TODO Not working in every case
  803. // Print to pdf fails for 1x1 pages
  804. if (bounds.width <= 1 && bounds.height <= 1)
  805. {
  806. bounds.width = 2;
  807. bounds.height = 2;
  808. }
  809. expScale = graph.view.scale || 1;
  810. // Converts the graph to a vertical sequence of pages for PDF export
  811. if (graph.pdfPageVisible)
  812. {
  813. var pf = graph.pageFormat || mxConstants.PAGE_FORMAT_A4_PORTRAIT;
  814. var scale = (data.print || data.format == 'pdf') ? data.scale : 1 / graph.pageScale;
  815. var autoOrigin = ((data.print || data.format == 'pdf') && data.fit == '1') ||
  816. data.crop == '1' || xmlDoc.documentElement.getAttribute('page') != '1';
  817. var border = 0;
  818. // Negative coordinates are cropped or shifted if page visible
  819. var x0 = 0;
  820. var y0 = 0;
  821. if (data.crop == '1')
  822. {
  823. pf.width = (gb.width + 1.5) * scale;
  824. pf.height = (gb.height + 1.5) * scale;
  825. }
  826. // Starts at first visible page
  827. if (!autoOrigin)
  828. {
  829. var layout = graph.getPageLayout();
  830. x0 -= layout.x * pf.width;
  831. y0 -= layout.y * pf.height;
  832. }
  833. var anchorId = (currentPageId != null) ? 'page/id,' + currentPageId : null;
  834. if (preview == null)
  835. {
  836. preview = new mxPrintPreview(graph, scale, pf, border, x0, y0);
  837. preview.printBackgroundImage = true;
  838. preview.autoOrigin = autoOrigin;
  839. preview.backgroundColor = bg;
  840. preview.pageMargin = (data.pageMargin != null) ?
  841. parseInt(data.pageMargin) : ((data.crop == '1') ?
  842. 0 : preview.pageMargin);
  843. // Replaces background images with SVG subtrees
  844. var previewDrawBackgroundImage = preview.drawBackgroundImage;
  845. preview.drawBackgroundImage = function(img)
  846. {
  847. previewDrawBackgroundImage.apply(this, arguments);
  848. if (img.node != null)
  849. {
  850. EditorUi.embedSvgImages(img.node);
  851. graph.disableSvgLinks(img.node, function(link)
  852. {
  853. link.setAttribute('href', 'javascript:void(0)');
  854. });
  855. }
  856. };
  857. // Renders the grid and configures math
  858. var previewAddGraphFragment = preview.addGraphFragment;
  859. preview.addGraphFragment = function(dx, dy, scale, pageNumber, div, clip)
  860. {
  861. previewAddGraphFragment.apply(this, arguments);
  862. // Disables math rendering in graph
  863. if (!graph.mathEnabled)
  864. {
  865. div.classList.add('geDisableMathJax')
  866. }
  867. waitForGoogleFontImports(div);
  868. };
  869. // Renders print output into this document and removes the graph container
  870. preview.gridColor = gridColor;
  871. preview.gridSize = graph.gridSize;
  872. preview.gridSteps = graph.view.gridSteps;
  873. preview.open(null, window, null, null, anchorId, pf);
  874. }
  875. else
  876. {
  877. preview.gridColor = gridColor;
  878. preview.gridSize = graph.gridSize;
  879. preview.gridSteps = graph.view.gridSteps;
  880. preview.backgroundColor = bg;
  881. preview.autoOrigin = autoOrigin;
  882. preview.appendGraph(graph, scale, x0, y0, null, null, anchorId, pf);
  883. }
  884. // Adds shadow
  885. // NOTE: Shadow rasterizes output
  886. /*if (mxClient.IS_SVG && xmlDoc.documentElement.getAttribute('shadow') == '1')
  887. {
  888. var svgs = document.getElementsByTagName('svg');
  889. for (var i = 0; i < svgs.length; i++)
  890. {
  891. var svg = svgs[i];
  892. var filter = graph.addSvgShadow(svg, null, true);
  893. filter.setAttribute('id', 'shadow-' + i);
  894. svg.appendChild(filter);
  895. svg.setAttribute('filter', 'url(#' + 'shadow-' + i + ')');
  896. }
  897. border = 7;
  898. }*/
  899. bounds = new mxRectangle(0, 0, pf.width, pf.height);
  900. }
  901. else
  902. {
  903. var bgImg = graph.backgroundImage;
  904. if (bgImg != null)
  905. {
  906. var t = graph.view.translate;
  907. var s = graph.view.scale;
  908. bounds.add(new mxRectangle(
  909. (t.x + bgImg.x) * s, (t.y + bgImg.y) * s,
  910. bgImg.width * s, bgImg.height * s));
  911. if (t.x < 0 || t.y < 0)
  912. {
  913. graph.view.setTranslate(
  914. t.x < 0 ? Math.max(-bgImg.x * s, t.x) : t.x,
  915. t.y < 0 ? Math.max(-bgImg.y * s, t.y) : t.y);
  916. bounds.x = 0.5;
  917. bounds.y = 0.5;
  918. }
  919. }
  920. // Adds shadow
  921. // NOTE: PDF shadow rasterizes output so it's disabled
  922. if (data.format != 'pdf' && mxClient.IS_SVG && xmlDoc.documentElement.getAttribute('shadow') == '1')
  923. {
  924. graph.addSvgShadow(graph.view.canvas.ownerSVGElement, null, true);
  925. graph.setShadowVisible(true);
  926. bounds.width += 7;
  927. bounds.height += 7;
  928. }
  929. document.body.style.width = Math.ceil(bounds.x + bounds.width) + 'px';
  930. document.body.style.height = Math.ceil(bounds.y + bounds.height) + 'px';
  931. }
  932. };
  933. if (diagrams != null && diagrams.length > 0)
  934. {
  935. var to = diagrams.length - 1;
  936. // Parameters to and all pages should not be sent with formats other than PDF with page view enabled
  937. if (data.allPages != '1')
  938. {
  939. if (data.pageId != null)
  940. {
  941. for (var i = 0; i < diagrams.length; i++)
  942. {
  943. if (data.pageId == diagrams[i].getAttribute('id'))
  944. {
  945. from = i;
  946. to = i;
  947. break;
  948. }
  949. }
  950. }
  951. else
  952. {
  953. from = Math.max(0, Math.min(parseInt(data.from) || from, diagrams.length - 1));
  954. to = parseInt(data.to);
  955. //If to is not defined, use from (so one page), otherwise, to is restricted to the range from "from" to diagrams.length - 1
  956. to = isNaN(to)? from : Math.max(from, Math.min(to, diagrams.length - 1));
  957. }
  958. }
  959. /**
  960. * Implements %page% and %pagenumber% placeholders
  961. */
  962. var graphGetGlobalVariable = graph.getGlobalVariable;
  963. graph.getGlobalVariable = function(name)
  964. {
  965. if (name == 'page')
  966. {
  967. return (diagrams == null) ? 'Page-1' :
  968. (diagrams[from].getAttribute('name') || ('Page-' + (from + 1)));
  969. }
  970. else if (name == 'pagenumber')
  971. {
  972. return from + 1;
  973. }
  974. return graphGetGlobalVariable.apply(this, arguments);
  975. };
  976. waitCounter += to - from + 1;
  977. for (var i = from; i <= to; i++)
  978. {
  979. if (diagrams[i] != null)
  980. {
  981. if (pageId == null)
  982. {
  983. pageId = diagrams[i].getAttribute('id');
  984. }
  985. xmlDoc = Editor.parseDiagramNode(diagrams[i]);
  986. if (xmlDoc != null)
  987. {
  988. xmlDoc = xmlDoc.ownerDocument;
  989. }
  990. graph.getModel().clear();
  991. from = i;
  992. renderPage(diagrams[i].getAttribute('id'));
  993. }
  994. decrementWaitCounter();
  995. }
  996. }
  997. else
  998. {
  999. renderPage();
  1000. }
  1001. if (preview != null)
  1002. {
  1003. preview.addPendingCss(document);
  1004. Graph.rewritePageLinks(document, true);
  1005. }
  1006. if (fallbackFont)
  1007. {
  1008. // Add a fallbackFont font to all labels in case the selected font doesn't support the character
  1009. // Some systems doesn't have a good fallback fomt that supports all languages
  1010. // Use this with a custom font-face in export-fonts.css file
  1011. document.querySelectorAll('foreignObject div').forEach(d => d.style.fontFamily = (d.style.fontFamily || '') + ', ' + fallbackFont);
  1012. }
  1013. if (data.format == 'pdf')
  1014. {
  1015. graph.container.parentNode.removeChild(graph.container);
  1016. }
  1017. // Includes images in SVG and HTML labels
  1018. waitForImages('image', 'xlink:href');
  1019. waitForImages('img', 'src');
  1020. renderMath(document.body);
  1021. // Invokes callback
  1022. decrementWaitCounter();
  1023. return graph;
  1024. };
  1025. //Electron pdf export
  1026. if (mxIsElectron)
  1027. {
  1028. try
  1029. {
  1030. electron.registerMsgListener('render', (arg) =>
  1031. {
  1032. try
  1033. {
  1034. if (window.shapesLoaded)
  1035. {
  1036. render(arg);
  1037. }
  1038. else
  1039. {
  1040. window.pendingRequest = arg;
  1041. }
  1042. }
  1043. catch(e)
  1044. {
  1045. console.log(e);
  1046. electron.sendMessage('render-finished', null);
  1047. }
  1048. });
  1049. }
  1050. catch(e)
  1051. {
  1052. console.log(e);
  1053. }
  1054. }