Skip to content

Commit eb0cf2f

Browse files
committed
Switched fonts to support other languages
1 parent ae6fb16 commit eb0cf2f

6 files changed

Lines changed: 65 additions & 9 deletions

File tree

Dockerfile

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,22 @@ RUN dnf install -y \
4242
texlive-collection-latexrecommended.noarch \
4343
texlive-iftex.noarch \
4444
texlive-braket.noarch \
45-
texlive-cancel.noarch
45+
texlive-cancel.noarch \
46+
texlive-xecjk.noarch
47+
48+
# Install Noto Sans fonts: base (Latin/Greek/Cyrillic) + CJK (Korean/Chinese/Japanese)
49+
# These are the default fonts used by the template for broad Unicode coverage.
50+
RUN curl -fsSL https://github.com/notofonts/latin-greek-cyrillic/releases/download/NotoSans-v2.013/NotoSans-v2.013.zip \
51+
-o /tmp/NotoSans.zip \
52+
&& unzip -j /tmp/NotoSans.zip "*/unhinted/ttf/NotoSans-Regular.ttf" \
53+
"*/unhinted/ttf/NotoSans-Bold.ttf" \
54+
"*/unhinted/ttf/NotoSans-Italic.ttf" \
55+
"*/unhinted/ttf/NotoSans-BoldItalic.ttf" \
56+
-d /usr/share/fonts/noto \
57+
&& rm /tmp/NotoSans.zip \
58+
&& curl -fsSL https://github.com/googlefonts/noto-cjk/releases/download/Sans2.004R/NotoSansCJKkr-Regular.otf \
59+
-o /usr/share/fonts/noto/NotoSansCJKkr-Regular.otf \
60+
&& fc-cache -fv
4661

4762
# Copy the LaTeX template
4863
COPY ./src/template.latex template.latex

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ In addition to the runtime dependencies above, running the full test suite local
6565
On macOS these can be installed via Homebrew:
6666
```bash
6767
brew install pandoc mactex poppler
68+
brew install --cask font-noto-sans font-noto-sans-cjk
6869
```
6970

7071
### Running tests

index.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,16 @@ describe("schema", () => {
5252
const data = { userId: "u1", fileName: "doc", typeOfFile: "PDF", markdown: "text" };
5353
expect(schema.safeParse(data).success).toBe(false);
5454
});
55+
56+
it("validates a request with a variables map", () => {
57+
const data = [{ userId: "u1", fileName: "doc", typeOfFile: "PDF", markdown: "text", variables: { lang: "ko", CJKmainfont: "Noto Sans CJK KR" } }];
58+
expect(schema.safeParse(data).success).toBe(true);
59+
});
60+
61+
it("rejects variables with non-string values", () => {
62+
const data = [{ userId: "u1", fileName: "doc", typeOfFile: "PDF", markdown: "text", variables: { lang: 42 } }];
63+
expect(schema.safeParse(data).success).toBe(false);
64+
});
5565
});
5666

