From b908ee16d4d09587a65455ed6c3ca148be4337ce Mon Sep 17 00:00:00 2001 From: Philippe Faist Date: Fri, 25 Feb 2022 15:11:47 +0100 Subject: [PATCH] tweaks for (internal) svg export --- generate_ecc_zoo.py | 2 + javascripts/canvas2svg.js | 1214 +++++++++++++++++ javascripts/cytoscape-svg.js | 1 + minilatextohtml/__init__.py | 60 +- .../pretty_code_graph.html | 11 + .../pretty_code_graph.js | 18 +- 6 files changed, 1275 insertions(+), 31 deletions(-) create mode 100644 javascripts/canvas2svg.js create mode 100644 javascripts/cytoscape-svg.js diff --git a/generate_ecc_zoo.py b/generate_ecc_zoo.py index fe210e3..9ca3ac0 100644 --- a/generate_ecc_zoo.py +++ b/generate_ecc_zoo.py @@ -515,6 +515,8 @@ def _ml_as_text(s): ('misc.js', 'misc.js'), #('edit_code.js', 'edit_code.js'), ('mathjaxinit.js', 'mathjaxinit.js'), + ('canvas2svg.js', 'canvas2svg.js'), + ('cytoscape-svg.js', 'cytoscape-svg.js'), ] for root_js, root_js_out in root_js_list: diff --git a/javascripts/canvas2svg.js b/javascripts/canvas2svg.js new file mode 100644 index 0000000..f71d359 --- /dev/null +++ b/javascripts/canvas2svg.js @@ -0,0 +1,1214 @@ +/*!! + * Canvas 2 Svg v1.0.19 + * A low level canvas to SVG converter. Uses a mock canvas context to build an SVG document. + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/mit-license.php + * + * Author: + * Kerry Liu + * + * Copyright (c) 2014 Gliffy Inc. + */ + +;(function () { + "use strict"; + + var STYLES, ctx, CanvasGradient, CanvasPattern, namedEntities; + + //helper function to format a string + function format(str, args) { + var keys = Object.keys(args), i; + for (i=0; i 1) { + options = defaultOptions; + options.width = arguments[0]; + options.height = arguments[1]; + } else if ( !o ) { + options = defaultOptions; + } else { + options = o; + } + + if (!(this instanceof ctx)) { + //did someone call this without new? + return new ctx(options); + } + + //setup options + this.width = options.width || defaultOptions.width; + this.height = options.height || defaultOptions.height; + this.enableMirroring = options.enableMirroring !== undefined ? options.enableMirroring : defaultOptions.enableMirroring; + + this.canvas = this; ///point back to this instance! + this.__document = options.document || document; + + // allow passing in an existing context to wrap around + // if a context is passed in, we know a canvas already exist + if (options.ctx) { + this.__ctx = options.ctx; + } else { + this.__canvas = this.__document.createElement("canvas"); + this.__ctx = this.__canvas.getContext("2d"); + } + + this.__setDefaultStyles(); + this.__stack = [this.__getStyleState()]; + this.__groupStack = []; + + //the root svg element + this.__root = this.__document.createElementNS("http://www.w3.org/2000/svg", "svg"); + this.__root.setAttribute("version", 1.1); + this.__root.setAttribute("xmlns", "http://www.w3.org/2000/svg"); + this.__root.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xlink", "http://www.w3.org/1999/xlink"); + this.__root.setAttribute("width", this.width); + this.__root.setAttribute("height", this.height); + + //make sure we don't generate the same ids in defs + this.__ids = {}; + + //defs tag + this.__defs = this.__document.createElementNS("http://www.w3.org/2000/svg", "defs"); + this.__root.appendChild(this.__defs); + + //also add a group child. the svg element can't use the transform attribute + this.__currentElement = this.__document.createElementNS("http://www.w3.org/2000/svg", "g"); + this.__root.appendChild(this.__currentElement); + }; + + + /** + * Creates the specified svg element + * @private + */ + ctx.prototype.__createElement = function (elementName, properties, resetFill) { + if (typeof properties === "undefined") { + properties = {}; + } + + var element = this.__document.createElementNS("http://www.w3.org/2000/svg", elementName), + keys = Object.keys(properties), i, key; + if (resetFill) { + //if fill or stroke is not specified, the svg element should not display. By default SVG's fill is black. + element.setAttribute("fill", "none"); + element.setAttribute("stroke", "none"); + } + for (i=0; i 0) { + if (this.__currentElement.nodeName === "path") { + if (!this.__currentElementsToStyle) this.__currentElementsToStyle = {element: parent, children: []}; + this.__currentElementsToStyle.children.push(this.__currentElement) + this.__applyCurrentDefaultPath(); + } + + var group = this.__createElement("g"); + parent.appendChild(group); + this.__currentElement = group; + } + + var transform = this.__currentElement.getAttribute("transform"); + if (transform) { + transform += " "; + } else { + transform = ""; + } + transform += t; + this.__currentElement.setAttribute("transform", transform); + }; + + /** + * scales the current element + */ + ctx.prototype.scale = function (x, y) { + if (y === undefined) { + y = x; + } + this.__addTransform(format("scale({x},{y})", {x:x, y:y})); + }; + + /** + * rotates the current element + */ + ctx.prototype.rotate = function (angle) { + var degrees = (angle * 180 / Math.PI); + this.__addTransform(format("rotate({angle},{cx},{cy})", {angle:degrees, cx:0, cy:0})); + }; + + /** + * translates the current element + */ + ctx.prototype.translate = function (x, y) { + this.__addTransform(format("translate({x},{y})", {x:x,y:y})); + }; + + /** + * applies a transform to the current element + */ + ctx.prototype.transform = function (a, b, c, d, e, f) { + this.__addTransform(format("matrix({a},{b},{c},{d},{e},{f})", {a:a, b:b, c:c, d:d, e:e, f:f})); + }; + + /** + * Create a new Path Element + */ + ctx.prototype.beginPath = function () { + var path, parent; + + // Note that there is only one current default path, it is not part of the drawing state. + // See also: https://html.spec.whatwg.org/multipage/scripting.html#current-default-path + this.__currentDefaultPath = ""; + this.__currentPosition = {}; + + path = this.__createElement("path", {}, true); + parent = this.__closestGroupOrSvg(); + parent.appendChild(path); + this.__currentElement = path; + }; + + /** + * Helper function to apply currentDefaultPath to current path element + * @private + */ + ctx.prototype.__applyCurrentDefaultPath = function () { + var currentElement = this.__currentElement; + if (currentElement.nodeName === "path") { + currentElement.setAttribute("d", this.__currentDefaultPath); + } else { + console.error("Attempted to apply path command to node", currentElement.nodeName); + } + }; + + /** + * Helper function to add path command + * @private + */ + ctx.prototype.__addPathCommand = function (command) { + this.__currentDefaultPath += " "; + this.__currentDefaultPath += command; + }; + + /** + * Adds the move command to the current path element, + * if the currentPathElement is not empty create a new path element + */ + ctx.prototype.moveTo = function (x,y) { + if (this.__currentElement.nodeName !== "path") { + this.beginPath(); + } + + // creates a new subpath with the given point + this.__currentPosition = {x: x, y: y}; + this.__addPathCommand(format("M {x} {y}", {x:x, y:y})); + }; + + /** + * Closes the current path + */ + ctx.prototype.closePath = function () { + if (this.__currentDefaultPath) { + this.__addPathCommand("Z"); + } + }; + + /** + * Adds a line to command + */ + ctx.prototype.lineTo = function (x, y) { + this.__currentPosition = {x: x, y: y}; + if (this.__currentDefaultPath.indexOf('M') > -1) { + this.__addPathCommand(format("L {x} {y}", {x:x, y:y})); + } else { + this.__addPathCommand(format("M {x} {y}", {x:x, y:y})); + } + }; + + /** + * Add a bezier command + */ + ctx.prototype.bezierCurveTo = function (cp1x, cp1y, cp2x, cp2y, x, y) { + this.__currentPosition = {x: x, y: y}; + this.__addPathCommand(format("C {cp1x} {cp1y} {cp2x} {cp2y} {x} {y}", + {cp1x:cp1x, cp1y:cp1y, cp2x:cp2x, cp2y:cp2y, x:x, y:y})); + }; + + /** + * Adds a quadratic curve to command + */ + ctx.prototype.quadraticCurveTo = function (cpx, cpy, x, y) { + this.__currentPosition = {x: x, y: y}; + this.__addPathCommand(format("Q {cpx} {cpy} {x} {y}", {cpx:cpx, cpy:cpy, x:x, y:y})); + }; + + + /** + * Return a new normalized vector of given vector + */ + var normalize = function (vector) { + var len = Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1]); + return [vector[0] / len, vector[1] / len]; + }; + + /** + * Adds the arcTo to the current path + * + * @see http://www.w3.org/TR/2015/WD-2dcontext-20150514/#dom-context-2d-arcto + */ + ctx.prototype.arcTo = function (x1, y1, x2, y2, radius) { + // Let the point (x0, y0) be the last point in the subpath. + var x0 = this.__currentPosition && this.__currentPosition.x; + var y0 = this.__currentPosition && this.__currentPosition.y; + + // First ensure there is a subpath for (x1, y1). + if (typeof x0 == "undefined" || typeof y0 == "undefined") { + return; + } + + // Negative values for radius must cause the implementation to throw an IndexSizeError exception. + if (radius < 0) { + throw new Error("IndexSizeError: The radius provided (" + radius + ") is negative."); + } + + // If the point (x0, y0) is equal to the point (x1, y1), + // or if the point (x1, y1) is equal to the point (x2, y2), + // or if the radius radius is zero, + // then the method must add the point (x1, y1) to the subpath, + // and connect that point to the previous point (x0, y0) by a straight line. + if (((x0 === x1) && (y0 === y1)) + || ((x1 === x2) && (y1 === y2)) + || (radius === 0)) { + this.lineTo(x1, y1); + return; + } + + // Otherwise, if the points (x0, y0), (x1, y1), and (x2, y2) all lie on a single straight line, + // then the method must add the point (x1, y1) to the subpath, + // and connect that point to the previous point (x0, y0) by a straight line. + var unit_vec_p1_p0 = normalize([x0 - x1, y0 - y1]); + var unit_vec_p1_p2 = normalize([x2 - x1, y2 - y1]); + if (unit_vec_p1_p0[0] * unit_vec_p1_p2[1] === unit_vec_p1_p0[1] * unit_vec_p1_p2[0]) { + this.lineTo(x1, y1); + return; + } + + // Otherwise, let The Arc be the shortest arc given by circumference of the circle that has radius radius, + // and that has one point tangent to the half-infinite line that crosses the point (x0, y0) and ends at the point (x1, y1), + // and that has a different point tangent to the half-infinite line that ends at the point (x1, y1), and crosses the point (x2, y2). + // The points at which this circle touches these two lines are called the start and end tangent points respectively. + + // note that both vectors are unit vectors, so the length is 1 + var cos = (unit_vec_p1_p0[0] * unit_vec_p1_p2[0] + unit_vec_p1_p0[1] * unit_vec_p1_p2[1]); + var theta = Math.acos(Math.abs(cos)); + + // Calculate origin + var unit_vec_p1_origin = normalize([ + unit_vec_p1_p0[0] + unit_vec_p1_p2[0], + unit_vec_p1_p0[1] + unit_vec_p1_p2[1] + ]); + var len_p1_origin = radius / Math.sin(theta / 2); + var x = x1 + len_p1_origin * unit_vec_p1_origin[0]; + var y = y1 + len_p1_origin * unit_vec_p1_origin[1]; + + // Calculate start angle and end angle + // rotate 90deg clockwise (note that y axis points to its down) + var unit_vec_origin_start_tangent = [ + -unit_vec_p1_p0[1], + unit_vec_p1_p0[0] + ]; + // rotate 90deg counter clockwise (note that y axis points to its down) + var unit_vec_origin_end_tangent = [ + unit_vec_p1_p2[1], + -unit_vec_p1_p2[0] + ]; + var getAngle = function (vector) { + // get angle (clockwise) between vector and (1, 0) + var x = vector[0]; + var y = vector[1]; + if (y >= 0) { // note that y axis points to its down + return Math.acos(x); + } else { + return -Math.acos(x); + } + }; + var startAngle = getAngle(unit_vec_origin_start_tangent); + var endAngle = getAngle(unit_vec_origin_end_tangent); + + // Connect the point (x0, y0) to the start tangent point by a straight line + this.lineTo(x + unit_vec_origin_start_tangent[0] * radius, + y + unit_vec_origin_start_tangent[1] * radius); + + // Connect the start tangent point to the end tangent point by arc + // and adding the end tangent point to the subpath. + this.arc(x, y, radius, startAngle, endAngle); + }; + + /** + * Sets the stroke property on the current element + */ + ctx.prototype.stroke = function () { + if (this.__currentElement.nodeName === "path") { + this.__currentElement.setAttribute("paint-order", "fill stroke markers"); + } + this.__applyCurrentDefaultPath(); + this.__applyStyleToCurrentElement("stroke"); + }; + + /** + * Sets fill properties on the current element + */ + ctx.prototype.fill = function () { + if (this.__currentElement.nodeName === "path") { + this.__currentElement.setAttribute("paint-order", "stroke fill markers"); + } + this.__applyCurrentDefaultPath(); + this.__applyStyleToCurrentElement("fill"); + }; + + /** + * Adds a rectangle to the path. + */ + ctx.prototype.rect = function (x, y, width, height) { + if (this.__currentElement.nodeName !== "path") { + this.beginPath(); + } + this.moveTo(x, y); + this.lineTo(x+width, y); + this.lineTo(x+width, y+height); + this.lineTo(x, y+height); + this.lineTo(x, y); + this.closePath(); + }; + + + /** + * adds a rectangle element + */ + ctx.prototype.fillRect = function (x, y, width, height) { + var rect, parent; + rect = this.__createElement("rect", { + x : x, + y : y, + width : width, + height : height + }, true); + parent = this.__closestGroupOrSvg(); + parent.appendChild(rect); + this.__currentElement = rect; + this.__applyStyleToCurrentElement("fill"); + }; + + /** + * Draws a rectangle with no fill + * @param x + * @param y + * @param width + * @param height + */ + ctx.prototype.strokeRect = function (x, y, width, height) { + var rect, parent; + rect = this.__createElement("rect", { + x : x, + y : y, + width : width, + height : height + }, true); + parent = this.__closestGroupOrSvg(); + parent.appendChild(rect); + this.__currentElement = rect; + this.__applyStyleToCurrentElement("stroke"); + }; + + + /** + * Clear entire canvas: + * 1. save current transforms + * 2. remove all the childNodes of the root g element + */ + ctx.prototype.__clearCanvas = function () { + var current = this.__closestGroupOrSvg(), + transform = current.getAttribute("transform"); + var rootGroup = this.__root.childNodes[1]; + var childNodes = rootGroup.childNodes; + for (var i = childNodes.length - 1; i >= 0; i--) { + if (childNodes[i]) { + rootGroup.removeChild(childNodes[i]); + } + } + this.__currentElement = rootGroup; + //reset __groupStack as all the child group nodes are all removed. + this.__groupStack = []; + if (transform) { + this.__addTransform(transform); + } + }; + + /** + * "Clears" a canvas by just drawing a white rectangle in the current group. + */ + ctx.prototype.clearRect = function (x, y, width, height) { + //clear entire canvas + if (x === 0 && y === 0 && width === this.width && height === this.height) { + this.__clearCanvas(); + return; + } + var rect, parent = this.__closestGroupOrSvg(); + rect = this.__createElement("rect", { + x : x, + y : y, + width : width, + height : height, + fill : "#FFFFFF" + }, true); + parent.appendChild(rect); + }; + + /** + * Adds a linear gradient to a defs tag. + * Returns a canvas gradient object that has a reference to it's parent def + */ + ctx.prototype.createLinearGradient = function (x1, y1, x2, y2) { + var grad = this.__createElement("linearGradient", { + id : randomString(this.__ids), + x1 : x1+"px", + x2 : x2+"px", + y1 : y1+"px", + y2 : y2+"px", + "gradientUnits" : "userSpaceOnUse" + }, false); + this.__defs.appendChild(grad); + return new CanvasGradient(grad, this); + }; + + /** + * Adds a radial gradient to a defs tag. + * Returns a canvas gradient object that has a reference to it's parent def + */ + ctx.prototype.createRadialGradient = function (x0, y0, r0, x1, y1, r1) { + var grad = this.__createElement("radialGradient", { + id : randomString(this.__ids), + cx : x1+"px", + cy : y1+"px", + r : r1+"px", + fx : x0+"px", + fy : y0+"px", + "gradientUnits" : "userSpaceOnUse" + }, false); + this.__defs.appendChild(grad); + return new CanvasGradient(grad, this); + + }; + + /** + * Parses the font string and returns svg mapping + * @private + */ + ctx.prototype.__parseFont = function () { + var regex = /^\s*(?=(?:(?:[-a-z]+\s*){0,2}(italic|oblique))?)(?=(?:(?:[-a-z]+\s*){0,2}(small-caps))?)(?=(?:(?:[-a-z]+\s*){0,2}(bold(?:er)?|lighter|[1-9]00))?)(?:(?:normal|\1|\2|\3)\s*){0,3}((?:xx?-)?(?:small|large)|medium|smaller|larger|[.\d]+(?:\%|in|[cem]m|ex|p[ctx]))(?:\s*\/\s*(normal|[.\d]+(?:\%|in|[cem]m|ex|p[ctx])))?\s*([-,\'\"\sa-z0-9]+?)\s*$/i; + var fontPart = regex.exec( this.font ); + var data = { + style : fontPart[1] || 'normal', + size : fontPart[4] || '10px', + family : fontPart[6] || 'sans-serif', + weight: fontPart[3] || 'normal', + decoration : fontPart[2] || 'normal', + href : null + }; + + //canvas doesn't support underline natively, but we can pass this attribute + if (this.__fontUnderline === "underline") { + data.decoration = "underline"; + } + + //canvas also doesn't support linking, but we can pass this as well + if (this.__fontHref) { + data.href = this.__fontHref; + } + + return data; + }; + + /** + * Helper to link text fragments + * @param font + * @param element + * @return {*} + * @private + */ + ctx.prototype.__wrapTextLink = function (font, element) { + if (font.href) { + var a = this.__createElement("a"); + a.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", font.href); + a.appendChild(element); + return a; + } + return element; + }; + + /** + * Fills or strokes text + * @param text + * @param x + * @param y + * @param action - stroke or fill + * @private + */ + ctx.prototype.__applyText = function (text, x, y, action) { + var font = this.__parseFont(), + parent = this.__closestGroupOrSvg(), + textElement = this.__createElement("text", { + "font-family" : font.family, + "font-size" : font.size, + "font-style" : font.style, + "font-weight" : font.weight, + "text-decoration" : font.decoration, + "x" : x, + "y" : y, + "text-anchor": getTextAnchor(this.textAlign), + "dominant-baseline": getDominantBaseline(this.textBaseline) + }, true); + + textElement.appendChild(this.__document.createTextNode(text)); + this.__currentElement = textElement; + this.__applyStyleToCurrentElement(action); + parent.appendChild(this.__wrapTextLink(font,textElement)); + }; + + /** + * Creates a text element + * @param text + * @param x + * @param y + */ + ctx.prototype.fillText = function (text, x, y) { + this.__applyText(text, x, y, "fill"); + }; + + /** + * Strokes text + * @param text + * @param x + * @param y + */ + ctx.prototype.strokeText = function (text, x, y) { + this.__applyText(text, x, y, "stroke"); + }; + + /** + * No need to implement this for svg. + * @param text + * @return {TextMetrics} + */ + ctx.prototype.measureText = function (text) { + this.__ctx.font = this.font; + return this.__ctx.measureText(text); + }; + + /** + * Arc command! + */ + ctx.prototype.arc = function (x, y, radius, startAngle, endAngle, counterClockwise) { + // in canvas no circle is drawn if no angle is provided. + if (startAngle === endAngle) { + return; + } + startAngle = startAngle % (2*Math.PI); + endAngle = endAngle % (2*Math.PI); + if (startAngle === endAngle) { + //circle time! subtract some of the angle so svg is happy (svg elliptical arc can't draw a full circle) + endAngle = ((endAngle + (2*Math.PI)) - 0.001 * (counterClockwise ? -1 : 1)) % (2*Math.PI); + } + var endX = x+radius*Math.cos(endAngle), + endY = y+radius*Math.sin(endAngle), + startX = x+radius*Math.cos(startAngle), + startY = y+radius*Math.sin(startAngle), + sweepFlag = counterClockwise ? 0 : 1, + largeArcFlag = 0, + diff = endAngle - startAngle; + + // https://github.com/gliffy/canvas2svg/issues/4 + if (diff < 0) { + diff += 2*Math.PI; + } + + if (counterClockwise) { + largeArcFlag = diff > Math.PI ? 0 : 1; + } else { + largeArcFlag = diff > Math.PI ? 1 : 0; + } + + this.lineTo(startX, startY); + this.__addPathCommand(format("A {rx} {ry} {xAxisRotation} {largeArcFlag} {sweepFlag} {endX} {endY}", + {rx:radius, ry:radius, xAxisRotation:0, largeArcFlag:largeArcFlag, sweepFlag:sweepFlag, endX:endX, endY:endY})); + + this.__currentPosition = {x: endX, y: endY}; + }; + + /** + * Generates a ClipPath from the clip command. + */ + ctx.prototype.clip = function () { + var group = this.__closestGroupOrSvg(), + clipPath = this.__createElement("clipPath"), + id = randomString(this.__ids), + newGroup = this.__createElement("g"); + + this.__applyCurrentDefaultPath(); + group.removeChild(this.__currentElement); + clipPath.setAttribute("id", id); + clipPath.appendChild(this.__currentElement); + + this.__defs.appendChild(clipPath); + + //set the clip path to this group + group.setAttribute("clip-path", format("url(#{id})", {id:id})); + + //clip paths can be scaled and transformed, we need to add another wrapper group to avoid later transformations + // to this path + group.appendChild(newGroup); + + this.__currentElement = newGroup; + + }; + + /** + * Draws a canvas, image or mock context to this canvas. + * Note that all svg dom manipulation uses node.childNodes rather than node.children for IE support. + * http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-drawimage + */ + ctx.prototype.drawImage = function () { + //convert arguments to a real array + var args = Array.prototype.slice.call(arguments), + image=args[0], + dx, dy, dw, dh, sx=0, sy=0, sw, sh, parent, svg, defs, group, + currentElement, svgImage, canvas, context, id; + + if (args.length === 3) { + dx = args[1]; + dy = args[2]; + sw = image.width; + sh = image.height; + dw = sw; + dh = sh; + } else if (args.length === 5) { + dx = args[1]; + dy = args[2]; + dw = args[3]; + dh = args[4]; + sw = image.width; + sh = image.height; + } else if (args.length === 9) { + sx = args[1]; + sy = args[2]; + sw = args[3]; + sh = args[4]; + dx = args[5]; + dy = args[6]; + dw = args[7]; + dh = args[8]; + } else { + throw new Error("Invalid number of arguments passed to drawImage: " + arguments.length); + } + + parent = this.__closestGroupOrSvg(); + currentElement = this.__currentElement; + var translateDirective = "translate(" + dx + ", " + dy + ")"; + if (image instanceof ctx) { + //canvas2svg mock canvas context. In the future we may want to clone nodes instead. + //also I'm currently ignoring dw, dh, sw, sh, sx, sy for a mock context. + svg = image.getSvg().cloneNode(true); + if (svg.childNodes && svg.childNodes.length > 1) { + defs = svg.childNodes[0]; + while(defs.childNodes.length) { + id = defs.childNodes[0].getAttribute("id"); + this.__ids[id] = id; + this.__defs.appendChild(defs.childNodes[0]); + } + group = svg.childNodes[1]; + if (group) { + //save original transform + var originTransform = group.getAttribute("transform"); + var transformDirective; + if (originTransform) { + transformDirective = originTransform+" "+translateDirective; + } else { + transformDirective = translateDirective; + } + group.setAttribute("transform", transformDirective); + parent.appendChild(group); + } + } + } else if (image.nodeName === "CANVAS" || image.nodeName === "IMG") { + //canvas or image + svgImage = this.__createElement("image"); + svgImage.setAttribute("width", dw); + svgImage.setAttribute("height", dh); + svgImage.setAttribute("preserveAspectRatio", "none"); + + if (sx || sy || sw !== image.width || sh !== image.height) { + //crop the image using a temporary canvas + canvas = this.__document.createElement("canvas"); + canvas.width = dw; + canvas.height = dh; + context = canvas.getContext("2d"); + context.drawImage(image, sx, sy, sw, sh, 0, 0, dw, dh); + image = canvas; + } + svgImage.setAttribute("transform", translateDirective); + svgImage.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", + image.nodeName === "CANVAS" ? image.toDataURL() : image.getAttribute("src")); + parent.appendChild(svgImage); + } + }; + + /** + * Generates a pattern tag + */ + ctx.prototype.createPattern = function (image, repetition) { + var pattern = this.__document.createElementNS("http://www.w3.org/2000/svg", "pattern"), id = randomString(this.__ids), + img; + pattern.setAttribute("id", id); + pattern.setAttribute("width", image.width); + pattern.setAttribute("height", image.height); + if (image.nodeName === "CANVAS" || image.nodeName === "IMG") { + img = this.__document.createElementNS("http://www.w3.org/2000/svg", "image"); + img.setAttribute("width", image.width); + img.setAttribute("height", image.height); + img.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", + image.nodeName === "CANVAS" ? image.toDataURL() : image.getAttribute("src")); + pattern.appendChild(img); + this.__defs.appendChild(pattern); + } else if (image instanceof ctx) { + pattern.appendChild(image.__root.childNodes[1]); + this.__defs.appendChild(pattern); + } + return new CanvasPattern(pattern, this); + }; + + ctx.prototype.setLineDash = function (dashArray) { + if (dashArray && dashArray.length > 0) { + this.lineDash = dashArray.join(","); + } else { + this.lineDash = null; + } + }; + + /** + * Not yet implemented + */ + ctx.prototype.drawFocusRing = function () {}; + ctx.prototype.createImageData = function () {}; + ctx.prototype.getImageData = function () {}; + ctx.prototype.putImageData = function () {}; + ctx.prototype.globalCompositeOperation = function () {}; + ctx.prototype.setTransform = function () {}; + + //add options for alternative namespace + if (typeof window === "object") { + window.C2S = ctx; + } + + // CommonJS/Browserify + if (typeof module === "object" && typeof module.exports === "object") { + module.exports = ctx; + } + +}()); diff --git a/javascripts/cytoscape-svg.js b/javascripts/cytoscape-svg.js new file mode 100644 index 0000000..636d0fe --- /dev/null +++ b/javascripts/cytoscape-svg.js @@ -0,0 +1 @@ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.cytoscapeSvg=e():t.cytoscapeSvg=e()}(window,(function(){return function(t){var e={};function r(i){if(e[i])return e[i].exports;var n=e[i]={i:i,l:!1,exports:{}};return t[i].call(n.exports,n,n.exports,r),n.l=!0,n.exports}return r.m=t,r.c=e,r.d=function(t,e,i){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:i})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var i=Object.create(null);if(r.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var n in t)r.d(i,n,function(e){return t[e]}.bind(null,n));return i},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=0)}([function(t,e,r){"use strict";var i=r(1),n=function(t){t&&t("core","svg",i.svg)};"undefined"!=typeof cytoscape&&n(cytoscape),t.exports=n},function(t,e,r){"use strict";var i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},n=r(2),o={},s={};s.number=function(t){return null!=t&&(void 0===t?"undefined":i(t))===i(1)&&!isNaN(t)},o.bufferCanvasImage=function(t,e){var r=e.renderer().usePaths;e.renderer().usePaths=function(){return!1},e.elements().forEach((function(t){t._private.rscratch.pathCacheKey=null,t._private.rscratch.pathCache=null}));var i=e.renderer(),o=e.mutableElements().boundingBox(),a=i.findContainerClientCoords(),l=t.full?Math.ceil(o.w):a[2],h=t.full?Math.ceil(o.h):a[3],c=s.number(t.maxWidth)||s.number(t.maxHeight),u=i.getPixelRatio(),p=1;if(void 0!==t.scale)l*=t.scale,h*=t.scale,p=t.scale;else if(c){var _=1/0,d=1/0;s.number(t.maxWidth)&&(_=p*t.maxWidth/l),s.number(t.maxHeight)&&(d=p*t.maxHeight/h),l*=p=Math.min(_,d),h*=p}c||(l*=u,h*=u,p*=u);var f=null,g=f=new n(l,h);if(l>0&&h>0){f.clearRect(0,0,l,h),t.bg&&(f.globalCompositeOperation="destination-over",f.fillStyle=t.bg,f.fillRect(0,0,l,h)),f.globalCompositeOperation="source-over";var m=i.getCachedZSortedEles();if(t.full)f.translate(-o.x1*p,-o.y1*p),f.scale(p,p),i.drawElements(f,m),f.scale(1/p,1/p),f.translate(o.x1*p,o.y1*p);else{var y=e.pan(),v={x:y.x*p,y:y.y*p};p*=e.zoom(),f.translate(v.x,v.y),f.scale(p,p),i.drawElements(f,m),f.scale(1/p,1/p),f.translate(-v.x,-v.y)}}return e.renderer().usePaths=r,g},o.svg=function(t){return o.bufferCanvasImage(t||{},this).getSerializedSvg()},t.exports=o},function(t,e,r){!function(){"use strict";var e,r,i,n,o;function s(t,e){var r,i=Object.keys(e);for(r=0;r1?((e=i).width=arguments[0],e.height=arguments[1]):e=t||i,!(this instanceof r))return new r(e);this.width=e.width||i.width,this.height=e.height||i.height,this.enableMirroring=void 0!==e.enableMirroring?e.enableMirroring:i.enableMirroring,this.canvas=this,this.__document=e.document||document,e.ctx?this.__ctx=e.ctx:(this.__canvas=this.__document.createElement("canvas"),this.__ctx=this.__canvas.getContext("2d")),this.__setDefaultStyles(),this.__stack=[this.__getStyleState()],this.__groupStack=[],this.__root=this.__document.createElementNS("http://www.w3.org/2000/svg","svg"),this.__root.setAttribute("version",1.1),this.__root.setAttribute("xmlns","http://www.w3.org/2000/svg"),this.__root.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:xlink","http://www.w3.org/1999/xlink"),this.__root.setAttribute("width",this.width),this.__root.setAttribute("height",this.height),this.__ids={},this.__defs=this.__document.createElementNS("http://www.w3.org/2000/svg","defs"),this.__root.appendChild(this.__defs),this.__currentElement=this.__document.createElementNS("http://www.w3.org/2000/svg","g"),this.__root.appendChild(this.__currentElement)}).prototype.__createElement=function(t,e,r){void 0===e&&(e={});var i,n,o=this.__document.createElementNS("http://www.w3.org/2000/svg",t),s=Object.keys(e);for(r&&(o.setAttribute("fill","none"),o.setAttribute("stroke","none")),i=0;i0){"path"===this.__currentElement.nodeName&&(this.__currentElementsToStyle||(this.__currentElementsToStyle={element:e,children:[]}),this.__currentElementsToStyle.children.push(this.__currentElement),this.__applyCurrentDefaultPath());var r=this.__createElement("g");e.appendChild(r),this.__currentElement=r}var i=this.__currentElement.getAttribute("transform");i?i+=" ":i="",i+=t,this.__currentElement.setAttribute("transform",i)},r.prototype.scale=function(t,e){void 0===e&&(e=t),this.__addTransform(s("scale({x},{y})",{x:t,y:e}))},r.prototype.rotate=function(t){var e=180*t/Math.PI;this.__addTransform(s("rotate({angle},{cx},{cy})",{angle:e,cx:0,cy:0}))},r.prototype.translate=function(t,e){this.__addTransform(s("translate({x},{y})",{x:t,y:e}))},r.prototype.transform=function(t,e,r,i,n,o){this.__addTransform(s("matrix({a},{b},{c},{d},{e},{f})",{a:t,b:e,c:r,d:i,e:n,f:o}))},r.prototype.beginPath=function(){var t;this.__currentDefaultPath="",this.__currentPosition={},t=this.__createElement("path",{},!0),this.__closestGroupOrSvg().appendChild(t),this.__currentElement=t},r.prototype.__applyCurrentDefaultPath=function(){var t=this.__currentElement;"path"===t.nodeName?t.setAttribute("d",this.__currentDefaultPath):console.error("Attempted to apply path command to node",t.nodeName)},r.prototype.__addPathCommand=function(t){this.__currentDefaultPath+=" ",this.__currentDefaultPath+=t},r.prototype.moveTo=function(t,e){"path"!==this.__currentElement.nodeName&&this.beginPath(),this.__currentPosition={x:t,y:e},this.__addPathCommand(s("M {x} {y}",{x:t,y:e}))},r.prototype.closePath=function(){this.__currentDefaultPath&&this.__addPathCommand("Z")},r.prototype.lineTo=function(t,e){this.__currentPosition={x:t,y:e},this.__currentDefaultPath.indexOf("M")>-1?this.__addPathCommand(s("L {x} {y}",{x:t,y:e})):this.__addPathCommand(s("M {x} {y}",{x:t,y:e}))},r.prototype.bezierCurveTo=function(t,e,r,i,n,o){this.__currentPosition={x:n,y:o},this.__addPathCommand(s("C {cp1x} {cp1y} {cp2x} {cp2y} {x} {y}",{cp1x:t,cp1y:e,cp2x:r,cp2y:i,x:n,y:o}))},r.prototype.quadraticCurveTo=function(t,e,r,i){this.__currentPosition={x:r,y:i},this.__addPathCommand(s("Q {cpx} {cpy} {x} {y}",{cpx:t,cpy:e,x:r,y:i}))};var h=function(t){var e=Math.sqrt(t[0]*t[0]+t[1]*t[1]);return[t[0]/e,t[1]/e]};r.prototype.arcTo=function(t,e,r,i,n){var o=this.__currentPosition&&this.__currentPosition.x,s=this.__currentPosition&&this.__currentPosition.y;if(void 0!==o&&void 0!==s){if(n<0)throw new Error("IndexSizeError: The radius provided ("+n+") is negative.");if(o===t&&s===e||t===r&&e===i||0===n)this.lineTo(t,e);else{var a=h([o-t,s-e]),l=h([r-t,i-e]);if(a[0]*l[1]!=a[1]*l[0]){var c=a[0]*l[0]+a[1]*l[1],u=Math.acos(Math.abs(c)),p=h([a[0]+l[0],a[1]+l[1]]),_=n/Math.sin(u/2),d=t+_*p[0],f=e+_*p[1],g=[-a[1],a[0]],m=[l[1],-l[0]],y=function(t){var e=t[0];return t[1]>=0?Math.acos(e):-Math.acos(e)},v=y(g),b=y(m);this.lineTo(d+g[0]*n,f+g[1]*n),this.arc(d,f,n,v,b)}else this.lineTo(t,e)}}},r.prototype.stroke=function(){"path"===this.__currentElement.nodeName&&this.__currentElement.setAttribute("paint-order","fill stroke markers"),this.__applyCurrentDefaultPath(),this.__applyStyleToCurrentElement("stroke")},r.prototype.fill=function(){"path"===this.__currentElement.nodeName&&this.__currentElement.setAttribute("paint-order","stroke fill markers"),this.__applyCurrentDefaultPath(),this.__applyStyleToCurrentElement("fill")},r.prototype.rect=function(t,e,r,i){"path"!==this.__currentElement.nodeName&&this.beginPath(),this.moveTo(t,e),this.lineTo(t+r,e),this.lineTo(t+r,e+i),this.lineTo(t,e+i),this.lineTo(t,e),this.closePath()},r.prototype.fillRect=function(t,e,r,i){var n;n=this.__createElement("rect",{x:t,y:e,width:r,height:i},!0),this.__closestGroupOrSvg().appendChild(n),this.__currentElement=n,this.__applyStyleToCurrentElement("fill")},r.prototype.strokeRect=function(t,e,r,i){var n;n=this.__createElement("rect",{x:t,y:e,width:r,height:i},!0),this.__closestGroupOrSvg().appendChild(n),this.__currentElement=n,this.__applyStyleToCurrentElement("stroke")},r.prototype.__clearCanvas=function(){for(var t=this.__closestGroupOrSvg().getAttribute("transform"),e=this.__root.childNodes[1],r=e.childNodes,i=r.length-1;i>=0;i--)r[i]&&e.removeChild(r[i]);this.__currentElement=e,this.__groupStack=[],t&&this.__addTransform(t)},r.prototype.clearRect=function(t,e,r,i){if(0!==t||0!==e||r!==this.width||i!==this.height){var n,o=this.__closestGroupOrSvg();n=this.__createElement("rect",{x:t,y:e,width:r,height:i,fill:"#FFFFFF"},!0),o.appendChild(n)}else this.__clearCanvas()},r.prototype.createLinearGradient=function(t,e,r,n){var o=this.__createElement("linearGradient",{id:a(this.__ids),x1:t+"px",x2:r+"px",y1:e+"px",y2:n+"px",gradientUnits:"userSpaceOnUse"},!1);return this.__defs.appendChild(o),new i(o,this)},r.prototype.createRadialGradient=function(t,e,r,n,o,s){var l=this.__createElement("radialGradient",{id:a(this.__ids),cx:n+"px",cy:o+"px",r:s+"px",fx:t+"px",fy:e+"px",gradientUnits:"userSpaceOnUse"},!1);return this.__defs.appendChild(l),new i(l,this)},r.prototype.__parseFont=function(){var t=/^\s*(?=(?:(?:[-a-z]+\s*){0,2}(italic|oblique))?)(?=(?:(?:[-a-z]+\s*){0,2}(small-caps))?)(?=(?:(?:[-a-z]+\s*){0,2}(bold(?:er)?|lighter|[1-9]00))?)(?:(?:normal|\1|\2|\3)\s*){0,3}((?:xx?-)?(?:small|large)|medium|smaller|larger|[.\d]+(?:\%|in|[cem]m|ex|p[ctx]))(?:\s*\/\s*(normal|[.\d]+(?:\%|in|[cem]m|ex|p[ctx])))?\s*([-,\'\"\sa-z0-9]+?)\s*$/i.exec(this.font),e={style:t[1]||"normal",size:t[4]||"10px",family:t[6]||"sans-serif",weight:t[3]||"normal",decoration:t[2]||"normal",href:null};return"underline"===this.__fontUnderline&&(e.decoration="underline"),this.__fontHref&&(e.href=this.__fontHref),e},r.prototype.__wrapTextLink=function(t,e){if(t.href){var r=this.__createElement("a");return r.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",t.href),r.appendChild(e),r}return e},r.prototype.__applyText=function(t,e,r,i){var n,o,s=this.__parseFont(),a=this.__closestGroupOrSvg(),h=this.__createElement("text",{"font-family":s.family,"font-size":s.size,"font-style":s.style,"font-weight":s.weight,"text-decoration":s.decoration,x:e,y:r,"text-anchor":(n=this.textAlign,o={left:"start",right:"end",center:"middle",start:"start",end:"end"},o[n]||o.start),"dominant-baseline":l(this.textBaseline)},!0);h.appendChild(this.__document.createTextNode(t)),this.__currentElement=h,this.__applyStyleToCurrentElement(i),a.appendChild(this.__wrapTextLink(s,h))},r.prototype.fillText=function(t,e,r){this.__applyText(t,e,r,"fill")},r.prototype.strokeText=function(t,e,r){this.__applyText(t,e,r,"stroke")},r.prototype.measureText=function(t){return this.__ctx.font=this.font,this.__ctx.measureText(t)},r.prototype.arc=function(t,e,r,i,n,o){if(i!==n){(i%=2*Math.PI)===(n%=2*Math.PI)&&(n=(n+2*Math.PI-.001*(o?-1:1))%(2*Math.PI));var a=t+r*Math.cos(n),l=e+r*Math.sin(n),h=t+r*Math.cos(i),c=e+r*Math.sin(i),u=o?0:1,p=0,_=n-i;_<0&&(_+=2*Math.PI),p=o?_>Math.PI?0:1:_>Math.PI?1:0,this.lineTo(h,c),this.__addPathCommand(s("A {rx} {ry} {xAxisRotation} {largeArcFlag} {sweepFlag} {endX} {endY}",{rx:r,ry:r,xAxisRotation:0,largeArcFlag:p,sweepFlag:u,endX:a,endY:l})),this.__currentPosition={x:a,y:l}}},r.prototype.clip=function(){var t=this.__closestGroupOrSvg(),e=this.__createElement("clipPath"),r=a(this.__ids),i=this.__createElement("g");this.__applyCurrentDefaultPath(),t.removeChild(this.__currentElement),e.setAttribute("id",r),e.appendChild(this.__currentElement),this.__defs.appendChild(e),t.setAttribute("clip-path",s("url(#{id})",{id:r})),t.appendChild(i),this.__currentElement=i},r.prototype.drawImage=function(){var t,e,i,n,o,s,a,l,h,c,u,p,_,d=Array.prototype.slice.call(arguments),f=d[0],g=0,m=0;if(3===d.length)t=d[1],e=d[2],i=o=f.width,n=s=f.height;else if(5===d.length)t=d[1],e=d[2],i=d[3],n=d[4],o=f.width,s=f.height;else{if(9!==d.length)throw new Error("Inavlid number of arguments passed to drawImage: "+arguments.length);g=d[1],m=d[2],o=d[3],s=d[4],t=d[5],e=d[6],i=d[7],n=d[8]}a=this.__closestGroupOrSvg(),this.__currentElement;var y="translate("+t+", "+e+")";if(f instanceof r){if((l=f.getSvg().cloneNode(!0)).childNodes&&l.childNodes.length>1){for(h=l.childNodes[0];h.childNodes.length;)_=h.childNodes[0].getAttribute("id"),this.__ids[_]=_,this.__defs.appendChild(h.childNodes[0]);if(c=l.childNodes[1]){var v,b=c.getAttribute("transform");v=b?b+" "+y:y,c.setAttribute("transform",v),a.appendChild(c)}}}else"CANVAS"!==f.nodeName&&"IMG"!==f.nodeName||((u=this.__createElement("image")).setAttribute("width",i),u.setAttribute("height",n),u.setAttribute("opacity",this.globalAlpha),u.setAttribute("preserveAspectRatio","none"),(p=this.__document.createElement("canvas")).width=i,p.height=n,p.getContext("2d").drawImage(f,g,m,o,s,0,0,i,n),f=p,u.setAttribute("transform",y),u.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href","CANVAS"===f.nodeName?f.toDataURL():f.getAttribute("src")),a.appendChild(u))},r.prototype.createPattern=function(t,e){var i,o=this.__document.createElementNS("http://www.w3.org/2000/svg","pattern"),s=a(this.__ids);return o.setAttribute("id",s),o.setAttribute("width",t.width),o.setAttribute("height",t.height),"CANVAS"===t.nodeName||"IMG"===t.nodeName?((i=this.__document.createElementNS("http://www.w3.org/2000/svg","image")).setAttribute("width",t.width),i.setAttribute("height",t.height),i.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href","CANVAS"===t.nodeName?t.toDataURL():t.getAttribute("src")),o.appendChild(i),this.__defs.appendChild(o)):t instanceof r&&(o.appendChild(t.__root.childNodes[1]),this.__defs.appendChild(o)),new n(o,this)},r.prototype.setLineDash=function(t){t&&t.length>0?this.lineDash=t.join(","):this.lineDash=null},r.prototype.drawFocusRing=function(){},r.prototype.createImageData=function(){},r.prototype.getImageData=function(){},r.prototype.putImageData=function(){},r.prototype.globalCompositeOperation=function(){},r.prototype.setTransform=function(){},"object"==typeof window&&(window.C2S=r),"object"==typeof t.exports&&(t.exports=r)}()}])})); \ No newline at end of file diff --git a/minilatextohtml/__init__.py b/minilatextohtml/__init__.py index 96f51c5..ee6f8dd 100644 --- a/minilatextohtml/__init__.py +++ b/minilatextohtml/__init__.py @@ -11,8 +11,6 @@ def htmlescape(x): import logging logger = logging.getLogger(__name__) -logger_debug = logger.debug -logger_error = logger.error from pylatexenc import latexnodes, latexwalker, macrospec @@ -30,42 +28,50 @@ def htmlescape(x): # -------------------------------------------------------------------- -class _ItemToHtmlSpec(object): - def __init__(self, *args, item_to_html=None, **kwargs): - super(_ItemToHtmlSpec, self).__init__(*args, **kwargs) - if item_to_html is None: - self.item_to_html = ItemToHtmlVerbatim() - elif isinstance(item_to_html, str): - self.item_to_html = ItemToHtmlConst(item_to_html) - else: - self.item_to_html = item_to_html +# class _ItemToHtmlSpec(object): +# def __init__(self, *args, item_to_html=None, **kwargs): +# super().__init__(*args, **kwargs) +# if item_to_html is None: +# self.item_to_html = ItemToHtmlVerbatim() +# elif isinstance(item_to_html, str): +# self.item_to_html = ItemToHtmlConst(item_to_html) +# else: +# self.item_to_html = item_to_html + +def make_item_to_html(item_to_html): + if item_to_html is None: + return ItemToHtmlVerbatim() + elif isinstance(item_to_html, str): + return ItemToHtmlConst(item_to_html) + else: + return item_to_html -class MiniHtmlMacroSpec(_ItemToHtmlSpec, macrospec.MacroSpec): +class MiniHtmlMacroSpec(macrospec.MacroSpec): def __init__(self, macroname, arguments_spec_list=None, item_to_html=None): - super(MiniHtmlMacroSpec, self).__init__( + super().__init__( macroname, arguments_spec_list=arguments_spec_list, - item_to_html=item_to_html, ) + self.item_to_html = make_item_to_html(item_to_html) -class MiniHtmlEnvironmentSpec(macrospec.EnvironmentSpec, _ItemToHtmlSpec): +class MiniHtmlEnvironmentSpec(macrospec.EnvironmentSpec): def __init__(self, macroname, arguments_spec_list=None, body_parser=None, item_to_html=None): - super(MiniHtmlEnvironmentSpec, self).__init__( + super().__init__( macroname, arguments_spec_list=arguments_spec_list, body_parser=body_parser, - item_to_html=item_to_html, ) + self.item_to_html = make_item_to_html(item_to_html) -class MiniHtmlSpecialsSpec(macrospec.SpecialsSpec, _ItemToHtmlSpec): +class MiniHtmlSpecialsSpec(macrospec.SpecialsSpec): def __init__(self, specials_chars, arguments_spec_list=None, item_to_html=None): - super(MiniHtmlSpecialsSpec, self).__init__( + super().__init__( specials_chars, arguments_spec_list=arguments_spec_list, - item_to_html=item_to_html, ) + self.item_to_html = make_item_to_html(item_to_html) # ------------------ @@ -82,7 +88,7 @@ def html_wrap_in_tag(tagname, htmlcontent, *, attrs=None, class_=None): s += '>' s += str(htmlcontent) s += f'' - #logger_debug(f"html_wrap_in_tag: code is ‘{s}’") + #logger.debug(f"html_wrap_in_tag: code is ‘{s}’") return s # ---- @@ -159,7 +165,7 @@ def as_text(self, node, doccontext): class ItemToHtmlVerbatimContentsWrapTag(ItemToHtmlVerbatimWrapTag): def __init__(self, class_="verbatim", is_environment=False): - super(ItemToHtmlVerbatimContentsWrapTag, self).__init__( + super().__init__( tagname='span', class_=class_, ) @@ -194,7 +200,7 @@ def ref_to_html(self, reftarget, doccontext, display_html=None): (target_html, target_href) = self.get_ref(reftarget, doccontext) if display_html is None: display_html = target_html - logger_debug(f"Ref: ‘{reftarget}’ → ‘{display_html}’") + logger.debug(f"Ref: ‘{reftarget}’ → ‘{display_html}’") return html_wrap_in_tag( 'a', display_html, @@ -262,7 +268,7 @@ def __call__(self, node, doccontext): # ('', ',') and represents a citation key. It was parsed using # pylatexenc3's LatexCharsCommaSeparatedListParser. - #logger_debug(f"Citation key nodes: {citekeylist_nodelist=}") + #logger.debug(f"Citation key nodes: {citekeylist_nodelist=}") s_items = [] for citekeygroupnode in citekeylist_nodelist: @@ -310,7 +316,7 @@ def __call__(self, node, doccontext): s = "".join(s_items) - #logger_debug(f"Citation: ‘{mn.latex_verbatim()}’ → ‘{s}’") + #logger.debug(f"Citation: ‘{mn.latex_verbatim()}’ → ‘{s}’") return s def as_text(self, node, doccontext): @@ -690,6 +696,8 @@ def _make_lw_context(): } ) + # ignore unknown macros -- TODO !! only ignore in math mode !! (note + # unknown macro instances are still caught and reported at to-html time) lw_context.set_unknown_macro_spec(MiniHtmlMacroSpec('')) lw_context.set_unknown_environment_spec(MiniHtmlEnvironmentSpec('')) @@ -1030,7 +1038,7 @@ def get_nodearglist(self, node, arg_i): raise ValueError(f"No argument named ‘{arg_i}’ found in node {node=}") arg_i = j if arg_i >= len(node.nodeargd.argnlist): - logger_error(f"Invalid argument #{arg_i} for macro ‘\\{node.macroname}’") + logger.error(f"Invalid argument #{arg_i} for macro ‘\\{node.macroname}’") raise ValueError(f"Invalid argument #{arg_i} for macro ‘\\{node.macroname}’") argnode = node.nodeargd.argnlist[arg_i] if argnode is None: diff --git a/special_pages_gen/pretty_code_graph_data/pretty_code_graph.html b/special_pages_gen/pretty_code_graph_data/pretty_code_graph.html index c6e84de..f4fad10 100644 --- a/special_pages_gen/pretty_code_graph_data/pretty_code_graph.html +++ b/special_pages_gen/pretty_code_graph_data/pretty_code_graph.html @@ -20,4 +20,15 @@ src="https://unpkg.com/cytoscape-fcose/cytoscape-fcose.js"> + + + + + + + {% endblock contents %} + +{# (function(){var a=document.createElement("a");a.setAttribute("href", codegraph.cy.jpeg());a.setAttribute("download", "test.jpeg"); a.click();})() #} diff --git a/special_pages_gen/pretty_code_graph_data/pretty_code_graph.js b/special_pages_gen/pretty_code_graph_data/pretty_code_graph.js index 1d7acde..6aea5a8 100644 --- a/special_pages_gen/pretty_code_graph_data/pretty_code_graph.js +++ b/special_pages_gen/pretty_code_graph_data/pretty_code_graph.js @@ -9,7 +9,7 @@ make_pretty_code_graph_data = function() pretty_code_graph_setup = function() { - var codegraph = {}; + window.codegraph = {}; var main_element = document.getElementById('main'); @@ -20,7 +20,7 @@ pretty_code_graph_setup = function() pretty_code_graph_data = make_pretty_code_graph_data(); - codegraph.cy = cytoscape({ + window.codegraph.cy = cytoscape({ // Main HTML DOM container container: main_element, //'cy-code-graph'), @@ -217,7 +217,7 @@ pretty_code_graph_setup = function() virtualElement.getBoundingClientRect = generateGetBoundingClientRect(base_pos.x + node_pos.x - window.pageXOffset, base_pos.y + node_pos.y - window.pageYOffset); - codegraph.popper_obj.update(); + window.codegraph.popper_obj.update(); tooltip_element.style.display = 'block'; }; @@ -239,7 +239,7 @@ pretty_code_graph_setup = function() getBoundingClientRect: generateGetBoundingClientRect(), }; - codegraph.popper_obj = Popper.createPopper( + window.codegraph.popper_obj = Popper.createPopper( virtualElement, tooltip_element, { @@ -254,7 +254,7 @@ pretty_code_graph_setup = function() } ) - codegraph.cy.on('tap', function(event) { + window.codegraph.cy.on('tap', function(event) { // target holds a reference to the originator // of the event (core or element) var eventTarget = event.target; @@ -308,6 +308,14 @@ pretty_code_graph_setup = function() } }); + + codegraph.downloadsvg = function() { + var a = document.createElement("a"); + a.setAttribute("href", "data:image/svg+xml;base64,"+btoa(codegraph.cy.svg())); + a.setAttribute("download", "codegraph.svg"); + a.click(); + }; + };