//! Image Demo - Demonstrates JPEG and PNG image embedding //! //! Usage: ./image_demo [path_to_image] //! Supports: JPEG (.jpg, .jpeg) and PNG (.png) images //! If no path is provided, creates a simple PDF with text explaining the features. const std = @import("std"); const pdf = @import("zpdf"); pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator(); std.debug.print("zpdf - Image Demo\n", .{}); // Get command line args const args = try std.process.argsAlloc(allocator); defer std.process.argsFree(allocator, args); var doc = pdf.Pdf.init(allocator, .{}); defer doc.deinit(); doc.setTitle("Image Demo"); doc.setAuthor("zpdf"); var page = try doc.addPage(.{}); page.setMargins(50, 50, 50); page.setXY(50, 800); // Title try page.setFont(.helvetica_bold, 24); page.setFillColor(pdf.Color.rgb(41, 98, 255)); try page.cell(0, 30, "Image Demo - JPEG & PNG Support", pdf.Border.none, .center, false); page.ln(40); // Check if an image path was provided if (args.len > 1) { const image_path = args[1]; std.debug.print("Loading image: {s}\n", .{image_path}); // Try to load the image (auto-detect format) const image_index = doc.addImageFromFile(image_path) catch |err| { std.debug.print("Error loading image: {any}\n", .{err}); try page.setFont(.helvetica, 12); page.setFillColor(pdf.Color.red); try page.cell(0, 20, "Error: Could not load image file", pdf.Border.none, .left, false); page.ln(25); try page.setFont(.helvetica, 10); page.setFillColor(pdf.Color.black); const long_text = \\Make sure the file exists and is a valid JPEG or PNG image. \\ \\Supported formats: \\- JPEG/JPG images (RGB, Grayscale, CMYK) \\- PNG images (RGB, RGBA, Grayscale, Indexed) ; try page.multiCell(450, null, long_text, pdf.Border.none, .left, false); const filename = "image_demo.pdf"; try doc.save(filename); std.debug.print("Created: {s}\n", .{filename}); return; }; // Get image info const img_info = doc.getImage(image_index).?; // Show image info try page.setFont(.helvetica_bold, 14); page.setFillColor(pdf.Color.black); try page.cell(0, 20, "Image Information:", pdf.Border.none, .left, false); page.ln(25); try page.setFont(.helvetica, 11); // Display image properties var buf: [256]u8 = undefined; const size_text = std.fmt.bufPrint(&buf, "Dimensions: {d} x {d} pixels", .{ img_info.width, img_info.height }) catch "Error"; try page.cell(0, 16, size_text, pdf.Border.none, .left, false); page.ln(18); const format_text = std.fmt.bufPrint(&buf, "Format: {s}", .{if (img_info.format == .jpeg) "JPEG" else "PNG"}) catch "Error"; try page.cell(0, 16, format_text, pdf.Border.none, .left, false); page.ln(18); const color_text = std.fmt.bufPrint(&buf, "Color Space: {s}", .{img_info.color_space.pdfName()}) catch "Error"; try page.cell(0, 16, color_text, pdf.Border.none, .left, false); page.ln(18); const bpc_text = std.fmt.bufPrint(&buf, "Bits per Component: {d}", .{img_info.bits_per_component}) catch "Error"; try page.cell(0, 16, bpc_text, pdf.Border.none, .left, false); page.ln(18); const alpha_text = std.fmt.bufPrint(&buf, "Has Alpha Channel: {s}", .{if (img_info.hasAlpha()) "Yes" else "No"}) catch "Error"; try page.cell(0, 16, alpha_text, pdf.Border.none, .left, false); page.ln(30); // Draw the image try page.setFont(.helvetica_bold, 14); try page.cell(0, 20, "Image Preview:", pdf.Border.none, .left, false); page.ln(25); // Calculate size to fit in available space (max 400x400) const max_w: f32 = 400; const max_h: f32 = 400; const img_w: f32 = @floatFromInt(img_info.width); const img_h: f32 = @floatFromInt(img_info.height); const scale = @min(max_w / img_w, max_h / img_h, 1.0); const display_w = img_w * scale; const display_h = img_h * scale; // Draw border around image area const img_x = page.getX(); const img_y = page.getY() - display_h; page.setStrokeColor(pdf.Color.light_gray); try page.drawRect(img_x - 2, img_y - 2, display_w + 4, display_h + 4); // Draw the image try page.image(image_index, img_info, img_x, img_y, display_w, display_h); std.debug.print("Image embedded: {d}x{d} pixels, displayed at {d:.0}x{d:.0} points\n", .{ img_info.width, img_info.height, display_w, display_h }); if (img_info.hasAlpha()) { std.debug.print("Alpha channel: embedded as soft mask\n", .{}); } } else { // No image provided - show instructions try page.setFont(.helvetica_bold, 14); page.setFillColor(pdf.Color.black); try page.cell(0, 20, "How to Use Images:", pdf.Border.none, .left, false); page.ln(25); try page.setFont(.helvetica, 11); const instructions = \\To include an image in your PDF: \\ \\1. Load the image file (auto-detects format): \\ const img_idx = try doc.addImageFromFile("photo.jpg"); \\ // Or specifically: \\ const jpg_idx = try doc.addJpegImageFromFile("photo.jpg"); \\ const png_idx = try doc.addPngImageFromFile("image.png"); \\ \\2. Get the image info: \\ const info = doc.getImage(img_idx).?; \\ \\3. Draw on a page: \\ try page.image(img_idx, info, x, y, width, height); \\ \\Or use imageFit to auto-scale: \\ try page.imageFit(img_idx, info, x, y, max_w, max_h); \\ \\Run this example with an image file path: \\ ./image_demo photo.jpg \\ ./image_demo logo.png ; try page.multiCell(450, null, instructions, pdf.Border.all, .left, false); page.ln(30); // Supported features try page.setFont(.helvetica_bold, 14); try page.cell(0, 20, "Supported Features:", pdf.Border.none, .left, false); page.ln(25); try page.setFont(.helvetica, 11); // JPEG Features page.setFillColor(pdf.Color.rgb(0, 128, 0)); try page.cell(20, 16, "[OK]", pdf.Border.none, .left, false); page.setFillColor(pdf.Color.black); try page.cell(0, 16, "JPEG images (RGB, Grayscale, CMYK)", pdf.Border.none, .left, false); page.ln(18); page.setFillColor(pdf.Color.rgb(0, 128, 0)); try page.cell(20, 16, "[OK]", pdf.Border.none, .left, false); page.setFillColor(pdf.Color.black); try page.cell(0, 16, "JPEG direct passthrough (no re-encoding)", pdf.Border.none, .left, false); page.ln(18); // PNG Features page.setFillColor(pdf.Color.rgb(0, 128, 0)); try page.cell(20, 16, "[OK]", pdf.Border.none, .left, false); page.setFillColor(pdf.Color.black); try page.cell(0, 16, "PNG images (RGB, RGBA, Grayscale, Indexed)", pdf.Border.none, .left, false); page.ln(18); page.setFillColor(pdf.Color.rgb(0, 128, 0)); try page.cell(20, 16, "[OK]", pdf.Border.none, .left, false); page.setFillColor(pdf.Color.black); try page.cell(0, 16, "PNG transparency (alpha channel as soft mask)", pdf.Border.none, .left, false); page.ln(18); page.setFillColor(pdf.Color.rgb(0, 128, 0)); try page.cell(20, 16, "[OK]", pdf.Border.none, .left, false); page.setFillColor(pdf.Color.black); try page.cell(0, 16, "PNG filters (None, Sub, Up, Average, Paeth)", pdf.Border.none, .left, false); page.ln(18); // General features page.setFillColor(pdf.Color.rgb(0, 128, 0)); try page.cell(20, 16, "[OK]", pdf.Border.none, .left, false); page.setFillColor(pdf.Color.black); try page.cell(0, 16, "Automatic format detection", pdf.Border.none, .left, false); page.ln(18); page.setFillColor(pdf.Color.rgb(0, 128, 0)); try page.cell(20, 16, "[OK]", pdf.Border.none, .left, false); page.setFillColor(pdf.Color.black); try page.cell(0, 16, "Aspect ratio preservation", pdf.Border.none, .left, false); page.ln(18); // Not yet supported page.setFillColor(pdf.Color.rgb(255, 165, 0)); try page.cell(20, 16, "[--]", pdf.Border.none, .left, false); page.setFillColor(pdf.Color.black); try page.cell(0, 16, "PNG interlaced images (not supported)", pdf.Border.none, .left, false); } // Footer page.setXY(50, 50); try page.setFont(.helvetica, 9); page.setFillColor(pdf.Color.medium_gray); try page.cell(0, 15, "Generated with zpdf - Pure Zig PDF Library", pdf.Border.none, .center, false); // Save const filename = "image_demo.pdf"; try doc.save(filename); std.debug.print("Created: {s}\n", .{filename}); std.debug.print("Done!\n", .{}); }