Generate full-screen images

This commit is contained in:
Christoph Hagen 2022-12-08 17:16:54 +01:00
parent 3bd75a63ab
commit 59667af4b0
5 changed files with 101 additions and 29 deletions

View File

@ -15,6 +15,11 @@ struct Configuration: Codable {
*/ */
let pageImageWidth: Int let pageImageWidth: Int
/**
The maximum width (in pixels) for images shown full screen.
*/
let fullScreenImageWidth: Int
/** /**
Automatically minify all `.css` and `.js` resources which are copied Automatically minify all `.css` and `.js` resources which are copied
to the output folder. to the output folder.

View File

@ -4,6 +4,8 @@ import Splash
struct PageContentGenerator { struct PageContentGenerator {
private let largeImageIndicator = "*large*"
private let factory: TemplateFactory private let factory: TemplateFactory
private let swift = SyntaxHighlighter(format: HTMLOutputFormat()) private let swift = SyntaxHighlighter(format: HTMLOutputFormat())
@ -20,9 +22,10 @@ struct PageContentGenerator {
func generate(page: Element, language: String, content: String) -> (content: String, includesCode: Bool) { func generate(page: Element, language: String, content: String) -> (content: String, includesCode: Bool) {
var hasCodeContent = false var hasCodeContent = false
var largeImageCount = 0
let imageModifier = Modifier(target: .images) { html, markdown in let imageModifier = Modifier(target: .images) { html, markdown in
processMarkdownImage(markdown: markdown, html: html, page: page, language: language) processMarkdownImage(markdown: markdown, html: html, page: page, language: language, largeImageCount: &largeImageCount)
} }
let codeModifier = Modifier(target: .codeBlocks) { html, markdown in let codeModifier = Modifier(target: .codeBlocks) { html, markdown in
if markdown.starts(with: "```swift") { if markdown.starts(with: "```swift") {
@ -74,10 +77,10 @@ struct PageContentGenerator {
return html return html
} }
private func processMarkdownImage(markdown: Substring, html: String, page: Element, language: String) -> String { private func processMarkdownImage(markdown: Substring, html: String, page: Element, language: String, largeImageCount: inout Int) -> String {
// Split the markdown ![alt](file title) // Split the markdown ![alt](file title)
// There are several known shorthand commands // There are several known shorthand commands
// For images: ![left_title](file right_title) // For images: ![*large* left_title](file right_title)
// For videos: ![option1,option2,...](file) // For videos: ![option1,option2,...](file)
// For svg with custom area: ![x,y,width,height](file.svg) // For svg with custom area: ![x,y,width,height](file.svg)
// For downloads: ![download](file1, text1; file2, text2, ...) // For downloads: ![download](file1, text1; file2, text2, ...)
@ -95,7 +98,7 @@ struct PageContentGenerator {
let fileExtension = file.lastComponentAfter(".").lowercased() let fileExtension = file.lastComponentAfter(".").lowercased()
if let _ = ImageType(fileExtension: fileExtension) { if let _ = ImageType(fileExtension: fileExtension) {
return handleImage(page: page, file: file, rightTitle: title, leftTitle: alt) return handleImage(page: page, file: file, rightTitle: title, leftTitle: alt, largeImageCount: &largeImageCount)
} }
if let _ = VideoType(rawValue: fileExtension) { if let _ = VideoType(rawValue: fileExtension) {
return handleVideo(page: page, file: file, optionString: alt) return handleVideo(page: page, file: file, optionString: alt)
@ -121,22 +124,48 @@ struct PageContentGenerator {
} }
} }
private func handleImage(page: Element, file: String, rightTitle: String?, leftTitle: String?) -> String { private func handleImage(page: Element, file: String, rightTitle: String?, leftTitle: String?, largeImageCount: inout Int) -> String {
let imagePath = page.pathRelativeToRootForContainedInputFile(file) let imagePath = page.pathRelativeToRootForContainedInputFile(file)
let left: String
let createFullScreenVersion: Bool
if let leftTitle {
createFullScreenVersion = leftTitle.hasPrefix(largeImageIndicator)
left = leftTitle.dropBeforeFirst(largeImageIndicator).trimmed
} else {
left = ""
createFullScreenVersion = false
}
let size = results.requireFullSizeMultiVersionImage( let size = results.requireFullSizeMultiVersionImage(
source: imagePath, source: imagePath,
destination: imagePath, destination: imagePath,
requiredBy: page.path) requiredBy: page.path)
let content: [PageImageTemplate.Key : String] = [ guard createFullScreenVersion else {
let content: [PageImageTemplate.Key : String] = [
.image: file.dropAfterLast("."),
.imageExtension: file.lastComponentAfter("."),
.width: "\(Int(size.width))",
.height: "\(Int(size.height))",
.leftText: left,
.rightText: rightTitle ?? ""]
return factory.image.generate(content)
}
results.requireOriginalSizeImages(
source: imagePath,
destination: imagePath,
requiredBy: page.path)
largeImageCount += 1
let content: [EnlargeableImageTemplate.Key : String] = [
.image: file.dropAfterLast("."), .image: file.dropAfterLast("."),
.imageExtension: file.lastComponentAfter("."), .imageExtension: file.lastComponentAfter("."),
.width: "\(Int(size.width))", .width: "\(Int(size.width))",
.height: "\(Int(size.height))", .height: "\(Int(size.height))",
.leftText: leftTitle ?? "", .leftText: left,
.rightText: rightTitle ?? ""] .rightText: rightTitle ?? "",
return factory.image.generate(content) .number: "\(largeImageCount)"]
return factory.largeImage.generate(content)
} }
private func handleVideo(page: Element, file: String, optionString: String?) -> String { private func handleVideo(page: Element, file: String, optionString: String?) -> String {

View File

@ -314,7 +314,18 @@ final class GenerationResultsHandler {
requireMultiVersionImage(source: source, destination: destination, requiredBy: path, width: configuration.pageImageWidth, desiredHeight: nil) requireMultiVersionImage(source: source, destination: destination, requiredBy: path, width: configuration.pageImageWidth, desiredHeight: nil)
} }
@discardableResult func requireOriginalSizeImages(
source: String,
destination: String,
requiredBy path: String) {
_ = requireScaledMultiImage(
source: source,
destination: destination.insert("@full", beforeLast: "."),
requiredBy: path,
width: configuration.fullScreenImageWidth,
desiredHeight: nil)
}
private func requireScaledMultiImage(source: String, destination: String, requiredBy path: String, width: Int, desiredHeight: Int?) -> NSSize { private func requireScaledMultiImage(source: String, destination: String, requiredBy path: String, width: Int, desiredHeight: Int?) -> NSSize {
let rawDestinationPath = destination.dropAfterLast(".") let rawDestinationPath = destination.dropAfterLast(".")
let avifPath = rawDestinationPath + ".avif" let avifPath = rawDestinationPath + ".avif"

View File

@ -0,0 +1,21 @@
import Foundation
struct EnlargeableImageTemplate: Template {
enum Key: String, CaseIterable {
case image = "IMAGE"
case imageExtension = "IMAGE_EXT"
case width = "WIDTH"
case height = "HEIGHT"
case leftText = "LEFT_TEXT"
case rightText = "RIGHT_TEXT"
case number = "NUMBER"
}
static let templateName = "image-enlargeable.html"
let raw: String
let results: GenerationResultsHandler
}

View File

@ -51,6 +51,8 @@ final class TemplateFactory {
let image: PageImageTemplate let image: PageImageTemplate
let largeImage: EnlargeableImageTemplate
let video: PageVideoTemplate let video: PageVideoTemplate
// MARK: Slideshow // MARK: Slideshow
@ -69,24 +71,28 @@ final class TemplateFactory {
init(templateFolder: URL, results: GenerationResultsHandler) throws { init(templateFolder: URL, results: GenerationResultsHandler) throws {
self.templateFolder = templateFolder self.templateFolder = templateFolder
self.backNavigation = try .init(in: templateFolder, results: results) func create<T>() throws -> T where T: Template {
self.pageHead = try .init(in: templateFolder, results: results) try .init(in: templateFolder, results: results)
self.topBar = try .init(in: templateFolder, results: results) }
self.overviewSection = try .init(in: templateFolder, results: results) self.backNavigation = try create()
self.overviewSectionClean = try .init(in: templateFolder, results: results) self.pageHead = try create()
self.box = try .init(in: templateFolder, results: results) self.topBar = try create()
self.pageLink = try .init(in: templateFolder, results: results) self.overviewSection = try create()
self.largeThumbnail = try .init(in: templateFolder, results: results) self.overviewSectionClean = try create()
self.squareThumbnail = try .init(in: templateFolder, results: results) self.box = try create()
self.smallThumbnail = try .init(in: templateFolder, results: results) self.pageLink = try create()
self.leftHeader = try .init(in: templateFolder, results: results) self.largeThumbnail = try create()
self.centeredHeader = try .init(in: templateFolder, results: results) self.squareThumbnail = try create()
self.page = try .init(in: templateFolder, results: results) self.smallThumbnail = try create()
self.image = try .init(in: templateFolder, results: results) self.leftHeader = try create()
self.video = try .init(in: templateFolder, results: results) self.centeredHeader = try create()
self.slideshow = try .init(in: templateFolder, results: results) self.page = try create()
self.slideshows = try .init(in: templateFolder, results: results) self.image = try create()
self.slideshowImage = try .init(in: templateFolder, results: results) self.largeImage = try create()
self.video = try create()
self.slideshow = try create()
self.slideshows = try create()
self.slideshowImage = try create()
self.html = .init() self.html = .init()
} }