1/**2 * Copyright (c) 2020-2025, JGraph Holdings Ltd3 * Copyright (c) 2020-2025, draw.io AG4 */5var mxIsElectron = navigator.userAgent != null &&6 navigator.userAgent.toLowerCase().indexOf(' electron/') > -1 && 7 navigator.userAgent.indexOf(' draw.io/') > -1;8var GOOGLE_APPS_MAX_AREA = 25000000;9var GOOGLE_SHEET_MAX_AREA = 1000000; // The maximum number of pixels is 1 million.10var shadowBlocker = null;1112/**13 * Adds shadow blocker style.14 */15function addShadowBlocker()16{17 if (shadowBlocker == null)18 {19 shadowBlocker = document.createElement('style');20 shadowBlocker.setAttribute('type', 'text/css');21 shadowBlocker.innerHTML = '@media print {\n' +22 ' g[style*="filter: drop-shadow("] {\n' +23 ' filter: none !important;\n' +24 ' }\n' +25 '}\n';26 27 var head = document.getElementsByTagName('head')[0];28 29 if (head != null)30 {31 head.appendChild(shadowBlocker);32 }33 }34}3536/**37 * Adds meta tag to the page.38 */39function mxmeta(content, httpEquiv)40{41 try42 {43 var s = document.createElement('meta');44 45 s.setAttribute('content', content);46 s.setAttribute('http-equiv', httpEquiv);4748 var t = document.getElementsByTagName('meta')[0];49 t.parentNode.insertBefore(s, t);50 }51 catch (e)52 {53 // ignore54 }55};5657function mxscript(src, onLoad)58{59 var s = document.createElement('script');60 s.setAttribute('type', 'text/javascript');61 s.setAttribute('src', src);62 63 if (onLoad != null)64 {65 var r = false;66 67 s.onload = s.onreadystatechange = function()68 {69 if (!r && (!this.readyState || this.readyState == 'complete'))70 {71 r = true;72 onLoad();73 }74 };75 }7677 var t = document.getElementsByTagName('script')[0];78 79 if (t != null)80 {81 t.parentNode.insertBefore(s, t);82 }83};8485if (mxIsElectron)86{87 mxmeta('default-src \'self\'; script-src \'self\'; connect-src \'self\' https://*.draw.io https://*.diagrams.net https://fonts.googleapis.com ' +88 'https://fonts.gstatic.com; img-src * data:; media-src *; font-src * data:; frame-src \'none\'; style-src \'self\' \'unsafe-inline\' ' +89 'https://fonts.googleapis.com; base-uri \'none\';child-src \'self\';object-src \'none\';', 'Content-Security-Policy');90 91 // We can't use eval in Electron because of CSP, so load all shapes and disable eval92 mxscript('js/stencils.min.js', function()93 {94 mxscript('js/shapes-14-6-5.min.js', function()95 {96 if (window.pendingRequest != null)97 {98 render(window.pendingRequest);99 }100101 window.shapesLoaded = true;102 });103 });104 105 // Disables eval for JS (uses shapes-14-6-5.min.js)106 mxStencilRegistry.allowEval = false;107}108109// TODO Add support for loading math from a local folder110Editor.initMath((remoteMath? 'https://app.diagrams.net/' : '') + 'math4/es5/startup.js');111112// Marks individual font and CSS URLs as preloaded113var fontPreload = {};114var cssPreload = {};115116function render(data)117{118 if (data.shadows == '0')119 {120 addShadowBlocker();121 }122 123 if (data.csv != null)124 {125 // CSV loads orgChart asynchronously and needs mxscript126 window.mxscript = function (src, onLoad, id)127 {128 var s = document.createElement('script');129 s.setAttribute('type', 'text/javascript');130 s.setAttribute('defer', 'true');131 s.setAttribute('src', src);132133 if (id != null)134 {135 s.setAttribute('id', id);136 }137 138 if (onLoad != null)139 {140 var r = false;141 142 s.onload = s.onreadystatechange = function()143 {144 if (!r && (!this.readyState || this.readyState == 'complete'))145 {146 r = true;147 onLoad();148 }149 };150 }151 152 var t = document.getElementsByTagName('script')[0];153 154 if (t != null)155 {156 t.parentNode.insertBefore(s, t);157 }158 };159160 var editorUi = new HeadlessEditorUi();161 162 editorUi.importCsv(data.csv, function()163 {164 data.xml = mxUtils.getXml(editorUi.editor.getGraphXml());165 delete data.csv;166 render(data);167 });168169 return;170 }171172 var autoScale = false;173 174 if (data.scale == 'auto')175 {176 autoScale = true;177 data.scale = 1;178 }179 180 document.body.innerText = '';181 var container = document.createElement('div');182 container.id = 'graph';183 container.style.width = '100%';184 container.style.height = '100%';185 document.body.appendChild(container);186 187 var graph = new Graph(container);188 graph.enableFlowAnimation = true;189 190 data.border = parseInt(data.border) || 0;191 data.w = parseFloat(data.w) || 0;192 data.h = parseFloat(data.h) || 0;193 data.scale = parseFloat(data.scale) || 1;194 195 var extras = null;196 197 try198 {199 extras = JSON.parse(data.extras);200 } 201 catch (e)202 {203 try 204 {205 extras = JSON.parse(decodeURIComponent(data.extras));206 }207 catch (e)208 {209 // ignore210 }211 }212213 var gridColor = null;214215 if (extras != null && extras.grid != null)216 {217 graph.gridSize = extras.grid.size;218 graph.view.gridSteps = extras.grid.steps;219 gridColor = extras.grid.color;220 }221 222 if (extras != null && extras.diagramLanguage != null)223 {224 Graph.diagramLanguage = extras.diagramLanguage;225 Graph.translateDiagram = true;226 }227228 if (data.fileTitle != null)229 {230 document.title = data.fileTitle;231 }232 else if (extras != null && extras.globalVars != null &&233 extras.globalVars.filename != null)234 {235 document.title = extras.globalVars.filename;236 }237 238 // Overrides graph bounds to include background images239 var graphGetGraphBounds = graph.getGraphBounds;240241 graph.getGraphBounds = function()242 {243 var bounds = graphGetGraphBounds.apply(this, arguments);244 var img = this.backgroundImage;245 246 if (img != null && img.width != null && img.height != null)247 {248 var t = this.view.translate;249 var s = this.view.scale;250251 bounds = mxRectangle.fromRectangle(bounds);252 bounds.add(new mxRectangle(253 (t.x + img.x) * s, (t.y + img.y) * s,254 img.width * s, img.height * s));255 }256257 return bounds;258 };259 260 // PNG+XML format261 if (data.xml.substring(0, 5) == 'iVBOR' || (extras != null && extras.isPng))262 {263 data.xml = Editor.extractGraphModelFromPng('data:image/png;base64,' + data.xml);264 }265 266 // IE11 sends incorrect xml267 if (data.xml.substring(0, 11) == '<#document>')268 {269 data.xml = data.xml.substring(11, data.xml.length - 12);270 }271 272 // Parses XML273 var doc = mxUtils.parseXml(data.xml);274 var node = Editor.extractGraphModel(doc.documentElement, true);275276 if (node == null)277 {278 //Electron pdf export279 try 280 {281 electron.sendMessage('render-finished', null);282 }283 catch(e)284 {285 console.log(e);286 }287 288 return graph;289 }290 291 var xmlDoc = node.ownerDocument;292 var origXmlDoc = xmlDoc;293 var diagrams = null;294 var from = 0;295296 function getFileXml(uncompressed)297 {298 var xml = mxUtils.getXml(origXmlDoc);299 var editorUi = new HeadlessEditorUi();300 var tmpFile = new LocalFile(editorUi, xml);301 editorUi.setCurrentFile(tmpFile);302 editorUi.setFileData(xml);303 return editorUi.createFileData(editorUi.getXmlFileData(null, null, uncompressed));304 };305306 if (mxIsElectron && data.format == 'xml')307 {308 try309 {310 electron.sendMessage('xml-data', getFileXml(data.uncompressed));311 }312 catch(e)313 {314 electron.sendMessage('xml-data-error');315 }316 317 return;318 }319320 // Handles mxfile321 if (xmlDoc.documentElement.nodeName == 'mxfile')322 {323 diagrams = xmlDoc.documentElement.getElementsByTagName('diagram');324 }325 326 //Add global variables to graph327 if (extras != null && extras.globalVars != null)328 {329 graph.globalVars = extras.globalVars;330 }331332 /**333 * Disables custom links but allows page links.334 */335 function isLinkIgnored(graph, link)336 {337 return link == null || (graph.isCustomLink(link) && !Graph.isPageLink(link));338 };339340 /**341 * Disables custom links on shapes.342 */343 var graphGetLinkForCell = graph.getLinkForCell;344 345 graph.getLinkForCell = function(cell)346 {347 var link = graphGetLinkForCell.apply(this, arguments);348349 if (isLinkIgnored(this, link))350 {351 link = null;352 }353 354 return link;355 };356 357 /**358 * Disables custom links in labels.359 */360 var cellRendererRedrawLabelShape = graph.cellRenderer.redrawLabelShape;361 362 graph.cellRenderer.redrawLabelShape = function(shape)363 {364 cellRendererRedrawLabelShape.apply(this, arguments);365 366 if (shape.node != null)367 {368 var links = shape.node.getElementsByTagName('a');369370 for (var i = 0; i < links.length; i++)371 {372 var href = links[i].getAttribute('href');373 374 if (isLinkIgnored(graph, href))375 {376 links[i].setAttribute('href', '#');377 }378 } 379 }380 };381 382 var preview = null;383 var waitCounter = 1;384 var bounds;385 var pageId;386 var expScale;387 // Waits for all images to finish loading388 var cache = new Object();389 var math = false;390 391 // Decrements waitCounter and invokes callback when finished392 function decrementWaitCounter()393 {394 if (--waitCounter < 1)395 {396 //Note: This code targets Chrome as it is the browser used by export server397 //Ensure that all fonts have been loaded, this promise is never rejected398 document.fonts.ready.then(function() 399 {400 // Rewrite page links401 Graph.rewritePageLinks(document);402 403 var doneDiv = document.createElement("div");404 var pageCount = diagrams != null? diagrams.length : 1;405 doneDiv.id = 'LoadingComplete';406 doneDiv.style.display = 'none';407 doneDiv.setAttribute('bounds', JSON.stringify(bounds));408 doneDiv.setAttribute('page-id', pageId);409 doneDiv.setAttribute('scale', expScale);410 doneDiv.setAttribute('pageCount', pageCount);411 document.body.appendChild(doneDiv);412413 //Electron pdf export414 if (mxIsElectron)415 {416 try417 {418 electron.registerMsgListener('get-svg-data', (arg) => 419 {420 graph.mathEnabled = math; //Enable math such that getSvg works as expected421 // Returns the exported SVG for the given graph (see EditorUi.exportSvg)422 var bg = graph.background;423 424 if (bg == mxConstants.NONE)425 {426 bg = null;427 }428429 var theme = 'auto';430431 if (data.theme != null)432 {433 theme = data.theme;434 }435 436 var linkTarget = null;437438 if (data.linkTarget == 'same-win')439 {440 linkTarget = '_top';441 }442 else if (data.linkTarget == 'new-win')443 {444 linkTarget = '_blank';445 }446447 var svgRoot = graph.getSvg(bg, expScale, data.border, false, null,448 true, null, null, linkTarget, null, null, theme);449 450 if (graph.shadowVisible)451 {452 graph.addSvgShadow(svgRoot);453 }454 455 // TODO addFontCss cannot be used as it requires this456 // Adds CSS457 //Editor.prototype.addFontCss(svgRoot);458 459 if (math)460 {461 Editor.prototype.addMathCss(svgRoot);462 }463 464 function doSend() 465 {466 var editable = data.embedXml == '1';467468 if (editable)469 {470 svgRoot.setAttribute('content', getFileXml());471 }472473 electron.sendMessage('svg-data',474 Graph.xmlDeclaration + '\n' +475 ((editable) ? Graph.svgFileComment + '\n' : '') +476 Graph.svgDoctype + '\n' + mxUtils.getXml(svgRoot));477 };478479 function embedFontsDone()480 {481 if (data.embedImages == '1')482 {483 var tmpEditor = new Editor();484 tmpEditor.convertImages(svgRoot, doSend);485 }486 else487 {488 doSend();489 }490 }491492 if (data.embedFonts == '1')493 {494 var extFonts = graph.getCustomFonts();495 496 // Adds external fonts497 // TODO CSP will not allow external fonts!498 if (extFonts.length > 0)499 {500 var svgDoc = svgRoot.ownerDocument;501 var style = (svgDoc.createElementNS != null) ?502 svgDoc.createElementNS(mxConstants.NS_SVG, 'style') : svgDoc.createElement('style');503 (svgDoc.setAttributeNS != null) ? style.setAttributeNS('type', 'text/css') :504 style.setAttribute('type', 'text/css');505 506 var prefix = '';507 var postfix = '';508 509 for (var i = 0; i < extFonts.length; i++)510 {511 var fontName = extFonts[i].name, fontUrl = extFonts[i].url;512 513 if (Graph.isCssFontUrl(fontUrl))514 {515 prefix += '@import url(' + Graph.rewriteGoogleFontUrl(fontUrl) + ');\n';516 }517 else518 {519 postfix += '@font-face {\n' +520 'font-family: "' + fontName + '";\n' + 521 'src: url("' + fontUrl + '");\n}\n';522 } 523 }524 525 style.appendChild(svgDoc.createTextNode(prefix + postfix));526 svgRoot.getElementsByTagName('defs')[0].appendChild(style);527 }528529 EditorUi.prototype.replaceAlternateContent(svgRoot, null, embedFontsDone);530 }531 else532 {533 embedFontsDone();534 }535 });536 537 //For some reason, Electron 9 doesn't send this object as is without stringifying. Usually when variable is external to function own scope538 electron.sendMessage('render-finished', {bounds: JSON.stringify(bounds), pageCount: pageCount});539 }540 catch(e)541 {542 console.log(e);543 }544 }545 });546 }547 };548 549 function waitForImages(tagName, attributeName)550 {551 var imgs = document.body.getElementsByTagName(tagName);552 waitCounter += imgs.length;553554 for (var i = 0; i < imgs.length; i++)555 {556 // No load events for image elements in Phantom using indirection instead557 var src = imgs[i].getAttribute(attributeName);558 559 if (src != null && src.length > 0 && cache[src] == null)560 {561 cache[src] = new Image();562 cache[src].onload = decrementWaitCounter;563 cache[src].onerror = decrementWaitCounter;564 cache[src].src = src;565 }566 else567 {568 decrementWaitCounter();569 }570 }571 };572 573 // Waits for MathJax autoloading and rendering574 var editorOnMathJaxDone = Editor.onMathJaxDone;575 576 Editor.onMathJaxDone = function()577 {578 editorOnMathJaxDone.apply(this, arguments);579 decrementWaitCounter();580 };581582 // Adds MathJax rendering task583 function renderMath(elt)584 {585 if (Editor.MathJaxRender != null)586 {587 waitCounter++;588 Editor.MathJaxRender(elt);589 }590 };591592 // Waits for the given font593 function waitForFont(url)594 {595 try596 {597 if (url != null && fontPreload[url] == null)598 {599 waitCounter++;600 fontPreload[url] = true;601 mxUtils.get(url, decrementWaitCounter,602 decrementWaitCounter, false, 10000,603 decrementWaitCounter);604 }605 }606 catch (e)607 {608 // ignore font609 }610 };611612 // Waits for the fonts in the given CSS613 function waitForFonts(fontCss)614 {615 var parts = fontCss.split('url(');616 617 for (var i = 1; i < parts.length; i++)618 {619 try620 {621 var idx = parts[i].indexOf(')');622 var url = Editor.trimCssUrl(parts[i].substring(0, idx));623 waitForFont(url);624 }625 catch (e)626 {627 // ignore font css628 }629 }630 };631632 // Loads and processes the fonts in the given Google Font URL633 function processGoogleFontCss(url)634 {635 try636 {637 if (Graph.isGoogleFontUrl(url) && cssPreload[url] == null)638 { 639 cssPreload[url] = true;640641 var link = document.createElement('link');642 link.setAttribute('rel', 'preload');643 link.setAttribute('as', 'style');644 link.setAttribute('href', url);645 document.getElementsByTagName('head')[0].appendChild(link);646647 // Loads the stylesheet to wait for fonts648 waitCounter++;649650 mxUtils.get(url, mxUtils.bind(this, function(req)651 {652 try653 {654 if (req.getStatus() >= 200 && req.getStatus() <= 299)655 {656 waitForFonts(req.getText());657 }658659 decrementWaitCounter();660 }661 catch(e)662 {663 decrementWaitCounter();664 }665 }), decrementWaitCounter, false, 10000, decrementWaitCounter);666 }667 }668 catch (e)669 {670 // ignore stylesheet671 }672 };673674 // Waits for Google Font CSS imports in the given element675 function waitForGoogleFontImports(elt)676 {677 var style = elt.getElementsByTagName('style');678679 for (var i = 0; i < style.length; i++)680 {681 var parts = style[i].innerHTML.split('@import url(');682683 for (var i = 1; i < parts.length; i++)684 {685 try686 {687 var idx = parts[i].indexOf(')');688 var url = Editor.trimCssUrl(parts[i].substring(0, idx));689 processGoogleFontCss(url);690 }691 catch (e)692 {693 // ignore import694 }695 }696 }697 };698699 // Intercepts loading of Google Fonts CSS700 var origCreateFontElement = Graph.createFontElement;701702 Graph.createFontElement = function(name, url)703 {704 var elt = origCreateFontElement.apply(this, arguments);705706 try707 {708 if (elt != null && elt.nodeName.toLowerCase() == 'link' &&709 elt.getAttribute('type') == 'text/css' &&710 elt.getAttribute('rel') == 'stylesheet')711 {712 processGoogleFontCss(elt.getAttribute('href'));713 }714 }715 catch (e)716 {717 // ignore stylesheet718 }719720 return elt;721 };722723 // Adds wait counter for loading fonts724 var origAddFont = Graph.addFont;725 726 Graph.addFont = function(name, url)727 {728 waitCounter++;729 return origAddFont.call(this, name, url, decrementWaitCounter); 730 };731 732 /**733 * Renders the given page or all pages.734 */735 function renderPage(currentPageId)736 {737 // Configures math typesetting738 math = xmlDoc.documentElement.getAttribute('math') == '1';739 graph.mathEnabled = math;740741 // Sets grid size742 var gs = xmlDoc.documentElement.getAttribute('gridSize');743744 if (gs != null)745 {746 graph.gridSize = parseInt(gs);747 }748 else749 {750 graph.gridSize = mxGraph.prototype.gridSize;751 }752753 // Configure graph754 graph.foldingEnabled = false;755 graph.setEnabled(false);756 757 // Sets background image758 var bgImg = xmlDoc.documentElement.getAttribute('backgroundImage');759760 if (bgImg != null)761 {762 bgImg = JSON.parse(bgImg);763 graph.setBackgroundImage(new mxImage(bgImg.src, bgImg.width,764 bgImg.height, bgImg.x, bgImg.y));765 }766 else767 {768 graph.setBackgroundImage(null);769 }770 771 // Parses XML into graph772 var codec = new mxCodec(xmlDoc);773 var model = graph.getModel();774 codec.decode(xmlDoc.documentElement, model);775776 var bg;777 778 if (data.format == 'pdf')779 {780 if (data.bg == 'none' || bg == '')781 {782 bg = null;783 }784 else785 {786 bg = xmlDoc.documentElement.getAttribute('background');787 788 if (bg == 'none' || !bg)789 {790 bg = '#ffffff';791 }792 }793 }794 else795 {796 // Loads background color797 bg = xmlDoc.documentElement.getAttribute('background');798799 // Normalizes values for transparent backgrounds800 if (bg == 'none' || bg == '')801 {802 bg = null;803 }804 805 // Checks if export format supports transparent backgrounds806 if (bg == null && data.format != 'gif' && data.format != 'png' && data.format != 'svg')807 {808 bg = '#ffffff';809 }810 811 // Sets background color on page812 if (bg != null)813 {814 document.body.style.backgroundColor = bg;815 }816 }817 818 //handle layers819 if (extras != null && ((extras.layers != null && extras.layers.length > 0) || 820 (extras.layerIds != null && extras.layerIds.length > 0)))821 {822 var childCount = model.getChildCount(model.root);823 824 // Hides all layers825 for (var i = 0; i < childCount; i++)826 {827 model.setVisible(model.getChildAt(model.root, i), false);828 }829830 if (extras.layerIds != null)831 {832 for (var i = 0; i < extras.layerIds.length; i++)833 {834 model.setVisible(model.getCell(extras.layerIds[i]), true);835 }836 }837 else838 {839 for (var i = 0; i < extras.layers.length; i++)840 {841 var layer = model.getChildAt(model.root, extras.layers[i]);842 843 if (layer != null)844 {845 model.setVisible(layer, true);846 }847 }848 }849 }850 851 // Sets initial value for PDF page background852 var gb = graph.getGraphBounds();853 graph.pdfPageVisible = false;854 855 // Handles PDF output where the output should match the page format if the page is visible856 if (data.print || data.format == 'pdf')857 {858 var printScale = 1;859 860 var pw = data.pageWidth || xmlDoc.documentElement.getAttribute('pageWidth');861 var ph = data.pageHeight || xmlDoc.documentElement.getAttribute('pageHeight');862 graph.pdfPageVisible = true;863 864 if (pw != null && ph != null)865 {866 graph.pageFormat = new mxRectangle(0, 0, parseFloat(pw), parseFloat(ph));867 }868 869 var ps = data.pageScale || xmlDoc.documentElement.getAttribute('pageScale');870 871 if (ps != null)872 {873 graph.pageScale = ps;874 }875876 var pf = graph.pageFormat;877 var temp = data.scale;878 pf.width = Math.ceil(pf.width * graph.pageScale);879 pf.height = Math.ceil(pf.height * graph.pageScale);880 var scale = 1;881882 if (data.fit == '1' && data.sheetsAcross != null && data.sheetsDown != null)883 {884 var h = data.sheetsAcross;885 var v = data.sheetsDown;886887 if (!isNaN(temp))888 {889 pf.width = Math.ceil(pf.width * temp);890 pf.height = Math.ceil(pf.height * temp);891 }892 893 scale = Math.min((pf.height * v) / (gb.height / graph.view.scale),894 (pf.width * h) / (gb.width / graph.view.scale));895 }896 else897 {898 scale = !isNaN(temp) ? temp : 1;899 }900901 // Applies print scale902 data.scale = scale * printScale;903904 graph.getPageSize = function()905 {906 return new mxRectangle(0, 0, this.pageFormat.width * this.pageScale,907 this.pageFormat.height * this.pageScale);908 };909 910 graph.getPageLayout = function()911 {912 var size = this.getPageSize();913 var bounds = this.getGraphBounds();914915 if (bounds.width == 0 || bounds.height == 0)916 {917 return new mxRectangle(0, 0, 1, 1);918 }919 else920 {921 // Computes untransformed graph bounds922 var x = Math.ceil(bounds.x / this.view.scale - this.view.translate.x);923 var y = Math.ceil(bounds.y / this.view.scale - this.view.translate.y);924 var w = Math.floor(bounds.width / this.view.scale);925 var h = Math.floor(bounds.height / this.view.scale);926 927 var x0 = Math.floor(x / size.width);928 var y0 = Math.floor(y / size.height);929 var w0 = Math.ceil((x + w) / size.width) - x0;930 var h0 = Math.ceil((y + h) / size.height) - y0;931 932 return new mxRectangle(x0, y0, w0, h0);933 }934 };935936 // Fits the number of background pages to the graph937 graph.view.getBackgroundPageBounds = function()938 {939 var layout = this.graph.getPageLayout();940 var page = this.graph.getPageSize();941 942 return new mxRectangle(943 this.scale * (this.translate.x + layout.x * page.width),944 this.scale * (this.translate.y + layout.y * page.height),945 this.scale * layout.width * page.width,946 this.scale * layout.height * page.height);947 };948 }949950 if (!graph.pdfPageVisible)951 {952 var b = graph.getGraphBounds();953 954 // Floor is needed to keep rendering crisp955 if (data.w > 0 || data.h > 0)956 {957 var s = 1;958 959 if (data.w > 0 && data.h > 0)960 {961 s = Math.min(data.w / b.width, data.h / b.height);962 }963 else if (data.w > 0)964 {965 s = data.w / b.width;966 }967 else968 {969 s = data.h / b.height;970 }971 972 graph.view.scaleAndTranslate(s,973 Math.floor(data.border / s - Math.floor(b.x)),974 Math.floor(data.border / s - Math.floor(b.y)));975 }976 else977 {978 var s = data.scale;979 980 if (autoScale)981 {982 var pageWidth = (extras != null && extras.pageWidth != null) ? extras.pageWidth : 800;983 984 if (b.width < pageWidth & b.height < 1.5 * pageWidth)985 {986 s = 4;987 }988 else if (b.width < 2 * pageWidth & b.height < 3 * pageWidth)989 {990 s = 3;991 }992 else if (b.width < 4 * pageWidth && b.height < 6 * pageWidth)993 {994 s = 2;995 }996 997 if (extras != null && extras.isGoogleSheet != null)998 {999 GOOGLE_APPS_MAX_AREA = GOOGLE_SHEET_MAX_AREA;1000 }1001 1002 //The image cannot exceed 25 MP to be included in Google Apps1003 if (b.width * s * b.height * s > GOOGLE_APPS_MAX_AREA)1004 {1005 //Subtracting 0.01 to prevent any other rounding that can make slightly over 25 MP 1006 s = Math.sqrt(GOOGLE_APPS_MAX_AREA / (b.width * b.height)) - 0.01;1007 }1008 }1009 1010 graph.view.scaleAndTranslate(s,1011 Math.floor(data.border - Math.floor(b.x)),1012 Math.floor(data.border - Math.floor(b.y)));1013 }1014 }1015 else1016 {1017 // Disables border for PDF page export1018 data.border = 0;1019 1020 // Moves to first page in page layout1021 var layout = graph.getPageLayout();1022 var page = graph.getPageSize();1023 var dx = layout.x * page.width;1024 var dy = layout.y * page.height;1025 1026 if (dx != 0 || dy != 0)1027 {1028 graph.view.setTranslate(Math.floor(-dx), Math.floor(-dy));1029 }1030 }1031 1032 // Gets the diagram bounds and sets the document size1033 bounds = (graph.pdfPageVisible) ? graph.view.getBackgroundPageBounds() : graph.getGraphBounds();1034 bounds.width = Math.ceil(bounds.width + data.border) + 1; //The 1 extra pixels to prevent cutting the cells on the edges when crop is enabled1035 bounds.height = Math.ceil(bounds.height + data.border) + 1; //The 1 extra pixels to prevent starting a new page. TODO Not working in every case1036 1037 // Print to pdf fails for 1x1 pages1038 if (bounds.width <= 1 && bounds.height <= 1)1039 {1040 bounds.width = 2;1041 bounds.height = 2;1042 }10431044 expScale = graph.view.scale || 1;1045 1046 // Converts the graph to a vertical sequence of pages for PDF export1047 if (graph.pdfPageVisible)1048 {1049 var pf = graph.pageFormat || mxConstants.PAGE_FORMAT_A4_PORTRAIT;1050 var scale = (data.print || data.format == 'pdf') ? data.scale : 1 / graph.pageScale;1051 var autoOrigin = ((data.print || data.format == 'pdf') && data.fit == '1') ||1052 data.crop == '1' || xmlDoc.documentElement.getAttribute('page') != '1';1053 var border = 0;10541055 // Negative coordinates are cropped or shifted if page visible1056 var x0 = 0;1057 var y0 = 0;1058 1059 if (data.crop == '1')1060 {1061 pf.width = (gb.width + 1.5) * scale;1062 pf.height = (gb.height + 1.5) * scale;1063 }10641065 // Starts at first visible page1066 if (!autoOrigin)1067 {1068 var layout = graph.getPageLayout();1069 x0 -= layout.x * pf.width;1070 y0 -= layout.y * pf.height;1071 }10721073 var anchorId = (currentPageId != null) ? 'page/id,' + currentPageId : null;1074 1075 if (preview == null)1076 {1077 preview = new mxPrintPreview(graph, scale, pf, border, x0, y0);1078 preview.printBackgroundImage = true;1079 preview.autoOrigin = autoOrigin;1080 preview.backgroundColor = bg;1081 preview.pageMargin = (data.pageMargin != null) ?1082 parseInt(data.pageMargin) : ((data.crop == '1') ?1083 0 : preview.pageMargin);1084 1085 // Replaces background images with SVG subtrees1086 var previewDrawBackgroundImage = preview.drawBackgroundImage;1087 1088 preview.drawBackgroundImage = function(img)1089 {1090 previewDrawBackgroundImage.apply(this, arguments);10911092 if (img.node != null)1093 {1094 EditorUi.embedSvgImages(img.node);10951096 graph.disableSvgLinks(img.node, function(link)1097 {1098 link.setAttribute('href', 'javascript:void(0)'); 1099 });1100 }1101 };11021103 // Renders the grid and configures math1104 var previewAddGraphFragment = preview.addGraphFragment;11051106 preview.addGraphFragment = function(dx, dy, scale, pageNumber, div, clip)1107 {1108 previewAddGraphFragment.apply(this, arguments);11091110 // Disables math rendering in graph1111 if (!graph.mathEnabled)1112 {1113 div.classList.add('geDisableMathJax')1114 }1115 1116 // Adds shadow1117 if (xmlDoc.documentElement.getAttribute('shadow') == '1')1118 {1119 var svgs = div.getElementsByTagName('svg');1120 1121 for (var i = 0; i < svgs.length; i++)1122 {1123 graph.addSvgShadow(svgs[i]);1124 }1125 }1126 1127 waitForGoogleFontImports(div);1128 };11291130 // Renders print output into this document and removes the graph container1131 preview.gridColor = gridColor;1132 preview.gridSize = graph.gridSize;1133 preview.gridSteps = graph.view.gridSteps;1134 preview.open(null, window, null, null, anchorId, pf);1135 }1136 else1137 {1138 preview.gridColor = gridColor;1139 preview.gridSize = graph.gridSize;1140 preview.gridSteps = graph.view.gridSteps;1141 preview.backgroundColor = bg;1142 preview.autoOrigin = autoOrigin; 1143 preview.appendGraph(graph, scale, x0, y0, null, null, anchorId, pf);1144 }11451146 bounds = new mxRectangle(0, 0, pf.width, pf.height);1147 }1148 else1149 {1150 var bgImg = graph.backgroundImage;11511152 if (bgImg != null)1153 {1154 var t = graph.view.translate;1155 var s = graph.view.scale;11561157 bounds.add(new mxRectangle(1158 (t.x + bgImg.x) * s, (t.y + bgImg.y) * s,1159 bgImg.width * s, bgImg.height * s));11601161 if (t.x < 0 || t.y < 0)1162 {1163 graph.view.setTranslate(1164 t.x < 0 ? Math.max(-bgImg.x * s, t.x) : t.x,1165 t.y < 0 ? Math.max(-bgImg.y * s, t.y) : t.y);1166 bounds.x = 0.5;1167 bounds.y = 0.5;1168 }1169 }11701171 // Adds shadow1172 if (xmlDoc.documentElement.getAttribute('shadow') == '1')1173 {1174 var size = graph.setShadowVisible(true);1175 bounds.width += size;1176 bounds.height += size;1177 }1178 1179 document.body.style.width = Math.ceil(bounds.x + bounds.width) + 'px';1180 document.body.style.height = Math.ceil(bounds.y + bounds.height) + 'px';1181 }1182 };1183 1184 if (diagrams != null && diagrams.length > 0)1185 {1186 var to = diagrams.length - 1;1187 1188 // Parameters to and all pages should not be sent with formats other than PDF with page view enabled1189 if (data.allPages != '1')1190 {1191 if (data.pageId != null)1192 {1193 for (var i = 0; i < diagrams.length; i++)1194 {1195 if (data.pageId == diagrams[i].getAttribute('id'))1196 {1197 from = i;1198 to = i;1199 break;1200 }1201 }1202 }1203 else1204 {1205 from = Math.max(0, Math.min(parseInt(data.from) || from, diagrams.length - 1));1206 to = parseInt(data.to);1207 //If to is not defined, use from (so one page), otherwise, to is restricted to the range from "from" to diagrams.length - 11208 to = isNaN(to)? from : Math.max(from, Math.min(to, diagrams.length - 1));1209 }1210 }1211 1212 /**1213 * Implements %page% and %pagenumber% placeholders1214 */1215 var graphGetGlobalVariable = graph.getGlobalVariable;1216 1217 graph.getGlobalVariable = function(name)1218 {1219 if (name == 'page')1220 {1221 return (diagrams == null) ? 'Page-1' :1222 (diagrams[from].getAttribute('name') || ('Page-' + (from + 1)));1223 }1224 else if (name == 'pagenumber' && data.from != null)1225 {1226 return from + 1;1227 }1228 1229 return graphGetGlobalVariable.apply(this, arguments);1230 };12311232 waitCounter += to - from + 1;1233 1234 for (var i = from; i <= to; i++) 1235 {1236 if (diagrams[i] != null)1237 {1238 if (pageId == null)1239 {1240 pageId = diagrams[i].getAttribute('id');1241 }1242 1243 xmlDoc = Editor.parseDiagramNode(diagrams[i]);1244 1245 if (xmlDoc != null)1246 {1247 xmlDoc = xmlDoc.ownerDocument;1248 }12491250 graph.getModel().clear();1251 from = i;1252 renderPage(diagrams[i].getAttribute('id'));1253 }12541255 decrementWaitCounter();1256 }1257 }1258 else1259 {1260 renderPage();1261 }12621263 if (preview != null)1264 {1265 preview.addPendingCss(document);1266 Graph.rewritePageLinks(document, true);1267 }12681269 if (fallbackFont)1270 {1271 // Add a fallbackFont font to all labels in case the selected font doesn't support the character1272 // Some systems doesn't have a good fallback fomt that supports all languages1273 // Use this with a custom font-face in export-fonts.css file1274 document.querySelectorAll('foreignObject div').forEach(d => d.style.fontFamily = (d.style.fontFamily || '') + ', ' + fallbackFont);1275 }12761277 if (data.format == 'pdf')1278 {1279 graph.container.parentNode.removeChild(graph.container);1280 }12811282 // Includes images in SVG and HTML labels1283 waitForImages('image', 'xlink:href');1284 waitForImages('img', 'src');1285 renderMath(document.body);1286 1287 // Invokes callback1288 decrementWaitCounter();1289 1290 return graph;1291};12921293//Electron pdf export1294if (mxIsElectron)1295{1296 try 1297 {1298 electron.registerMsgListener('render', (arg) => 1299 {1300 try1301 {1302 if (window.shapesLoaded)1303 {1304 render(arg);1305 }1306 else1307 {1308 window.pendingRequest = arg;1309 }1310 }1311 catch(e)1312 {1313 console.log(e);1314 electron.sendMessage('render-finished', null);1315 }1316 });1317 }1318 catch(e)1319 {1320 console.log(e);1321 }1322}