Skip to content
This repository has been archived by the owner on Aug 18, 2023. It is now read-only.

Commit

Permalink
- Fix the cache data not being saved correctly
Browse files Browse the repository at this point in the history
- Allow completely generic user commands
- Added console messages for when the plugin renders an SVG and when it uses one from the cache
- Fix unnecessary renders of SVGs
- Updated README examples
  • Loading branch information
fenjalien committed Feb 8, 2023
1 parent 6f7d011 commit a6f0573
Show file tree
Hide file tree
Showing 13 changed files with 1,034 additions and 7,410 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
# Changelog
## v0.1.0
- Fix the cache data not being saved correctly
- Allow completely generic user commands
- Added console messages for when the plugin renders an SVG and when it uses one from the cache
- Fix unnecessary renders of SVGs
- Updated README examples

## v0.0.3
- Added caching of in-use SVGs, they automatically get deleted once they are not in use.
## v0.0.2
Expand Down
38 changes: 9 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,45 +1,25 @@
# Obsidian Latex Renderer

This plugin renders codeblocks with the label `latex` into an SVG and displays them inline in the note on preview. You are required to bring your own command that outputs a `.svg` file from a `.tex` file input, examples are given below.
This plugin renders codeblocks with the label `latex` into an SVG and displays them inline in the note on preview. **You are required to bring your own command** that outputs a `.svg` file from a `.tex` file input, examples are given below.

# Setup
1. Install latex
1. Install latex system
2. Install this plugin
3. Set the command in settings

## Command Syntax
When these strings appear in your command, they will be replaced with their respective values. I would recommend wrapping them in quote marks `""` in the event of spaces.

`{tex-file}`: The relative path to the generated tex file with respect to the current working directory.
`{file-path}`: The absolute path to the file to be processed without the file extension. Most latex commands don't require the file extension, if one does you can just add it.

`{pdf-file}`: The absolute path to the generated pdf file.
### Example
latex -interaction=nonstopmode -halt-on-error -shell-escape "{file-path}" && dvisvgm "{file-path}"

`{output-dir}`: The absolute path to the current working directory.
This command uses `latex` to output a `.dvi` file and `dvisvgm` to convert the `.dvi` into an `.svg`. Both of these commands should be available from most tex systems.

## Examples
Note that these were tested on Windows only, I do not know if they work on other OSs.

### `pdflatex` and `pdf2svg2`
This is the first way I tried to generate latex to an `.svg`. The given command is set by default.

Install
- Miktex(this installs `pdflatex`): https://miktex.org/
- `pdf2svg2`: https://community.jalios.com/jcms/jc2_183627/en/pdf2svg2-bat-script

Set the command

pdflatex -interaction=nonstopmode -halt-on-error -shell-escape "{tex-file}" && pdf2svg "{pdf-file}" "{output-dir}"

You may want to use the absolute paths to `pdflatex` and `pdf2svg`.

### `tectonic` and `pdf2svg2` (recommended)
Install
- `tectonic`: https://tectonic-typesetting.github.io/en-US/
- `pdf2svg2`: https://community.jalios.com/jcms/jc2_183627/en/pdf2svg2-bat-script

