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

sile-typesetter / sile / 9428435077

08 Jun 2024 11:35AM UTC coverage: 64.56% (-9.9%) from 74.46%
9428435077

push

github

web-flow
Merge pull request #2047 from alerque/end-pars

23 of 46 new or added lines in 5 files covered. (50.0%)

1684 existing lines in 60 files now uncovered.

11145 of 17263 relevant lines covered (64.56%)

4562.45 hits per line

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

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

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

UNCOV
6
local fallbackQueue = pl.class()
×
7

UNCOV
8
function fallbackQueue:_init (text, fallbacks)
×
UNCOV
9
   self.fallbacks = fallbacks
×
UNCOV
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
   }
UNCOV
20
   self._fallbacks = fallbacks
×
UNCOV
21
   self.text = text
×
UNCOV
22
   self.pending = nil
×
23
end
24

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

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

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

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

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

UNCOV
46
function fallbackQueue:currentText ()
×
UNCOV
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
UNCOV
50
   return self.text:sub(run.start, run.stop)
×
51
end
52

UNCOV
53
function fallbackQueue:addRun (offset, start)
×
UNCOV
54
   if not self.pending then
×
UNCOV
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)
UNCOV
58
      local options = self:nextFallback()
×
UNCOV
59
      if not options then
×
60
         return false
×
61
      end
UNCOV
62
      options.size = SILE.types.measurement(options.size):tonumber()
×
UNCOV
63
      self.pending = {
×
64
         options = options,
65
         offset = offset,
66
         start = start,
67
      }
68
   end
UNCOV
69
   return true
×
70
end
71

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

UNCOV
83
local activeFallbacks = {}
×
84

UNCOV
85
function shaper:shapeToken (text, options)
×
UNCOV
86
   local items = {}
×
UNCOV
87
   local fallbackOptions = { options }
×
UNCOV
88
   for _, font in ipairs(activeFallbacks) do
×
UNCOV
89
      table.insert(fallbackOptions, pl.tablex.merge(options, font, true))
×
90
   end
UNCOV
91
   local shapeQueue = fallbackQueue(text, fallbackOptions)
×
92
   repeat -- iterate fallbacks
UNCOV
93
      SU.debug("font-fallback", function ()
×
94
         return ("Start fallback iteration for text '%s'"):format(text)
×
95
      end)
UNCOV
96
      local run = shapeQueue:currentRun()
×
UNCOV
97
      local face = run.options.family:len() > 0 and run.options.family or run.options.filename
×
UNCOV
98
      local chunk = shapeQueue:currentText()
×
UNCOV
99
      SU.debug("font-fallback", function ()
×
100
         return ("Try shaping chunk '%s' with '%s'"):format(chunk, face)
×
101
      end)
UNCOV
102
      local candidate_items = self._base.shapeToken(self, chunk, run.options)
×
103
      local _index
UNCOV
104
      for _, item in ipairs(candidate_items) do
×
UNCOV
105
         item.fontOptions = run.options
×
UNCOV
106
         if item.gid == 0 or item.name == ".null" or item.name == ".notdef" then
×
UNCOV
107
            SU.debug("font-fallback", function ()
×
108
               return ("Glyph %s not found in %s"):format(item.text, face)
×
109
            end)
UNCOV
110
            local newstart = run.start + item.index
×
UNCOV
111
            local pending = shapeQueue:addRun(run.offset, newstart)
×
UNCOV
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
UNCOV
122
            SU.debug("font-fallback", function ()
×
123
               return ("Found glyph '%s' in '%s'"):format(item.text, face)
×
124
            end)
UNCOV
125
            shapeQueue:pushNextRun(run.start + item.index - 1) -- if notdef run pending, end it
×
UNCOV
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
UNCOV
133
               _index = run.index
×
UNCOV
134
               run.offset = run.offset + 1
×
UNCOV
135
               table.insert(items, run.offset, item)
×
136
            end
137
         end
138
      end
UNCOV
139
      shapeQueue:pushNextRun(run.stop) -- if notdef run pending, end it
×
UNCOV
140
      shapeQueue:popRun()
×
UNCOV
141
   until not shapeQueue:currentRun()
×
UNCOV
142
   return items
×
143
end
144

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

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

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

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

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

UNCOV
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