]> mj.ucw.cz Git - paperjam.git/blob - cmds.cc
Measure bboxes by Ghostscripting
[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 /*** select ***/
175
176 class select_cmd : public cmd_exec {
177 public:
178   pipeline *pipe;
179   vector<page *> process(vector<page *> &pages);
180 };
181
182 static int validate_page_index(vector<page *> &pages, int idx)
183 {
184   if (idx >= 1 && idx <= (int) pages.size())
185     return idx - 1;
186   if (idx <= -1 && idx >= (int) -pages.size())
187     return idx + pages.size();
188   die("Page index %d out of range", idx);
189 }
190
191 vector<page *> select_cmd::process(vector<page *> &pages)
192 {
193   vector<page *> out;
194   for (auto pb: pipe->branches)
195     {
196       vector<page *> selected;
197       for (auto ps: pb->selectors)
198         {
199           int f = validate_page_index(pages, ps.from);
200           int t = validate_page_index(pages, ps.to);
201           int step = (f <= t) ? 1 : -1;
202           for (int i=f; f<=t; f += step)
203             selected.push_back(pages[i]);
204         }
205       auto processed = run_command_list(pb->commands, selected);
206       for (auto p: processed)
207         out.push_back(p);
208     }
209   return out;
210 }
211
212 static cmd_exec *select_ctor(cmd *c)
213 {
214   select_cmd *r = new select_cmd;
215   r->pipe = c->pipe;
216   return r;
217 }
218
219 /*** apply ***/
220
221 class apply_cmd : public cmd_exec {
222 public:
223   pipeline *pipe;
224   vector<page *> process(vector<page *> &pages);
225 };
226
227 static pipeline_branch *find_branch(pipeline *pipe, vector <page *> &pages, int idx)
228 {
229   for (auto pb: pipe->branches)
230     for (auto ps: pb->selectors)
231       {
232         int f = validate_page_index(pages, ps.from);
233         int t = validate_page_index(pages, ps.to);
234         if (f <= idx && idx <= t || t <= idx && idx <= f)
235           return pb;
236       }
237   return NULL;
238 }
239
240 vector<page *> apply_cmd::process(vector<page *> &pages)
241 {
242   vector<page *> out;
243
244   int cnt = 0;
245   for (auto p: pages)
246     {
247       pipeline_branch *pb = find_branch(pipe, pages, cnt);
248       if (pb)
249         {
250           vector<page *> tmp;
251           tmp.push_back(p);
252           auto processed = run_command_list(pb->commands, tmp);
253           for (auto q: processed)
254             out.push_back(q);
255         }
256       else
257         out.push_back(p);
258       cnt++;
259     }
260
261   return out;
262 }
263
264 static cmd_exec *apply_ctor(cmd *c)
265 {
266   apply_cmd *r = new apply_cmd;
267   r->pipe = c->pipe;
268   return r;
269 }
270
271 /*** modulo ***/
272
273 class modulo_cmd : public cmd_exec {
274 public:
275   pipeline *pipe;
276   int n;
277   vector<page *> process(vector<page *> &pages);
278 };
279
280 vector<page *> modulo_cmd::process(vector<page *> &pages)
281 {
282   vector<page *> out;
283   int tuples = ((int) pages.size() + n - 1) / n;
284
285   for (int tuple=0; tuple < tuples; tuple++)
286     {
287       debug("# Tuple %d", tuple);
288       debug_indent += 4;
289       for (auto pb: pipe->branches)
290         {
291           vector<page *> tmp;
292           for (auto ps: pb->selectors)
293             {
294               int f = ps.from;
295               int t = ps.to;
296               int step = (f <= t) ? 1 : -1;
297               for (int i=f; i<=t; i += step)
298                 {
299                   int j;
300                   if (i > 0 && i <= n)
301                     j = tuple*n + i - 1;
302                   else if (i < 0 && i >= -n)
303                     j = (tuples-1-tuple)*n + (-i) - 1;
304                   else
305                     die("Modulo: invalid index %d", i);
306                   if (j < (int) pages.size())
307                     tmp.push_back(pages[j]);
308                   else
309                     {
310                       page *ref_page = pages[tuple*n];
311                       tmp.push_back(new empty_page(ref_page->width, ref_page->height));
312                     }
313                 }
314             }
315           auto processed = run_command_list(pb->commands, tmp);
316           for (auto q: processed)
317             out.push_back(q);
318         }
319       debug_indent -= 4;
320     }
321
322   return out;
323 }
324
325 static const arg_def modulo_args[] = {
326   { "n",        AT_INT | AT_MANDATORY | AT_POSITIONAL },
327   { NULL,       0 }
328 };
329
330 static cmd_exec *modulo_ctor(cmd *c)
331 {
332   modulo_cmd *m = new modulo_cmd;
333   m->n = c->args.at(0)->as_int(0);
334   if (m->n <= 0)
335     die("Modulo must have n > 0");
336   m->pipe = c->pipe;
337   return m;
338 }
339
340 /*** draw_bbox ***/
341
342 class draw_bbox_cmd : public cmd_exec {
343 public:
344   vector<page *> process(vector<page *> &pages);
345 };
346
347 class draw_bbox_page : public page {
348   page *orig_page;
349 public:
350   void render(out_context *out, pdf_matrix xform);
351   draw_bbox_page(page *p) : page(p) { orig_page = p; }
352 };
353
354 void draw_bbox_page::render(out_context *out, pdf_matrix xform)
355 {
356   orig_page->render(out, xform);
357   out->contents +=
358      "q " +
359      xform.to_string() + " cm " +
360      "0 1 0 RG " +
361      bbox.to_rect() + " re S " +
362      "Q ";
363 }
364
365 vector<page *> draw_bbox_cmd::process(vector<page *> &pages)
366 {
367   vector<page *> out;
368   for (auto p: pages)
369     out.push_back(new draw_bbox_page(p));
370   return out;
371 }
372
373 static cmd_exec *draw_bbox_ctor(cmd *c UNUSED)
374 {
375   draw_bbox_cmd *m = new draw_bbox_cmd;
376   return m;
377 }
378
379 /*** Command table ***/
380
381 const cmd_def cmd_table[] = {
382   { "null",     no_args,        0,      null_ctor       },
383   { "move",     move_args,      0,      move_ctor       },
384   { "scale",    scale_args,     0,      scale_ctor      },
385   { "rotate",   rotate_args,    0,      rotate_ctor     },
386   { "select",   no_args,        1,      select_ctor     },
387   { "apply",    no_args,        1,      apply_ctor      },
388   { "modulo",   modulo_args,    1,      modulo_ctor     },
389   { "draw_bbox",no_args,        0,      draw_bbox_ctor  },
390   { NULL,       NULL,           0,      NULL    }
391 };