Set the command
The text in the output of the above command make look strange because by default `dvisvgm` uses `<font>` tags which are not supported by Obsidian. To fix this the `--font-format` option can be set. (See: https://dvisvgm.de/Manpage/)

tectonic -X compile "{tex-file}" && pdf2svg2 "{pdf-file}" "{output-dir}"
latex -interaction=nonstopmode -halt-on-error -shell-escape "{file-path}" && dvisvgm --font-format=woff2,ah "{file-path}"

# Usage
The content inside of `latex` code blocks will be rendered using the given command. The document class `standalone` will be set for you using `\documentclass{standalone}`. You can load any packages you need with `\usepackage{}`.
Expand All @@ -52,7 +32,7 @@ By default the plugin will keep generated `.svg` files in `.obsidian/obsidian-la
This should allow (hasn't been tested) `latex` code blocks to appear as `.svg` in notes when the vault is synced across different devices that may not have `latex` installed. Just don't edit the code block otherwise it won't be happy.

## Examples
The svgs shown below have been generated in Obsidian with the setup in [`tectonic` and `pdf2svg2`](###-`tectonic`-and-`pdf2svg2`-(recommended))
The svgs shown below have been generated in Obsidian with the setup in

<img align="right" src="./assets/svg1.svg" style="background-color: white">

Expand Down
202 changes: 47 additions & 155 deletions assets/svg1.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
165 changes: 49 additions & 116 deletions assets/svg2.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7,139 changes: 638 additions & 6,501 deletions assets/svg3.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
395 changes: 106 additions & 289 deletions assets/svg4.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
158 changes: 47 additions & 111 deletions assets/svg5.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
283 changes: 99 additions & 184 deletions assets/svg6.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 24 additions & 18 deletions main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,14 @@ interface MyPluginSettings {
command: string,
timeout: number,
enableCache: boolean,
cache: Map<string, Set<string>>; // Key: md5 hash of latex source. Value: Set of file path names.
cache: Array<[string, Set<string>]>;
}

const DEFAULT_SETTINGS: MyPluginSettings = {
// `"${this.settings.pdflatexCommand}" -interaction=nonstopmode -halt-on-error -shell-escape "${texFile}" && "${this.settings.pdf2svgCommand}" "${path.join(cwd, pdfFile)}" "${cwd}"`
//
command: `pdflatex -interaction=nonstopmode -halt-on-error -shell-escape "{tex-file}" && pdf2svg "{pdf-file}" "{output-dir}"`,
command: ``,
timeout: 10000,
enableCache: true,
cache: new Map(),
cache: [],
}

export default class MyPlugin extends Plugin {
Expand All @@ -30,7 +28,6 @@ export default class MyPlugin extends Plugin {
async onload() {
await this.loadSettings();
if (this.settings.enableCache) await this.loadCache();

this.addSettingTab(new SampleSettingTab(this.app, this));
this.registerMarkdownCodeBlockProcessor("latex", (source, el, ctx) => this.renderLatexToElement(source, el, ctx));
}
Expand All @@ -54,7 +51,11 @@ export default class MyPlugin extends Plugin {
fs.mkdirSync(this.cacheFolderPath);
this.cache = new Map();
} else {
this.cache = this.settings.cache;
this.cache = new Map(this.settings.cache);
// For some reason `this.cache` at this point is actually `Map<string, Array<string>>`
for (const [k, v] of this.cache) {
this.cache.set(k, new Set(v))
}
}
}

Expand All @@ -67,24 +68,26 @@ export default class MyPlugin extends Plugin {
}

hashLatexSource(source: string) {
return Md5.hashStr(source);
return Md5.hashStr(source.trim());
}


async renderLatexToElement(source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext) {
return new Promise<void>((resolve, reject) => {
source = this.formatLatexSource(source);
let md5Hash = this.hashLatexSource(source);
let svgPath = path.join(this.cacheFolderPath, `${md5Hash}.svg`);

// SVG file has already been cached
// Could have a case where svgCache has the key but the cached file has been deleted
if (this.settings.enableCache && this.cache.has(md5Hash) && fs.existsSync(svgPath)) {
console.log("Using cached SVG: ", md5Hash);
el.innerHTML = fs.readFileSync(svgPath).toString();
this.addFileToCache(md5Hash, ctx.sourcePath);
resolve();
}
else {
console.log("Rendering SVG: ", md5Hash);

this.renderLatexToSVG(source, md5Hash, svgPath).then((v: string) => {
if (this.settings.enableCache) this.addFileToCache(md5Hash, ctx.sourcePath);
el.innerHTML = v;
Expand All @@ -97,21 +100,20 @@ export default class MyPlugin extends Plugin {

renderLatexToSVG(source: string, md5Hash: string, svgPath: string) {
return new Promise(async (resolve, reject) => {
let texFile = md5Hash + ".tex";
let pdfFile = md5Hash + ".pdf";
source = this.formatLatexSource(source);

temp.mkdir("obsidian-latex-renderer", (err, dirPath) => {
if (err) reject(err);
fs.writeFileSync(path.join(dirPath, texFile), source);
fs.writeFileSync(path.join(dirPath, md5Hash + ".tex"), source);
exec(
this.settings.command.replace("{tex-file}", texFile).replace("{pdf-file}", path.join(dirPath, pdfFile)).replace("{output-dir}", dirPath)
this.settings.command.replace(/{file-path}/g, md5Hash)
,
{ timeout: this.settings.timeout, cwd: dirPath },
async (err, stdout, stderr) => {
if (err) reject([err, stdout, stderr]);
else {
if (this.settings.enableCache) fs.copyFileSync(path.join(dirPath, "content1.svg"), svgPath);
let svgData = fs.readFileSync(path.join(dirPath, "content1.svg"));
if (this.settings.enableCache) fs.copyFileSync(path.join(dirPath, md5Hash + ".svg"), svgPath);
let svgData = fs.readFileSync(path.join(dirPath, md5Hash + ".svg"));
resolve(svgData);
};
},
Expand All @@ -121,8 +123,13 @@ export default class MyPlugin extends Plugin {
}

async saveCache() {
this.settings.cache = this.cache;
let temp = new Map();
for (const [k, v] of this.cache) {
temp.set(k, [...v])
}
this.settings.cache = [...temp];
await this.saveSettings();

}

addFileToCache(hash: string, file_path: string) {
Expand Down Expand Up @@ -196,8 +203,7 @@ export default class MyPlugin extends Plugin {
let lines = (await this.app.vault.read(file)).split('\n');
for (const section of sections) {
if (section.type != "code" && lines[section.position.start.line].match("``` *latex") == null) continue;
let file_source = lines.slice(section.position.start.line + 1, section.position.end.line).join("\n");
let source = this.formatLatexSource(file_source);
let source = lines.slice(section.position.start.line + 1, section.position.end.line).join("\n");
let hash = this.hashLatexSource(source);
hashes.push(hash);
}
Expand Down
4 changes: 2 additions & 2 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"id": "obsidian-latex-render",
"name": "Latex Renderer",
"version": "0.0.3",
"minAppVersion": "0.15.0",
"version": "0.1.0",
"minAppVersion": "1",
"description": "",
"author": "fenjalien",
"isDesktopOnly": true
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "obsidian-latex-render",
"version": "0.0.3",
"version": "0.1.0",
"description": "",
"main": "main.js",
"scripts": {
Expand Down
1 change: 1 addition & 0 deletions versions.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"0.1.0": "0.15.0",
"0.0.3": "0.15.0",
"0.0.2": "0.15.0",
"0.0.1": "0.15.0"
Expand Down

0 comments on commit a6f0573

Please sign in to comment.