- Renamed all references from zpdf to zcatpdf
- Module import: @import("zcatpdf")
- Consistent with zcatui, zcatgui naming convention
- All lowercase per Zig standards
Note: Directory rename (zpdf -> zcatpdf) and Forgejo repo rename
should be done manually after this commit.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
179 lines
6.3 KiB
Zig
179 lines
6.3 KiB
Zig
//! Invoice PDF example - Demonstrates a realistic use case
|
|
|
|
const std = @import("std");
|
|
const pdf = @import("zcatpdf");
|
|
|
|
pub fn main() !void {
|
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
defer _ = gpa.deinit();
|
|
const allocator = gpa.allocator();
|
|
|
|
std.debug.print("zcatpdf - Invoice example\n", .{});
|
|
|
|
var doc = pdf.Document.init(allocator);
|
|
defer doc.deinit();
|
|
|
|
var page = try doc.addPage(.a4);
|
|
|
|
// Company header
|
|
try page.setFont(.helvetica_bold, 24);
|
|
page.setFillColor(pdf.Color.rgb(41, 98, 255)); // Blue
|
|
try page.drawText(50, 780, "ACME Corporation");
|
|
|
|
try page.setFont(.helvetica, 10);
|
|
page.setFillColor(pdf.Color.medium_gray);
|
|
try page.drawText(50, 765, "123 Business Street, City 12345");
|
|
try page.drawText(50, 753, "Tel: +34 123 456 789 | Email: info@acme.com");
|
|
try page.drawText(50, 741, "CIF: B12345678");
|
|
|
|
// Invoice title
|
|
try page.setFont(.helvetica_bold, 28);
|
|
page.setFillColor(pdf.Color.black);
|
|
try page.drawText(400, 780, "FACTURA");
|
|
|
|
try page.setFont(.helvetica, 12);
|
|
try page.drawText(400, 760, "No: FAC-2025-0001");
|
|
try page.drawText(400, 745, "Fecha: 08/12/2025");
|
|
|
|
// Separator line
|
|
try page.setLineWidth(1);
|
|
page.setStrokeColor(pdf.Color.light_gray);
|
|
try page.drawLine(50, 720, 545, 720);
|
|
|
|
// Client info box
|
|
page.setFillColor(pdf.Color.rgb(245, 245, 245));
|
|
try page.fillRect(50, 640, 250, 70);
|
|
|
|
try page.setFont(.helvetica_bold, 11);
|
|
page.setFillColor(pdf.Color.black);
|
|
try page.drawText(60, 695, "CLIENTE:");
|
|
|
|
try page.setFont(.helvetica, 11);
|
|
try page.drawText(60, 680, "Empresa Cliente S.L.");
|
|
try page.drawText(60, 667, "Calle Principal 45, 2o B");
|
|
try page.drawText(60, 654, "08001 Barcelona");
|
|
try page.drawText(60, 641, "NIF: B87654321");
|
|
|
|
// Table header
|
|
const table_top: f32 = 610;
|
|
const col1: f32 = 50;
|
|
const col2: f32 = 300;
|
|
const col3: f32 = 370;
|
|
const col4: f32 = 430;
|
|
const col5: f32 = 490;
|
|
const row_height: f32 = 25;
|
|
|
|
// Header background
|
|
page.setFillColor(pdf.Color.rgb(41, 98, 255));
|
|
try page.fillRect(col1, table_top - row_height, 495, row_height);
|
|
|
|
// Header text
|
|
try page.setFont(.helvetica_bold, 10);
|
|
page.setFillColor(pdf.Color.white);
|
|
try page.drawText(col1 + 5, table_top - 18, "DESCRIPCION");
|
|
try page.drawText(col2 + 5, table_top - 18, "CANT.");
|
|
try page.drawText(col3 + 5, table_top - 18, "PRECIO");
|
|
try page.drawText(col4 + 5, table_top - 18, "IVA");
|
|
try page.drawText(col5 + 5, table_top - 18, "TOTAL");
|
|
|
|
// Table rows
|
|
const items = [_]struct { desc: []const u8, qty: []const u8, price: []const u8, vat: []const u8, total: []const u8 }{
|
|
.{ .desc = "Servicio de consultoria", .qty = "10h", .price = "50.00", .vat = "21%", .total = "605.00" },
|
|
.{ .desc = "Desarrollo web", .qty = "1", .price = "1500.00", .vat = "21%", .total = "1815.00" },
|
|
.{ .desc = "Mantenimiento mensual", .qty = "1", .price = "200.00", .vat = "21%", .total = "242.00" },
|
|
.{ .desc = "Hosting anual", .qty = "1", .price = "120.00", .vat = "21%", .total = "145.20" },
|
|
};
|
|
|
|
try page.setFont(.helvetica, 10);
|
|
page.setFillColor(pdf.Color.black);
|
|
|
|
var y = table_top - row_height;
|
|
for (items, 0..) |item, i| {
|
|
y -= row_height;
|
|
|
|
// Alternate row background
|
|
if (i % 2 == 0) {
|
|
page.setFillColor(pdf.Color.rgb(250, 250, 250));
|
|
try page.fillRect(col1, y, 495, row_height);
|
|
}
|
|
|
|
// Row content
|
|
page.setFillColor(pdf.Color.black);
|
|
try page.setFont(.helvetica, 10);
|
|
try page.drawText(col1 + 5, y + 8, item.desc);
|
|
try page.drawText(col2 + 5, y + 8, item.qty);
|
|
try page.drawText(col3 + 5, y + 8, item.price);
|
|
try page.drawText(col4 + 5, y + 8, item.vat);
|
|
try page.drawText(col5 + 5, y + 8, item.total);
|
|
|
|
// Row border
|
|
page.setStrokeColor(pdf.Color.light_gray);
|
|
try page.drawLine(col1, y, col1 + 495, y);
|
|
}
|
|
|
|
// Table border
|
|
try page.setLineWidth(0.5);
|
|
page.setStrokeColor(pdf.Color.medium_gray);
|
|
try page.drawRect(col1, y, 495, table_top - y - row_height);
|
|
|
|
// Vertical lines
|
|
try page.drawLine(col2, y, col2, table_top - row_height);
|
|
try page.drawLine(col3, y, col3, table_top - row_height);
|
|
try page.drawLine(col4, y, col4, table_top - row_height);
|
|
try page.drawLine(col5, y, col5, table_top - row_height);
|
|
|
|
// Totals section
|
|
const totals_y = y - 40;
|
|
|
|
try page.setFont(.helvetica, 11);
|
|
page.setFillColor(pdf.Color.black);
|
|
try page.drawText(380, totals_y, "Subtotal:");
|
|
try page.drawText(480, totals_y, "2,320.00");
|
|
|
|
try page.drawText(380, totals_y - 18, "IVA (21%):");
|
|
try page.drawText(480, totals_y - 18, "487.20");
|
|
|
|
// Total line
|
|
try page.setLineWidth(1);
|
|
page.setStrokeColor(pdf.Color.black);
|
|
try page.drawLine(380, totals_y - 30, 545, totals_y - 30);
|
|
|
|
try page.setFont(.helvetica_bold, 14);
|
|
try page.drawText(380, totals_y - 48, "TOTAL:");
|
|
try page.drawText(475, totals_y - 48, "2,807.20 EUR");
|
|
|
|
// Payment info
|
|
const payment_y = totals_y - 100;
|
|
|
|
page.setFillColor(pdf.Color.rgb(245, 245, 245));
|
|
try page.fillRect(50, payment_y - 50, 300, 70);
|
|
|
|
try page.setFont(.helvetica_bold, 10);
|
|
page.setFillColor(pdf.Color.black);
|
|
try page.drawText(60, payment_y + 5, "FORMA DE PAGO:");
|
|
|
|
try page.setFont(.helvetica, 10);
|
|
try page.drawText(60, payment_y - 10, "Transferencia bancaria");
|
|
try page.drawText(60, payment_y - 25, "IBAN: ES12 1234 5678 9012 3456 7890");
|
|
try page.drawText(60, payment_y - 40, "Vencimiento: 30 dias");
|
|
|
|
// Footer
|
|
try page.setLineWidth(0.5);
|
|
page.setStrokeColor(pdf.Color.light_gray);
|
|
try page.drawLine(50, 80, 545, 80);
|
|
|
|
try page.setFont(.helvetica, 8);
|
|
page.setFillColor(pdf.Color.medium_gray);
|
|
try page.drawText(50, 65, "Esta factura ha sido generada electronicamente y es valida sin firma.");
|
|
try page.drawText(50, 55, "ACME Corporation - Inscrita en el Registro Mercantil de Madrid, Tomo 12345, Folio 67, Hoja M-123456");
|
|
|
|
// Page number
|
|
try page.drawText(500, 55, "Pagina 1/1");
|
|
|
|
// Save
|
|
const filename = "invoice.pdf";
|
|
try doc.saveToFile(filename);
|
|
|
|
std.debug.print("Created: {s}\n", .{filename});
|
|
std.debug.print("Done!\n", .{});
|
|
}
|