├── .circleci └── config.yml ├── .gitignore ├── .gitmodules ├── Gemfile ├── Gruntfile.js ├── LICENSE.txt ├── README.md ├── articles ├── afterword.re ├── book.css ├── catalog.yml ├── conclusion.re ├── config.yml ├── epub_style.scss ├── images │ ├── .gitkeep │ ├── cover-print.png │ ├── cover-web.jpg │ ├── cover │ │ └── original.afdesign │ ├── issue │ │ ├── bad-list-and-footnote.png │ │ └── bad-reset-hashbang.png │ ├── practice │ │ ├── box-example.png │ │ └── header.png │ ├── syntax-example │ │ ├── sample-image1.png │ │ ├── sample-image2.png │ │ └── sample-numberlessimage.png │ └── why-css-typesetting │ │ └── questionnaire.png ├── issue.re ├── layouts │ ├── layout.html.erb │ └── layout.tex.erb ├── locale.yml ├── practice.re ├── preface.re ├── prh.yml ├── review-ext.rb ├── sty │ ├── bxglyphwiki.sty │ ├── jumoline.sty │ ├── pdftexcmds.sty │ ├── reviewmacro.sty │ ├── samplemacro.sty │ ├── tatsumacro.sty │ ├── techbooster-doujin.sty │ └── ulem.sty ├── style-web.scss ├── style.scss ├── syntax-example.re ├── whats-css-typesetting.re └── why-css-typesetting.re ├── build-in-docker.sh ├── package-lock.json ├── package.json ├── redpen-conf-ja.xml ├── scripts ├── main.ts └── tsconfig.json └── setup.sh /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | docker: 5 | - image: vvakame/review:2.4 6 | steps: 7 | - checkout 8 | - restore_cache: 9 | keys: 10 | - npm-cache-{{ checksum "package-lock.json" }} 11 | - run: 12 | name: Setup 13 | command: npm install 14 | - save_cache: 15 | key: npm-cache-{{ checksum "package-lock.json" }} 16 | paths: 17 | - ./node_modules 18 | - run: 19 | name: Setup 20 | command: bundler install 21 | - run: 22 | name: Test 23 | command: npm test 24 | - run: 25 | name: Build PDF by LaTeX 26 | command: npm run pdf:latex 27 | - store_artifacts: 28 | path: ./articles/review-css-typesetting.pdf 29 | destination: review-css-typesetting.pdf 30 | - run: 31 | name: Build CSS 32 | command: npm run css 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | articles/*.pdf 3 | articles/*-pdf/ 4 | 5 | # Graphvizとか使うと生成されるやつ 6 | articles/images/html/ 7 | articles/images/latex/ 8 | 9 | # 他の環境で作ったlockがあるとCIがコケる 10 | Gemfile.lock 11 | 12 | .DS_Store 13 | Thumbs.db 14 | 15 | distribute/ 16 | 17 | articles/*.html 18 | scripts/*.js 19 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "prh-rules"] 2 | path = prh-rules 3 | url = https://github.com/prh/rules.git 4 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # A sample Gemfile 2 | source "https://rubygems.org" 3 | 4 | gem 'review', '2.4.0' 5 | gem 'review-peg', '0.2.2' 6 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | let fs = require("fs"); 4 | let yaml = require("js-yaml"); 5 | 6 | const articles = "articles"; 7 | const bookConfig = yaml.safeLoad(fs.readFileSync(`${articles}/config.yml`, "utf8")); 8 | 9 | const reviewPrefix = process.env["REVIEW_PREFIX"] || "bundle exec "; 10 | const reviewPostfix = process.env["REVIEW_POSTFIX"] || ""; // REVIEW_POSTFIX="-peg" npm run pdf とかするとPEGでビルドできるよ 11 | const reviewPreproc = `${reviewPrefix}review-preproc${reviewPostfix}`; 12 | const reviewCompile = `${reviewPrefix}review-compile${reviewPostfix}`; 13 | const reviewPdfMaker = `${reviewPrefix}review-pdfmaker${reviewPostfix}`; 14 | const reviewEpubMaker = `${reviewPrefix}review-epubmaker${reviewPostfix}`; 15 | 16 | module.exports = grunt => { 17 | grunt.initConfig({ 18 | clean: { 19 | review: { 20 | src: [ 21 | `${articles}/${bookConfig.bookname}-*/`, // pdf, epub temp dir 22 | `${articles}/*.pdf`, 23 | `${articles}/*.epub`, 24 | `${articles}/*.html`, 25 | `${articles}/*.md`, 26 | `${articles}/*.xml`, 27 | `${articles}/*.txt` 28 | ] 29 | } 30 | }, 31 | shell: { 32 | preprocess: { 33 | options: { 34 | execOptions: { 35 | cwd: articles, 36 | } 37 | }, 38 | command: `${reviewPreproc} -r --tabwidth=2 *.re` 39 | }, 40 | compile2text: { 41 | options: { 42 | execOptions: { 43 | cwd: articles, 44 | } 45 | }, 46 | command: `${reviewCompile} --target=text` 47 | }, 48 | compile2markdown: { 49 | options: { 50 | execOptions: { 51 | cwd: articles, 52 | } 53 | }, 54 | command: `${reviewCompile} --target=markdown` 55 | }, 56 | compile2html: { 57 | options: { 58 | execOptions: { 59 | cwd: articles, 60 | } 61 | }, 62 | command: `${reviewCompile} --target=html --stylesheet=style.css --chapterlink` 63 | }, 64 | compile2latex: { 65 | options: { 66 | execOptions: { 67 | cwd: articles, 68 | } 69 | }, 70 | command: `${reviewCompile} --target=latex --footnotetext` 71 | }, 72 | compile2idgxml: { 73 | options: { 74 | execOptions: { 75 | cwd: articles, 76 | } 77 | }, 78 | command: `${reviewCompile} --target=idgxml` 79 | }, 80 | compile2pdf: { 81 | options: { 82 | execOptions: { 83 | cwd: articles, 84 | } 85 | }, 86 | command: `${reviewPdfMaker} config.yml` 87 | }, 88 | compile2epub: { 89 | options: { 90 | execOptions: { 91 | cwd: articles, 92 | } 93 | }, 94 | command: `${reviewEpubMaker} config.yml` 95 | } 96 | } 97 | }); 98 | 99 | function generateTask(target) { 100 | return ["clean", "shell:preprocess", `shell:compile2${target}`]; 101 | } 102 | 103 | grunt.registerTask( 104 | "default", 105 | "原稿をコンパイルしてPDFファイルにする", 106 | "pdf"); 107 | 108 | grunt.registerTask( 109 | "text", 110 | "原稿をコンパイルしてTextファイルにする", 111 | generateTask("text")); 112 | 113 | grunt.registerTask( 114 | "markdown", 115 | "原稿をコンパイルしてMarkdownファイルにする", 116 | generateTask("markdown")); 117 | 118 | grunt.registerTask( 119 | "html", 120 | "原稿をコンパイルしてHTMLファイルにする", 121 | generateTask("html")); 122 | 123 | grunt.registerTask( 124 | "idgxml", 125 | "原稿をコンパイルしてInDesign用XMLファイルにする", 126 | generateTask("idgxml")); 127 | 128 | grunt.registerTask( 129 | "pdf", 130 | "原稿をコンパイルしてpdfファイルにする", 131 | generateTask("pdf")); 132 | 133 | grunt.registerTask( 134 | "epub", 135 | "原稿をコンパイルしてepubファイルにする", 136 | generateTask("epub")); 137 | 138 | require('load-grunt-tasks')(grunt); 139 | }; 140 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 vvakame 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Re:VIEW+CSS組版やっていき [![CircleCI](https://circleci.com/gh/vvakame/review-css-typesetting.svg?style=svg)](https://circleci.com/gh/vvakame/review-css-typesetting) 2 | 3 | このリポジトリは技術書典4にて[ひかる黄金わかめ帝国](https://techbookfest.org/event/tbf04/circle/11680001)が頒布した『Re:VIEW+CSS組版やっていき』の全てです。 4 | [Re:VIEW](https://github.com/kmuto/review/)と[Vivliostyle](https://vivliostyle.org/)を使って、同人誌をこしらえます。 5 | 6 | ## この本のビルドの仕方 7 | 8 | ### LaTeX版 9 | 10 | Dockerのセットアップが必要です。 11 | 12 | ``` 13 | $ ./build-in-docker.sh 14 | $ open ./articles/review-css-typesetting.pdf 15 | ``` 16 | 17 | ### CSS版 18 | 19 | 頒布時は Vivliostyle 2017.6 + Google Chrome Version 65.0.3325.181 を利用しました。 20 | 21 | https://vivliostyle.org/download/ からVivliostyle.jsをダウンロード。 22 | 23 | ``` 24 | # Vivliostyleを解凍したディレクトリで 25 | $ ./start-webserver 26 | ``` 27 | ``` 28 | # このリポジトリのディレクトリで 29 | $ ./setup.sh 30 | $ npm run css 31 | $ npm run serve 32 | $ open http://127.0.0.1:8000/viewer/vivliostyle-viewer.html#x=http://127.0.0.1:8989/book.html 33 | ``` 34 | 35 | あとはChromeの印刷機能で出力します。 36 | MarginsはNone、Background graphicsのチェックはONでPDFとして保存します。 37 | 38 | ## ライセンス 39 | 40 | 本リポジトリは画像以外はMITライセンス、画像はライセンスなしとします。 41 | 画像を別用途に勝手に使ったりしなければ、forkなどについては難しく考えなくて大丈夫です。 42 | 43 | ## このリポジトリの目標 44 | 45 | 1. LaTeX版の出力に近づける 46 | 2. LaTeX版同様のワークフローを実現する 47 | 3. CSS組版のナレッジを集積する 48 | 49 | ### コントリビュートについて 50 | 51 | 上記目標に沿う内容であれば随時受け付けています。 52 | また、Issue化されていなくても書籍中で言及されている課題についての改善は大歓迎です。 53 | リポジトリ内をTODOで検索して、これを消化してくれるのも助かります。 54 | -------------------------------------------------------------------------------- /articles/afterword.re: -------------------------------------------------------------------------------- 1 | = おわりに 2 | 3 | やっていってください。 4 | 以上! 5 | 6 | ページの余白に、当日準備と印刷見本の作り方をメモしておきます。 7 | このやり方ははじめてなので、上手くいってなかったら察してください。 8 | 9 | 大原則として、頒布は省力でやる。 10 | どうせ当日運営やっててブースにいないし…。 11 | 12 | お金の処理はめんどくさいので後払い決済のみとする。 13 | 頒布物は電子のみとし、印刷は行わない。 14 | そもそも印刷所に入稿できるレベルに辿り着いてない…。 15 | TechBoooster式の電書カード製造@{qrcode-generator}は個別にQRコード生成するのめんどい。 16 | 印刷したシールを裏に貼るのもめんどい。 17 | どうせ全部公開するし、提供するQRコードは1種類でいいんじゃないかな! 18 | 前回"にきてきな"でやってみた結果、イベント前日の工数が吸われてやばかったですからね…。 19 | 見本誌も用意し忘れたし。 20 | なので、今回は性善説で攻めて行くぞ! 21 | 失うものは小銭しかない! 22 | 23 | #@# prh:disable 24 | //footnote[qrcode-generator][@{https://github.com/TechBooster/C89-FirstStepReVIEW-v2/tree/master/qr-generator}] 25 | 26 | 見本誌はセブンイレブンの同人印刷@{doujin-print}を利用する。 27 | LaTeX版とCSS版の両方の見本誌を用意して見比べてもらうようにしたい。 28 | 表紙画像1枚(PNG)をカラープリントする。 29 | 中身(PDF)は全部白黒プリントする。 30 | なお、印刷所入稿ではないのでトンボは出力しないほうがよさそうなので消しとく。 31 | 2つをガッチャンコして巻き巻きしたら完成だ! 32 | 印刷所さんに頼む時も表紙データと中身は別で入稿するし何も違いはない…! 33 | 34 | //footnote[doujin-print][@{http://www.doujinshi-print.com/}] 35 | 36 | お品書きとかは一応用意する。 37 | 後払い用QRコード、サークル名、頒布物、書影、ざっくりした概要、価格、あとでGitHubで全文公開予定!、見本誌が2種類あるよ、持って帰らないでね、あたりを盛り込めばいいかな? 38 | これを適当にA4とかにまとめてカラー印刷でフィニッシュだ! 39 | 40 | やっぱりLaTeX版のほうが出力の再現性が高くて、ページの余白ギリギリまで書くみたいな調整がし易い気がする。 41 | LaTeX版はビルドは長いけど、終わったら安定した出力が得られる。 42 | CSS版は表示は一瞬だけど、レンダリングが安定するのに時間がかかるし今のところバッドノウハウが多い。 43 | という気持ち。 44 | -------------------------------------------------------------------------------- /articles/book.css: -------------------------------------------------------------------------------- 1 | @media print { 2 | body { 3 | font-family: 'Noto Sans JP', sans-serif; 4 | } 5 | /* @import url(https://fonts.googleapis.com/earlyaccess/notosansjp.css); */ 6 | /* 7 | * Noto Sans JP (japanese) http://www.google.com/fonts/earlyaccess 8 | */ 9 | @font-face { 10 | font-family: 'Noto Sans JP'; 11 | font-style: normal; 12 | font-weight: 100; 13 | src: local("Noto Sans CJK JP Thin"), 14 | url(https://fonts.gstatic.com/ea/notosansjp/v5/NotoSansJP-Thin.woff2) format('woff2'), 15 | url(https://fonts.gstatic.com/ea/notosansjp/v5/NotoSansJP-Thin.woff) format('woff'), 16 | url(https://fonts.gstatic.com/ea/notosansjp/v5/NotoSansJP-Thin.otf) format('opentype'); 17 | } 18 | @font-face { 19 | font-family: 'Noto Sans JP'; 20 | font-style: normal; 21 | font-weight: 300; 22 | src: local("Noto Sans CJK JP Light"), 23 | url(https://fonts.gstatic.com/ea/notosansjp/v5/NotoSansJP-Light.woff2) format('woff2'), 24 | url(https://fonts.gstatic.com/ea/notosansjp/v5/NotoSansJP-Light.woff) format('woff'), 25 | url(https://fonts.gstatic.com/ea/notosansjp/v5/NotoSansJP-Light.otf) format('opentype'); 26 | } 27 | @font-face { 28 | font-family: 'Noto Sans JP'; 29 | font-style: normal; 30 | font-weight: 400; 31 | src: local("Noto Sans CJK JP Regular"), 32 | url(https://fonts.gstatic.com/ea/notosansjp/v5/NotoSansJP-Regular.woff2) format('woff2'), 33 | url(https://fonts.gstatic.com/ea/notosansjp/v5/NotoSansJP-Regular.woff) format('woff'), 34 | url(https://fonts.gstatic.com/ea/notosansjp/v5/NotoSansJP-Regular.otf) format('opentype'); 35 | } 36 | @font-face { 37 | font-family: 'Noto Sans JP'; 38 | font-style: normal; 39 | font-weight: 500; 40 | src: local("Noto Sans CJK JP Medium"), 41 | url(https://fonts.gstatic.com/ea/notosansjp/v5/NotoSansJP-Medium.woff2) format('woff2'), 42 | url(https://fonts.gstatic.com/ea/notosansjp/v5/NotoSansJP-Medium.woff) format('woff'), 43 | url(https://fonts.gstatic.com/ea/notosansjp/v5/NotoSansJP-Medium.otf) format('opentype'); 44 | } 45 | @font-face { 46 | font-family: 'Noto Sans JP'; 47 | font-style: normal; 48 | font-weight: 700; 49 | src: local("Noto Sans CJK JP Bold"), 50 | url(https://fonts.gstatic.com/ea/notosansjp/v5/NotoSansJP-Bold.woff2) format('woff2'), 51 | url(https://fonts.gstatic.com/ea/notosansjp/v5/NotoSansJP-Bold.woff) format('woff'), 52 | url(https://fonts.gstatic.com/ea/notosansjp/v5/NotoSansJP-Bold.otf) format('opentype'); 53 | } 54 | @font-face { 55 | font-family: 'Noto Sans JP'; 56 | font-style: normal; 57 | font-weight: 900; 58 | src: local("Noto Sans CJK JP Black"), 59 | url(https://fonts.gstatic.com/ea/notosansjp/v5/NotoSansJP-Black.woff2) format('woff2'), 60 | url(https://fonts.gstatic.com/ea/notosansjp/v5/NotoSansJP-Black.woff) format('woff'), 61 | url(https://fonts.gstatic.com/ea/notosansjp/v5/NotoSansJP-Black.otf) format('opentype'); 62 | } 63 | } 64 | 65 | body { 66 | page-break-before: left; 67 | counter-reset: footnote; 68 | } 69 | @media screen { 70 | :root { 71 | line-height: 1.3; 72 | } 73 | .print-only { 74 | display: none; 75 | } 76 | } 77 | @media print { 78 | :root { 79 | font-weight: 400; 80 | line-height: 1.5; 81 | padding-top: 1.5rem; 82 | } 83 | /* A5設定 */ 84 | :root { 85 | font-size: 9pt; 86 | } 87 | 88 | a { 89 | text-decoration: none; 90 | font-style: italic; 91 | } 92 | a:link, 93 | a:visited { 94 | color: black; 95 | } 96 | } 97 | 98 | @media print { 99 | .titlepage-page > .titlepage { 100 | text-align: center; 101 | } 102 | } 103 | .titlepage-page > .titlepage > h1 { 104 | font-size: 2.5rem; 105 | } 106 | .titlepage-page > .titlepage > .author { 107 | font-size: 2rem; 108 | } 109 | @media print { 110 | .colophon-page > .colophon { 111 | text-align: right; 112 | } 113 | } 114 | .colophon-page > .colophon > h1 { 115 | font-size: 1.8rem; 116 | } 117 | .colophon-page > .colophon > hr { 118 | width: 50%; 119 | margin-right: 0; 120 | border: 1px solid black; 121 | } 122 | 123 | section { 124 | break-before: page; 125 | break-after: page; 126 | } 127 | nav { 128 | break-before: page; 129 | break-after: page; 130 | } 131 | aside { 132 | break-before: page; 133 | break-after: page; 134 | } 135 | 136 | p.footnote { 137 | float: footnote; 138 | } 139 | a.noteref { 140 | vertical-align: super; 141 | } 142 | /* NOTE footnoteの自動採番がやりたかった奴〜〜 */ 143 | /* 144 | section { 145 | counter-reset: footnote; 146 | } 147 | p.footnote { 148 | counter-increment: footnote; 149 | } 150 | p.footnote::footnote-marker { 151 | content: '* ' counter(footnote); 152 | } 153 | a.noteref::before { 154 | content: '*' target-counter(attr(href), footnote); 155 | vertical-align: super; 156 | } 157 | */ 158 | 159 | nav.toc a::after { 160 | content: target-counter(attr(href), page); 161 | float: right; 162 | } 163 | 164 | p { 165 | text-indent: 1rem; 166 | text-align: justify; 167 | margin-top: 0.3rem; 168 | margin-bottom: 0; 169 | } 170 | 171 | /* .image, */ 172 | p.caption { 173 | text-align: center; 174 | text-indent: 0; 175 | } 176 | /* list, emlist, tableのキャプションは左寄せ */ 177 | .caption-code p.caption, 178 | .emlist-code p.caption { 179 | text-align: start; 180 | } 181 | p.caption::before { 182 | color: gray; 183 | content: "▲"; 184 | } 185 | .caption-code p.caption::before, 186 | .emlist-code p.caption::before, 187 | .table p.caption::before { 188 | color: gray; 189 | content: "▼"; 190 | } 191 | 192 | pre.cmd { 193 | font-size: 0.8rem; 194 | background-color: #444; 195 | color: white; 196 | padding: 1rem 2rem; 197 | } 198 | pre.list, pre.emlist { 199 | font-size: 0.8rem; 200 | padding: 1rem 2rem; 201 | background-color: #eee; 202 | border: solid 3px gray; 203 | border-radius: 0.3rem; 204 | } 205 | 206 | table { 207 | margin: 0 auto 2em auto; 208 | border-collapse: collapse; 209 | } 210 | table tr th { 211 | border:1px black solid; 212 | font-size: 0.9rem; 213 | padding: 0.3rem; 214 | } 215 | table tr td { 216 | border:1px black solid; 217 | font-size: 0.9rem; 218 | padding: 0.3rem; 219 | } 220 | p.tablecaption, table caption { 221 | color: #666; 222 | font-weight: bold; 223 | text-indent: 0; 224 | } 225 | div.image { 226 | text-align: center; 227 | } 228 | div.column { 229 | padding: 0.8rem 1.5rem; 230 | border: solid 3px gray; 231 | border-radius: 0.3rem; 232 | } 233 | div.column h4 { 234 | margin-top: 0; 235 | } 236 | 237 | /* ブロック系の途中で改ページされるのを避ける。本当はかかってもいいんだけど脚注がある時に背景がぶっ壊れる… */ 238 | pre.cmd { 239 | page-break-inside: avoid; 240 | } 241 | pre.list, pre.emlist { 242 | page-break-inside: avoid; 243 | } 244 | table { 245 | page-break-inside: avoid; 246 | } 247 | 248 | @page { 249 | size: A5; 250 | /* トンボ */ 251 | /* marks: crop cross; */ 252 | 253 | /* 裁ち落としのとこまで塗りたかったら pentapod本を トンボ で検索 */ 254 | } 255 | 256 | @page { 257 | margin: 10mm; 258 | margin-top: 15mm; 259 | } 260 | /* A4設定 */ 261 | /* 262 | @page :left { 263 | margin-left: 30mm; 264 | padding: 1rem; 265 | } 266 | @page :right { 267 | margin-right: 30mm; 268 | padding: 1rem; 269 | } 270 | */ 271 | /* A5設定 */ 272 | @page :left { 273 | margin-left: 15mm; 274 | padding: 1rem; 275 | } 276 | @page :right { 277 | margin-right: 15mm; 278 | padding: 1rem; 279 | } 280 | 281 | @page :left { 282 | @top-left { 283 | /* 本当は現在の深さ1の見出しを出したい */ 284 | /* かつ、sectionの1ページ目だったら出したくない */ 285 | content: "Re:VIEW+CSS組版やっていき"; 286 | vertical-align: bottom; 287 | border-bottom: solid 1px black; 288 | } 289 | @top-right { 290 | content: " "; /* 内容ないと枠でない */ 291 | border-bottom: solid 1px black; 292 | } 293 | @top-center { 294 | content: " "; /* 内容ないと枠でない */ 295 | border-bottom: solid 1px black; 296 | } 297 | 298 | @bottom-center { 299 | content: "←" counter(page); 300 | } 301 | } 302 | 303 | @page :right { 304 | @top-right { 305 | /* 本当は現在の深さ2の見出しを出したい */ 306 | /* かつ、sectionの1ページ目だったら出したくない */ 307 | content: "Re:VIEW+CSS組版やっていき"; 308 | vertical-align: bottom; 309 | border-bottom: solid 1px black; 310 | } 311 | @top-left { 312 | content: " "; /* 内容ないと枠でない */ 313 | border-bottom: solid 1px black; 314 | } 315 | @top-center { 316 | content: " "; /* 内容ないと枠でない */ 317 | border-bottom: solid 1px black; 318 | } 319 | 320 | @bottom-center { 321 | content: counter(page) "→"; 322 | } 323 | } 324 | 325 | /* NOTE これイケるのでは?と思ったけどダメだった 326 | @page foobar:right { 327 | @top-right { 328 | content: "章タイトルだよー"; 329 | } 330 | } 331 | section.foobar { 332 | page: foobar; 333 | } 334 | */ 335 | 336 | /* NOTE これもイケるのでは?と思ったけどサポートされてなさそう 337 | @page :right { 338 | @top-right { 339 | content: element(chaptitle); 340 | } 341 | } 342 | section h1 { 343 | position: running(chaptitle); 344 | } 345 | */ 346 | 347 | @page :first { 348 | padding-top: 0mm; 349 | } 350 | /* 1ページ目は扉なのでページ上部の装飾無し */ 351 | @page :first { 352 | @top-right { 353 | content: " "; 354 | border-bottom: none; 355 | } 356 | @top-left { 357 | content: " "; 358 | border-bottom: none; 359 | } 360 | @top-center { 361 | content: " "; 362 | border-bottom: none; 363 | } 364 | } 365 | /* TODO 最終ページは奥付なのでページ上部の装飾無し */ 366 | /* …はてどうやるんだ??名前付きページはまだサポートされてなさそう… */ 367 | /* 368 | section.colophon-page { 369 | page: colophon; 370 | } 371 | @page colophon { 372 | @top-right { 373 | content: " "; 374 | border-bottom: none; 375 | } 376 | @top-left { 377 | content: " "; 378 | border-bottom: none; 379 | } 380 | @top-center { 381 | content: " "; 382 | border-bottom: none; 383 | } 384 | } 385 | */ 386 | 387 | .width-010per { width: 10%; } 388 | .width-020per { width: 20%; } 389 | .width-025per { width: 25%; } 390 | .width-030per { width: 30%; } 391 | .width-033per { width: 33%; } 392 | .width-040per { width: 40%; } 393 | .width-050per { width: 50%; } 394 | .width-060per { width: 60%; } 395 | .width-067per { width: 67%; } 396 | .width-070per { width: 70%; } 397 | .width-075per { width: 75%; } 398 | .width-080per { width: 80%; } 399 | .width-090per { width: 90%; } 400 | .width-100per { width: 100%; } 401 | -------------------------------------------------------------------------------- /articles/catalog.yml: -------------------------------------------------------------------------------- 1 | PREDEF: 2 | - preface.re 3 | 4 | CHAPS: 5 | - why-css-typesetting.re 6 | - whats-css-typesetting.re 7 | - practice.re 8 | - issue.re 9 | - conclusion.re 10 | 11 | POSTDEF: 12 | - afterword.re 13 | - syntax-example.re 14 | -------------------------------------------------------------------------------- /articles/conclusion.re: -------------------------------------------------------------------------------- 1 | = まとめ 2 | 3 | ここまでで、筆者的には割りとそれっぽいPDFが得られるようになりました。 4 | 後は細部を詰めるのと、識者に赤を入れてもらい泣きながら修正する、というのが次の一手でしょうか。 5 | フォント埋込…ウッ頭が! 6 | 7 | この技法の研鑽が積まれ、紙、電子、Webという3つの出力結果が高品位になっていくと嬉しいです。 8 | 現状、電子といってもPDF以外の出力をTechBoosterやひかる黄金わかめ帝国では出していません。 9 | リフロー可能な形式であるepubについては、見た目を整えるコストは高く、epubを求める需要は大変低いため、ないがしろにされています。 10 | しかし、1つの出力先を改善すると全体が改善されるとなると、多少のコストを払う覚悟ができるでしょう。 11 | 12 | また、我々は同人誌の組版をしたい、というのもCSS組版に対して有利に働きます。 13 | プロユースの組版というのはナレッジの塊で、Re:VIEW開発者会議などで話を聞く心底関心してしまいます。 14 | プロは細部のこだわりが凄まじく、なおかつそれを捨てられない場合が多くあるようです。 15 | つまり、プロが既存技法を捨てCSS組版に移行する場合、大変な労力が要求されるのです。 16 | 一方、我々は素人で同人ですので、"それっぽさ"がある程度高まれば満足することができますし、読者もそれで満足できるでしょう。 17 | プロの組版と素人の組版の差は、筆者は"ABテストをしたらプロが勝つが、素人の組版に明確な不備を指摘できる素人はいない"という感じだと筆者は思っています。 18 | 19 | CSS組版自体はまだまだブラウザのサポートが十分ではないのが現状です。 20 | つまり、筆者が意図したルックアンドフィールを少ない労力で得られる環境では(まだ)ない、ということです。 21 | このあたりは、Dockerイメージの提供や本の素体を提供するなどで、CSS組版の高速道路が整備されればある程度無視することができます。 22 | 23 | そして、ブラウザ側の思惑です。 24 | ブラウザはインターネット上のWebページを閲覧するのが主目的です。 25 | 別に紙面をレンダリングしたりPDFを生成したりする組版エンジンになりたいわけではない、というのが大多数のコミッタの思いなのではないでしょうか。 26 | つまり、人的資源が少ないため、Web関連技術にしては仕様の策定や実装の進み方がかなりゆっくりであると感じます。 27 | ブラウザベンダのコミッタに話を聞いたわけではありませんので、実際にPaged Media周りの実装に絡んでいる人の話を聞いてみたいところです。 28 | 29 | CSS組版が手法として確立したら、みんなが簡単にこのワークフローを使えるよう各所にコントリビュートしていきたいところですね。 30 | まずは今回作ったCSSの完成度をあげていく必要があります。 31 | 改善すべき箇所や、取り込むべき技術があれば、ぜひGitHubのリポジトリまでIssueやPull Requestを送ってきてください。 32 | 次の技術書典でCSS組版で作られた本を増やすのは君だ! 33 | 34 | //comment{ 35 | TODO 残タスク的な 36 | * 先達の確認と真似っこ 37 | * 既存の何かの本の組版を参考にする 38 | * ResetCSS系の何かっているんか?? 39 | * HTMLBuilderを改造してページ内リンクを修正する 40 | ** クリックするとdareka.htmlへのリンクになってたりする 41 | * https://www.npmjs.com/package/vivliostyle 42 | ** ライセンス問題 43 | ** 毎回AGPLってどこまでアレなのかとか理解できない話 44 | ** gccでコンパイルしたソフトウェアのこととかあんまし気にしないしさほど気にするユースケースじゃない気もする 45 | //} 46 | 47 | //comment{ 48 | 参考資料的な 49 | * http://vivliostyle.github.io/vivliostyle.js/samples/css-secrets-long/doc/index.xhtml 50 | * ノンブル 51 | ** http://libroworks.co.jp/?p=646 ノンブルでページ内検索 52 | * https://twitter.com/vvakame/status/976395216872652801 53 | * https://www.antenna.co.jp/AHF/ahf5/CSSInfo/CSS-Page-Tutorial.pdf 54 | * 脚注の採番とかの良さそうなサンプル 55 | ** https://github.com/vivliostyle/vivliostyle.js/issues/406 56 | * https://libroworks.co.jp/?p=838 57 | //} 58 | -------------------------------------------------------------------------------- /articles/config.yml: -------------------------------------------------------------------------------- 1 | # review-epubmaker向けの設定ファイルの例。 2 | # yamlファイルをRe:VIEWファイルのある場所に置き、 3 | # 「review-epubmaker yamlファイル」を実行すると、.epubファイルが 4 | # 生成されます。 5 | # このファイルはUTF-8エンコーディングで記述してください。 6 | 7 | # この設定ファイルでサポートするRe:VIEWのバージョン番号。 8 | # major versionが違うときにはエラーを出す。 9 | review_version: 2.0 10 | 11 | # ほかの設定ファイルの継承を指定できる。同じパラメータに異なる値がある場合は、 12 | # 呼び出し元の値が優先される。 13 | # A.yml、B.ymlのパラメータを継承する例。A.ymlとB.ymlに同じパラメータがある 14 | # 場合、B.ymlの値が優先される。さらに今このファイルに同じパラメータがあるなら、 15 | # その値がB.ymlよりも優先される。 16 | # 同様にA.yml、B.yml内でさらにinherit:パラメータを使うこともできる。 17 | # inherit: ["A.yml", "B.yml"] 18 | 19 | # ブック名(ファイル名になるもの。ASCII範囲の文字を使用) 20 | bookname: review-css-typesetting 21 | # 記述言語。省略した場合はja 22 | language: ja 23 | 24 | # 書名 25 | # 読みを入れる例 booktitle: {name: "Re:VIEW EPUBサンプル", file-as: "リビューイーパブサンプル"} 26 | booktitle: "Re:VIEW+CSS組版やっていき" 27 | 28 | # 著者名。「, 」で区切って複数指定できる 29 | # 読みを入れる例 aut: [{name: "青木峰郎", file-as: "アオキミネロウ"}, {name: "武藤健志", file-as: "ムトウケンシ"}, {name: "高橋征義", file-as: "タカハシマサヨシ"}, {name: "角征典", file-as: "カドマサノリ"}] 30 | aut: ["vvakame"] 31 | 32 | # 以下はオプション 33 | # 以下はオプション(autと同じように配列書式で複数指定可能)。 34 | # 読みの指定はaut:の例を参照。 35 | # a-が付いているものはcreator側、 36 | # 付いていないものはcontributor側(二次協力者)に入る 37 | # a-adp, adp: 異なるメディア向けに作り直した者 38 | # a-ann, ann: 注釈記述者 39 | # a-arr, arr: アレンジした者 40 | # a-art, art: グラフィックデザインおよび芸術家 41 | # a-asn, asn: 関連・かつての所有者・関係者 42 | # a-aqt, aqt: 大きく引用された人物 43 | # a-aft, aft: 後書き・奥付の責任者 44 | # a-aui, aui: 序論・序文・前書きの責任者 45 | # a-ant, ant: 目録責任者 46 | # a-bkp, bkp: メディア制作責任者 47 | # a-clb, clb: 限定参加または補足者 48 | # a-cmm, cmm: 解釈・分析・考察者 49 | # a-csl, csl: 監修者 50 | # a-dsr, dsr: デザイナ 51 | dsr: ["vvakame"] 52 | # a-edt, edt: 編集者 53 | edt: ["vvakame"] 54 | # a-ill, ill: イラストレータ 55 | # a-lyr, lyr: 歌詞作成者 56 | # a-mdc, mdc: メタデータセットの一次的責任者 57 | # a-mus, mus: 音楽家 58 | # a-nrt, nrt: 語り手 59 | # a-oth, oth: その他 60 | # a-pht, pht: 撮影責任者 61 | # a-pbl, pbl: 出版社(発行所) 62 | pbl: ひかる黄金わかめ帝国 63 | # a-prt, prt: 印刷所 64 | # a-red, red: 項目の枠組起草者 65 | # a-rev, rev: 評論者 66 | # a-spn, spn: 援助者 67 | # a-ths, ths: 監督者 68 | # a-trc, trc: 筆記・タイプ作業者 69 | # a-trl, trl: 翻訳者 70 | 71 | # 刊行日(省略した場合は実行時の日付) 72 | date: 2018-04-22 73 | # 発行年月。YYYY-MM-DD形式による配列指定。省略した場合はdateを使用する 74 | # 複数指定する場合は次のように記述する 75 | # [["初版第1刷の日付", "初版第2刷の日付"], ["第2版第1刷の日付"]] 76 | # 日付の後ろを空白文字で区切り、任意の文字列を置くことも可能。 77 | history: [["2018-04-22 技術書典4版 v1.0.0"]] 78 | # 権利表記(配列で複数指定可) 79 | # rights: (C) 2016 Re:VIEW Developers 80 | rights: © 2018 ひかる黄金わかめ帝国 81 | # description: 説明 82 | # subject: 短い説明用タグ(配列で複数指定可) 83 | # type: 書籍のカテゴリーなど(配列で複数指定可) 84 | # format: メディアタイプおよび特徴(配列で複数指定可) 85 | # source: 出版物生成の重要なリソース情報(配列で複数指定可) 86 | # relation: 補助的リソース(配列で複数指定可) 87 | # coverage: 内容の範囲や領域(配列で複数指定可) 88 | 89 | # デバッグフラグ。nullでないときには一時ファイルをカレントディレクトリに作成し、削除もしない 90 | debug: null 91 | 92 | # 固有IDに使用するドメイン。省略した場合は時刻に基づくランダムUUIDが入る 93 | urnid: urn:uid:https://github.com/vvakame/review-css-typesetting 94 | # 95 | # ISBN。省略した場合はurnidが入る 96 | # isbn: null 97 | # 98 | # HTMLファイルの拡張子(省略した場合はhtml) 99 | htmlext: html 100 | # 101 | # CSSファイル(配列で複数指定可、yamlファイルおよびRe:VIEWファイルを置いたディレクトリにあること) 102 | stylesheet: ["style.scss"] 103 | 104 | # ePUBのバージョン (2か3) 105 | epubversion: 3 106 | # 107 | # HTMLのバージョン (4か5。epubversionを3にしたときには5にする) 108 | htmlversion: 5 109 | 110 | # 目次として抽出する見出しレベル 111 | toclevel: 3 112 | 113 | # 採番の設定。採番させたくない見出しには「==[nonum]」のようにnonum指定をする 114 | # 115 | # 本文でセクション番号を表示する見出しレベル 116 | secnolevel: 3 117 | 118 | # 以下のsecnolevelはまだ未実装。 119 | # 前付でセクション番号を表示する見出しレベル(未実装) 120 | # pre_secnolevel: 0 121 | # 122 | # 後付(付録)でセクション番号を表示する見出しレベル(未実装) 123 | # post_secnolevel: 1 124 | # 125 | # 部番号を表示する見出しレベル(未実装) 126 | # part_secnolevel: 1 127 | 128 | # 本文中に目次ページを作成するか。省略した場合はnull (作成しない) 129 | toc: true 130 | 131 | # EPUB2標準の目次(NCX)以外に物理目次ファイルを作成するか。省略した場合はnull (作成しない) 132 | # ePUB3においてはこの設定によらず必ず作成される 133 | # mytoc: true 134 | 135 | # 表紙にするHTMLファイル。ファイル名を指定すると表紙として入る 136 | # cover: null 137 | # 138 | # 表紙に配置し、書籍の影絵にも利用する画像ファイル。省略した場合はnull (画像を使わない)。画像ディレクトリ内に置いてもディレクトリ名は不要(例: cover.jpg) 139 | # coverimage: cover-print.png 140 | # 141 | # 表紙の後に大扉ページを作成するか。省略した場合はnull (作成しない) 142 | titlepage: true 143 | # 144 | # 自動生成される大扉ページを上書きするファイル。ファイル名を指定すると大扉として入る 145 | # titlefile: null 146 | # 147 | # 原書大扉ページにするHTMLファイル。ファイル名を指定すると原書大扉として入る 148 | # originaltitlefile: null 149 | # 150 | # 権利表記ページファイル。ファイル名を指定すると権利表記として入る 151 | # creditfile: null 152 | 153 | # 奥付を作成するか。デフォルトでは作成されない。trueを指定するとデフォルトの奥付、ファイル名を指定するとそれがcolophon.htmlとしてコピーされる 154 | colophon: true 155 | 156 | # 裏表紙ファイル (画像はcoversまたはimagesに配置する)。ファイル名を指定すると裏表紙として入る 157 | # backcover: null 158 | 159 | # プロフィールページファイル。ファイル名を指定すると著者紹介として入る 160 | # profile: null 161 | # プロフィールページの目次上の見出し 162 | # profiletitle: 著者紹介 163 | 164 | # 広告ファイル。ファイル名を指定すると広告として入る 165 | # advfile: null 166 | 167 | # 取り込む画像が格納されているディレクトリ。省略した場合は以下 168 | # imagedir: images 169 | 170 | # 取り込むフォントが格納されているディレクトリ。省略した場合は以下 171 | # fontdir: fonts 172 | 173 | # imagedir内から取り込まれる対象となるファイル拡張子。省略した場合は以下 174 | # image_ext: ["png", "gif", "jpg", "jpeg", "svg", "ttf", "woff", "otf"] 175 | 176 | # fontdir内から取り込まれる対象となるファイル拡張子。省略した場合は以下 177 | # font_ext: ["ttf", "woff", "otf"] 178 | 179 | # ソースコードハイライトを利用する (pygmentsには外部gemが必要) 180 | # highlight: 181 | # html: "rouge" 182 | # html: "pygments" 183 | # latex: "listings" 184 | 185 | # カタログファイル名を指定する 186 | # catalogfile: catalog.yml 187 | 188 | # 1ページの行数文字数と1kbごとのページ数を用紙サイズで指定する(A5 or B5)。 189 | # page_metric: A5 190 | # 191 | # あるいは、配列で指定することもできる 192 | # 各数字の意味は、順にリストの行数、リストの1行字数、テキストの行数、テキストの1行字数、1kバイト毎のページ数 193 | # page_metric: [40,80,40,80,2] 194 | 195 | # ページ送りの送り方向、page-progression-directionの値("ltr"|"rtl"|"default") 196 | direction: "ltr" 197 | 198 | # EPUBのOPFへの固有の追加ルール 199 | # 要素に追加する名前空間 200 | # opf_prefix: {ebpaj: "http://www.ebpaj.jp/"} 201 | # 追加する要素のプロパティとその値 202 | # opf_meta: {"ebpaj:guide-version": "1.1.3"} 203 | 204 | # 以下のパラメータを有効にするときには、 205 | # epubmaker: 206 | # パラメータ: 値 207 | # パラメータ: 値 208 | # ... 209 | # という構成にする必要がある(インデントさせる) 210 | 211 | epubmaker: 212 | # HTMLファイルの拡張子 213 | htmlext: xhtml 214 | # 215 | # 目次を要素の階層表現にしない。省略した場合(null)は階層化する。 216 | # 特に部扉が入るなどの理由で、構成によっては階層化目次でepubcheckに 217 | # パスしない目次ができるが、そのようなときにはこれをtrueにする 218 | # flattoc: null 219 | # 220 | # 目次のインデントレベルをスペース文字で表現する(flattocがtrueのときのみ) 221 | # flattocindent: true 222 | # 223 | # NCX目次の見出しレベルごとの飾り(配列で設定)。EPUB3ではNCXは作られない 224 | # ncxindent: 225 | #- 226 | #- - 227 | # フックは、各段階で介入したいときのプログラムを指定する。自動で適切な引数が渡される 228 | # プログラムには実行権限が必要 229 | # ファイル変換処理の前に実行するプログラム。スタイルシートのコンパイルをしたいときなどに利用する。 230 | # 渡される引数1=作業用展開ディレクトリ 231 | # hook_beforeprocess: null 232 | # 233 | # 前付の作成後に実行するプログラム。作業用展開ディレクトリにある目次ファイル(toc-html.txt)を操作したいときなどに利用する。 234 | # 渡される引数1=作業用展開ディレクトリ 235 | # hook_afterfrontmatter: null 236 | # 237 | # 本文の変換後に実行するプログラム。作業用展開ディレクトリにある目次ファイル(toc-html.txt)を操作したいときなどに利用する。 238 | # 渡される引数1=作業用展開ディレクトリ 239 | # hook_afterbody: null 240 | # 241 | # 後付の作成後に実行するプログラム。作業用展開ディレクトリにある目次ファイル(toc-html.txt)を操作したいときなどに利用する。 242 | # 渡される引数1=作業用展開ディレクトリ 243 | # hook_afterbackmatter: null 244 | # 245 | # 画像およびフォントをコピーした後に実行するプログラム。別の画像やフォントを追加したいときなどに利用する。 246 | # 渡される引数1=作業用展開ディレクトリ 247 | # hook_aftercopyimage: null 248 | # 249 | # ePUB zipアーカイブ直前に実行するプログラム。メタ情報などを加工したいときなどに利用する。 250 | # 渡される引数1=ePUB準備ディレクトリ 251 | # hook_prepack: null 252 | # 253 | # 変換したHTMLファイルおよびCSSを解析して厳密に使用している画像ファイルだけを取り込むか。デフォルトはnull(imagesディレクトリすべてを取り込む) 254 | # なお、フォント、カバー、広告についてはこの設定によらずディレクトリ内のものがすべて取り込まれる 255 | # verify_target_images: null 256 | # 257 | # verify_target_imagesがtrueの状態において、解析で発見されなくても強制的に取り込むファイルの相対パスの配列 258 | # force_include_images: [] 259 | # 260 | # Re:VIEWファイル名を使わず、前付にpre01,pre02...、本文にchap01,chap02l...、後付にpost01,post02...という名前付けルールにするか 261 | # rename_for_legacy: null 262 | # 263 | # ePUBアーカイブの非圧縮実行 264 | # zip_stage1: "zip -0Xq" 265 | # 266 | # ePUBアーカイブの圧縮実行 267 | # zip_stage2: "zip -Xr9Dq" 268 | # 269 | # ePUBアーカイブに追加するパス(デフォルトはmimetype、META-INF、OEBPS) 270 | # zip_addpath: null 271 | # 272 | # EPUBで表紙をコンテンツに含めるか。デフォルトでは作成されない。yesにするとiBooks等でも最初に表紙が表示されるようになる 273 | # cover_linear: null 274 | # 275 | # @タグでの外部リンクを禁止し、地の文にする(falseで禁止する) 276 | # externallink: true 277 | # 278 | # epubmaker:階層を使うものはここまで 279 | 280 | # LaTeX用のスタイルファイル(styディレクトリ以下に置くこと) 281 | # tatsumacroは、電子書籍版の制作に利用する 282 | # texstyle: tatsumacro 283 | texstyle: techbooster-doujin 284 | # 285 | # LaTeX用のdocumentclassを指定する 286 | # texdocumentclass: ["jsbook", "oneside,14pt,uplatex"] 287 | # texdocumentclass: ["jsbook", "b5j,twoside,openany,uplatex"] 288 | texdocumentclass: ["jsbook", "a5j,twoside,openany,uplatex,9pt"] 289 | # 290 | # LaTeX用のコマンドを指定する 291 | # texcommand: "uplatex" 292 | # 293 | # LaTeXのコマンドに渡すオプションを指定する 294 | # texoptions: null 295 | # 296 | # LaTeX用のdvi変換コマンドを指定する(dvipdfmx) 297 | # dvicommand: "dvipdfmx" 298 | # 299 | # LaTeX用のdvi変換コマンドのオプションを指定する 300 | # dvioptions: "-d 5" 301 | 302 | # 以下のパラメータを有効にするときには、 303 | # pdfmaker: 304 | # パラメータ: 値 305 | # パラメータ: 値 306 | # ... 307 | # という構成にする必要がある(インデントさせる) 308 | # 309 | pdfmaker: 310 | # 311 | # TeXコンパイル前に実行するプログラム。変換後のTeXソースを調整したいときに使用する。 312 | # 渡される引数1=作業用展開ディレクトリ、引数2=呼び出しを実行したディレクトリ 313 | # hook_beforetexcompile: null 314 | # 315 | # TeXコンパイル後に実行するプログラム。索引作業をして再度コンパイルしたいときなどに使用する。 316 | # 渡される引数1=作業用展開ディレクトリ、引数2=呼び出しを実行したディレクトリ 317 | # hook_aftertexcompile: null 318 | # 319 | # PDF(book.pdf)作成後に実行するプログラム。PDFに加工を施したいときに使用する。 320 | # 渡される引数1=作業用展開ディレクトリ、引数2=呼び出しを実行したディレクトリ 321 | # hook_afterdvipdf: null 322 | # 323 | # 画像のscale=X.Xという指定を画像拡大縮小率からページ最大幅の相対倍率に変換します。 324 | # image_scale2width: true 325 | # 326 | # 奥付を作成するか。trueを指定するとデフォルトの奥付、ファイル名を指定するとそれがcolophon.htmlとしてコピーされる 327 | colophon: true 328 | # pdfmaker:階層を使うものはここまで 329 | 330 | # 以下のパラメータを有効にするときには、 331 | # webmaker: 332 | # パラメータ: 値 333 | # パラメータ: 値 334 | # ... 335 | # という構成にする必要がある(インデントさせる) 336 | # 337 | webmaker: 338 | stylesheet: ["style.css","style-web.css"] 339 | -------------------------------------------------------------------------------- /articles/epub_style.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | /* Tatujin-Publishing */ 3 | /* Style sheet for epub */ 4 | /* Ver.0.8b1 */ 5 | 6 | /* 7 | Scale & Rhythm 8 | line-height 1.6 9 | 16px = 1em 10 | x:p:h1:h2:h3 = 12px:14px:16px:24px:30px 11 | */ 12 | * { 13 | } 14 | body { 15 | margin: 0; 16 | padding: 0; 17 | font-size: 1em; 18 | line-height:1.6; 19 | font-family: "ShinGoPro-Regular","ShinGo-Regular", sans-serif; 20 | /* 21 | word-break: normal; 22 | -webkit-line-break: after-white-space; 23 | */ 24 | } 25 | p, ul, ol, dl, pre, table { 26 | font-family: "ShinGo Regular","ShinGo R","新ゴR","新ゴ R", sans-serif; 27 | font-size: 0.875em; 28 | } 29 | /* Heading */ 30 | h1 { 31 | margin: 0 0 3em; 32 | padding: 0.5em 0 0; 33 | border-top: 14px #326450 solid; 34 | text-align: left; 35 | font-size: 1.875em; 36 | font-weight: bold; 37 | } 38 | h2 { 39 | margin: 3em 0 0.5em; 40 | padding: 0.5em 0 0; 41 | border-top: 2px #326450 solid; 42 | text-align: left; 43 | font-size: 1.5em; 44 | font-weight: bold; 45 | } 46 | h3 { 47 | margin: 3em 0 0.5em; 48 | padding: 0; 49 | text-align: left; 50 | font-size: 1em; 51 | font-weight: bold; 52 | } 53 | h4, h5, h6 { 54 | margin:0.7em 0; 55 | padding: 0; 56 | text-align: left; 57 | line-height: 1.6; 58 | font-weight: bold; 59 | } 60 | /* Paragraph */ 61 | p { 62 | margin:0.7em 0; 63 | padding: 0; 64 | text-align: left; 65 | text-indent: 1em; 66 | line-height: 1.6; 67 | } 68 | div.lead p { 69 | color: #666; 70 | line-height: 1.6; 71 | font-size: 0.75em; 72 | } 73 | /* List */ 74 | ul, ol { 75 | margin: 2em 0 2em 2em; 76 | padding: 0; 77 | list-style-position: outside; 78 | } 79 | ul > li, 80 | ol > li { 81 | margin: 0 0 0.7em 0; 82 | padding: 0; 83 | line-height: 1.6; 84 | } 85 | dl { 86 | margin: 2em 0; 87 | padding: 0; 88 | } 89 | dt { 90 | margin: 0; 91 | padding: 0; 92 | font-weight: bold; 93 | } 94 | dd { 95 | margin: 0 0 1em 2em; 96 | padding: 0; 97 | line-height: 1.6; 98 | } 99 | /* Table 100 | p.tablecaptionではなく 101 | table caption {}を使う方が良いかも? 102 | */ 103 | table { 104 | margin: 0 auto 2em auto; 105 | border-collapse: collapse; 106 | } 107 | table tr th { 108 | background-color: #eee; 109 | border:1px #aaa solid; 110 | font-size: 0.75em; 111 | font-weight: normal; 112 | } 113 | table tr td { 114 | padding: 0.3em; 115 | border:1px #aaa solid; 116 | font-size: 0.75em; 117 | } 118 | p.tablecaption, table caption { 119 | margin: 0; 120 | color: #666; 121 | font-size: 0.75em; 122 | font-weight: bold; 123 | text-indent: 0; 124 | } 125 | /* Quote */ 126 | blockquote { 127 | margin: 2em 0 2em 2em; 128 | padding: 0.3em 1em; 129 | border: 1px #aaa solid; 130 | } 131 | /* Column Block */ 132 | div.column { 133 | margin: 2em 0 2em 2em; 134 | padding: 0.3em 1em; 135 | background-color: #eee; 136 | -webkit-border-radius: 0.7em; 137 | } 138 | div.column *{ 139 | margin:0.7em 0; 140 | } 141 | div.column ul, 142 | div.column ol { 143 | list-style-position: inside; 144 | } 145 | /* Code Block */ 146 | /* 147 | ※シンプルにできるかも 148 | div.code {} 149 | div.code pre.list, 150 | div.code pre.cmd {} 151 | div.code p.caption {} 152 | */ 153 | div.code, div.caption-code, div.source-code, div.emlist-code, div.emlistnum-code { 154 | margin: 1em 0 2em 2em; 155 | padding: 0; 156 | } 157 | pre.emlist, pre.source, pre.list { 158 | margin: 0; 159 | padding: 5px; 160 | border: 1px #aaa solid; 161 | } 162 | div p.caption { 163 | margin: 0; 164 | color: #666; 165 | font-size: 0.75em; 166 | font-weight: bold; 167 | } 168 | div.cmd-code pre.cmd { 169 | margin: 0; 170 | padding: 5px; 171 | color: #ccc; 172 | font-weight: bold; 173 | background-color: #444; 174 | -webkit-border-radius: 0.5em; 175 | } 176 | pre.cmd, pre.emlist, pre.list, pre.source { 177 | white-space: pre-wrap; 178 | } 179 | 180 | /* Image Block */ 181 | /* div.image p.caption {} 182 | ※captionをそろえた方が良いかも?*/ 183 | div.image { 184 | margin: 2em auto; 185 | padding: 0; 186 | } 187 | div.image img { 188 | margin: 0 auto; 189 | padding: 0; 190 | display: block; 191 | } 192 | div.image p.caption { 193 | margin: 0 auto; 194 | text-align: center; 195 | color: #666; 196 | font-size: 0.75em; 197 | font-weight: bold; 198 | text-indent: 0; 199 | } 200 | /* Footnote Block */ 201 | /* p.footnoteはいらないかも? */ 202 | div.footnote { 203 | } 204 | div.footnote p.footnote { 205 | color: #666; 206 | line-height: 1.6; 207 | font-size: 0.75em; 208 | text-indent: 0; 209 | } 210 | /* Colophon */ 211 | div.colophon { 212 | margin: 3em auto; 213 | } 214 | div.colophon p { 215 | text-indent: 0; 216 | } 217 | div.colophon p.title { 218 | font-size: 1.5em; 219 | } 220 | div.colophon table { 221 | margin: 1em 0 2em; 222 | border: none; 223 | } 224 | div.colophon table tr th { 225 | background-color: #fff; 226 | font-size: 1.2em; 227 | font-weight: normal; 228 | border: none; 229 | } 230 | div.colophon table tr td { 231 | font-size: 1.2em; 232 | font-weight: normal; 233 | border: none; 234 | } 235 | 236 | /* Inline */ 237 | a[href], 238 | a:link, 239 | a:visited { 240 | border-bottom: 1px dotted #531084; 241 | text-decoration: none; 242 | } 243 | b { 244 | font-weight: bold; 245 | } 246 | strong { 247 | font-weight: bold; 248 | } 249 | em { 250 | font-style: italic; 251 | } 252 | 253 | /** 254 | * from EBPAJ EPUB 3 File Creation Guide sample style 255 | * 256 | * cf. http://ebpaj.jp/counsel/guide 257 | */ 258 | 259 | /* image width definition(pacentage) */ 260 | .width-010per { width: 10%; } 261 | .width-020per { width: 20%; } 262 | .width-025per { width: 25%; } 263 | .width-030per { width: 30%; } 264 | .width-033per { width: 33%; } 265 | .width-040per { width: 40%; } 266 | .width-050per { width: 50%; } 267 | .width-060per { width: 60%; } 268 | .width-067per { width: 67%; } 269 | .width-070per { width: 70%; } 270 | .width-075per { width: 75%; } 271 | .width-080per { width: 80%; } 272 | .width-090per { width: 90%; } 273 | .width-100per { width: 100%; } 274 | -------------------------------------------------------------------------------- /articles/images/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvakame/review-css-typesetting/600cfc2913d70dc00ed90fb4331d53df6a770228/articles/images/.gitkeep -------------------------------------------------------------------------------- /articles/images/cover-print.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvakame/review-css-typesetting/600cfc2913d70dc00ed90fb4331d53df6a770228/articles/images/cover-print.png -------------------------------------------------------------------------------- /articles/images/cover-web.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvakame/review-css-typesetting/600cfc2913d70dc00ed90fb4331d53df6a770228/articles/images/cover-web.jpg -------------------------------------------------------------------------------- /articles/images/cover/original.afdesign: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvakame/review-css-typesetting/600cfc2913d70dc00ed90fb4331d53df6a770228/articles/images/cover/original.afdesign -------------------------------------------------------------------------------- /articles/images/issue/bad-list-and-footnote.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvakame/review-css-typesetting/600cfc2913d70dc00ed90fb4331d53df6a770228/articles/images/issue/bad-list-and-footnote.png -------------------------------------------------------------------------------- /articles/images/issue/bad-reset-hashbang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvakame/review-css-typesetting/600cfc2913d70dc00ed90fb4331d53df6a770228/articles/images/issue/bad-reset-hashbang.png -------------------------------------------------------------------------------- /articles/images/practice/box-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvakame/review-css-typesetting/600cfc2913d70dc00ed90fb4331d53df6a770228/articles/images/practice/box-example.png -------------------------------------------------------------------------------- /articles/images/practice/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvakame/review-css-typesetting/600cfc2913d70dc00ed90fb4331d53df6a770228/articles/images/practice/header.png -------------------------------------------------------------------------------- /articles/images/syntax-example/sample-image1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvakame/review-css-typesetting/600cfc2913d70dc00ed90fb4331d53df6a770228/articles/images/syntax-example/sample-image1.png -------------------------------------------------------------------------------- /articles/images/syntax-example/sample-image2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvakame/review-css-typesetting/600cfc2913d70dc00ed90fb4331d53df6a770228/articles/images/syntax-example/sample-image2.png -------------------------------------------------------------------------------- /articles/images/syntax-example/sample-numberlessimage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvakame/review-css-typesetting/600cfc2913d70dc00ed90fb4331d53df6a770228/articles/images/syntax-example/sample-numberlessimage.png -------------------------------------------------------------------------------- /articles/images/why-css-typesetting/questionnaire.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vvakame/review-css-typesetting/600cfc2913d70dc00ed90fb4331d53df6a770228/articles/images/why-css-typesetting/questionnaire.png -------------------------------------------------------------------------------- /articles/issue.re: -------------------------------------------------------------------------------- 1 | = 未解決の課題 2 | 3 | はい。そして未解決の課題がいっぱい残っています。 4 | これは読者への課題とします(PR待ってます)! 5 | 6 | 英語、技術書典運営のタスク、原稿などに挟まれてぎうぎうになっているため調査する時間とMPが足りなくなりました。 7 | よって、ここに筆者の考える残タスクを開陳するものであります。 8 | 押忍。 9 | 10 | == 問題の上手な切り分け方 11 | 12 | ある仕様が未サポートだったり上手く動かない場合、それはChromeの振る舞いなのか、それともvivliostyle.jsか、切り分けが難しいです。 13 | vivliostyle.jsはブラウザ未サポートのCSSをJSで頑張ってDOMのstyle属性に突っ込んでくれるため、DOM自体の可読性も恐ろしく悪くなります。 14 | 15 | 誰かナレッジがあったら分けてください…。 16 | 17 | == PDFの生成とフォントの埋込 18 | 19 | 印刷所に入稿するデータはPDF形式であり、そこにはフォントデータを埋め込まなければなりません@{why-must-font-embed}。 20 | しかし、現時点ではChromeの印刷機能でPDFを生成する時に、フォントデータを埋め込む方法が分かっていません。 21 | 22 | //footnote[why-must-font-embed][印刷に使うフォントと同一のものを印刷所が持っているかわからないし同一性の検証もめんどくさいため] 23 | 24 | pentapodさんの本によると、Adobe Acrobatに課金してフォントデータを後乗せで埋め込むという手法が選択肢としてあるそうです。 25 | 羊にこの手法を試してもらいましたが、上手くいきませんでした@{i-cant-understand-it}。 26 | 27 | //footnote[i-cant-understand-it][羊が何か理由を説明してたけど筆者が理解できなかったパターン。なんて??] 28 | 29 | よって、今回の印刷ではフォントデータが欠損していても無視して無理やり印刷します。 30 | 現状の到達点だから…!仕方ないから…! 31 | 32 | kmutoさんにTwitterで教えていただいた@{font-judge}ところによると、印刷所的にはフォントの埋込はCID、Type1はOK、Type3はアウト、という感じらしいです。 33 | 知らない用語が多かったので調べたところ、モリサワのフォント用語集@{morisawa-dictionary}を見るのがよさそうでした。 34 | 35 | //footnote[font-judge][@{https://twitter.com/kmuto/statuses/985670249289629696}] 36 | //footnote[morisawa-dictionary][@{http://www.morisawa.co.jp/culture/dictionary/}] 37 | 38 | 宿題に挑戦する読者のために、筆者が調べた範囲で参考になりそうなURLを掲載しておきます。 39 | 40 | * Vivliostyle.jsのフォーラムでの同様のやり取り 41 | ** @{https://groups.google.com/forum/#!topic/vivliostyle-ja/HCoewxEmLfM} 42 | * StackOverflowでの同様のやり取り 43 | ** @{https://stackoverflow.com/questions/11604726/} 44 | * Google ChromeのIssue 45 | ** @{https://bugs.chromium.org/p/chromium/issues/detail?id=516317} 46 | * NotoフォントをWebフォントとして使う 47 | ** @{https://fonts.google.com/earlyaccess#Noto+Sans+JP} 48 | 49 | //comment{ 50 | * kmutoさんはpdfToolbox(有償)を使っているとか… 51 | //} 52 | 53 | == 凝ったデザインへの挑戦 54 | 55 | やりたかったやつー! 56 | ページ番号周りでわかめこちゃんがキャッキャしたりして余白が楽しそうなやつー! 57 | 58 | やりたかったやつー! 59 | 小口のところまでスミ乗っけておいて辞書の検索用索引みたいなの出すやつー! 60 | 61 | まぁ多分頑張ればできそうだけどCSS力が圧倒的に足りない。 62 | EPUB Adaptive Layoutの仕様とかを使えばできるものもあるそうですが、標準ではないものはあまり使いたくないし…。 63 | 64 | == HTMLBuilderが吐き出すアンカーとリンクを修正する 65 | 66 | Re:VIEWのHTMLBuilderが吐き出すHTMLは1ファイル==1章形式であり、すべてのファイルをconcatする前提ではありません。 67 | なので、単純にconcatしただけだと章間のリンクが壊れたり、同名のfootnoteが複数章にまたがって存在すると不都合が生じます。 68 | 69 | これを直すためには、review-ext.rbなどを経由してHTMLBuilderの挙動に手を入れる必要があります。 70 | しかし、ここに手をいれるのはちょっとまとまった時間が必要そうなので、今回はいったん保留とします。 71 | 72 | == CSSの管理方法 73 | 74 | CSSをよりよく上手に管理する方法が必要です。 75 | 今のところ生CSSを書いていて、たとえば"リスト表示に対するパラメータ設定"みたいなのがどこで意図されて設定されているのか判然としません。 76 | これを解決するために、mixin的な機能などをもったAltCSS(SCSSとか)を導入したほうが健康によさそうだと考えています。 77 | というか、生CSSだとコメントを書くのがめんどくさすぎて憤死しそうです。 78 | CSSこんなめんどくさかったっけ? 79 | 80 | 最終的に、Re:VIEWユーザがCSS組版のために使える共有財産として設定を育てたいと考えた場合、どこをいじるとどこが変わるのか、パッと見て分かる必要があるでしょう。 81 | 自分で書いたCSSが何を意図しているか忘れないうちに…早く…! 82 | 83 | == ボックス系の見た目と脚注が被る話 84 | 85 | リストなどのボックス系の見た目のものと、脚注が被ると見た目がおかしくなる場合があります(@{bad-list-and-footnote})。 86 | 87 | //image[bad-list-and-footnote][ページまたぎのリストと脚注の相性の悪さ…][scale=0.7]{ 88 | //} 89 | 90 | これについて、今のところボックス系のものをページまたぎせずに配置するよう指定して迂回しました(@{avoid-list-and-footnote-conflict})。 91 | 92 | //list[avoid-list-and-footnote-conflict][ページまたぎを許さない]{ 93 | pre.cmd { 94 | page-break-inside: avoid; 95 | } 96 | pre.list, pre.emlist { 97 | page-break-inside: avoid; 98 | } 99 | table { 100 | page-break-inside: avoid; 101 | } 102 | //} 103 | 104 | この方法だと、ページ的に空白の部分が多く生まれてしまい、印刷費用(と頒布費用)が無駄になってしまいます。 105 | ページまたぎが発生しても問題なく表示できる方法を見つけるか、空いた空間にリストなどの先にある文字を流し込めればよさそうです。 106 | 今のところ、組版用語としてこのような処理にどういう名前がついているのかわからず調べられずにいます…。 107 | なきわかれ? 108 | 109 | == vivliostyle.jsのレンダリングがリロードで壊れる場合がある 110 | 111 | 図を見たほうがわかりやすいんですが、@{bad-reset-hashbang}のようになります。 112 | ボックスの改ページと脚注が被ったりします。 113 | 114 | //image[bad-reset-hashbang][レンダリングが被る場合がある][scale=0.7]{ 115 | //} 116 | 117 | #@# prh:disable 118 | これは、ページ途中でリロードした時に発生しがちな現象です。 119 | Vivliostyle Viewer、Chrome拡張共に発生します。 120 | これは解決可能で、vivliostyle.jsが生成するURLのhashbang部分を削って、本の先頭からレンダリングさせると改善します。 121 | Vivliostyle Viewerを使っている時のURL末尾に付く@{&f=epubcfi(/2!/4/12[chapter-vvakame]/139:0)}のような部分ですね。 122 | おそらく、vivliostyle.jsのレンダリングが、本の先頭からのときと本の途中からのときで挙動が違うのだと考えられます。 123 | 124 | この現象を完全に回避する方法は今のところ筆者は発見できていないので、PDF生成後に全部のページレイアウトを目で毎回確認する必要があります。 125 | 126 | == CI上でPDFを生成させ再現性を持たせる 127 | 128 | これに至ってはまったく未着手です。 129 | 130 | 多人数で執筆を行う時、また、単に本を書きたいだけのときに他人が作ったテンプレートを流用できるのは嬉しいものです。 131 | しかしながら、環境構築で躓くことも多く、可能であればできあがった環境をそのまま利用したいものですね。 132 | Dockerのvvakame/reviewイメージはそういう点で、リポジトリとコレがあれば本ができる!CIで回せる!という意味で重宝されているのだと理解しています。 133 | 134 | CSS組版の普及のためには最終的にdocker上でビルドし、PDFが得られるところまで行く必要があると考えています。 135 | 136 | LaTeXはビルドが完了すれば、再現性があり問題のないPDFが得られるかわりに、ビルドエラーになった時にエラーの内容がわかりにくく血反吐を吐くことになります。 137 | 138 | 一方、CSS組版は基盤技術がWebページなだけあり、ぶっ壊れたデータを突っ込んでもなにがしかの結果を得ることができます。 139 | 逆にいうと、結果が壊れている(=リンクが切れていたり図版が無かったり)しても、何らかの出力が得られてしまいます。 140 | これは、入稿データを作る時には細心の注意が必要であるということになり、少々不都合です。 141 | 142 | この問題を解消するためには、プリプロセッサ(=Re:VIEW)レベルでエラーをより多く検出すること、ブラウザ上でレンダリングした時に信用できるエラーレポートが得られること、それをコマンドライン上に反映できること、が必要だと考えられます。 143 | これはなかなか道のりが長そうです…。 144 | 145 | これを実現するための要素技術としては、"Headless Chrome PDF"とかでググると結構色々と出て来るので、取り組めば割りとイケそうな感じだと思います。 146 | vivliostyle.jsを使ってHTMLをレンダリングしないといけないのですが、vivliostyle.js自体もAPIのドキュメントがほぼないので調査がめんどくさい感じです。 147 | 適当にpuppeteerとかでやればええんちゃいますのん…? 148 | 149 | Vivliostyle Viewerを立ち上げて、Chromeでレンダリングさせて、それの完了を待って、印刷するためにブラウザが色々と処理して、データを得る! 150 | これは手元でやってみるとわかりますが、結構時間のかかる処理です。 151 | 現在TechBoosterのこの本は無編集で150Pを超えていますが、Chromeでレンダリングを待つ(目次のレンダリング完了が目安)のに50秒、印刷機能を読み出してLoading previewの表示が終わるのに20秒かかります。 152 | うーん、気が重い…。 153 | 154 | == 他のブラウザを検討する 155 | 156 | 今回はGoogle Chromeのみに絞って調査・チューニングをしました。 157 | 代わりに、Mozilla Firefoxなどの別のブラウザを使うとどうなるでしょう…。 158 | 159 | kmutoさんに教えていただいた@{ff-and-font}ところによると、PDFへのフォント埋め込み事情はFirefoxのほうが優れているそうです。 160 | テキトーに試してみたところ、Firefoxが生成したPDFはフォントの埋込はバッチリで、紙面のレンダリングはさんざん…という結果でした。 161 | 162 | //footnote[ff-and-font][@{https://twitter.com/kmuto/statuses/985672642047819776}] 163 | 164 | == 他の組版エンジンを検討する 165 | 166 | vivliostyle.jsを使っているが、他に選択肢があるのでは?という疑問があります。 167 | 米O'Reilly社はAtlasというシステムを作っていて、HTML+CSSでepubやPDFを生成できます。 168 | これは裏でアンテナハウス社のAH Formatter@{antennahouse}を使っています。 169 | 170 | //footnote[antennahouse][vivliostyle(現トリムマーク)の村上さんは2014年までアンテナハウス社にいました @{http://blog.antenna.co.jp/CSSPage2/archives/165}] 171 | 172 | 他にも、Prince@{prince}などもあるようです。 173 | 174 | //footnote[prince][@{https://www.princexml.com/}] 175 | 176 | が、全体的に商用ソフトウェアなんですよね。 177 | ソフトウェアに金を落とさないエンジニアだ…と思うとちょっと自分のことを嫌な奴だなと思って凹みます。 178 | まぁ同人誌制作の場合、利益を期待していないので制作費にお金を使わずに済ませたい…という事情があるわけです。 179 | その費用が1万円とかそういうオーダーならば、割り切って払うという選択肢もあるのですが…。 180 | 181 | ===[column] トリムマーク社が爆誕した話 182 | 183 | vivliostyle.jsを開発していたのは今までVivliostyle社でした。 184 | Vivliostyle社はアンテナハウス社でXML組版やCSS組版エンジンの開発者としてブイブイいわせていた村上さんが、アンテナハウス社からの出資のもと設立された会社でした@{antennahouse-and-vivliostyle}。 185 | 186 | //footnote[antennahouse-and-vivliostyle][@{http://blog.antenna.co.jp/CSSPage2/archives/165}] 187 | 188 | 今回、この記事を書いている最中にVivliostyle社に変化がありました。 189 | メインビジネスの変化に伴い、Vivliostyleという名前はオープンソースソフトウェアの名称として残り、会社自体はトリムマーク社に変更@{vivliostyle-and-trimmark}されました。 190 | 191 | //footnote[vivliostyle-and-trimmark][@{https://vivliostyle.org/ja/blog/2018/03/26/a-new-beginning/}] 192 | 193 | これに伴い、vivliostyle.jsなどのOSSプロダクトは@{https://vivliostyle.org/}に移行されました。 194 | 195 | 新しい組織に移行するに伴い、ユーザとしては変化に対して一抹の不安もあります。 196 | 旧Vivliostyle社の方々はCSS組版の分野について一流ですので、今後も変わらず辣腕を奮っていただけると大変うれしいですね。 197 | 198 | ===[/column] 199 | -------------------------------------------------------------------------------- /articles/layouts/layout.html.erb: -------------------------------------------------------------------------------- 1 | <%= @body %> 2 | -------------------------------------------------------------------------------- /articles/layouts/layout.tex.erb: -------------------------------------------------------------------------------- 1 | \documentclass[<%= @documentclassoption %>]{<%= @documentclass %>} 2 | <%- if @texcompiler == "uplatex" -%> 3 | \usepackage[deluxe,uplatex]{otf} 4 | <%- else -%> 5 | \usepackage[deluxe]{otf} 6 | <%- end -%> 7 | \usepackage[dvipdfmx]{xcolor} 8 | \usepackage[dvipdfmx]{graphicx} 9 | \usepackage{framed} 10 | \usepackage{wrapfig} 11 | \definecolor{shadecolor}{gray}{0.9} 12 | \definecolor{shadecolorb}{gray}{0.1} 13 | \definecolor{reviewgreen}{rgb}{0,0.4,0} 14 | \definecolor{reviewblue}{rgb}{0.2,0.2,0.4} 15 | \definecolor{reviewred}{rgb}{0.7,0,0} 16 | \definecolor{reviewdarkred}{rgb}{0.3,0,0} 17 | \usepackage{lmodern} 18 | \usepackage[T1]{fontenc} 19 | \usepackage{textcomp} 20 | \usepackage[utf8]{inputenc} 21 | \usepackage{ascmac} 22 | \usepackage{float} 23 | \usepackage{alltt} 24 | \usepackage{amsmath} 25 | 26 | 27 | %% if you use @{} (underline), use jumoline.sty 28 | %%\usepackage{jumoline} 29 | 30 | \newenvironment{shadedb}{% 31 | \def\FrameCommand{\fboxsep=\FrameSep \colorbox{shadecolorb}}% 32 | \MakeFramed {\FrameRestore}}% 33 | {\endMakeFramed} 34 | 35 | <%- unless ["utbook", "tbook"].include?(@documentclass) -%> 36 | %\usepackage[top=10zw,bottom=12zw,left=10zw,right=10zw]{geometry} 37 | %\usepackage[top=5zw,bottom=5zw,left=1zw,right=1zw]{geometry} 38 | <%- end -%> 39 | 40 | \newcommand{\parasep}{\vspace*{3zh}} 41 | \setlength{\footskip}{30pt} 42 | 43 | <%- if @texcompiler == "uplatex" -%> 44 | \usepackage[dvipdfmx,bookmarks=true,bookmarksnumbered=true,colorlinks=true,% 45 | pdftitle={<%= @config.name_of("booktitle") %>},% 46 | pdfauthor={<%= @config.names_of("aut").join(I18n.t("names_splitter")) %>}]{hyperref} 47 | % uplatexでのBookmarkの文字化け対策(日本語向け) 48 | \usepackage[dvipdfmx]{pxjahyper} 49 | <%- else -%> 50 | %% Bookmarkの文字化け対策(日本語向け) 51 | \ifnum 46273=\euc"B4C1 % 46273 == 0xB4C1 == 漢(EUC-JP) 52 | \usepackage{atbegshi}% 53 | \AtBeginShipoutFirst{\special{pdf:tounicode EUC-UCS2}}% 54 | %%\AtBeginDvi{\special{pdf:tounicode EUC-UCS2}}% 55 | \else 56 | \usepackage{atbegshi}% 57 | \AtBeginShipoutFirst{\special{pdf:tounicode 90ms-RKSJ-UCS2}}% 58 | %%\AtBeginDvi{\special{pdf:tounicode 90ms-RKSJ-UCS2}}% 59 | \fi 60 | 61 | \usepackage[dvipdfm,bookmarks=true,bookmarksnumbered=true,colorlinks=true,% 62 | pdftitle={<%= @config.name_of("booktitle") %>},% 63 | pdfauthor={<%= @config.names_of("aut").join(I18n.t("names_splitter")) %>}]{hyperref} 64 | <%- end -%> 65 | 66 | <%- if ["utbook", "tbook"].include?(@documentclass) -%> 67 | \newcommand{\headfont}{\gtfamily\sffamily\bfseries} 68 | \usepackage{plext} 69 | <%- end -%> 70 | 71 | <%- if config["highlight"] && config["highlight"]["latex"] == "listings" -%> 72 | <%- if config["language"] == "ja" -%> 73 | \usepackage{listings,jlisting} 74 | <%- else -%> 75 | \usepackage{listings} 76 | <%- end -%> 77 | \renewcommand{\lstlistingname}{<%= I18n.t("list")%>} 78 | \lstset{% 79 | breaklines=true,% 80 | breakautoindent=false,% 81 | breakindent=0pt,% 82 | fontadjust=true,% 83 | backgroundcolor=\color{shadecolor},% 84 | frame=single,% 85 | framerule=0pt,% 86 | basicstyle=\ttfamily\scriptsize,% 87 | commentstyle=\color{reviewgreen},% 88 | identifierstyle=\color{reviewblue},% 89 | stringstyle=\color{reviewred},% 90 | keywordstyle=\bfseries\color{reviewdarkred},% 91 | } 92 | 93 | \lstnewenvironment{reviewemlistlst}[1][]{\lstset{#1}}{} 94 | \lstnewenvironment{reviewemlistnumlst}[1][]{\lstset{numbers=left, #1}}{} 95 | \lstnewenvironment{reviewlistlst}[1][]{\lstset{#1}}{} 96 | \lstnewenvironment{reviewlistnumlst}[1][]{\lstset{numbers=left, #1}}{} 97 | \lstnewenvironment{reviewcmdlst}[1][]{\lstset{backgroundcolor=\color{white}, frameround=tttt, frame=trbl, #1}}{} 98 | <%- end -%> 99 | 100 | \newenvironment{reviewimage}{% 101 | \begin{figure}[H] 102 | \begin{center}}{% 103 | \end{center} 104 | \end{figure}} 105 | 106 | \newenvironment{reviewdummyimage}{% 107 | \begin{figure}[H] 108 | \begin{center}\begin{alltt}}{% 109 | \end{alltt}\end{center} 110 | \end{figure}} 111 | 112 | \newenvironment{reviewemlist}{% 113 | \medskip\small\begin{shaded}\setlength{\baselineskip}{1.3zw}\begin{alltt}}{% 114 | \end{alltt}\end{shaded}} 115 | 116 | \newenvironment{reviewlist}{% 117 | \begin{shaded}\small\setlength{\baselineskip}{1.3zw}\begin{alltt}}{% 118 | \end{alltt}\end{shaded}\par\vspace*{0.5zw}} 119 | 120 | \newenvironment{reviewcmd}{% 121 | \color{white}\medskip\small\begin{shadedb}\setlength{\baselineskip}{1.3zw}\begin{alltt}}{% 122 | \end{alltt}\end{shadedb}} 123 | 124 | \newenvironment{reviewbox}{% 125 | \medskip\small\begin{framed}\setlength{\baselineskip}{1.3zw}\begin{alltt}}{% 126 | \end{alltt}\end{framed}} 127 | 128 | \newenvironment{reviewtable}[1]{% 129 | \begin{center}\small\setlength{\baselineskip}{1.2zw} 130 | \begin{tabular}{#1}}{% 131 | \end{tabular} 132 | \end{center}} 133 | 134 | \newenvironment{reviewcolumn}{% 135 | \begin{framed} 136 | }{% 137 | \end{framed} 138 | \vspace{2zw}} 139 | 140 | \newcommand{\reviewcolumnhead}[2]{% 141 | {\noindent\large <%= I18n.t("column_head")%>: #2}} 142 | 143 | \newcommand{\reviewtablecaption}[1]{% 144 | \caption{#1}} 145 | 146 | \newcommand{\reviewimgtablecaption}[1]{% 147 | \caption{#1}\vspace{-3mm}} 148 | 149 | \newcommand{\reviewbackslash}[0]{% 150 | \textbackslash{}} 151 | 152 | \newcommand{\reviewlistcaption}[1]{% 153 | \medskip{\small\noindent #1}\vspace*{-1.3zw}} 154 | 155 | \newcommand{\reviewemlistcaption}[1]{% 156 | \medskip{\small\noindent #1}\vspace*{-1.3zw}} 157 | 158 | \newcommand{\reviewcmdcaption}[1]{% 159 | \medskip{\small\noindent #1}\vspace*{-1.3zw}} 160 | 161 | \newcommand{\reviewindepimagecaption}[1]{% 162 | \begin{center}#1\end{center}} 163 | 164 | \newcommand{\reviewboxcaption}[1]{% 165 | \medskip{\small\noindent #1}\vspace*{-1.3zw}} 166 | 167 | \newcommand{\reviewimageref}[2]{<%= I18n.t("image")%> #1} 168 | \newcommand{\reviewtableref}[2]{<%= I18n.t("table")%> #1} 169 | \newcommand{\reviewlistref}[1]{<%= I18n.t("list")%> #1} 170 | \newcommand{\reviewbibref}[2]{#1} 171 | \newcommand{\reviewcolumnref}[2]{<%= I18n.t("columnname")%> #1} 172 | \newcommand{\reviewsecref}[2]{#1} 173 | 174 | \newcommand{\reviewminicolumntitle}[1]{% 175 | {\large <%= I18n.t("memo_head")%>: #1}\\} 176 | 177 | <%- if @config["toctitle"].present? -%> 178 | \renewcommand{\contentsname}{<%= @config["toctitle"]%>} 179 | <%- end -%> 180 | 181 | \newenvironment{reviewminicolumn}{% 182 | \vspace{1.5zw}\begin{screen}}{% 183 | \end{screen}\vspace{2zw}} 184 | 185 | \newcommand{\reviewkw}[1]{\textbf{\textgt{#1}}} 186 | \newcommand{\reviewami}[1]{\mask{#1}{A}} 187 | \newcommand{\reviewem}[1]{\textbf{#1}} 188 | \newcommand{\reviewstrong}[1]{\textbf{#1}} 189 | \newcommand{\reviewunderline}{\Underline} 190 | 191 | %% @ is ignored in LaTeX with default style 192 | \newcommand{\reviewstrike}[1]{#1} 193 | 194 | %%%% for ulem.sty: 195 | %%\renewcommand{\reviewstrike}[1]{\sout{#1}} 196 | %% 197 | %%%% for jumoline.sty: 198 | %%\renewcommand{\reviewstrike}[1]{\Middleline{#1}} 199 | 200 | \newcommand{\reviewth}[1]{\textgt{#1}} 201 | \newcommand{\reviewtitlefont}[0]{\usefont{T1}{phv}{b}{n}\gtfamily} 202 | \newcommand{\reviewmainfont}[0]{} 203 | \newcommand{\reviewcolophon}[0]{\clearpage} 204 | \newcommand{\reviewappendix}[0]{\appendix} 205 | 206 | \makeatletter 207 | %% maxwidth is the original width if it is less than linewidth 208 | %% otherwise use linewidth (to make sure the graphics do not exceed the margin) 209 | \def\maxwidth{% 210 | \ifdim\Gin@nat@width>\linewidth 211 | \linewidth 212 | \else 213 | \Gin@nat@width 214 | \fi 215 | } 216 | \makeatother 217 | 218 | <%- if @config["usepackage"] -%> 219 | <%= @config["usepackage"] %> 220 | <%- end -%> 221 | 222 | \usepackage[T1]{fontenc} 223 | 224 | \begin{document} 225 | 226 | \reviewmainfont 227 | 228 | \frontmatter 229 | \pagenumbering{arabic} 230 | 231 | <%- if @config["titlepage"] -%> 232 | <%- if @custom_titlepage -%> 233 | <%= @custom_titlepage %> 234 | <%- else -%> 235 | \begin{titlepage} 236 | <%- if @config["coverimage"] -%> 237 | \begin{center} 238 | \includegraphics[<%= @coverimageoption%>]{./images/<%= @config["coverimage"] %>} 239 | \end{center} 240 | \clearpage 241 | <%- end -%> 242 | \thispagestyle{plainhead} 243 | \setcounter{page}{1} 244 | \begin{center}% 245 | \mbox{} \vskip5zw 246 | \reviewtitlefont% 247 | {\Huge <%= @config.name_of("booktitle") %> \par}% 248 | \vskip 15em% 249 | {\huge 250 | \lineskip .75em 251 | \begin{tabular}[t]{c}% 252 | <%= @authors %> 253 | \end{tabular}\par}% 254 | \vfill 255 | {\large <%= @config["date"] %> <%= I18n.t("edition")%>\hspace{2zw}<%= I18n.t("published_by", @config.names_of("pbl").join(I18n.t("names_splitter")))%>\par}% 256 | \vskip4zw\mbox{} 257 | \end{center}% 258 | \end{titlepage} 259 | <%- end -%> 260 | <%- end -%> 261 | 262 | %%% originaltitle 263 | <%- if @config["originaltitlefile"] -%> 264 | <%= @custom_originaltitlepage %> 265 | <%- end -%> 266 | 267 | %%% credit 268 | <%- if @config["creditfile"] -%> 269 | <%= @custom_creditpage %> 270 | <%- end -%> 271 | 272 | %% preface 273 | <%= @input_files["PREDEF"] %> 274 | 275 | <%- if @config["toc"] -%> 276 | \setcounter{tocdepth}{<%= @config["toclevel"] %>} 277 | \tableofcontents 278 | <%- end -%> 279 | 280 | \begingroup 281 | \cleardoublepage 282 | \edef\continuenumber{\endgroup 283 | \noexpand\mainmatter 284 | \setcounter{page}{\the\value{page}}% 285 | } 286 | 287 | \mainmatter 288 | \continuenumber 289 | 290 | \renewcommand{\chaptermark}[1]{\markboth{\prechaptername\thechapter\postchaptername~#1}{}} 291 | <%= @input_files["CHAPS"] %> 292 | \renewcommand{\chaptermark}[1]{\markboth{\appendixname\thechapter~#1}{}} 293 | \reviewappendix 294 | <%= @input_files["APPENDIX"] %> 295 | 296 | %% backmatter begins 297 | <%- if @input_files["POSTDEF"] or @config["colophon"] -%> 298 | \backmatter 299 | <%- end -%> 300 | 301 | <%- if @input_files["POSTDEF"] -%> 302 | <%= @input_files["POSTDEF"] %> 303 | <%- end -%> 304 | 305 | %%% profile 306 | <%- if @config["profile"] -%> 307 | <%= @custom_profilepage %> 308 | <%- end -%> 309 | 310 | %%% advfile 311 | <%- if @config["advfile"] -%> 312 | <%= @custom_advfilepage %> 313 | <%- end -%> 314 | 315 | %%% colophon 316 | <%- if @config["colophon"] -%> 317 | <%- if @custom_colophonpage -%> 318 | <%= @custom_colophonpage %> 319 | <%- else -%> 320 | %% okuduke 321 | \reviewcolophon 322 | \thispagestyle{plainhead} 323 | 324 | \vspace*{\fill} 325 | 326 | {\noindent\reviewtitlefont\Large <%= @config.name_of("booktitle") %>} \\ 327 | \rule[8pt]{14cm}{1pt} \\ 328 | {\noindent 329 | <%= @config["pubhistory"].to_s.gsub(/\n/){"\n\n\\noindent\n"} %> 330 | } 331 | 332 | \begin{tabular}{ll} 333 | <%= @okuduke %> 334 | \end{tabular} 335 |  \\ 336 | \rule[0pt]{14cm}{1pt} \\ 337 | <%= @config.names_of("rights").join('\\' + '\\') %> \\ 338 | <%- end -%> 339 | <%- end -%> 340 | 341 | %%% backcover 342 | <%- if @config["backcover"] -%> 343 | <%= @custom_backcoverpage %> 344 | <%- end -%> 345 | 346 | \end{document} 347 | -------------------------------------------------------------------------------- /articles/locale.yml: -------------------------------------------------------------------------------- 1 | locale: ja 2 | column_head: ■コラム 3 | columnname: コラム 4 | memo_head: ■メモ 5 | image: 図 6 | table: 表 7 | list: リスト 8 | -------------------------------------------------------------------------------- /articles/practice.re: -------------------------------------------------------------------------------- 1 | = 実際にやってみる 2 | 3 | 粛々とやれるところをやっていきましょう。 4 | 今回の本の内容は@{https://github.com/vvakame/review-css-typesetting}に置いておく予定です。 5 | 4/23以降になってもこのURLのリポジトリがpublicになっていなかったり、内容が空だったりした場合、運営のタスクで忙殺されたんだな…と察してください。 6 | そして、Twitterで『まだリポジトリ公開されないんですか?』と煽ってください。 7 | 8 | また、現在の筆者によるCSS組版のフローは試行錯誤の道中であり、苦悶の欠片であり、鵜呑みにすると痛い目を見ること間違いなしです。 9 | 10 | CSSについての細かい内容については前述したpentapodさんの本を参照してください。 11 | 僕はこの本で説明されている内容を我々の流儀っぽく適用できないか試したり調べたりしただけです。 12 | みんな、買おう! 13 | 14 | また、筆者が試して書いた内容について、実は書き方が悪いだけでやり方次第ではちゃんと動く、ということもあるかもしれません。 15 | その場合、前述のリポジトリにIssueとして報告してもらえると大変うれしいです。 16 | PRをいただけると神になります。 17 | 18 | == TechBoosterのワークフロー with Re:VIEW 19 | 20 | まずは我々の執筆フローをおさらいしていきます。 21 | 22 | 1. GitHubで原稿管理 23 | 2. Re:VIEW形式で原稿執筆 24 | 3. Dockerのvvakame/reviewイメージ@{docker-review}でビルド(PDF生成) 25 | 4. Circle CIで毎回ビルド&チェック 26 | 5. 羊が睡眠時間を爆発四散させながら編集&入稿する 27 | 28 | //footnote[docker-review][@{https://hub.docker.com/r/vvakame/review/} Docker HubとGitHub両方にスターお願いします!] 29 | 30 | その他、詳しいことについては『技術書をかこう!〜はじめてのRe:VIEW〜』@{firststepreview}を参照してください。 31 | たぶん書いてあったような気がします。 32 | 書いてなかったら許してください。 33 | 34 | #@# prh:disable 35 | //footnote[firststepreview][@{https://github.com/TechBooster/C89-FirstStepReVIEW-v2}] 36 | 37 | このフローを踏襲し、利用するDockerイメージを変更するだけでCSS組版による入稿データが得られる、というのが目指すべき到達点ですね。 38 | 39 | == ブラウザからPDF出力の下準備 40 | 41 | #@# prh:disable:言う 42 | 何はなくとも同人誌を刷らないといけないので入稿可能なデータ、もっと言うとPDF形式での出力を行う必要があります。 43 | 本文の入稿データを作るためには単一のPDFにする必要があります。 44 | ブラウザの印刷機能でPDFを生成するので、本全体を単一のHTMLにしておく必要があります。 45 | 46 | Re:VIEWでは各章をHTMLに変換することができますが、すべての章をconcatして出力することはできません。 47 | そこで、すべての章をなるべく余計な出力なしにHTMLで出力し、後から別処理でconcatしてやる必要があります。 48 | 具体的に、layouts/layout.html.erbを作成し、中身は@{<%= @body %>}のみとしました。 49 | 50 | 今回、Vivliostyleの利用にはChrome拡張版@{viv-chrome-ext}を利用します。 51 | 真面目にやるなら再現性を考えてnpm経由でvivliostyleを利用するべきでしょう。 52 | 53 | #@# prh:disable 54 | //footnote[viv-chrome-ext][@{https://chrome.google.com/webstore/detail/vivliostyle/ffeiildjegeigkbobbakjjmfeacadbne?hl=ja}] 55 | 56 | == 1枚のHTMLを出力する!気合で 57 | 58 | Chromeで単一のPDFを生成したいので、すべての章を1つのHTMLデータに結合する必要があります。 59 | Re:VIEWは1章を1つの.reファイルにし、これを1つの.htmlファイルに変換します。 60 | このままでは都合が悪いため、なんとかしてconcatする必要があります。 61 | 62 | Re:VIEWがHTMLファイルを出力するとき、タグやタグなどの今回のユースケースでは邪魔なタグが出力されます。 63 | これを排除するために、Re:VIEWが使うHTMLファイル出力時に使うテンプレートに手を加えます。 64 | @{layouts/layout.html.erb}を置くと、これがテンプレートとして使われるようになります。 65 | 中身は@{layouts/layout.html.erb}のように大変簡素なものにしました。 66 | 67 | #@# prh:disable 68 | //list[layouts/layout.html.erb][の中身が出ればよい!]{ 69 | #@mapfile(../articles/layouts/layout.html.erb) 70 | <%= @body %> 71 | #@end 72 | //} 73 | 74 | これで章毎のパーツが手に入るので、htmlやbodyタグを補ってやります。 75 | この処理はTypeScriptで書き、catalog.ymlやconfig.ymlを読み込みつつ気合でconcatしました。 76 | 77 | 最終的には次のような処理を行うことになりました。 78 | 79 | 1. 扉を出力 80 | 1. 目次を出力 81 | 1. 各章をsectionタグで包みidを振って本文を出力 82 | 1. 奥付を出力 83 | 84 | == 扉や奥付のページを表示したい! 85 | 86 | 扉というのは、表紙をめくった普通の紙の1枚目、奥付というのは裏表紙をめくった普通の紙の1枚目のことを指します。 87 | 扉には書名、著者名が。 88 | 奥付には書名、著者名、発行者、印刷者、出版年月日などの情報が記してある場合が多いです。 89 | 90 | 今回はconfig.ymlから情報を取ってきて扉と奥付を生成します。 91 | 出力する項目はlatex版で出力しているものを踏襲します。 92 | 扉には@{title-page}のように、書名、著者名、刊行日、発行所を出力します。 93 | 奥付には@{colophon}のように、書名、発行年月、著者名、編集者名、発行所、権利表記を出力します。 94 | 95 | #@# prh:disable 96 | //list[title-page][扉の例]{ 97 | 103 | //} 104 | 105 | #@# prh:disable 106 | //list[colophon][奥付の例]{ 107 | 115 | //} 116 | 117 | あとは、これらの要素をCSSで装飾して見た目をlatexに近づけてやればOKです。 118 | 僕はCSSが嫌いなので@{why-write-this-contents}、まだ頑張ってません。 119 | 120 | //footnote[why-write-this-contents][なぜこの記事を書こうと思ったんだ!? と思ったアナタ、気にしてはいけません] 121 | 122 | HTMLのセマンティクス的にはここに