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

sile-typesetter / sile / 6713098919

31 Oct 2023 10:21PM UTC coverage: 52.831% (-21.8%) from 74.636%
6713098919

push

github

web-flow
Merge d0a2a1ee9 into b185d4972

45 of 45 new or added lines in 3 files covered. (100.0%)

8173 of 15470 relevant lines covered (52.83%)

6562.28 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

54
end
55

56
function package:registerCommands ()
×
57

58
  self:registerCommand("ruby:font", function (_, _)
×
59
    SILE.call("font", { size = "0.6zw", weight = 800 })
×
60
  end)
61

62
  self:registerCommand("ruby", function (options, content)
×
63
    local reading = SU.required(options, "reading", "\\ruby")
×
64
    SILE.typesetter:setpar("")
×
65

66
    checkIfSpacerNeeded(reading)
×
67

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

111
end
112

113
package.documentation = [[
114
\begin{document}
115
\font:add-fallback[family=Noto Sans CJK JP]
116
\use[module=packages.ruby]
117
Japanese texts often contain pronunciation hints (called \em{furigana}) for difficult kanji or foreign words.
118
These hints are traditionally placed either above (in horizontal typesetting) or beside (in vertical typesetting) the word that they explain.
119
The typesetting term for these glosses is \em{ruby}.
120

121
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.
122
For example:
123

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

127
\begin{autodoc:codeblock}
128
\\ruby[reading=\ja{れいわ}]\{\ja{令和}\}
129
\end{autodoc:codeblock}
130

131
Produces:
132
\medskip
133
\ja{\ruby[reading=れいわ]{令和}}
134

135
\font:remove-fallback
136
\end{document}
137
]]
×
138

139
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

© 2025 Coveralls, Inc