Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Underline is broken when the ruby is longer than the base text #3

Open
ad-bird opened this issue Nov 18, 2024 · 2 comments
Open

Underline is broken when the ruby is longer than the base text #3

ad-bird opened this issue Nov 18, 2024 · 2 comments
Labels
bug Something isn't working upsteam Have to wait to be resolved upstream

Comments

@ad-bird
Copy link

ad-bird commented Nov 18, 2024

#underline[私は]#ruby[プラクティス][#underline[練習]]#underline[する]

2024-10-04 00 55 55 typst app 13655e6f8db0

@Andrew15-5
Copy link
Owner

Andrew15-5 commented Nov 18, 2024

こんにちは。 This is an upstream issue: typst/typst#1716. Currently, underline(h()) doesn't work in Typst. But I made a workaround:

workaround
// Copyright (C) 2023  Andrew Voynov
// AGPL-3.0-only license is in the LICENSE file in the root of the project
// or at https://www.gnu.org/licenses/agpl-3.0.txt

#let _ruby(rt, rb, size, pos, dy, alignment, delimiter, auto-spacing, underline) = {
  if not ("center", "start", "between", "around").contains(alignment) {
    panic("'" + repr(alignment) + "' is not a valid ruby alignment")
  }

  if not (top, bottom).contains(pos) {
    panic("pos can be either top or bottom but '" + repr(pos) + "'")
  }

  let extract-content(content, fn: it => it) = {
    let func = content.func()
    return if func == text or func == raw {
      (content.text, fn)
    } else {
      extract-content(content.body, fn: it => func(fn(it)))
    }
  }

  let add-spacing-if-enabled(text) = {
    if auto-spacing != true { return text }
    return (
      if text.first() != delimiter { delimiter }
      + text
      + if text.last() != delimiter { delimiter }
    )
  }

  let rt-array = if type(rt) == content {
    let (inner, func) = extract-content(rt)
    add-spacing-if-enabled(inner).split(delimiter).map(func)
  } else if type(rt) == str {
    add-spacing-if-enabled(rt).split(delimiter)
  } else {(rt,)}
  assert(type(rt-array) == array)

  let rb-array = if type(rb) == content {
    let (inner, func) = extract-content(rb)
    add-spacing-if-enabled(inner).split(delimiter).map(func)
  } else if type(rb) == str {
    add-spacing-if-enabled(rb).split(delimiter)
  } else {(rb,)}
  assert(type(rb-array) == array)

  if rt-array.len() != rb-array.len() {
    rt-array = (rt,)
    rb-array = (rb,)
  }

  let gutter = if (alignment == "center" or alignment == "start") {
    h(0pt)
  } else if (alignment == "between" or alignment == "around") {
    h(1fr)
  }

  box(style(st => {
    let sum-body = []
    let sum-width = 0pt
    let i = 0
    while i < rb-array.len() {
      let (body, ruby) = (rb-array.at(i), rt-array.at(i))
      let bodysize = measure(body, st)
      let rt-plain-width = measure(text(size: size, ruby), st).width
      let width = if rt-plain-width > bodysize.width {
        rt-plain-width
      } else {
        bodysize.width
      }
      let chars = if(alignment == "around") {
          h(0.5fr) + ruby.clusters().join(gutter) + h(0.5fr)
      } else {
          ruby.clusters().join(gutter)
      }
      let rubytext = box(
        width: width,
        align(
          if (alignment == "start") { left } else { center },
          text(size: size, chars)
        )
      )
      let textsize = measure(rubytext, st)
      let dx = textsize.width - bodysize.width
      let (t-dx, l-dx, r-dx) = if (alignment == "start") {
        (0pt, 0pt, dx)
      } else {
        (-dx/2, dx/2, dx/2)
      }
      let (l, r) = (i != 0,  i != rb-array.len() - 1)
      sum-width += if l { 0pt } else { t-dx }
      let dy = if pos == top {
        -1.5 * textsize.height - dy
      } else {
        bodysize.height + textsize.height/2 + dy
      }
      place(
        top + left,
        dx: sum-width,
        dy: dy,
        rubytext
      )
      sum-width += width
      let fix(width) = box(
        width: width,
        stroke: (bottom: std.underline.stroke),
        outset: (bottom: std.underline.offset),
        sym.zws,
      )
      if underline {
        sum-body += if l { fix(l-dx) } + body + if r { fix(r-dx) }
      } else {
        sum-body += if l { h(l-dx) } + body + if r { h(r-dx) }
      }
      i += 1
    }
    if underline {
      std.underline(sum-body)
    }
    else {
      sum-body
    }
  }))
}

#let get-ruby(
  size: 0.5em,
  dy: 0pt,
  pos: top,
  alignment: "center",
  delimiter: "|",
  auto-spacing: true,
  underline: false
) = (rt, rb, alignment: alignment) => (
  _ruby(rt, rb, size, pos, dy, alignment, delimiter, auto-spacing, underline)
)

Now you can do this:

#import "@preview/rubby:0.10.1": get-ruby // You need to patch it or use the patched function directly.

#set text(font: "Noto Serif CJK JP")
#set underline(stroke: 0.5pt, offset: 2pt)

#let ruby = get-ruby()
#let uruby = get-ruby(underline: true)

#underline[私は]#ruby[プラクティス][#underline[練習]]#underline[する]

#underline[私は]#uruby[プラクティス][練習]#underline[する]

image

@Andrew15-5 Andrew15-5 added bug Something isn't working upsteam Have to wait to be resolved upstream labels Nov 18, 2024
@Andrew15-5 Andrew15-5 changed the title Underline is broken when the ruby is longer than the parent character (Kanji). Underline is broken when the ruby is longer than the base text Nov 18, 2024
@ad-bird
Copy link
Author

ad-bird commented Nov 19, 2024

Thanks to you I am able to do it.
Andrew15-5さん、ありがとう!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working upsteam Have to wait to be resolved upstream
Projects
None yet
Development

No branches or pull requests

2 participants