From 46f16d5dc4a2cf73ad61dcb79bb3f157159a4d27 Mon Sep 17 00:00:00 2001 From: Omikhleia Date: Tue, 8 Oct 2024 15:16:15 +0200 Subject: [PATCH] feat: More cover creation options in master documents --- examples/manual-masterdoc/bookmatters.dj | 35 ++++++++- inputters/silm.lua | 30 +++++--- packages/resilient/bookmatters/init.lua | 96 +++++++++++++++++++----- templates/cover.djt | 29 +++++++ 4 files changed, 163 insertions(+), 27 deletions(-) create mode 100644 templates/cover.djt diff --git a/examples/manual-masterdoc/bookmatters.dj b/examples/manual-masterdoc/bookmatters.dj index 63d7cd7..684d88b 100644 --- a/examples/manual-masterdoc/bookmatters.dj +++ b/examples/manual-masterdoc/bookmatters.dj @@ -89,6 +89,10 @@ book: image: mycover-back.jpg ``` +For convenience, the `image` can be provided globally in the `cover` section. +This is useful when the same image is used for both the front and back covers. +In the absence of an image, a `background` element can be used to set a color for the cover, either globally or for the front and back covers separately. We will see this in a moment. + The covers can be disabled by passing the `cover=false` option to the master document handler: ```shell @@ -143,7 +147,36 @@ book: ``` The frame containing the back-cover text now has a colored background. -The text is automatically typeset in either white or black, to ensure a good contrast. +The text is automatically set in either white or black, depending on the background, to ensure optimal contrast. + +If a back cover image is specified but no background color, the frame will default to white, with black text. If no image is provided, the background color will be applied to the entire back cover. +You also have the option to specify a separate background color specifically for the back-cover text frame: + +```yaml +book: + cover: + back: + background: ⟨named color⟩|"#⟨RGB color⟩" + content: mybackcover.dj + content-background: ⟨named color⟩|"#⟨RGB color⟩" +``` + +## Configuring the front cover + +In most cases, the text on the front cover of a book is part of the overall cover design, with its own typography and layout incorporated into the image. +If this applies to you, no further steps are needed beyond the options previously discussed. + +However, if you're in a situation where the cover design is not yet finalized or the image lacks text, you can customize the front cover using a "Djot template." +The text will be automatically typeset in either white or black, based on the background color, to ensure optimal contrast. + +```yaml +book: + cover: + front: + image: ⟨file name⟩ + background: ⟨named color⟩|"#⟨RGB color⟩" + template: cover +``` ## Other options diff --git a/inputters/silm.lua b/inputters/silm.lua index 2bda4d0..0cf177b 100644 --- a/inputters/silm.lua +++ b/inputters/silm.lua @@ -75,10 +75,14 @@ local BookSchema = { cover = { type = "object", properties = { + image = { type = "string" }, + background = { type = "string" }, front = { type = "object", properties = { - image = { type = "string" } + image = { type = "string" }, + background = { type = "string" }, + template = { type = "string" }, } }, back = { @@ -86,7 +90,8 @@ local BookSchema = { properties = { image = { type = "string" }, background = { type = "string" }, - content = SingleFileSchema + content = SingleFileSchema, + ['content-background'] = { type = "string" }, } } } @@ -513,7 +518,7 @@ function inputter:parse (doc) halftitle = bookmatter.halftitle or {}, title = bookmatter.title or {}, endpaper = bookmatter.endpaper or {}, - cover = bookmatter.cover or {}, + cover = bookmatter.cover, enabled = SU.boolean(bookmatter.enabled, false) } local enabledBook = isRoot and SU.boolean(self.options.bookmatter, bookmatter.enabled) @@ -525,9 +530,12 @@ function inputter:parse (doc) }) end - if enabledCover and bookmatter.cover.front then + if enabledCover and bookmatter.cover then + local cover = bookmatter.cover.front content[#content+1] = createCommand("bookmatters:front-cover", { - image = bookmatter.cover.front.image, + image = cover and cover.image or bookmatter.cover.image, + background = cover and cover.background or bookmatter.cover.background, + template = cover and cover.template, metadata = metadataOptions }) end @@ -562,12 +570,16 @@ function inputter:parse (doc) metadata = metadataOptions }) end - if enabledCover and bookmatter.cover.back then + if enabledCover and bookmatter.cover then + local cover = bookmatter.cover.back + local background = cover and cover.background or bookmatter.cover.background + local coverContent = cover and cover.content or nil content[#content+1] = createCommand("bookmatters:back-cover", { - image = bookmatter.cover.back.image, - background = bookmatter.cover.back.background, + image = cover and cover.image or bookmatter.cover.image, + background = background, + bgcontent = cover and cover["content-background"] or background, metadata = metadataOptions - }, doBackCoverContent(bookmatter.cover.back.content, metadataOptions)) + }, doBackCoverContent(coverContent, metadataOptions)) end if isRoot then diff --git a/packages/resilient/bookmatters/init.lua b/packages/resilient/bookmatters/init.lua index d25f2a4..17f9c8a 100644 --- a/packages/resilient/bookmatters/init.lua +++ b/packages/resilient/bookmatters/init.lua @@ -97,36 +97,60 @@ function package:registerCommands () -- Book matter support commands self:registerCommand("bookmatters:front-cover", function (options, _) - local image = SU.required(options, "image", "bookmatters:front-cover") - local src = SILE.resolveFile(image) or SU.error("Cannot find image file: " .. image) - -- local metadata = options.metadata or {} -- Unusual: table of metadata options + local image = options.image + local backgroundColor = options.background + local metadata = options.metadata or {} -- Unusual: table of metadata options + local template = options.template + local textColor = contrastColor(SILE.types.color(backgroundColor or "white")) SILE.call("switch-master-one-page", { id = "bookmatters-front-cover" }) SILE.call("hbox") -- To ensure some content - SILE.call("background", { - src = src, - allpages = false - }) + if image then + local src = SILE.resolveFile(image) or SU.error("Cannot find image file: " .. image) + SILE.call("background", { + src = src, + allpages = false + }) + elseif backgroundColor then + SILE.call("background", { + color = backgroundColor, + allpages = false + }) + end SILE.call("noheaders") SILE.call("nofolios") - SILE.call("eject") + if template then + SILE.call("color", { color = textColor }, function () + SILE.call("include", getTemplateInclude(template, metadata)) + end) + end + SILE.call("framebreak") SILE.call("set-counter", { id = "folio", value = -1 }) end, "Create the front cover") self:registerCommand("bookmatters:back-cover", function (options, content) - local image = SU.required(options, "image", "bookmatters:back-cover") - local src = SILE.resolveFile(image) or SU.error("Cannot find image file: " .. image) + local image = options.image local metadata = options.metadata or {} -- Unusual: table of metadata options - local backgroundColor = options.background or "white" - local textColor = contrastColor(SILE.types.color(backgroundColor)) + local backgroundColor = options.background + local backgroundContentColor = options.bgcontent or backgroundColor + local textColor = contrastColor(SILE.types.color(backgroundContentColor or "white")) SILE.call("open-on-even-page") SILE.call("switch-master-one-page", { id = "bookmatters-back-cover" }) SILE.call("hbox") -- To ensure some content - SILE.call("background", { - src = src, - allpages = false - }) + if image then + local src = SILE.resolveFile(image) or SU.error("Cannot find image file: " .. image) + SILE.call("background", { + src = src, + allpages = false + }) + backgroundContentColor = backgroundContentColor or "white" + elseif backgroundColor then + SILE.call("background", { + color = backgroundColor, + allpages = false + }) + end SILE.call("noheaders") SILE.call("nofolios") @@ -153,7 +177,7 @@ function package:registerCommands () SILE.call("skip", { height = H }) SILE.call("noindent") SILE.call("kern", { width = offset }) - SILE.call("framebox", { fillcolor = backgroundColor, padding = pad2, borderwidth = 0 }, function () + SILE.call("framebox", { fillcolor = backgroundContentColor, padding = pad2, borderwidth = 0 }, function () SILE.call("color", { color = textColor }, function () SILE.typesetter:pushHbox(pbox) end) @@ -227,6 +251,44 @@ function package:registerCommands () end function package:registerStyles () + -- Some default styles for (usually) the front cover + -- (When template-generated) + self:registerStyle("bookmatter-coverpage", {}, { + }) + self:registerStyle("bookmatter-cover-title", { inherit = "bookmatter-coverpage" }, { + font = { + size = "20pt" + }, + paragraph = { + after = { + skip = "5%fh" + }, + before = { + skip = "30%fh" + } + }, + }) + self:registerStyle("bookmatter-cover-subtitle", { inherit = "bookmatter-coverpage" }, { + font = { + size = "16pt" + }, + paragraph = { + after = { + skip = "5%fh" + } + } + }) + self:registerStyle("bookmatter-cover-author", { inherit = "bookmatter-coverpage" }, { + font = { + size = "16pt" + }, + properties = { + case = "upper" + }, + }) + self:registerStyle("bookmatter-cover-publisher", { inherit = "bookmatter-coverpage" }, { + }) + -- Some default styles for (usually) half-title page recto self:registerStyle("bookmatter-halftitle", {}, { font = { diff --git a/templates/cover.djt b/templates/cover.djt new file mode 100644 index 0000000..fd2f8e5 --- /dev/null +++ b/templates/cover.djt @@ -0,0 +1,29 @@ +``` =sile-lua +SILE.call("hbox") -- so top page glues are not suppressed +``` +{custom-style="center"} +:::: +{?author custom-style="bookmatter-cover-author"} +::: +:author: +::: + +{?title custom-style="bookmatter-cover-title"} +::: +:title: +::: + +{?subtitle custom-style="bookmatter-cover-subtitle"} +::: +:subtitle: +::: + +``` =sile-lua +SILE.call("vfill") +``` + +{?publisher custom-style="bookmatter-cover-publisher"} +::: +:publisher: +::: +::::