First version
This commit is contained in:
60
WebsiteGenerator/Generators/IndexPageGenerator.swift
Normal file
60
WebsiteGenerator/Generators/IndexPageGenerator.swift
Normal file
@@ -0,0 +1,60 @@
|
||||
import Foundation
|
||||
|
||||
private struct AboveRootDummy: SiteElement {
|
||||
var sortIndex: Int? { nil }
|
||||
|
||||
var sortDate: Date? { nil }
|
||||
|
||||
var path: String { "" }
|
||||
|
||||
let inputFolder: URL
|
||||
|
||||
func title(for language: String) -> String { "" }
|
||||
|
||||
func cornerText(for language: String) -> String? { nil }
|
||||
|
||||
var elements: [SiteElement] { [] }
|
||||
}
|
||||
|
||||
struct IndexPageGenerator {
|
||||
|
||||
private let factory: LocalizedSiteTemplate
|
||||
|
||||
init(factory: LocalizedSiteTemplate, imageProcessor: ImageProcessor) {
|
||||
self.factory = factory
|
||||
}
|
||||
|
||||
func generate(
|
||||
site: Site,
|
||||
language: String,
|
||||
languageButton: String?,
|
||||
sectionItemCount: Int,
|
||||
to url: URL) throws {
|
||||
let localized = site.localized(for: language)
|
||||
|
||||
var content = [OverviewPageTemplate.Key : String]()
|
||||
content[.head] = try makeHead(site: site, language: language)
|
||||
content[.topBar] = factory.topBar.generate(section: nil, languageButton: languageButton)
|
||||
content[.title] = localized.title
|
||||
content[.subtitle] = localized.subtitle
|
||||
content[.titleText] = localized.description
|
||||
let sections = site.elements.compactMap { $0 as? Section }
|
||||
content[.sections] = try factory.overviewSection.generate(
|
||||
sections: sections,
|
||||
in: site,
|
||||
language: language,
|
||||
sectionItemCount: sectionItemCount)
|
||||
content[.footer] = SiteGenerator.pageFooter
|
||||
try factory.overviewPage.generate(content, to: url)
|
||||
}
|
||||
|
||||
private func makeHead(site: Site, language: String) throws -> String {
|
||||
let localized = site.localized(for: language)
|
||||
return try factory.pageHead.generate(page: PageHeadInfo(
|
||||
author: site.metadata.author,
|
||||
linkPreviewTitle: localized.linkPreviewTitle,
|
||||
linkPreviewDescription: localized.linkPreviewDescription,
|
||||
linkPreviewImage: site.linkPreviewImage(for: language),
|
||||
customHeadContent: try site.customHeadContent()))
|
||||
}
|
||||
}
|
130
WebsiteGenerator/Generators/MarkdownProcessor.swift
Normal file
130
WebsiteGenerator/Generators/MarkdownProcessor.swift
Normal file
@@ -0,0 +1,130 @@
|
||||
import Foundation
|
||||
import Ink
|
||||
|
||||
struct PageContentGenerator {
|
||||
|
||||
private let imageProcessor: ImageProcessor
|
||||
|
||||
init(imageProcessor: ImageProcessor) {
|
||||
self.imageProcessor = imageProcessor
|
||||
}
|
||||
|
||||
func generate(page: Page, language: String, at url: URL) throws -> String {
|
||||
var errorToThrow: Error? = nil
|
||||
|
||||
let content = try wrap(.missingPage(page: url.path, language: language)) {
|
||||
try String(contentsOf: url)
|
||||
}
|
||||
|
||||
var hasCodeContent = false
|
||||
|
||||
let imageModifier = Modifier(target: .images) { html, markdown in
|
||||
let result = processMarkdownImage(markdown: markdown, html: html, page: page)
|
||||
switch result {
|
||||
case .success(let content):
|
||||
return content
|
||||
case .failure(let error):
|
||||
errorToThrow = error
|
||||
return ""
|
||||
}
|
||||
}
|
||||
let codeModifier = Modifier(target: .codeBlocks) { html, markdown in
|
||||
if markdown.starts(with: "```swift") {
|
||||
#warning("Syntax highlight swift code")
|
||||
return html
|
||||
}
|
||||
hasCodeContent = true
|
||||
return html
|
||||
}
|
||||
let parser = MarkdownParser(modifiers: [imageModifier, codeModifier])
|
||||
|
||||
#warning("Check links in markdown for (missing) files to copy")
|
||||
if hasCodeContent {
|
||||
#warning("Automatically add hljs hightlighting if code samples are found")
|
||||
}
|
||||
|
||||
let result = parser.html(from: content)
|
||||
if let error = errorToThrow {
|
||||
throw error
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private func processMarkdownImage(markdown: Substring, html: String, page: Page) -> Result<String, Error> {
|
||||
let fileAndTitle = markdown
|
||||
.components(separatedBy: "(").last!
|
||||
.components(separatedBy: ")").first!
|
||||
|
||||
let file = fileAndTitle.components(separatedBy: " \"").first! // Remove title
|
||||
let rightSubtitle: String?
|
||||
if fileAndTitle.contains(" \"") {
|
||||
rightSubtitle = fileAndTitle.dropBeforeFirst("\"").dropAfterLast("\"")
|
||||
} else {
|
||||
rightSubtitle = nil
|
||||
}
|
||||
let leftSubtitle = markdown
|
||||
.components(separatedBy: "]").first!
|
||||
.components(separatedBy: "[").last!.nonEmpty
|
||||
|
||||
#warning("Specify page image width in configuration")
|
||||
let pageImageWidth = 748
|
||||
let size: NSSize
|
||||
let imagePath = page.pathRelativeToRootForContainedInputFile(file)
|
||||
do {
|
||||
size = try imageProcessor.requireImage(
|
||||
source: imagePath,
|
||||
destination: imagePath,
|
||||
width: pageImageWidth,
|
||||
desiredHeight: nil,
|
||||
createDoubleVersion: true)
|
||||
} catch {
|
||||
return .failure(error)
|
||||
}
|
||||
let file2x = file.insert("@2x", beforeLast: ".")
|
||||
#warning("Move HTML code to single location")
|
||||
let result = articelImage(
|
||||
image: file,
|
||||
image2x: file2x,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
rightSubtitle: rightSubtitle,
|
||||
leftSubtitle: leftSubtitle)
|
||||
return .success(result)
|
||||
}
|
||||
|
||||
private func articelImage(image: String, image2x: String, width: CGFloat, height: CGFloat, rightSubtitle: String?, leftSubtitle: String?) -> String {
|
||||
let subtitleCode = subtitle(left: leftSubtitle, right: rightSubtitle)
|
||||
return fullImageCode(image: image, image2x: image2x, width: width, height: height, subtitle: subtitleCode)
|
||||
}
|
||||
|
||||
private func articleImageWithoutSubtitle(image: String, image2x: String, width: CGFloat, height: CGFloat) -> String {
|
||||
"""
|
||||
<span class="image">
|
||||
<img src="\(image)" srcset="\(image2x) 2x" width="\(Int(width))" height="\(Int(height))" loading="lazy"/>
|
||||
</span>
|
||||
"""
|
||||
}
|
||||
|
||||
private func subtitle(left: String?, right: String?) -> String {
|
||||
guard left != nil || right != nil else {
|
||||
return ""
|
||||
}
|
||||
let leftCode = left.unwrapped { "<span class=\"left\">\($0)</span>" } ?? ""
|
||||
let rightCode = right.unwrapped { "<span class=\"right\">\($0)</span>" } ?? ""
|
||||
return """
|
||||
<div class="subtitle">
|
||||
\(leftCode)
|
||||
\(rightCode)
|
||||
</div>
|
||||
"""
|
||||
}
|
||||
|
||||
private func fullImageCode(image: String, image2x: String, width: CGFloat, height: CGFloat, subtitle: String) -> String {
|
||||
"""
|
||||
<span class="image">
|
||||
<img src="\(image)" srcset="\(image2x) 2x" width="\(Int(width))" height="\(Int(height))" loading="lazy"/>
|
||||
\(subtitle)
|
||||
</span>
|
||||
"""
|
||||
}
|
||||
}
|
62
WebsiteGenerator/Generators/OverviewPageGenerator.swift
Normal file
62
WebsiteGenerator/Generators/OverviewPageGenerator.swift
Normal file
@@ -0,0 +1,62 @@
|
||||
import Foundation
|
||||
|
||||
struct OverviewPageGenerator {
|
||||
|
||||
private let factory: LocalizedSiteTemplate
|
||||
|
||||
let outputFolder: URL
|
||||
|
||||
init(factory: LocalizedSiteTemplate, imageProcessor: ImageProcessor) {
|
||||
self.factory = factory
|
||||
self.outputFolder = imageProcessor.outputFolder
|
||||
}
|
||||
|
||||
func generate(
|
||||
section: Section,
|
||||
language: String,
|
||||
backText: String?) throws {
|
||||
let url = outputFolder.appendingPathComponent(section.localizedPath(for: language))
|
||||
|
||||
let metadata = section.localized(for: language)
|
||||
|
||||
var content = [OverviewPageTemplate.Key : String]()
|
||||
content[.head] = try makeHead(section: section, language: language)
|
||||
let languageButton = section.nextLanguage(for: language)
|
||||
content[.topBar] = factory.topBar.generate(
|
||||
section: section.sectionId,
|
||||
languageButton: languageButton)
|
||||
content[.sections] = try makeContent(section: section, language: language)
|
||||
content[.title] = metadata.title
|
||||
content[.subtitle] = metadata.subtitle
|
||||
content[.titleText] = metadata.description
|
||||
content[.footer] = SiteGenerator.pageFooter
|
||||
content[.backLink] = backText.unwrapped { factory.makeBackLink(text: $0, language: language) }
|
||||
try factory.overviewPage.generate(content, to: url)
|
||||
}
|
||||
|
||||
private func makeContent(section: Section, language: String) throws -> String {
|
||||
if section.hasNestingElements {
|
||||
let sections = section.elements.compactMap { $0 as? Section }
|
||||
return try factory.overviewSection.generate(
|
||||
sections: sections,
|
||||
in: section,
|
||||
language: language,
|
||||
sectionItemCount: section.metadata.sectionOverviewItemCount)
|
||||
} else {
|
||||
return try factory.overviewSection.generate(section: section, language: language)
|
||||
}
|
||||
}
|
||||
|
||||
private func makeHead(section: Section, language: String) throws -> String {
|
||||
let localized = section.localized(for: language)
|
||||
|
||||
let image = section.linkPreviewImage(for: language)
|
||||
let info = PageHeadInfo(
|
||||
author: factory.author,
|
||||
linkPreviewTitle: localized.title,
|
||||
linkPreviewDescription: localized.linkPreviewDescription,
|
||||
linkPreviewImage: image,
|
||||
customHeadContent: try section.customHeadContent())
|
||||
return try factory.pageHead.generate(page: info)
|
||||
}
|
||||
}
|
69
WebsiteGenerator/Generators/OverviewSectionGenerator.swift
Normal file
69
WebsiteGenerator/Generators/OverviewSectionGenerator.swift
Normal file
@@ -0,0 +1,69 @@
|
||||
import Foundation
|
||||
|
||||
struct OverviewSectionGenerator {
|
||||
|
||||
private let multipleSectionsTemplate: OverviewSectionTemplate
|
||||
|
||||
private let singleSectionsTemplate: OverviewSectionCleanTemplate
|
||||
|
||||
let imageProcessor: ImageProcessor
|
||||
|
||||
private let generator: ThumbnailListGenerator
|
||||
|
||||
init(factory: TemplateFactory, imageProcessor: ImageProcessor) {
|
||||
self.multipleSectionsTemplate = factory.overviewSection
|
||||
self.singleSectionsTemplate = factory.overviewSectionClean
|
||||
self.imageProcessor = imageProcessor
|
||||
self.generator = ThumbnailListGenerator(factory: factory, imageProcessor: imageProcessor)
|
||||
}
|
||||
|
||||
func generate(sections: [Section], in parent: SiteElement, language: String, sectionItemCount: Int) throws -> String {
|
||||
try sections.map { section in
|
||||
let metadata = section.localized(for: language)
|
||||
let fullUrl = section.fullPageUrl(for: language)
|
||||
let relativeUrl = parent.relativePathToFileWithPath(fullUrl)
|
||||
|
||||
var content = [OverviewSectionTemplate.Key : String]()
|
||||
content[.url] = relativeUrl
|
||||
content[.title] = metadata.title
|
||||
content[.items] = try sectionContent(section: section, in: parent, language: language, shownItemCount: sectionItemCount)
|
||||
content[.more] = metadata.moreLinkTitle
|
||||
|
||||
return multipleSectionsTemplate.generate(content)
|
||||
}
|
||||
.joined(separator: "\n")
|
||||
}
|
||||
|
||||
func generate(section: Section, language: String) throws -> String {
|
||||
var content = [OverviewSectionCleanTemplate.Key : String]()
|
||||
content[.items] = try sectionContent(section: section, in: section, language: language, shownItemCount: nil)
|
||||
return singleSectionsTemplate.generate(content)
|
||||
}
|
||||
|
||||
private func sectionContent(section: Section, in parent: SiteElement, language: String, shownItemCount: Int?) throws -> String {
|
||||
let sectionItems: [SiteElement]
|
||||
if let shownItemCount = shownItemCount {
|
||||
sectionItems = Array(section.sortedItems.prefix(shownItemCount))
|
||||
} else {
|
||||
sectionItems = section.sortedItems
|
||||
}
|
||||
|
||||
let items: [ThumbnailInfo] = sectionItems.map { item in
|
||||
#warning("Check if page exists for the language")
|
||||
let fullPageUrl = item.fullPageUrl(for: language)
|
||||
let relativePageUrl = parent.relativePathToFileWithPath(fullPageUrl)
|
||||
let fullThumbnailPath = item.thumbnailFilePath(for: language)
|
||||
let relativeImageUrl = parent.relativePathToFileWithPath(fullThumbnailPath)
|
||||
|
||||
return ThumbnailInfo(
|
||||
url: relativePageUrl,
|
||||
imageFilePath: fullThumbnailPath,
|
||||
imageHtmlUrl: relativeImageUrl,
|
||||
title: item.title(for: language),
|
||||
cornerText: item.cornerText(for: language))
|
||||
}
|
||||
return try generator.generateContent(
|
||||
items: items,
|
||||
style: section.metadata.thumbnailStyle)
|
||||
}
|
||||
}
|
71
WebsiteGenerator/Generators/PageGenerator.swift
Normal file
71
WebsiteGenerator/Generators/PageGenerator.swift
Normal file
@@ -0,0 +1,71 @@
|
||||
import Foundation
|
||||
import Ink
|
||||
|
||||
struct PageGenerator {
|
||||
|
||||
struct NavigationLink {
|
||||
|
||||
let link: String
|
||||
|
||||
let text: String
|
||||
}
|
||||
|
||||
private let factory: LocalizedSiteTemplate
|
||||
|
||||
private let imageProcessor: ImageProcessor
|
||||
|
||||
init(factory: LocalizedSiteTemplate, imageProcessor: ImageProcessor) {
|
||||
self.factory = factory
|
||||
self.imageProcessor = imageProcessor
|
||||
}
|
||||
|
||||
func generate(page: Page, language: String, backText: String, nextPage: NavigationLink?, previousPage: NavigationLink?) throws {
|
||||
guard !page.isExternalPage else {
|
||||
return
|
||||
}
|
||||
guard !page.metadata.isDraft else {
|
||||
return
|
||||
}
|
||||
let path = page.fullPageUrl(for: language)
|
||||
let inputContentUrl = page.inputFolder.appendingPathComponent("\(language).md")
|
||||
#warning("Make prev and next navigation relative")
|
||||
let metadata = page.localized(for: language)
|
||||
let nextLanguage = page.nextLanguage(for: language)
|
||||
var content = [ContentPageTemplate.Key : String]()
|
||||
content[.head] = try makeHead(page: page, language: language)
|
||||
content[.topBar] = factory.topBar.generate(section: page.sectionId, languageButton: nextLanguage)
|
||||
content[.backLink] = factory.makeBackLink(text: backText, language: language)
|
||||
content[.title] = metadata.title
|
||||
content[.subtitle] = metadata.subtitle
|
||||
content[.date] = factory.makeDateString(start: page.metadata.date, end: page.metadata.endDate)
|
||||
content[.content] = try makeContent(page: page, language: language, url: inputContentUrl)
|
||||
content[.previousPageLinkText] = previousPage.unwrapped { factory.makePrevText($0.text) }
|
||||
content[.previousPageUrl] = previousPage?.link
|
||||
content[.nextPageLinkText] = nextPage.unwrapped { factory.makeNextText($0.text) }
|
||||
content[.nextPageUrl] = nextPage?.link
|
||||
content[.footer] = try page.customFooterContent()
|
||||
|
||||
let url = imageProcessor.outputFolder.appendingPathComponent(path)
|
||||
try factory.contentPage.generate(content, to: url)
|
||||
}
|
||||
|
||||
private func makeContent(page: Page, language: String, url: URL) throws -> String {
|
||||
guard url.exists else {
|
||||
print("Generated empty page \(page.path)")
|
||||
return factory.placeholder
|
||||
}
|
||||
print("Generated page \(page.path)")
|
||||
return try PageContentGenerator(imageProcessor: imageProcessor).generate(page: page, language: language, at: url)
|
||||
}
|
||||
|
||||
private func makeHead(page: Page, language: String) throws -> String {
|
||||
let metadata = page.localized(for: language)
|
||||
let info = PageHeadInfo(
|
||||
author: page.metadata.author ?? factory.author,
|
||||
linkPreviewTitle: metadata.linkPreviewTitle,
|
||||
linkPreviewDescription: metadata.linkPreviewDescription,
|
||||
linkPreviewImage: page.linkPreviewImage(for: language),
|
||||
customHeadContent: try page.customHeadContent())
|
||||
return try factory.pageHead.generate(page: info)
|
||||
}
|
||||
}
|
61
WebsiteGenerator/Generators/PageHeadGenerator.swift
Normal file
61
WebsiteGenerator/Generators/PageHeadGenerator.swift
Normal file
@@ -0,0 +1,61 @@
|
||||
import Foundation
|
||||
|
||||
protocol PageHeadInfoProvider {
|
||||
|
||||
var author: String { get }
|
||||
|
||||
var linkPreviewTitle: String { get }
|
||||
|
||||
var linkPreviewDescription: String { get }
|
||||
|
||||
var linkPreviewImage: String? { get }
|
||||
|
||||
var customHeadContent: String? { get }
|
||||
}
|
||||
|
||||
struct PageHeadInfo: PageHeadInfoProvider {
|
||||
|
||||
let author: String
|
||||
|
||||
let linkPreviewTitle: String
|
||||
|
||||
let linkPreviewDescription: String
|
||||
|
||||
let linkPreviewImage: String?
|
||||
|
||||
let customHeadContent: String?
|
||||
}
|
||||
|
||||
struct PageHeadGenerator {
|
||||
|
||||
let template: PageHeadTemplate
|
||||
|
||||
let imageProcessor: ImageProcessor
|
||||
|
||||
init(factory: TemplateFactory, imageProcessor: ImageProcessor) {
|
||||
self.template = factory.pageHead
|
||||
self.imageProcessor = imageProcessor
|
||||
}
|
||||
|
||||
func generate(page: PageHeadInfoProvider) throws -> String {
|
||||
var content = [PageHeadTemplate.Key : String]()
|
||||
content[.author] = page.author
|
||||
content[.title] = page.linkPreviewTitle
|
||||
content[.description] = page.linkPreviewDescription
|
||||
if let image = page.linkPreviewImage {
|
||||
// Note: Generate separate destination link for the image,
|
||||
// since we don't want a single large image for thumbnails.
|
||||
// Warning: Link preview source path must be relative to root
|
||||
let linkPreviewImagePath = image.insert("-link", beforeLast: ".")
|
||||
try imageProcessor.requireImage(
|
||||
source: image,
|
||||
destination: linkPreviewImagePath,
|
||||
width: Site.linkPreviewDesiredImageWidth)
|
||||
#warning("Make link preview image path absolute")
|
||||
content[.image] = "<meta property=\"og:image\" content=\"\(linkPreviewImagePath)\" />"
|
||||
}
|
||||
content[.customPageContent] = page.customHeadContent
|
||||
|
||||
return template.generate(content)
|
||||
}
|
||||
}
|
80
WebsiteGenerator/Generators/SiteGenerator.swift
Normal file
80
WebsiteGenerator/Generators/SiteGenerator.swift
Normal file
@@ -0,0 +1,80 @@
|
||||
import Foundation
|
||||
|
||||
struct SiteGenerator {
|
||||
|
||||
let site: Site
|
||||
|
||||
let templates: TemplateFactory
|
||||
|
||||
private let imageProcessor: ImageProcessor
|
||||
|
||||
private var outputFolder: URL {
|
||||
imageProcessor.outputFolder
|
||||
}
|
||||
|
||||
init(site: Site, imageProcessor: ImageProcessor) throws {
|
||||
self.site = site
|
||||
let templatesFolder = site.inputFolder.appendingPathComponent("templates")
|
||||
self.templates = try TemplateFactory(templateFolder: templatesFolder)
|
||||
self.imageProcessor = imageProcessor
|
||||
}
|
||||
|
||||
func generate() throws {
|
||||
try site.metadata.languages.forEach { metadata in
|
||||
let language = metadata.languageIdentifier
|
||||
let template = try LocalizedSiteTemplate(
|
||||
factory: templates,
|
||||
language: language,
|
||||
site: site,
|
||||
imageProcessor: imageProcessor)
|
||||
|
||||
|
||||
// Generate sections
|
||||
let overviewGenerator = OverviewPageGenerator(factory: template, imageProcessor: imageProcessor)
|
||||
let pageGenerator = PageGenerator(factory: template, imageProcessor: imageProcessor)
|
||||
let backLinkText = try site.backLinkText(for: language)
|
||||
var elementsToProcess: [(element: SiteElement, backText: String?)] = site.elements.map { ($0, backLinkText) }
|
||||
while let (element, backText) = elementsToProcess.popLast() {
|
||||
if let section = element as? Section {
|
||||
try overviewGenerator.generate(
|
||||
section: section,
|
||||
language: language,
|
||||
backText: backText)
|
||||
let elementBackText = try element.backLinkText(for: language)
|
||||
let nestedElements = section.elements.map { ($0, elementBackText) }
|
||||
elementsToProcess.append(contentsOf: nestedElements)
|
||||
}
|
||||
if let page = element as? Page {
|
||||
#warning("Determine previous and next pages")
|
||||
try pageGenerator.generate(
|
||||
page: page,
|
||||
language: language,
|
||||
backText: backText ?? metadata.defaultBackLinkText,
|
||||
nextPage: nil,
|
||||
previousPage: nil)
|
||||
}
|
||||
}
|
||||
|
||||
let generator = IndexPageGenerator(
|
||||
factory: template,
|
||||
imageProcessor: imageProcessor)
|
||||
|
||||
// Generate front page
|
||||
let relativeUrl = site.localizedPath(for: language)
|
||||
let indexPageUrl = outputFolder.appendingPathComponent(relativeUrl)
|
||||
let button = site.nextLanguage(for: language)
|
||||
try generator.generate(
|
||||
site: site,
|
||||
language: language,
|
||||
languageButton: button,
|
||||
sectionItemCount: 6,
|
||||
to: indexPageUrl)
|
||||
}
|
||||
}
|
||||
|
||||
static let pageFooter: String =
|
||||
"""
|
||||
<script src="/assets/js/jquery.js"></script>
|
||||
<script src="/assets/js/global.min.js"></script>
|
||||
"""
|
||||
}
|
14
WebsiteGenerator/Generators/ThumbnailInfo.swift
Normal file
14
WebsiteGenerator/Generators/ThumbnailInfo.swift
Normal file
@@ -0,0 +1,14 @@
|
||||
import Foundation
|
||||
|
||||
struct ThumbnailInfo {
|
||||
|
||||
let url: String?
|
||||
|
||||
let imageFilePath: String
|
||||
|
||||
let imageHtmlUrl: String
|
||||
|
||||
let title: String
|
||||
|
||||
let cornerText: String?
|
||||
}
|
38
WebsiteGenerator/Generators/ThumbnailListGenerator.swift
Normal file
38
WebsiteGenerator/Generators/ThumbnailListGenerator.swift
Normal file
@@ -0,0 +1,38 @@
|
||||
import Foundation
|
||||
|
||||
struct ThumbnailListGenerator {
|
||||
|
||||
private let factory: TemplateFactory
|
||||
|
||||
let imageProcessor: ImageProcessor
|
||||
|
||||
init(factory: TemplateFactory, imageProcessor: ImageProcessor) {
|
||||
self.factory = factory
|
||||
self.imageProcessor = imageProcessor
|
||||
}
|
||||
|
||||
func generateContent(items: [ThumbnailInfo], style: ThumbnailStyle) throws -> String {
|
||||
try items.map { try itemContent($0, style: style) }
|
||||
.joined(separator: "\n")
|
||||
}
|
||||
|
||||
private func itemContent(_ thumbnail: ThumbnailInfo, style: ThumbnailStyle) throws -> String {
|
||||
var content = [ThumbnailKey : String]()
|
||||
content[.url] = thumbnail.url.unwrapped { "href=\"\($0)\"" }
|
||||
content[.image] = thumbnail.imageHtmlUrl
|
||||
content[.title] = thumbnail.title
|
||||
content[.image2x] = thumbnail.imageHtmlUrl.insert("@2x", beforeLast: ".")
|
||||
content[.corner] = thumbnail.cornerText.unwrapped {
|
||||
factory.largeThumbnail.makeCorner(text: $0)
|
||||
}
|
||||
|
||||
try imageProcessor.requireImage(
|
||||
source: thumbnail.imageFilePath,
|
||||
destination: thumbnail.imageFilePath,
|
||||
width: style.width,
|
||||
desiredHeight: style.height,
|
||||
createDoubleVersion: true)
|
||||
|
||||
return try factory.thumbnail(style: style).generate(content, shouldIndent: false)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user