diff --git a/packages/core/src/xhtml/builder.ts b/packages/core/src/xhtml/builder.ts index cedcc0e..e67965e 100644 --- a/packages/core/src/xhtml/builder.ts +++ b/packages/core/src/xhtml/builder.ts @@ -6,6 +6,7 @@ import { Item, Style } from '../epub/item'; import { TextCSS, TextXHTML } from '../constant'; import type { XHTMLNode } from './types'; + import { Fragment } from './render'; const builder = new XMLBuilder({ @@ -65,6 +66,8 @@ export class XHTMLBuilder { private _body: XHTMLNode[] = []; + private _bodyAttrs: Record = {}; + public constructor(filename: string) { this._filename = filename; this._meta.title = path.basename(filename); @@ -117,6 +120,15 @@ export class XHTMLBuilder { return this; } + public bodyAttrs(attrs: Record = {}) { + const a = Object.entries(attrs).map(([key, value]) => [`@_${key}`, value]); + this._bodyAttrs = { + ...this._bodyAttrs, + ...Object.fromEntries(a) + }; + return this; + } + public build(): XHTML { const content = builder.build({ html: { @@ -128,7 +140,10 @@ export class XHTMLBuilder { title: this._meta.title, ...list(this._head) }, - body: list(this._body) + body: { + ...this._bodyAttrs, + ...list(this._body) + } } }); diff --git a/packages/theme-default/package.json b/packages/theme-default/package.json index 1f5f866..c8aaf6f 100644 --- a/packages/theme-default/package.json +++ b/packages/theme-default/package.json @@ -50,9 +50,9 @@ "node": ">=v18.14.2" }, "epubook": { - "pages": [], "styles": [ - "styles/main.css" + "styles/main.css", + "styles/cover.css" ], "images": [] } diff --git a/packages/theme-default/src/index.ts b/packages/theme-default/src/index.ts index 15446ab..bb1f232 100644 --- a/packages/theme-default/src/index.ts +++ b/packages/theme-default/src/index.ts @@ -10,7 +10,7 @@ import { DefaultThemePageTemplate } from './types'; export * from './types'; export * from './chapter'; -const styles = ['./styles/main.css']; +const styles = ['./styles/main.css', './styles/cover.css']; export async function DefaultTheme(): Promise> { return { @@ -21,6 +21,7 @@ export async function DefaultTheme(): Promise> { cover(file, { image, title = '封面' }) { return new XHTMLBuilder(file) .title(title) + .bodyAttrs({ class: 'cover' }) .body({ tag: 'img', attrs: { src: image.relative(file) } }); }, nav(file, { nav, option }) { diff --git a/packages/theme-default/styles/cover.css b/packages/theme-default/styles/cover.css new file mode 100644 index 0000000..acf9e33 --- /dev/null +++ b/packages/theme-default/styles/cover.css @@ -0,0 +1,13 @@ +body.cover, body.cover div, body.cover img { + margin: 0; + padding: 0; + text-align: center; +} + +body.cover img { + height: 100%; + max-width: 100%; + border: 0 none; + vertical-align: bottom; + object-fit: contain; +} diff --git a/packages/theme-default/styles/main.css b/packages/theme-default/styles/main.css index b8d96de..5e24058 100644 --- a/packages/theme-default/styles/main.css +++ b/packages/theme-default/styles/main.css @@ -1,74 +1,263 @@ +/* See: https://github.com/spajak/epub3-boilerplate */ + +@charset "UTF-8"; + +@page { + margin-left: 10pt; + margin-right: 10pt; + padding: 0; +} + +.hidden, [hidden] { + display: none; +} + +/* --- sections --- */ +article, +aside, +blockquote, +figure, +figcaption, +footer, +header, +main, +nav, +section { + display: block; +} + body { - padding: 0%; - margin-top: 0%; - margin-bottom: 0%; - margin-left: 1%; - margin-right: 1%; - line-height: 130%; + font-size: 100%; + font-weight: normal; text-align: justify; + line-height: 1.35; + margin: 0; + padding: 0; + -webkit-hyphens: auto; + hyphens: auto; + widows: 2; + orphans: 2; } -h1 { - line-height: 120%; +section, nav { + page-break-before: always; + page-break-after: always; +} + +h1, h2, h3, h4, h5, h6 { text-align: center; + line-height: 1.25; font-weight: bold; - font-size: 1.65em; - margin-top: 0.1em; - margin-bottom: 0.4em; + margin: 1.5em 0; + -epub-hyphens: none; + hyphens: none; + page-break-after: avoid; + page-break-inside: avoid; +} + +h1 { + font-size: 2em; } h2 { - font-size: 1.8em; - text-align: center; - line-height: 1.2em; - text-indent: 0em; - font-weight: bold; - margin-top: 0em; - margin-bottom: 2em; + font-size: 1.625em; + margin-top: 2.75em; } h3 { - font-size: 1.5em; - text-align: center; - line-height: 1.2em; - text-indent: 0em; - font-weight: bold; - margin-top: 1em; - margin-bottom: 1.5em; + font-size: 1.375em; + margin-top: 2em; + margin-bottom: 1.25em; } h4 { - font-weight: bold; - font-size: 1.2em; - text-indent: 0em; - margin-top: 0em; - margin-bottom: 0.3em; - border-bottom: 0.1em solid #000000; - padding: 0.3em 0 0.2em 0; + font-size: 1.125em; + margin-bottom: 1em; } -h5 { +h5, h6 { + font-size: 1em; + margin-top: 1em; + margin-bottom: 0.75em; +} + +h6 { + font-style: italic; +} + +/* --- Content grouping --- */ +p, blockquote, figure, ol, ul, dl, pre { + margin-top: 1em; + margin-bottom: 1em; + text-indent: 0; +} + +p.classic, +p.book, +p.novel { + margin-top: 0; + margin-bottom: 0; +} + +p + p.classic, +p + p.book, +p + p.novel { + text-indent: 1.125em; +} + +p.stanza, p.verses, p.verse, p.poetry { + margin-top: 1em; + margin-bottom: 1em; + text-align: left; + text-indent: 0; +} + +blockquote { + padding-left: 18pt; + padding-right: 18pt; + margin-left: 0; + margin-right: 0; +} + +blockquote.fancy { + padding-left: 10pt; + padding-right: 16pt; + border-left: 0.875rem solid #9F9F9F; +} + +dt { font-weight: bold; - font-size: 1.1em; - text-indent: 0em; - margin-top: 0.4em; - margin-bottom: 0.4em; - border-left: 3px solid #ff00ff; - border-bottom: 1px solid #ff00ff; + text-align: left; + page-break-after: avoid; +} + +dd { padding: 0; + margin-left: 0; + margin-right: 0; + margin-bottom: 0.25em; + padding-left: 22pt; + page-break-before: avoid; } -div { - margin: 0px; - padding: 0px; - text-align: justify; - font-family: 'DK-SYMBOL'; +figure { + padding: 0; + margin-left: 0; + margin-right: 0; + text-align: center; + page-break-inside: avoid; +} + +figcaption { + margin: 0.25em auto; + line-height: 1.25; + font-size: 0.875em; + text-align: center; } -p { - text-indent: 2em; +hr { + height: 0; + width: auto; + padding: 0; + border: 0 none; + border-top: 0.125rem solid currentColor; + margin: 1.5em auto; + text-align: center; + page-break-after: avoid; +} + +pre { + tab-size: 2; + text-align: left; display: block; - line-height: 1.3em; - margin-top: 0.4em; - margin-bottom: 0.4em; + white-space: pre-wrap; + font-size: 0.8125em; + line-height: 1.25; +} + +/* --- List --- */ +ul, ol { + text-align: initial; + list-style-position: outside; + margin-left: 0; +} + +ol { + list-style-type: decimal; + padding-left: 34pt; +} + +ol.wide { + padding-left: 44pt; +} + +ul { + list-style-type: disc; + padding-left: 24pt; +} + +ul ul { + list-style-type: circle; +} + +li ol, li ul { + margin-top: 0; + margin-bottom: 0; + padding-left: 24pt; +} + +li ul { + padding-left: 16pt; +} + +/* --- Image --- */ +img { + max-width: 100%; + max-height: 100%; + object-fit: contain; + vertical-align: bottom; +} + +/* --- Text --- */ +em, cite, q, i { + font-style: italic; +} + +q { + quotes: none !important; + font-style: italic !important; +} + +dfn { + font-style: normal; + font-weight: bold; +} + +strong, b { + font-weight: bold; +} + +small { + font-size: 0.875em; +} + +sub, sup { + font-size: 0.675em; + line-height: 1.2; } + +sub { + vertical-align: sub; + vertical-align: -20%; +} + +sup { + vertical-align: super; + vertical-align: 35%; +} + +code { + white-space: pre; + text-align: left; + font-family: monospace; +} \ No newline at end of file