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

sile-typesetter / sile / 10881107806

16 Sep 2024 09:29AM UTC coverage: 61.663% (-7.2%) from 68.912%
10881107806

push

github

web-flow
chore(deps): Bump DeterminateSystems/nix-installer-action from 13 to 14 (#2110)

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

10751 of 17435 relevant lines covered (61.66%)

2338.25 hits per line

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

0.0
/shapers/fallback.lua
1
local harfbuzz = require("shapers.harfbuzz")
×
2

3
local shaper = pl.class(harfbuzz)
×
4
shaper._name = "fallback"
×
5

6
local fallbackQueue = pl.class()
×
7

8
function fallbackQueue:_init (text, fallbacks)
×
9
   self.fallbacks = fallbacks
×
10
   self.runs = {
×
11
      {
12
         options = self:currentOptions(),
13
         offset = 0,
14
         start = 1,
15
         -- WARNING: shaper index is in bytes, not UTF8 aware character
16
         -- lengths so do *not* use luautf8.len() here
17
         stop = text:len(),
18
      },
19
   }
20
   self._fallbacks = fallbacks
×
21
   self.text = text
×
22
   self.pending = nil
×
23
end
24

25
function fallbackQueue:popFallback ()
×
26
   return table.remove(self.fallbacks, 1)
×
27
end
28

29
function fallbackQueue:popRun ()
×
30
   self:popFallback()
×
31
   return table.remove(self.runs, 1)
×
32
end
33

34
function fallbackQueue:currentOptions ()
×
35
   return self.fallbacks[1]
×
36
end
37

38
function fallbackQueue:nextFallback ()
×
39
   return self.fallbacks[2]
×
40
end
41

42
function fallbackQueue:currentRun ()
×
43
   return self.runs[1]
×
44
end
45

46
function fallbackQueue:currentText ()
×
47
   local run = self:currentRun()
×
48
   -- WARNING: shaper index is in bytes, not UTF8 aware character
49
   -- lengths so do *not* use luautf8.sub() here
50
   return self.text:sub(run.start, run.stop)
×
51
end
52

53
function fallbackQueue:addRun (offset, start)
×
54
   if not self.pending then
×
55
      SU.debug("font-fallback", function ()
×
56
         return ("New run pending for %s starting byte %s insert at %s"):format(self:currentText(), start, offset)
×
57
      end)
58
      local options = self:nextFallback()
×
59
      if not options then
×
60
         return false
×
61
      end
62
      options.size = SILE.types.measurement(options.size):tonumber()
×
63
      self.pending = {
×
64
         options = options,
65
         offset = offset,
66
         start = start,
67
      }
68
   end
69
   return true
×
70
end
71

72
function fallbackQueue:pushNextRun (stop)
×
73
   if self.pending then
×
74
      SU.debug("font-fallback", function ()
×
75
         return ("Push pending run for %s ending at %s"):format(self:currentText(), stop)
×
76
      end)
77
      self.pending.stop = stop
×
78
      table.insert(self.runs, self.pending)
×
79
      self.pending = nil
×
80
   end
81
end
82

83
local activeFallbacks = {}
×
84

85
function shaper:shapeToken (text, options)
×
86
   local items = {}
×
87
   local fallbackOptions = { options }
×
88
   for _, font in ipairs(activeFallbacks) do
×
89
      table.insert(fallbackOptions, pl.tablex.merge(options, font, true))
×
90
   end
91
   local shapeQueue = fallbackQueue(text, fallbackOptions)
×
92
   repeat -- iterate fallbacks
93
      SU.debug("font-fallback", function ()
×
94
         return ("Start fallback iteration for text '%s'"):format(text)
×
95
      end)
96
      local run = shapeQueue:currentRun()
×
97
      local face = run.options.family:len() > 0 and run.options.family or run.options.filename
×
98
      local chunk = shapeQueue:currentText()
×
99
      SU.debug("font-fallback", function ()
×
100
         return ("Try shaping chunk '%s' with '%s'"):format(chunk, face)
×
101
      end)
102
      local candidate_items = self._base.shapeToken(self, chunk, run.options)
×
103
      local _index
104
      for _, item in ipairs(candidate_items) do
×
105
         item.fontOptions = run.options
×
106
         if item.gid == 0 or item.name == ".null" or item.name == ".notdef" then
×
107
            SU.debug("font-fallback", function ()
×
108
               return ("Glyph %s not found in %s"):format(item.text, face)
×
109
            end)
110
            local newstart = run.start + item.index
×
111
            local pending = shapeQueue:addRun(run.offset, newstart)
×
112
            if not pending then
×
113
               SU.warn(
×
114
                  ("Glyph(s) '%s' not available in any fallback font,\n  run with '-d font-fallback' for more detail.\n"):format(
×
115
                     item.text
116
                  )
117
               )
118
               run.offset = run.offset + 1
×
119
               table.insert(items, run.offset, item) -- output tofu if we're out of fallbacks
×
120
            end
121
         else
122
            SU.debug("font-fallback", function ()
×
123
               return ("Found glyph '%s' in '%s'"):format(item.text, face)
×
124
            end)
125
            shapeQueue:pushNextRun(run.start + item.index - 1) -- if notdef run pending, end it
×
126
            if item.index == _index then
×
127
               local previous = items[run.offset]
×
128
               while previous.next do
×
129
                  previous = previous.next
×
130
               end
131
               previous.next = item
×
132
            else
133
               _index = run.index
×
134
               run.offset = run.offset + 1
×
135
               table.insert(items, run.offset, item)
×
136
            end
137
         end
138
      end
139
      shapeQueue:pushNextRun(run.stop) -- if notdef run pending, end it
×
140
      shapeQueue:popRun()
×
141
   until not shapeQueue:currentRun()
×
142
   return items
×
143
end
144

145
function shaper:createNnodes (token, options)
×
146
   options.tracking = SILE.settings:get("shaper.tracking")
×
147
   local items, _ = self:shapeToken(token, options)
×
148
   if #items < 1 then
×
149
      return {}
×
150
   end
151
   local lang = options.language
×
152
   SILE.languageSupport.loadLanguage(lang)
×
153
   local nodeMaker = SILE.nodeMakers[lang] or SILE.nodeMakers.unicode
×
154
   local run = { [1] = { slice = {}, fontOptions = items[1].fontOptions, chunk = "" } }
×
155
   for i = 1, #items do
×
156
      if items[i].fontOptions ~= run[#run].fontOptions then
×
157
         run[#run + 1] = { slice = {}, chunk = "", fontOptions = items[i].fontOptions }
×
158
         if i < #items then
×
159
            run[#run].fontOptions = items[i].fontOptions
×
160
         end
161
      end
162
      run[#run].chunk = run[#run].chunk .. items[i].text
×
163
      run[#run].slice[#run[#run].slice + 1] = items[i]
×
164
   end
165
   local nodes = {}
×
166
   for i = 1, #run do
×
167
      options = run[i].fontOptions
×
168
      SU.debug("font-fallback", "Shaping", run[i].chunk, "in", options.family)
×
169
      for node in nodeMaker(options):iterator(run[i].slice, run[i].chunk) do
×
170
         nodes[#nodes + 1] = node
×
171
      end
172
   end
173
   SU.debug("font-fallback", nodes)
×
174
   return nodes
×
175
end
176

177
function shaper.clearFallbacks (_)
×
178
   activeFallbacks = {}
×
179
end
180

181
function shaper.addFallback (_, options)
×
182
   table.insert(activeFallbacks, options)
×
183
end
184

185
function shaper.removeFallback (_)
×
186
   table.remove(activeFallbacks)
×
187
end
188

189
function shaper.dumpFallbacks (_)
×
190
   return activeFallbacks
×
191
end
192

193
return shaper
×
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