Space inserted to the end of a line in the source code

Hi there,
I’m new of Typst and I started to explore the user experience as an old LaTeX user.
I’m writing a code to typeset multiple boxes one after the other.
Every box is width 32pt and has a centered text inside as follow:

#[
    #set box(
      outset: 0pt,
      inset: 0pt,
      stroke:0.2pt,
      width:32pt,
    )
    #set align(center)
    #box[1]
    #box[4]
    #box[6]
    #box[4]
    #box[1]
]

Typst leaves a space among the boxes. To avoid those spaces the only way I found is make a one line istruction like:

#[
    // options as before
    #box[1]#box[4]#box[6]#box[4]#box[1]
]

It’s not a problem but if the content inside the boxes becomes more complex than a single letter the user may will write every box in a single dedicated line for clearness.
In LaTeX is simply a matter to add an ending comment sign % but in Typst this is not the case.
How to eliminate the spurious spaces? Something of a par option?
Thank you.
Roberto

You can try this syntax and see if you like it as an alternative. # is for entering code mode and (...) is an array. Each box is content so it can be joined without separator in the end.

    #(
      box[1],
      box[4],
      box[6],
      box[4],
      box[1],
    ).join()

Thanks. Interesting solution almost unexpected.

So, the engine always insert a space in order to typeset consecutive non-empty lines. I’m wondering if this can affect the system in impossible situations.

After some attempts, the answer is no, you can relax :grinning:. In fact the long comment form works:

#[  #set box(
    outset: 0pt,
    inset: 0pt,
    stroke:0.2pt,
    width:32pt,
  )

  #box[#align(center)[ 1 ]]/*
  */#box[#align(center)[ 4 ]]/*
  */#box[#align(center)[ 6 ]]/*
  */#box[#align(center)[ 4 ]]/*
  */#box[#align(center)[ 1 ]]
]

Respect the previuos code now the alignment is managed inside each box and the entire sequence is typeset aligned to the left as expected.

Therefore, the single line comment doesn’t works in this case.

R.

Maybe there’s a way to influence that - if someone knows maybe they can answer, I don’t.

I think using a show rule to apply centering is a good way to handle it, something like this

    #show text: set align(center)
    #(
      box[1],
      box[4],
      box[6],
      box[4],
      box[1]
    ).join()

bild

Or - I’m agnostic about how you solve the spacing issue, you can help yourself and define a “new kind of box” to make the repetition easier this way too, as an alternative

    // #let cbox = x => box(align(center, x))  /* easy version */
    #let cbox(body, ..args) = box(..args, align(center, body))  /* general version */
    #cbox[1]/*
  */#cbox[4]/*
  */#cbox[6]/*
  */#cbox[4]/*
  */#cbox[1]
1 Like

Very enjoyable code!
Thanks a lot.

I third way is typeset the boxes inside a code block:

#{
  set box(
    outset: 0pt,
    inset: 0pt,
    stroke:0.2pt,
    width:32pt,
  )

box[#align(center)[ 1 ]]
box[#align(center)[ 4 ]]
box[#align(center)[ 6 ]]
box[#align(center)[ 4 ]]
box[#align(center)[ 1 ]]
}

Or better using a for loop:

#let mycenteredbox = x => box(
  align(center, x),
  stroke:0.2pt,
  width:32pt,
)

#for value in (1, 4, 6, 4, 1)  {
  [#mycenteredbox[#value]]
}

Great user experience!
Again, thank you.

2 Likes

Unless you have some ulterior motive to use boxes, I would recommend you to use a grid here. It makes the code a lot cleaner and you can still easily also adjust the width, stroke, inset etc…

#grid(
  columns: 5 * (32pt,),
  align: center,
  stroke: 0.2pt,
  ..(1, 4, 6, 4, 1).map(str),
)
2 Likes

Thank you @janekfleper .

At the moment I don’t understand the double dot syntax.
Some further experiment is needed.
Actually I’m trying to typeset a Pascal Triangle as a toy problem.
I’ll upload the code here when finished.
Nice community.

Yet another option is to collapse the space between the boxes with a weak horizontal space:

Explicit weak spacing
Explicit weak spacing
#[
    #set box(
      outset: 0pt,
      inset: 0pt,
      stroke:0.2pt,
      width:32pt,
    )
    #set align(center)
    #box[1]
    #h(0pt, weak: true)
    #box[4]
    #h(0pt, weak: true)
    #box[6]
    #h(0pt, weak: true)
    #box[4]
    #h(0pt, weak: true)
    #box[1]
    Next content
]

Which is pretty verbose and can be shortened with a custom function:

Custom function that adds weak spacing after each box
Custom function that adds weak spacing after each box
#[
    #let cbox(body) = {
      box(
        outset: 0pt,
        inset: 0pt,
        stroke:0.2pt,
        width:32pt,
        body
      )
      h(0pt, weak: true)
  }
    #set align(center)
    #cbox[1]
    #cbox[4]
    #cbox[6]
    #cbox[4]
    #cbox[1]
    Next content
]

But that alters the spacing to the next piece of content that comes after, so defining your boxes as an array then joining them as @bluss suggested is probably better. Unless you want the next content directly next to your boxes of course.

Results of each option:
image

1 Like

I’m concerned this is out of topic but is so funny!
My best to build a triangle Pascal:

A solution for typeset the Pascal's Triangle
= Pascal's Triangle

#{
  let mycenteredbox = x => box(
    align(center, [#x]),
    //stroke:0.2pt,
    width:32pt,
  )

  // function that step forward a triangle row
  let step_row(r) = {
      for i in range(r.len() - 1, 0, step: -1) {
      r.at(i) = r.at(i) + r.at(i - 1)
    }
    r.push(1)
    return r
  }

  // print a single row
  let typeset_row( row ) = {
    for value in row {
      mycenteredbox(value)
    }
    // ending newline
    [

    ]
  }
  // print the entire Triangle
  set align(center)
  let row = ()
  for _ in range(0, 13) {
    row = step_row(row)
    typeset_row(row)
  }
}

The code works and compile fast even if at every row a new array is created. The original idea for the step_row() function was adding numbers and push a new 1 element to the same array, the only one growing array.
Unfortunately the edited array did not survive out of the function. It seam that global modification is not possible in contrary to what the docs of pure function says (array.push() should be works locally over a global variable).

I think this is absolutely normal. Typst has a very new syntax to learn for me.

So the next step is to use @janekfleper suggestion and cleaned the code and substitute boxes with a single grid.
Thank you.

The double dot syntax is the spread operator which basically turns an array into a bunch of elements. You need this here since a grid expects the content for the individual cells as separate arguments. The code in my example turns the array of integers into an array of strings and then “spreads” the array into the grid. The verbose version would be:

#grid(
  columns: 5 * (32pt,),
  align: center,
  stroke: 0.2pt,
  "1", "4", "6", "4", "1",
)

If you just want to show your code beyond the scope of the question, feel free to post it in the Showcase category. You can then just add a reference to this post.

1 Like

For your example, this is the canonical way (although the centering has side effects here). In code mode Typst only inserts what you tell it to insert, but in markup mode there are things like new lines, line breaks, par breaks, etc.

#{
  set box(outset: 0pt, inset: 0pt, stroke: 0.2pt, width: 32pt)
  set align(center)
  box[1]
  box[4]
  box[6]
  box[4]
  box[1]
}

But this approach is great if we ignore all other things like the intended use, etc.

3 Likes