diff --git a/build.js b/build.js index ef57a4e..846673d 100644 --- a/build.js +++ b/build.js @@ -6,8 +6,6 @@ const builders = require('./src/lib/builders'); const mdHtml = require('./src/lib/md-html'); const htmlProcess = require('./src/lib/html-process'); -const css = fs.readFileSync('./src/style.css', 'utf-8'); - const l10n = { en: require('./src/en/l10n.json'), ru: require('./src/ru/l10n.json') @@ -66,20 +64,20 @@ async function buildDoc (lang, targets, l10n) { }, [templates.sectionTitle(section)]).join('')) ]; - const html = targets.html || targets.pdf ? (await htmlProcess( - templates.html(htmlContent.join(''), css, l10n), { - base: __dirname - } - )).contents : ''; - return Promise.all(['html', 'pdf', 'epub'].map((target) => { - return targets[target] ? builders[target]({ - lang, - structure, - html, - l10n, - path: path.join(__dirname, 'docs', `API.${lang}.${target}`) - }) : Promise.resolve(); + if (targets[target]) { + return prepareHtml(htmlContent.join(''), l10n, target).then((html) => { + return builders[target]({ + lang, + structure, + html, + l10n, + path: path.join(__dirname, 'docs', `API.${lang}.${target}`) + }); + }); + } else { + return Promise.resolve(); + } })); } @@ -129,4 +127,16 @@ async function getStructure ({ path, l10n, pageBreak}) { return structure; +} + +async function prepareHtml (content, l10n, target) { + if (target == 'epub') { + return ''; + } else { + return (await htmlProcess( + templates[target == 'html' ? 'screenHtml' : 'printHtml'](content, l10n), { + base: __dirname + } + )).contents; + } } \ No newline at end of file diff --git a/package.json b/package.json index d1f919b..e6126c5 100644 --- a/package.json +++ b/package.json @@ -5,14 +5,15 @@ "author": "Sergey Konstantinov ", "repository": "github.com:twirl/The-API-Book", "devDependencies": { - "puppeteer": "^5.5.0", + "css": "^3.0.0", "epub-gen": "^0.1.0", - "unified": "^9.2.0", - "remark-parse": "^8.0.3", - "remark-rehype": "^7.0.0", + "image-data-uri": "^2.0.1", + "puppeteer": "^5.5.0", "rehype-parse": "^7.0.1", "rehype-stringify": "^8.0.0", - "image-data-uri": "^2.0.1" + "remark-parse": "^8.0.3", + "remark-rehype": "^7.0.0", + "unified": "^9.2.0" }, "scripts": { "build": "node build.js" diff --git a/src/cover_300dpi.png b/src/cover_300dpi.png new file mode 100644 index 0000000..c863b95 Binary files /dev/null and b/src/cover_300dpi.png differ diff --git a/src/cover_96dpi.png b/src/cover_96dpi.png new file mode 100644 index 0000000..06be47a Binary files /dev/null and b/src/cover_96dpi.png differ diff --git a/src/epub.css b/src/epub.css index 1e3c9d6..f092627 100644 --- a/src/epub.css +++ b/src/epub.css @@ -58,6 +58,10 @@ h3 { font-variant: small-caps; } +h3 a, h5 a { + color: inherit; +} + h4, h5 { font-size: 120%; } diff --git a/src/lib/builders.js b/src/lib/builders.js index 4bb3b65..ea271e8 100644 --- a/src/lib/builders.js +++ b/src/lib/builders.js @@ -1,22 +1,17 @@ const fs = require('fs'); -const path = require('path'); +const fsPath = require('path'); const puppeteer = require('puppeteer'); const Epub = require('epub-gen'); -const css = fs.readFileSync(path.resolve(__dirname, '..', 'epub.css'), 'utf-8'); +const css = fs.readFileSync(fsPath.resolve(__dirname, '..', 'epub.css'), 'utf-8'); module.exports = { html: function ({ html, path }) { return new Promise((resolve, reject) => { - fs.writeFile(path, html, (e) => { - if (e) { - reject(e); - } else { - resolve(); - } - }); - }); + fs.writeFileSync(path, html); + resolve(); + }); }, pdf: async function ({ path, html }) { const browser = await puppeteer.launch({ headless: true }); diff --git a/src/lib/css-process.js b/src/lib/css-process.js new file mode 100644 index 0000000..b8ccde1 --- /dev/null +++ b/src/lib/css-process.js @@ -0,0 +1,22 @@ +const path = require('path'); +const fs = require('fs'); + +const imageDataUri = require('image-data-uri'); +const reworkCss = require('css'); + +module.exports = (css) => { + const ast = reworkCss.parse(css); + + (ast.stylesheet.rules || []).forEach((rule) => { + (rule.declarations || []).forEach((declaration) => { + if (declaration.property == 'background-image') { + const file = declaration.value.match(/url\((.+)\)/)[1]; + const data = fs.readFileSync(path.resolve(__dirname, '../..', file)); + const uri = imageDataUri.encode(data, 'image/png'); + declaration.value = `url(${uri})`; + } + }); + }) + + return reworkCss.stringify(ast); +} \ No newline at end of file diff --git a/src/lib/md-html.js b/src/lib/md-html.js index eaffedb..e83cbba 100644 --- a/src/lib/md-html.js +++ b/src/lib/md-html.js @@ -27,11 +27,41 @@ const chapterProcessor = () => { } } + let h5counter = 0; tree.children.slice().forEach((node, index) => { switch (node.tagName) { case 'h3': title.push(node.children[0].value); tree.children.splice(index, 1); + h5counter = 0; + break; + case 'h5': + let value = node.children[0].value; + let number; + const match = value.match(/^\d+/); + if (!match) { + number = ++h5counter; + value = `${number}. ${value}`; + } else { + number = match[0]; + } + const anchor = `chapter-${counter}-paragraph-${number}`; + + node.children[0] = { + type: 'element', + tagName: 'a', + properties: { + href: '#' + anchor, + name: anchor, + className: ['anchor'] + }, + children: [{ + type: 'text', + value + }], + position: node.children[0].position + }; + break; } imageProcess(node); diff --git a/src/print.css b/src/print.css new file mode 100644 index 0000000..d36ede6 --- /dev/null +++ b/src/print.css @@ -0,0 +1,58 @@ +html, body { + margin: 0; + padding: 0; + font-size: 14pt; +} + +.github-corner { + display: none; +} + +pre, img { + margin-right: 0.1em; +} + +img { + margin-top: 1em; +} + +:root { + --main-font: 'PT Serif'; + --alt-font: 'PT Serif'; + --code-font: Inconsolata; +} + +.cover { + background-image: url(./src/cover_300dpi.png); + background-size: 100% auto; + background-repeat: no-repeat; + background-position: 50% 100%; + width: 100%; + height: 10.05in; + margin: 0; + padding: 0; +} + +.annotation { + padding-top: 6in; +} + +h1 { + padding: 1in 0.5in; + font-size: 40pt; + margin: 0; +} + +p, ul, ol { + orphans: 4; +} + +@page { + margin: 0.5in; + size: 8.5in 11in; +} + +@page:first { + margin: 0; + size: 7.5in 10in; +} \ No newline at end of file diff --git a/src/screen.css b/src/screen.css new file mode 100644 index 0000000..adad07b --- /dev/null +++ b/src/screen.css @@ -0,0 +1,12 @@ +h1 { + font-size: 48px; + background-image: url(./src/cover_96dpi.png); + background-repeat: no-repeat; + background-position: 50% 100%; + height: 1056px; + min-width: 300px; + max-width: 816px; + margin: 0 auto; + padding-top: 4em; + padding-left: 2em; +} \ No newline at end of file diff --git a/src/style.css b/src/style.css index d091d39..7813758 100644 --- a/src/style.css +++ b/src/style.css @@ -108,6 +108,10 @@ h4, h5 { font-size: 120%; } +.annotation { + text-align: left; +} + ul.table-of-contents { list-style-type: none; padding-left: 0; @@ -130,17 +134,6 @@ a.anchor { text-decoration: none; } -@page { - size: 8.5in 11in; - margin: 0.5in; -} - -:root { - --main-font: 'PT Serif'; - --alt-font: 'PT Serif'; - --code-font: Inconsolata; -} - @media screen { body { margin: 2em auto; @@ -155,14 +148,19 @@ a.anchor { content: 'ΒΆ'; width: 0.8em; color: lightgray; + text-indent: 0; + } + + h5 a.anchor:before { + color: transparent; } a.anchor:hover:before { color: black; } - h2:not(.toc), h3 { - margin-left: -0.8em; + h2:not(.toc), h3, h5 { + text-indent: -0.8em; } @keyframes octocat-wave { @@ -181,19 +179,6 @@ a.anchor { } } -@media print { - h1 { - margin: 4in 0 4in 0; - } - body { - font-size: 14pt; - } - - .github-corner { - display: none; - } -} - @media screen and (max-width: 1000px) { body { padding: 2em; diff --git a/src/templates.js b/src/templates.js index 5b05a06..5c1e814 100644 --- a/src/templates.js +++ b/src/templates.js @@ -1,5 +1,13 @@ +const fs = require('fs'); +const path = require('path'); +const css = fs.readFileSync(path.resolve(__dirname, 'style.css'), 'utf-8'); + +const cssProcess = require('./lib/css-process'); +const printCss = cssProcess(fs.readFileSync(path.resolve(__dirname, 'print.css'), 'utf-8')); +const screenCss = cssProcess(fs.readFileSync(path.resolve(__dirname, 'screen.css'), 'utf-8')); + const templates = module.exports = { - html: (html, css, l10n) => { + screenHtml: (html, l10n) => { return ` ${l10n.author}. ${l10n.title} @@ -12,6 +20,28 @@ const templates = module.exports = { + + +
+ ${html} +
+ `; + }, + + printHtml: (html, l10n) => { + return ` + + ${l10n.author}. ${l10n.title} + + + + + + + + + +
${html}