Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 50 additions & 43 deletions src/resources/formats/typst/pandoc/quarto/typst-template.typ
Original file line number Diff line number Diff line change
Expand Up @@ -61,54 +61,61 @@
}
}

place(top, float: true, scope: "parent", clearance: 4mm)[
#if title != none {
align(center, block(inset: 2em)[
#set par(leading: heading-line-height) if heading-line-height != none
#set text(font: heading-family) if heading-family != none
#set text(weight: heading-weight)
#set text(style: heading-style) if heading-style != "normal"
#set text(fill: heading-color) if heading-color != black
place(
top,
float: true,
scope: "parent",
clearance: 4mm,
block(below: 1em, width: 100%)[

#text(size: title-size)[#title #if thanks != none {
footnote(thanks, numbering: "*")
counter(footnote).update(n => n - 1)
}]
#(if subtitle != none {
parbreak()
text(size: subtitle-size)[#subtitle]
})
])
}
#if title != none {
align(center, block(inset: 2em)[
#set par(leading: heading-line-height) if heading-line-height != none
#set text(font: heading-family) if heading-family != none
#set text(weight: heading-weight)
#set text(style: heading-style) if heading-style != "normal"
#set text(fill: heading-color) if heading-color != black

#text(size: title-size)[#title #if thanks != none {
footnote(thanks, numbering: "*")
counter(footnote).update(n => n - 1)
}]
#(if subtitle != none {
parbreak()
text(size: subtitle-size)[#subtitle]
})
])
}

#if authors != none and authors != () {
let count = authors.len()
let ncols = calc.min(count, 3)
grid(
columns: (1fr,) * ncols,
row-gutter: 1.5em,
..authors.map(author =>
align(center)[
#author.name \
#author.affiliation \
#author.email
]
#if authors != none and authors != () {
let count = authors.len()
let ncols = calc.min(count, 3)
grid(
columns: (1fr,) * ncols,
row-gutter: 1.5em,
..authors.map(author =>
align(center)[
#author.name \
#author.affiliation \
#author.email
]
)
)
)
}
}

#if date != none {
align(center)[#block(inset: 1em)[
#date
]]
}
#if date != none {
align(center)[#block(inset: 1em)[
#date
]]
}

#if abstract != none {
block(inset: 2em)[
#text(weight: "semibold")[#abstract-title] #h(1em) #abstract
]
}
]
#if abstract != none {
block(inset: 2em)[
#text(weight: "semibold")[#abstract-title] #h(1em) #abstract
]
}
]
)

