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

sile-typesetter / sile / 9307368959

30 May 2024 06:23PM UTC coverage: 62.627% (-11.5%) from 74.124%
9307368959

push

github

web-flow
Merge e809c4f8a into 70ff5c335

1706 of 2574 new or added lines in 108 files covered. (66.28%)

2038 existing lines in 83 files now uncovered.

10763 of 17186 relevant lines covered (62.63%)

2549.72 hits per line

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

76.92
/packages/rotate/init.lua
1
local base = require("packages.base")
1✔
2

3
local package = pl.class(base)
1✔
4
package._name = "rotate"
1✔
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
3✔
10
      return
2✔
11
   end
12
   if not SILE.outputter.enterFrameRotate then
1✔
NEW
13
      return SU.warn(
×
14
         "Frame '"
NEW
15
            .. self.id("' will not be rotated: backend '")
×
NEW
16
            .. SILE.outputter._name
×
NEW
17
            .. "' does not support rotation"
×
18
      )
19
   end
20
   local theta = -math.rad(self.rotate)
2✔
21
   -- Keep center point the same
22
   local x0 = self:left():tonumber()
2✔
23
   local x1 = x0 + math.sin(theta) * self:height():tonumber()
3✔
24
   local y0 = self:bottom():tonumber()
2✔
25
   SILE.outputter:enterFrameRotate(x0, x1, y0, theta) -- Unstable API
1✔
26
end
27

28
local leave = function (self, _)
29
   if not self.rotate then
8✔
30
      return
4✔
31
   end
32
   if not SILE.outputter.enterFrameRotate then
4✔
NEW
33
      return
×
34
   end -- no enter no leave.
35
   SILE.outputter:leaveFrameRotate()
4✔
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
3✔
51
   local theta = self.value.theta
3✔
52

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

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

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

78
function package:registerCommands ()
1✔
79
   self:registerCommand("rotate", function (options, content)
2✔
80
      if not SILE.outputter.rotateFn then
3✔
NEW
81
         SU.warn("Output will not be rotated: backend '" .. SILE.outputter._name .. "' does not support rotation")
×
NEW
82
         return SILE.process(content)
×
83
      end
84
      local angle = SU.required(options, "angle", "rotate command")
3✔
85
      local theta = -math.rad(angle)
6✔
86
      local origbox, hlist = SILE.typesetter:makeHbox(content)
3✔
87
      local h = origbox.height + origbox.depth
3✔
88
      local w = origbox.width.length
3✔
89
      local st = math.sin(theta)
3✔
90
      local ct = math.cos(theta)
3✔
91
      local height, width, depth
92
      if st <= 0 and ct <= 0 then
3✔
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
3✔
97
         width = w * ct - h * st
9✔
98
         height = 0.5 * (h + h * ct - w * st)
15✔
99
         depth = 0.5 * (h - h * ct + w * st)
18✔
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
3✔
110
      if depth < SILE.types.length(0) then
6✔
NEW
111
         depth = SILE.types.length(0)
×
112
      end
113
      SILE.typesetter:pushHbox({
6✔
114
         value = { orig = origbox, theta = theta },
3✔
115
         height = height,
3✔
116
         width = width,
3✔
117
         depth = depth,
3✔
118
         outputYourself = outputRotatedHbox,
3✔
119
      })
120
      SILE.typesetter:pushHlist(hlist)
3✔
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
]]
1✔
146

147
return package
1✔
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