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