ntjess / typst-drafting

Some common drafting utilities for the `typst` document typesetter
The Unlicense
52 stars 2 forks source link

Compiler warning when building with drafting. #7

Closed SebastianJL closed 7 months ago

SebastianJL commented 7 months ago

typst --version: typst 0.9.0 (b5ef7893) drafting version: 0.1.1

When I include notes I get the following compiler warning:

warning: layout did not converge within 5 attempts
 = hint: check if any states or queries are updating themselves

To check if drafting is the culprit I quickly redefined

#let margin-note(body) = ""
#let inline-note(body) = ""

and now the warning doesn't show up anymore.

This is my `drafting setup

  let margin = (left: 2.5cm, right: 2.5cm)  // Increased margins for notes.
  set page(margin: margin, header: getHeader(), numbering: "1", number-align: center)
  set-page-properties(margin-left: margin.left, margin-right: margin.right)

It is part of a larger template

#import "@preview/drafting:0.1.1": set-page-properties

#let buildMainHeader(mainHeadingContent) = {
  [
    #align(center, smallcaps(mainHeadingContent)) 
    #line(length: 100%)
  ]
}

#let buildSecondaryHeader(mainHeadingContent, secondaryHeadingContent) = {
  [
    #smallcaps(mainHeadingContent)  #h(1fr)  #emph(secondaryHeadingContent) 
    #line(length: 100%)
  ]
}

// To know if the secondary heading appears after the main heading
#let isAfter(secondaryHeading, mainHeading) = {
  let secHeadPos = secondaryHeading.location().position()
  let mainHeadPos = mainHeading.location().position()
  if (secHeadPos.at("page") > mainHeadPos.at("page")) {
    return true
  }
  if (secHeadPos.at("page") == mainHeadPos.at("page")) {
    return secHeadPos.at("y") > mainHeadPos.at("y")
  }
  return false
}

#let getHeader() = {
  locate(loc => {
    // Find if there is a level 1 heading on the current page
    let nextMainHeading = query(selector(heading).after(loc), loc).find(headIt => {
     headIt.location().page() == loc.page() and headIt.level == 1
    })
    if (nextMainHeading != none) {
      return buildMainHeader(nextMainHeading.body)
    }
    // Find the last previous level 1 heading -- at this point surely there's one :-)
    let lastMainHeading = query(selector(heading).before(loc), loc).filter(headIt => {
      headIt.level == 1
    }).last()
    // Find if the last level > 1 heading in previous pages
    let previousSecondaryHeadingArray = query(selector(heading).before(loc), loc).filter(headIt => {
      headIt.level > 1
    })
    let lastSecondaryHeading = if (previousSecondaryHeadingArray.len() != 0) {previousSecondaryHeadingArray.last()} else {none}
    // Find if the last secondary heading exists and if it's after the last main heading
    if (lastSecondaryHeading != none and isAfter(lastSecondaryHeading, lastMainHeading)) {
      return buildSecondaryHeader(lastMainHeading.body, lastSecondaryHeading.body)
    }
    return buildMainHeader(lastMainHeading.body)
  })
}

#let project(
  thesis_type: "",
  title: "",
  abstract: [],
  acknowledgements: [],
  authors: (),
  supervisor: (),
  logo: none,
  body
) = {
  // Set the document's basic properties.
  set document(author: authors.map(a => a.name), title: title)
  set text(font: "New Computer Modern", lang: "en")
  show math.equation: set text(weight: 400)
  set heading(numbering: "1.1")
  show par: set par(justify: true)
  show table: set par(justify: false)
  set figure(placement: auto)
  set math.equation(numbering: "(1)")
  show link: li => text(fill:blue, [#underline(li)])

  // ---------- Title page -----------------------------------------------------------
  v(0.25fr)
  align(center, text(1.5em, weight: 700, thesis_type))

  v(0.25fr)
  align(center)[
    #text(2em, weight: 700, title)
  ]

  v(0.4fr)
  // Author information.
  align(center)[
    #text(1.2em, weight: 400, "by")
  ]
  grid(
    columns: (1fr),
    gutter: 1em,
    ..authors.map(author => align(center)[
      *#author.name* \
      #author.email \
      #author.affiliation \
    ]),
  )
  v(0.7fr)

  align(center)[
    #text(1.2em, weight: 700, [
      Institute of Computational Science \
      of the University of Zurich
      ]
    )
  ]

  v(0.5fr)
  // Supervisor information
  align(center)[
    #text(1.2em, weight: 400,"Supervisor")
  ]
  align(center)[
    *#supervisor.name* \
    #supervisor.email \
    #supervisor.affiliation \
  ]

  v(0.3fr)
  // Date
  align(center, text()[
    November 2023
  ])

  // Logo
  if logo != none {
    v(0.5fr)
    align(center, image(logo, width: 30%))
    v(0.25fr)
  } else {
    v(0.75fr)
  }

  set page(numbering: "I", number-align: center)
  // ---------- Abstract page -----------------------------------------------------
  if not abstract == [] {
    pagebreak()
    v(1fr)
    align(center)[
      #heading(
        outlined: false,
        numbering: none,
        text(0.85em, smallcaps[Abstract]),
      )
    ]
    abstract
    v(1.618fr)
    counter(page).update(1)
  }

  // ---------- Acknowledgements page ---------------------------------------------
  if not acknowledgements == [] {
    pagebreak()

    v(1fr)
    align(center)[
      #heading(
        outlined: false,
        numbering: none,
        text(0.85em, smallcaps[Acknowledgements]),
      )
    ]
    acknowledgements
    v(1.618fr)
  }
  // ---------- Table of contents -------------------------------------------------
  pagebreak()
  outline(depth: 3, indent: true)

  // ---------- Main body ---------------------------------------------------------
  pagebreak() // Seems to somehow not be needed here. Don't know why. 
  let margin = (left: 2.5cm, right: 2.5cm)  // Increased margins for notes.
  set page(margin: margin, header: getHeader(), numbering: "1", number-align: center)
  set-page-properties(margin-left: margin.left, margin-right: margin.right)
  counter(page).update(1)
  set par(first-line-indent: 20pt)
  body
}

/// Change settings to make the rest of the document appear as appendix.
///
/// - numbering (none, str, function): Numbering style passed on to heading().
/// - body (Content): Part of the document that should be rendered as appendix.
#let appendix(numbering: "A.1", body) = {
  pagebreak()
  counter(heading).update(0)
  set heading(numbering: numbering)
  body
}
ntjess commented 7 months ago

I've tried compiling this MWE using typst's official 0.9 release without seeing any errors:

```typst #import "@preview/drafting:0.1.1": set-page-properties, margin-note #let buildMainHeader(mainHeadingContent) = { [ #align(center, smallcaps(mainHeadingContent)) #line(length: 100%) ] } #let buildSecondaryHeader(mainHeadingContent, secondaryHeadingContent) = { [ #smallcaps(mainHeadingContent) #h(1fr) #emph(secondaryHeadingContent) #line(length: 100%) ] } // To know if the secondary heading appears after the main heading #let isAfter(secondaryHeading, mainHeading) = { let secHeadPos = secondaryHeading.location().position() let mainHeadPos = mainHeading.location().position() if (secHeadPos.at("page") > mainHeadPos.at("page")) { return true } if (secHeadPos.at("page") == mainHeadPos.at("page")) { return secHeadPos.at("y") > mainHeadPos.at("y") } return false } #let getHeader() = { locate(loc => { // Find if there is a level 1 heading on the current page let nextMainHeading = query(selector(heading).after(loc), loc).find(headIt => { headIt.location().page() == loc.page() and headIt.level == 1 }) if (nextMainHeading != none) { return buildMainHeader(nextMainHeading.body) } // Find the last previous level 1 heading -- at this point surely there's one :-) let lastMainHeading = query(selector(heading).before(loc), loc).filter(headIt => { headIt.level == 1 }).last() // Find if the last level > 1 heading in previous pages let previousSecondaryHeadingArray = query(selector(heading).before(loc), loc).filter(headIt => { headIt.level > 1 }) let lastSecondaryHeading = if (previousSecondaryHeadingArray.len() != 0) {previousSecondaryHeadingArray.last()} else {none} // Find if the last secondary heading exists and if it's after the last main heading if (lastSecondaryHeading != none and isAfter(lastSecondaryHeading, lastMainHeading)) { return buildSecondaryHeader(lastMainHeading.body, lastSecondaryHeading.body) } return buildMainHeader(lastMainHeading.body) }) } #let project( thesis_type: "", title: "", abstract: [], acknowledgements: [], authors: (), supervisor: (name: "bob", email: "", affiliation: ""), logo: none, body ) = { // Set the document's basic properties. set document(author: authors.map(a => a.name), title: title) set text(font: "New Computer Modern", lang: "en") show math.equation: set text(weight: 400) set heading(numbering: "1.1") show par: set par(justify: true) show table: set par(justify: false) set figure(placement: auto) set math.equation(numbering: "(1)") show link: li => text(fill:blue, [#underline(li)]) // ---------- Title page ----------------------------------------------------------- v(0.25fr) align(center, text(1.5em, weight: 700, thesis_type)) v(0.25fr) align(center)[ #text(2em, weight: 700, title) ] v(0.4fr) // Author information. align(center)[ #text(1.2em, weight: 400, "by") ] grid( columns: (1fr), gutter: 1em, ..authors.map(author => align(center)[ *#author.name* \ #author.email \ #author.affiliation \ ]), ) v(0.7fr) align(center)[ #text(1.2em, weight: 700, [ Institute of Computational Science \ of the University of Zurich ] ) ] v(0.5fr) // Supervisor information align(center)[ #text(1.2em, weight: 400,"Supervisor") ] align(center)[ *#supervisor.name* \ #supervisor.email \ #supervisor.affiliation \ ] v(0.3fr) // Date align(center, text()[ November 2023 ]) // Logo if logo != none { v(0.5fr) align(center, image(logo, width: 30%)) v(0.25fr) } else { v(0.75fr) } set page(numbering: "I", number-align: center) // ---------- Abstract page ----------------------------------------------------- if not abstract == [] { pagebreak() v(1fr) align(center)[ #heading( outlined: false, numbering: none, text(0.85em, smallcaps[Abstract]), ) ] abstract v(1.618fr) counter(page).update(1) } // ---------- Acknowledgements page --------------------------------------------- if not acknowledgements == [] { pagebreak() v(1fr) align(center)[ #heading( outlined: false, numbering: none, text(0.85em, smallcaps[Acknowledgements]), ) ] acknowledgements v(1.618fr) } // ---------- Table of contents ------------------------------------------------- pagebreak() outline(depth: 3, indent: true) // ---------- Main body --------------------------------------------------------- pagebreak() // Seems to somehow not be needed here. Don't know why. let margin = (left: 2.5cm, right: 2.5cm) // Increased margins for notes. set page(margin: margin, header: getHeader(), numbering: "1", number-align: center) set-page-properties(margin-left: margin.left, margin-right: margin.right) counter(page).update(1) set par(first-line-indent: 20pt) body } /// Change settings to make the rest of the document appear as appendix. /// /// - numbering (none, str, function): Numbering style passed on to heading(). /// - body (Content): Part of the document that should be rendered as appendix. #let appendix(numbering: "A.1", body) = { pagebreak() counter(heading).update(0) set heading(numbering: numbering) body } #show: project #for ii in range(1, 10) { lorem(ii * 15) margin-note(lorem(ii)) } ```

Snippet of page: image

Is it possible you have >= 4 overlapping notes? This is a known scenario that produces the compiler warning due to typst limitations: image

SebastianJL commented 7 months ago

@ntjess Could this be the offending page?

image

SebastianJL commented 7 months ago

Is there any way you could emit a warning in the console when this happens?

ntjess commented 7 months ago

Unfortunately, until https://github.com/typst/typst/issues/1322 or a similar mechanism is provisioned in typst, warnings are not possible to modify at the package level

ntjess commented 7 months ago

@ntjess Could this be the offending page?

My thought is "no", since there is only a problem when 4 notes overlap -- you should easily be able to place notes on the same page. However, if you include a reproducible example I can check, I will be able to give a definite answer.

ntjess commented 7 months ago

I'm closing this issue for now since the posted MWE no longer shows compiler warnings, and there have been a few improvements in the main branch. I will submit another release PR to typst/packages in the next week or two that hopefully resolves most easy warning scenarios. In the meantime, if you encounter another situation that warns without containing overlapping notes or nested locate() calls, feel free to reopen this issue or submit a new one.