diff --git a/dev-docs/feature-format-matrix/qmd-files/css-properties/border/border-width-ex.qmd b/dev-docs/feature-format-matrix/qmd-files/css-properties/border/border-width-ex.qmd
index 910b76007fe..6106db9091a 100644
--- a/dev-docs/feature-format-matrix/qmd-files/css-properties/border/border-width-ex.qmd
+++ b/dev-docs/feature-format-matrix/qmd-files/css-properties/border/border-width-ex.qmd
@@ -19,7 +19,7 @@ _quarto:
typst:
ensureTypstFileRegexMatches:
-
- - ' \[A\], \[B\]'
+ - ' \[A\], \[B\]'
- []
---
diff --git a/dev-docs/feature-format-matrix/qmd-files/css-properties/border/border-width-rem.qmd b/dev-docs/feature-format-matrix/qmd-files/css-properties/border/border-width-rem.qmd
index 740df70e78c..c34bc5ace67 100644
--- a/dev-docs/feature-format-matrix/qmd-files/css-properties/border/border-width-rem.qmd
+++ b/dev-docs/feature-format-matrix/qmd-files/css-properties/border/border-width-rem.qmd
@@ -19,7 +19,7 @@ _quarto:
typst:
ensureTypstFileRegexMatches:
-
- - ' table.cell\(stroke: \(thickness: 17em\)\)\[A\], \[B\]'
+ - ' table.cell\(stroke: \(thickness: 17em\)\)\[A\], \[B\]'
- []
---
diff --git a/dev-docs/feature-format-matrix/qmd-files/css-properties/border/border-width-rpx.qmd b/dev-docs/feature-format-matrix/qmd-files/css-properties/border/border-width-rpx.qmd
index 16665d78fdf..63dd69aa567 100644
--- a/dev-docs/feature-format-matrix/qmd-files/css-properties/border/border-width-rpx.qmd
+++ b/dev-docs/feature-format-matrix/qmd-files/css-properties/border/border-width-rpx.qmd
@@ -19,7 +19,7 @@ _quarto:
typst:
ensureTypstFileRegexMatches:
-
- - ' \[A\], \[B\]'
+ - ' \[A\], \[B\]'
- []
---
diff --git a/dev-docs/feature-format-matrix/qmd-files/css-properties/border/border-width-too-many.qmd b/dev-docs/feature-format-matrix/qmd-files/css-properties/border/border-width-too-many.qmd
new file mode 100644
index 00000000000..5cd5ea5be90
--- /dev/null
+++ b/dev-docs/feature-format-matrix/qmd-files/css-properties/border/border-width-too-many.qmd
@@ -0,0 +1,31 @@
+---
+format:
+ html:
+ quality: -1
+ pdf:
+ quality: na
+ typst:
+ quality: -1
+ comment: "invalid"
+ dashboard:
+ quality: -1
+ docx:
+ quality: na
+ pptx:
+ quality: na
+keep-typ: true
+_quarto:
+ tests:
+ typst:
+ ensureTypstFileRegexMatches:
+ -
+ - ' \[A\], \[B\]'
+ - []
+---
+
+```{=html}
+
+```
+
diff --git a/src/resources/editor/tools/vs-code.mjs b/src/resources/editor/tools/vs-code.mjs
index b020cd377aa..69e48a165d0 100644
--- a/src/resources/editor/tools/vs-code.mjs
+++ b/src/resources/editor/tools/vs-code.mjs
@@ -11955,36 +11955,6 @@ var require_yaml_intelligence_resources = __commonJS({
id: "brand-color-value",
schema: "string"
},
- {
- id: "logo-string-layout",
- description: "Source path or source path with layout options for logo",
- anyOf: [
- "string",
- {
- object: {
- closed: true,
- properties: {
- location: {
- schema: "string",
- description: "X-Y positioning of logo\n"
- },
- padding: {
- schema: "string",
- description: "Padding of logo\n"
- },
- width: {
- schema: "string",
- description: "Width of logo\n"
- },
- src: {
- schema: "path",
- description: "Source path of logo\n"
- }
- }
- }
- }
- ]
- },
{
id: "brand-color",
description: "The brand's custom color palette and theme.\n",
@@ -17677,7 +17647,10 @@ var require_yaml_intelligence_resources = __commonJS({
]
},
schema: {
- ref: "logo-string-layout"
+ anyOf: [
+ "string",
+ "object"
+ ]
},
description: "Logo image (placed in bottom right corner of slides)"
},
@@ -24150,12 +24123,12 @@ var require_yaml_intelligence_resources = __commonJS({
mermaid: "%%"
},
"handlers/mermaid/schema.yml": {
- _internalId: 192356,
+ _internalId: 192336,
type: "object",
description: "be an object",
properties: {
"mermaid-format": {
- _internalId: 192348,
+ _internalId: 192328,
type: "enum",
enum: [
"png",
@@ -24171,7 +24144,7 @@ var require_yaml_intelligence_resources = __commonJS({
exhaustiveCompletions: true
},
theme: {
- _internalId: 192355,
+ _internalId: 192335,
type: "anyOf",
anyOf: [
{
diff --git a/src/resources/editor/tools/yaml/web-worker.js b/src/resources/editor/tools/yaml/web-worker.js
index f1e51dd5ded..d8a034e1621 100644
--- a/src/resources/editor/tools/yaml/web-worker.js
+++ b/src/resources/editor/tools/yaml/web-worker.js
@@ -11956,36 +11956,6 @@ try {
id: "brand-color-value",
schema: "string"
},
- {
- id: "logo-string-layout",
- description: "Source path or source path with layout options for logo",
- anyOf: [
- "string",
- {
- object: {
- closed: true,
- properties: {
- location: {
- schema: "string",
- description: "X-Y positioning of logo\n"
- },
- padding: {
- schema: "string",
- description: "Padding of logo\n"
- },
- width: {
- schema: "string",
- description: "Width of logo\n"
- },
- src: {
- schema: "path",
- description: "Source path of logo\n"
- }
- }
- }
- }
- ]
- },
{
id: "brand-color",
description: "The brand's custom color palette and theme.\n",
@@ -17678,7 +17648,10 @@ try {
]
},
schema: {
- ref: "logo-string-layout"
+ anyOf: [
+ "string",
+ "object"
+ ]
},
description: "Logo image (placed in bottom right corner of slides)"
},
@@ -24151,12 +24124,12 @@ try {
mermaid: "%%"
},
"handlers/mermaid/schema.yml": {
- _internalId: 192356,
+ _internalId: 192336,
type: "object",
description: "be an object",
properties: {
"mermaid-format": {
- _internalId: 192348,
+ _internalId: 192328,
type: "enum",
enum: [
"png",
@@ -24172,7 +24145,7 @@ try {
exhaustiveCompletions: true
},
theme: {
- _internalId: 192355,
+ _internalId: 192335,
type: "anyOf",
anyOf: [
{
diff --git a/src/resources/editor/tools/yaml/yaml-intelligence-resources.json b/src/resources/editor/tools/yaml/yaml-intelligence-resources.json
index 6d69cf755ba..4473adb75b3 100644
--- a/src/resources/editor/tools/yaml/yaml-intelligence-resources.json
+++ b/src/resources/editor/tools/yaml/yaml-intelligence-resources.json
@@ -4927,36 +4927,6 @@
"id": "brand-color-value",
"schema": "string"
},
- {
- "id": "logo-string-layout",
- "description": "Source path or source path with layout options for logo",
- "anyOf": [
- "string",
- {
- "object": {
- "closed": true,
- "properties": {
- "location": {
- "schema": "string",
- "description": "X-Y positioning of logo\n"
- },
- "padding": {
- "schema": "string",
- "description": "Padding of logo\n"
- },
- "width": {
- "schema": "string",
- "description": "Width of logo\n"
- },
- "src": {
- "schema": "path",
- "description": "Source path of logo\n"
- }
- }
- }
- }
- ]
- },
{
"id": "brand-color",
"description": "The brand's custom color palette and theme.\n",
@@ -10649,7 +10619,10 @@
]
},
"schema": {
- "ref": "logo-string-layout"
+ "anyOf": [
+ "string",
+ "object"
+ ]
},
"description": "Logo image (placed in bottom right corner of slides)"
},
@@ -17122,12 +17095,12 @@
"mermaid": "%%"
},
"handlers/mermaid/schema.yml": {
- "_internalId": 192356,
+ "_internalId": 192336,
"type": "object",
"description": "be an object",
"properties": {
"mermaid-format": {
- "_internalId": 192348,
+ "_internalId": 192328,
"type": "enum",
"enum": [
"png",
@@ -17143,7 +17116,7 @@
"exhaustiveCompletions": true
},
"theme": {
- "_internalId": 192355,
+ "_internalId": 192335,
"type": "anyOf",
"anyOf": [
{
diff --git a/src/resources/filters/modules/typst.lua b/src/resources/filters/modules/typst.lua
index 506881d60d6..4f157f290d1 100644
--- a/src/resources/filters/modules/typst.lua
+++ b/src/resources/filters/modules/typst.lua
@@ -75,10 +75,26 @@ local function _main()
return result
end
end
+
+ local function as_typst_dictionary(tab)
+ local entries = {}
+ for k, v in _quarto.utils.table.sortedPairs(tab) do
+ if type(v) == 'table' then
+ v = as_typst_dictionary(v)
+ end
+ if k and v then
+ table.insert(entries, k .. ': ' .. v)
+ end
+ end
+ if #entries == 0 then return nil end
+ return '(' .. table.concat(entries, ', ') .. ')'
+ end
return {
function_call = typst_function_call,
+ sortedPairs = sortedPairs,
as_typst_content = as_typst_content,
+ as_typst_dictionary = as_typst_dictionary,
css = require("modules/typst_css")
}
end
diff --git a/src/resources/filters/modules/typst_css.lua b/src/resources/filters/modules/typst_css.lua
index 6b3fcb48080..29a216cbab4 100644
--- a/src/resources/filters/modules/typst_css.lua
+++ b/src/resources/filters/modules/typst_css.lua
@@ -175,6 +175,7 @@ local typst_named_colors = {
green = '#2ecc40',
lime = '#01ff70',
}
+
-- css can have fraction or percent
-- typst can have int or percent
-- what goes for opacity also goes for alpha
@@ -533,6 +534,152 @@ local function translate_length(csslen, warnings)
return length and output_length(length, warnings)
end
+-- only a few of these map to typst, again seems simplest to parse anyway
+local border_styles = {
+ 'none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset', 'inherit', 'initial', 'revert', 'revert-layer', 'unset'
+}
+
+function parse_multiple(s, limit, callback)
+ local start = 0
+ local count = 0
+ repeat
+ start = callback(s, start)
+ -- not really necessary with string:find
+ -- as evidenced that s.sub also works
+ while s:sub(start, start) == ' ' do
+ start = start + 1
+ end
+ count = count + 1
+ until count >=limit or start >= #s
+end
+
+local border_width_keywords = {
+ thin = '1px',
+ medium = '3px',
+ thick = '5px'
+}
+
+local function translate_border_width(v, warnings)
+ v = border_width_keywords[v] or v
+ local thickness = translate_length(v, warnings)
+ return thickness == '0pt' and 'delete' or thickness
+end
+
+local function quote(s)
+ return '"' .. s .. '"'
+end
+
+local function translate_border_style(v, _warnings)
+ local dash
+ if v == 'none' then
+ return 'delete'
+ elseif tcontains({'dotted', 'dashed'}, v) then
+ return quote(v)
+ end
+ return nil
+end
+
+local function translate_border_color(v, warnings)
+ return output_color(parse_color(v, warnings), nil, warnings)
+end
+
+-- border shorthand
+-- https://developer.mozilla.org/en-US/docs/Web/CSS/border
+local function translate_border(v, warnings)
+ -- not sure why the default style that works is not the same one specified
+ local width = 'medium'
+ local style = 'solid' -- css specifies none
+ local paint = 'black' -- css specifies currentcolor
+ parse_multiple(v, 3, function(s, start)
+ local fbeg, fend = s:find('%w+%b()', start)
+ if fbeg then
+ local paint2 = translate_border_color(s:sub(fbeg, fend), warnings)
+ if paint2 then
+ paint = paint2
+ end
+ return fend + 1
+ else
+ fbeg, fend = s:find('%S+', start)
+ local term = v:sub(fbeg, fend)
+ if tcontains(border_styles, term) then
+ style = term
+ else
+ if parse_length_unit(term) or border_width_keywords[term] then
+ width = term
+ else
+ local paint2 = translate_border_color(term, warnings)
+ if paint2 then
+ paint = paint2
+ else
+ output_warning(warnings, 'invalid border shorthand ' .. term)
+ end
+ end
+ end
+ return fend + 1
+ end
+ end)
+ return {
+ thickness = translate_border_width(width, warnings),
+ dash = translate_border_style(style, warnings),
+ paint = paint
+ }
+end
+
+local function consume_width(s, start, warnings)
+ fbeg, fend = s:find('%S+', start)
+ local term = s:sub(fbeg, fend)
+ local thickness = translate_border_width(term, warnings)
+ return thickness, fend + 1
+end
+
+local function consume_style(s, start, warnings)
+ fbeg, fend = s:find('%S+', start)
+ local term = s:sub(fbeg, fend)
+ local dash = translate_border_style(term, warnings)
+ return dash, fend + 1
+end
+
+local function consume_color(s, start, warnings)
+ local fbeg, fend = s:find('%w+%b()', start)
+ if not fbeg then
+ fbeg, fend = s:find('%S+', start)
+ end
+ if not fbeg then return nil end
+ local paint = translate_border_color(s:sub(fbeg, fend), warnings)
+ return paint, fend + 1
+end
+
+-- the most css thing ever
+local function expand_side_shorthand(items, context, warnings)
+ local sides = {}
+ if #items == 0 then
+ output_warning(warnings, 'no valid ' .. context)
+ elseif #items == 1 then
+ sides.top = items[1]
+ sides.right = items[1]
+ sides.bottom = items[1]
+ sides.left = items[1]
+ elseif #items == 2 then
+ sides.top = items[1]
+ sides.right = items[2]
+ sides.bottom = items[1]
+ sides.left = items[2]
+ elseif #items == 3 then
+ sides.top = items[1]
+ sides.right = items[2]
+ sides.bottom = items[3]
+ sides.left = items[2]
+ elseif #items == 4 then
+ sides.top = items[1]
+ sides.right = items[2]
+ sides.bottom = items[3]
+ sides.left = items[4]
+ else
+ output_warning(warnings, 'too many ' .. context)
+ end
+ return sides
+end
+
return {
parse_color = parse_color,
parse_opacity = parse_opacity,
@@ -540,5 +687,14 @@ return {
parse_length_unit = parse_length_unit,
parse_length = parse_length,
output_length = output_length,
- translate_length = translate_length
+ translate_length = translate_length,
+ parse_multiple = parse_multiple,
+ expand_side_shorthand = expand_side_shorthand,
+ translate_border = translate_border,
+ translate_border_width = translate_border_width,
+ translate_border_style = translate_border_style,
+ translate_border_color = translate_border_color,
+ consume_width = consume_width,
+ consume_style = consume_style,
+ consume_color = consume_color
}
diff --git a/src/resources/filters/quarto-post/typst-brand-yaml.lua b/src/resources/filters/quarto-post/typst-brand-yaml.lua
index 979923a3585..394d82e1859 100644
--- a/src/resources/filters/quarto-post/typst-brand-yaml.lua
+++ b/src/resources/filters/quarto-post/typst-brand-yaml.lua
@@ -3,26 +3,12 @@ function render_typst_brand_yaml()
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
+ for k, v in _quarto.utils.table.sortedPairs(tab) do
if type(v) == 'table' then
v = to_typst_dict_indent(v, inside, indent)
end
@@ -243,8 +229,55 @@ function render_typst_brand_yaml()
logoOptions.path = foundLogo.dark.path
logoOptions.alt = foundLogo.dark.alt
end
- -- todo: path relative to brand.yaml
- logoOptions.padding = _quarto.modules.typst.css.translate_length(logoOptions.padding or '0.5in')
+
+ local pads = {}
+ for k, v in _quarto.utils.table.sortedPairs(logoOptions) do
+ if k == 'padding' then
+ quarto.log.output('foo', k)
+ local widths = {}
+ _quarto.modules.typst.css.parse_multiple(v, 5, function(s, start)
+ local width, newstart = _quarto.modules.typst.css.consume_width(s, start)
+ table.insert(widths, width)
+ return newstart
+ end)
+ local sides = _quarto.modules.typst.css.expand_side_shorthand(
+ widths,
+ 'widths in padding list: ' .. v)
+ pads.top = sides.top
+ pads.right = sides.right
+ pads.bottom = sides.bottom
+ pads.left = sides.left
+ elseif k:find '^padding-' then
+ quarto.log.output('foo', k)
+ local _, ndash = k:gsub('-', '')
+ if ndash == 1 then
+ local side = k:match('^padding--(%a+)')
+ local padding_sides = {'left', 'top', 'right', 'bottom'}
+ if tcontains(padding_sides, side) then
+ pads[side] = _quarto.modules.typst.css.translate_length(v)
+ else
+ quarto.log.warning('invalid padding key ' .. k)
+ end
+ else
+ quarto.log.warning('invalid padding key ' .. k)
+ end
+ end
+ end
+ local inset = nil
+ if next(pads) then
+ if pads.top == pads.right and
+ pads.right == pads.bottom and
+ pads.bottom == pads.left
+ then
+ inset = pads.top
+ elseif pads.top == pads.bottom and pads.left == pads.right then
+ inset = _quarto.modules.typst.as_typst_dictionary({x = pads.left, y = pads.top})
+ else
+ inset = _quarto.modules.typst.as_typst_dictionary(pads)
+ end
+ else
+ inset = '0.5in'
+ end
logoOptions.width = _quarto.modules.typst.css.translate_length(logoOptions.width or '2in')
logoOptions.location = logoOptions.location and
location_to_typst_align(logoOptions.location) or 'left+top'
@@ -252,7 +285,7 @@ function render_typst_brand_yaml()
local altProp = logoOptions.alt and (', alt: "' .. logoOptions.alt .. '"') or ''
local dblbackslash = string.gsub(logoOptions.path, '\\', '\\\\') -- double backslash?
quarto.doc.include_text('in-header',
- '#set page(background: align(' .. logoOptions.location .. ', box(inset: ' .. logoOptions.padding .. ', image("' .. dblbackslash .. '", width: ' .. logoOptions.width .. altProp .. '))))')
+ '#set page(background: align(' .. logoOptions.location .. ', box(inset: ' .. inset .. ', image("' .. dblbackslash .. '", width: ' .. logoOptions.width .. altProp .. '))))')
end
end
end,
diff --git a/src/resources/filters/quarto-post/typst-css-property-processing.lua b/src/resources/filters/quarto-post/typst-css-property-processing.lua
index c41ca5fc6ae..324e30ef299 100644
--- a/src/resources/filters/quarto-post/typst-css-property-processing.lua
+++ b/src/resources/filters/quarto-post/typst-css-property-processing.lua
@@ -32,20 +32,6 @@ function render_typst_css_property_processing()
end
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 dequote(s)
return s:gsub('^["\']', ''):gsub('["\']$', '')
end
@@ -84,28 +70,8 @@ function render_typst_css_property_processing()
return nil
end
- local function to_typst_dict(tab)
- local entries = {}
- for k, v in sortedPairs(tab) do
- if type(v) == 'table' then
- v = to_typst_dict(v)
- end
- if k and v then
- table.insert(entries, k .. ': ' .. v)
- end
- end
- if #entries == 0 then return nil end
- return '(' .. table.concat(entries, ', ') .. ')'
- end
-
local border_sides = {'left', 'top', 'right', 'bottom'}
local border_properties = {'width', 'style', 'color'}
- local border_width_keywords = {
- thin = '1px',
- medium = '3px',
- thick = '5px'
- }
-
local function all_equal(seq)
local a = seq[1]
for i = 2, #seq do
@@ -116,135 +82,31 @@ function render_typst_css_property_processing()
return true
end
- local function translate_border_width(v)
- v = border_width_keywords[v] or v
- local thickness = _quarto.format.typst.css.translate_length(v, _warnings)
- return thickness == '0pt' and 'delete' or thickness
- end
-
- local function translate_border_style(v)
- local dash
- if v == 'none' then
- return 'delete'
- elseif tcontains({'dotted', 'dashed'}, v) then
- return quote(v)
- end
- return nil
- end
-
- local function translate_border_color(v)
- return _quarto.format.typst.css.output_color(_quarto.modules.typst.css.parse_color(v, _warnings), nil, _warnings)
- end
local border_translators = {
width = {
prop = 'thickness',
- fn = translate_border_width
+ fn = _quarto.modules.typst.css.translate_border_width
},
style = {
prop = 'dash',
- fn = translate_border_style
+ fn = _quarto.modules.typst.css.translate_border_style
},
color = {
prop = 'paint',
- fn = translate_border_color
+ fn = _quarto.modules.typst.css.translate_border_color
}
}
- -- only a few of these map to typst, again seems simplest to parse anyway
- local border_styles = {
- 'none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset', 'inherit', 'initial', 'revert', 'revert-layer', 'unset'
- }
-
- function parse_multiple(s, limit, callback)
- local start = 0
- local count = 0
- repeat
- start = callback(s, start)
- -- not really necessary with string:find
- -- as evidenced that s.sub also works
- while s:sub(start, start) == ' ' do
- start = start + 1
- end
- count = count + 1
- until count >=limit or start >= #s
- end
-
- -- border shorthand
- -- https://developer.mozilla.org/en-US/docs/Web/CSS/border
- local function translate_border(v)
- -- not sure why the default style that works is not the same one specified
- local width = 'medium'
- local style = 'solid' -- css specifies none
- local paint = 'black' -- css specifies currentcolor
- parse_multiple(v, 3, function(s, start)
- local fbeg, fend = s:find('%w+%b()', start)
- if fbeg then
- local paint2 = translate_border_color(s:sub(fbeg, fend))
- if paint2 then
- paint = paint2
- end
- return fend + 1
- else
- fbeg, fend = s:find('%S+', start)
- local term = v:sub(fbeg, fend)
- if tcontains(border_styles, term) then
- style = term
- else
- if _quarto.format.typst.css.parse_length_unit(term) or border_width_keywords[term] then
- width = term
- else
- local paint2 = translate_border_color(term)
- if paint2 then
- paint = paint2
- else
- _warnings:insert('invalid border shorthand ' .. term)
- end
- end
- end
- return fend + 1
- end
- end)
- return {
- thickness = translate_border_width(width),
- dash = translate_border_style(style),
- paint = paint
- }
- end
-
- local function consume_width(s, start)
- fbeg, fend = s:find('%S+', start)
- local term = s:sub(fbeg, fend)
- local thickness = translate_border_width(term)
- return thickness, fend + 1
- end
-
- local function consume_style(s, start)
- fbeg, fend = s:find('%S+', start)
- local term = s:sub(fbeg, fend)
- local dash = translate_border_style(term)
- return dash, fend + 1
- end
-
- local function consume_color(s, start)
- local fbeg, fend = s:find('%w+%b()', start)
- if not fbeg then
- fbeg, fend = s:find('%S+', start)
- end
- if not fbeg then return nil end
- local paint = translate_border_color(s:sub(fbeg, fend))
- return paint, fend + 1
- end
-
local border_consumers = {
- width = consume_width,
- style = consume_style,
- color = consume_color,
+ width = _quarto.modules.typst.css.consume_width,
+ style = _quarto.modules.typst.css.consume_style,
+ color = _quarto.modules.typst.css.consume_color,
}
local function handle_border(k, v, borders)
local _, ndash = k:gsub('-', '')
if ndash == 0 then
- local border = translate_border(v)
+ local border = _quarto.modules.typst.css.translate_border(v, _warnings)
for _, side in ipairs(border_sides) do
borders[side] = borders[side] or {}
for k2, v2 in pairs(border) do
@@ -255,13 +117,14 @@ function render_typst_css_property_processing()
local part = k:match('^border--(%a+)')
if tcontains(border_sides, part) then
borders[part] = borders[part] or {}
- local border = translate_border(v)
+ local border = _quarto.modules.typst.css.translate_border(v, _warnings)
for k2, v2 in pairs(border) do
borders[part][k2] = v2
end
elseif tcontains(border_properties, part) then
local items = {}
- parse_multiple(v, 4, function(s, start)
+ -- one extra only so we can error on it
+ _quarto.modules.typst.css.parse_multiple(v, 5, function(s, start)
local item, newstart = border_consumers[part](s, start)
table.insert(items, item)
return newstart
@@ -270,32 +133,14 @@ function render_typst_css_property_processing()
borders[side] = borders[side] or {}
end
local xlate = border_translators[part]
- if #items == 0 then
- _warnings:insert('no valid ' .. part .. 's in ' .. v)
- -- the most css thing ever
- elseif #items == 1 then
- borders.top[xlate.prop] = items[1]
- borders.right[xlate.prop] = items[1]
- borders.bottom[xlate.prop] = items[1]
- borders.left[xlate.prop] = items[1]
- elseif #items == 2 then
- borders.top[xlate.prop] = items[1]
- borders.right[xlate.prop] = items[2]
- borders.bottom[xlate.prop] = items[1]
- borders.left[xlate.prop] = items[2]
- elseif #items == 3 then
- borders.top[xlate.prop] = items[1]
- borders.right[xlate.prop] = items[2]
- borders.bottom[xlate.prop] = items[3]
- borders.left[xlate.prop] = items[2]
- elseif #items == 4 then
- borders.top[xlate.prop] = items[1]
- borders.right[xlate.prop] = items[2]
- borders.bottom[xlate.prop] = items[3]
- borders.left[xlate.prop] = items[4]
- else
- _warnings:insert('too many values in ' .. k .. ' list: ' .. v)
- end
+ local sides = _quarto.modules.typst.css.expand_side_shorthand(
+ items,
+ part .. 's in ' .. k .. ' list: ' .. v,
+ _warnings)
+ borders.top[xlate.prop] = sides.top
+ borders.right[xlate.prop] = sides.right
+ borders.bottom[xlate.prop] = sides.bottom
+ borders.left[xlate.prop] = sides.left
else
_warnings:insert('invalid 2-item border key ' .. k)
end
@@ -304,7 +149,7 @@ function render_typst_css_property_processing()
if tcontains(border_sides, side) and tcontains(border_properties, prop) then
borders[side] = borders[side] or {}
local tr = border_translators[prop]
- borders[side][tr.prop] = tr.fn(v)
+ borders[side][tr.prop] = tr.fn(v, _warnings)
else
_warnings:insert('invalid 3-item border key ' .. k)
end
@@ -355,7 +200,7 @@ function render_typst_css_property_processing()
-- inset seems either buggy or hard to get right, see
-- https://github.com/quarto-dev/quarto-cli/pull/9387#issuecomment-2076015962
-- if next(paddings) ~= nil then
- -- cell.attributes['typst:inset'] = to_typst_dict(paddings)
+ -- cell.attributes['typst:inset'] = _quarto.modules.typst.as_typst_dictionary(paddings)
-- end
-- since e.g. the left side of one cell can override the right side of another
@@ -386,9 +231,9 @@ function render_typst_css_property_processing()
quarto.log.debug('paints', table.unpack(paints))
if all_equal(thicknesses) and all_equal(dashes) and all_equal(paints) then
assert(borders.left)
- cell.attributes['typst:stroke'] = to_typst_dict(borders.left)
+ cell.attributes['typst:stroke'] = _quarto.modules.typst.as_typst_dictionary(borders.left)
else
- cell.attributes['typst:stroke'] = to_typst_dict(borders)
+ cell.attributes['typst:stroke'] = _quarto.modules.typst.as_typst_dictionary(borders)
end
end
end
@@ -418,7 +263,7 @@ function render_typst_css_property_processing()
hlprops.fill = 'rgb(0,0,0,0)'
end
return pandoc.Inlines({
- pandoc.RawInline('typst', '#highlight' .. to_typst_dict(hlprops) .. '['),
+ pandoc.RawInline('typst', '#highlight' .. _quarto.modules.typst.as_typst_dictionary(hlprops) .. '['),
span,
pandoc.RawInline('typst', ']')
})
diff --git a/src/resources/pandoc/datadir/_utils.lua b/src/resources/pandoc/datadir/_utils.lua
index e48c5e5eba7..e2d986646e2 100644
--- a/src/resources/pandoc/datadir/_utils.lua
+++ b/src/resources/pandoc/datadir/_utils.lua
@@ -235,6 +235,22 @@ local function tcontains(t, value)
return false
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 get_type(v)
local pandoc_type = pandoc.utils.type(v)
if pandoc_type == "Inline" then
@@ -546,7 +562,8 @@ return {
type = get_type,
table = {
isarray = tisarray,
- contains = tcontains
+ contains = tcontains,
+ sortedPairs = sortedPairs
},
as_inlines = as_inlines,
as_blocks = as_blocks,
diff --git a/src/resources/schema/definitions.yml b/src/resources/schema/definitions.yml
index fa5705b408a..9a9f9c455b8 100644
--- a/src/resources/schema/definitions.yml
+++ b/src/resources/schema/definitions.yml
@@ -2568,30 +2568,6 @@
- id: brand-color-value
schema: string
-- id: logo-string-layout
- description: Source path or source path with layout options for logo
- anyOf:
- - string
- - object:
- closed: true
- properties:
- location:
- schema: string
- description: >
- X-Y positioning of logo
- padding:
- schema: string
- description: >
- Padding of logo
- width:
- schema: string
- description: >
- Width of logo
- src:
- schema: path
- description: >
- Source path of logo
-
- id: brand-color
description: >
The brand's custom color palette and theme.
diff --git a/src/resources/schema/document-reveal-content.yml b/src/resources/schema/document-reveal-content.yml
index 3cb0c25b57b..0cfed2c3898 100644
--- a/src/resources/schema/document-reveal-content.yml
+++ b/src/resources/schema/document-reveal-content.yml
@@ -2,7 +2,9 @@
tags:
formats: [revealjs, typst]
schema:
- ref: logo-string-layout
+ anyOf:
+ - string
+ - object # typst: location, padding, padding-*, width, source
description: "Logo image (placed in bottom right corner of slides)"
- name: footer
diff --git a/src/resources/schema/json-schemas.json b/src/resources/schema/json-schemas.json
index 7c895d22d83..9542a2f5060 100644
--- a/src/resources/schema/json-schemas.json
+++ b/src/resources/schema/json-schemas.json
@@ -3295,31 +3295,6 @@
"BrandColorValue": {
"type": "string"
},
- "LogoStringLayout": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "object": {
- "properties": {
- "location": {
- "type": "string"
- },
- "padding": {
- "type": "string"
- },
- "width": {
- "type": "string"
- },
- "src": {
- "type": "string"
- }
- }
- }
- }
- ]
- },
"BrandColor": {
"object": {
"properties": {
diff --git a/src/resources/types/schema-types.ts b/src/resources/types/schema-types.ts
index 4c215554dff..c31339ebf4c 100644
--- a/src/resources/types/schema-types.ts
+++ b/src/resources/types/schema-types.ts
@@ -1301,13 +1301,6 @@ export type BrandNamedLogo =
export type BrandColorValue = string;
-export type LogoStringLayout = string | {
- location?: string;
- padding?: string;
- src?: string;
- width?: string;
-}; /* Source path or source path with layout options for logo */
-
export type BrandColor = {
background?: BrandColorValue;
danger?: BrandColorValue;
diff --git a/tests/docs/smoke-all/typst/brand-yaml/logo/padding-xy/_brand.yml b/tests/docs/smoke-all/typst/brand-yaml/logo/padding-xy/_brand.yml
new file mode 100644
index 00000000000..20a0a3456eb
--- /dev/null
+++ b/tests/docs/smoke-all/typst/brand-yaml/logo/padding-xy/_brand.yml
@@ -0,0 +1,13 @@
+logo:
+ images:
+ large-light: quarto.png
+ large: large-light
+defaults:
+ quarto:
+ format:
+ typst:
+ logo:
+ src: large
+ location: center-top
+ width: 300px
+ padding: 1in
diff --git a/tests/docs/smoke-all/typst/brand-yaml/logo/padding-xy/brand-logo.qmd b/tests/docs/smoke-all/typst/brand-yaml/logo/padding-xy/brand-logo.qmd
new file mode 100644
index 00000000000..46c8abf1b38
--- /dev/null
+++ b/tests/docs/smoke-all/typst/brand-yaml/logo/padding-xy/brand-logo.qmd
@@ -0,0 +1,18 @@
+---
+title: logo background
+format:
+ typst:
+ keep-typ: true
+logo:
+ padding: 1in 2in
+_quarto:
+ tests:
+ typst:
+ ensureTypstFileRegexMatches:
+ -
+ - '#set page\(background: align\(center\+top, box\(inset: \(x: 2in, y: 1\in\), image\("quarto.png", width: 225pt\)\)\)\)'
+ - []
+---
+
+{{< lipsum 4 >}}
+
diff --git a/tests/docs/smoke-all/typst/brand-yaml/logo/padding-xy/quarto.png b/tests/docs/smoke-all/typst/brand-yaml/logo/padding-xy/quarto.png
new file mode 100644
index 00000000000..616d17b92cb
Binary files /dev/null and b/tests/docs/smoke-all/typst/brand-yaml/logo/padding-xy/quarto.png differ
diff --git a/tests/docs/smoke-all/typst/brand-yaml/logo/padding/_brand.yml b/tests/docs/smoke-all/typst/brand-yaml/logo/padding/_brand.yml
new file mode 100644
index 00000000000..20a0a3456eb
--- /dev/null
+++ b/tests/docs/smoke-all/typst/brand-yaml/logo/padding/_brand.yml
@@ -0,0 +1,13 @@
+logo:
+ images:
+ large-light: quarto.png
+ large: large-light
+defaults:
+ quarto:
+ format:
+ typst:
+ logo:
+ src: large
+ location: center-top
+ width: 300px
+ padding: 1in
diff --git a/tests/docs/smoke-all/typst/brand-yaml/logo/padding/brand-logo.qmd b/tests/docs/smoke-all/typst/brand-yaml/logo/padding/brand-logo.qmd
new file mode 100644
index 00000000000..8db14e3bdd0
--- /dev/null
+++ b/tests/docs/smoke-all/typst/brand-yaml/logo/padding/brand-logo.qmd
@@ -0,0 +1,20 @@
+---
+title: logo background
+format:
+ typst:
+ keep-typ: true
+logo:
+ padding-left: 4pt
+ padding: 9pt 2pt 3pt 17pt
+ padding-top: 1pt
+_quarto:
+ tests:
+ typst:
+ ensureTypstFileRegexMatches:
+ -
+ - '#set page\(background: align\(center\+top, box\(inset: \(bottom: 3pt, left: 4pt, right: 2pt, top: 1pt\), image\("quarto.png", width: 225pt\)\)\)\)'
+ - []
+---
+
+{{< lipsum 4 >}}
+
diff --git a/tests/docs/smoke-all/typst/brand-yaml/logo/padding/quarto.png b/tests/docs/smoke-all/typst/brand-yaml/logo/padding/quarto.png
new file mode 100644
index 00000000000..616d17b92cb
Binary files /dev/null and b/tests/docs/smoke-all/typst/brand-yaml/logo/padding/quarto.png differ