[ADD] Add basic support for custom styles in LaTeX

I still need to fix that syncing issue.
This commit is contained in:
LoaD Accumulator 2023-06-13 16:36:36 +02:00
parent 1f49413e18
commit dff9dd2683
No known key found for this signature in database
GPG key ID: 6898757653ABE3E6
4 changed files with 73 additions and 15 deletions

View file

@ -1,21 +1,21 @@
from nio import * from nio import *
import utils
latex_regex = r'((?:\$[^\$]+\$)|(?:\$\$[^\$]+\$\$)|(?:\\\[[^\]]+\\\]))'
# Our message callback. It should be passed through a router. # Our message callback. It should be passed through a router.
async def msg_cb(room: MatrixRoom, event: RoomMessageText) -> None: async def msg_cb(room: MatrixRoom, event: RoomMessageText) -> None:
import re
import latex import latex
import utils
import re
client = utils.get_client() client = utils.get_client()
if event.sender == client.user_id: if event.sender == client.user_id:
return return
print("ae", client)
for tex in re.findall(r'((?:\$[^\$]+\$)|(?:\$\$[^\$]+\$\$)|(?:\\\[[^\]]+\\\]))', event.body, re.M): for tex in re.findall(latex_regex, event.body, re.M):
print("Text:", tex)
try: try:
filename = latex.render("", tex) filename = latex.render(event.sender, tex)
await utils.send_png(room, filename) await utils.send_png(room, filename)
except FileNotFoundError as e: except FileNotFoundError as e:
content = { content = {
@ -35,5 +35,20 @@ async def msg_cb(room: MatrixRoom, event: RoomMessageText) -> None:
# Our file callback. # Our file callback.
async def file_cb(room: MatrixRoom, event: RoomMessageFile) -> None: async def file_cb(room: MatrixRoom, event: RoomMessageFile) -> None:
import utils
import os
url = event.url url = event.url
filename = event.body filename = event.body
if filename.endswith(".sty"):
# Download file and save it for user.
client = utils.get_client()
directory = utils.create_user_dir(event.sender)
response = await client.download(url, filename)
if not isinstance(response, DownloadResponse):
return
with open(os.path.join(directory, filename), 'wb') as f:
f.write(response.body)

View file

@ -1,10 +1,12 @@
# Converts a TeX file into a PNG/SVG file. # Converts a TeX file into a PNG/SVG file.
template = r""" template = r"""
\documentclass[utf8,preview]{{standalone}} \documentclass[utf8,preview,varwidth]{{standalone}}
\usepackage{{amsmath}} \usepackage{{amsmath}}
\usepackage[active,tightpage]{{preview}} \usepackage[active]{{preview}}
\usepackage[utf8]{{inputenc}} \usepackage[utf8]{{inputenc}}
\usepackage{{tikz}}
% TODO: Add user packages here. % TODO: Add user packages here.
{packages}
\begin{{document}} \begin{{document}}
% The user content goes here. % The user content goes here.
{content} {content}
@ -16,13 +18,25 @@ template = r"""
def render(user: str, source: str, png: bool = True) -> str: def render(user: str, source: str, png: bool = True) -> str:
import tempfile import tempfile
import subprocess import subprocess
import utils
import os
fmt = template.format(content=source) # Put the user's packages in the format too.
directory = utils.create_user_dir(user)
pkgs = ""
for f in os.listdir(directory):
if f.endswith(".sty"):
base = os.path.join(directory, f[:-4])
pkgs += "\\usepackage{{{base}}}\n ".format(base=base)
print(pkgs)
fmt = template.format(content=source, packages=pkgs)
tmp = tempfile.NamedTemporaryFile(delete=False) tmp = tempfile.NamedTemporaryFile(delete=False)
tmp.write(bytes(fmt, encoding="utf8")) tmp.write(bytes(fmt, encoding="utf8"))
tmp.close() tmp.close()
print(tmp.name)
# Parse our templated file thru' LaTeX and dvipng. # Parse our templated file thru' LaTeX and dvipng.
# TODO: Allow arbitrary DPI and foreground color. # TODO: Allow arbitrary DPI and foreground color.
ret = subprocess.run(["latex", "-halt-on-error", tmp.name], cwd="/tmp", capture_output=True) ret = subprocess.run(["latex", "-halt-on-error", tmp.name], cwd="/tmp", capture_output=True)
@ -30,8 +44,7 @@ def render(user: str, source: str, png: bool = True) -> str:
raise FileNotFoundError(ret.stdout.decode()) raise FileNotFoundError(ret.stdout.decode())
if png: if png:
print("PNG") subprocess.run(["dvipng", "-D", "5000", tmp.name + ".dvi", "-bg", "Transparent",
subprocess.run(["dvipng", "-D", "2000", tmp.name + ".dvi", "-bg", "Transparent",
"-fg", "rgb 1 1 1", "-o", tmp.name + ".png"], cwd="/tmp") "-fg", "rgb 1 1 1", "-o", tmp.name + ".png"], cwd="/tmp")
return tmp.name + ".png" return tmp.name + ".png"

View file

@ -18,7 +18,8 @@ async def main() -> None:
homeserver = str(config["homeserver"]) homeserver = str(config["homeserver"])
user = str(config["user"]) user = str(config["user"])
token = str(config["token"]) token = str(config["token"])
# path = str(config["path"]) # not used path = str(config["data"]) # Unused there, required.
utils.set_config(config)
# if config file does not exist, quit # if config file does not exist, quit
except FileNotFoundError: except FileNotFoundError:
print("No config file found.") print("No config file found.")
@ -32,8 +33,8 @@ async def main() -> None:
client.access_token = token client.access_token = token
client.user_id = user client.user_id = user
# Bad kludge! # TODO: Fix this hot mess before merging onto main.
await client.sync(timeout=3000) client.sync(timeout=10000)
# Register all of the callbacks # Register all of the callbacks
utils.set_client(client) utils.set_client(client)

View file

@ -1,12 +1,41 @@
from nio import * from nio import *
def set_client(c: AsyncClient) -> None: def set_client(c: AsyncClient) -> None:
global client global client
client = c client = c
def get_client() -> AsyncClient: def get_client() -> AsyncClient:
return client return client
def set_config(c: dict) -> None:
global config
config = c
def get_config() -> dict:
return config
def create_user_dir(user: str) -> str:
import hashlib
import os
# NOTE: I would have used the user directly, but the Spec allows MXIDs
# with '/' and UNIX-based OSes will **not** like that.
sha = hashlib.sha256(bytes(user, 'ascii')).hexdigest()
path = os.path.join(get_config()["data"], sha)
if not os.path.exists(path):
os.makedirs(path)
return os.path.abspath(path)
async def send_png(room: MatrixRoom, filename: str) -> None: async def send_png(room: MatrixRoom, filename: str) -> None:
import imagesize import imagesize
import os import os