]> mj.ucw.cz Git - paperjam.git/blob - cmds.cc
Simplify passing of arguments
[paperjam.git] / cmds.cc
1 /*
2  *      PaperJam -- Commands
3  *
4  *      (c) 2018 Martin Mares <mj@ucw.cz>
5  */
6
7 #include <cassert>
8 #include <cstdlib>
9 #include <cstdio>
10
11 #include "jam.h"
12
13 /*** null ***/
14
15 class null_cmd : public cmd_exec {
16 public:
17   null_cmd(cmd *c UNUSED) { }
18   vector<page *> process(vector<page *> &pages) override { return pages; }
19 };
20
21 static const arg_def no_args[] = {
22   { NULL,       0 }
23 };
24
25 /*** Generic transformed page ***/
26
27 class xform_page : public page {
28   page *orig_page;
29   pdf_matrix xform;
30 public:
31   void render(out_context *out, pdf_matrix xform) override;
32   xform_page(page *p, pdf_matrix xf);
33 };
34
35 xform_page::xform_page(page *p, pdf_matrix xf)
36 {
37   orig_page = p;
38   index = p->index;
39   xform = xf;
40
41   BBox media(p->width, p->height);
42   media.transform(xf);
43   width = media.width();
44   height = media.height();
45
46   bbox = p->bbox;
47   bbox.transform(xf);
48 }
49
50 void xform_page::render(out_context *out, pdf_matrix parent_xform)
51 {
52   orig_page->render(out, xform * parent_xform);
53 }
54
55 class cmd_exec_simple : public cmd_exec {
56   virtual page *process_page(page *p) = 0;
57   vector<page *> process(vector<page *> &pages) override;
58 };
59
60 vector<page *> cmd_exec_simple::process(vector<page *> &pages)
61 {
62   vector<page *> out;
63   for (auto p: pages)
64     out.push_back(process_page(p));
65   return out;
66 }
67
68 /*** move ***/
69
70 class move_cmd : public cmd_exec_simple {
71   double x, y;
72 public:
73   move_cmd(cmd *c)
74     {
75       x = c->arg("x")->as_double(0);
76       y = c->arg("y")->as_double(0);
77     }
78   page *process_page(page *p) override
79     {
80       pdf_matrix m;
81       m.shift(x, y);
82       return new xform_page(p, m);
83     }
84 };
85
86 static const arg_def move_args[] = {
87   { "x",        AT_DIMEN | AT_MANDATORY | AT_POSITIONAL },
88   { "y",        AT_DIMEN | AT_MANDATORY | AT_POSITIONAL },
89   { NULL,       0 }
90 };
91
92 /*** scale ***/
93
94 class scale_cmd : public cmd_exec_simple {
95   double x_factor, y_factor;
96 public:
97   scale_cmd(cmd *c)
98     {
99       x_factor = c->arg("x")->as_double(1);
100       y_factor = c->arg("y")->as_double(x_factor);
101     }
102   page *process_page(page *p) override
103     {
104       pdf_matrix m;
105       m.scale(x_factor, y_factor);
106       return new xform_page(p, m);
107     }
108 };
109
110 static const arg_def scale_args[] = {
111   { "x",        AT_DOUBLE | AT_MANDATORY | AT_POSITIONAL },
112   { "y",        AT_DOUBLE | AT_POSITIONAL },
113   { NULL,       0 }
114 };
115
116 /*** rotate ***/
117
118 class rotate_cmd : public cmd_exec_simple {
119   int deg;
120 public:
121   rotate_cmd(cmd *c)
122     {
123       deg = c->arg("deg")->as_int(0) % 360;
124       if (deg < 0)
125         deg += 360;
126       if (deg % 90)
127         die("Rotate requires a multiple of 90 degrees");
128     }
129   page *process_page(page *p) override
130     {
131       pdf_matrix m;
132       switch (deg)
133         {
134         case 0:
135           break;
136         case 90:
137           m.rotate_deg(-90);
138           m.shift(0, p->width);
139           break;
140         case 180:
141           m.rotate_deg(180);
142           m.shift(p->width, p->height);
143           break;
144         case 270:
145           m.rotate_deg(90);
146           m.shift(p->height, 0);
147           break;
148         default:
149           abort();
150         }
151       return new xform_page(p, m);
152     }
153 };
154
155 static const arg_def rotate_args[] = {
156   { "angle",    AT_INT | AT_MANDATORY | AT_POSITIONAL },
157   { NULL,       0 }
158 };
159
160 /*** flip ***/
161
162 class flip_cmd : public cmd_exec_simple {
163   bool horizontal;
164   bool vertical;
165 public:
166   flip_cmd(cmd *c)
167     {
168       horizontal = c->arg("h")->as_int(0);
169       vertical = c->arg("v")->as_int(0);
170       if (!horizontal && !vertical)
171         die("Flip has no direction specified");
172     }
173   page *process_page(page *p) override
174     {
175       pdf_matrix m;
176       if (vertical)
177         {
178           m.scale(1, -1);
179           m.shift(0, p->height);
180         }
181       if (horizontal)
182         {
183           m.scale(-1, 1);
184           m.shift(p->width, 0);
185         }
186       return new xform_page(p, m);
187     }
188 };
189
190 static const arg_def flip_args[] = {
191   { "h",        AT_SWITCH },
192   { "v",        AT_SWITCH },
193   { NULL,       0 }
194 };
195
196 /*** select ***/
197
198 class select_cmd : public cmd_exec {
199   pipeline *pipe;
200 public:
201   select_cmd(cmd *c)
202     {
203       pipe = c->pipe;
204     }
205   vector<page *> process(vector<page *> &pages);
206 };
207
208 static int validate_page_index(vector<page *> &pages, int idx)
209 {
210   if (idx >= 1 && idx <= (int) pages.size())
211     return idx - 1;
212   if (idx <= -1 && idx >= (int) -pages.size())
213     return idx + pages.size();
214   die("Page index %d out of range", idx);
215 }
216
217 vector<page *> select_cmd::process(vector<page *> &pages)
218 {
219   vector<page *> out;
220   for (auto pb: pipe->branches)
221     {
222       vector<page *> selected;
223       for (auto ps: pb->selectors)
224         {
225           int f = validate_page_index(pages, ps.from);
226           int t = validate_page_index(pages, ps.to);
227           int step = (f <= t) ? 1 : -1;
228           for (int i=f; f<=t; f += step)
229             selected.push_back(pages[i]);
230         }
231       auto processed = run_command_list(pb->commands, selected);
232       for (auto p: processed)
233         out.push_back(p);
234     }
235   return out;
236 }
237
238 /*** apply ***/
239
240 class apply_cmd : public cmd_exec {
241   pipeline *pipe;
242 public:
243   apply_cmd(cmd *c)
244     {
245       pipe = c->pipe;
246     }
247   vector<page *> process(vector<page *> &pages);
248 };
249
250 static pipeline_branch *find_branch(pipeline *pipe, vector <page *> &pages, int idx)
251 {
252   for (auto pb: pipe->branches)
253     for (auto ps: pb->selectors)
254       {
255         int f = validate_page_index(pages, ps.from);
256         int t = validate_page_index(pages, ps.to);
257         if (f <= idx && idx <= t || t <= idx && idx <= f)
258           return pb;
259       }
260   return NULL;
261 }
262
263 vector<page *> apply_cmd::process(vector<page *> &pages)
264 {
265   vector<page *> out;
266
267   int cnt = 0;
268   for (auto p: pages)
269     {
270       pipeline_branch *pb = find_branch(pipe, pages, cnt);
271       if (pb)
272         {
273           vector<page *> tmp;
274           tmp.push_back(p);
275           auto processed = run_command_list(pb->commands, tmp);
276           for (auto q: processed)
277             out.push_back(q);
278         }
279       else
280         out.push_back(p);
281       cnt++;
282     }
283
284   return out;
285 }
286
287 /*** modulo ***/
288
289 class modulo_cmd : public cmd_exec {
290   pipeline *pipe;
291   int n;
292 public:
293   modulo_cmd(cmd *c)
294     {
295       n = c->arg("n")->as_int(0);
296       if (n <= 0)
297         die("Modulo must have n > 0");
298       pipe = c->pipe;
299     }
300   vector<page *> process(vector<page *> &pages);
301 };
302
303 vector<page *> modulo_cmd::process(vector<page *> &pages)
304 {
305   vector<page *> out;
306   int tuples = ((int) pages.size() + n - 1) / n;
307
308   for (int tuple=0; tuple < tuples; tuple++)
309     {
310       debug("# Tuple %d", tuple);
311       debug_indent += 4;
312       for (auto pb: pipe->branches)
313         {
314           vector<page *> tmp;
315           for (auto ps: pb->selectors)
316             {
317               int f = ps.from;
318               int t = ps.to;
319               int step = (f <= t) ? 1 : -1;
320               for (int i=f; i<=t; i += step)
321                 {
322                   int j;
323                   if (i > 0 && i <= n)
324                     j = tuple*n + i - 1;
325                   else if (i < 0 && i >= -n)
326                     j = (tuples-1-tuple)*n + (-i) - 1;
327                   else
328                     die("Modulo: invalid index %d", i);
329                   if (j < (int) pages.size())
330                     tmp.push_back(pages[j]);
331                   else
332                     {
333                       page *ref_page = pages[tuple*n];
334                       tmp.push_back(new empty_page(ref_page->width, ref_page->height));
335                     }
336                 }
337             }
338           auto processed = run_command_list(pb->commands, tmp);
339           for (auto q: processed)
340             out.push_back(q);
341         }
342       debug_indent -= 4;
343     }
344
345   return out;
346 }
347
348 static const arg_def modulo_args[] = {
349   { "n",        AT_INT | AT_MANDATORY | AT_POSITIONAL },
350   { NULL,       0 }
351 };
352
353 /*** draw_bbox ***/
354
355 class draw_bbox_cmd : public cmd_exec {
356 public:
357   draw_bbox_cmd(cmd *c UNUSED) { }
358   vector<page *> process(vector<page *> &pages);
359 };
360
361 class draw_bbox_page : public page {
362   page *orig_page;
363 public:
364   void render(out_context *out, pdf_matrix xform);
365   draw_bbox_page(page *p) : page(p) { orig_page = p; }
366 };
367
368 void draw_bbox_page::render(out_context *out, pdf_matrix xform)
369 {
370   orig_page->render(out, xform);
371   out->contents +=
372      "q " +
373      xform.to_string() + " cm " +
374      "0 1 0 RG " +
375      bbox.to_rect() + " re S " +
376      "Q ";
377 }
378
379 vector<page *> draw_bbox_cmd::process(vector<page *> &pages)
380 {
381   vector<page *> out;
382   for (auto p: pages)
383     out.push_back(new draw_bbox_page(p));
384   return out;
385 }
386
387 /*** Command table ***/
388
389 template<typename T> cmd_exec *ctor(cmd *c) { return new T(c); }
390
391 const cmd_def cmd_table[] = {
392   { "null",     no_args,        0,      &ctor<null_cmd>         },
393   { "move",     move_args,      0,      &ctor<move_cmd>         },
394   { "scale",    scale_args,     0,      &ctor<scale_cmd>        },
395   { "rotate",   rotate_args,    0,      &ctor<rotate_cmd>       },
396   { "flip",     flip_args,      0,      &ctor<flip_cmd>         },
397   { "select",   no_args,        1,      &ctor<select_cmd>       },
398   { "apply",    no_args,        1,      &ctor<apply_cmd>        },
399   { "modulo",   modulo_args,    1,      &ctor<modulo_cmd>       },
400   { "draw_bbox",no_args,        0,      &ctor<draw_bbox_cmd>    },
401   { NULL,       NULL,           0,      NULL    }
402 };