Generate full-screen images
This commit is contained in:
parent
3bd75a63ab
commit
59667af4b0
@ -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.
|
||||||
|
@ -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,24 +124,50 @@ 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)
|
||||||
|
|
||||||
|
guard createFullScreenVersion else {
|
||||||
let content: [PageImageTemplate.Key : String] = [
|
let content: [PageImageTemplate.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)
|
return factory.image.generate(content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
results.requireOriginalSizeImages(
|
||||||
|
source: imagePath,
|
||||||
|
destination: imagePath,
|
||||||
|
requiredBy: page.path)
|
||||||
|
|
||||||
|
largeImageCount += 1
|
||||||
|
let content: [EnlargeableImageTemplate.Key : String] = [
|
||||||
|
.image: file.dropAfterLast("."),
|
||||||
|
.imageExtension: file.lastComponentAfter("."),
|
||||||
|
.width: "\(Int(size.width))",
|
||||||
|
.height: "\(Int(size.height))",
|
||||||
|
.leftText: left,
|
||||||
|
.rightText: rightTitle ?? "",
|
||||||
|
.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 {
|
||||||
let options: [PageVideoTemplate.VideoOption] = optionString.unwrapped { string in
|
let options: [PageVideoTemplate.VideoOption] = optionString.unwrapped { string in
|
||||||
string.components(separatedBy: " ").compactMap { optionText -> PageVideoTemplate.VideoOption? in
|
string.components(separatedBy: " ").compactMap { optionText -> PageVideoTemplate.VideoOption? in
|
||||||
|
@ -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"
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
}
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user