Skip to content

Commit

Permalink
Add simplified Moire, font; fix GitHub Actions
Browse files Browse the repository at this point in the history
We add simplified version of Moire parser instead of installing it from
the index, since it is small enough and has no dependencies. We also
embed Doulos SIL, Noto Sans Korean and CMU Serif Roman fonts for XeLaTeX
build. This should fix GitHub actions.
  • Loading branch information
enzet committed Aug 15, 2024
1 parent 22899f8 commit ed67518
Show file tree
Hide file tree
Showing 10 changed files with 749 additions and 21 deletions.
17 changes: 9 additions & 8 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,21 @@ jobs:
with:
source-dir: .
build-dir: build
- name: Set up Python 3.9
uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Create TeX file
run: |
mkdir out
build/language > out/ipa.tex
cp data/*.tex out/
- name: Construct `table.pdf`
uses: xu-cheng/latex-action@v3
with:
working_directory: out
root_file: table.tex
latexmk_use_xelatex: true
python python/moire_converter.py --input data/text.moi --output out/text.tex --format tex
- name: Construct `text.pdf`
uses: xu-cheng/latex-action@v3
with:
working_directory: out
root_file: text.tex
latexmk_use_xelatex: true
extra_fonts: |
../fonts/DoulosSIL-Regular.ttf
../fonts/NotoSansKR-VariableFont_wght.ttf
../fonts/cmunrm.ttf
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@ Requirements:

* C++20 compiler,
* CMake version 3.30 or higher,
* `xelatex` compiler ([XeTeX](https://tug.org/xetex/)),
* [Doulos SIL](https://software.sil.org/doulos/download/) font.
* `xelatex` compiler ([XeTeX](https://tug.org/xetex/)).

We include fonts under Open Font License (`fonts` directory):

* [Doulos SIL](https://software.sil.org/doulos/download/),
* [Noto Sans Korean](https://fonts.google.com/noto/specimen/Noto+Sans+KR),
* [CMU Serif Roman](https://cm-unicode.sourceforge.io/index.html).

To get a PDF file with the alphabet, run

Expand All @@ -27,7 +32,6 @@ It will create `build` directory and `out` directory with output PDF file.

Language utility has two commands: `table` and `symbol`:


* `table <rows> <columns>`, where `rows` is the list of phoneme parameters separated by `,`. E.g. `table "dental,alveolar" "trill;voiceless,trill;voiced"`.
* `symbol <descriptors>`, where `descriptors` is the list of symbol element descriptors. E.g. `symbol vc hc`.

Expand Down
9 changes: 7 additions & 2 deletions doc/readme.moi
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,13 @@ Requirements:
\list
{C++20 compiler,}
{CMake version 3.30 or higher,}
{\m {xelatex} compiler (\ref {https://tug.org/xetex/} {XeTeX}),}
{\ref {https://software.sil.org/doulos/download/} {Doulos SIL} font.}
{\m {xelatex} compiler (\ref {https://tug.org/xetex/} {XeTeX}).}

We include fonts under Open Font License (\m {fonts} directory):
\list
{\ref {https://software.sil.org/doulos/download/} {Doulos SIL},}
{\ref {https://fonts.google.com/noto/specimen/Noto+Sans+KR} {Noto Sans Korean},}
{\ref {https://cm-unicode.sourceforge.io/index.html} {CMU Serif Roman}.}

To get a PDF file with the alphabet, run

Expand Down
Binary file added fonts/DoulosSIL-Regular.ttf
Binary file not shown.
Binary file added fonts/NotoSansKR-Regular.ttf
Binary file not shown.
Binary file added fonts/cmunrm.ttf
Binary file not shown.
169 changes: 169 additions & 0 deletions python/moire/default.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import sys
from argparse import ArgumentParser, Namespace
from typing import Any, Dict, List, Set, Tuple
from textwrap import dedent

from moire.moire import Moire

__author__ = "Sergey Vartanov"
__email__ = "[email protected]"

depth = 0
status = {}
BLOCK_TAGS: Set[str] = {
"block", "body", "code", "title", "number", "list", "image", "table"
} # fmt: skip
Arguments = List[Any]


class TagNotImplementedError(NotImplementedError):
"""Tag is not implemented in the parser."""

def __init__(self, tag: str = "") -> None:
self.tag: str = tag

def __str__(self) -> str:
return f"Tag \\{self.tag} is not implemented in the parser"


class Default(Moire):
"""Default tag declaration."""

def __init__(self) -> None:
super().__init__()

def title(self, arg: Arguments) -> str:
"""Document title."""
return ""

def header(self, arg: Arguments, level: int) -> str:
"""Header.
Arguments: <header text> <header identifier>?
"""
raise TagNotImplementedError("header")

def m(self, arg: Arguments) -> str:
"""Monospaced text."""
raise TagNotImplementedError("m")


class DefaultTeX(Default):
"""TeX syntax."""

name = "Tex"
id_: str = "tex"
extension = "tex"

escape_symbols = {
"_": "\\_",
}
block_tags = BLOCK_TAGS
headers: List[str] = [
"section", "subsection", "subsubsection", "paragraph", "subparagraph"
] # fmt: skip

def body(self, arg: Arguments) -> str:
s = dedent(
"""\
\\documentclass[twoside,psfig]{article}
\\usepackage[utf8]{inputenc}
\\usepackage[russian]{babel}
\\usepackage{enumitem}
\\usepackage{float}
\\usepackage[margin=3cm,hmarginratio=1:1,top=32mm,columnsep=20pt]
{geometry}
\\usepackage{graphicx}
\\usepackage{hyperref}
\\usepackage{multicol}
\\begin{document}
"""
)
s += self.parse(arg[0], in_block=True)
s += "\\end {document}"
return s

def title(self, arg: Arguments) -> str:
s = f"\\title{{{self.parse(arg[0])}}}\n"
s += "\\maketitle"
return s

def author(self, arg: Arguments) -> str:
return f"\\author{{{self.parse(arg[0])}}}"

def header(self, arg: Arguments, number: int) -> str:
if number < 6:
return f"\\{self.headers[number - 1]}{{{self.parse(arg[0])}}}"
return self.parse(arg[0])

def table(self, arg: Arguments) -> str:
s = "\\begin{table}[h]\n\\begin{center}\n\\begin{tabular}{|"
max_tds = 0
for tr in arg:
if isinstance(tr, list):
tds = 0
for td in tr:
if isinstance(td, list):
tds += 1
if tds > max_tds:
max_tds = tds
for k in range(max_tds):
s += "l|"
s += "}\n\\hline\n"
for tr in arg:
if isinstance(tr, list):
tds = []
for td in tr:
if isinstance(td, list):
tds.append(td)
for td in tds[:-1]:
s += self.parse(td) + " & "
s += self.parse(tds[-1])
s += " \\\\\n\\hline\n"
s += "\\end{tabular}\n\\end{center}\n\\end{table}\n"
return s

def list__(self, arg: Arguments) -> str:
s = "\\begin{itemize}\n"
for item in arg:
s += f"\\item {self.parse(item)}\n\n"
s += "\\end{itemize}\n"
return s

def abstract(self, arg: Arguments) -> str:
return (
"\\begin{abstract}\n\n"
+ self.parse(arg[0], in_block=True)
+ "\\end{abstract}\n\n"
)

def date(self, arg: Arguments) -> str:
pass

def text(self, arg: Arguments) -> str:
return self.parse(arg[0]) + "\n\n"

def m(self, arg: Arguments) -> str:
return "{\\tt " + self.parse(arg[0]) + "}"


if __name__ == "__main__":
parser: ArgumentParser = ArgumentParser()

parser.add_argument("-i", "--input", help="Moire input file", required=True)
parser.add_argument("-o", "--output", help="output file", required=True)
parser.add_argument("-f", "--format", help="output format", required=True)

options: Namespace = parser.parse_args(sys.argv[1:])

with open(options.input, "r") as input_file:
converter: Moire = getattr(sys.modules[__name__], options.format)()
output: str = converter.convert(input_file.read())

if not output:
print("Fatal: output was no produced.")
sys.exit(1)

with open(options.output, "w+") as output_file:
output_file.write(output)
print(f"Converted to {options.output}.")
64 changes: 64 additions & 0 deletions python/moire/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"""
Command line Python tool for file conversion from Moire markup language to other
formats, such as HTML, TeX, etc.
"""

import logging
import sys
from argparse import ArgumentParser, Namespace
from pathlib import Path
from typing import List, Optional

from moire.default import Default
from moire.moire import Moire

__author__ = "Sergey Vartanov"
__email__ = "[email protected]"


def main(arguments: List[str] = None, top_class=None):
if not arguments:
arguments = sys.argv[1:]
if not top_class:
top_class = Default

logging.basicConfig(level=logging.INFO, format="%(message)s")

parser: ArgumentParser = ArgumentParser()

parser.add_argument("-i", "--input", help="Moire input file", required=True)
parser.add_argument("-o", "--output", help="output file")
parser.add_argument("-f", "--format", help="output format", required=True)
parser.add_argument("--wrap", action="store_true", default=True)

options: Namespace = parser.parse_args(arguments)

converter: Optional[Moire] = None
for class_ in top_class.__subclasses__():
if class_.id_ == options.format:
converter = class_()

if not converter:
logging.fatal(
f"No converter class found for format `{options.format}`."
)
exit(1)

with Path(options.input).open() as input_file:
converter.file_name = options.input
output: str = converter.convert(input_file.read(), wrap=options.wrap)

if not output:
logging.fatal("No output was produced.")
sys.exit(1)

if options.output:
with open(options.output, "w") as output_file:
output_file.write(output)
logging.info(f"Converted to {options.output}.")
else:
sys.stdout.write(output)


if __name__ == "__main__":
main(sys.argv[1:], Default)
Loading

0 comments on commit ed67518

Please sign in to comment.