Using typst content as background very inefficient

I want to generate ~40 pages with a quite complicated background which is the same for all pages. My current approach is very slow and has a large resulting pdf file.

There is a workaround to compile the background first and use the image function to include it which makes is a lot faster and the resulting file is small.

Is there a way to achieve this without a 2 step process?

Here my current typst document:

#let text_top = "Niedersächsiche Landesrunde der 64. Mathematik-Olympiade"
#let text_bottom = "21./22. Februar 2025"
#let names = ("Test Firstname Lastname",) * 200


#import "@preview/curvly:0.1.0"
#import "@preview/cetz:0.4.2"

#set page(margin: 0mm)

#set text(
  font: "Liberation Serif"
)

#let inner = 34mm
#let middle = 42.8mm
#let outer = 56.7mm
#let stroke_var = (dash: "dashed", thickness: 0.2mm)

#let button() = {
cetz.canvas({
  import cetz.draw: *
  arc((0,0), radius: middle, start: 30deg, delta: 30deg, stroke: stroke_var, anchor: "origin")
  arc((0,0), radius: middle, start: 120deg, delta: 30deg, stroke: stroke_var, anchor: "origin")
  arc((0,0), radius: middle, start: 210deg, delta: 30deg, stroke: stroke_var, anchor: "origin")
  arc((0,0), radius: middle, start: 300deg, delta: 30deg, stroke: stroke_var, anchor: "origin")
  arc((0,0), radius: outer, start: 30deg, delta: 30deg, stroke: stroke_var, anchor: "origin")
  arc((0,0), radius: outer, start: 120deg, delta: 30deg, stroke: stroke_var, anchor: "origin")
  arc((0,0), radius: outer, start: 210deg, delta: 30deg, stroke: stroke_var, anchor: "origin")
  arc((0,0), radius: outer, start: 300deg, delta: 30deg, stroke: stroke_var, anchor: "origin")
  circle((0, 0), radius: inner, stroke: stroke_var)
  rect((-middle, -middle), (middle, middle), stroke: stroke_var)
  //content((0,0), image("mologo-grey.pdf", width: middle))
  
  content((0,0mm), context {
    let l_top = measure([#text_top]).width
    let l_bottom = measure([#text_bottom]).width
    let both = l_top + l_bottom
    
    curvly.text-on-circle(text_top,text_bottom,  50mm, l_top * 0.9 / both * 360deg, l_bottom * 0.9 / both * 360deg)
  })
  
}, padding: (-4mm, 0mm), debug: false)}

#let button_name(name) = {
cetz.canvas({
  import cetz.draw: *
  hide(arc((0,0), radius: outer, start: 30deg, delta: 30deg, stroke: stroke_var, anchor: "origin"), bounds: true)
  hide(arc((0,0), radius: outer, start: 120deg, delta: 30deg, stroke: stroke_var, anchor: "origin"), bounds: true)
  hide(arc((0,0), radius: outer, start: 210deg, delta: 30deg, stroke: stroke_var, anchor: "origin"), bounds: true)
  hide(arc((0,0), radius: outer, start: 300deg, delta: 30deg, stroke: stroke_var, anchor: "origin"), bounds: true)
  
  content((0,0), context {
    set align(center + horizon)
    block(width: 0.95 * middle, text(name, size: 15pt, lang: "de", hyphenate: false, weight: "bold"))
  })
  
}, padding: (-4mm, 0mm), debug: false)}

#let background = [
  #columns(2)[
    #button()
    #button()
    #button()
    #colbreak()
    #button()
    #button()
    #button()
  ]
]

// Precompiled background, is fast and small file size
// #set page(background: image("test-14.pdf"))

// Slow and large pdf file
#set page(background: background)

#let buttons(names) = {
  let len = names.len()

  names.chunks(6, exact: false).map(
    names => place(dx: 1.4mm, dy: 8mm)[#columns(2)[
      #button_name(names.at(0, default: ""))
      #button_name(names.at(2, default: ""))
      #button_name(names.at(4, default: ""))
      #colbreak()
      #button_name(names.at(1, default: ""))
      #button_name(names.at(3, default: ""))
      #button_name(names.at(5, default: ""))
    ]]
  ).join(pagebreak())
}
#buttons(names)
1 Like

Hi, welcome to the forum!

It looks like that the main cause is the misuse of context {…}.
We can make it faster by moving the context keywords (including the one in curvly) to better places and replacing cetz+hide with grid.
However, I can’t fully explain all the phenomena…

See also Why is the value I receive from context always content? - #2 by laurmaedje

Test results

  • 𝑎 → 𝑏 means 𝑎 is faster than 𝑏.
  • The thicker the arrow, the greater the difference.

Source codes are embedded in the PDF.
draw.pdf (43.9 KB)
(Updated, the last version missed some files)

(For security reasons, this forum does not allow uploading any zip file.)

3 Likes

Thank you for taking the time for this :slight_smile:

So this means that curvly is the main culprit for time here?
no_curvly means there is no curved text?

Also how can I extract source code from pdf?

Indeed, but I’m not sure whether curvly doesn’t implement it correctly, or curved text is just too complex…

Yes, it means curvly.text-on-circle(…) has been deleted.

Which PDF viewer do you use? In most viewers (Firefox/pdf.js, Adobe Acrobat, SumatraPDF, …), the files are listed somewhere around PDF bookmarks. Double click should open it.

(I attach the sources to the PDF with pdf.attach.)