5767
describe("handler", () => {
@@ -95,6 +105,18 @@ describe("handler", () => {
95105
expect(result.statusCode).toBe(200);
96106
});
97107

108+
it("passes variables as --variable flags to Pandoc", async () => {
109+
const executeMock = vi.fn().mockResolvedValue("");
110+
vi.mocked(PdcTs).mockImplementationOnce(() => ({ Execute: executeMock }) as any);
111+
112+
const event = [{ userId: "user1", fileName: "test-doc", typeOfFile: "TEX", markdown: "# Hello", variables: { lang: "ko", CJKmainfont: "Noto Sans CJK KR" } }];
113+
await handler(event as any);
114+
115+
const calledArgs: string[] = executeMock.mock.calls[0]?.[0]?.pandocArgs ?? [];
116+
expect(calledArgs).toContain("--variable=lang:ko");
117+
expect(calledArgs).toContain("--variable=CJKmainfont:Noto Sans CJK KR");
118+
});
119+
98120
it("returns 500 when Pandoc execution fails", async () => {
99121
vi.mocked(PdcTs).mockImplementationOnce(() => ({
100122
Execute: vi.fn()

index.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export const schema = z.array(
1414
typeOfFile: TypeOfFileSchema,
1515
markdown: z.string(),
1616
implicitFigures: z.boolean().optional(),
17+
variables: z.record(z.string(), z.string()).optional(),
1718
})
1819
);
1920

@@ -147,13 +148,15 @@ export const handler = async function (
147148
for (let eachRequestData of requestData) {
148149
const markdown = eachRequestData.markdown;
149150
const implicitFigures = eachRequestData.implicitFigures;
151+
const variableArgs = Object.entries(eachRequestData.variables ?? {})
152+
.map(([k, v]) => `--variable=${k}:${v}`);
150153

151154
switch (eachRequestData.typeOfFile) {
152155
case "PDF":
153156
const filenamePDF = `${eachRequestData.fileName}.pdf`;
154157
const localPathPDF = `/tmp/${filenamePDF}`;
155158
const generatePDFResult = await generateFile(
156-
["--pdf-engine=xelatex", `--template=./template.latex`],
159+
["--pdf-engine=xelatex", `--template=./template.latex`, ...variableArgs],
157160
localPathPDF,
158161
markdown,
159162
implicitFigures
@@ -170,7 +173,7 @@ export const handler = async function (
170173
const filenameTEX = `${eachRequestData.fileName}.tex`;
171174
const localPathTEX = `/tmp/${filenameTEX}`;
172175
await generateFile(
173-
[`--template=./template.latex`],
176+
[`--template=./template.latex`, ...variableArgs],
174177
localPathTEX,
175178
markdown,
176179
implicitFigures

src/pandoc.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,21 @@ describe("Pandoc markdown → LaTeX output", () => {
7878
});
7979
});
8080

81+
describe("pandoc variables", () => {
82+
it("accepts a --variable flag without error", async () => {
83+
// Verifies the variable-passing mechanism works; mainfont is a safe no-op variable
84+
// since the template only applies it under XeLaTeX, which isn't invoked here
85+
const latex = await new PdcTs().Execute({
86+
from: "markdown",
87+
to: "latex",
88+
outputToFile: false,
89+
sourceText: "# Hello",
90+
pandocArgs: ["--variable=mainfont:Latin Modern Roman"],
91+
});
92+
expect(latex).toContain("\\section{Hello}");
93+
});
94+
});
95+
8196
describe("unicode characters", () => {
8297
it("passes Greek letters in prose through to LaTeX", async () => {
8398
const latex = await toLatex("Lowercase: α, β, γ, Δ, Σ");

src/template.latex

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ $if(euro)$
3333
$endif$
3434
$if(mainfont)$
3535
\setmainfont[$for(mainfontoptions)$$mainfontoptions$$sep$,$endfor$]{$mainfont$}
36+
$else$
37+
\setmainfont{Noto Sans}
3638
$endif$
3739
$if(sansfont)$
3840
\setsansfont[$for(sansfontoptions)$$sansfontoptions$$sep$,$endfor$]{$sansfont$}
@@ -43,10 +45,8 @@ $endif$
4345
$if(mathfont)$
4446
\setmathfont(Digits,Latin,Greek)[$for(mathfontoptions)$$mathfontoptions$$sep$,$endfor$]{$mathfont$}
4547
$endif$
46-
$if(CJKmainfont)$
47-
\usepackage{xeCJK}
48-
\setCJKmainfont[$for(CJKoptions)$$CJKoptions$$sep$,$endfor$]{$CJKmainfont$}
49-
$endif$
48+
\usepackage{xeCJK}
49+
\setCJKmainfont[$for(CJKoptions)$$CJKoptions$$sep$,$endfor$]{$if(CJKmainfont)$$CJKmainfont$$else$Noto Sans CJK KR$endif$}
5050
\fi
5151
% use upquote if available, for straight quotes in verbatim environments
5252
\IfFileExists{upquote.sty}{\usepackage{upquote}}{}
@@ -260,8 +260,8 @@ $endif$
260260

261261

262262
\usepackage[a4paper,top=2cm,bottom=2cm,left=2cm,right=2cm]{geometry}%
263-
\usepackage{lmodern}% font style must be sans serif to comply with
264-
\renewcommand*\familydefault{\sfdefault}% the college's regulations with respect to disabilities!
263+
% Noto Sans (sans-serif) is set as the default mainfont above, satisfying the
264+
% college's accessibility requirement for sans-serif fonts throughout the document.
265265

266266

267267
\begin{document}

0 commit comments

Comments
 (0)