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

sile-typesetter / sile / 11929316253

20 Nov 2024 08:14AM UTC coverage: 36.551% (-19.8%) from 56.367%
11929316253

push

github

alerque
ci(tooling): Don't require developer workflow tooling in CI

6633 of 18147 relevant lines covered (36.55%)

3388.89 hits per line

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

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

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

6
local enter = function (self, _)
7
   -- Probably broken, see:
8
   -- https://github.com/sile-typesetter/sile/issues/427
9
   if not self.rotate then
×
10
      return
×
11
   end
12
   if not SILE.outputter.enterFrameRotate then
×
13
      return SU.warn(
×
14
         "Frame '"
15
            .. self.id("' will not be rotated: backend '")
×
16
            .. SILE.outputter._name
×
17
            .. "' does not support rotation"
×
18
      )
19
   end
20
   local theta = -math.rad(self.rotate)
×
21
   -- Keep center point the same
22
   local x0 = self:left():tonumber()
×
23
   local x1 = x0 + math.sin(theta) * self:height():tonumber()
×
24
   local y0 = self:bottom():tonumber()
×
25
   SILE.outputter:enterFrameRotate(x0, x1, y0, theta) -- Unstable API
×
26
end
27

28
local leave = function (self, _)
29
   if not self.rotate then
×
30
      return
×
31
   end
32
   if not SILE.outputter.enterFrameRotate then
×
33
      return
×
34
   end -- no enter no leave.
35
   SILE.outputter:leaveFrameRotate()
×
36
end
37

38
-- What is the width, depth and height of a rectangle width w and height h rotated by angle theta?
39
-- rect1 = Rectangle[{0, 0}, {w, h}]
40
-- {{xmin, xmax}, {ymin, ymax}} = Refine[RegionBounds[TransformedRegion[rect1,
41
--                                                     RotationTransform[theta, {w/2,h/2}]]],
42
--                                      w > 0 && h > 0 && theta > 0 && theta < 2 Pi ]
43
-- PiecewiseExpand[xmax - xmin]
44
-- \[Piecewise]  -w Cos[theta]-h Sin[theta]  Sin[theta]<=0&&Cos[theta]<=0
45
--                w Cos[theta]-h Sin[theta]  Sin[theta]<=0&&Cos[theta]>0
46
--               -w Cos[theta]+h Sin[theta]  Sin[theta]>0&&Cos[theta]<=0
47
--                w Cos[theta]+h Sin[theta]  True
48

49
local outputRotatedHbox = function (self, typesetter, line)
50
   local origbox = self.value.orig
×
51
   local theta = self.value.theta
×
52

53
   -- Find origin of untransformed hbox
54
   local X = typesetter.frame.state.cursorX
×
55
   local Y = typesetter.frame.state.cursorY
×
56
   typesetter.frame.state.cursorX = X - (origbox.width.length - self.width) / 2
×
57
   local horigin = X + origbox.width.length / 2
×
58
   local vorigin = Y - (origbox.height - origbox.depth) / 2
×
59

60
   SILE.outputter:rotateFn(horigin, vorigin, theta, function ()
×
61
      origbox:outputYourself(typesetter, line)
×
62
   end)
63
   typesetter.frame.state.cursorX = X
×
64
   typesetter.frame.state.cursorY = Y
×
65
   typesetter.frame:advanceWritingDirection(self.width)
×
66
end
67

68
function package:_init ()
×
69
   base._init(self)
×
70
   if SILE.typesetter and SILE.typesetter.frame then
×
71
      enter(SILE.typesetter.frame, SILE.typesetter)
×
72
      table.insert(SILE.typesetter.frame.leaveHooks, leave)
×
73
   end
74
   table.insert(SILE.framePrototype.enterHooks, enter)
×
75
   table.insert(SILE.framePrototype.leaveHooks, leave)
×
76
end
77

78
function package:registerCommands ()
×
79
   self:registerCommand("rotate", function (options, content)
×
80
      if not SILE.outputter.rotateFn then
×
81
         SU.warn("Output will not be rotated: backend '" .. SILE.outputter._name .. "' does not support rotation")
×
82
         return SILE.process(content)
×
83
      end
84
      local angle = SU.required(options, "angle", "rotate command")
×
85
      local theta = -math.rad(angle)
×
86
      local origbox, hlist = SILE.typesetter:makeHbox(content)
×
87
      local h = origbox.height + origbox.depth
×
88
      local w = origbox.width.length
×
89
      local st = math.sin(theta)
×
90
      local ct = math.cos(theta)
×
91
      local height, width, depth
92
      if st <= 0 and ct <= 0 then
×
93
         width = -w * ct - h * st
×
94
         height = 0.5 * (h - h * ct - w * st)
×
95
         depth = 0.5 * (h + h * ct + w * st)
×
96
      elseif st <= 0 and ct > 0 then
×
97
         width = w * ct - h * st
×
98
         height = 0.5 * (h + h * ct - w * st)
×
99
         depth = 0.5 * (h - h * ct + w * st)
×
100
      elseif st > 0 and ct <= 0 then
×
101
         width = -w * ct + h * st
×
102
         height = 0.5 * (h - h * ct + w * st)
×
103
         depth = 0.5 * (h + h * ct - w * st)
×
104
      else
105
         width = w * ct + h * st
×
106
         height = 0.5 * (h + h * ct + w * st)
×
107
         depth = 0.5 * (h - h * ct - w * st)
×
108
      end
109
      depth = -depth
×
110
      if depth < SILE.types.length(0) then
×
111
         depth = SILE.types.length(0)
×
112
      end
113
      SILE.typesetter:pushHbox({
×
114
         value = { orig = origbox, theta = theta },
115
         height = height,
116
         width = width,
117
         depth = depth,
118
         outputYourself = outputRotatedHbox,
119
      })
120
      SILE.typesetter:pushHlist(hlist)
×
121
   end)
122
end
123

124
package.documentation = [[
125
\begin{document}
126
\use[module=packages.rotate]
127
The \autodoc:package{rotate} package allows you to rotate things. You can rotate entire
128
frames, by adding the \autodoc:parameter{rotate=<angle>} declaration to your frame declaration,
129
and you can rotate any content by issuing the command \autodoc:command{\rotate[angle=<angle>]{<content>}},
130
where the angle is measured in degrees.
131

132
Content which is rotated is placed in a box and rotated. The height and width of
133
the rotated box is measured, and then put into the normal horizontal list for
134
typesetting. The effect is that space is reserved around the rotated content.
135
The best way to understand this is by example: here is some text rotated by
136
\rotate[angle=10]{ten}, \rotate[angle=20]{twenty}, and \rotate[angle=40]{forty} degrees.
137

138
The previous line was produced by the following code:
139

140
\begin[type=autodoc:codeblock]{raw}
141
here is some text rotated by
142
\rotate[angle=10]{ten}, \rotate[angle=20]{twenty}, and \rotate[angle=40]{forty} degrees.
143
\end{raw}
144
\end{document}
145
]]
×
146

147
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