Generate booklets

This commit is contained in:
Oystein Kristoffer Tveit 2023-03-21 21:16:19 +01:00
parent 9429ea82ad
commit 7b7eb07cd8
Signed by: oysteikt
GPG Key ID: 9F2F7D8250F35146
5 changed files with 184 additions and 58 deletions

15
2page-booklet-wrapper.tex Normal file
View 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}

View File

@ -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"; };
}; };
}; };
} }

View File

@ -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}

View File

@ -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}

View File

@ -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