Browse Source

Formátítko se nyní umí pustit jen jako KaTeX server a vrátit socket, který se dá předávat jiným formátítkům.

Jan Černohorský 1 year ago
parent
commit
440c6a6f9f
  1. 16
      src/formatitko/formatitko.py
  2. 5
      src/formatitko/katex-server/index.mjs
  3. 29
      src/formatitko/katex.py

16
src/formatitko/formatitko.py

@ -29,12 +29,24 @@ def main():
parser.add_argument("-t", "--output-tex", help="The TEX file to write into.") parser.add_argument("-t", "--output-tex", help="The TEX file to write into.")
parser.add_argument("-m", "--output-md", help="The Markdown file to write into. (Uses pandoc to generate markdown)") parser.add_argument("-m", "--output-md", help="The Markdown file to write into. (Uses pandoc to generate markdown)")
parser.add_argument("-j", "--output-json", help="The JSON file to dump the pandoc-compatible AST into.") parser.add_argument("-j", "--output-json", help="The JSON file to dump the pandoc-compatible AST into.")
parser.add_argument("input_filename", help="The markdown file to process.") parser.add_argument("--katex-server", action='store_true', help="Starts a KaTeX server and prints the socket filename onto stdout. Useful for running formatitko many times without starting the KaTeX server each time.")
parser.add_argument("-k", "--katex-socket", help="The KaTeX server socket filename obtained by running with `--katex-server`.")
parser.add_argument("input_filename", help="The markdown file to process.", nargs="?" if "--katex-server" in sys.argv else None)
parser.add_argument("--debug", action='store_true') parser.add_argument("--debug", action='store_true')
args = parser.parse_args() args = parser.parse_args()
# TODO: Accept path to unix socket for katexClient, then don't init our own, # TODO: Accept path to unix socket for katexClient, then don't init our own,
# just connect to an existing one. For formátíking many files in a row. # just connect to an existing one. For formátíking many files in a row.
if args.katex_server:
with KatexClient(connect=False) as katexClient:
print(katexClient.get_socket())
try:
while True:
pass
except KeyboardInterrupt:
print("Exiting...")
return
# Use panflute to parse the input MD file # Use panflute to parse the input MD file
doc = import_md(open(args.input_filename, "r").read()) doc = import_md(open(args.input_filename, "r").read())
@ -48,7 +60,7 @@ def main():
if args.output_html is not None: if args.output_html is not None:
# Initialize KaTeX client (this runs the node app and connects to a unix socket) # Initialize KaTeX client (this runs the node app and connects to a unix socket)
with KatexClient() as katexClient: with KatexClient(socket=args.katex_socket) as katexClient:
HTMLGenerator(open(args.output_html, "w"), katexClient, imageProcessor).generate(doc) HTMLGenerator(open(args.output_html, "w"), katexClient, imageProcessor).generate(doc)
if args.output_md is not None: if args.output_md is not None:

5
src/formatitko/katex-server/index.mjs

@ -83,7 +83,7 @@ async function handleClient(client) {
* the one from the parent group and can add its own stuff without * the one from the parent group and can add its own stuff without
* affecting the parent. * affecting the parent.
*/ */
const macroStack = [{}] let macroStack = [{}]
for await (const line of rl) { for await (const line of rl) {
try { try {
// The custom commands for pushing and popping the macro stack. // The custom commands for pushing and popping the macro stack.
@ -94,6 +94,9 @@ async function handleClient(client) {
} else if (line === "endgroup") { } else if (line === "endgroup") {
macroStack.pop() macroStack.pop()
continue continue
} else if (line === "init") {
macroStack = [{}]
continue
} }
const query = JSON.parse(line) const query = JSON.parse(line)
const results = [] const results = []

29
src/formatitko/katex.py

@ -19,8 +19,21 @@ class KatexClient:
_server_process: subprocess.Popen[bytes] _server_process: subprocess.Popen[bytes]
_socket_file: str _socket_file: str
_temp_dir: tempfile.TemporaryDirectory[str] _temp_dir: tempfile.TemporaryDirectory[str]
_connected: bool
def __init__(self): def __init__(self, socket: str=None, connect: bool=True):
if socket is not None:
self._socket_file = socket
else:
self.open_socket()
if connect:
self.connect()
self._client.sendall("init\n".encode("utf-8")) # Reinitialize KaTeX Server in case it was reused.
self._connected = True
else:
self._connected = False
def open_socket(self):
# Create temporary directory for socket # Create temporary directory for socket
self._temp_dir = tempfile.TemporaryDirectory(prefix='formatitko') self._temp_dir = tempfile.TemporaryDirectory(prefix='formatitko')
self._socket_file = self._temp_dir.name + "/katex-socket" self._socket_file = self._temp_dir.name + "/katex-socket"
@ -40,15 +53,20 @@ class KatexClient:
self._server_process = subprocess.Popen(["node", srcdir + "/katex-server/index.mjs", self._socket_file], stdout=subprocess.PIPE) self._server_process = subprocess.Popen(["node", srcdir + "/katex-server/index.mjs", self._socket_file], stdout=subprocess.PIPE)
self._client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
ok = self._server_process.stdout.readline() ok = self._server_process.stdout.readline()
if ok != b"OK\n": if ok != b"OK\n":
raise KatexServerError("Failed to connect to katex-server") raise KatexServerError("Failed to connect to katex-server")
def connect(self):
self._client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
self._client.connect(self._socket_file) self._client.connect(self._socket_file)
def get_socket(self):
return self._socket_file
def render(self, tex: str, options: dict={}): def render(self, tex: str, options: dict={}):
if not self._connected:
raise KatexServerError("KatexClient not connected to Katex server. It should be initialized with connect=True.")
# Send formulas to translate # Send formulas to translate
self._client.sendall((json.dumps({"formulas":[{"tex":tex}], "options":options})+"\n").encode("utf-8")) self._client.sendall((json.dumps({"formulas":[{"tex":tex}], "options":options})+"\n").encode("utf-8"))
@ -67,13 +85,18 @@ class KatexClient:
# Special commands implemented in the JS file for grouping defs together. # Special commands implemented in the JS file for grouping defs together.
def begingroup(self): def begingroup(self):
if not self._connected:
raise KatexServerError("KatexClient not connected to Katex server. It should be initialized with connect=True.")
self._client.sendall("begingroup\n".encode("utf-8")) self._client.sendall("begingroup\n".encode("utf-8"))
def endgroup(self): def endgroup(self):
if not self._connected:
raise KatexServerError("KatexClient not connected to Katex server. It should be initialized with connect=True.")
self._client.sendall("endgroup\n".encode("utf-8")) self._client.sendall("endgroup\n".encode("utf-8"))
def __enter__(self): def __enter__(self):
return self return self
def __exit__(self, type, value, tb): def __exit__(self, type, value, tb):
if hasattr(self, "_server_process") and self._server_process is not None:
self._server_process.terminate() self._server_process.terminate()

Loading…
Cancel
Save