Skip to content

Instantly share code, notes, and snippets.

@baldurbjarnason
Last active February 13, 2023 13:56
Show Gist options
  • Save baldurbjarnason/31bcd04cc898f42754ea6873aa3a9374 to your computer and use it in GitHub Desktop.
Save baldurbjarnason/31bcd04cc898f42754ea6873aa3a9374 to your computer and use it in GitHub Desktop.
Book production files for Out of the Software Crisis

Book production files

For Out of the Software Crisis.

These are under the MIT License. Do what you want, essentially. Probably doesn't work out of the box. Uses Pandoc (version 2.19.2), zx, weasyprint, and Literata.

You'll need to download them for this to work. And you'll need to edit the build script to point at the correct CSS files. And you'l need to edit the CSS to point at the correct font files.

import { $, chalk, echo, glob, path, fs } from "zx";
$.verbose = false;
const bookDir = "publications";
const metadata = await glob([`${bookDir}/**/*.yaml`]);
const books = metadata.map((name) => path.basename(name, ".yaml"));
let bookData = [];
for (const meta of metadata) {
const book = path.basename(meta, ".yaml");
const dir = path.dirname(meta);
bookData = bookData.concat({
slug: book,
metadata: meta,
chapters: await glob([`${dir}/*.md`]),
});
}
for (const book of bookData) {
const chapters = book.chapters.join(" ");
const wc = $`wc -w ${book.chapters}`;
console.log(chalk.bold.blue(book.slug));
echo`\nWord count for ${book.slug}: \n${await wc}\n`;
console.log("Building", chalk.bold(`dist/${book.slug}.pdf`));
const cssPath = (await fs.pathExists(
`${bookDir}/${book.slug}/${book.slug}.css`
))
? `--css=${bookDir}/${book.slug}/${book.slug}.css`
: "";
await $`pandoc ${book.chapters} -o dist/${book.slug}.pdf --css=src/pdf/style.css --resource-path=.:static:src:src/img --pdf-engine=weasyprint --toc --template=special ${cssPath}`;
console.log("Building", chalk.bold(`dist/${book.slug}.epub`));
await $`pandoc ${book.chapters} ${book.metadata} -o dist/${book.slug}.epub --css=src/pdf/epub.css --resource-path=.:static:src:src/img --toc --shift-heading-level-by=-1`;
console.log(book.slug, ": done\n\n");
}
.Photo {
height: 100%;
/* page-break-after: always; */
}
.Notice {
display: none;
}
h1 {
margin: 0;
padding: 0;
font-family: Literata36, "Iowan Old Style", "Sitka Text", Palatino,
"Book Antiqua", serif;
font-weight: 300;
line-height: 1;
font-size: 2em;
text-align: center;
}
.author {
font-size: 2.5em;
text-align: center;
margin: 0;
padding: 2em 0;
}
.date {
font-size: 3em;
text-align: center;
font-style: italic;
margin: 0;
padding: 0;
display: none;
}
.Preamble {
font-style: italic;
text-align: center;
width: 75%;
margin: 0 auto;
color: white;
}
h2 {
/* page-break-after: always; */
}
html {
hyphens: auto;
}
html {
font-family: Literata, "Iowan Old Style", "Sitka Text", Palatino,
"Book Antiqua", serif;
}
/* Base */
* {
box-sizing: border-box;
}
img {
max-width: 100%;
height: auto;
}
body {
font-family: Literata, "Iowan Old Style", "Sitka Text", Palatino,
"Book Antiqua", serif;
margin: 0;
line-height: 1.25;
hyphens: auto;
}
hr {
border: none;
border-bottom: 1px solid #333;
width: 50%;
margin: 0 auto;
padding-top: 2.5em;
margin-bottom: 2.5em;
}
blockquote {
-webkit-hyphens: auto;
-ms-hyphens: auto;
hyphens: auto;
padding: 0;
margin-left: 1.25em;
}
blockquote,
blockquote p {
font-size: 0.95em;
line-height: 1.2;
}
a:link {
color: #003366;
position: relative;
}
a:visited {
color: #620;
}
a:hover {
color: #620;
}
[aria-hidden="true"] {
display: none;
}
.contact-list {
padding: 0;
list-style: none;
margin: 0 0 2em;
}
p {
margin: 0;
}
p,
ol,
ul,
dl,
blockquote,
figure,
table,
hr,
section,
article,
details,
figcaption {
margin-top: 1.25em;
-webkit-hyphens: auto;
-ms-hyphens: auto;
hyphens: auto;
text-align: left;
}
figcaption {
font-style: italic;
}
abbr {
font-variant: none;
text-transform: uppercase;
font-variant-caps: all-small-caps;
}
pre code {
page-break-inside: avoid;
white-space: pre-wrap;
}
body > header {
margin: 1em;
}
main header {
margin: 0 0 3em 0;
display: flex;
flex-direction: column;
justify-content: space-around;
}
h1,
h2,
h3,
hr {
font-family: Literata36, "Iowan Old Style", "Sitka Text", Palatino,
"Book Antiqua", serif;
/* break-before: always;
page-break-before: always; */
padding-top: 5em;
hyphens: manual;
}
h1 {
padding: 1rem 0;
margin: 3rem 0 3rem;
line-height: 1.25;
border-bottom: 1px solid black;
}
h1.main {
border: none;
margin-top: 3em;
}
.subtitle {
font-size: 1.5rem;
text-align: center;
}
.author {
font-size: 2rem;
text-align: center;
font-style: italic;
}
.rights {
text-align: center;
}
h1.main::before {
border: none;
content: "";
}
h1::before {
content: attr(data-number);
display: block;
padding: 1rem 0;
margin: 1rem 0;
border-bottom: 1px solid black;
}
/* section div.PartPage {
-webkit-page-break-after: always;
-webkit-page-break-before: always;
page-break-after: always;
page-break-before: always;
-webkit-column-break-after: always;
-webkit-column-break-before: always;
column-break-after: always;
column-break-before: always;
page-break-after: page;
page-break-before: page;
break-after: always;
break-before: always;
} */
.part-title {
text-align: center;
font-size: 250%;
line-height: 1;
display: block;
background-color: page;
}
.part {
display: block;
font-size: 125%;
text-align: center;
margin: 0;
padding-top: 10rem;
}
.part-title + .part {
padding-top: 1rem;
}
hr.epub-break {
border: none;
/* page-break-after: always;
-webkit-page-break-after: always; */
/* page-break-after: always;
-webkit-column-break-after: always;
column-break-after: always; */
/* page-break-after: page;
break-after: always; */
}
h2 {
font-size: 1.5em;
font-weight: 600;
margin: 0;
padding: 1rem 0 0;
line-height: 1.2;
text-transform: uppercase;
}
h3 {
font-size: 1.1em;
line-height: 1.2;
font-weight: 600;
margin: 0;
padding: 0.5rem 0 0;
font-style: italic;
}
code {
font-family: Fira Mono, DejaVu Sans Mono, Menlo, Consolas, Liberation Mono,
Monaco, Lucida Console, monospace;
}
#toc ol ol {
display: none;
}
.Aside {
background-color: #f0f0f0;
padding: 0.25rem 2.2rem 1.1rem;
margin: 1.1rem -2.2rem;
position: relative;
}
.references li p {
hyphens: manual;
text-align: left;
}
<!DOCTYPE html>
<html
xmlns="http://www.w3.org/1999/xhtml"
lang="$lang$"
xml:lang="$lang$"
$if(dir)$
dir="$dir$"
$endif$
>
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=yes"
/>
$for(author-meta)$
<meta name="author" content="$author-meta$" />
$endfor$ $if(date-meta)$
<meta name="dcterms.date" content="$date-meta$" />
$endif$ $if(keywords)$
<meta name="keywords" content="$for(keywords)$$keywords$$sep$, $endfor$" />
$endif$ $if(description-meta)$
<meta name="description" content="$description-meta$" />
$endif$
<title>$if(title-prefix)$$title-prefix$ – $endif$$pagetitle$</title>
<style>
$styles.html()$
</style>
$for(css)$
<link rel="stylesheet" href="$css$" />
$endfor$ $if(math)$ $math$ $endif$
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
<!-- <script src="node_modules/pagedjs/dist/paged.polyfill.js"></script> -->
$for(header-includes)$ $header-includes$ $endfor$
</head>
<body class="hyphenate">
$for(include-before)$ $include-before$ $endfor$ $if(cover-image)$
<div id="cover">
<img src="$cover-image$" alt="Cover for $title$" />
</div>
$endif$ $if(title)$
<div id="title-block-header2">
<div class="title" id="faketitle">$title$</div>
</div>
<hr class="page-break" />
<header id="title-block-header">
<h1 class="title" id="$idprefix$main-title">$title$</h1>
$if(subtitle)$
<p class="subtitle">$subtitle$</p>
$endif$ $for(author)$
<p class="author">$author$</p>
$endfor$ $if(date)$
<p class="date">$date$</p>
$endif$ $if(abstract)$
<p class="abstract">$abstract$</p>
$endif$
</header>
$endif$ $if(copyright)$
<div id="copyright">
<p>$copyright$</p>
<p>All rights reserved</p>
<p>Published in Hveragerði, Iceland</p>
<p>$copyright-additional$</p>
</div>
$endif$ $if(toc)$
<nav id="$idprefix$TOC" role="doc-toc">
$if(toc-title)$
<h2 id="$idprefix$toc-title">$toc-title$</h2>
$endif$ $table-of-contents$
</nav>
$endif$
<main>$body$</main>
$for(include-after)$ $include-after$ $endfor$
</body>
</html>
@font-face {
font-family: "Literata Regular";
src: url("Literata/Literata-Italic.ttf");
font-style: italic;
}
@font-face {
font-family: "Literata Regular";
src: url("Literata/Literata-Regular.ttf");
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: "Literata Regular";
src: url("Literata/Literata-BoldItalic.ttf");
font-weight: 600;
font-style: italic;
}
@font-face {
font-family: "Literata Regular";
src: url("Literata/Literata-Bold.ttf");
font-weight: 600;
}
@font-face {
font-family: "Literata36";
src: url("Literata_36pt/Literata_36pt-Italic.ttf");
font-style: italic;
}
@font-face {
font-family: "Literata36";
src: url("Literata_36pt/Literata_36pt-Bold.ttf");
font-weight: 600;
}
@font-face {
font-family: "Literata36";
src: url("Literata_36pt/Literata_36pt-BoldItalic.ttf");
font-weight: 600;
font-style: italic;
}
@font-face {
font-family: "Literata36";
src: url("Literata_36pt/Literata_36pt-Light.ttf");
font-weight: 300;
}
@font-face {
font-family: "Literata36";
src: url("Literata_36pt/Literata_36pt-LightItalic.ttf");
font-weight: 300;
font-style: italic;
}
@font-face {
font-family: "Literata36";
src: url("Literata_36pt/Literata_36pt-Regular.ttf");
font-weight: 400;
}
html {
font-variant-ligatures: common-ligatures;
font-family: "Literata Regular", "Iowan Old Style", "Sitka Text", Palatino,
"Book Antiqua", serif;
font-weight: normal;
font-style: normal;
}
p,
span {
font-weight: normal;
font-style: normal;
}
#TOC {
page: empty;
line-height: 1.4;
}
#TOC > ul {
counter-reset: tocCounter;
counter-reset: subTocCounter;
list-style: none;
padding: 0;
margin: 0;
font-size: 1rem;
}
#TOC > ul ul {
list-style: none;
padding: 0;
margin: 0;
padding-left: 1.25em;
font-size: 1rem;
}
#TOC > ul > li {
counter-increment: tocCounter;
margin: 0.5rem 0;
font-weight: normal;
}
#TOC li {
padding: 0;
}
#TOC > ul > li li {
counter-increment: subTocCounter;
margin: 0.5rem 0;
}
#TOC > ul > li > a {
text-transform: uppercase;
}
#TOC li a {
text-decoration: none;
color: black;
}
#TOC li a::after {
content: leader(".") target-counter(attr(href), page);
}
#TOC li a::before {
margin-right: 1rem;
}
#TOC li li a::before {
content: counter(subTocCounter) ".";
margin-right: 1rem;
display: inline-block;
width: 1.5rem;
text-align: right;
font-variant-numeric: tabular-nums;
}
#TOC li a#toc-do-you-feel-lost-in-a-wilderness-of-web-dev-frameworks {
display: none;
}
#TOC ul ul ul {
display: none;
}
@page {
size: A4;
margin: 2.5cm 2.75cm;
@top-center {
content: string(chapter);
font-style: italic;
font-size: 1rem;
}
@bottom-center {
font-style: italic;
content: counter(page);
}
@footnote {
border-top: 1px solid black;
}
}
@page :left {
@top-center {
content: string(chapter);
font-style: italic;
font-size: 1rem;
}
}
@page :right {
@top-center {
content: string(title);
font-style: italic;
font-size: 1rem;
}
}
@page headingpage {
margin: 2cm 2.75cm;
@top-center {
content: "";
}
@bottom-center {
content: "";
}
}
@page partpage {
background-color: #f5f5f5;
margin: 2cm;
@top-center {
content: "";
}
@bottom-center {
content: "";
}
}
@page subheadingpage {
margin: 2cm;
@top-center {
content: "";
}
}
@page empty {
margin: 2.5cm 3cm;
@top-center {
content: "";
}
@bottom-center {
content: "";
}
}
@page :blank {
margin: 2.5cm 3cm;
@top-center {
content: "";
}
@bottom-center {
content: "";
}
}
@page titlepage {
/* color: white;
background-color: #d4015e; */
margin: 1cm 3cm;
font-weight: normal;
@top-center {
content: "";
}
@bottom-center {
content: "";
}
}
@page photopage {
background-color: #333;
margin: 2cm;
@top-center {
height: 0;
content: "";
}
@bottom-center {
height: 0;
content: "";
}
}
@page cover {
background-color: transparent;
margin: 0;
@top-center {
height: 0;
content: "";
}
@bottom-center {
height: 0;
content: "";
}
}
#cover {
page: cover;
width: 100%;
height: 100%;
}
#cover img {
object-fit: cover;
display: block;
height: 100%;
width: 100%;
}
.Photo {
page: photopage;
height: 100%;
page-break-after: always;
}
.Notice {
display: none;
}
#title-block-header,
#title-block-header2,
#copyright {
page: titlepage;
padding: 0;
margin: 0;
margin-top: 12rem;
page-break-before: right;
}
#copyright {
page-break-before: left;
}
#title-block-header h1,
#title-block-header2 h1,
#faketitle {
margin: 0;
margin-bottom: 2rem;
padding: 0 1rem;
font-family: Literata36, "Iowan Old Style", "Sitka Text", Palatino,
"Book Antiqua", serif;
font-weight: 600;
line-height: 1;
font-size: 4.5rem;
text-align: center;
string-set: title content();
counter-reset: chapterCounter;
}
#title-block-header2 h1 {
bookmark-level: none;
}
#copyright p {
font-style: italic;
text-align: center;
text-indent: 0;
margin: 0.5rem 0;
}
.author {
font-size: 2.5rem;
text-align: center;
margin: 2rem 0;
padding: 0;
}
.subtitle {
font-size: 1.5rem;
text-align: center;
font-style: italic;
padding: 1rem 0;
}
.date {
font-size: 3rem;
text-align: center;
font-style: italic;
margin: 0;
padding: 0;
display: none;
}
.abstract {
font-style: italic;
text-align: center;
width: 75%;
margin: 0 auto;
color: white;
page-break-after: always;
text-indent: 0;
}
/* Base */
* {
box-sizing: border-box;
}
img {
max-width: 100%;
height: auto;
}
body {
margin: 0;
line-height: 1.475;
font-size: 1.1rem;
hyphens: auto;
}
hr {
border: none;
border-bottom: 1px solid #333;
width: 50%;
margin: 0 auto;
padding-top: 2.2rem;
padding-bottom: 2.2rem;
height: 1px;
}
hr.page-break {
border: none;
margin: 0;
page-break-before: always;
page-break-after: always;
page: empty;
}
hr.page-break-after {
border: none;
margin: 0;
page-break-after: always;
}
hr.space {
border-color: transparent;
}
blockquote {
-webkit-hyphens: auto;
-ms-hyphens: auto;
hyphens: auto;
margin-left: 2.5em;
}
blockquote,
blockquote p {
font-size: 1rem;
line-height: 1.2;
}
a:link {
color: #003366;
position: relative;
}
a:visited {
color: #620;
}
a:hover {
color: #620;
}
[aria-hidden="true"] {
display: none;
}
.contact-list {
padding: 0;
list-style: none;
margin: 0 0 2em;
}
p {
margin: 0;
}
p + p {
text-indent: 1.5rem;
}
ol,
blockquote,
ul,
dl,
section,
article,
details,
figcaption {
padding: 1.1em 0;
-webkit-hyphens: auto;
-ms-hyphens: auto;
hyphens: auto;
text-indent: 0;
margin: 0;
}
li {
text-align: left;
-webkit-hyphens: auto;
-ms-hyphens: auto;
hyphens: auto;
text-indent: 0;
margin: 0;
padding-left: 1.5rem;
padding-right: 0.7rem;
}
li + li {
padding-top: 1.1rem;
}
figure {
padding: 2.2rem 2.2rem 1.1rem;
-webkit-hyphens: auto;
-ms-hyphens: auto;
hyphens: auto;
text-indent: 0;
margin: 0;
page-break-inside: avoid;
}
figure.image {
padding: 1.1rem 0;
}
figure.image > figcaption {
padding: 0.55rem 2.2rem;
}
figure blockquote,
figure blockquote > p:first-child {
padding: 0;
}
figure figcaption {
padding-top: 0.55rem;
font-size: 0.9rem;
padding-bottom: 0.55rem;
font-style: italic;
text-align: right;
}
figure blockquote {
font-size: 0.85rem;
}
figure + figure {
padding-top: 0;
}
h3 + figure {
padding-top: 1.1rem;
margin-top: 0;
}
table {
width: 100%;
page-break-inside: avoid;
margin: 1.1em 0;
-webkit-hyphens: manual;
-ms-hyphens: manual;
hyphens: manual;
text-indent: 0;
/* table-layout: fixed; */
width: 100%;
border-collapse: collapse;
}
th {
-webkit-hyphens: manual;
-ms-hyphens: manual;
hyphens: manual;
}
main * {
text-align: justify;
}
.sourceCode * {
text-align: left;
hyphens: none;
white-space: pre-wrap;
}
abbr {
font-variant: none;
text-transform: uppercase;
font-variant-caps: all-small-caps;
}
pre {
max-width: 100%;
background-color: white;
}
h2 {
font-family: Literata36, "Iowan Old Style", "Sitka Text", Palatino,
"Book Antiqua", serif;
padding-top: 0em;
margin-top: -5rem;
}
main h3 {
font-family: Literata36, "Iowan Old Style", "Sitka Text", Palatino,
"Book Antiqua", serif;
counter-increment: subChapterCounter;
}
h3 {
font-size: 2rem;
line-height: 1.2;
font-weight: 400;
text-align: left;
margin-bottom: 0.55rem;
}
h2::before {
content: counter(chapterCounter);
border-bottom: 1px solid;
font-size: 2.5rem;
width: 18rem;
font-weight: normal;
margin: 1.75rem auto 1.5rem;
display: block;
padding: 1.75rem 0 1.5rem;
}
h2#introduction::before {
content: "";
}
h2::after {
content: " ";
border-top: 1px solid;
width: 18rem;
height: 1px;
margin: 1.75rem auto;
display: block;
}
h2 {
font-size: 2.5rem;
font-weight: 600;
padding: 0.5em 0;
margin: 2em 0 0;
line-height: 1.2;
text-align: center;
}
#toc-title {
page-break-after: avoid;
page-break-before: avoid;
text-align: left;
padding: 0;
margin: 3rem 0 2rem;
font-size: 2rem;
string-set: chapter "";
bookmark-level: 1;
}
main h2 {
string-set: chapter content();
page-break-before: right;
break-before: right;
page: headingpage;
bookmark-level: 1;
}
main h1 {
page-break-before: left;
break-before: left;
page: partpage;
font-size: 6rem;
text-align: center;
font-weight: 400;
line-height: 1;
bookmark-level: 1;
}
main h1::before {
content: "";
display: block;
break-after: always;
}
h1 .part {
display: block;
font-size: 50%;
text-align: center;
margin: 0;
padding-top: 10rem;
}
main .part-title + .part {
display: block;
font-size: 50%;
text-align: center;
margin: 0;
padding-top: 1rem;
}
#what-next .part-title {
padding-top: 10rem;
display: block;
text-align: center;
}
main h2:not(#introduction):not(#toc-title) {
counter-increment: chapterCounter;
bookmark-level: 2;
}
#toc-title::before,
#toc-title::after {
display: none;
}
code {
font-size: 75%;
line-height: 1;
font-family: Fira Mono, DejaVu Sans Mono, Menlo, Consolas, Liberation Mono,
Monaco, Lucida Console, monospace;
}
* {
text-rendering: geometricprecision !important;
}
.Aside,
.Releases {
background-color: #f0f0f0;
padding: 0.25rem 2.2rem 1.95rem;
margin: 1.1rem -2.2rem;
position: relative;
}
.Aside p:first-child {
padding-top: 1.35rem;
}
h4 {
margin-bottom: 0;
}
.Aside h4 {
margin: 0;
padding: 0;
}
.Releases h4 {
margin: 0;
padding: 0;
padding-top: 1.35rem;
font-size: 1.35rem;
}
.Releases li {
padding-left: 1.1rem;
}
.references li p {
hyphens: manual;
text-align: left;
}
#toc-appendix-a-choosing-a-web-framework ul {
display: none;
}
hr.epub-break {
display: none;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment