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