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} + + +
AB
+``` + 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