diff --git a/WebsiteGenerator/Content/Element.swift b/WebsiteGenerator/Content/Element.swift index ebfd212..03ac19a 100644 --- a/WebsiteGenerator/Content/Element.swift +++ b/WebsiteGenerator/Content/Element.swift @@ -78,6 +78,13 @@ struct Element { */ let requiredFiles: Set + /** + Additional images required by the element. + + These images are specified as: `source_name destination_name width (height)`. + */ + let images: [ManualImage] + /** The style of thumbnail to use when generating overviews. @@ -160,6 +167,7 @@ struct Element { self.sortIndex = log.unused(metadata.sortIndex, "sortIndex", source: source) self.externalFiles = metadata.externalFiles ?? [] self.requiredFiles = metadata.requiredFiles ?? [] // Paths are already relative to root + self.images = metadata.images?.compactMap { ManualImage(input: $0, path: "") } ?? [] self.thumbnailStyle = log.unused(metadata.thumbnailStyle, "thumbnailStyle", source: source) ?? .large self.useManualSorting = log.unused(metadata.useManualSorting, "useManualSorting", source: source) ?? true self.overviewItemCount = metadata.overviewItemCount ?? Element.overviewItemCountDefault @@ -228,6 +236,7 @@ struct Element { // TODO: Propagate external files from the parent if subpath matches? self.externalFiles = Element.rootPaths(for: metadata.externalFiles, path: path) self.requiredFiles = Element.rootPaths(for: metadata.requiredFiles, path: path) + self.images = metadata.images?.compactMap { ManualImage(input: $0, path: path) } ?? [] self.thumbnailStyle = log.thumbnailStyle(metadata.thumbnailStyle, source: source) self.useManualSorting = metadata.useManualSorting ?? false self.overviewItemCount = metadata.overviewItemCount ?? parent.overviewItemCount @@ -365,12 +374,20 @@ extension Element { } static func containedFileRelativeToRoot(filePath: String, folder path: String) -> String? { + if path == "" { + return filePath + } if filePath.hasPrefix("/") || filePath.hasPrefix("http") || filePath.hasPrefix("mailto:") { return nil } return "\(path)/\(filePath)" } + /** + Convert a set of relative paths to paths that are relative to the root element. + - Parameter input: The set of paths to convert. + - Parameter path: The path to the folder where the paths are currently relative to. + */ static func rootPaths(for input: Set?, path: String) -> Set { guard let input = input else { return [] @@ -536,3 +553,44 @@ extension Element { elements.forEach { $0.printTree(indentation: indentation + " ") } } } + +// MARK: Images + +extension Element { + + struct ManualImage { + + let sourcePath: String + + let destinationPath: String + + let desiredWidth: Int + + let desiredHeight: Int? + + init?(input: String, path: String) { + let parts = input.components(separatedBy: " ").filter { !$0.isEmpty } + guard parts.count == 3 || parts.count == 4 else { + log.add(error: "Invalid image specification, expected 'source dest width (height)", source: path) + return nil + } + guard let width = Int(parts[2]) else { + log.add(error: "Invalid width for image \(parts[0])", source: path) + return nil + } + + self.sourcePath = Element.relativeToRoot(filePath: parts[0], folder: path) + self.destinationPath = Element.relativeToRoot(filePath: parts[1], folder: path) + self.desiredWidth = width + guard parts.count == 4 else { + self.desiredHeight = nil + return + } + guard let height = Int(parts[3]) else { + log.add(error: "Invalid height for image \(parts[0])", source: path) + return nil + } + self.desiredHeight = height + } + } +} diff --git a/WebsiteGenerator/Content/GenericMetadata.swift b/WebsiteGenerator/Content/GenericMetadata.swift index c548f61..be7d4bf 100644 --- a/WebsiteGenerator/Content/GenericMetadata.swift +++ b/WebsiteGenerator/Content/GenericMetadata.swift @@ -77,6 +77,13 @@ struct GenericMetadata { */ let requiredFiles: Set? + /** + Additional images required by the element. + + These images are specified as: `source_name destination_name width (height)`. + */ + let images: Set? + /** The style of thumbnail to use when generating overviews. @@ -128,6 +135,7 @@ extension GenericMetadata: Codable { .sortIndex, .externalFiles, .requiredFiles, + .images, .thumbnailStyle, .useManualSorting, .overviewItemCount, @@ -198,6 +206,7 @@ extension GenericMetadata { sortIndex: 1, externalFiles: [], requiredFiles: [], + images: [], thumbnailStyle: "", useManualSorting: false, overviewItemCount: 6, diff --git a/WebsiteGenerator/Generators/SiteGenerator.swift b/WebsiteGenerator/Generators/SiteGenerator.swift index c25b2f0..1acd7ae 100644 --- a/WebsiteGenerator/Generators/SiteGenerator.swift +++ b/WebsiteGenerator/Generators/SiteGenerator.swift @@ -10,8 +10,6 @@ struct SiteGenerator { } func generate(site: Element) { - site.requiredFiles.forEach(files.require) - site.externalFiles.forEach(files.exclude) site.languages.forEach { generate(site: site, metadata: $0) } @@ -33,8 +31,7 @@ struct SiteGenerator { // Move recursively down to all pages elementsToProcess.append(contentsOf: element.elements) - element.requiredFiles.forEach(files.require) - element.externalFiles.forEach(files.exclude) + processAllFiles(for: element) if !element.elements.isEmpty { overviewGenerator.generate(section: element, language: language) @@ -48,4 +45,16 @@ struct SiteGenerator { } } } + + private func processAllFiles(for element: Element) { + element.requiredFiles.forEach(files.require) + element.externalFiles.forEach(files.exclude) + element.images.forEach { + files.requireImage( + source: $0.sourcePath, + destination: $0.destinationPath, + width: $0.desiredWidth, + desiredHeight: $0.desiredHeight) + } + } }