Improve printing and image creation

This commit is contained in:
Christoph Hagen
2022-12-04 19:15:22 +01:00
parent 6a52f62402
commit 956cfb52c4
23 changed files with 1421 additions and 1077 deletions

View File

@ -4,15 +4,17 @@ struct OverviewPageGenerator {
private let factory: LocalizedSiteTemplate
init(factory: LocalizedSiteTemplate) {
private let results: GenerationResultsHandler
init(factory: LocalizedSiteTemplate, results: GenerationResultsHandler) {
self.factory = factory
self.results = results
}
func generate(
section: Element,
language: String) {
let path = section.localizedPath(for: language)
let url = files.urlInOutputFolder(path)
let metadata = section.localized(for: language)
@ -26,11 +28,8 @@ struct OverviewPageGenerator {
content[.contentClass] = "overview"
content[.header] = makeHeader(page: section, metadata: metadata, language: language)
content[.content] = makeContent(section: section, language: language)
content[.footer] = section.customFooterContent()
guard factory.page.generate(content, to: url) else {
return
}
files.generated(page: path)
content[.footer] = results.getContentOfOptionalFile(at: section.additionalFooterContentPath, source: section.path)
factory.page.generate(content, to: path, source: section.path)
}
private func makeContent(section: Element, language: String) -> String {

View File

@ -8,10 +8,10 @@ struct OverviewSectionGenerator {
private let generator: ThumbnailListGenerator
init(factory: TemplateFactory) {
init(factory: TemplateFactory, results: GenerationResultsHandler) {
self.multipleSectionsTemplate = factory.overviewSection
self.singleSectionsTemplate = factory.overviewSectionClean
self.generator = ThumbnailListGenerator(factory: factory)
self.generator = ThumbnailListGenerator(factory: factory, results: results)
}
func generate(sections: [Element], in parent: Element, language: String, sectionItemCount: Int) -> String {
@ -25,8 +25,13 @@ struct OverviewSectionGenerator {
}
private func newsSectionContent(for element: Element, language: String, sectionItemCount: Int) -> String {
let shownElements = element.mostRecentElements(sectionItemCount)
// let shownElements = element.mostRecentElements(sectionItemCount)
return ""
// return generator.generateContent(
// items: shownElements,
// parent: element,
// language: language,
// style: element.thumbnailStyle)
}
private func sectionsContent(_ sections: [Element], in parent: Element, language: String, sectionItemCount: Int) -> String {

View File

@ -10,9 +10,12 @@ struct PageContentGenerator {
private let siteRoot: Element
init(factory: TemplateFactory, siteRoot: Element) {
private let results: GenerationResultsHandler
init(factory: TemplateFactory, siteRoot: Element, results: GenerationResultsHandler) {
self.factory = factory
self.siteRoot = siteRoot
self.results = results
}
func generate(page: Element, language: String, content: String) -> (content: String, includesCode: Bool) {
@ -44,8 +47,7 @@ struct PageContentGenerator {
let file = markdown.between("(", and: ")")
if file.hasPrefix("page:") {
let pageId = file.replacingOccurrences(of: "page:", with: "")
guard let pagePath = files.getPage(for: pageId) else {
log.add(warning: "Page id '\(pageId)' not found", source: page.path)
guard let pagePath = results.getPagePath(for: pageId, source: page.path) else {
// Remove link since the page can't be found
return markdown.between("[", and: "]")
}
@ -57,13 +59,13 @@ struct PageContentGenerator {
if let filePath = page.nonAbsolutePathRelativeToRootForContainedInputFile(file) {
// The target of the page link must be present after generation is complete
files.expect(file: filePath, source: page.path)
results.expect(file: filePath, source: page.path)
}
return html
}
private func handleHTML(page: Element, language: String, html: String, markdown: Substring) -> String {
#warning("Check HTML code in markdown for required resources")
// TODO: Check HTML code in markdown for required resources
//print("[HTML] Found in page \(page.path):")
//print(markdown)
// Things to check:
@ -123,7 +125,7 @@ struct PageContentGenerator {
private func handleImage(page: Element, file: String, rightTitle: String?, leftTitle: String?) -> String {
let imagePath = page.pathRelativeToRootForContainedInputFile(file)
let size = files.requireFullSizeMultiVersionImage(
let size = results.requireFullSizeMultiVersionImage(
source: imagePath,
destination: imagePath,
requiredBy: page.path)
@ -140,28 +142,28 @@ struct PageContentGenerator {
private func handleVideo(page: Element, file: String, optionString: String?) -> String {
let options: [PageVideoTemplate.VideoOption] = optionString.unwrapped { string in
string.components(separatedBy: " ").compactMap { optionText in
string.components(separatedBy: " ").compactMap { optionText -> PageVideoTemplate.VideoOption? in
guard let optionText = optionText.trimmed.nonEmpty else {
return nil
}
guard let option = PageVideoTemplate.VideoOption(rawValue: optionText) else {
log.add(warning: "Unknown video option \(optionText)", source: page.path)
results.warning("Unknown video option \(optionText)", source: page.path)
return nil
}
return option
}
} ?? []
#warning("Check page folder for alternative video versions")
// TODO: Check page folder for alternative video versions
let sources: [PageVideoTemplate.VideoSource] = [(url: file, type: .mp4)]
let filePath = page.pathRelativeToRootForContainedInputFile(file)
files.require(file: filePath)
results.require(file: filePath, source: page.path)
return factory.video.generate(sources: sources, options: options)
}
private func handleSvg(page: Element, file: String, area: String?) -> String {
let imagePath = page.pathRelativeToRootForContainedInputFile(file)
files.require(file: imagePath)
results.require(file: imagePath, source: page.path)
guard let area = area else {
return factory.html.svgImage(file: file)
@ -172,7 +174,7 @@ struct PageContentGenerator {
let y = Int(parts[1].trimmed),
let width = Int(parts[2].trimmed),
let height = Int(parts[3].trimmed) else {
log.add(warning: "Invalid area string for svg image", source: page.path)
results.warning("Invalid area string for svg image", source: page.path)
return factory.html.svgImage(file: file)
}
@ -180,7 +182,7 @@ struct PageContentGenerator {
}
private func handleFile(page: Element, file: String, fileExtension: String) -> String {
log.add(warning: "Unhandled file \(file) with extension \(fileExtension)", source: page.path)
results.warning("Unhandled file \(file) with extension \(fileExtension)", source: page.path)
return ""
}
@ -190,7 +192,7 @@ struct PageContentGenerator {
.compactMap { button -> (file: String, text: String, downloadName: String?)? in
let parts = button.components(separatedBy: ",")
guard parts.count == 2 || parts.count == 3 else {
log.add(warning: "Invalid button definition", source: page.path)
results.warning("Invalid button definition", source: page.path)
return nil
}
let file = parts[0].trimmed
@ -199,7 +201,7 @@ struct PageContentGenerator {
// Ensure that file is available
let filePath = page.pathRelativeToRootForContainedInputFile(file)
files.require(file: filePath)
results.require(file: filePath, source: page.path)
return (file, title, downloadName)
}
@ -212,7 +214,7 @@ struct PageContentGenerator {
.compactMap { button -> (url: String, text: String)? in
let parts = button.components(separatedBy: ",")
guard parts.count == 2 else {
log.add(warning: "Invalid external link definition", source: page.path)
results.warning("Invalid external link definition", source: page.path)
return nil
}
let url = parts[0].trimmed
@ -224,23 +226,14 @@ struct PageContentGenerator {
}
private func handleExternalHTML(page: Element, file: String) -> String {
let url = page.inputFolder.appendingPathComponent(file)
guard url.exists else {
log.add(error: "File \(file) not found", source: page.path)
return ""
}
do {
return try String(contentsOf: url)
} catch {
log.add(error: "File \(file) could not be read", source: page.path, error: error)
return ""
}
let path = page.pathRelativeToRootForContainedInputFile(file)
return results.getContentOfRequiredFile(at: path, source: page.path) ?? ""
}
private func handleSimpleBox(page: Element, content: String) -> String {
let parts = content.components(separatedBy: ";")
guard parts.count > 1 else {
log.add(error: "Invalid box specification", source: page.path)
results.warning("Invalid box specification", source: page.path)
return ""
}
let title = parts[0]
@ -250,7 +243,8 @@ struct PageContentGenerator {
private func handlePageLink(page: Element, language: String, pageId: String) -> String {
guard let linkedPage = siteRoot.find(pageId) else {
log.add(warning: "Page id '\(pageId)' not found", source: page.path)
// Checking the page path will add it to the missing pages
_ = results.getPagePath(for: pageId, source: page.path)
// Remove link since the page can't be found
return ""
}
@ -259,6 +253,7 @@ struct PageContentGenerator {
content[.title] = linkedPage.title(for: language)
let fullThumbnailPath = linkedPage.thumbnailFilePath(for: language).destination
// Note: Here we assume that the thumbnail was already used elsewhere, so already generated
let relativeImageUrl = page.relativePathToOtherSiteElement(file: fullThumbnailPath)
let metadata = linkedPage.localized(for: language)

View File

@ -7,20 +7,24 @@ struct PageGenerator {
private let contentGenerator: PageContentGenerator
init(factory: LocalizedSiteTemplate, siteRoot: Element) {
private let results: GenerationResultsHandler
init(factory: LocalizedSiteTemplate, siteRoot: Element, results: GenerationResultsHandler) {
self.factory = factory
self.contentGenerator = PageContentGenerator(factory: factory.factory, siteRoot: siteRoot)
self.results = results
self.contentGenerator = PageContentGenerator(factory: factory.factory, siteRoot: siteRoot, results: results)
}
func generate(page: Element, language: String, previousPage: Element?, nextPage: Element?) {
guard !page.isExternalPage else {
results.didCompletePage()
return
}
let path = page.fullPageUrl(for: language)
let inputContentPath = page.path + "/\(language).md"
let metadata = page.localized(for: language)
let nextLanguage = page.nextLanguage(for: language)
let (pageContent, pageIncludesCode, pageIsEmpty) = makeContent(
let (pageContent, pageIncludesCode) = makeContent(
page: page, metadata: metadata, language: language, path: inputContentPath)
var content = [PageTemplate.Key : String]()
@ -35,23 +39,17 @@ struct PageGenerator {
content[.previousPageUrl] = navLink(from: page, to: previousPage, language: language)
content[.nextPageLinkText] = nextText(for: nextPage, language: language)
content[.nextPageUrl] = navLink(from: page, to: nextPage, language: language)
content[.footer] = page.customFooterContent()
content[.footer] = results.getContentOfOptionalFile(at: page.additionalFooterContentPath, source: page.path)
if pageIncludesCode {
let highlightCode = factory.factory.html.codeHighlightFooter()
content[.footer] = (content[.footer].unwrapped { $0 + "\n" } ?? "") + highlightCode
}
let url = files.urlInOutputFolder(path)
if page.state == .draft {
files.isDraft(path: page.path)
} else if pageIsEmpty, page.state != .hidden {
files.isEmpty(page: path)
results.markPageAsDraft(page: page.path)
}
guard factory.page.generate(content, to: url) else {
return
}
files.generated(page: path)
factory.page.generate(content, to: path, source: page.path)
}
private func navLink(from element: Element, to destination: Element?, language: String) -> String? {
@ -75,14 +73,14 @@ struct PageGenerator {
return factory.factory.html.makeNextText(text)
}
private func makeContent(page: Element, metadata: Element.LocalizedMetadata, language: String, path: String) -> (content: String, includesCode: Bool, isEmpty: Bool) {
if let raw = files.contentOfMdFile(atPath: path, source: page.path)?.trimmed.nonEmpty {
private func makeContent(page: Element, metadata: Element.LocalizedMetadata, language: String, path: String) -> (content: String, includesCode: Bool) {
if let raw = results.getContentOfMdFile(at: path, source: page.path)?.trimmed.nonEmpty {
let (content, includesCode) = contentGenerator.generate(page: page, language: language, content: raw)
return (content, includesCode, false)
return (content, includesCode)
} else {
let (content, includesCode) = contentGenerator.generate(page: page, language: language, content: metadata.placeholderText)
let placeholder = factory.factory.makePlaceholder(title: metadata.placeholderTitle, text: content)
return (placeholder, includesCode, true)
return (placeholder, includesCode)
}
}

View File

@ -2,12 +2,16 @@ import Foundation
struct PageHeadGenerator {
// TODO: Add to configuration
static let linkPreviewDesiredImageWidth = 1600
let factory: TemplateFactory
init(factory: TemplateFactory) {
private let results: GenerationResultsHandler
init(factory: TemplateFactory, results: GenerationResultsHandler) {
self.factory = factory
self.results = results
}
func generate(page: Element, language: String, includesCode: Bool = false) -> String {
@ -24,14 +28,14 @@ struct PageHeadGenerator {
let linkPreviewImageName = "thumbnail-link.\(image.lastComponentAfter("."))"
let sourceImagePath = page.pathRelativeToRootForContainedInputFile(image)
let destinationImagePath = page.pathRelativeToRootForContainedInputFile(linkPreviewImageName)
files.requireSingleImage(
results.requireSingleImage(
source: sourceImagePath,
destination: destinationImagePath,
requiredBy: page.path,
width: PageHeadGenerator.linkPreviewDesiredImageWidth)
content[.image] = factory.html.linkPreviewImage(file: linkPreviewImageName)
}
content[.customPageContent] = page.customHeadContent()
content[.customPageContent] = results.getContentOfOptionalFile(at: page.additionalHeadContentPath, source: page.path)
if includesCode {
let scriptPath = "assets/js/highlight.js"
let relative = page.relativePathToOtherSiteElement(file: scriptPath)

View File

@ -6,9 +6,12 @@ struct SiteGenerator {
let templates: TemplateFactory
init() throws {
let templatesFolder = files.urlInContentFolder("templates")
self.templates = try TemplateFactory(templateFolder: templatesFolder)
let results: GenerationResultsHandler
init(results: GenerationResultsHandler) throws {
self.results = results
let templatesFolder = results.contentFolder.appendingPathComponent("templates")
self.templates = try TemplateFactory(templateFolder: templatesFolder, results: results)
}
func generate(site: Element) {
@ -22,11 +25,12 @@ struct SiteGenerator {
let template = LocalizedSiteTemplate(
factory: templates,
language: language,
site: site)
site: site,
results: results)
// Generate sections
let overviewGenerator = OverviewPageGenerator(factory: template)
let pageGenerator = PageGenerator(factory: template, siteRoot: site)
let overviewGenerator = OverviewPageGenerator(factory: template, results: results)
let pageGenerator = PageGenerator(factory: template, siteRoot: site, results: results)
var elementsToProcess: [LinkedElement] = [(nil, site, nil)]
while let (previous, element, next) = elementsToProcess.popLast() {
@ -34,7 +38,6 @@ struct SiteGenerator {
elementsToProcess.append(contentsOf: element.linkedElements)
processAllFiles(for: element)
if !element.elements.isEmpty {
overviewGenerator.generate(section: element, language: language)
} else {
@ -48,10 +51,10 @@ struct SiteGenerator {
}
private func processAllFiles(for element: Element) {
element.requiredFiles.forEach(files.require)
element.externalFiles.forEach(files.exclude)
element.externalFiles.forEach { results.exclude(file: $0, source: element.path) }
element.requiredFiles.forEach { results.require(file: $0, source: element.path) }
element.images.forEach {
files.requireSingleImage(
results.requireSingleImage(
source: $0.sourcePath,
destination: $0.destinationPath,
requiredBy: element.path,

View File

@ -4,8 +4,11 @@ struct ThumbnailListGenerator {
private let factory: TemplateFactory
init(factory: TemplateFactory) {
private let results: GenerationResultsHandler
init(factory: TemplateFactory, results: GenerationResultsHandler) {
self.factory = factory
self.results = results
}
func generateContent(items: [Element], parent: Element, language: String, style: ThumbnailStyle) -> String {
@ -37,7 +40,7 @@ struct ThumbnailListGenerator {
factory.largeThumbnail.makeCorner(text: $0)
}
files.requireMultiVersionImage(
results.requireMultiVersionImage(
source: thumbnailSourcePath,
destination: thumbnailDestNoExtension + ".jpg",
requiredBy: item.path,