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