Generate booklets
This commit is contained in:
parent
9429ea82ad
commit
7b7eb07cd8
15
2page-booklet-wrapper.tex
Normal file
15
2page-booklet-wrapper.tex
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
\documentclass[a4paper]{article}
|
||||||
|
|
||||||
|
\usepackage[final]{pdfpages}
|
||||||
|
|
||||||
|
\newcommand*{\numberofpages}[1]{%
|
||||||
|
\the\XeTeXpdfpagecount"#1" %
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcounter{numberofsignaturepages}%
|
||||||
|
|
||||||
|
\begin{document}
|
||||||
|
\setcounter{numberofsignaturepages}{\numberofpages{yokutango.pdf}}%
|
||||||
|
\addtocounter{numberofsignaturepages}{4}%
|
||||||
|
\includepdf[pages=-,nup=1x2,landscape,signature=\thenumberofsignaturepages]{yokutango.pdf}
|
||||||
|
\end{document}
|
54
flake.nix
54
flake.nix
@ -28,13 +28,37 @@
|
|||||||
self.packages.${system}.noto-sans-cjk-jp
|
self.packages.${system}.noto-sans-cjk-jp
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mkYokutango-pdf = { paper ? "a4paper" }: let
|
||||||
|
template = lib.fileContents ./template.tex;
|
||||||
|
y2t-output-d = pkgs.runCommand "yokutango2tex-output" {} ''
|
||||||
|
${self.packages.${system}.yokutango2tex}/bin/yokutango2tex ${self.packages.${system}.yokutango-json} > $out
|
||||||
|
'';
|
||||||
|
y2t-output = lib.fileContents y2t-output-d;
|
||||||
|
combined = pkgs.writeText ''yokutango.tex'' (builtins.replaceStrings [
|
||||||
|
"% --- %"
|
||||||
|
"$paper$"
|
||||||
|
] [
|
||||||
|
y2t-output
|
||||||
|
paper
|
||||||
|
] template);
|
||||||
|
in pkgs.runCommand "yokutango-pdf" { inherit nativeBuildInputs FONTCONFIG_FILE; } ''
|
||||||
|
mkdir $out
|
||||||
|
ln -s ${combined} yokutango.tex
|
||||||
|
ln -s ${combined} $out/yokutango.tex
|
||||||
|
xelatex yokutango.tex
|
||||||
|
xelatex yokutango.tex
|
||||||
|
ln -s ${./2page-booklet-wrapper.tex} yokutango-book.tex
|
||||||
|
xelatex yokutango-book.tex
|
||||||
|
mv yokutango.pdf yokutango-book.pdf $out
|
||||||
|
'';
|
||||||
in {
|
in {
|
||||||
devshells.default = {
|
devShells.${system}.default = pkgs.mkShell {
|
||||||
inherit nativeBuildInputs FONTCONFIG_FILE;
|
inherit nativeBuildInputs FONTCONFIG_FILE;
|
||||||
};
|
};
|
||||||
|
|
||||||
packages.${system} = {
|
packages.${system} = {
|
||||||
default = self.packages.${system}.yokutango-pdf;
|
default = self.packages.${system}.yokutango-pdf-a5;
|
||||||
|
|
||||||
noto-sans-cjk-jp = pkgs.runCommandLocal "noto-sans-cjk-jp" {} ''
|
noto-sans-cjk-jp = pkgs.runCommandLocal "noto-sans-cjk-jp" {} ''
|
||||||
mkdir -p $out/share/fonts/opentype/noto-cjk
|
mkdir -p $out/share/fonts/opentype/noto-cjk
|
||||||
@ -45,25 +69,6 @@
|
|||||||
ln -s ${yokutango}/json $out
|
ln -s ${yokutango}/json $out
|
||||||
'';
|
'';
|
||||||
|
|
||||||
yokutango2tex = pkgs.writers.writeHaskellBin "yokutango2tex" {
|
|
||||||
libraries = with pkgs.haskellPackages; [ aeson bytestring ];
|
|
||||||
} (lib.fileContents ./yokutango2tex.hs);
|
|
||||||
|
|
||||||
yokutango-pdf = let
|
|
||||||
template = lib.fileContents ./template.tex;
|
|
||||||
y2t-output-d = pkgs.runCommand "yokutango2tex-output" {} ''
|
|
||||||
${self.packages.${system}.yokutango2tex}/bin/yokutango2tex ${self.packages.${system}.yokutango-json} > $out
|
|
||||||
'';
|
|
||||||
y2t-output = lib.fileContents y2t-output-d;
|
|
||||||
combined = pkgs.writeText ''yokutango.tex'' (builtins.replaceStrings ["% --- %"] [y2t-output] template);
|
|
||||||
in pkgs.runCommand "yokutango-pdf" { inherit nativeBuildInputs FONTCONFIG_FILE; } ''
|
|
||||||
mkdir $out
|
|
||||||
ln -s ${combined} yokutango.tex
|
|
||||||
ln -s ${combined} $out/yokutango.tex
|
|
||||||
xelatex yokutango.tex
|
|
||||||
mv yokutango.pdf $out
|
|
||||||
'';
|
|
||||||
|
|
||||||
yokutango-test = let
|
yokutango-test = let
|
||||||
template = lib.fileContents ./template.tex;
|
template = lib.fileContents ./template.tex;
|
||||||
body = lib.fileContents ./testContent.tex;
|
body = lib.fileContents ./testContent.tex;
|
||||||
@ -73,9 +78,16 @@
|
|||||||
ln -s ${combined} yokutango-test.tex
|
ln -s ${combined} yokutango-test.tex
|
||||||
ln -s ${combined} $out/yokutango-test.tex
|
ln -s ${combined} $out/yokutango-test.tex
|
||||||
xelatex yokutango-test.tex
|
xelatex yokutango-test.tex
|
||||||
|
xelatex yokutango-test.tex
|
||||||
mv yokutango-test.pdf $out
|
mv yokutango-test.pdf $out
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
yokutango2tex = pkgs.writers.writeHaskellBin "yokutango2tex" {
|
||||||
|
libraries = with pkgs.haskellPackages; [ aeson bytestring ];
|
||||||
|
} (lib.fileContents ./yokutango2tex.hs);
|
||||||
|
|
||||||
|
yokutango-pdf-a5 = mkYokutango-pdf { paper = "a5paper"; };
|
||||||
|
yokutango-pdf-a4 = mkYokutango-pdf { paper = "a4paper"; };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
44
template.tex
44
template.tex
@ -2,28 +2,58 @@
|
|||||||
|
|
||||||
\usepackage{fontspec}
|
\usepackage{fontspec}
|
||||||
\usepackage{xeCJK}
|
\usepackage{xeCJK}
|
||||||
\usepackage{ruby}
|
|
||||||
\usepackage{longtable}
|
\usepackage{longtable}
|
||||||
\usepackage{parskip}
|
\usepackage{parskip}
|
||||||
\usepackage{booktabs}
|
\usepackage[twoside,paper=$paper$]{geometry}
|
||||||
|
|
||||||
\usepackage[portrait,paper=a4paper,total={6in,8in}]{geometry}
|
|
||||||
|
|
||||||
\usepackage[dvipsnames, table]{xcolor}
|
\usepackage[dvipsnames, table]{xcolor}
|
||||||
\definecolor{rowColor}{RGB}{64, 120, 20}
|
\usepackage{mathtools}
|
||||||
|
\usepackage{multicol}
|
||||||
|
|
||||||
\setmainfont{Noto Sans}
|
\setmainfont{Noto Sans}
|
||||||
\setCJKmainfont{Noto Sans CJK JP}
|
\setCJKmainfont{Noto Sans CJK JP}
|
||||||
|
|
||||||
|
\definecolor{rowColor}{RGB}{149, 184, 230}
|
||||||
|
\definecolor{headerColor}{gray}{1}
|
||||||
\rowcolors{1}{}{rowColor!30!white}
|
\rowcolors{1}{}{rowColor!30!white}
|
||||||
|
|
||||||
\setlength{\parindent}{0pt}
|
\setlength{\parindent}{0pt}
|
||||||
\setlength{\parskip}{\baselineskip}
|
\setlength{\parskip}{\baselineskip}
|
||||||
|
|
||||||
\renewcommand{\rubysep}{1mm}
|
\setlength{\columnsep}{1.5cm}
|
||||||
|
\setlength{\columnseprule}{0.2pt}
|
||||||
|
|
||||||
|
\setcounter{secnumdepth}{0}
|
||||||
|
|
||||||
|
\newcommand{\ruby}[2]{$\stackrel{\text{#2}}{\text{#1}}$}
|
||||||
|
|
||||||
|
\renewcommand\arraystretch{1.5}
|
||||||
|
\setlength\extrarowheight{5pt}
|
||||||
\begin{document}
|
\begin{document}
|
||||||
|
|
||||||
|
\thispagestyle{empty}
|
||||||
|
|
||||||
|
\topskip0pt
|
||||||
|
\vspace*{\fill}
|
||||||
|
\begin{center}
|
||||||
|
{
|
||||||
|
\fontsize{30pt}{36pt}\normalfont
|
||||||
|
よく単語
|
||||||
|
}
|
||||||
|
|
||||||
|
\vspace*{1cm}
|
||||||
|
|
||||||
|
March 2023
|
||||||
|
\end{center}
|
||||||
|
\vspace*{\fill}
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
|
||||||
|
\begin{multicols}{2}
|
||||||
|
\tableofcontents
|
||||||
|
\end{multicols}
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
|
||||||
% --- %
|
% --- %
|
||||||
|
|
||||||
\end{document}
|
\end{document}
|
||||||
|
@ -1,10 +1,17 @@
|
|||||||
\def\arraystretch{1.5}
|
\def\arraystretch{1.5}
|
||||||
\begin{longtable}[]{@{}ll@{}}
|
\setlength\extrarowheight{5pt}
|
||||||
\toprule
|
\begin{longtable}[]{|l|c@{}|l@{}|l|}
|
||||||
日本語 & Norsk \\
|
Status & 日本語 & Norsk & Notes \\
|
||||||
\midrule
|
|
||||||
\endhead
|
\endhead
|
||||||
\ruby{言葉}{ことば} & ord \\
|
& \ruby{統合失調症}{とうごうしっちょうしょう} & schizofrenia & \\
|
||||||
\ruby{文}{ぶん} & setning \\
|
& \ruby{点火する}{てんかする} & å sette fyr & \\
|
||||||
\bottomrule
|
& \ruby{石英}{せきえい} & kvarts & \\
|
||||||
|
& \ruby{房}{ふさ} & en bunke & \\
|
||||||
|
& \ruby{新来者}{しんらいしゃ} & nykommer & \\
|
||||||
|
& \ruby{伝わる}{つたわる} & å sende (et signal) & \\
|
||||||
|
& \ruby{伝わる}{つたわる} & å sende $\begin{cases} \text{et signal} \\ \text{en melding} \\ \text{bølger} \\ \text{to transmit} \end{cases}$ & \\
|
||||||
|
& \ruby{伝わる}{つたわる} & \begin{tabular}{l@{}}
|
||||||
|
å sende (et signal) \\
|
||||||
|
å sende $\begin{pmatrix} \text{et signal} \\ \text{en melding} \\ \text{bølger} \\ \text{to transmit} \end{pmatrix}$ \\
|
||||||
|
\end{tabular} & \\
|
||||||
\end{longtable}
|
\end{longtable}
|
||||||
|
104
yokutango2tex.hs
104
yokutango2tex.hs
@ -2,7 +2,6 @@
|
|||||||
{-# LANGUAGE DeriveGeneric #-}
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
{-# LANGUAGE DuplicateRecordFields #-}
|
{-# LANGUAGE DuplicateRecordFields #-}
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE RecordWildCards #-}
|
|
||||||
{-# LANGUAGE NamedFieldPuns #-}
|
{-# LANGUAGE NamedFieldPuns #-}
|
||||||
|
|
||||||
import Data.Aeson
|
import Data.Aeson
|
||||||
@ -12,7 +11,8 @@ import System.Directory
|
|||||||
import System.Environment
|
import System.Environment
|
||||||
import System.FilePath
|
import System.FilePath
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.List (intersperse)
|
import Data.List (intersperse, sort)
|
||||||
|
import Control.Applicative ((<|>))
|
||||||
|
|
||||||
import qualified Data.Text.Lazy as T
|
import qualified Data.Text.Lazy as T
|
||||||
import qualified Data.Text.Lazy.IO as T
|
import qualified Data.Text.Lazy.IO as T
|
||||||
@ -48,13 +48,33 @@ instance FromJSON Card
|
|||||||
instance ToJSON Card where
|
instance ToJSON Card where
|
||||||
toEncoding = genericToEncoding defaultOptions
|
toEncoding = genericToEncoding defaultOptions
|
||||||
|
|
||||||
|
data LaTeXRow = LaTeXRow { jw :: T.Text
|
||||||
|
, nw :: T.Text
|
||||||
|
}
|
||||||
|
|
||||||
|
(+?) :: Semigroup a => a -> Maybe a -> a
|
||||||
|
a +? b = case b of
|
||||||
|
Nothing -> a
|
||||||
|
Just b' -> a <> b'
|
||||||
|
|
||||||
|
flipAppend :: T.Text -> T.Text -> T.Text
|
||||||
|
flipAppend = flip T.append
|
||||||
|
|
||||||
|
wrap :: T.Text -> T.Text -> T.Text -> T.Text
|
||||||
|
wrap s e w = mconcat [s, w, e]
|
||||||
|
|
||||||
|
indent :: T.Text -> T.Text
|
||||||
|
indent = mconcat
|
||||||
|
. map (wrap "\t" "\n")
|
||||||
|
. T.splitOn "\n"
|
||||||
|
|
||||||
readJsonFile :: FilePath -> IO (Either String WordBlock)
|
readJsonFile :: FilePath -> IO (Either String WordBlock)
|
||||||
readJsonFile path = do
|
readJsonFile path = do
|
||||||
fileContent <- BS.readFile path
|
fileContent <- BS.readFile path
|
||||||
return $ cardsToWordblock <$> eitherDecode fileContent
|
return $ cardsToWordblock <$> eitherDecode fileContent
|
||||||
where
|
where
|
||||||
formatPath :: FilePath -> T.Text
|
formatPath :: FilePath -> T.Text
|
||||||
formatPath = T.append " "
|
formatPath = T.append "単語 "
|
||||||
. fromMaybe "???"
|
. fromMaybe "???"
|
||||||
. T.stripPrefix "yokutango_"
|
. T.stripPrefix "yokutango_"
|
||||||
. fromString
|
. fromString
|
||||||
@ -64,42 +84,84 @@ readJsonFile path = do
|
|||||||
, cards = cards
|
, cards = cards
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cardToRow :: Card -> LaTeXRow
|
||||||
cardToRow :: Card -> T.Text
|
cardToRow card = LaTeXRow { jw = japanesePart card
|
||||||
cardToRow card = mconcat [ japanesePart card, " $\\longleftrightarrow$ ", norwegianPart card ]
|
, nw = norwegianPart card
|
||||||
|
}
|
||||||
where
|
where
|
||||||
|
ruby :: T.Text -> T.Text -> T.Text
|
||||||
|
ruby main top = mconcat [ "\\ruby{", main, "}{", top, "}" ]
|
||||||
|
|
||||||
|
subtable :: [T.Text] -> T.Text
|
||||||
|
subtable rows = mconcat [ "\\begin{tabular}{@{}l@{}}\n"
|
||||||
|
, mconcat $ map (\t -> mconcat ["\t", t, " \\\\\n"]) rows
|
||||||
|
, "\\end{tabular}"
|
||||||
|
]
|
||||||
|
|
||||||
|
hintsToPmatrix :: [T.Text] -> T.Text
|
||||||
|
hintsToPmatrix = wrap " $\\begin{pmatrix}" "\\end{pmatrix}$"
|
||||||
|
. mconcat
|
||||||
|
. intersperse " \\\\ "
|
||||||
|
. map (wrap "\\text{" "}")
|
||||||
|
|
||||||
jpWordToText :: JapaneseWord -> T.Text
|
jpWordToText :: JapaneseWord -> T.Text
|
||||||
jpWordToText JapaneseWord { word, romaji, hints } =
|
jpWordToText JapaneseWord { word, romaji, hints } =
|
||||||
case (word, romaji, hints) of
|
case hints of
|
||||||
(w, Just r, _) -> mconcat [ "\\ruby{", w, "}{", r, "}" ]
|
Nothing -> furiganaBlock
|
||||||
(w, Nothing, _) -> w
|
(Just [hint]) -> mconcat [ furiganaBlock, " (", hint, ")" ]
|
||||||
|
(Just hints) -> subtable [mconcat [furiganaBlock, hintsToPmatrix hints ]]
|
||||||
|
where
|
||||||
|
rubyStr = ruby word <$> romaji
|
||||||
|
furiganaBlock = fromMaybe word rubyStr
|
||||||
|
|
||||||
noWordToText :: NorwegianWord -> T.Text
|
noWordToText :: NorwegianWord -> T.Text
|
||||||
noWordToText NorwegianWord { word, hints } = word
|
noWordToText NorwegianWord { word, hints } =
|
||||||
|
case hints of
|
||||||
japanesePart (Card { japanese }) = mconcat $ intersperse ("\\\\\n" :: T.Text) $ map jpWordToText japanese
|
Nothing -> word
|
||||||
norwegianPart (Card { norwegian }) = mconcat $ intersperse ("\\\\\n" :: T.Text) $ map noWordToText norwegian
|
Just [hint] -> mconcat [word, " (", hint, ")"]
|
||||||
|
Just hints -> subtable [mconcat [ word, hintsToPmatrix hints]]
|
||||||
|
|
||||||
|
japanesePart :: Card -> T.Text
|
||||||
|
japanesePart (Card { japanese = [jpWord] }) = jpWordToText jpWord
|
||||||
|
japanesePart (Card { japanese }) = subtable $ map jpWordToText japanese
|
||||||
|
|
||||||
|
norwegianPart :: Card -> T.Text
|
||||||
|
norwegianPart (Card { norwegian = [noWord] }) = noWordToText noWord
|
||||||
|
norwegianPart (Card { norwegian }) = subtable $ map noWordToText norwegian
|
||||||
|
|
||||||
wordblockToTable :: WordBlock -> T.Text
|
wordblockToTable :: WordBlock -> T.Text
|
||||||
wordblockToTable block = mconcat tablePieces
|
wordblockToTable block = mconcat tablePieces
|
||||||
where
|
where
|
||||||
rows = map ((\t -> T.append t "\\\\\n") . cardToRow) (cards block)
|
rows :: [LaTeXRow]
|
||||||
tablePieces = [ "\\section*{", title block, "}\n" ] ++ rows ++ ["\\newpage"]
|
rows = map cardToRow (cards block)
|
||||||
|
|
||||||
|
rowToText :: LaTeXRow -> T.Text
|
||||||
|
rowToText (LaTeXRow { jw, nw }) = indent $ mconcat [ " & ", jw, " & ", nw, " & \\\\\n" ]
|
||||||
|
|
||||||
|
tablePieces :: [T.Text]
|
||||||
|
tablePieces = [ "\\section{", title block, "}\n"
|
||||||
|
, "\\begin{longtable}{|l|l@{}|l@{}|l|}\n"
|
||||||
|
, "\\hline\n"
|
||||||
|
, "\\rowcolor{headerColor}\n"
|
||||||
|
, "Status & 日本語 & Norsk & Extra Notes \\\\\n"
|
||||||
|
, "\\hline\n"
|
||||||
|
, "\\endhead\n"
|
||||||
|
, "\\hline"
|
||||||
|
, "\\endfoot"
|
||||||
|
]
|
||||||
|
++ map rowToText rows
|
||||||
|
++ [ "\\end{longtable}\n"
|
||||||
|
, "\\newpage\n\n"
|
||||||
|
]
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = do
|
main = do
|
||||||
dir <- head <$> getArgs
|
dir <- head <$> getArgs
|
||||||
filePaths <- map (\x -> joinPath [dir, x]) <$> listDirectory dir
|
filePaths <- sort . map (\x -> joinPath [dir, x]) <$> listDirectory dir
|
||||||
wordBlocks :: Either str [WordBlock] <- sequence <$> mapM readJsonFile filePaths
|
wordBlocks :: Either str [WordBlock] <- sequence <$> mapM readJsonFile filePaths
|
||||||
|
|
||||||
let output = case wordBlocks of
|
let output = case wordBlocks of
|
||||||
Right blocks -> mconcat $ map wordblockToTable blocks
|
Right blocks -> mconcat $ map wordblockToTable blocks
|
||||||
Left err -> fromString err
|
Left err -> fromString err
|
||||||
|
|
||||||
BS.putStr $ T.encodeUtf8 $ output
|
BS.putStr $ T.encodeUtf8 output
|
||||||
-- case jsonFiles of
|
|
||||||
-- Right cards -> mapM_ print cards
|
|
||||||
-- Left err -> putStr err
|
|
||||||
|
Loading…
Reference in New Issue
Block a user