if toc {
let title = if toc_title == none {
Expand Down
12 changes: 11 additions & 1 deletion tests/docs/smoke-all/typst/columns/basic-two-column.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,20 @@ _quarto:
# Page columns set via set page()
- 'columns: 2,'
# Title block wrapped with place(scope: "parent") for column spanning
- 'place\(top, float: true, scope: "parent", clearance: 4mm\)'
- 'place\(\s+top,\s+float: true,\s+scope: "parent",\s+clearance: 4mm,\s+block\(below: 1em, width: 100%\)'
- # Patterns that must NOT be found
# Should NOT have columns() function call wrapping doc at end of article()
- 'columns\(cols, doc\)\n\}'
ensurePdfTextPositions:
- - subject:
text: "Basic Two Column Layout"
edge: centerX
relation: leftAligned
object:
role: Page
page: 1
edge: centerX
tolerance: 5
---

## Introduction
Expand Down
21 changes: 20 additions & 1 deletion tests/docs/smoke-all/typst/columns/two-column-landscape.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,33 @@ _quarto:
# Page columns set via set page()
- 'columns: 2,'
# Title block wrapped with place(scope: "parent") for column spanning
- 'place\(top, float: true, scope: "parent", clearance: 4mm\)'
- 'place\(\s+top,\s+float: true,\s+scope: "parent",\s+clearance: 4mm,\s+block\(below: 1em, width: 100%\)'
# Landscape page start
- '#set page\(flipped: true\)'
# Landscape page end
- '#set page\(flipped: false\)'
- # Patterns that must NOT be found
# Should NOT have columns() function call wrapping doc at end of article()
- 'columns\(cols, doc\)\n\}'
ensurePdfTextPositions:
- - subject:
text: "Two Column with Landscape Section"
edge: centerX
relation: leftAligned
object:
role: Page
page: 1
edge: centerX
tolerance: 22
- subject:
text: "Testing Column Layout with Page Orientation Changes"
edge: centerX
relation: leftAligned
object:
role: Page
page: 1
edge: centerX
tolerance: 22
---

## Introduction
Expand Down
21 changes: 20 additions & 1 deletion tests/docs/smoke-all/typst/columns/two-column-title-block.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ _quarto:
# Page columns set via set page()
- 'columns: 2,'
# Title block wrapped with place(scope: "parent") for column spanning
- 'place\(top, float: true, scope: "parent", clearance: 4mm\)'
- 'place\(\s+top,\s+float: true,\s+scope: "parent",\s+clearance: 4mm,\s+block\(below: 1em, width: 100%\)'
# Title passed to article()
- 'title: \[Two Column with Full Title Block\]'
# Subtitle passed to article()
Expand All @@ -44,6 +44,25 @@ _quarto:
- # Patterns that must NOT be found
# Should NOT have columns() function call wrapping doc at end of article()
- 'columns\(cols, doc\)\n\}'
ensurePdfTextPositions:
- - subject:
text: "Two Column with Full Title Block"
edge: centerX
relation: leftAligned
object:
role: Page
page: 1
edge: centerX
tolerance: 22
- subject:
text: "Testing Column-Spanning Title Elements"
edge: centerX
relation: leftAligned
object:
role: Page
page: 1
edge: centerX
tolerance: 22
---

## Introduction
Expand Down
12 changes: 11 additions & 1 deletion tests/docs/smoke-all/typst/columns/two-column-toc.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ _quarto:
# Page columns set via set page()
- 'columns: 2,'
# Title block wrapped with place(scope: "parent") for column spanning
- 'place\(top, float: true, scope: "parent", clearance: 4mm\)'
- 'place\(\s+top,\s+float: true,\s+scope: "parent",\s+clearance: 4mm,\s+block\(below: 1em, width: 100%\)'
# TOC enabled in article() call
- 'toc: true'
# TOC title passed
Expand All @@ -28,6 +28,16 @@ _quarto:
- # Patterns that must NOT be found
# Should NOT have columns() function call wrapping doc at end of article()
- 'columns\(cols, doc\)\n\}'
ensurePdfTextPositions:
- - subject:
text: "Two Column with Table of Contents"
edge: centerX
relation: leftAligned
object:
role: Page
page: 1
edge: centerX
tolerance: 22
---

## Introduction
Expand Down
37 changes: 37 additions & 0 deletions tests/smoke/verify/pdf-text-position.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ testQuartoCmd("render", [fixtureQmd, "--to", "typst"], [], {
await runDistanceConstraintTests();
await runDistanceConstraintErrorTests();
await runPageRoleWithEdgeTests();
await runCenterEdgeTests();

// Cleanup
if (safeExistsSync(fixturePdf)) {
Expand Down Expand Up @@ -575,3 +576,39 @@ async function runPageRoleWithEdgeTests() {
]);
await headerNearPageTop.verify([]);
}

/**
* Test centerX and centerY edge functionality
*/
async function runCenterEdgeTests() {
// Test: centerX - title's horizontal centre should be near page's horizontal centre (inset tolerance for minor misalignment)
const centerXPageTest = ensurePdfTextPositions(fixturePdf, [
{
subject: { text: "FIXTURE_TITLE_TEXT", edge: "centerX" },
relation: "leftAligned",
object: { role: "Page", page: 1, edge: "centerX" },
tolerance: 35,
},
]);
await centerXPageTest.verify([]);

// Test: centerY directional - header decoration's centerY should be above title's centerY
const centerYDirectionalTest = ensurePdfTextPositions(fixturePdf, [
{
subject: { text: "FIXTURE_HEADER_TEXT", role: "Decoration", edge: "centerY" },
relation: "above",
object: { text: "FIXTURE_TITLE_TEXT", edge: "centerY" },
},
]);
await centerYDirectionalTest.verify([]);

// Test: centerX directional - a left-aligned heading's centerX should be leftOf page centerX
const centerXDirectionalTest = ensurePdfTextPositions(fixturePdf, [
{
subject: { text: "FIXTURE_H1_TEXT", edge: "centerX" },
relation: "leftOf",
object: { role: "Page", page: 1, edge: "centerX" },
},
]);
await centerXDirectionalTest.verify([]);
}
6 changes: 5 additions & 1 deletion tests/verify-pdf-text-position.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import { ExecuteOutput, Verify } from "./test.ts";
// ============================================================================

// Edge schema for precise bbox edge selection
export const EdgeSchema = z.enum(["left", "right", "top", "bottom"]);
export const EdgeSchema = z.enum(["left", "right", "top", "bottom", "centerX", "centerY"]);
export type Edge = z.infer<typeof EdgeSchema>;

// Relation schemas
Expand Down Expand Up @@ -215,6 +215,10 @@ function getEdgeValue(bbox: BBox, edge: Edge): number {
return bbox.y;
case "bottom":
return bbox.y + bbox.height;
case "centerX":
return bbox.x + bbox.width / 2;
case "centerY":
return bbox.y + bbox.height / 2;
}
}

Expand Down
Loading