static void xps_draw_linear_gradient(fz_context *ctx, xps_document *doc, const fz_matrix *ctm, const fz_rect *area, struct stop *stops, int count, fz_xml *root, int spread) { float x0, y0, x1, y1; int i, mi, ma; float dx, dy, x, y, k; fz_point p1, p2; fz_matrix inv; fz_rect local_area = *area; char *start_point_att = fz_xml_att(root, "StartPoint"); char *end_point_att = fz_xml_att(root, "EndPoint"); x0 = y0 = 0; x1 = y1 = 1; if (start_point_att) xps_parse_point(ctx, doc, start_point_att, &x0, &y0); if (end_point_att) xps_parse_point(ctx, doc, end_point_att, &x1, &y1); p1.x = x0; p1.y = y0; p2.x = x1; p2.y = y1; fz_transform_rect(&local_area, fz_invert_matrix(&inv, ctm)); x = p2.x - p1.x; y = p2.y - p1.y; k = ((local_area.x0 - p1.x) * x + (local_area.y0 - p1.y) * y) / (x * x + y * y); mi = floorf(k); ma = ceilf(k); k = ((local_area.x1 - p1.x) * x + (local_area.y0 - p1.y) * y) / (x * x + y * y); mi = fz_mini(mi, floorf(k)); ma = fz_maxi(ma, ceilf(k)); k = ((local_area.x0 - p1.x) * x + (local_area.y1 - p1.y) * y) / (x * x + y * y); mi = fz_mini(mi, floorf(k)); ma = fz_maxi(ma, ceilf(k)); k = ((local_area.x1 - p1.x) * x + (local_area.y1 - p1.y) * y) / (x * x + y * y); mi = fz_mini(mi, floorf(k)); ma = fz_maxi(ma, ceilf(k)); dx = x1 - x0; dy = y1 - y0; if (spread == SPREAD_REPEAT) { for (i = mi; i < ma; i++) xps_draw_one_linear_gradient(ctx, doc, ctm, stops, count, 0, x0 + i * dx, y0 + i * dy, x1 + i * dx, y1 + i * dy); } else if (spread == SPREAD_REFLECT) { if ((mi % 2) != 0) mi--; for (i = mi; i < ma; i += 2) { xps_draw_one_linear_gradient(ctx, doc, ctm, stops, count, 0, x0 + i * dx, y0 + i * dy, x1 + i * dx, y1 + i * dy); xps_draw_one_linear_gradient(ctx, doc, ctm, stops, count, 0, x0 + (i + 2) * dx, y0 + (i + 2) * dy, x1 + i * dx, y1 + i * dy); } } else { xps_draw_one_linear_gradient(ctx, doc, ctm, stops, count, 1, x0, y0, x1, y1); } }
fz_bbox fz_union_bbox(fz_bbox a, fz_bbox b) { fz_bbox r; /* Check for empty box before infinite box */ if (fz_is_empty_rect(a)) return b; if (fz_is_empty_rect(b)) return a; if (fz_is_infinite_rect(a)) return a; if (fz_is_infinite_rect(b)) return b; r.x0 = fz_mini(a.x0, b.x0); r.y0 = fz_mini(a.y0, b.y0); r.x1 = fz_maxi(a.x1, b.x1); r.y1 = fz_maxi(a.y1, b.y1); return r; }
fz_bbox fz_intersect_bbox(fz_bbox a, fz_bbox b) { fz_bbox r; /* Check for empty box before infinite box */ if (fz_is_empty_rect(a)) return fz_empty_bbox; if (fz_is_empty_rect(b)) return fz_empty_bbox; if (fz_is_infinite_rect(a)) return b; if (fz_is_infinite_rect(b)) return a; r.x0 = fz_maxi(a.x0, b.x0); r.y0 = fz_maxi(a.y0, b.y0); r.x1 = fz_mini(a.x1, b.x1); r.y1 = fz_mini(a.y1, b.y1); return (r.x1 < r.x0 || r.y1 < r.y0) ? fz_empty_bbox : r; }
static void split_block(fz_context *ctx, fz_text_page *page, int block_num, int linenum) { int split_len; fz_text_block *block, *block2; if (page->len == page->cap) { int new_cap = fz_maxi(16, page->cap * 2); page->blocks = fz_resize_array(ctx, page->blocks, new_cap, sizeof(*page->blocks)); page->cap = new_cap; } memmove(page->blocks+block_num+1, page->blocks+block_num, (page->len - block_num)*sizeof(*page->blocks)); page->len++; block2 = fz_malloc_struct(ctx, fz_text_block); block = page->blocks[block_num].u.text; page->blocks[block_num+1].type = FZ_PAGE_BLOCK_TEXT; page->blocks[block_num+1].u.text = block2; split_len = block->len - linenum; block2->bbox = block->bbox; /* FIXME! */ block2->cap = 0; block2->len = 0; block2->lines = NULL; block2->lines = fz_malloc_array(ctx, split_len, sizeof(fz_text_line)); block2->cap = block2->len; block2->len = split_len; block->len = linenum; memcpy(block2->lines, block->lines + linenum, split_len * sizeof(fz_text_line)); block2->lines[0].distance = 0; }
static void split_block(fz_context *ctx, fz_text_page *page, int blocknum, int linenum) { int split_len; if (page->len == page->cap) { int new_cap = fz_maxi(16, page->cap * 2); page->blocks = fz_resize_array(ctx, page->blocks, new_cap, sizeof(*page->blocks)); page->cap = new_cap; } memmove(page->blocks+blocknum+1, page->blocks+blocknum, (page->len - blocknum)*sizeof(*page->blocks)); page->len++; split_len = page->blocks[blocknum].len - linenum; page->blocks[blocknum+1].bbox = page->blocks[blocknum].bbox; /* FIXME! */ page->blocks[blocknum+1].cap = 0; page->blocks[blocknum+1].len = 0; page->blocks[blocknum+1].lines = NULL; page->blocks[blocknum+1].lines = fz_malloc_array(ctx, split_len, sizeof(fz_text_line)); page->blocks[blocknum+1].cap = page->blocks[blocknum+1].len; page->blocks[blocknum+1].len = split_len; page->blocks[blocknum].len = linenum; memcpy(page->blocks[blocknum+1].lines, page->blocks[blocknum].lines + linenum, split_len * sizeof(fz_text_line)); }
static void append_line(fz_context *ctx, fz_text_block *block, fz_text_line *line) { if (block->len == block->cap) { int new_cap = fz_maxi(16, block->cap * 2); block->lines = fz_resize_array(ctx, block->lines, new_cap, sizeof *block->lines); block->cap = new_cap; } fz_union_rect(&block->bbox, &line->bbox); block->lines[block->len++] = *line; }
static void push_cmd(fz_context *ctx, fz_path *path, int cmd) { if (path->cmd_len + 1 >= path->cmd_cap) { int new_cmd_cap = fz_maxi(16, path->cmd_cap * 2); path->cmds = fz_resize_array(ctx, path->cmds, new_cmd_cap, sizeof(unsigned char)); path->cmd_cap = new_cmd_cap; } path->cmds[path->cmd_len++] = cmd; path->last_cmd = cmd; }
static void append_span(fz_context *ctx, fz_text_line *line, fz_text_span *span) { if (span->len == 0) return; if (line->len == line->cap) { int new_cap = fz_maxi(8, line->cap * 2); line->spans = fz_resize_array(ctx, line->spans, new_cap, sizeof(*line->spans)); line->cap = new_cap; } fz_union_rect(&line->bbox, &span->bbox); line->spans[line->len++] = *span; }
static void append_char(fz_context *ctx, fz_text_span *span, int c, fz_rect bbox) { if (span->len == span->cap) { int new_cap = fz_maxi(64, span->cap * 2); span->text = fz_resize_array(ctx, span->text, new_cap, sizeof(*span->text)); span->cap = new_cap; } fz_union_rect(&span->bbox, &bbox); span->text[span->len].c = c; span->text[span->len].bbox = bbox; span->len++; }
/* cf. http://code.google.com/p/sumatrapdf/issues/detail?id=1333 */ static fz_pixmap * decomp_image_banded(fz_context *ctx, fz_stream *stm, fz_image *image, int indexed, int l2factor, int native_l2factor) { fz_pixmap *tile = NULL, *part = NULL; int w = (image->w + (1 << l2factor) - 1) >> l2factor; int h = (image->h + (1 << l2factor) - 1) >> l2factor; int part_h, orig_h = image->h; int band = 1 << fz_maxi(8, l2factor); fz_var(tile); fz_var(part); fz_try(ctx) { fz_colorspace *cs = image->colorspace; if (indexed) cs = *(fz_colorspace **)cs->data; // cf. struct indexed in res_colorspace.c tile = fz_new_pixmap(ctx, cs, w, h); tile->interpolate = image->interpolate; tile->has_alpha = 0; /* SumatraPDF: allow optimizing non-alpha pixmaps */ /* decompress the image in bands of 256 lines */ for (part_h = h; part_h > 0; part_h -= band >> l2factor) { image->h = part_h > band >> l2factor ? band : ((orig_h - 1) % band) + 1; part = fz_decomp_image_from_stream(ctx, fz_keep_stream(stm), image, -1 - indexed, l2factor, native_l2factor); memcpy(tile->samples + (h - part_h) * tile->w * tile->n, part->samples, part->h * part->w * part->n); tile->has_alpha |= part->has_alpha; /* SumatraPDF: allow optimizing non-alpha pixmaps */ fz_drop_pixmap(ctx, part); part = NULL; } /* cf. http://bugs.ghostscript.com/show_bug.cgi?id=693517 */ if (image->usecolorkey && image->mask) fz_unblend_masked_tile(ctx, tile, image); } fz_always(ctx) { image->h = orig_h; fz_close(stm); } fz_catch(ctx) { fz_drop_pixmap(ctx, part); fz_drop_pixmap(ctx, tile); fz_rethrow(ctx); } return tile; }
static void push_coord(fz_context *ctx, fz_path *path, float x, float y) { if (path->coord_len + 2 >= path->coord_cap) { int new_coord_cap = fz_maxi(32, path->coord_cap * 2); path->coords = fz_resize_array(ctx, path->coords, new_coord_cap, sizeof(float)); path->coord_cap = new_coord_cap; } path->coords[path->coord_len++] = x; path->coords[path->coord_len++] = y; path->current.x = x; path->current.y = y; }
static fz_text_block * lookup_block_for_line(fz_context *ctx, fz_text_page *page, fz_text_line *line) { float size = line->len > 0 && line->spans[0].len > 0 ? line->spans[0].style->size : 1; int i; /* SumatraPDF: TODO: tweak heuristic below */ if (page->len > 0) return page->blocks; for (i = 0; i < page->len; i++) { fz_text_block *block = page->blocks + i; float w = block->bbox.x1 - block->bbox.x0; float dx = line->bbox.x0 - block->bbox.x0; float dy = line->bbox.y0 - block->bbox.y1; if (dy > -size * 1.5f && dy < size * PARAGRAPH_DIST) if (line->bbox.x0 <= block->bbox.x1 && line->bbox.x1 >= block->bbox.x0) if (fz_abs(dx) < w / 2) return block; } if (page->len == page->cap) { int new_cap = fz_maxi(16, page->cap * 2); page->blocks = fz_resize_array(ctx, page->blocks, new_cap, sizeof(*page->blocks)); page->cap = new_cap; } page->blocks[page->len].bbox = fz_empty_rect; page->blocks[page->len].len = 0; page->blocks[page->len].cap = 0; page->blocks[page->len].lines = NULL; return &page->blocks[page->len++]; }
static void xps_draw_radial_gradient(fz_context *ctx, xps_document *doc, const fz_matrix *ctm, const fz_rect *area, struct stop *stops, int count, fz_xml *root, int spread) { float x0, y0, r0; float x1, y1, r1; float xrad = 1; float yrad = 1; float invscale; int i, ma = 1; fz_matrix local_ctm = *ctm; fz_matrix inv; fz_rect local_area = *area; char *center_att = fz_xml_att(root, "Center"); char *origin_att = fz_xml_att(root, "GradientOrigin"); char *radius_x_att = fz_xml_att(root, "RadiusX"); char *radius_y_att = fz_xml_att(root, "RadiusY"); x0 = y0 = 0.0; x1 = y1 = 1.0; xrad = 1.0; yrad = 1.0; if (origin_att) xps_parse_point(ctx, doc, origin_att, &x0, &y0); if (center_att) xps_parse_point(ctx, doc, center_att, &x1, &y1); if (radius_x_att) xrad = fz_atof(radius_x_att); if (radius_y_att) yrad = fz_atof(radius_y_att); xrad = fz_max(0.01f, xrad); yrad = fz_max(0.01f, yrad); /* scale the ctm to make ellipses */ if (fz_abs(xrad) > FLT_EPSILON) { fz_pre_scale(&local_ctm, 1, yrad/xrad); } if (yrad != 0.0) { invscale = xrad / yrad; y0 = y0 * invscale; y1 = y1 * invscale; } r0 = 0; r1 = xrad; fz_transform_rect(&local_area, fz_invert_matrix(&inv, &local_ctm)); ma = fz_maxi(ma, ceilf(hypotf(local_area.x0 - x0, local_area.y0 - y0) / xrad)); ma = fz_maxi(ma, ceilf(hypotf(local_area.x1 - x0, local_area.y0 - y0) / xrad)); ma = fz_maxi(ma, ceilf(hypotf(local_area.x0 - x0, local_area.y1 - y0) / xrad)); ma = fz_maxi(ma, ceilf(hypotf(local_area.x1 - x0, local_area.y1 - y0) / xrad)); if (spread == SPREAD_REPEAT) { for (i = ma - 1; i >= 0; i--) xps_draw_one_radial_gradient(ctx, doc, &local_ctm, stops, count, 0, x0, y0, r0 + i * xrad, x1, y1, r1 + i * xrad); } else if (spread == SPREAD_REFLECT) { if ((ma % 2) != 0) ma++; for (i = ma - 2; i >= 0; i -= 2) { xps_draw_one_radial_gradient(ctx, doc, &local_ctm, stops, count, 0, x0, y0, r0 + i * xrad, x1, y1, r1 + i * xrad); xps_draw_one_radial_gradient(ctx, doc, &local_ctm, stops, count, 0, x0, y0, r0 + (i + 2) * xrad, x1, y1, r1 + i * xrad); } } else { xps_draw_one_radial_gradient(ctx, doc, &local_ctm, stops, count, 1, x0, y0, r0, x1, y1, r1); } }
static void do_app(void) { if (ui.key == KEY_F4 && ui.mod == GLFW_MOD_ALT) quit(); if (ui.down || ui.middle || ui.right || ui.key) showinfo = 0; if (!ui.focus && ui.key) { switch (ui.key) { case 'q': quit(); break; case 'm': if (number == 0) push_history(); else if (number > 0 && number < nelem(marks)) marks[number] = currentpage; break; case 't': if (number == 0) { if (history_count > 0) pop_history(); } else if (number > 0 && number < nelem(marks)) { jump_to_page(marks[number]); } break; case 'T': if (number == 0) { if (future_count > 0) pop_future(); } break; case 'N': search_dir = -1; if (search_hit_page == currentpage) search_page = currentpage + search_dir; else search_page = currentpage; if (search_page >= 0 && search_page < fz_count_pages(ctx, doc)) { search_hit_page = -1; if (search_needle) search_active = 1; } break; case 'n': search_dir = 1; if (search_hit_page == currentpage) search_page = currentpage + search_dir; else search_page = currentpage; if (search_page >= 0 && search_page < fz_count_pages(ctx, doc)) { search_hit_page = -1; if (search_needle) search_active = 1; } break; case 'f': toggle_fullscreen(); break; case 'w': shrinkwrap(); break; case 'r': reload(); break; case 'o': toggle_outline(); break; case 'W': auto_zoom_w(); break; case 'H': auto_zoom_h(); break; case 'Z': auto_zoom(); break; case 'z': currentzoom = number > 0 ? number : DEFRES; break; case '<': currentpage -= 10 * fz_maxi(number, 1); break; case '>': currentpage += 10 * fz_maxi(number, 1); break; case ',': case KEY_PAGE_UP: currentpage -= fz_maxi(number, 1); break; case '.': case KEY_PAGE_DOWN: currentpage += fz_maxi(number, 1); break; case 'b': number = fz_maxi(number, 1); while (number--) smart_move_backward(); break; case ' ': number = fz_maxi(number, 1); while (number--) smart_move_forward(); break; case 'g': jump_to_page(number - 1); break; case 'G': jump_to_page(fz_count_pages(ctx, doc) - 1); break; case '+': currentzoom = zoom_in(currentzoom); break; case '-': currentzoom = zoom_out(currentzoom); break; case '[': currentrotate += 90; break; case ']': currentrotate -= 90; break; case 'l': showlinks = !showlinks; break; case 'i': showinfo = !showinfo; break; case '/': search_dir = 1; showsearch = 1; search_input.p = search_input.text; search_input.q = search_input.end; break; case '?': search_dir = -1; showsearch = 1; search_input.p = search_input.text; search_input.q = search_input.end; break; case KEY_UP: scroll_y -= 10; break; case KEY_DOWN: scroll_y += 10; break; case KEY_LEFT: scroll_x -= 10; break; case KEY_RIGHT: scroll_x += 10; break; } if (ui.key >= '0' && ui.key <= '9') number = number * 10 + ui.key - '0'; else number = 0; currentpage = fz_clampi(currentpage, 0, fz_count_pages(ctx, doc) - 1); currentzoom = fz_clamp(currentzoom, MINRES, MAXRES); while (currentrotate < 0) currentrotate += 360; while (currentrotate >= 360) currentrotate -= 360; if (search_hit_page != currentpage) search_hit_page = -1; /* clear highlights when navigating */ ui_needs_update = 1; ui.key = 0; /* we ate the key event, so zap it */ } }
static void ui_scrollbar(int x0, int y0, int x1, int y1, int *value, int page_size, int max) { static float saved_top = 0; static int saved_ui_y = 0; float top; int total_h = y1 - y0; int thumb_h = fz_maxi(x1 - x0, total_h * page_size / max); int avail_h = total_h - thumb_h; max -= page_size; if (max <= 0) { *value = 0; glColor4f(0.6f, 0.6f, 0.6f, 1.0f); glRectf(x0, y0, x1, y1); return; } top = (float) *value * avail_h / max; if (ui.down && !ui.active) { if (ui.x >= x0 && ui.x < x1 && ui.y >= y0 && ui.y < y1) { if (ui.y < top) { ui.active = "pgdn"; *value -= page_size; } else if (ui.y >= top + thumb_h) { ui.active = "pgup"; *value += page_size; } else { ui.hot = value; ui.active = value; saved_top = top; saved_ui_y = ui.y; } } } if (ui.active == value) { *value = (saved_top + ui.y - saved_ui_y) * max / avail_h; } if (*value < 0) *value = 0; else if (*value > max) *value = max; top = (float) *value * avail_h / max; glColor4f(0.6f, 0.6f, 0.6f, 1.0f); glRectf(x0, y0, x1, y1); glColor4f(0.8f, 0.8f, 0.8f, 1.0f); glRectf(x0, top, x1, top + thumb_h); }
static fz_pixmap * decomp_image_banded(fz_context *ctx, fz_stream *stm, pdf_image *image, int indexed, int l2factor, int native_l2factor, int cache) { fz_pixmap *tile = NULL, *part = NULL; int w = (image->base.w + (1 << l2factor) - 1) >> l2factor; int h = (image->base.h + (1 << l2factor) - 1) >> l2factor; int part_h, orig_h = image->base.h; int band = 1 << fz_maxi(8, l2factor); fz_var(tile); fz_var(part); fz_try(ctx) { tile = fz_new_pixmap(ctx, image->base.colorspace, w, h); tile->interpolate = image->interpolate; tile->has_alpha = 0; /* SumatraPDF: allow optimizing non-alpha pixmaps */ /* decompress the image in bands of 256 lines */ for (part_h = h; part_h > 0; part_h -= band >> l2factor) { image->base.h = part_h > band >> l2factor ? band : (orig_h - 1) % band + 1; part = decomp_image_from_stream(ctx, fz_keep_stream(stm), image, -1, indexed, l2factor, native_l2factor, 0); memcpy(tile->samples + (h - part_h) * tile->w * tile->n, part->samples, part->h * part->w * part->n); tile->has_alpha |= part->has_alpha; /* SumatraPDF: allow optimizing non-alpha pixmaps */ fz_drop_pixmap(ctx, part); part = NULL; } /* cf. http://bugs.ghostscript.com/show_bug.cgi?id=693517 */ if (image->usecolorkey && image->base.mask) pdf_unblend_masked_tile(ctx, tile, image); } fz_always(ctx) { image->base.h = orig_h; fz_close(stm); } fz_catch(ctx) { fz_drop_pixmap(ctx, part); fz_drop_pixmap(ctx, tile); fz_rethrow(ctx); } if (cache) { pdf_image_key *key = NULL; fz_var(key); fz_try(ctx) { key = fz_malloc_struct(ctx, pdf_image_key); key->refs = 1; key->image = fz_keep_image(ctx, &image->base); key->l2factor = l2factor; fz_store_item(ctx, key, tile, fz_pixmap_size(ctx, tile), &pdf_image_store_type); } fz_always(ctx) { pdf_drop_image_key(ctx, key); } fz_catch(ctx) { } } return tile; }