#include #include #include // 1 or 0 to AA the final output const int DO_AA = 0; static double pointToPix(const double pntDist, const double res) { // Convert Points to Inches, then multiply by the resolution. return (pntDist * 25.4/72.0 / 25.4 * res); } // Extract a portion of a vector (PDF) surface to a higher-resolution // image. The result should not be blurry, but unfortunately, it is // if done using a naive approach of just copy+scale (cairo 1.8.6). // // This sample will draw some random data to a PDF surface. It will then // copy AND SCALE the data to a second PDF/vector surface, so that the // scaling is done in vector space. Finally, the rescaled PDF is copied // to the output image, for the final rasterization. // // Build with: // gcc -g `pkg-config --cflags --libs cairo` copy2.c int main(int argc, char *argv[]) { // Make a PDF surface const char fileName[] = "source.pdf"; const double pntWidth = 1000; const double pntHeight = 1000; cairo_surface_t *pdfSurface = cairo_pdf_surface_create( fileName, pntWidth, pntHeight); cairo_t *pdfContext = cairo_create(pdfSurface); if (DO_AA == 0) cairo_set_antialias(pdfContext, CAIRO_ANTIALIAS_NONE); // Draw a background gradient (From FAQ) { cairo_pattern_t *pat; pat = cairo_pattern_create_linear( 0.0, 0.0, pntWidth, pntHeight); cairo_pattern_add_color_stop_rgba(pat, 1, 0, 0, 0, 1); cairo_pattern_add_color_stop_rgba(pat, 0, 1, 1, 1, 1); cairo_rectangle(pdfContext, 0, 0, pntWidth, pntHeight); cairo_set_source(pdfContext, pat); cairo_fill(pdfContext); cairo_pattern_destroy (pat); } // Draw some circles { cairo_set_source_rgba(pdfContext, 0.0, 1.0, 0.0, 1); const double ang1 = 0; const double ang2 = 360 * M_PI/180.0; const double cenX = pntWidth / 2.0; const double cenY = pntHeight / 2.0; int i; for (i=50; i<=700; i+=50) { cairo_arc(pdfContext, cenX, cenY, i, ang1, ang2); cairo_stroke(pdfContext); } } #if 1 // Set up a transform, mostly to ensure that correct user units // are used when appropriate. Also, draw some more stuff. // NB - This is all optional { const double xMinUser = 0.0; const double yMinUser = 0.0; const double xMaxUser = 10000.0; const double yMaxUser = 10000.0; const double userWidth = xMaxUser - xMinUser; const double userHeight = yMaxUser - yMinUser; const double scaleX = pntWidth / userWidth; const double scaleY = pntHeight / userHeight; // Rotate about the centre of the VP const int vpCenX = pntWidth / 2; const int vpCenY = pntHeight / 2; const double txRotRad = 0 * M_PI / 180.0; cairo_translate(pdfContext, vpCenX, vpCenY); cairo_rotate(pdfContext, txRotRad); cairo_translate(pdfContext, -vpCenX, -vpCenY); // Scale+flip to put the Cartesian User space in place cairo_translate(pdfContext, 0, pntHeight); cairo_scale(pdfContext, scaleX, -scaleY); cairo_translate(pdfContext, -xMinUser, -yMinUser); // Draw some more stuff (In user units) // Line cairo_move_to(pdfContext, xMaxUser-100, yMaxUser-100); cairo_line_to(pdfContext, xMinUser+100, yMinUser+100); cairo_set_source_rgba(pdfContext, 1.0, 0.0, 0.0, 1); cairo_stroke(pdfContext); // Triangle cairo_move_to(pdfContext, (xMinUser + xMaxUser)/2.0, (yMinUser + yMaxUser)/2.0); cairo_rel_line_to(pdfContext, 0, 250); cairo_rel_line_to(pdfContext, -250, -250); cairo_close_path(pdfContext); cairo_set_source_rgba(pdfContext, 0.0, 0.0, 1.0, 1); cairo_fill(pdfContext); } #endif // This is the (arbitrary) region that we want to extract // (magnify and rasterize). Device units const double eMinX = 460; const double eMinY = 440; const double eMaxX = 530; const double eMaxY = 590; const double eWidth = eMaxX - eMinX; const double eHeight = eMaxY - eMinY; // Draw a box around the region we're going to extract, to make // it visible { cairo_save(pdfContext); cairo_identity_matrix(pdfContext); cairo_set_source_rgba(pdfContext, 1.0, 1.0, 0.0, 1); cairo_rectangle(pdfContext, eMinX, eMinY, eWidth, eHeight); cairo_stroke(pdfContext); cairo_restore(pdfContext); } // The resolution of the final image const double res = 792; // See how many pixels we'd need for the desired area at the // specified resolution. const double pixWidth = pointToPix(eWidth, res); const double pixHeight = pointToPix(eHeight, res); cairo_surface_t *tmpSurface; cairo_t *tmpContext; // Make a temporary PDF surface, and scale+copy the data to it { // Make a temp PDF surface with the same number of device // units as the desired image tmpSurface = cairo_surface_create_similar( pdfSurface, CAIRO_CONTENT_COLOR_ALPHA, pixWidth, pixHeight); tmpContext = cairo_create(tmpSurface); // Compute the scaling difference between the actual // extract size, and the desired image size. const double scaleX = pixWidth / eWidth; const double scaleY = pixHeight / eHeight; // Set the original PDF as the source on this temporary // surface. Scale+Shift it by the appropriate amount so // that the desired extract area is over this surface cairo_translate(tmpContext, -(eMinX*scaleX), -(eMinY*scaleY)); cairo_scale(tmpContext, scaleX, scaleY); cairo_set_source_surface(tmpContext, pdfSurface, 0, 0); // IMPORTANT - Turn off the filtering. Otherwise, the // result will look like it's just a stretched raster cairo_pattern_set_filter(cairo_get_source(tmpContext), CAIRO_FILTER_NEAREST); // Do the copy cairo_paint(tmpContext); } // Make the image surface, at the same size as the temporary PDF // we calculated/created above cairo_surface_t *imgSurface = cairo_image_surface_create( CAIRO_FORMAT_RGB24, pixWidth, pixHeight); cairo_t *imgContext = cairo_create(imgSurface); // Set the intermediate PDF surface as the source, and paint it // to the image cairo_set_source_surface(imgContext, tmpSurface, 0, 0); cairo_paint(imgContext); // Save the image to a file (for validation) cairo_surface_write_to_png(imgSurface, "extract.png"); // Cleanup cairo_destroy(imgContext); cairo_destroy(pdfContext); cairo_destroy(tmpContext); cairo_surface_destroy(tmpSurface); cairo_surface_destroy(imgSurface); cairo_surface_destroy(pdfSurface); cairo_debug_reset_static_data(); // Valgrind return 0; }