Skip to content

Commit

Permalink
postprocessor for brand.yml in typst
Browse files Browse the repository at this point in the history
includes template fix for
jgm/pandoc#9996

title-font needs to be removed

overlays format-specific brand.defaults on format.render

includes intentionally broken logo default

brand.yaml logo is (for now) still just a string path
open question whether width and padding should be CSS
or perhaps typst would additionally support
e.g. typst-width and inset for direct typst input
  • Loading branch information
gordonwoodhull committed Jul 30, 2024
1 parent 843a420 commit 42378e7
Show file tree
Hide file tree
Showing 28 changed files with 567 additions and 53 deletions.
24 changes: 22 additions & 2 deletions src/command/render/render-contexts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@
* Copyright (C) 2021-2024 Posit Software, PBC
*/

import { Format, FormatExecute, Metadata } from "../../config/types.ts";
import {
Format,
FormatExecute,
FormatRender,
Metadata,
} from "../../config/types.ts";
import {
RenderContext,
RenderFile,
Expand Down Expand Up @@ -600,7 +605,22 @@ async function resolveFormats(
};

// resolve brand in project and forward it to format
mergedFormats[format].render.brand = await project.resolveBrand();
const brand = await project.resolveBrand();
console.log("before", format, brand);
mergedFormats[format].render.brand = brand;
const format_defaults: FormatRender =
(brand?.brand?.defaults?.quarto as unknown as Record<
string,
Record<string, object>
>)?.format
?.[format as string];
if (format_defaults) {
mergedFormats[format].render = mergeConfigs(
mergedFormats[format].render,
format_defaults,
);
console.log("after", format, mergedFormats[format].render);
}

// ensure that we have a valid forma
const formatIsValid = isValidFormat(
Expand Down
19 changes: 18 additions & 1 deletion src/resources/filters/customnodes/callout.lua
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,14 @@ function _callout_main()
end, function(callout)
ensure_typst_font_awesome()

local callout_theme_color_map = {
note = "primary",
warning = "warning",
important = "danger",
tip = "success",
caution = nil -- ?
}

local attrs = _quarto.modules.callouts.callout_attrs[callout.type]
local background_color, icon_color, icon
if attrs == nil then
Expand All @@ -242,7 +250,16 @@ function _callout_main()
icon_color = "rgb(\"#" .. attrs.color .. "\")";
icon = attrs.fa_icon_typst
end

local brand = param("brand")
if brand then
brand = brand.brand or brand
local theme = brand and brand.color
if theme and callout_theme_color_map[callout.type] and
theme[callout_theme_color_map[callout.type]] then
background_color = "brand-theme-background." .. callout_theme_color_map[callout.type]
icon_color = "brand-theme." .. callout_theme_color_map[callout.type]
end
end
local title = callout.title
if title == nil then
title = pandoc.Plain(_quarto.modules.callouts.displayName(callout.type))
Expand Down
2 changes: 2 additions & 0 deletions src/resources/filters/main.lua
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ import("./quarto-post/ipynb.lua")
import("./quarto-post/latex.lua")
import("./quarto-post/typst.lua")
import("./quarto-post/typst-css-to-props.lua")
import("./quarto-post/typst-brand-yml.lua")
import("./quarto-post/latexdiv.lua")
import("./quarto-post/meta.lua")
import("./quarto-post/ojs.lua")
Expand Down Expand Up @@ -377,6 +378,7 @@ local quarto_post_filters = {
{ name = "post-render-ipynb-fixups", filter = render_ipynb_fixups() },
{ name = "post-render-typst-fixups", filter = render_typst_fixups() },
{ name = "post-render-typst-css-to-props", filter = render_typst_css_to_props() },
{ name = "post-render-typst-brand-yml", filter = render_typst_brand_yml() },
{ name = "post-render-gfm-fixups", filter = render_gfm_fixups() },
{ name = "post-render-hugo-fixups", filter = render_hugo_fixups() },
{ name = "post-render-email", filters = render_email() },
Expand Down
182 changes: 182 additions & 0 deletions src/resources/filters/quarto-post/typst-brand-yml.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
function render_typst_brand_yml()
if not _quarto.format.isTypstOutput() then
return {}
end

local function sortedPairs(t, f)
local a = {}
for n in pairs(t) do table.insert(a, n) end
table.sort(a, f)
local i = 0 -- iterator variable
local iter = function() -- iterator function
i = i + 1
if a[i] == nil then return nil
else return a[i], t[a[i]]
end
end
return iter
end

local function to_typst_dict_indent(tab, curr, indent)
curr = curr or ''
indent = indent or ' '
local entries = {}
local inside = curr .. indent
for k, v in sortedPairs(tab) do
if type(v) == 'table' then
v = to_typst_dict_indent(v, inside, indent)
end
if k and v then
table.insert(entries, k .. ': ' .. v)
end
end
if #entries == 0 then return nil end
return '(\n' .. inside .. table.concat(entries, ',\n' .. inside) .. '\n' .. curr .. ')'
end

local horz_to_typst = {
left = "left",
center = "center",
right = "right",
}
local vert_to_typst = {
top = "top",
middle = "horizon",
bottom = "bottom",
}

local function location_to_typst_align(location)
local _, ndash = location:gsub('-', '')
if ndash ~= 1 then return nil end
local horz, vert = location:match '(%a+)--(%a+)'
quarto.log.output('lota', horz, vert)
if not horz_to_typst[horz] or not vert_to_typst[vert] then return nil end
quarto.log.output('lota3', horz, vert)
return horz_to_typst[horz] .. '+' .. vert_to_typst[vert]
end

return {
Pandoc = function(pandoc)
local brand = param('brand')
if not brand then return nil end
brand = brand.brand or brand

-- logo
if brand.logo then
local logo = brand.logo
if type(logo) ~= 'string' then
if logo.large then
logo = logo.large
end
-- and dark/light
end
if logo then
local src = logo
local padding = '0.5in'
local width = '2in'
local location = 'left+top'
if type(logo) ~= 'string' then
src = logo.src
padding = logo.padding or padding
width = logo.width or width
location = logo.location and location_to_typst_align(logo.location) or location
quarto.log.output('logggooo', location)
end
if src then
quarto.doc.include_text('in-header',
'#set page(background: align(' .. location .. ', box(inset: ' .. padding .. ', image("' .. src .. '", width: ' .. width .. '))))')
end
end
end

-- color
if brand.color and brand.color.with then
local palette = {}
for name, color in pairs(brand.color.with) do
palette[name] = output_typst_color(parse_css_color(color))
end
local decl = '#let brand-palette = ' .. to_typst_dict_indent(palette)
quarto.doc.include_text('in-header', decl)
end
if brand.color then
local BACKGROUND_OPACITY = 0.1
local theme = {}
local themebk = {}
for name, color in pairs(brand.color) do
if name ~= 'with' then
if brand.color.with and brand.color.with[color] then
theme[name] = 'brand-palette.' .. color
color = brand.color.with[color] -- no nice idiomatic way to do bk color
else
theme[name] = output_typst_color(parse_css_color(color))
end
themebk[name] = output_typst_color(parse_css_color(color),
{unit = 'fraction', value = BACKGROUND_OPACITY})
end
end
local decl = '#let brand-theme = ' .. to_typst_dict_indent(theme)
quarto.doc.include_text('in-header', decl)
-- for demo purposes only, should implement backgroundcolor and fontcolor
if brand.color.background then
quarto.doc.include_text('in-header', '#set page(fill: brand-theme.background)')
end
if brand.color.foreground then
quarto.doc.include_text('in-header', '#set text(fill: brand-theme.foreground)')
end
local decl = '// theme colors at opacity ' .. BACKGROUND_OPACITY .. '\n#let brand-theme-background = ' .. to_typst_dict_indent(themebk)
quarto.doc.include_text('in-header', decl)
end

-- typography
if brand.typography then
-- this is the only diagnostic Typst currently offers for font not found
quarto.doc.include_text('in-header', '#set text(fallback: false)')
local fontdir
for target, font in pairs(brand.typography) do
if target ~= 'font' then -- handled in Meta
local family = font.family
if target == 'headings' then
quarto.doc.include_text('in-header', '#show heading: set text(font: "' .. family .. '")')
-- quarto.doc.include_text('in-header', '#show article: set text(font: "' .. family .. '")')
elseif target == 'monospace' then
quarto.doc.include_text('in-header', '#show raw: set text(font: "' .. family .. '")')
end
end
end
end
end,
Meta = function(meta)
local brand = param('brand')
if not brand then return nil end
brand = brand.brand or brand

if brand and brand.typography then
if brand.typography.base then
meta['mainfont'] = brand.typography.base.family
end
if brand.typography.headings then
meta['title-font'] = brand.typography.headings.family
end
if brand.typography.font then
local kFontPaths = 'font-paths' -- no luck importing this
for _, entry in ipairs(brand.typography.font) do
if entry['files'] then fontdir = '.' end
end
if not fontdir then
quarto.log.warning('hacky brand.yml only supports font: file: right now')
else
local fontPaths = meta[kFontPaths]
if not fontPaths then
meta[kFontPaths] = {fontdir}
-- alas, lua cannot change ts metadata?
-- at least, this is not seen at command/render/output-typst.ts
end
-- lots of other cases here to politely upgrade nil -> str -> array
end
end
return meta
end
end
}
end

Loading

0 comments on commit 42378e7

Please sign in to comment.