fz_rect fz_boundmasknode(fz_masknode *node, fz_matrix ctm) { fz_node *shape; fz_node *color; fz_rect one, two; shape = node->super.first; color = shape->next; one = fz_boundnode(shape, ctm); two = fz_boundnode(color, ctm); return fz_intersectrects(one, two); }
fz_rect fz_boundtree(fz_tree *tree, fz_matrix ctm) { if (tree->root) return fz_boundnode(tree->root, ctm); return fz_emptyrect; }
fz_rect fz_boundtransformnode(fz_transformnode *node, fz_matrix ctm) { if (!node->super.first) return fz_emptyrect; return fz_boundnode(node->super.first, fz_concat(node->m, ctm)); }
static fz_error * rendertext(fz_renderer *gc, fz_textnode *text, fz_matrix ctm) { fz_error *error; fz_irect tbox; fz_irect clip; fz_matrix tm, trm; fz_glyph glyph; int i, x, y, cid; tbox = fz_roundrect(fz_boundnode((fz_node*)text, ctm)); clip = fz_intersectirects(gc->clip, tbox); DEBUG("text %s n=%d [%g %g %g %g];\n", text->font->name, text->len, text->trm.a, text->trm.b, text->trm.c, text->trm.d); if (fz_isemptyrect(clip)) return fz_okay; if (!(gc->flag & FOVER)) { error = fz_newpixmapwithrect(&gc->dest, clip, 1); if (error) return error; fz_clearpixmap(gc->dest); } tm = text->trm; for (i = 0; i < text->len; i++) { cid = text->els[i].cid; tm.e = text->els[i].x; tm.f = text->els[i].y; trm = fz_concat(tm, ctm); x = fz_floor(trm.e); y = fz_floor(trm.f); trm.e = QUANT(trm.e - fz_floor(trm.e), HSUBPIX); trm.f = QUANT(trm.f - fz_floor(trm.f), VSUBPIX); error = fz_renderglyph(gc->cache, &glyph, text->font, cid, trm); if (error) return error; if (!(gc->flag & FOVER)) drawglyph(gc, gc->dest, &glyph, x, y); else drawglyph(gc, gc->over, &glyph, x, y); } return fz_okay; }
static int fitsinside(fz_node *node, fz_rect clip) { fz_rect bbox; bbox = fz_boundnode(node, fz_identity()); if (fz_isinfiniterect(bbox)) return 0; if (fz_isemptyrect(bbox)) return 1; if (bbox.x0 < clip.x0) return 0; if (bbox.x1 > clip.x1) return 0; if (bbox.y0 < clip.y0) return 0; if (bbox.y1 > clip.y1) return 0; return 1; }
/* * Over */ fz_error fz_newovernode(fz_node **nodep) { fz_node *node; node = *nodep = fz_malloc(sizeof (fz_overnode)); if (!node) return fz_rethrow(-1, "out of memory"); fz_initnode(node, FZ_NOVER); return fz_okay; } #if 0 /* cf. http://code.google.com/p/sumatrapdf/issues/detail?id=533 */ fz_rect fz_boundovernode(fz_overnode *node, fz_matrix ctm) { fz_node *child; fz_rect bbox; fz_rect temp; child = node->super.first; if (!child) return fz_emptyrect; bbox = fz_boundnode(child, ctm); child = child->next; while (child) { temp = fz_boundnode(child, ctm); bbox = fz_mergerects(temp, bbox); child = child->next; } return bbox; }
static fz_error * rendershade(fz_renderer *gc, fz_shadenode *node, fz_matrix ctm) { fz_error *error; fz_irect bbox; assert(!gc->maskonly); DEBUG("shade;\n"); bbox = fz_roundrect(fz_boundnode((fz_node*)node, ctm)); bbox = fz_intersectirects(gc->clip, bbox); error = fz_newpixmapwithrect(&gc->dest, bbox, gc->model->n + 1); if (error) return error; return fz_rendershade(node->shade, ctm, gc->model, gc->dest); }
fz_error* processPage( soPdfFile* inFile, int pageNo, fz_rect *bbRect, int rectCount ) { fz_error *error; fz_obj *pageRef; pdf_page *pdfPage; fz_rect contentBox; fz_rect mediaBox; // Initialize for (int ctr = 0; ctr < rectCount; ctr++) bbRect[ctr] = fz_emptyrect; // Get the page reference and load the page contents pageRef = pdf_getpageobject(inFile->pageTree, pageNo); error = pdf_loadpage(&pdfPage, inFile->xref, pageRef); if (error != NULL) { // Ideally pdf_loadpage should render all the pages // and this should never happen return processErrorPage(inFile, pageRef, pageNo, bbRect, error); } // Get the bounding box for the page mediaBox = pdfPage->mediabox; float mbHeight = mediaBox.y1 - mediaBox.y0; // calculate the bounding box for all the elements in the page contentBox = fz_boundnode(pdfPage->tree->root, fz_identity()); float cbHeight = contentBox.y1 - contentBox.y0; // If there is nothing on the page we return nothing. // should we return an empty page instead ??? if (fz_isemptyrect(contentBox)) goto Cleanup; // if contentBox is bigger than mediaBox then there are some // elements that should not be display and hence we reset the // content box to media box if ((cbHeight > mbHeight) || ((contentBox.x1 - contentBox.x0) > (mediaBox.x1 - mediaBox.x0))) { // Calculate the new content box based on the content that is // inside the the media box and recalculate cbHeight contentBox = getContainingRect(pdfPage->tree->root, mediaBox); cbHeight = contentBox.y1 - contentBox.y0; } #ifdef _blahblah printf("-->Page %d\n", pageNo); bbdump(pdfPage->tree->root, 1); #endif // The rotation takes place when we insert the page into destination // If only the mupdf renderer could give accurate values of the // bounding box of all the elements in a page, the splitting would // be so much more easier without overlapping, cutting text, etc switch(p_mode) { case FitHeight: case FitWidth: bbRect[0] = contentBox; goto Cleanup; case Fit2xHeight: case Fit2xWidth: // Let the processing happen. break; case SmartFitHeight: case SmartFitWidth: default: return fz_throw("Mode(%d) not yet implemented.", p_mode); break; } // If the contentBox is 60% of mediaBox then do not split if (((cbHeight / mbHeight) * 100) <= 55) { bbRect[0] = contentBox; goto Cleanup; } // Get the first split contents from top. The box we specify is // top 55% (bottom + 45) of the contents bbRect[0] = contentBox; bbRect[0].y0 = bbRect[0].y0 + (float)(0.45 * cbHeight); bbRect[0] = getContainingRect(pdfPage->tree->root, bbRect[0]); // Check if the contents we got in first split is more than 40% // of the total contents float bbRect0Height = bbRect[0].y1 - bbRect[0].y0; if (((bbRect0Height / cbHeight) * 100) >= 40) { // The content is more than 40%. Put the rest of the // content in the second split and exit bbRect[1] = contentBox; bbRect[1].y1 = bbRect[1].y1 - bbRect0Height; // Adjust the split box height by X points to make sure // we get everything and dont have annoying tail cuts bbRect[0].y0 -= 2; goto Cleanup; } // Since the contents we got in first split is less than 40% // of the total contents, split the content in half with overlap float overlap = (cbHeight * (float)(p_overlap / 100)) / 2; bbRect[0] = contentBox; bbRect[0].y0 = bbRect[0].y0 + (float)(0.5 * cbHeight) - overlap; bbRect[1] = contentBox; bbRect[1].y1 = bbRect[1].y1 - (float)(0.5 * cbHeight) + overlap; Cleanup: // This function can overflow the stack when the pdf page // to be rendered is very complex. I had to increase the // stack size reserved for exe using compiler option pdf_droppage(pdfPage); return error; }
static fz_error * rendermask(fz_renderer *gc, fz_masknode *mask, fz_matrix ctm) { fz_error *error; int oldmaskonly; fz_pixmap *oldover; fz_irect oldclip; fz_irect bbox; fz_irect clip; fz_pixmap *shapepix = nil; fz_pixmap *colorpix = nil; fz_node *shape; fz_node *color; float rgb[3]; shape = mask->super.first; color = shape->next; /* special case black voodo */ if (gc->flag & FOVER) { if (fz_issolidnode(color)) { fz_solidnode *solid = (fz_solidnode*)color; fz_convertcolor(solid->cs, solid->samples, gc->model, rgb); gc->argb[0] = solid->a * 255; gc->argb[1] = rgb[0] * solid->a * 255; gc->argb[2] = rgb[1] * solid->a * 255; gc->argb[3] = rgb[2] * solid->a * 255; gc->argb[4] = rgb[0] * 255; gc->argb[5] = rgb[1] * 255; gc->argb[6] = rgb[2] * 255; gc->flag |= FRGB; /* we know these can handle the FRGB shortcut */ if (fz_ispathnode(shape)) return renderpath(gc, (fz_pathnode*)shape, ctm); if (fz_istextnode(shape)) return rendertext(gc, (fz_textnode*)shape, ctm); if (fz_isimagenode(shape)) return renderimage(gc, (fz_imagenode*)shape, ctm); } } oldclip = gc->clip; oldover = gc->over; bbox = fz_roundrect(fz_boundnode(shape, ctm)); clip = fz_intersectirects(bbox, gc->clip); bbox = fz_roundrect(fz_boundnode(color, ctm)); clip = fz_intersectirects(bbox, clip); if (fz_isemptyrect(clip)) return fz_okay; DEBUG("mask [%d %d %d %d]\n{\n", clip.x0, clip.y0, clip.x1, clip.y1); { fz_irect sbox = fz_roundrect(fz_boundnode(shape, ctm)); fz_irect cbox = fz_roundrect(fz_boundnode(color, ctm)); if (cbox.x0 >= sbox.x0 && cbox.x1 <= sbox.x1) if (cbox.y0 >= sbox.y0 && cbox.y1 <= sbox.y1) DEBUG("potentially useless mask\n"); } gc->clip = clip; gc->over = nil; oldmaskonly = gc->maskonly; gc->maskonly = 1; error = rendernode(gc, shape, ctm); if (error) goto cleanup; shapepix = gc->dest; gc->dest = nil; gc->maskonly = oldmaskonly; error = rendernode(gc, color, ctm); if (error) goto cleanup; colorpix = gc->dest; gc->dest = nil; gc->clip = oldclip; gc->over = oldover; if (shapepix && colorpix) { if (gc->over) { blendmask(gc, colorpix, shapepix, gc->over, 1); } else { clip.x0 = MAX(colorpix->x, shapepix->x); clip.y0 = MAX(colorpix->y, shapepix->y); clip.x1 = MIN(colorpix->x+colorpix->w, shapepix->x+shapepix->w); clip.y1 = MIN(colorpix->y+colorpix->h, shapepix->y+shapepix->h); error = fz_newpixmapwithrect(&gc->dest, clip, colorpix->n); if (error) goto cleanup; blendmask(gc, colorpix, shapepix, gc->dest, 0); } } DEBUG("}\n"); if (shapepix) fz_droppixmap(shapepix); if (colorpix) fz_droppixmap(colorpix); return fz_okay; cleanup: if (shapepix) fz_droppixmap(shapepix); if (colorpix) fz_droppixmap(colorpix); return error; }
static fz_error * renderimage(fz_renderer *gc, fz_imagenode *node, fz_matrix ctm) { fz_error *error; fz_image *image = node->image; fz_irect bbox; fz_irect clip; int dx, dy; fz_pixmap *tile; fz_pixmap *temp; fz_matrix imgmat; fz_matrix invmat; int fa, fb, fc, fd; int u0, v0; int x0, y0; int w, h; int tileheight; DEBUG("image %dx%d %d+%d %s\n{\n", image->w, image->h, image->n, image->a, image->cs?image->cs->name:"(nil)"); bbox = fz_roundrect(fz_boundnode((fz_node*)node, ctm)); clip = fz_intersectirects(gc->clip, bbox); if (fz_isemptyrect(clip)) return fz_okay; if (image->w == 0 || image->h == 0) return fz_okay; if (image->n + image->a == 0) return fz_okay; calcimagescale(ctm, image->w, image->h, &dx, &dy); /* try to fit tile into a typical L2 cachce */ tileheight = 512 * 1024 / (image->w * (image->n + image->a)); /* tileheight must be an even multiple of dy, except for last band */ tileheight = (tileheight + dy - 1) / dy * dy; if ((dx != 1 || dy != 1) && image->h > tileheight) { int y = 0; DEBUG(" load image tile size = %dx%d\n", image->w, tileheight); error = fz_newpixmap(&tile, 0, 0, image->w, tileheight, image->n + 1); if (error) return error; error = fz_newscaledpixmap(&temp, image->w, image->h, image->n + 1, dx, dy); if (error) goto cleanup; do { if (y + tileheight > image->h) tileheight = image->h - y; tile->y = y; tile->h = tileheight; DEBUG(" tile xywh=%d %d %d %d sxsy=1/%d 1/%d\n", 0, y, image->w, tileheight, dx, dy); error = image->loadtile(image, tile); if (error) goto cleanup1; error = fz_scalepixmaptile(temp, 0, y, tile, dx, dy); if (error) goto cleanup1; y += tileheight; } while (y < image->h); fz_droppixmap(tile); tile = temp; } else { DEBUG(" load image\n"); error = fz_newpixmap(&tile, 0, 0, image->w, image->h, image->n + 1); if (error) return error; error = image->loadtile(image, tile); if (error) goto cleanup; if (dx != 1 || dy != 1) { DEBUG(" scale image 1/%d 1/%d\n", dx, dy); error = fz_scalepixmap(&temp, tile, dx, dy); if (error) goto cleanup; fz_droppixmap(tile); tile = temp; } } if (image->cs && image->cs != gc->model) { DEBUG(" convert from %s to %s\n", image->cs->name, gc->model->name); error = fz_newpixmap(&temp, tile->x, tile->y, tile->w, tile->h, gc->model->n + 1); if (error) goto cleanup; fz_convertpixmap(image->cs, tile, gc->model, temp); fz_droppixmap(tile); tile = temp; } imgmat.a = 1.0 / tile->w; imgmat.b = 0.0; imgmat.c = 0.0; imgmat.d = -1.0 / tile->h; imgmat.e = 0.0; imgmat.f = 1.0; invmat = fz_invertmatrix(fz_concat(imgmat, ctm)); w = clip.x1 - clip.x0; h = clip.y1 - clip.y0; x0 = clip.x0; y0 = clip.y0; u0 = (invmat.a * (x0+0.5) + invmat.c * (y0+0.5) + invmat.e) * 65536; v0 = (invmat.b * (x0+0.5) + invmat.d * (y0+0.5) + invmat.f) * 65536; fa = invmat.a * 65536; fb = invmat.b * 65536; fc = invmat.c * 65536; fd = invmat.d * 65536; #define PSRC tile->samples, tile->w, tile->h #define PDST(p) p->samples + ((y0-p->y) * p->w + (x0-p->x)) * p->n, p->w * p->n #define PCTM u0, v0, fa, fb, fc, fd, w, h switch (gc->flag) { case FNONE: { DEBUG(" fnone %d x %d\n", w, h); if (image->cs) error = fz_newpixmapwithrect(&gc->dest, clip, gc->model->n + 1); else error = fz_newpixmapwithrect(&gc->dest, clip, 1); if (error) goto cleanup; if (image->cs) fz_img_4c4(PSRC, PDST(gc->dest), PCTM); else fz_img_1c1(PSRC, PDST(gc->dest), PCTM); } break; case FOVER: { DEBUG(" fover %d x %d\n", w, h); if (image->cs) fz_img_4o4(PSRC, PDST(gc->over), PCTM); else fz_img_1o1(PSRC, PDST(gc->over), PCTM); } break; case FOVER | FRGB: DEBUG(" fover+rgb %d x %d\n", w, h); fz_img_w4i1o4(gc->argb, PSRC, PDST(gc->over), PCTM); break; default: assert(!"impossible flag in image span function"); } DEBUG("}\n"); fz_droppixmap(tile); return fz_okay; cleanup1: fz_droppixmap(temp); cleanup: fz_droppixmap(tile); return error; }
fz_rect fz_boundovernode(fz_overnode *node, fz_matrix ctm) { struct { int cap, ix; struct { fz_node *node; fz_rect bbox; } *items; } stack; fz_node *child = node->super.first; fz_rect bbox = fz_emptyrect; fz_rect temp; if (!child) return fz_emptyrect; stack.cap = 8; stack.ix = 0; stack.items = nil; do { if (child->kind != FZ_NOVER) { temp = fz_boundnode(child, ctm); bbox = fz_mergerects(temp, bbox); child = child->next; } else if (child->first) { if (!stack.items) stack.items = fz_malloc(stack.cap * sizeof(*stack.items)); else if (stack.ix >= stack.cap) { stack.cap *= 2; stack.items = fz_realloc(stack.items, stack.cap * sizeof(*stack.items)); if (!stack.items) return fz_emptyrect; } stack.items[stack.ix].node = child; stack.items[stack.ix].bbox = bbox; stack.ix++; bbox = fz_emptyrect; child = child->first; } else { child = child->next; } while (!child && stack.ix > 0) { stack.ix--; temp = stack.items[stack.ix].bbox; bbox = fz_mergerects(temp, bbox); child = stack.items[stack.ix].node->next; } } while (child); fz_free(stack.items); return bbox; }
static fz_error * addpatternshape(pdf_gstate *gs, fz_node *shape, pdf_pattern *pat, fz_colorspace *cs, float *v) { fz_error *error; fz_node *xform; fz_node *over; fz_node *mask; fz_node *link; fz_matrix ctm; fz_matrix inv; fz_matrix ptm; fz_rect bbox; int x, y, x0, y0, x1, y1; /* patterns are painted in user space */ ctm = getmatrix(gs->head); inv = fz_invertmatrix(ctm); error = fz_newmasknode(&mask); if (error) return fz_rethrow(error, "cannot create mask node"); ptm = fz_concat(pat->matrix, fz_invertmatrix(ctm)); error = fz_newtransformnode(&xform, ptm); if (error) { fz_dropnode(mask); return fz_rethrow(error, "cannot create transform node"); } error = pdf_newovernode(&over, gs); if (error) { fz_dropnode(xform); fz_dropnode(mask); return fz_rethrow(error, "cannot create over node"); } fz_insertnodelast(mask, shape); fz_insertnodelast(mask, xform); fz_insertnodelast(xform, over); xform = nil; /* over, xform, mask are now owned by the tree */ /* get bbox of shape in pattern space for stamping */ ptm = fz_concat(ctm, fz_invertmatrix(pat->matrix)); bbox = fz_boundnode(shape, ptm); /* expand bbox by pattern bbox */ bbox.x0 += pat->bbox.x0; bbox.y0 += pat->bbox.y0; bbox.x1 += pat->bbox.x1; bbox.y1 += pat->bbox.y1; x0 = fz_floor(bbox.x0 / pat->xstep); y0 = fz_floor(bbox.y0 / pat->ystep); x1 = fz_ceil(bbox.x1 / pat->xstep); y1 = fz_ceil(bbox.y1 / pat->ystep); for (y = y0; y <= y1; y++) { for (x = x0; x <= x1; x++) { ptm = fz_translate(x * pat->xstep, y * pat->ystep); error = fz_newtransformnode(&xform, ptm); if (error) return fz_rethrow(error, "cannot create transform node for stamp"); error = fz_newlinknode(&link, pat->tree); if (error) { fz_dropnode(xform); return fz_rethrow(error, "cannot create link node for stamp"); } fz_insertnodelast(xform, link); fz_insertnodelast(over, xform); } } if (pat->ismask) { error = addcolorshape(gs, mask, 1.0, cs, v); if (error) return fz_rethrow(error, "cannot add colored shape"); return fz_okay; } fz_insertnodelast(gs->head, mask); return fz_okay; }
static fz_error * renderimage(fz_renderer *gc, fz_imagenode *node, fz_matrix ctm) { fz_error *error; fz_image *image = node->image; fz_irect bbox; fz_irect clip; int dx, dy; fz_pixmap *tile; fz_pixmap *temp; fz_matrix imgmat; fz_matrix invmat; int fa, fb, fc, fd; int u0, v0; int x0, y0; int w, h; DEBUG("image %dx%d %d+%d %s\n{\n", image->w, image->h, image->n, image->a, image->cs?image->cs->name:"(nil)"); bbox = fz_roundrect(fz_boundnode((fz_node*)node, ctm)); clip = fz_intersectirects(gc->clip, bbox); if (fz_isemptyrect(clip)) return nil; calcimagescale(ctm, image->w, image->h, &dx, &dy); DEBUG(" load image\n"); error = fz_newpixmap(&tile, 0, 0, image->w, image->h, image->n + 1); if (error) return error; error = image->loadtile(image, tile); if (error) goto cleanup; if (dx != 1 || dy != 1) { DEBUG(" scale image 1/%d 1/%d\n", dx, dy); error = fz_scalepixmap(&temp, tile, dx, dy); if (error) goto cleanup; fz_droppixmap(tile); tile = temp; } if (image->cs && image->cs != gc->model) { DEBUG(" convert from %s to %s\n", image->cs->name, gc->model->name); error = fz_newpixmap(&temp, tile->x, tile->y, tile->w, tile->h, gc->model->n + 1); if (error) goto cleanup; fz_convertpixmap(image->cs, tile, gc->model, temp); fz_droppixmap(tile); tile = temp; } imgmat.a = 1.0 / tile->w; imgmat.b = 0.0; imgmat.c = 0.0; imgmat.d = -1.0 / tile->h; imgmat.e = 0.0; imgmat.f = 1.0; invmat = fz_invertmatrix(fz_concat(imgmat, ctm)); w = clip.x1 - clip.x0; h = clip.y1 - clip.y0; x0 = clip.x0; y0 = clip.y0; u0 = (invmat.a * (x0+0.5) + invmat.c * (y0+0.5) + invmat.e) * 65536; v0 = (invmat.b * (x0+0.5) + invmat.d * (y0+0.5) + invmat.f) * 65536; fa = invmat.a * 65536; fb = invmat.b * 65536; fc = invmat.c * 65536; fd = invmat.d * 65536; #define PSRC tile->samples, tile->w, tile->h #define PDST(p) p->samples + ((y0-p->y) * p->w + (x0-p->x)) * p->n, p->w * p->n #define PCTM u0, v0, fa, fb, fc, fd, w, h switch (gc->flag) { case FNONE: { DEBUG(" fnone %d x %d\n", w, h); if (image->cs) error = fz_newpixmapwithrect(&gc->dest, clip, gc->model->n + 1); else error = fz_newpixmapwithrect(&gc->dest, clip, 1); if (error) goto cleanup; if (image->cs) fz_img_4c4(PSRC, PDST(gc->dest), PCTM); else fz_img_1c1(PSRC, PDST(gc->dest), PCTM); } break; case FOVER: { DEBUG(" fover %d x %d\n", w, h); if (image->cs) fz_img_4o4(PSRC, PDST(gc->over), PCTM); else fz_img_1o1(PSRC, PDST(gc->over), PCTM); } break; case FOVER | FRGB: DEBUG(" fover+rgb %d x %d\n", w, h); fz_img_w3i1o4(gc->rgb, PSRC, PDST(gc->over), PCTM); break; default: assert(!"impossible flag in image span function"); } DEBUG("}\n"); fz_droppixmap(tile); return nil; cleanup: fz_droppixmap(tile); return error; }