Mercurial > hgrepos > Python2 > PyMuPDF
comparison src_classic/helper-devices.i @ 1:1d09e1dec1d9 upstream
ADD: PyMuPDF v1.26.4: the original sdist.
It does not yet contain MuPDF. This normally will be downloaded when
building PyMuPDF.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:37:51 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 1:1d09e1dec1d9 |
|---|---|
| 1 %{ | |
| 2 /* | |
| 3 # ------------------------------------------------------------------------ | |
| 4 # Copyright 2020-2022, Harald Lieder, mailto:harald.lieder@outlook.com | |
| 5 # License: GNU AFFERO GPL 3.0, https://www.gnu.org/licenses/agpl-3.0.html | |
| 6 # | |
| 7 # Part of "PyMuPDF", a Python binding for "MuPDF" (http://mupdf.com), a | |
| 8 # lightweight PDF, XPS, and E-book viewer, renderer and toolkit which is | |
| 9 # maintained and developed by Artifex Software, Inc. https://artifex.com. | |
| 10 # ------------------------------------------------------------------------ | |
| 11 */ | |
| 12 typedef struct | |
| 13 { | |
| 14 fz_device super; | |
| 15 PyObject *out; | |
| 16 size_t seqno; | |
| 17 long depth; | |
| 18 int clips; | |
| 19 PyObject *method; | |
| 20 } jm_lineart_device; | |
| 21 | |
| 22 static PyObject *dev_pathdict = NULL; | |
| 23 static PyObject *scissors = NULL; | |
| 24 static float dev_linewidth = 0; // border width if present | |
| 25 static fz_matrix trace_device_ptm; // page transformation matrix | |
| 26 static fz_matrix trace_device_ctm; // trace device matrix | |
| 27 static fz_matrix trace_device_rot; | |
| 28 static fz_point dev_lastpoint = {0, 0}; | |
| 29 static fz_point dev_firstpoint = {0, 0}; | |
| 30 static int dev_havemove = 0; | |
| 31 static fz_rect dev_pathrect; | |
| 32 static float dev_pathfactor = 0; | |
| 33 static int dev_linecount = 0; | |
| 34 static char *layer_name=NULL; // optional content name | |
| 35 static int path_type = 0; // one of the following values: | |
| 36 #define FILL_PATH 1 | |
| 37 #define STROKE_PATH 2 | |
| 38 #define CLIP_PATH 3 | |
| 39 #define CLIP_STROKE_PATH 4 | |
| 40 | |
| 41 static void trace_device_reset() | |
| 42 { | |
| 43 Py_CLEAR(dev_pathdict); | |
| 44 Py_CLEAR(scissors); | |
| 45 layer_name = NULL; | |
| 46 dev_linewidth = 0; | |
| 47 trace_device_ptm = fz_identity; | |
| 48 trace_device_ctm = fz_identity; | |
| 49 trace_device_rot = fz_identity; | |
| 50 dev_lastpoint.x = 0; | |
| 51 dev_lastpoint.y = 0; | |
| 52 dev_firstpoint.x = 0; | |
| 53 dev_firstpoint.y = 0; | |
| 54 dev_pathrect.x0 = 0; | |
| 55 dev_pathrect.y0 = 0; | |
| 56 dev_pathrect.x1 = 0; | |
| 57 dev_pathrect.y1 = 0; | |
| 58 dev_pathfactor = 0; | |
| 59 dev_linecount = 0; | |
| 60 path_type = 0; | |
| 61 } | |
| 62 | |
| 63 // Every scissor of a clip is a sub rectangle of the preceeding clip | |
| 64 // scissor if the clip level is larger. | |
| 65 static fz_rect compute_scissor() | |
| 66 { | |
| 67 PyObject *last_scissor = NULL; | |
| 68 fz_rect scissor; | |
| 69 if (!scissors) { | |
| 70 scissors = PyList_New(0); | |
| 71 } | |
| 72 Py_ssize_t num_scissors = PyList_Size(scissors); | |
| 73 if (num_scissors > 0) { | |
| 74 last_scissor = PyList_GET_ITEM(scissors, num_scissors-1); | |
| 75 scissor = JM_rect_from_py(last_scissor); | |
| 76 scissor = fz_intersect_rect(scissor, dev_pathrect); | |
| 77 } else { | |
| 78 scissor = dev_pathrect; | |
| 79 } | |
| 80 LIST_APPEND_DROP(scissors, JM_py_from_rect(scissor)); | |
| 81 return scissor; | |
| 82 } | |
| 83 | |
| 84 | |
| 85 static void | |
| 86 jm_increase_seqno(fz_context *ctx, fz_device *dev_, ...) | |
| 87 { | |
| 88 jm_lineart_device *dev = (jm_lineart_device *) dev_; | |
| 89 dev->seqno += 1; | |
| 90 } | |
| 91 | |
| 92 /* | |
| 93 -------------------------------------------------------------------------- | |
| 94 Check whether the last 4 lines represent a quad. | |
| 95 Because of how we count, the lines are a polyline already, i.e. last point | |
| 96 of a line equals 1st point of next line. | |
| 97 So we check for a polygon (last line's end point equals start point). | |
| 98 If not true we return 0. | |
| 99 -------------------------------------------------------------------------- | |
| 100 */ | |
| 101 static int | |
| 102 jm_checkquad() | |
| 103 { | |
| 104 PyObject *items = PyDict_GetItem(dev_pathdict, dictkey_items); | |
| 105 Py_ssize_t i, len = PyList_Size(items); | |
| 106 float f[8]; // coordinates of the 4 corners | |
| 107 fz_point temp, lp; // line = (temp, lp) | |
| 108 PyObject *rect; | |
| 109 PyObject *line; | |
| 110 // fill the 8 floats in f, start from items[-4:] | |
| 111 for (i = 0; i < 4; i++) { // store line start points | |
| 112 line = PyList_GET_ITEM(items, len - 4 + i); | |
| 113 temp = JM_point_from_py(PyTuple_GET_ITEM(line, 1)); | |
| 114 f[i * 2] = temp.x; | |
| 115 f[i * 2 + 1] = temp.y; | |
| 116 lp = JM_point_from_py(PyTuple_GET_ITEM(line, 2)); | |
| 117 } | |
| 118 if (lp.x != f[0] || lp.y != f[1]) { | |
| 119 // not a polygon! | |
| 120 //dev_linecount -= 1; | |
| 121 return 0; | |
| 122 } | |
| 123 | |
| 124 // we have detected a quad | |
| 125 dev_linecount = 0; // reset this | |
| 126 // a quad item is ("qu", (ul, ur, ll, lr)), where the tuple items | |
| 127 // are pairs of floats representing a quad corner each. | |
| 128 rect = PyTuple_New(2); | |
| 129 PyTuple_SET_ITEM(rect, 0, PyUnicode_FromString("qu")); | |
| 130 /* ---------------------------------------------------- | |
| 131 * relationship of float array to quad points: | |
| 132 * (0, 1) = ul, (2, 3) = ll, (6, 7) = ur, (4, 5) = lr | |
| 133 ---------------------------------------------------- */ | |
| 134 fz_quad q = fz_make_quad(f[0], f[1], f[6], f[7], f[2], f[3], f[4], f[5]); | |
| 135 PyTuple_SET_ITEM(rect, 1, JM_py_from_quad(q)); | |
| 136 PyList_SetItem(items, len - 4, rect); // replace item -4 by rect | |
| 137 PyList_SetSlice(items, len - 3, len, NULL); // delete remaining 3 items | |
| 138 return 1; | |
| 139 } | |
| 140 | |
| 141 | |
| 142 /* | |
| 143 -------------------------------------------------------------------------- | |
| 144 Check whether the last 3 path items represent a rectangle. | |
| 145 Line 1 and 3 must be horizontal, line 2 must be vertical. | |
| 146 Returns 1 if we have modified the path, otherwise 0. | |
| 147 -------------------------------------------------------------------------- | |
| 148 */ | |
| 149 static int | |
| 150 jm_checkrect() | |
| 151 { | |
| 152 dev_linecount = 0; // reset line count | |
| 153 long orientation = 0; // area orientation of rectangle | |
| 154 fz_point ll, lr, ur, ul; | |
| 155 fz_rect r; | |
| 156 PyObject *rect; | |
| 157 PyObject *line0, *line2; | |
| 158 PyObject *items = PyDict_GetItem(dev_pathdict, dictkey_items); | |
| 159 Py_ssize_t len = PyList_Size(items); | |
| 160 | |
| 161 line0 = PyList_GET_ITEM(items, len - 3); | |
| 162 ll = JM_point_from_py(PyTuple_GET_ITEM(line0, 1)); | |
| 163 lr = JM_point_from_py(PyTuple_GET_ITEM(line0, 2)); | |
| 164 // no need to extract "line1"! | |
| 165 line2 = PyList_GET_ITEM(items, len - 1); | |
| 166 ur = JM_point_from_py(PyTuple_GET_ITEM(line2, 1)); | |
| 167 ul = JM_point_from_py(PyTuple_GET_ITEM(line2, 2)); | |
| 168 | |
| 169 /* | |
| 170 --------------------------------------------------------------------- | |
| 171 Assumption: | |
| 172 When decomposing rects, MuPDF always starts with a horizontal line, | |
| 173 followed by a vertical line, followed by a horizontal line. | |
| 174 First line: (ll, lr), third line: (ul, ur). | |
| 175 If 1st line is below 3rd line, we record anti-clockwise (+1), else | |
| 176 clockwise (-1) orientation. | |
| 177 --------------------------------------------------------------------- | |
| 178 */ | |
| 179 if (ll.y != lr.y || | |
| 180 ll.x != ul.x || | |
| 181 ur.y != ul.y || | |
| 182 ur.x != lr.x) { | |
| 183 goto drop_out; // not a rectangle | |
| 184 } | |
| 185 | |
| 186 // we have a rect, replace last 3 "l" items by one "re" item. | |
| 187 if (ul.y < lr.y) { | |
| 188 r = fz_make_rect(ul.x, ul.y, lr.x, lr.y); | |
| 189 orientation = 1; | |
| 190 } else { | |
| 191 r = fz_make_rect(ll.x, ll.y, ur.x, ur.y); | |
| 192 orientation = -1; | |
| 193 } | |
| 194 rect = PyTuple_New(3); | |
| 195 PyTuple_SET_ITEM(rect, 0, PyUnicode_FromString("re")); | |
| 196 PyTuple_SET_ITEM(rect, 1, JM_py_from_rect(r)); | |
| 197 PyTuple_SET_ITEM(rect, 2, PyLong_FromLong(orientation)); | |
| 198 PyList_SetItem(items, len - 3, rect); // replace item -3 by rect | |
| 199 PyList_SetSlice(items, len - 2, len, NULL); // delete remaining 2 items | |
| 200 return 1; | |
| 201 drop_out:; | |
| 202 return 0; | |
| 203 } | |
| 204 | |
| 205 static PyObject * | |
| 206 jm_lineart_color(fz_context *ctx, fz_colorspace *colorspace, const float *color) | |
| 207 { | |
| 208 float rgb[3]; | |
| 209 if (colorspace) { | |
| 210 fz_convert_color(ctx, colorspace, color, fz_device_rgb(ctx), | |
| 211 rgb, NULL, fz_default_color_params); | |
| 212 return Py_BuildValue("fff", rgb[0], rgb[1], rgb[2]); | |
| 213 } | |
| 214 return PyTuple_New(0); | |
| 215 } | |
| 216 | |
| 217 static void | |
| 218 trace_moveto(fz_context *ctx, void *dev_, float x, float y) | |
| 219 { | |
| 220 dev_lastpoint = fz_transform_point(fz_make_point(x, y), trace_device_ctm); | |
| 221 if (fz_is_infinite_rect(dev_pathrect)) { | |
| 222 dev_pathrect = fz_make_rect(dev_lastpoint.x, dev_lastpoint.y, | |
| 223 dev_lastpoint.x, dev_lastpoint.y); | |
| 224 } | |
| 225 dev_firstpoint = dev_lastpoint; | |
| 226 dev_havemove = 1; | |
| 227 dev_linecount = 0; // reset # of consec. lines | |
| 228 } | |
| 229 | |
| 230 static void | |
| 231 trace_lineto(fz_context *ctx, void *dev_, float x, float y) | |
| 232 { | |
| 233 fz_point p1 = fz_transform_point(fz_make_point(x, y), trace_device_ctm); | |
| 234 dev_pathrect = fz_include_point_in_rect(dev_pathrect, p1); | |
| 235 PyObject *list = PyTuple_New(3); | |
| 236 PyTuple_SET_ITEM(list, 0, PyUnicode_FromString("l")); | |
| 237 PyTuple_SET_ITEM(list, 1, JM_py_from_point(dev_lastpoint)); | |
| 238 PyTuple_SET_ITEM(list, 2, JM_py_from_point(p1)); | |
| 239 dev_lastpoint = p1; | |
| 240 PyObject *items = PyDict_GetItem(dev_pathdict, dictkey_items); | |
| 241 LIST_APPEND_DROP(items, list); | |
| 242 dev_linecount += 1; // counts consecutive lines | |
| 243 if (dev_linecount == 4 && path_type != FILL_PATH) { // shrink to "re" or "qu" item | |
| 244 jm_checkquad(); | |
| 245 } | |
| 246 } | |
| 247 | |
| 248 static void | |
| 249 trace_curveto(fz_context *ctx, void *dev_, float x1, float y1, float x2, float y2, float x3, float y3) | |
| 250 { | |
| 251 dev_linecount = 0; // reset # of consec. lines | |
| 252 fz_point p1 = fz_make_point(x1, y1); | |
| 253 fz_point p2 = fz_make_point(x2, y2); | |
| 254 fz_point p3 = fz_make_point(x3, y3); | |
| 255 p1 = fz_transform_point(p1, trace_device_ctm); | |
| 256 p2 = fz_transform_point(p2, trace_device_ctm); | |
| 257 p3 = fz_transform_point(p3, trace_device_ctm); | |
| 258 dev_pathrect = fz_include_point_in_rect(dev_pathrect, p1); | |
| 259 dev_pathrect = fz_include_point_in_rect(dev_pathrect, p2); | |
| 260 dev_pathrect = fz_include_point_in_rect(dev_pathrect, p3); | |
| 261 | |
| 262 PyObject *list = PyTuple_New(5); | |
| 263 PyTuple_SET_ITEM(list, 0, PyUnicode_FromString("c")); | |
| 264 PyTuple_SET_ITEM(list, 1, JM_py_from_point(dev_lastpoint)); | |
| 265 PyTuple_SET_ITEM(list, 2, JM_py_from_point(p1)); | |
| 266 PyTuple_SET_ITEM(list, 3, JM_py_from_point(p2)); | |
| 267 PyTuple_SET_ITEM(list, 4, JM_py_from_point(p3)); | |
| 268 dev_lastpoint = p3; | |
| 269 PyObject *items = PyDict_GetItem(dev_pathdict, dictkey_items); | |
| 270 LIST_APPEND_DROP(items, list); | |
| 271 } | |
| 272 | |
| 273 static void | |
| 274 trace_close(fz_context *ctx, void *dev_) | |
| 275 { | |
| 276 if (dev_linecount == 3) { | |
| 277 if (jm_checkrect()) { | |
| 278 return; | |
| 279 } | |
| 280 } | |
| 281 dev_linecount = 0; // reset # of consec. lines | |
| 282 if (dev_havemove) { | |
| 283 if (dev_firstpoint.x != dev_lastpoint.x || dev_firstpoint.y != dev_lastpoint.y) { | |
| 284 PyObject *list = PyTuple_New(3); | |
| 285 PyTuple_SET_ITEM(list, 0, PyUnicode_FromString("l")); | |
| 286 PyTuple_SET_ITEM(list, 1, JM_py_from_point(dev_lastpoint)); | |
| 287 PyTuple_SET_ITEM(list, 2, JM_py_from_point(dev_firstpoint)); | |
| 288 dev_lastpoint = dev_firstpoint; | |
| 289 PyObject *items = PyDict_GetItem(dev_pathdict, dictkey_items); | |
| 290 LIST_APPEND_DROP(items, list); | |
| 291 } | |
| 292 dev_havemove = 0; | |
| 293 DICT_SETITEMSTR_DROP(dev_pathdict, "closePath", JM_BOOL(0)); | |
| 294 } else { | |
| 295 DICT_SETITEMSTR_DROP(dev_pathdict, "closePath", JM_BOOL(1)); | |
| 296 } | |
| 297 } | |
| 298 | |
| 299 static const fz_path_walker trace_path_walker = | |
| 300 { | |
| 301 trace_moveto, | |
| 302 trace_lineto, | |
| 303 trace_curveto, | |
| 304 trace_close | |
| 305 }; | |
| 306 | |
| 307 /* | |
| 308 --------------------------------------------------------------------- | |
| 309 Create the "items" list of the path dictionary | |
| 310 * either create or empty the path dictionary | |
| 311 * reset the end point of the path | |
| 312 * reset count of consecutive lines | |
| 313 * invoke fz_walk_path(), which create the single items | |
| 314 * if no items detected, empty path dict again | |
| 315 --------------------------------------------------------------------- | |
| 316 */ | |
| 317 static void | |
| 318 jm_lineart_path(fz_context *ctx, jm_lineart_device *dev, const fz_path *path) | |
| 319 { | |
| 320 dev_pathrect = fz_infinite_rect; | |
| 321 dev_linecount = 0; | |
| 322 dev_lastpoint = fz_make_point(0, 0); | |
| 323 if (dev_pathdict) { | |
| 324 Py_CLEAR(dev_pathdict); | |
| 325 } | |
| 326 dev_pathdict = PyDict_New(); | |
| 327 DICT_SETITEM_DROP(dev_pathdict, dictkey_items, PyList_New(0)); | |
| 328 fz_walk_path(ctx, path, &trace_path_walker, dev); | |
| 329 // Check if any items were added ... | |
| 330 if (!PyDict_GetItem(dev_pathdict, dictkey_items) || !PyList_Size(PyDict_GetItem(dev_pathdict, dictkey_items))) { | |
| 331 Py_CLEAR(dev_pathdict); | |
| 332 } | |
| 333 } | |
| 334 | |
| 335 //--------------------------------------------------------------------------- | |
| 336 // Append current path to list or merge into last path of the list. | |
| 337 // (1) Append if first path, different item lists or not a 'stroke' version | |
| 338 // of previous path | |
| 339 // (2) If new path has the same items, merge its content into previous path | |
| 340 // and change path["type"] to "fs". | |
| 341 // (3) If "out" is callable, skip the previous and pass dictionary to it. | |
| 342 //--------------------------------------------------------------------------- | |
| 343 static void | |
| 344 jm_append_merge(PyObject *out, PyObject *method) | |
| 345 { | |
| 346 if (PyCallable_Check(out) || method != Py_None) { // function or method | |
| 347 goto callback; | |
| 348 } | |
| 349 Py_ssize_t len = PyList_Size(out); // len of output list so far | |
| 350 if (len == 0) { // always append first path | |
| 351 goto append; | |
| 352 } | |
| 353 const char *thistype = PyUnicode_AsUTF8(PyDict_GetItem(dev_pathdict, dictkey_type)); | |
| 354 if (strcmp(thistype, "s") != 0) { // if not stroke, then append | |
| 355 goto append; | |
| 356 } | |
| 357 PyObject *prev = PyList_GET_ITEM(out, len - 1); // get prev path | |
| 358 const char *prevtype = PyUnicode_AsUTF8(PyDict_GetItem(prev, dictkey_type)); | |
| 359 if (strcmp(prevtype, "f") != 0) { // if previous not fill, append | |
| 360 goto append; | |
| 361 } | |
| 362 // last check: there must be the same list of items for "f" and "s". | |
| 363 PyObject *previtems = PyDict_GetItem(prev, dictkey_items); | |
| 364 PyObject *thisitems = PyDict_GetItem(dev_pathdict, dictkey_items); | |
| 365 if (PyObject_RichCompareBool(previtems, thisitems, Py_NE)) { | |
| 366 goto append; | |
| 367 } | |
| 368 int rc = PyDict_Merge(prev, dev_pathdict, 0); // merge with no override | |
| 369 if (rc == 0) { | |
| 370 DICT_SETITEM_DROP(prev, dictkey_type, PyUnicode_FromString("fs")); | |
| 371 goto postappend; | |
| 372 } else { | |
| 373 PySys_WriteStderr("could not merge stroke and fill path"); | |
| 374 goto append; | |
| 375 } | |
| 376 append:; | |
| 377 PyList_Append(out, dev_pathdict); | |
| 378 postappend:; | |
| 379 Py_CLEAR(dev_pathdict); | |
| 380 return; | |
| 381 | |
| 382 callback:; // callback function or method | |
| 383 PyObject *resp = NULL; | |
| 384 if (method == Py_None) { | |
| 385 resp = PyObject_CallFunctionObjArgs(out, dev_pathdict, NULL); | |
| 386 } else { | |
| 387 resp = PyObject_CallMethodObjArgs(out, method, dev_pathdict, NULL); | |
| 388 } | |
| 389 if (resp) { | |
| 390 Py_DECREF(resp); | |
| 391 } else { | |
| 392 PySys_WriteStderr("calling cdrawings callback function/method failed!"); | |
| 393 PyErr_Clear(); | |
| 394 } | |
| 395 Py_CLEAR(dev_pathdict); | |
| 396 return; | |
| 397 } | |
| 398 | |
| 399 | |
| 400 static void | |
| 401 jm_lineart_fill_path(fz_context *ctx, fz_device *dev_, const fz_path *path, | |
| 402 int even_odd, fz_matrix ctm, fz_colorspace *colorspace, | |
| 403 const float *color, float alpha, fz_color_params color_params) | |
| 404 { | |
| 405 jm_lineart_device *dev = (jm_lineart_device *) dev_; | |
| 406 PyObject *out = dev->out; | |
| 407 trace_device_ctm = ctm; //fz_concat(ctm, trace_device_ptm); | |
| 408 path_type = FILL_PATH; | |
| 409 jm_lineart_path(ctx, dev, path); | |
| 410 if (!dev_pathdict) { | |
| 411 return; | |
| 412 } | |
| 413 DICT_SETITEM_DROP(dev_pathdict, dictkey_type, PyUnicode_FromString("f")); | |
| 414 DICT_SETITEMSTR_DROP(dev_pathdict, "even_odd", JM_BOOL(even_odd)); | |
| 415 DICT_SETITEMSTR_DROP(dev_pathdict, "fill_opacity", Py_BuildValue("f", alpha)); | |
| 416 DICT_SETITEMSTR_DROP(dev_pathdict, "fill", jm_lineart_color(ctx, colorspace, color)); | |
| 417 DICT_SETITEM_DROP(dev_pathdict, dictkey_rect, JM_py_from_rect(dev_pathrect)); | |
| 418 DICT_SETITEMSTR_DROP(dev_pathdict, "seqno", PyLong_FromSize_t(dev->seqno)); | |
| 419 DICT_SETITEMSTR_DROP(dev_pathdict, "layer", JM_UnicodeFromStr(layer_name)); | |
| 420 if (dev->clips) { | |
| 421 DICT_SETITEMSTR_DROP(dev_pathdict, "level", PyLong_FromLong(dev->depth)); | |
| 422 } | |
| 423 jm_append_merge(out, dev->method); | |
| 424 dev->seqno += 1; | |
| 425 } | |
| 426 | |
| 427 static void | |
| 428 jm_lineart_stroke_path(fz_context *ctx, fz_device *dev_, const fz_path *path, | |
| 429 const fz_stroke_state *stroke, fz_matrix ctm, | |
| 430 fz_colorspace *colorspace, const float *color, float alpha, | |
| 431 fz_color_params color_params) | |
| 432 { | |
| 433 jm_lineart_device *dev = (jm_lineart_device *)dev_; | |
| 434 PyObject *out = dev->out; | |
| 435 int i; | |
| 436 dev_pathfactor = 1; | |
| 437 if (fz_abs(ctm.a) == fz_abs(ctm.d)) { | |
| 438 dev_pathfactor = fz_abs(ctm.a); | |
| 439 } | |
| 440 trace_device_ctm = ctm; // fz_concat(ctm, trace_device_ptm); | |
| 441 path_type = STROKE_PATH; | |
| 442 | |
| 443 jm_lineart_path(ctx, dev, path); | |
| 444 if (!dev_pathdict) { | |
| 445 return; | |
| 446 } | |
| 447 DICT_SETITEM_DROP(dev_pathdict, dictkey_type, PyUnicode_FromString("s")); | |
| 448 DICT_SETITEMSTR_DROP(dev_pathdict, "stroke_opacity", Py_BuildValue("f", alpha)); | |
| 449 DICT_SETITEMSTR_DROP(dev_pathdict, "color", jm_lineart_color(ctx, colorspace, color)); | |
| 450 DICT_SETITEM_DROP(dev_pathdict, dictkey_width, Py_BuildValue("f", dev_pathfactor * stroke->linewidth)); | |
| 451 DICT_SETITEMSTR_DROP(dev_pathdict, "lineCap", Py_BuildValue("iii", stroke->start_cap, stroke->dash_cap, stroke->end_cap)); | |
| 452 DICT_SETITEMSTR_DROP(dev_pathdict, "lineJoin", Py_BuildValue("f", dev_pathfactor * stroke->linejoin)); | |
| 453 if (!PyDict_GetItemString(dev_pathdict, "closePath")) { | |
| 454 DICT_SETITEMSTR_DROP(dev_pathdict, "closePath", JM_BOOL(0)); | |
| 455 } | |
| 456 | |
| 457 // output the "dashes" string | |
| 458 if (stroke->dash_len) { | |
| 459 fz_buffer *buff = fz_new_buffer(ctx, 256); | |
| 460 fz_append_string(ctx, buff, "[ "); // left bracket | |
| 461 for (i = 0; i < stroke->dash_len; i++) { | |
| 462 fz_append_printf(ctx, buff, "%g ", dev_pathfactor * stroke->dash_list[i]); | |
| 463 } | |
| 464 fz_append_printf(ctx, buff, "] %g", dev_pathfactor * stroke->dash_phase); | |
| 465 DICT_SETITEMSTR_DROP(dev_pathdict, "dashes", JM_EscapeStrFromBuffer(ctx, buff)); | |
| 466 fz_drop_buffer(ctx, buff); | |
| 467 } else { | |
| 468 DICT_SETITEMSTR_DROP(dev_pathdict, "dashes", PyUnicode_FromString("[] 0")); | |
| 469 } | |
| 470 | |
| 471 DICT_SETITEM_DROP(dev_pathdict, dictkey_rect, JM_py_from_rect(dev_pathrect)); | |
| 472 DICT_SETITEMSTR_DROP(dev_pathdict, "layer", JM_UnicodeFromStr(layer_name)); | |
| 473 DICT_SETITEMSTR_DROP(dev_pathdict, "seqno", PyLong_FromSize_t(dev->seqno)); | |
| 474 if (dev->clips) { | |
| 475 DICT_SETITEMSTR_DROP(dev_pathdict, "level", PyLong_FromLong(dev->depth)); | |
| 476 } | |
| 477 // output the dict - potentially merging it with a previous fill_path twin | |
| 478 jm_append_merge(out, dev->method); | |
| 479 dev->seqno += 1; | |
| 480 } | |
| 481 | |
| 482 static void | |
| 483 jm_lineart_clip_path(fz_context *ctx, fz_device *dev_, const fz_path *path, int even_odd, fz_matrix ctm, fz_rect scissor) | |
| 484 { | |
| 485 jm_lineart_device *dev = (jm_lineart_device *)dev_; | |
| 486 if (!dev->clips) return; | |
| 487 PyObject *out = dev->out; | |
| 488 trace_device_ctm = ctm; //fz_concat(ctm, trace_device_ptm); | |
| 489 path_type = CLIP_PATH; | |
| 490 jm_lineart_path(ctx, dev, path); | |
| 491 if (!dev_pathdict) { | |
| 492 return; | |
| 493 } | |
| 494 DICT_SETITEM_DROP(dev_pathdict, dictkey_type, PyUnicode_FromString("clip")); | |
| 495 DICT_SETITEMSTR_DROP(dev_pathdict, "even_odd", JM_BOOL(even_odd)); | |
| 496 if (!PyDict_GetItemString(dev_pathdict, "closePath")) { | |
| 497 DICT_SETITEMSTR_DROP(dev_pathdict, "closePath", JM_BOOL(0)); | |
| 498 } | |
| 499 DICT_SETITEMSTR_DROP(dev_pathdict, "scissor", JM_py_from_rect(compute_scissor())); | |
| 500 DICT_SETITEMSTR_DROP(dev_pathdict, "level", PyLong_FromLong(dev->depth)); | |
| 501 DICT_SETITEMSTR_DROP(dev_pathdict, "layer", JM_UnicodeFromStr(layer_name)); | |
| 502 jm_append_merge(out, dev->method); | |
| 503 dev->depth++; | |
| 504 } | |
| 505 | |
| 506 static void | |
| 507 jm_lineart_clip_stroke_path(fz_context *ctx, fz_device *dev_, const fz_path *path, const fz_stroke_state *stroke, fz_matrix ctm, fz_rect scissor) | |
| 508 { | |
| 509 jm_lineart_device *dev = (jm_lineart_device *)dev_; | |
| 510 if (!dev->clips) return; | |
| 511 PyObject *out = dev->out; | |
| 512 trace_device_ctm = ctm; //fz_concat(ctm, trace_device_ptm); | |
| 513 path_type = CLIP_STROKE_PATH; | |
| 514 jm_lineart_path(ctx, dev, path); | |
| 515 if (!dev_pathdict) { | |
| 516 return; | |
| 517 } | |
| 518 DICT_SETITEM_DROP(dev_pathdict, dictkey_type, PyUnicode_FromString("clip")); | |
| 519 DICT_SETITEMSTR_DROP(dev_pathdict, "even_odd", Py_BuildValue("s", NULL)); | |
| 520 if (!PyDict_GetItemString(dev_pathdict, "closePath")) { | |
| 521 DICT_SETITEMSTR_DROP(dev_pathdict, "closePath", JM_BOOL(0)); | |
| 522 } | |
| 523 DICT_SETITEMSTR_DROP(dev_pathdict, "scissor", JM_py_from_rect(compute_scissor())); | |
| 524 DICT_SETITEMSTR_DROP(dev_pathdict, "level", PyLong_FromLong(dev->depth)); | |
| 525 DICT_SETITEMSTR_DROP(dev_pathdict, "layer", JM_UnicodeFromStr(layer_name)); | |
| 526 jm_append_merge(out, dev->method); | |
| 527 dev->depth++; | |
| 528 } | |
| 529 | |
| 530 static void | |
| 531 jm_lineart_clip_stroke_text(fz_context *ctx, fz_device *dev_, const fz_text *text, const fz_stroke_state *stroke, fz_matrix ctm, fz_rect scissor) | |
| 532 { | |
| 533 jm_lineart_device *dev = (jm_lineart_device *)dev_; | |
| 534 if (!dev->clips) return; | |
| 535 PyObject *out = dev->out; | |
| 536 compute_scissor(); | |
| 537 dev->depth++; | |
| 538 } | |
| 539 | |
| 540 static void | |
| 541 jm_lineart_clip_text(fz_context *ctx, fz_device *dev_, const fz_text *text, fz_matrix ctm, fz_rect scissor) | |
| 542 { | |
| 543 jm_lineart_device *dev = (jm_lineart_device *)dev_; | |
| 544 if (!dev->clips) return; | |
| 545 PyObject *out = dev->out; | |
| 546 compute_scissor(); | |
| 547 dev->depth++; | |
| 548 } | |
| 549 | |
| 550 static void | |
| 551 jm_lineart_clip_image_mask(fz_context *ctx, fz_device *dev_, fz_image *image, fz_matrix ctm, fz_rect scissor) | |
| 552 { | |
| 553 jm_lineart_device *dev = (jm_lineart_device *)dev_; | |
| 554 if (!dev->clips) return; | |
| 555 PyObject *out = dev->out; | |
| 556 compute_scissor(); | |
| 557 dev->depth++; | |
| 558 } | |
| 559 | |
| 560 static void | |
| 561 jm_lineart_pop_clip(fz_context *ctx, fz_device *dev_) | |
| 562 { | |
| 563 jm_lineart_device *dev = (jm_lineart_device *)dev_; | |
| 564 if (!dev->clips) return; | |
| 565 if (!scissors) return; | |
| 566 Py_ssize_t len = PyList_Size(scissors); | |
| 567 if (len < 1) return; | |
| 568 PyList_SetSlice(scissors, len - 1, len, NULL); | |
| 569 dev->depth--; | |
| 570 } | |
| 571 | |
| 572 | |
| 573 static void | |
| 574 jm_lineart_begin_layer(fz_context *ctx, fz_device *dev_, const char *name) | |
| 575 { | |
| 576 layer_name = fz_strdup(ctx, name); | |
| 577 } | |
| 578 | |
| 579 static void | |
| 580 jm_lineart_end_layer(fz_context *ctx, fz_device *dev_) | |
| 581 { | |
| 582 fz_free(ctx, layer_name); | |
| 583 layer_name = NULL; | |
| 584 } | |
| 585 | |
| 586 static void | |
| 587 jm_lineart_begin_group(fz_context *ctx, fz_device *dev_, fz_rect bbox, fz_colorspace *cs, int isolated, int knockout, int blendmode, float alpha) | |
| 588 { | |
| 589 jm_lineart_device *dev = (jm_lineart_device *)dev_; | |
| 590 if (!dev->clips) return; | |
| 591 PyObject *out = dev->out; | |
| 592 dev_pathdict = Py_BuildValue("{s:s,s:N,s:N,s:N,s:s,s:f,s:i,s:N}", | |
| 593 "type", "group", | |
| 594 "rect", JM_py_from_rect(bbox), | |
| 595 "isolated", JM_BOOL(isolated), | |
| 596 "knockout", JM_BOOL(knockout), | |
| 597 "blendmode", fz_blendmode_name(blendmode), | |
| 598 "opacity", alpha, | |
| 599 "level", dev->depth, | |
| 600 "layer", JM_UnicodeFromStr(layer_name) | |
| 601 ); | |
| 602 jm_append_merge(out, dev->method); | |
| 603 dev->depth++; | |
| 604 } | |
| 605 | |
| 606 static void | |
| 607 jm_lineart_end_group(fz_context *ctx, fz_device *dev_) | |
| 608 { | |
| 609 jm_lineart_device *dev = (jm_lineart_device *)dev_; | |
| 610 if (!dev->clips) return; | |
| 611 dev->depth--; | |
| 612 } | |
| 613 | |
| 614 | |
| 615 static void | |
| 616 jm_dev_linewidth(fz_context *ctx, fz_device *dev_, const fz_path *path, const fz_stroke_state *stroke, fz_matrix ctm, fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params) | |
| 617 { | |
| 618 dev_linewidth = stroke->linewidth; | |
| 619 jm_increase_seqno(ctx, dev_); | |
| 620 } | |
| 621 | |
| 622 | |
| 623 static void | |
| 624 jm_trace_text_span(fz_context *ctx, PyObject *out, fz_text_span *span, int type, fz_matrix ctm, fz_colorspace *colorspace, const float *color, float alpha, size_t seqno) | |
| 625 { | |
| 626 fz_font *out_font = NULL; | |
| 627 int i; | |
| 628 const char *fontname = JM_font_name(ctx, span->font); | |
| 629 float rgb[3]; | |
| 630 PyObject *chars = PyTuple_New(span->len); | |
| 631 fz_matrix mat = fz_concat(span->trm, ctm); // text transformation matrix | |
| 632 fz_point dir = fz_transform_vector(fz_make_point(1, 0), mat); // writing direction | |
| 633 double fsize = sqrt(dir.x * dir.x + dir.y * dir.y); | |
| 634 | |
| 635 dir = fz_normalize_vector(dir); | |
| 636 double linewidth, adv, asc, dsc; | |
| 637 double space_adv = 0; | |
| 638 float x0, y0, x1, y1; | |
| 639 asc = (double) JM_font_ascender(ctx, span->font); | |
| 640 dsc = (double) JM_font_descender(ctx, span->font); | |
| 641 if (asc < 1e-3) { // probably Tesseract font | |
| 642 dsc = -0.1; | |
| 643 asc = 0.9; | |
| 644 } | |
| 645 // compute effective ascender / descender | |
| 646 double ascsize = asc * fsize / (asc - dsc); | |
| 647 double dscsize = dsc * fsize / (asc - dsc); | |
| 648 | |
| 649 int fflags = 0; // font flags | |
| 650 int mono = fz_font_is_monospaced(ctx, span->font); | |
| 651 fflags += mono * TEXT_FONT_MONOSPACED; | |
| 652 fflags += fz_font_is_italic(ctx, span->font) * TEXT_FONT_ITALIC; | |
| 653 fflags += fz_font_is_serif(ctx, span->font) * TEXT_FONT_SERIFED; | |
| 654 fflags += fz_font_is_bold(ctx, span->font) * TEXT_FONT_BOLD; | |
| 655 | |
| 656 if (dev_linewidth > 0) { // width of character border | |
| 657 linewidth = (double) dev_linewidth; | |
| 658 } else { | |
| 659 linewidth = fsize * 0.05; // default: 5% of font size | |
| 660 } | |
| 661 fz_point char_orig; | |
| 662 double last_adv = 0; | |
| 663 | |
| 664 // walk through characters of span | |
| 665 fz_rect span_bbox; | |
| 666 fz_matrix rot = fz_make_matrix(dir.x, dir.y, -dir.y, dir.x, 0, 0); | |
| 667 if (dir.x == -1) { // left-right flip | |
| 668 rot.d = 1; | |
| 669 } | |
| 670 | |
| 671 //PySys_WriteStdout("mat: (%g, %g, %g, %g)\n", mat.a, mat.b, mat.c, mat.d); | |
| 672 //PySys_WriteStdout("rot: (%g, %g, %g, %g)\n", rot.a, rot.b, rot.c, rot.d); | |
| 673 | |
| 674 for (i = 0; i < span->len; i++) { | |
| 675 adv = 0; | |
| 676 if (span->items[i].gid >= 0) { | |
| 677 adv = (double) fz_advance_glyph(ctx, span->font, span->items[i].gid, span->wmode); | |
| 678 } | |
| 679 adv *= fsize; | |
| 680 last_adv = adv; | |
| 681 if (span->items[i].ucs == 32) { | |
| 682 space_adv = adv; | |
| 683 } | |
| 684 char_orig = fz_make_point(span->items[i].x, span->items[i].y); | |
| 685 char_orig = fz_transform_point(char_orig, ctm); | |
| 686 fz_matrix m1 = fz_make_matrix(1, 0, 0, 1, -char_orig.x, -char_orig.y); | |
| 687 m1 = fz_concat(m1, rot); | |
| 688 m1 = fz_concat(m1, fz_make_matrix(1, 0, 0, 1, char_orig.x, char_orig.y)); | |
| 689 x0 = char_orig.x; | |
| 690 x1 = x0 + adv; | |
| 691 if (mat.d > 0 && (dir.x == 1 || dir.x == -1) || | |
| 692 mat.b !=0 && mat.b == -mat.c) { // up-down flip | |
| 693 y0 = char_orig.y + dscsize; | |
| 694 y1 = char_orig.y + ascsize; | |
| 695 } else { | |
| 696 y0 = char_orig.y - ascsize; | |
| 697 y1 = char_orig.y - dscsize; | |
| 698 } | |
| 699 fz_rect char_bbox = fz_make_rect(x0, y0, x1, y1); | |
| 700 char_bbox = fz_transform_rect(char_bbox, m1); | |
| 701 PyTuple_SET_ITEM(chars, (Py_ssize_t) i, Py_BuildValue("ii(ff)(ffff)", | |
| 702 span->items[i].ucs, span->items[i].gid, | |
| 703 char_orig.x, char_orig.y, char_bbox.x0, char_bbox.y0, char_bbox.x1, char_bbox.y1)); | |
| 704 if (i > 0) { | |
| 705 span_bbox = fz_union_rect(span_bbox, char_bbox); | |
| 706 } else { | |
| 707 span_bbox = char_bbox; | |
| 708 } | |
| 709 } | |
| 710 if (!space_adv) { | |
| 711 if (!mono) { | |
| 712 space_adv = fz_advance_glyph(ctx, span->font, | |
| 713 fz_encode_character_with_fallback(ctx, span->font, 32, 0, 0, &out_font), | |
| 714 span->wmode); | |
| 715 space_adv *= fsize; | |
| 716 if (!space_adv) { | |
| 717 space_adv = last_adv; | |
| 718 } | |
| 719 } else { | |
| 720 space_adv = last_adv; // for mono, any char width suffices | |
| 721 } | |
| 722 } | |
| 723 // make the span dictionary | |
| 724 PyObject *span_dict = PyDict_New(); | |
| 725 DICT_SETITEMSTR_DROP(span_dict, "dir", JM_py_from_point(dir)); | |
| 726 DICT_SETITEM_DROP(span_dict, dictkey_font, JM_EscapeStrFromStr(fontname)); | |
| 727 DICT_SETITEM_DROP(span_dict, dictkey_wmode, PyLong_FromLong((long) span->wmode)); | |
| 728 DICT_SETITEM_DROP(span_dict, dictkey_flags, PyLong_FromLong((long) fflags)); | |
| 729 DICT_SETITEMSTR_DROP(span_dict, "bidi_lvl", PyLong_FromLong((long) span->bidi_level)); | |
| 730 DICT_SETITEMSTR_DROP(span_dict, "bidi_dir", PyLong_FromLong((long) span->markup_dir)); | |
| 731 DICT_SETITEM_DROP(span_dict, dictkey_ascender, PyFloat_FromDouble(asc)); | |
| 732 DICT_SETITEM_DROP(span_dict, dictkey_descender, PyFloat_FromDouble(dsc)); | |
| 733 DICT_SETITEM_DROP(span_dict, dictkey_colorspace, PyLong_FromLong(3)); | |
| 734 | |
| 735 if (colorspace) { | |
| 736 fz_convert_color(ctx, colorspace, color, fz_device_rgb(ctx), | |
| 737 rgb, NULL, fz_default_color_params); | |
| 738 } else { | |
| 739 rgb[0] = rgb[1] = rgb[2] = 0; | |
| 740 } | |
| 741 | |
| 742 DICT_SETITEM_DROP(span_dict, dictkey_color, Py_BuildValue("fff", rgb[0], rgb[1], rgb[2])); | |
| 743 DICT_SETITEM_DROP(span_dict, dictkey_size, PyFloat_FromDouble(fsize)); | |
| 744 DICT_SETITEMSTR_DROP(span_dict, "opacity", PyFloat_FromDouble((double) alpha)); | |
| 745 DICT_SETITEMSTR_DROP(span_dict, "linewidth", PyFloat_FromDouble((double) linewidth)); | |
| 746 DICT_SETITEMSTR_DROP(span_dict, "spacewidth", PyFloat_FromDouble(space_adv)); | |
| 747 DICT_SETITEM_DROP(span_dict, dictkey_type, PyLong_FromLong((long) type)); | |
| 748 DICT_SETITEM_DROP(span_dict, dictkey_bbox, JM_py_from_rect(span_bbox)); | |
| 749 DICT_SETITEMSTR_DROP(span_dict, "layer", JM_UnicodeFromStr(layer_name)); | |
| 750 DICT_SETITEMSTR_DROP(span_dict, "seqno", PyLong_FromSize_t(seqno)); | |
| 751 DICT_SETITEM_DROP(span_dict, dictkey_chars, chars); | |
| 752 LIST_APPEND_DROP(out, span_dict); | |
| 753 } | |
| 754 | |
| 755 static void | |
| 756 jm_trace_text(fz_context *ctx, PyObject *out, const fz_text *text, int type, fz_matrix ctm, fz_colorspace *colorspace, const float *color, float alpha, size_t seqno) | |
| 757 { | |
| 758 fz_text_span *span; | |
| 759 for (span = text->head; span; span = span->next) | |
| 760 jm_trace_text_span(ctx, out, span, type, ctm, colorspace, color, alpha, seqno); | |
| 761 } | |
| 762 | |
| 763 /*--------------------------------------------------------- | |
| 764 There are 3 text trace types: | |
| 765 0 - fill text (PDF Tr 0) | |
| 766 1 - stroke text (PDF Tr 1) | |
| 767 3 - ignore text (PDF Tr 3) | |
| 768 ---------------------------------------------------------*/ | |
| 769 static void | |
| 770 jm_lineart_fill_text(fz_context *ctx, fz_device *dev_, const fz_text *text, fz_matrix ctm, fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params) | |
| 771 { | |
| 772 jm_lineart_device *dev = (jm_lineart_device *)dev_; | |
| 773 PyObject *out = dev->out; | |
| 774 jm_trace_text(ctx, out, text, 0, ctm, colorspace, color, alpha, dev->seqno); | |
| 775 dev->seqno += 1; | |
| 776 } | |
| 777 | |
| 778 static void | |
| 779 jm_lineart_stroke_text(fz_context *ctx, fz_device *dev_, const fz_text *text, const fz_stroke_state *stroke, fz_matrix ctm, fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params) | |
| 780 { | |
| 781 jm_lineart_device *dev = (jm_lineart_device *)dev_; | |
| 782 PyObject *out = dev->out; | |
| 783 jm_trace_text(ctx, out, text, 1, ctm, colorspace, color, alpha, dev->seqno); | |
| 784 dev->seqno += 1; | |
| 785 } | |
| 786 | |
| 787 | |
| 788 static void | |
| 789 jm_lineart_ignore_text(fz_context *ctx, fz_device *dev_, const fz_text *text, fz_matrix ctm) | |
| 790 { | |
| 791 jm_lineart_device *dev = (jm_lineart_device *)dev_; | |
| 792 PyObject *out = dev->out; | |
| 793 jm_trace_text(ctx, out, text, 3, ctm, NULL, NULL, 1, dev->seqno); | |
| 794 dev->seqno += 1; | |
| 795 } | |
| 796 | |
| 797 static void jm_lineart_drop_device(fz_context *ctx, fz_device *dev_) | |
| 798 { | |
| 799 jm_lineart_device *dev = (jm_lineart_device *)dev_; | |
| 800 if (PyList_Check(dev->out)) { | |
| 801 Py_CLEAR(dev->out); | |
| 802 } | |
| 803 Py_CLEAR(dev->method); | |
| 804 Py_CLEAR(scissors); | |
| 805 } | |
| 806 | |
| 807 //------------------------------------------------------------------- | |
| 808 // LINEART device for Python method Page.get_cdrawings() | |
| 809 //------------------------------------------------------------------- | |
| 810 fz_device *JM_new_lineart_device(fz_context *ctx, PyObject *out, int clips, PyObject *method) | |
| 811 { | |
| 812 jm_lineart_device *dev = fz_new_derived_device(ctx, jm_lineart_device); | |
| 813 | |
| 814 dev->super.close_device = NULL; | |
| 815 dev->super.drop_device = jm_lineart_drop_device; | |
| 816 dev->super.fill_path = jm_lineart_fill_path; | |
| 817 dev->super.stroke_path = jm_lineart_stroke_path; | |
| 818 dev->super.clip_path = jm_lineart_clip_path; | |
| 819 dev->super.clip_stroke_path = jm_lineart_clip_stroke_path; | |
| 820 | |
| 821 dev->super.fill_text = jm_increase_seqno; | |
| 822 dev->super.stroke_text = jm_increase_seqno; | |
| 823 dev->super.clip_text = jm_lineart_clip_text; | |
| 824 dev->super.clip_stroke_text = jm_lineart_clip_stroke_text; | |
| 825 dev->super.ignore_text = jm_increase_seqno; | |
| 826 | |
| 827 dev->super.fill_shade = jm_increase_seqno; | |
| 828 dev->super.fill_image = jm_increase_seqno; | |
| 829 dev->super.fill_image_mask = jm_increase_seqno; | |
| 830 dev->super.clip_image_mask = jm_lineart_clip_image_mask; | |
| 831 | |
| 832 dev->super.pop_clip = jm_lineart_pop_clip; | |
| 833 | |
| 834 dev->super.begin_mask = NULL; | |
| 835 dev->super.end_mask = NULL; | |
| 836 dev->super.begin_group = jm_lineart_begin_group; | |
| 837 dev->super.end_group = jm_lineart_end_group; | |
| 838 | |
| 839 dev->super.begin_tile = NULL; | |
| 840 dev->super.end_tile = NULL; | |
| 841 | |
| 842 dev->super.begin_layer = jm_lineart_begin_layer; | |
| 843 dev->super.end_layer = jm_lineart_end_layer; | |
| 844 | |
| 845 dev->super.begin_structure = NULL; | |
| 846 dev->super.end_structure = NULL; | |
| 847 | |
| 848 dev->super.begin_metatext = NULL; | |
| 849 dev->super.end_metatext = NULL; | |
| 850 | |
| 851 dev->super.render_flags = NULL; | |
| 852 dev->super.set_default_colorspaces = NULL; | |
| 853 | |
| 854 if (PyList_Check(out)) { | |
| 855 Py_INCREF(out); | |
| 856 } | |
| 857 Py_INCREF(method); | |
| 858 dev->out = out; | |
| 859 dev->seqno = 0; | |
| 860 dev->depth = 0; | |
| 861 dev->clips = clips; | |
| 862 dev->method = method; | |
| 863 trace_device_reset(); | |
| 864 return (fz_device *)dev; | |
| 865 } | |
| 866 | |
| 867 //------------------------------------------------------------------- | |
| 868 // Trace TEXT device for Python method Page.get_texttrace() | |
| 869 //------------------------------------------------------------------- | |
| 870 fz_device *JM_new_texttrace_device(fz_context *ctx, PyObject *out) | |
| 871 { | |
| 872 jm_lineart_device *dev = fz_new_derived_device(ctx, jm_lineart_device); | |
| 873 | |
| 874 dev->super.close_device = NULL; | |
| 875 dev->super.drop_device = jm_lineart_drop_device; | |
| 876 dev->super.fill_path = jm_increase_seqno; | |
| 877 dev->super.stroke_path = jm_dev_linewidth; | |
| 878 dev->super.clip_path = NULL; | |
| 879 dev->super.clip_stroke_path = NULL; | |
| 880 | |
| 881 dev->super.fill_text = jm_lineart_fill_text; | |
| 882 dev->super.stroke_text = jm_lineart_stroke_text; | |
| 883 dev->super.clip_text = NULL; | |
| 884 dev->super.clip_stroke_text = NULL; | |
| 885 dev->super.ignore_text = jm_lineart_ignore_text; | |
| 886 | |
| 887 dev->super.fill_shade = jm_increase_seqno; | |
| 888 dev->super.fill_image = jm_increase_seqno; | |
| 889 dev->super.fill_image_mask = jm_increase_seqno; | |
| 890 dev->super.clip_image_mask = NULL; | |
| 891 | |
| 892 dev->super.pop_clip = NULL; | |
| 893 | |
| 894 dev->super.begin_mask = NULL; | |
| 895 dev->super.end_mask = NULL; | |
| 896 dev->super.begin_group = NULL; | |
| 897 dev->super.end_group = NULL; | |
| 898 | |
| 899 dev->super.begin_tile = NULL; | |
| 900 dev->super.end_tile = NULL; | |
| 901 | |
| 902 dev->super.begin_layer = jm_lineart_begin_layer; | |
| 903 dev->super.end_layer = jm_lineart_end_layer; | |
| 904 | |
| 905 dev->super.begin_structure = NULL; | |
| 906 dev->super.end_structure = NULL; | |
| 907 | |
| 908 dev->super.begin_metatext = NULL; | |
| 909 dev->super.end_metatext = NULL; | |
| 910 | |
| 911 dev->super.render_flags = NULL; | |
| 912 dev->super.set_default_colorspaces = NULL; | |
| 913 | |
| 914 if (PyList_Check(out)) { | |
| 915 Py_XINCREF(out); | |
| 916 } | |
| 917 dev->out = out; | |
| 918 dev->seqno = 0; | |
| 919 dev->depth = 0; | |
| 920 dev->clips = 0; | |
| 921 dev->method = NULL; | |
| 922 trace_device_reset(); | |
| 923 | |
| 924 return (fz_device *)dev; | |
| 925 } | |
| 926 | |
| 927 //------------------------------------------------------------------- | |
| 928 // BBOX device | |
| 929 //------------------------------------------------------------------- | |
| 930 typedef struct jm_bbox_device_s | |
| 931 { | |
| 932 fz_device super; | |
| 933 PyObject *result; | |
| 934 int layers; | |
| 935 } jm_bbox_device; | |
| 936 | |
| 937 static void | |
| 938 jm_bbox_add_rect(fz_context *ctx, fz_device *dev, fz_rect rect, char *code) | |
| 939 { | |
| 940 jm_bbox_device *bdev = (jm_bbox_device *)dev; | |
| 941 if (!bdev->layers) { | |
| 942 LIST_APPEND_DROP(bdev->result, Py_BuildValue("sN", code, JM_py_from_rect(rect))); | |
| 943 } else { | |
| 944 LIST_APPEND_DROP(bdev->result, Py_BuildValue("sNN", code, JM_py_from_rect(rect), JM_UnicodeFromStr(layer_name))); | |
| 945 } | |
| 946 } | |
| 947 | |
| 948 static void | |
| 949 jm_bbox_fill_path(fz_context *ctx, fz_device *dev, const fz_path *path, int even_odd, fz_matrix ctm, | |
| 950 fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params) | |
| 951 { | |
| 952 jm_bbox_add_rect(ctx, dev, fz_bound_path(ctx, path, NULL, ctm), "fill-path"); | |
| 953 } | |
| 954 | |
| 955 static void | |
| 956 jm_bbox_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, const fz_stroke_state *stroke, | |
| 957 fz_matrix ctm, fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params) | |
| 958 { | |
| 959 jm_bbox_add_rect(ctx, dev, fz_bound_path(ctx, path, stroke, ctm), "stroke-path"); | |
| 960 } | |
| 961 | |
| 962 static void | |
| 963 jm_bbox_fill_text(fz_context *ctx, fz_device *dev, const fz_text *text, fz_matrix ctm, ...) | |
| 964 { | |
| 965 jm_bbox_add_rect(ctx, dev, fz_bound_text(ctx, text, NULL, ctm), "fill-text"); | |
| 966 } | |
| 967 | |
| 968 static void | |
| 969 jm_bbox_ignore_text(fz_context *ctx, fz_device *dev, const fz_text *text, fz_matrix ctm) | |
| 970 { | |
| 971 jm_bbox_add_rect(ctx, dev, fz_bound_text(ctx, text, NULL, ctm), "ignore-text"); | |
| 972 } | |
| 973 | |
| 974 static void | |
| 975 jm_bbox_stroke_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_stroke_state *stroke, fz_matrix ctm, ...) | |
| 976 { | |
| 977 jm_bbox_add_rect(ctx, dev, fz_bound_text(ctx, text, stroke, ctm), "stroke-text"); | |
| 978 } | |
| 979 | |
| 980 static void | |
| 981 jm_bbox_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shade, fz_matrix ctm, float alpha, fz_color_params color_params) | |
| 982 { | |
| 983 jm_bbox_add_rect(ctx, dev, fz_bound_shade(ctx, shade, ctm), "fill-shade"); | |
| 984 } | |
| 985 | |
| 986 static void | |
| 987 jm_bbox_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, fz_matrix ctm, float alpha, fz_color_params color_params) | |
| 988 { | |
| 989 jm_bbox_add_rect(ctx, dev, fz_transform_rect(fz_unit_rect, ctm), "fill-image"); | |
| 990 } | |
| 991 | |
| 992 static void | |
| 993 jm_bbox_fill_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, fz_matrix ctm, | |
| 994 fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params) | |
| 995 { | |
| 996 jm_bbox_add_rect(ctx, dev, fz_transform_rect(fz_unit_rect, ctm), "fill-imgmask"); | |
| 997 } | |
| 998 | |
| 999 fz_device * | |
| 1000 JM_new_bbox_device(fz_context *ctx, PyObject *result, int layers) | |
| 1001 { | |
| 1002 jm_bbox_device *dev = fz_new_derived_device(ctx, jm_bbox_device); | |
| 1003 | |
| 1004 dev->super.fill_path = jm_bbox_fill_path; | |
| 1005 dev->super.stroke_path = jm_bbox_stroke_path; | |
| 1006 dev->super.clip_path = NULL; | |
| 1007 dev->super.clip_stroke_path = NULL; | |
| 1008 | |
| 1009 dev->super.fill_text = jm_bbox_fill_text; | |
| 1010 dev->super.stroke_text = jm_bbox_stroke_text; | |
| 1011 dev->super.clip_text = NULL; | |
| 1012 dev->super.clip_stroke_text = NULL; | |
| 1013 dev->super.ignore_text = jm_bbox_ignore_text; | |
| 1014 | |
| 1015 dev->super.fill_shade = jm_bbox_fill_shade; | |
| 1016 dev->super.fill_image = jm_bbox_fill_image; | |
| 1017 dev->super.fill_image_mask = jm_bbox_fill_image_mask; | |
| 1018 dev->super.clip_image_mask = NULL; | |
| 1019 | |
| 1020 dev->super.pop_clip = NULL; | |
| 1021 | |
| 1022 dev->super.begin_mask = NULL; | |
| 1023 dev->super.end_mask = NULL; | |
| 1024 dev->super.begin_group = NULL; | |
| 1025 dev->super.end_group = NULL; | |
| 1026 | |
| 1027 dev->super.begin_tile = NULL; | |
| 1028 dev->super.end_tile = NULL; | |
| 1029 | |
| 1030 dev->super.begin_layer = jm_lineart_begin_layer; | |
| 1031 dev->super.end_layer = jm_lineart_end_layer; | |
| 1032 | |
| 1033 dev->super.begin_structure = NULL; | |
| 1034 dev->super.end_structure = NULL; | |
| 1035 | |
| 1036 dev->super.begin_metatext = NULL; | |
| 1037 dev->super.end_metatext = NULL; | |
| 1038 | |
| 1039 dev->super.render_flags = NULL; | |
| 1040 dev->super.set_default_colorspaces = NULL; | |
| 1041 | |
| 1042 dev->result = result; | |
| 1043 dev->layers = layers; | |
| 1044 trace_device_reset(); | |
| 1045 | |
| 1046 return (fz_device *)dev; | |
| 1047 } | |
| 1048 | |
| 1049 %} |
