gvrender_lasi.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617
  1. /*************************************************************************
  2. * Copyright (c) 2011 AT&T Intellectual Property
  3. * All rights reserved. This program and the accompanying materials
  4. * are made available under the terms of the Eclipse Public License v1.0
  5. * which accompanies this distribution, and is available at
  6. * https://www.eclipse.org/legal/epl-v10.html
  7. *
  8. * Contributors: Details at https://graphviz.org
  9. *************************************************************************/
  10. #include <cstddef>
  11. #include <iostream>
  12. #include <fstream>
  13. #include <stdexcept>
  14. #include <string>
  15. #include <LASi.h>
  16. #include "config.h"
  17. #include <gvc/gvplugin_render.h>
  18. #include <gvc/gvplugin_device.h>
  19. #include <gvc/gvio.h>
  20. #include <gvc/gvcint.h>
  21. #include <common/const.h>
  22. #include <common/utils.h>
  23. #include <util/agxbuf.h>
  24. #include <util/prisize_t.h>
  25. #include <util/unreachable.h>
  26. #include "../core/ps.h"
  27. using namespace LASi;
  28. using namespace std;
  29. // J$: added `pdfmark' URL embedding. PostScript rendered from
  30. // dot files with URL attributes will get active PDF links
  31. // from Adobe's Distiller.
  32. #define PDFMAX 14400 ///< Maximum size of PDF page
  33. typedef enum { FORMAT_PS, FORMAT_PS2, FORMAT_EPS } format_type;
  34. struct Context {
  35. using write_fn = size_t(*)(GVJ_t*, const char*, size_t);
  36. Context(write_fn fn): save_write_fn(fn) { }
  37. PostscriptDocument doc;
  38. write_fn save_write_fn;
  39. };
  40. static size_t lasi_head_writer(GVJ_t * job, const char *s, size_t len)
  41. {
  42. Context *ctxt = reinterpret_cast<Context*>(job->context);
  43. ctxt->doc.osHeader() << s;
  44. return len;
  45. }
  46. static size_t lasi_body_writer(GVJ_t * job, const char *s, size_t len)
  47. {
  48. Context *ctxt = reinterpret_cast<Context*>(job->context);
  49. ctxt->doc.osBody() << s;
  50. return len;
  51. }
  52. static size_t lasi_footer_writer(GVJ_t * job, const char *s, size_t len)
  53. {
  54. Context *ctxt = reinterpret_cast<Context*>(job->context);
  55. ctxt->doc.osFooter() << s;
  56. return len;
  57. }
  58. static void lasi_begin_job(GVJ_t * job)
  59. {
  60. job->context = new Context(job->gvc->write_fn);
  61. job->gvc->write_fn = lasi_head_writer;
  62. gvprintf(job, "%%%%Creator: %s version %s (%s)\n",
  63. job->common->info[0], job->common->info[1], job->common->info[2]);
  64. }
  65. static void lasi_end_job(GVJ_t * job)
  66. {
  67. job->gvc->write_fn = lasi_footer_writer;
  68. if (job->render.id != FORMAT_EPS)
  69. gvprintf(job, "%%%%Pages: %d\n", job->common->viewNum);
  70. if (job->common->show_boxes == nullptr)
  71. if (job->render.id != FORMAT_EPS)
  72. gvprintf(job, "%%%%BoundingBox: %d %d %d %d\n",
  73. job->boundingBox.LL.x, job->boundingBox.LL.y,
  74. job->boundingBox.UR.x, job->boundingBox.UR.y);
  75. gvputs(job, "end\nrestore\n");
  76. {
  77. // create the new stream to "redirect" cout's output to
  78. ostringstream output;
  79. // smart class that will swap streambufs and replace them
  80. // when object goes out of scope.
  81. class StreamBuf_Swapper
  82. {
  83. public:
  84. StreamBuf_Swapper(ostream & orig, ostream & replacement)
  85. : buf_(orig.rdbuf()), str_(orig)
  86. {
  87. orig.rdbuf(replacement.rdbuf());
  88. }
  89. ~StreamBuf_Swapper()
  90. {
  91. str_.rdbuf(buf_);
  92. }
  93. private:
  94. std::streambuf * buf_;
  95. std::ostream & str_;
  96. } swapper(cout, output);
  97. Context *ctxt = reinterpret_cast<Context*>(job->context);
  98. ctxt->doc.write(cout);
  99. job->gvc->write_fn = ctxt->save_write_fn;
  100. gvputs(job, output.str().c_str());
  101. delete ctxt;
  102. }
  103. }
  104. static void lasi_begin_graph(GVJ_t * job)
  105. {
  106. obj_state_t *obj = job->obj;
  107. job->gvc->write_fn = lasi_body_writer;
  108. if (job->common->viewNum == 0) {
  109. gvprintf(job, "%%%%Title: %s\n", agnameof(obj->u.g));
  110. if (job->render.id != FORMAT_EPS)
  111. gvputs(job, "%%Pages: (atend)\n");
  112. else
  113. gvputs(job, "%%Pages: 1\n");
  114. if (job->common->show_boxes == nullptr) {
  115. if (job->render.id != FORMAT_EPS)
  116. gvputs(job, "%%BoundingBox: (atend)\n");
  117. else
  118. gvprintf(job, "%%%%BoundingBox: %d %d %d %d\n",
  119. job->pageBoundingBox.LL.x, job->pageBoundingBox.LL.y,
  120. job->pageBoundingBox.UR.x, job->pageBoundingBox.UR.y);
  121. }
  122. gvputs(job, "%%EndComments\nsave\n");
  123. // include shape library
  124. cat_libfile(job, job->common->lib, ps_txt);
  125. // include epsf
  126. epsf_define(job);
  127. if (job->common->show_boxes) {
  128. const char* args[2];
  129. args[0] = job->common->show_boxes[0];
  130. args[1] = nullptr;
  131. cat_libfile(job, nullptr, args);
  132. }
  133. }
  134. // Set base URL for relative links (for Distiller ≥ 3.0)
  135. if (obj->url)
  136. gvprintf(job, "[ {Catalog} << /URI << /Base %s >> >>\n"
  137. "/PUT pdfmark\n", ps_string(obj->url, CHAR_UTF8));
  138. }
  139. static void lasi_begin_layer(GVJ_t * job, char*, int layerNum, int numLayers)
  140. {
  141. gvprintf(job, "%d %d setlayer\n", layerNum, numLayers);
  142. }
  143. static void lasi_begin_page(GVJ_t * job)
  144. {
  145. box pbr = job->pageBoundingBox;
  146. gvprintf(job, "%%%%Page: %d %d\n",
  147. job->common->viewNum + 1, job->common->viewNum + 1);
  148. if (job->common->show_boxes == nullptr)
  149. gvprintf(job, "%%%%PageBoundingBox: %d %d %d %d\n",
  150. pbr.LL.x, pbr.LL.y, pbr.UR.x, pbr.UR.y);
  151. gvprintf(job, "%%%%PageOrientation: %s\n",
  152. (job->rotation ? "Landscape" : "Portrait"));
  153. if (job->render.id == FORMAT_PS2)
  154. gvprintf(job, "<< /PageSize [%d %d] >> setpagedevice\n",
  155. pbr.UR.x, pbr.UR.y);
  156. gvprintf(job, "%d %d %d beginpage\n",
  157. job->pagesArrayElem.x, job->pagesArrayElem.y, job->numPages);
  158. if (job->common->show_boxes == nullptr)
  159. gvprintf(job, "gsave\n%d %d %d %d boxprim clip newpath\n",
  160. pbr.LL.x, pbr.LL.y, pbr.UR.x-pbr.LL.x, pbr.UR.y-pbr.LL.y);
  161. gvprintf(job, "%g %g set_scale %d rotate %g %g translate\n",
  162. job->scale.x, job->scale.y,
  163. job->rotation,
  164. job->translation.x, job->translation.y);
  165. // Define the size of the PS canvas
  166. if (job->render.id == FORMAT_PS2) {
  167. if (pbr.UR.x >= PDFMAX || pbr.UR.y >= PDFMAX)
  168. job->common->errorfn("canvas size (%d,%d) exceeds PDF limit (%d)\n"
  169. "\t(suggest setting a bounding box size, see dot(1))\n",
  170. pbr.UR.x, pbr.UR.y, PDFMAX);
  171. gvprintf(job, "[ /CropBox [%d %d %d %d] /PAGES pdfmark\n",
  172. pbr.LL.x, pbr.LL.y, pbr.UR.x, pbr.UR.y);
  173. }
  174. }
  175. static void lasi_end_page(GVJ_t * job)
  176. {
  177. if (job->common->show_boxes) {
  178. gvputs(job, "0 0 0 edgecolor\n");
  179. cat_libfile(job, nullptr, job->common->show_boxes + 1);
  180. }
  181. // the showpage is really a no-op, but at least one PS processor
  182. // out there needs to see this literal token. endpage does the real work.
  183. gvputs(job, "endpage\nshowpage\ngrestore\n");
  184. gvputs(job, "%%PageTrailer\n");
  185. gvprintf(job, "%%%%EndPage: %d\n", job->common->viewNum);
  186. }
  187. static void lasi_begin_cluster(GVJ_t * job)
  188. {
  189. obj_state_t *obj = job->obj;
  190. gvprintf(job, "%% %s\n", agnameof(obj->u.sg));
  191. gvputs(job, "gsave\n");
  192. }
  193. static void lasi_end_cluster(GVJ_t * job)
  194. {
  195. gvputs(job, "grestore\n");
  196. }
  197. static void lasi_begin_node(GVJ_t * job)
  198. {
  199. gvputs(job, "gsave\n");
  200. }
  201. static void lasi_end_node(GVJ_t * job)
  202. {
  203. gvputs(job, "grestore\n");
  204. }
  205. static void
  206. lasi_begin_edge(GVJ_t * job)
  207. {
  208. gvputs(job, "gsave\n");
  209. }
  210. static void lasi_end_edge(GVJ_t * job)
  211. {
  212. gvputs(job, "grestore\n");
  213. }
  214. static void lasi_begin_anchor(GVJ_t *job, char *url, char*, char*, char*)
  215. {
  216. obj_state_t *obj = job->obj;
  217. if (url && obj->url_map_p) {
  218. gvputs(job, "[ /Rect [ ");
  219. gvprintpointflist(job, obj->url_map_p, 2);
  220. gvputs(job, " ]\n");
  221. gvprintf(job, " /Border [ 0 0 0 ]\n"
  222. " /Action << /Subtype /URI /URI %s >>\n"
  223. " /Subtype /Link\n"
  224. "/ANN pdfmark\n",
  225. ps_string(url, CHAR_UTF8));
  226. }
  227. }
  228. static void ps_set_pen_style(GVJ_t *job)
  229. {
  230. double penwidth = job->obj->penwidth;
  231. char *p, *line, **s = job->obj->rawstyle;
  232. gvprintdouble(job, penwidth);
  233. gvputs(job," setlinewidth\n");
  234. while (s && (p = line = *s++)) {
  235. if (line == std::string{"setlinewidth"})
  236. continue;
  237. while (*p)
  238. p++;
  239. p++;
  240. while (*p) {
  241. gvprintf(job,"%s ", p);
  242. while (*p)
  243. p++;
  244. p++;
  245. }
  246. if (line == std::string{"invis"})
  247. job->obj->penwidth = 0;
  248. gvprintf(job, "%s\n", line);
  249. }
  250. }
  251. static void ps_set_color(GVJ_t *job, gvcolor_t *color)
  252. {
  253. const char *objtype;
  254. if (color) {
  255. switch (job->obj->type) {
  256. case ROOTGRAPH_OBJTYPE:
  257. case CLUSTER_OBJTYPE:
  258. objtype = "graph";
  259. break;
  260. case NODE_OBJTYPE:
  261. objtype = "node";
  262. break;
  263. case EDGE_OBJTYPE:
  264. objtype = "edge";
  265. break;
  266. default:
  267. objtype = "sethsb";
  268. break;
  269. }
  270. gvprintf(job, "%.3g %.3g %.3g %scolor\n",
  271. color->u.HSVA[0], color->u.HSVA[1], color->u.HSVA[2], objtype);
  272. }
  273. }
  274. static void lasi_textspan(GVJ_t * job, pointf p, textspan_t * span)
  275. {
  276. const char *font;
  277. const PangoFontDescription *pango_font;
  278. FontStretch stretch;
  279. FontStyle style;
  280. FontVariant variant;
  281. FontWeight weight;
  282. PostscriptAlias *pA;
  283. if (job->obj->pencolor.u.HSVA[3] < .5)
  284. return; // skip transparent text
  285. if (span->layout) {
  286. pango_font = pango_layout_get_font_description((PangoLayout*)(span->layout));
  287. font = pango_font_description_get_family(pango_font);
  288. switch (pango_font_description_get_stretch(pango_font)) {
  289. case PANGO_STRETCH_ULTRA_CONDENSED: stretch = ULTRACONDENSED; break;
  290. case PANGO_STRETCH_EXTRA_CONDENSED: stretch = EXTRACONDENSED; break;
  291. case PANGO_STRETCH_CONDENSED: stretch = CONDENSED; break;
  292. case PANGO_STRETCH_SEMI_CONDENSED: stretch = SEMICONDENSED; break;
  293. case PANGO_STRETCH_NORMAL: stretch = NORMAL_STRETCH; break;
  294. case PANGO_STRETCH_SEMI_EXPANDED: stretch = SEMIEXPANDED; break;
  295. case PANGO_STRETCH_EXPANDED: stretch = EXPANDED; break;
  296. case PANGO_STRETCH_EXTRA_EXPANDED: stretch = EXTRAEXPANDED; break;
  297. case PANGO_STRETCH_ULTRA_EXPANDED: stretch = ULTRAEXPANDED; break;
  298. default:
  299. UNREACHABLE();
  300. }
  301. switch (pango_font_description_get_style(pango_font)) {
  302. case PANGO_STYLE_NORMAL: style = NORMAL_STYLE; break;
  303. case PANGO_STYLE_OBLIQUE: style = OBLIQUE; break;
  304. case PANGO_STYLE_ITALIC: style = ITALIC; break;
  305. default:
  306. UNREACHABLE();
  307. }
  308. switch (pango_font_description_get_variant(pango_font)) {
  309. case PANGO_VARIANT_NORMAL: variant = NORMAL_VARIANT; break;
  310. case PANGO_VARIANT_SMALL_CAPS: variant = SMALLCAPS; break;
  311. #if PANGO_VERSION_CHECK(1, 50, 0)
  312. case PANGO_VARIANT_ALL_SMALL_CAPS: variant = SMALLCAPS; break;
  313. case PANGO_VARIANT_PETITE_CAPS: variant = SMALLCAPS; break;
  314. case PANGO_VARIANT_ALL_PETITE_CAPS: variant = SMALLCAPS; break;
  315. case PANGO_VARIANT_UNICASE: variant = SMALLCAPS; break;
  316. case PANGO_VARIANT_TITLE_CAPS: variant = SMALLCAPS; break;
  317. #endif
  318. default:
  319. UNREACHABLE();
  320. }
  321. switch (pango_font_description_get_weight(pango_font)) {
  322. #if PANGO_VERSION_CHECK(1, 24, 0)
  323. case PANGO_WEIGHT_THIN: weight = ULTRALIGHT; break; // no exact match in LASi
  324. #endif
  325. case PANGO_WEIGHT_ULTRALIGHT: weight = ULTRALIGHT; break;
  326. case PANGO_WEIGHT_LIGHT: weight = LIGHT; break;
  327. #if PANGO_VERSION_CHECK(1, 36, 7)
  328. case PANGO_WEIGHT_SEMILIGHT: weight = LIGHT; break; // no exact match in LASi
  329. #endif
  330. #if PANGO_VERSION_CHECK(1, 24, 0)
  331. case PANGO_WEIGHT_BOOK: weight = NORMAL_WEIGHT; break; // no exact match in LASi
  332. #endif
  333. case PANGO_WEIGHT_NORMAL: weight = NORMAL_WEIGHT; break;
  334. #if PANGO_VERSION_CHECK(1, 24, 0)
  335. case PANGO_WEIGHT_MEDIUM: weight = NORMAL_WEIGHT; break; // no exact match in LASi
  336. #endif
  337. case PANGO_WEIGHT_SEMIBOLD: weight = BOLD; break; /* no exact match in lasi */
  338. case PANGO_WEIGHT_BOLD: weight = BOLD; break;
  339. case PANGO_WEIGHT_ULTRABOLD: weight = ULTRABOLD; break;
  340. case PANGO_WEIGHT_HEAVY: weight = HEAVY; break;
  341. #if PANGO_VERSION_CHECK(1, 24, 0)
  342. case PANGO_WEIGHT_ULTRAHEAVY: weight = HEAVY; break; // no exact match in LASi
  343. #endif
  344. default:
  345. UNREACHABLE();
  346. }
  347. }
  348. else {
  349. pA = span->font->postscript_alias;
  350. font = pA->svg_font_family;
  351. stretch = NORMAL_STRETCH;
  352. if (pA->svg_font_style && pA->svg_font_style == std::string{"italic"})
  353. style = ITALIC;
  354. else
  355. style = NORMAL_STYLE;
  356. variant = NORMAL_VARIANT;
  357. if (pA->svg_font_weight && pA->svg_font_weight == std::string{"bold"})
  358. weight = BOLD;
  359. else
  360. weight = NORMAL_WEIGHT;
  361. }
  362. ps_set_color(job, &(job->obj->pencolor));
  363. Context *ctxt = reinterpret_cast<Context*>(job->context);
  364. ctxt->doc.osBody() << setFont(font, style, weight, variant, stretch) << setFontSize(span->font->size) << "\n";
  365. switch (span->just) {
  366. case 'r':
  367. p.x -= span->size.x;
  368. break;
  369. case 'l':
  370. p.x -= 0.0;
  371. break;
  372. case 'n':
  373. default:
  374. p.x -= span->size.x / 2.0;
  375. break;
  376. }
  377. p.y += span->yoffset_centerline;
  378. gvprintpointf(job, p);
  379. gvputs(job, " moveto ");
  380. ctxt->doc.osBody() << show(span->str) << "\n";
  381. }
  382. static void lasi_ellipse(GVJ_t * job, pointf * A, int filled)
  383. {
  384. // A[] contains 2 points: the center and corner.
  385. pointf AA[2];
  386. AA[0] = A[0];
  387. AA[1].x = A[1].x - A[0].x;
  388. AA[1].y = A[1].y - A[0].y;
  389. if (filled && job->obj->fillcolor.u.HSVA[3] > .5) {
  390. ps_set_color(job, &(job->obj->fillcolor));
  391. gvprintpointflist(job, AA, 2);
  392. gvputs(job, " ellipse_path fill\n");
  393. }
  394. if (job->obj->pencolor.u.HSVA[3] > .5) {
  395. ps_set_pen_style(job);
  396. ps_set_color(job, &(job->obj->pencolor));
  397. gvprintpointflist(job, AA, 2);
  398. gvputs(job, " ellipse_path stroke\n");
  399. }
  400. }
  401. static void lasi_bezier(GVJ_t *job, pointf *A, size_t n, int filled) {
  402. if (filled && job->obj->fillcolor.u.HSVA[3] > .5) {
  403. ps_set_color(job, &(job->obj->fillcolor));
  404. gvputs(job, "newpath ");
  405. gvprintpointf(job, A[0]);
  406. gvputs(job, " moveto\n");
  407. for (size_t j = 1; j < n; j += 3) {
  408. gvprintpointflist(job, &A[j], 3);
  409. gvputs(job, " curveto\n");
  410. }
  411. gvputs(job, "closepath fill\n");
  412. }
  413. if (job->obj->pencolor.u.HSVA[3] > .5) {
  414. ps_set_pen_style(job);
  415. ps_set_color(job, &(job->obj->pencolor));
  416. gvputs(job, "newpath ");
  417. gvprintpointf(job, A[0]);
  418. gvputs(job, " moveto\n");
  419. for (size_t j = 1; j < n; j += 3) {
  420. gvprintpointflist(job, &A[j], 3);
  421. gvputs(job, " curveto\n");
  422. }
  423. gvputs(job, "stroke\n");
  424. }
  425. }
  426. static void lasi_polygon(GVJ_t *job, pointf *A, size_t n, int filled) {
  427. if (filled && job->obj->fillcolor.u.HSVA[3] > .5) {
  428. ps_set_color(job, &(job->obj->fillcolor));
  429. gvputs(job, "newpath ");
  430. gvprintpointf(job, A[0]);
  431. gvputs(job, " moveto\n");
  432. for (size_t j = 1; j < n; j++) {
  433. gvprintpointf(job, A[j]);
  434. gvputs(job, " lineto\n");
  435. }
  436. gvputs(job, "closepath fill\n");
  437. }
  438. if (job->obj->pencolor.u.HSVA[3] > .5) {
  439. ps_set_pen_style(job);
  440. ps_set_color(job, &(job->obj->pencolor));
  441. gvputs(job, "newpath ");
  442. gvprintpointf(job, A[0]);
  443. gvputs(job, " moveto\n");
  444. for (size_t j = 1; j < n; j++) {
  445. gvprintpointf(job, A[j]);
  446. gvputs(job, " lineto\n");
  447. }
  448. gvputs(job, "closepath stroke\n");
  449. }
  450. }
  451. static void lasi_polyline(GVJ_t *job, pointf *A, size_t n) {
  452. if (job->obj->pencolor.u.HSVA[3] > .5) {
  453. ps_set_pen_style(job);
  454. ps_set_color(job, &(job->obj->pencolor));
  455. gvputs(job, "newpath ");
  456. gvprintpointf(job, A[0]);
  457. gvputs(job, " moveto\n");
  458. for (size_t j = 1; j < n; j++) {
  459. gvprintpointf(job, A[j]);
  460. gvputs(job, " lineto\n");
  461. }
  462. gvputs(job, "stroke\n");
  463. }
  464. }
  465. static void lasi_comment(GVJ_t * job, char *str)
  466. {
  467. gvputs(job, "% ");
  468. gvputs(job, str);
  469. gvputs(job, "\n");
  470. }
  471. static void lasi_library_shape(GVJ_t *job, char *name, pointf *A, size_t n,
  472. int filled) {
  473. if (filled && job->obj->fillcolor.u.HSVA[3] > .5) {
  474. ps_set_color(job, &(job->obj->fillcolor));
  475. gvputs(job, "[ ");
  476. gvprintpointflist(job, A, n);
  477. gvputs(job, " ");
  478. gvprintpointf(job, A[0]);
  479. gvprintf(job, " ] %" PRISIZE_T " true %s\n", n, name);
  480. }
  481. if (job->obj->pencolor.u.HSVA[3] > .5) {
  482. ps_set_pen_style(job);
  483. ps_set_color(job, &(job->obj->pencolor));
  484. gvputs(job, "[ ");
  485. gvprintpointflist(job, A, n);
  486. gvputs(job, " ");
  487. gvprintpointf(job, A[0]);
  488. gvprintf(job, " ] %" PRISIZE_T " false %s\n", n, name);
  489. }
  490. }
  491. static gvrender_engine_t lasi_engine = {
  492. lasi_begin_job,
  493. lasi_end_job,
  494. lasi_begin_graph,
  495. 0, // lasi_end_graph
  496. lasi_begin_layer,
  497. 0, // lasi_end_layer
  498. lasi_begin_page,
  499. lasi_end_page,
  500. lasi_begin_cluster,
  501. lasi_end_cluster,
  502. 0, // lasi_begin_nodes
  503. 0, // lasi_end_nodes
  504. 0, // lasi_begin_edges
  505. 0, // lasi_end_edges
  506. lasi_begin_node,
  507. lasi_end_node,
  508. lasi_begin_edge,
  509. lasi_end_edge,
  510. lasi_begin_anchor,
  511. 0, // lasi_end_anchor
  512. 0, // lasi_begin_label
  513. 0, // lasi_end_label
  514. lasi_textspan,
  515. 0, // lasi_resolve_color
  516. lasi_ellipse,
  517. lasi_polygon,
  518. lasi_bezier,
  519. lasi_polyline,
  520. lasi_comment,
  521. lasi_library_shape,
  522. };
  523. static gvrender_features_t render_features_lasi = {
  524. GVRENDER_DOES_TRANSFORM
  525. | GVRENDER_DOES_MAPS
  526. | GVRENDER_NO_WHITE_BG
  527. | GVRENDER_DOES_MAP_RECTANGLE,
  528. 4., // default pad - graph units
  529. nullptr, // knowncolors
  530. 0, // sizeof knowncolors
  531. HSVA_DOUBLE, // color_type
  532. };
  533. static gvdevice_features_t device_features_ps = {
  534. GVDEVICE_DOES_PAGES
  535. | GVDEVICE_DOES_LAYERS, // flags
  536. {36.,36.}, // default margin - points
  537. {612.,792.}, // default page width, height - points
  538. {72.,72.}, // default dpi
  539. };
  540. static gvdevice_features_t device_features_eps = {
  541. 0, // flags
  542. {36.,36.}, // default margin - points
  543. {612.,792.}, // default page width, height - points
  544. {72.,72.}, // default dpi
  545. };
  546. gvplugin_installed_t gvrender_lasi_types[] = {
  547. {FORMAT_PS, "lasi", -5, &lasi_engine, &render_features_lasi},
  548. {0, nullptr, 0, nullptr, nullptr}
  549. };
  550. gvplugin_installed_t gvdevice_lasi_types[] = {
  551. {FORMAT_PS, "ps:lasi", -5, nullptr, &device_features_ps},
  552. {FORMAT_PS2, "ps2:lasi", -5, nullptr, &device_features_ps},
  553. {FORMAT_EPS, "eps:lasi", -5, nullptr, &device_features_eps},
  554. {0, nullptr, 0, nullptr, nullptr}
  555. };