• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

sile-typesetter / sile / 6941442205

21 Nov 2023 08:56AM UTC coverage: 63.58% (+1.3%) from 62.266%
6941442205

Pull #1904

github

web-flow
Merge pull request #1891 from sile-typesetter/ot-tate
Pull Request #1904: Merge develop into master (commit to next release being breaking)

67 of 198 new or added lines in 20 files covered. (33.84%)

171 existing lines in 13 files now uncovered.

9907 of 15582 relevant lines covered (63.58%)

6710.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/packages/ruby/init.lua
1
local base = require("packages.base")
×
2

3
local package = pl.class(base)
×
4
package._name = "ruby"
×
5

6
local isLatin = function (char)
7
  return (char > 0x20 and char <= 0x24F) or (char >= 0x300 and char <= 0x36F)
×
8
    or (char >= 0x1DC0 and char <= 0x1EFF) or (char >= 0x2C60 and char <= 0x2c7F)
×
9
end
10

11
local checkIfSpacerNeeded = function (reading)
12
  -- First, did we have a ruby node at all?
13
  if not SILE.scratch.lastRubyBox then return end
×
14
  -- Does the current reading start with a latin?
15
  if not isLatin(SU.codepoint(SU.firstChar(reading))) then return end
×
16
  -- Did we have some nodes recently?
17
  local top = #SILE.typesetter.state.nodes
×
18
  if top < 2 then return end
×
19
  -- Have we had other stuff since the last ruby node?
20
  if SILE.typesetter.state.nodes[top] ~= SILE.scratch.lastRubyBox
×
21
     and SILE.typesetter.state.nodes[top-1] ~= SILE.scratch.lastRubyBox then
×
22
    return
×
23
  end
24
  -- Does the previous reading end with a latin?
25
  if not isLatin(SU.codepoint(SU.lastChar(SILE.scratch.lastRubyText))) then return end
×
26
  -- OK, we need a spacer!
27
  SILE.typesetter:pushGlue(SILE.settings:get("ruby.latinspacer"))
×
28
end
29

30
function package:_init ()
×
31
  base._init(self)
×
32
  -- Japanese language support defines units which are useful here
33
  self:loadPackage("font-fallback")
×
34
  SILE.call("font:add-fallback", { family = "Noto Sans CJK JP" })
×
35
  SILE.languageSupport.loadLanguage("ja")
×
36
end
37

38
function package.declareSettings (_)
×
39

40
  SILE.settings:declare({
×
41
    parameter = "ruby.height",
42
    type = "measurement",
43
    default = SILE.measurement("1zw"),
44
    help = "Vertical offset between the ruby and the main text"
×
45
  })
46

47
  SILE.settings:declare({
×
48
    parameter = "ruby.latinspacer",
49
    type = "glue",
50
    default = SILE.nodefactory.glue("0.25em"),
51
    help = "Glue added between consecutive Latin ruby"
×
52
  })
53

NEW
54
  SILE.settings:declare({
×
55
    parameter = "ruby.opentype",
56
    type = "boolean",
57
    default = true,
NEW
58
    help = "Use OpenType tate feature instead of of a bold weight"
×
59
  })
60

61
end
62

63
function package:registerCommands ()
×
64

65
  self:registerCommand("ruby:font", function (_, _)
×
NEW
66
    if SILE.settings:get("ruby.opentype") then
×
NEW
67
      SILE.call("font", { size = "0.6zw", features = "+ruby" })
×
68
    else
NEW
69
      SILE.call("font", { size = "0.6zw", weight = 700 })
×
70
    end
71
  end)
72

73
  self:registerCommand("ruby", function (options, content)
×
74
    local reading = SU.required(options, "reading", "\\ruby")
×
75
    SILE.typesetter:setpar("")
×
76

77
    checkIfSpacerNeeded(reading)
×
78

79
    local rubybox = SILE.call("hbox", {}, function ()
×
80
      SILE.settings:temporarily(function ()
×
81
        SILE.call("noindent")
×
82
        SILE.call("ruby:font")
×
83
        SILE.typesetter:typeset(reading)
×
84
      end)
85
    end)
86
    rubybox.outputYourself = function (box, typesetter, line)
87
      local ox = typesetter.frame.state.cursorX
×
88
      local oy = typesetter.frame.state.cursorY
×
89
      typesetter.frame:advanceWritingDirection(rubybox.width)
×
90
      typesetter.frame:advancePageDirection(-SILE.settings:get("ruby.height"))
×
91
      SILE.outputter:setCursor(typesetter.frame.state.cursorX, typesetter.frame.state.cursorY)
×
92
      for i = 1, #(box.value) do
×
93
        local node = box.value[i]
×
94
        node:outputYourself(typesetter, line)
×
95
      end
96
      typesetter.frame.state.cursorX = ox
×
97
      typesetter.frame.state.cursorY = oy
×
98
    end
99
    -- measure the content
100
    local cbox = SILE.call("hbox", {}, content)
×
101
    SU.debug("ruby", "base box is", cbox)
×
102
    SU.debug("ruby", "reading is", rubybox)
×
103
    if cbox:lineContribution() > rubybox:lineContribution() then
×
104
      SU.debug("ruby", "Base is longer, offsetting ruby to fit")
×
105
      -- This is actually the offset against the base
106
      rubybox.width = SILE.length(cbox:lineContribution() - rubybox:lineContribution())/2
×
107
    else
108
      local diff = rubybox:lineContribution() - cbox:lineContribution()
×
109
      local to_insert = SILE.length(diff / 2)
×
110
      SU.debug("ruby", "Ruby is longer, inserting", to_insert, "either side of base")
×
111
      cbox.width = rubybox:lineContribution()
×
112
      rubybox.height = 0
×
113
      rubybox.width = 0
×
114
      -- add spaces at beginning and end
115
      table.insert(cbox.value, 1, SILE.nodefactory.glue(to_insert))
×
116
      table.insert(cbox.value, SILE.nodefactory.glue(to_insert))
×
117
    end
118
    SILE.scratch.lastRubyBox = rubybox
×
119
    SILE.scratch.lastRubyText = reading
×
120
  end)
121

122
end
123

124
package.documentation = [[
125
\begin{document}
126
\font:add-fallback[family=Noto Sans CJK JP]
127
\use[module=packages.ruby]
128
Japanese texts often contain pronunciation hints (called \em{furigana}) for difficult kanji or foreign words.
129
These hints are traditionally placed either above (in horizontal typesetting) or beside (in vertical typesetting) the word that they explain.
130
The typesetting term for these glosses is \em{ruby}.
131

132
The \autodoc:package{ruby} package provides the \autodoc:command[check=false]{\ruby[reading=<ruby text>]{<base text>}} command which sets a piece of ruby above or beside the base text.
133
For example:
134

135
\set[parameter=ruby.height,value=12pt]
136
\define[command=ja]{\font[family=Noto Sans CJK JP,language=ja]{\process}}
137

138
\begin{autodoc:codeblock}
139
\\ruby[reading=\ja{れいわ}]\{\ja{令和}\}
140
\end{autodoc:codeblock}
141

142
Produces:
143
\medskip
144
\ja{\ruby[reading=れいわ]{令和}}
145

146
\font:remove-fallback
147
\end{document}
148
]]
×
149

150
return package
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc