diff --git a/flake.lock b/flake.lock index 2dbe3c8..60af1d8 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1678972866, - "narHash": "sha256-YV8BcNWfNVgS449B6hFYFUg4kwVIQMNehZP+FNDs1LY=", + "lastModified": 1679139072, + "narHash": "sha256-Gtw2Yj8DfETie3u7iHv1y5Wt+plGRmp6nTQ0EEfaPho=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "cd34d6ed7ba7d5c4e44b04a53dc97edb52f2766c", + "rev": "08ef7dc8334521605a5c8b7086cc248e74ee338b", "type": "github" }, "original": { @@ -15,9 +15,22 @@ "type": "indirect" } }, + "noto-cjk-jp-otf-regular": { + "flake": false, + "locked": { + "narHash": "sha256-o4SDCDPzhXj7A7qEslRiB5fbGtyqpy4fiVis7fnscN0=", + "type": "file", + "url": "https://github.com/notofonts/noto-cjk/raw/main/Sans/OTF/Japanese/NotoSansCJKjp-Regular.otf" + }, + "original": { + "type": "file", + "url": "https://github.com/notofonts/noto-cjk/raw/main/Sans/OTF/Japanese/NotoSansCJKjp-Regular.otf" + } + }, "root": { "inputs": { "nixpkgs": "nixpkgs", + "noto-cjk-jp-otf-regular": "noto-cjk-jp-otf-regular", "yokutango": "yokutango" } }, diff --git a/flake.nix b/flake.nix index bd6a099..e3ea9a9 100644 --- a/flake.nix +++ b/flake.nix @@ -1,25 +1,81 @@ { inputs = { nixpkgs.url = "nixpkgs/nixos-22.11"; + yokutango = { url = "git+https://git.nani.wtf/h7x4/yokutango?ref=master"; flake = false; }; + + noto-cjk-jp-otf-regular = { + url = "https://github.com/notofonts/noto-cjk/raw/main/Sans/OTF/Japanese/NotoSansCJKjp-Regular.otf"; + flake = false; + }; }; - outputs = { self, nixpkgs, yokutango }: let + outputs = { self, nixpkgs, yokutango, noto-cjk-jp-otf-regular }: let system = "x86_64-linux"; pkgs = nixpkgs.legacyPackages.${system}; + inherit (pkgs) lib; + + nativeBuildInputs = with pkgs; [ + self.packages.${system}.yokutango2tex + texlive.combined.scheme-full + ]; + + FONTCONFIG_FILE = pkgs.makeFontsConf { + fontDirectories = with pkgs; [ + noto-fonts + self.packages.${system}.noto-sans-cjk-jp + ]; + }; in { + devshells.default = { + inherit nativeBuildInputs FONTCONFIG_FILE; + }; + packages.${system} = { + default = self.packages.${system}.yokutango-pdf; + + noto-sans-cjk-jp = pkgs.runCommandLocal "noto-sans-cjk-jp" {} '' + mkdir -p $out/share/fonts/opentype/noto-cjk + ln -s ${noto-cjk-jp-otf-regular} $out/share/fonts/opentype/noto-cjk/NotoSansCJKjp-Regular.otf + ''; + yokutango-json = pkgs.runCommandLocal "yokutango-json" {} '' ln -s ${yokutango}/json $out ''; - yokutango2tex = pkgs.writers.writeHaskell "yokutango2tex" { + + yokutango2tex = pkgs.writers.writeHaskellBin "yokutango2tex" { libraries = with pkgs.haskellPackages; [ aeson bytestring ]; - } (pkgs.lib.fileContents ./yokutango2tex.hs); - yokutango-pdf = pkgs.runCommand "yokutango-pdf" {} '' - ${self.packages.${system}.yokutango2tex} ${self.packages.${system}.yokutango-json} + } (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 + template = lib.fileContents ./template.tex; + body = lib.fileContents ./testContent.tex; + combined = pkgs.writeText ''yokutango-test.tex'' (builtins.replaceStrings ["% --- %"] [body] template); + in pkgs.runCommand "yokutango-pdf" { inherit nativeBuildInputs FONTCONFIG_FILE; } '' + mkdir $out + ln -s ${combined} yokutango-test.tex + ln -s ${combined} $out/yokutango-test.tex + xelatex yokutango-test.tex + mv yokutango-test.pdf $out + ''; + }; }; } diff --git a/template.tex b/template.tex new file mode 100644 index 0000000..95b445d --- /dev/null +++ b/template.tex @@ -0,0 +1,29 @@ +\documentclass{article} + +\usepackage{fontspec} +\usepackage{xeCJK} +\usepackage{ruby} +\usepackage{longtable} +\usepackage{parskip} +\usepackage{booktabs} + +\usepackage[portrait,paper=a4paper,total={6in,8in}]{geometry} + +\usepackage[dvipsnames, table]{xcolor} +\definecolor{rowColor}{RGB}{64, 120, 20} + +\setmainfont{Noto Sans} +\setCJKmainfont{Noto Sans CJK JP} + +\rowcolors{1}{}{rowColor!30!white} + +\setlength{\parindent}{0pt} +\setlength{\parskip}{\baselineskip} + +\renewcommand{\rubysep}{1mm} + +\begin{document} + +% --- % + +\end{document} diff --git a/testContent.tex b/testContent.tex new file mode 100644 index 0000000..79fb5bb --- /dev/null +++ b/testContent.tex @@ -0,0 +1,10 @@ +\def\arraystretch{1.5} +\begin{longtable}[]{@{}ll@{}} +\toprule +日本語 & Norsk \\ +\midrule +\endhead + \ruby{言葉}{ことば} & ord \\ + \ruby{文}{ぶん} & setning \\ +\bottomrule +\end{longtable} diff --git a/yokutango2tex.hs b/yokutango2tex.hs index 8c9c558..26c24b9 100644 --- a/yokutango2tex.hs +++ b/yokutango2tex.hs @@ -1,26 +1,35 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE NamedFieldPuns #-} import Data.Aeson +import Data.String (fromString) import GHC.Generics import System.Directory import System.Environment import System.FilePath +import Data.Maybe +import Data.List (intersperse) +import qualified Data.Text.Lazy as T +import qualified Data.Text.Lazy.IO as T +import qualified Data.Text.Lazy.Encoding as T import qualified Data.ByteString.Lazy as BS -data NorwegianWord = NorwegianWord { word :: String - , hints :: Maybe [String] +data NorwegianWord = NorwegianWord { word :: T.Text + , hints :: Maybe [T.Text] } deriving (Generic, Show) instance FromJSON NorwegianWord instance ToJSON NorwegianWord where toEncoding = genericToEncoding defaultOptions -data JapaneseWord = JapaneseWord { word :: String - , romaji :: Maybe String - , hints :: Maybe [String] +data JapaneseWord = JapaneseWord { word :: T.Text + , romaji :: Maybe T.Text + , hints :: Maybe [T.Text] } deriving (Generic, Show) instance FromJSON JapaneseWord @@ -31,18 +40,66 @@ data Card = Card { norwegian :: [NorwegianWord] , japanese :: [JapaneseWord] } deriving (Generic, Show) +data WordBlock = WordBlock { title :: T.Text + , cards :: [Card] + } deriving (Show) + instance FromJSON Card instance ToJSON Card where toEncoding = genericToEncoding defaultOptions -readJsonFile :: FilePath -> IO (Either String [Card]) -readJsonFile path = eitherDecode <$> BS.readFile path +readJsonFile :: FilePath -> IO (Either String WordBlock) +readJsonFile path = do + fileContent <- BS.readFile path + return $ cardsToWordblock <$> eitherDecode fileContent + where + formatPath :: FilePath -> T.Text + formatPath = T.append " " + . fromMaybe "???" + . T.stripPrefix "yokutango_" + . fromString + . takeBaseName + + cardsToWordblock cards = WordBlock { title = formatPath path + , cards = cards + } + + +cardToRow :: Card -> T.Text +cardToRow card = mconcat [ japanesePart card, " $\\longleftrightarrow$ ", norwegianPart card ] + where + jpWordToText :: JapaneseWord -> T.Text + jpWordToText JapaneseWord { word, romaji, hints } = + case (word, romaji, hints) of + (w, Just r, _) -> mconcat [ "\\ruby{", w, "}{", r, "}" ] + (w, Nothing, _) -> w + + noWordToText :: NorwegianWord -> T.Text + noWordToText NorwegianWord { word, hints } = word + + japanesePart (Card { japanese }) = mconcat $ intersperse ("\\\\\n" :: T.Text) $ map jpWordToText japanese + norwegianPart (Card { norwegian }) = mconcat $ intersperse ("\\\\\n" :: T.Text) $ map noWordToText norwegian + + + + +wordblockToTable :: WordBlock -> T.Text +wordblockToTable block = mconcat tablePieces + where + rows = map ((\t -> T.append t "\\\\\n") . cardToRow) (cards block) + tablePieces = [ "\\section*{", title block, "}\n" ] ++ rows ++ ["\\newpage"] main :: IO () main = do dir <- head <$> getArgs filePaths <- map (\x -> joinPath [dir, x]) <$> listDirectory dir - jsonFiles <- mapM readJsonFile filePaths + wordBlocks :: Either str [WordBlock] <- sequence <$> mapM readJsonFile filePaths + + let output = case wordBlocks of + Right blocks -> mconcat $ map wordblockToTable blocks + Left err -> fromString err + + BS.putStr $ T.encodeUtf8 $ output -- case jsonFiles of -- Right cards -> mapM_ print cards -- Left err -> putStr err