diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectRow.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectRow.qml
index 0772da8..f1292cd 100644
--- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectRow.qml
+++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectRow.qml
@@ -100,10 +100,10 @@ Item {
anchors.left: parent.left
visible: Modules.Latex.enabled
property double depth: Screen.devicePixelRatio
- property var ltxInfo: visible ? Latex.render(obj.getLatexString(), depth*(parent.font.pixelSize+2), parent.color).split(",") : ["","0","0"]
- source: visible ? ltxInfo[0] : ""
- width: parseInt(ltxInfo[1])/depth
- height: parseInt(ltxInfo[2])/depth
+ property var ltxInfo: visible ? Modules.Latex.renderSync(obj.getLatexString(), depth*(parent.font.pixelSize+2), parent.color) : { source: "", width: 0, height: 0 }
+ source: visible ? ltxInfo.source : ""
+ width: ltxInfo.width/depth
+ height: ltxInfo.height/depth
}
MouseArea {
diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/canvas.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/canvas.mjs
index cdd0777..cff8ae9 100644
--- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/canvas.mjs
+++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/canvas.mjs
@@ -16,9 +16,10 @@
* along with this program. If not, see .
*/
-import {Module} from "./modules.mjs"
-import {textsup} from "./utils.mjs"
-import {Expression} from "./mathlib.mjs"
+import { Module } from "./modules.mjs"
+import { textsup } from "./utils.mjs"
+import { Expression } from "./mathlib.mjs"
+import Latex from "./math/latex.mjs"
class CanvasAPI extends Module {
@@ -168,6 +169,8 @@ class CanvasAPI extends Module {
obj.draw(this)
} catch(e) {
// Drawing throws an error. Generally, it's due to a new modification (or the opening of a file)
+ console.error(e)
+ console.log(e.stack)
this._drawingErrorDialog.showDialog(objType, obj.name, e.message)
Modules.History.undo()
}
@@ -452,23 +455,24 @@ class CanvasAPI extends Module {
* Renders latex markup ltxText to an image and loads it. Returns a dictionary with three values: source, width and height.
* @param {string} ltxText
* @param {string} color
- * @param {function({width: number, height: number, source: string})} callback
+ * @param {function(LatexRenderResult|{width: number, height: number, source: string})} callback
*/
renderLatexImage(ltxText, color, callback) {
- let [ltxSrc, ltxWidth, ltxHeight] = Latex.render(ltxText, this.textsize, color).split(",")
- let imgData = {
- "source": ltxSrc,
- "width": parseFloat(ltxWidth),
- "height": parseFloat(ltxHeight)
- };
- if(!this._canvas.isImageLoaded(ltxSrc) && !this._canvas.isImageLoading(ltxSrc)){
- // Wait until the image is loaded to callback.
- this._canvas.loadImage(ltxSrc)
- this._canvas.imageLoaders[ltxSrc] = [callback, imgData]
- } else {
- // Callback directly
- callback(imgData)
+ const onRendered = (imgData) => {
+ if(!this._canvas.isImageLoaded(imgData.source) && !this._canvas.isImageLoading(imgData.source)){
+ // Wait until the image is loaded to callback.
+ this._canvas.loadImage(imgData.source)
+ this._canvas.imageLoaders[imgData.source] = [callback, imgData]
+ } else {
+ // Callback directly
+ callback(imgData)
+ }
}
+ const prerendered = Latex.findPrerendered(ltxText, this.textsize, color)
+ if(prerendered !== null)
+ onRendered(prerendered)
+ else
+ Latex.requestAsyncRender(ltxText, this.textsize, color).then(onRendered)
}
//
@@ -519,4 +523,4 @@ class CanvasAPI extends Module {
/** @type {CanvasAPI} */
Modules.Canvas = Modules.Canvas || new CanvasAPI()
-export const API = Modules.Canvas
\ No newline at end of file
+export const API = Modules.Canvas
diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/common.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/common.mjs
index 229a294..3df3a77 100644
--- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/common.mjs
+++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/common.mjs
@@ -115,12 +115,12 @@ export class Action {
if(!Latex.enabled)
throw new Error("Cannot render an item as LaTeX when LaTeX is disabled.")
let imgDepth = Modules.History.imageDepth
- let [src, width, height] = Latex.render(
+ let { source, width, height } = Latex.renderSync(
latexString,
imgDepth * (Modules.History.fontSize + 2),
Modules.History.themeTextColor
- ).split(",")
- return ``
+ )
+ return ``
}
/**
diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/latex.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/latex.mjs
index 7418b9c..306f5be 100644
--- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/latex.mjs
+++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/latex.mjs
@@ -39,6 +39,20 @@ const equivalchars = ["\\alpha","\\beta","\\gamma","\\delta","\\epsilon","\\zeta
"{}_{4}","{}_{5}","{}_{6}","{}_{7}","{}_{8}","{}_{9}","{}_{0}",
"\\pi", "\\infty"]
+/**
+ * Class containing the result of a LaTeX render.
+ *
+ * @property {string} source - Exported PNG file
+ * @property {number} width
+ * @property {number} height
+ */
+class LatexRenderResult {
+ constructor(source, width, height) {
+ this.source = source
+ this.width = parseFloat(width)
+ this.height = parseFloat(height)
+ }
+}
class LatexAPI extends Module {
constructor() {
@@ -50,11 +64,50 @@ class LatexAPI extends Module {
* true if latex has been enabled by the user, false otherwise.
*/
this.enabled = Helper.getSettingBool("enable_latex")
- /**
- * Mirror method for Python object.
- * @type {function(string, number, string): string}.
- */
- this.render = Latex.render
+ }
+
+ /**
+ * Prepares and renders a latex string into a png file.
+ *
+ * @param {string} markup - LaTeX markup to render.
+ * @param {number} fontSize - Font size (in pt) to render.
+ * @param {color} color - Color of the text to render.
+ * @returns {LatexRenderResult}
+ */
+ renderSync(markup, fontSize, color) {
+ let args = Latex.render(markup, fontSize, color).split(",")
+ return new LatexRenderResult(...args)
+ }
+
+ /**
+ * Checks if the given markup (with given font size and color) has already been
+ * rendered, and if so, returns its data. Otherwise, returns null.
+ *
+ * @param {string} markup - LaTeX markup to render.
+ * @param {number} fontSize - Font size (in pt) to render.
+ * @param {color} color - Color of the text to render.
+ * @returns {LatexRenderResult|null}
+ */
+ findPrerendered(markup, fontSize, color) {
+ const data = Latex.findPrerendered(markup, fontSize, color)
+ let ret = null
+ if(data !== "")
+ ret = new LatexRenderResult(...data.split(","))
+ return ret
+ }
+
+ /**
+ * Prepares and renders a latex string into a png file asynchronously.
+ *
+ * @param {string} markup - LaTeX markup to render.
+ * @param {number} fontSize - Font size (in pt) to render.
+ * @param {color} color - Color of the text to render.
+ * @returns {Promize}
+ */
+ requestAsyncRender(markup, fontSize, color) {
+ return new Promise(resolve => {
+ resolve(this.renderSync(markup, fontSize, color))
+ })
}
/**
diff --git a/LogarithmPlotter/util/latex.py b/LogarithmPlotter/util/latex.py
index 9ebb1c4..166e224 100644
--- a/LogarithmPlotter/util/latex.py
+++ b/LogarithmPlotter/util/latex.py
@@ -92,15 +92,13 @@ class Latex(QObject):
except Exception as e:
valid_install = False # Should have sent an error message if failed to render
return valid_install
-
@Slot(str, float, QColor, result=str)
def render(self, latex_markup: str, font_size: float, color: QColor) -> str:
"""
Prepares and renders a latex string into a png file.
"""
- markup_hash = "render" + str(hash(latex_markup))
- export_path = path.join(self.tempdir.name, f'{markup_hash}_{int(font_size)}_{color.rgb()}')
+ markup_hash, export_path = self.create_export_path(latex_markup, font_size, color)
if self.latexSupported and not path.exists(export_path + ".png"):
print("Rendering", latex_markup, export_path)
# Generating file
@@ -121,6 +119,28 @@ class Latex(QObject):
img = QImage(export_path)
# Small hack, not very optimized since we load the image twice, but you can't pass a QImage to QML and expect it to be loaded
return f'{export_path}.png,{img.width()},{img.height()}'
+
+ @Slot(str, float, QColor, result=str)
+ def findPrerendered(self, latex_markup: str, font_size: float, color: QColor):
+ """
+ Finds a prerendered image and returns its data if possible, and an empty string if not.
+ """
+ markup_hash, export_path = self.create_export_path(latex_markup, font_size, color)
+ data = ""
+ if path.exists(export_path + ".png"):
+ img = QImage(export_path)
+ data = f'{export_path}.png,{img.width()},{img.height()}'
+ return data
+
+
+ def create_export_path(self, latex_markup: str, font_size: float, color: QColor):
+ """
+ Standardizes export path for renders.
+ """
+ markup_hash = "render" + str(hash(latex_markup))
+ export_path = path.join(self.tempdir.name, f'{markup_hash}_{int(font_size)}_{color.rgb()}')
+ return markup_hash, export_path
+
def create_latex_doc(self, export_path: str, latex_markup: str):
"""