Support GIFs

This commit is contained in:
Christoph Hagen
2025-01-05 21:32:25 +01:00
parent ac7fbdd638
commit c78c359819
9 changed files with 108 additions and 81 deletions

View File

@ -0,0 +1,65 @@
struct ImageCommandProcessor: CommandProcessor {
let commandType: ShorthandMarkdownKey = .image
let content: Content
let results: PageGenerationResults
let language: ContentLanguage
init(content: Content, results: PageGenerationResults, language: ContentLanguage) {
self.content = content
self.results = results
self.language = language
}
private var thumbnailWidth: Int {
content.settings.pages.contentWidth
}
private var largeImageWidth: Int {
content.settings.pages.largeImageWidth
}
/**
Format: `![image](<imageId>;<caption?>]`
*/
func process(_ arguments: [String], markdown: Substring) -> String {
guard (1...2).contains(arguments.count) else {
results.invalid(command: .image, markdown)
return ""
}
let imageId = arguments[0]
guard let image = content.image(imageId) else {
results.missing(file: imageId, source: "Image command")
return ""
}
results.used(file: image)
if image.type == .svg || image.type == .gif {
return simple(image: image)
}
let thumbnail = image.imageSet(width: thumbnailWidth, height: thumbnailWidth, language: language)
results.require(imageSet: thumbnail)
let largeImage = image.imageSet(width: largeImageWidth, height: largeImageWidth, language: language)
results.require(imageSet: largeImage)
let caption = arguments.count == 2 ? arguments[1] : nil
return PageImage(
imageId: imageId.replacingOccurrences(of: ".", with: "-"),
thumbnail: thumbnail,
largeImage: largeImage,
caption: caption).content
}
private func simple(image: FileResource) -> String {
results.require(file: image)
let path = image.absoluteUrl
let altText = image.localized(in: language)
return SimpleImage(imagePath: path, altText: altText).content
}
}

View File

@ -27,20 +27,14 @@ final class PageContentParser {
private let imageCompare: ImageCompareCommandProcessor
private let images: ImageCommandProcessor
// MARK: Other handlers
private let inlineLink: InlineLinkProcessor
private let code: PageCodeProcessor
var largeImageWidth: Int {
content.settings.pages.largeImageWidth
}
var thumbnailWidth: Int {
content.settings.pages.contentWidth
}
init(content: Content, language: ContentLanguage, results: PageGenerationResults) {
self.content = content
self.results = results
@ -53,6 +47,7 @@ final class PageContentParser {
self.html = .init(content: content, results: results, language: language)
self.video = .init(content: content, results: results, language: language)
self.imageCompare = .init(content: content, results: results, language: language)
self.images = .init(content: content, results: results, language: language)
self.inlineLink = .init(content: content, results: results, language: language)
self.code = .init(results: results)
@ -105,7 +100,7 @@ final class PageContentParser {
let rawCommand = percentDecoded(markdown.between("![", and: "]").trimmed)
guard rawCommand != "" else {
return handleImage(arguments, markdown: markdown)
return images.process(arguments, markdown: markdown)
}
guard let command = ShorthandMarkdownKey(rawValue: rawCommand) else {
@ -116,7 +111,7 @@ final class PageContentParser {
switch command {
case .image:
return handleImage(arguments, markdown: markdown)
return images.process(arguments, markdown: markdown)
case .labels:
return labelHandler.process(arguments, markdown: markdown)
case .buttons:
@ -144,42 +139,6 @@ final class PageContentParser {
}
}
/**
Format: `![image](<imageId>;<caption?>]`
*/
private func handleImage(_ arguments: [String], markdown: Substring) -> String {
guard (1...2).contains(arguments.count) else {
results.invalid(command: .image, markdown)
return ""
}
let imageId = arguments[0]
guard let image = content.image(imageId) else {
results.missing(file: imageId, source: "Image command")
return ""
}
results.used(file: image)
guard !image.type.isSvg else {
let path = image.absoluteUrl
let altText = image.localized(in: language)
return SvgImage(imagePath: path, altText: altText).content
}
let thumbnail = image.imageSet(width: thumbnailWidth, height: thumbnailWidth, language: language)
results.require(imageSet: thumbnail)
let largeImage = image.imageSet(width: largeImageWidth, height: largeImageWidth, language: language)
results.require(imageSet: largeImage)
let caption = arguments.count == 2 ? arguments[1] : nil
return PageImage(
imageId: imageId.replacingOccurrences(of: ".", with: "-"),
thumbnail: thumbnail,
largeImage: largeImage,
caption: caption).content
}
/**
Format: `![page](<pageId>)`
*/
@ -298,7 +257,7 @@ final class PageContentParser {
results.missing(file: imageId, source: "SVG command")
return ""
}
guard image.type.isSvg else {
guard image.type == .svg else {
results.invalid(command: .svg, markdown)
return ""
}
@ -313,17 +272,3 @@ final class PageContentParser {
.content
}
}
/*
private func handleGif(file: String, altText: String) -> String {
let imagePath = page.pathRelativeToRootForContainedInputFile(file)
results.require(file: imagePath, source: page.path)
guard let size = results.getImageSize(atPath: imagePath, source: page.path) else {
return ""
}
let width = Int(size.width)
let height = Int(size.height)
return factory.html.image(file: file, width: width, height: height, altText: altText)
}
*/

View File

@ -15,7 +15,6 @@ import SFSafeSymbols
- Images: Show list of generated versions
**Features**
- GIF Support (No image set, don't rescale)
- Files: Optional Property `customFilePath` for external files to place them in another location
- Files: Property `version` and `sourceUrl` to track asset files
- Posts: Generate separate pages for posts to link to

View File

@ -42,6 +42,7 @@ final class PageSettings: ObservableObject {
self.audioPlayerCssFile = file.audioPlayerCssFile.map { files[$0] }
self.modelViewerJsFile = file.modelViewerJsFile.map { files[$0] }
self.imageCompareCssFile = file.imageCompareCssFile.map { files[$0] }
self.imageCompareJsFile = file.imageCompareJsFile.map { files[$0] }
}
var file: PageSettingsFile {

View File

@ -26,16 +26,3 @@ struct PartialSvgImage: HtmlProducer {
result += "</span>"
}
}
struct SvgImage: HtmlProducer {
let imagePath: String
let altText: String
func populate(_ result: inout String) {
result += "<div class='content-image svg-image'>"
result += "<img src='\(imagePath)' loading='lazy' alt='\(altText)'/>"
result += "</div>"
}
}

View File

@ -0,0 +1,14 @@
struct SimpleImage: HtmlProducer {
let imagePath: String
let altText: String
func populate(_ result: inout String) {
result += "<div class='content-image svg-image'>"
result += "<img src='\(imagePath)' loading='lazy' alt='\(altText)'/>"
result += "</div>"
}
}