//! Transforms Demo - Rotation, Scaling, Skew, and Translation //! //! Demonstrates the transformation capabilities of zpdf including: //! - Rotation around a point //! - Scaling from a point //! - Skewing (shearing) //! - Translation //! - Combined transformations 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 - Transforms Demo\n", .{}); var doc = pdf.Pdf.init(allocator, .{}); defer doc.deinit(); doc.setTitle("Transforms Demo"); doc.setAuthor("zpdf"); // ========================================================================= // Page 1: Rotation // ========================================================================= { const page = try doc.addPage(.{}); // Title try page.setFont(.helvetica_bold, 24); page.setFillColor(pdf.Color.rgb(41, 98, 255)); try page.drawText(50, 780, "Rotation Transforms"); try page.setFont(.helvetica, 11); page.setFillColor(pdf.Color.black); try page.drawText(50, 755, "Rotate graphics around a specified point"); // Original rectangle (for reference) try page.setFont(.helvetica, 10); page.setFillColor(pdf.Color.dark_gray); try page.drawText(100, 680, "Original:"); page.setFillColor(pdf.Color.rgb(200, 200, 200)); page.setStrokeColor(pdf.Color.black); try page.setLineWidth(1); try page.drawFilledRect(100, 600, 80, 60); // Draw rotation center marker page.setFillColor(pdf.Color.red); try page.fillCircle(140, 630, 3); // Rotated rectangles const angles = [_]f32{ 15, 30, 45, 60 }; const colors = [_]pdf.Color{ pdf.Color.rgb(255, 100, 100), pdf.Color.rgb(100, 255, 100), pdf.Color.rgb(100, 100, 255), pdf.Color.rgb(255, 200, 100), }; var x: f32 = 250; for (angles, colors) |angle, color| { try page.setFont(.helvetica, 9); page.setFillColor(pdf.Color.dark_gray); var angle_buf: [32]u8 = undefined; const angle_str = std.fmt.bufPrint(&angle_buf, "{d} degrees", .{@as(i32, @intFromFloat(angle))}) catch "angle"; try page.drawText(x, 680, angle_str); // Save state before transformation try page.saveState(); // Rotate around center of rectangle const cx = x + 40; const cy: f32 = 630; try page.rotate(angle, cx, cy); // Draw rectangle page.setFillColor(color); page.setStrokeColor(pdf.Color.black); try page.drawFilledRect(x, 600, 80, 60); // Restore state try page.restoreState(); // Draw center point page.setFillColor(pdf.Color.red); try page.fillCircle(cx, cy, 3); x += 100; } // Rotated text example try page.setFont(.helvetica_bold, 14); page.setFillColor(pdf.Color.black); try page.drawText(50, 520, "Rotated Text:"); const text_angles = [_]f32{ 0, 15, 30, 45, 90 }; x = 100; for (text_angles) |angle| { try page.saveState(); try page.rotate(angle, x, 450); try page.setFont(.helvetica, 12); page.setFillColor(pdf.Color.rgb(0, 100, 150)); try page.drawText(x, 450, "Hello!"); try page.restoreState(); x += 80; } // Multiple rotations around same point (like clock hands) try page.setFont(.helvetica_bold, 14); page.setFillColor(pdf.Color.black); try page.drawText(50, 350, "Clock-like Rotation:"); const clock_cx: f32 = 200; const clock_cy: f32 = 250; // Draw clock circle page.setStrokeColor(pdf.Color.dark_gray); try page.setLineWidth(2); try page.drawCircle(clock_cx, clock_cy, 80); // Draw hour marks try page.setLineWidth(1); for (0..12) |i| { const fi: f32 = @floatFromInt(i); try page.saveState(); try page.rotate(fi * 30, clock_cx, clock_cy); page.setStrokeColor(pdf.Color.dark_gray); try page.drawLine(clock_cx, clock_cy + 70, clock_cx, clock_cy + 80); try page.restoreState(); } // Draw clock hands try page.setLineWidth(3); page.setStrokeColor(pdf.Color.rgb(50, 50, 150)); try page.saveState(); try page.rotate(60, clock_cx, clock_cy); // Hour hand at ~2 o'clock try page.drawLine(clock_cx, clock_cy, clock_cx, clock_cy + 45); try page.restoreState(); try page.setLineWidth(2); page.setStrokeColor(pdf.Color.rgb(150, 50, 50)); try page.saveState(); try page.rotate(210, clock_cx, clock_cy); // Minute hand at ~7 o'clock try page.drawLine(clock_cx, clock_cy, clock_cx, clock_cy + 65); try page.restoreState(); // Footer try page.setFont(.helvetica, 9); page.setFillColor(pdf.Color.medium_gray); try page.drawText(250, 50, "Page 1 of 2 - zpdf Transforms Demo"); } // ========================================================================= // Page 2: Scale, Skew, and Combined Transforms // ========================================================================= { const page = try doc.addPage(.{}); // Title try page.setFont(.helvetica_bold, 24); page.setFillColor(pdf.Color.rgb(41, 98, 255)); try page.drawText(50, 780, "Scale, Skew & Combined"); // Scaling section try page.setFont(.helvetica_bold, 14); page.setFillColor(pdf.Color.black); try page.drawText(50, 720, "Scaling:"); try page.setFont(.helvetica, 10); page.setFillColor(pdf.Color.dark_gray); try page.drawText(50, 700, "Scale graphics from a reference point"); // Original page.setFillColor(pdf.Color.light_gray); page.setStrokeColor(pdf.Color.black); try page.setLineWidth(1); try page.drawFilledRect(80, 620, 40, 40); try page.setFont(.helvetica, 9); page.setFillColor(pdf.Color.dark_gray); try page.drawText(80, 600, "1.0x"); // Scaled versions const scales = [_]struct { sx: f32, sy: f32, label: []const u8 }{ .{ .sx = 1.5, .sy = 1.5, .label = "1.5x" }, .{ .sx = 0.5, .sy = 0.5, .label = "0.5x" }, .{ .sx = 2.0, .sy = 1.0, .label = "2x,1x" }, .{ .sx = 1.0, .sy = 2.0, .label = "1x,2x" }, }; var x: f32 = 160; for (scales) |s| { try page.saveState(); try page.scale(s.sx, s.sy, x + 20, 640); page.setFillColor(pdf.Color.rgb(100, 200, 150)); page.setStrokeColor(pdf.Color.black); try page.drawFilledRect(x, 620, 40, 40); try page.restoreState(); try page.setFont(.helvetica, 9); page.setFillColor(pdf.Color.dark_gray); try page.drawText(x, 600, s.label); x += 100; } // Skewing section try page.setFont(.helvetica_bold, 14); page.setFillColor(pdf.Color.black); try page.drawText(50, 540, "Skewing:"); try page.setFont(.helvetica, 10); page.setFillColor(pdf.Color.dark_gray); try page.drawText(50, 520, "Skew (shear) graphics along X or Y axis"); // Original page.setFillColor(pdf.Color.light_gray); page.setStrokeColor(pdf.Color.black); try page.drawFilledRect(80, 440, 50, 50); try page.setFont(.helvetica, 9); page.setFillColor(pdf.Color.dark_gray); try page.drawText(80, 420, "Original"); // Skewed versions const skews = [_]struct { sx: f32, sy: f32, label: []const u8 }{ .{ .sx = 15, .sy = 0, .label = "X: 15 deg" }, .{ .sx = 30, .sy = 0, .label = "X: 30 deg" }, .{ .sx = 0, .sy = 15, .label = "Y: 15 deg" }, .{ .sx = 15, .sy = 15, .label = "XY: 15 deg" }, }; x = 170; for (skews) |s| { try page.saveState(); try page.translate(x, 465); try page.skew(s.sx, s.sy); try page.translate(-x, -465); page.setFillColor(pdf.Color.rgb(200, 150, 100)); page.setStrokeColor(pdf.Color.black); try page.drawFilledRect(x, 440, 50, 50); try page.restoreState(); try page.setFont(.helvetica, 9); page.setFillColor(pdf.Color.dark_gray); try page.drawText(x, 420, s.label); x += 100; } // Combined transforms section try page.setFont(.helvetica_bold, 14); page.setFillColor(pdf.Color.black); try page.drawText(50, 360, "Combined Transforms:"); try page.setFont(.helvetica, 10); page.setFillColor(pdf.Color.dark_gray); try page.drawText(50, 340, "Multiple transformations applied in sequence"); // Example 1: Rotate + Scale try page.saveState(); try page.rotate(30, 150, 260); try page.scale(1.5, 1.5, 150, 260); page.setFillColor(pdf.Color.rgb(255, 150, 150)); page.setStrokeColor(pdf.Color.rgb(200, 0, 0)); try page.setLineWidth(2); try page.drawFilledRect(120, 230, 60, 60); try page.restoreState(); try page.setFont(.helvetica, 9); page.setFillColor(pdf.Color.dark_gray); try page.drawText(100, 170, "Rotate + Scale"); // Example 2: Scale + Skew try page.saveState(); try page.translate(300, 260); try page.skew(20, 0); try page.scaleFromOrigin(1.2, 0.8); try page.translate(-300, -260); page.setFillColor(pdf.Color.rgb(150, 255, 150)); page.setStrokeColor(pdf.Color.rgb(0, 150, 0)); try page.drawFilledRect(270, 230, 60, 60); try page.restoreState(); try page.setFont(.helvetica, 9); page.setFillColor(pdf.Color.dark_gray); try page.drawText(250, 170, "Scale + Skew"); // Example 3: Rotate + Skew + Scale try page.saveState(); try page.rotate(-15, 450, 260); try page.translate(450, 260); try page.skew(10, 5); try page.scaleFromOrigin(1.3, 1.1); try page.translate(-450, -260); page.setFillColor(pdf.Color.rgb(150, 150, 255)); page.setStrokeColor(pdf.Color.rgb(0, 0, 200)); try page.drawFilledRect(420, 230, 60, 60); try page.restoreState(); try page.setFont(.helvetica, 9); page.setFillColor(pdf.Color.dark_gray); try page.drawText(400, 170, "Rotate + Skew + Scale"); // Footer try page.setFont(.helvetica, 9); page.setFillColor(pdf.Color.medium_gray); try page.drawText(250, 50, "Page 2 of 2 - zpdf Transforms Demo"); } // Save const filename = "transforms_demo.pdf"; try doc.save(filename); std.debug.print("Created: {s} ({d} pages)\n", .{ filename, doc.pageCount() }); std.debug.print("Done!\n", .{}); }