GraphCompose v1.4 is live. https://lnkd.in/eyJ2DK5b The last time I properly posted about GraphCompose here, it was v1.1.0. Since then, I did what every developer promises not to do: “I’ll just improve a few things.” A few things later, GraphCompose is no longer just a Java PDF generation project. It has moved much closer to a declarative document layout engine. The idea is simple: Instead of manually fighting PDF coordinates, margins, page breaks, and layout edge cases, you describe the document structure: sections, modules, paragraphs, tables, rows, layers, themes. GraphCompose handles layout, pagination, snapshots, and PDFBox rendering behind the scenes. The new v1.4 line adds: table column spans layer stacks for overlay-style layouts page and section backgrounds fluent rich text DSL reusable BusinessTheme design tokens visual regression scaffolding for generated PDFs stronger documentation and release guardrails Basically, I wanted PDF documents to feel less like drawing on a cave wall with coordinates and more like building UI layouts with components. Still Java. Still PDFBox underneath. But now with a much stronger layout engine on top. This release is a big step for the project: from “generate a PDF” toward “compose a designed document.” Repo: GraphCompose by DemchaAV hashtag #Java #OpenSource #PDFGeneration #SoftwareEngineering #BackendDevelopment #JavaDeveloper #PDFBox
GraphCompose v1.4: Declarative PDF Layout Engine for Java
More Relevant Posts
-
GraphCompose v1.1.0 is live. It started as a way to generate PDFs in Java without descending into the usual coordinate-based chaos where one small layout change somehow becomes a personal attack. With v1.1.0, GraphCompose moves further away from low-level PDF drawing and closer to something more useful in real projects: a document layout engine that actually helps you build documents, not just manually suffer through them. What’s new: • compose-first built-in templates • QR codes, barcodes, watermarks, headers/footers, bookmarks, metadata, and page breaks • layout snapshot testing for pagination and geometry regressions • runnable examples for CVs, cover letters, invoices, proposals, and weekly schedules • benchmark and diff tooling • an experimental live preview workflow for faster template iteration The goal is simple: Describe the document structure, and let the engine handle layout, wrapping, pagination, and rendering. That sounds obvious, but anyone who has worked with PDF generation long enough knows it usually is not. Too often, “document generation” really means “draw everything manually and hope page 2 behaves.” This release pushes GraphCompose further toward being a practical platform for real applications, not just a thin wrapper over PDF drawing primitives. Repository: https://lnkd.in/eyJ2DK5b Release: https://lnkd.in/eEASVcVJ Feedback is very welcome. #Java #OpenSource #PDF #DocumentGeneration #SoftwareEngineering
To view or add a comment, sign in
-
-
🚀 Day 24 – Builder Pattern: Designing Objects the Right Way The Builder Pattern is one of the most powerful creational patterns in Java. It solves the age-old problem of complex object creation — without telescoping constructors, without confusion, and without unreadable code. Here’s why the Builder Pattern is a must-have in modern Java architecture: 🔹 1. Solves the Telescoping Constructor Problem Without Builder: new User("Kuldeep", "Vyas", 32, "Pune", true, false); ➡ Hard to read ➡ Easy to misplace parameters With Builder: User.builder().name("Kuldeep").age(32).isActive(true).build(); ➡ Self-descriptive ➡ Error-proof 🔹 2. Enables Immutable Object Design Builder pattern encourages creating: ✔ final fields ✔ immutable objects ✔ side-effect-free models ➡ A perfect fit for microservices, domain models, and concurrency. 🔹 3. Highly Readable & Fluent APIs Builder methods produce code that reads like English: new Order.Builder().withId(1).withItems(items).withTotal(999).build(); ➡ Cleaner code → fewer bugs → easier maintenance. 🔹 4. Handles Optional Fields Gracefully Not every object needs every field. Builder allows selective initialization: ✔ Only required fields ✔ Ignore optional ones ➡ No confusing constructor overloads. 🔹 5. Supports Validation During Build You can enforce rules in build() like: ✔ required fields ✔ non-null checks ✔ business constraints ➡ Prevents invalid object states. 🔹 6. Works Beautifully With Lombok & Records Lombok’s @Builder makes implementation effortless Records + Custom Builders → perfect for DTOs & API models 🔹 7. Clean Separation of Construction vs Representation Builders focus on how objects are created. Objects focus on what they represent. ➡ A clean architectural separation that scales. 🔥 Architect’s Takeaway The Builder Pattern is not just a convenience — it is a design philosophy. It gives you: ✔ Safer object creation ✔ More readable code ✔ Immutable designs ✔ Fewer bugs ✔ Cleaner domain models Exactly what modern enterprise systems need. #100DaysOfJavaArchitecture #BuilderPattern #CleanCode #Java #DesignPatterns #Microservices #SoftwareArchitecture
To view or add a comment, sign in
-
-
Most developers write this kind of code at some point: if (obj instanceof File) else if (obj instanceof Folder) It works. But it doesn’t scale. As your system grows, this approach leads to: Increasing conditional logic Tight coupling Code that is hard to extend and maintain This is where the Composite Design Pattern becomes powerful. Instead of asking: "What type of object is this?" You design your system so that every object responds to the same behavior. Now you simply write: root.show(); No type checking. No conditional branching. Just clean, extensible code. The key idea: Treat individual objects and groups of objects uniformly. This pattern is widely used in: File systems UI component trees (Angular, React) Organization hierarchies Menu structures Why it matters in real projects: Reduces complexity Improves readability Makes systems easier to extend without modifying existing code Good design is not about making code work. It is about making code evolve. If you are preparing for interviews or working on scalable systems, this is a pattern worth mastering. #Java #SystemDesign #DesignPatterns #CleanCode #SoftwareEngineering #BackendDevelopment #InterviewPreparation
To view or add a comment, sign in
-
-
One design pattern that is very useful when object creation is expensive or repetitive is the 𝙋𝙧𝙤𝙩𝙤𝙩𝙮𝙥𝙚 𝙋𝙖𝙩𝙩𝙚𝙧𝙣. Its idea is simple: 𝒊𝒏𝒔𝒕𝒆𝒂𝒅 𝒐𝒇 𝒄𝒓𝒆𝒂𝒕𝒊𝒏𝒈 𝒂 𝒏𝒆𝒘 𝒐𝒃𝒋𝒆𝒄𝒕 𝒇𝒓𝒐𝒎 𝒔𝒄𝒓𝒂𝒕𝒄𝒉, 𝒚𝒐𝒖 𝒄𝒍𝒐𝒏𝒆 𝒂𝒏 𝒆𝒙𝒊𝒔𝒕𝒊𝒏𝒈 𝒐𝒏𝒆. This is helpful when an object contains many fields, complex configuration, or costly initialization logic. Rather than rebuilding the same structure again and again, you create a prototype once, then duplicate it when needed. Why it is useful: • it improves performance when creation is costly • it reduces repetitive initialization code • it helps create similar objects quickly • it is useful when object setup is complex A simple Java example: imagine a DocumentTemplate object with title, layout, theme, permissions, and metadata already configured. Instead of rebuilding every new document manually, you clone the template and then adjust only what is different. That means: • the prototype keeps the base configuration • the clone copies the existing state • the client modifies only the needed values This is the real value of the 𝙋𝙧𝙤𝙩𝙤𝙩𝙮𝙥𝙚 𝙋𝙖𝙩𝙩𝙚𝙧𝙣: reuse fully configured objects instead of recreating them from zero. It is especially useful in: • document templates • UI components • game objects • configuration-heavy models The 𝙋𝙧𝙤𝙩𝙤𝙩𝙮𝙥𝙚 𝙋𝙖𝙩𝙩𝙚𝙧𝙣 is a good reminder that sometimes the best way to create an object is not to build it, but to copy it intelligently. #Java #DesignPatterns #PrototypePattern #SoftwareEngineering #BackendDevelopment #OOP #CleanCode #Architecture
To view or add a comment, sign in
-
-
🚀 Java Design Patterns – Simple & Practical Guide Design patterns are proven solutions to common problems in software design. Here’s a quick breakdown with real use cases 👇 🔹 Creational Patterns (Object Creation) • Singleton – One instance (e.g., DB connection) • Factory Method – Object creation logic hidden • Abstract Factory – Create related objects • Builder – Build complex objects step by step • Prototype – Clone existing objects 🔹 Structural Patterns (Structure) • Adapter – Convert interface • Bridge – Separate abstraction & implementation • Composite – Tree structure (parent-child) • Decorator – Add behavior dynamically • Facade – Simplified interface • Flyweight – Memory optimization • Proxy – Control access 🔹 Behavioral Patterns (Interaction) • Observer – Event notification • Strategy – Change behavior dynamically • Command – Encapsulate request • Iterator – Traverse collection • Mediator – Central communication • Memento – Save/restore state • State – Change behavior based on state • Template Method – Define steps of algorithm • Visitor – Add operations without modifying class • Chain of Responsibility – Request handling chain 💡 Why use them? ✔ Clean code ✔ Reusability ✔ Scalability ✔ Better design #Java #DesignPatterns #SpringBoot #SoftwareEngineering #Coding
To view or add a comment, sign in
-
-
𝗣𝗮𝘁𝘁𝗲𝗿𝗻 𝗠𝗮𝘁𝗰𝗵𝗶𝗻𝗴 𝗶𝗻 𝗖# — 𝗼𝗻𝗲 𝗼𝗳 𝘁𝗵𝗼𝘀𝗲 𝗳𝗲𝗮𝘁𝘂𝗿𝗲𝘀 𝘆𝗼𝘂 𝗱𝗼𝗻’𝘁 𝗮𝗽𝗽𝗿𝗲𝗰𝗶𝗮𝘁𝗲... 𝘂𝗻𝘁𝗶𝗹 𝘆𝗼𝘂 𝗿𝗲𝗮𝗹𝗹𝘆 𝘂𝘀𝗲 𝗶𝘁 The more I work with modern C#, the more I realize how powerful and expressive pattern matching has become — especially since C# 9.0. At first glance, it looks like syntactic sugar. But in reality, it completely changes how you write conditional logic. 𝗜𝗻𝘀𝘁𝗲𝗮𝗱 𝗼𝗳 𝘁𝗵𝗶𝘀: if (x >= 10 && x <= 20) 𝗬𝗼𝘂 𝘄𝗿𝗶𝘁𝗲 𝘁𝗵𝗶𝘀: if (x is >= 10 and <= 20) It may seem small — but it reads like a rule, not an implementation. 𝗜𝗻𝘀𝘁𝗲𝗮𝗱 𝗼𝗳: if (value == "A" || value == "B") 𝗬𝗼𝘂 𝗴𝗲𝘁: if (value is "A" or "B") 𝘊𝘭𝘦𝘢𝘯𝘦𝘳. 𝘔𝘰𝘳𝘦 𝘥𝘦𝘤𝘭𝘢𝘳𝘢𝘵𝘪𝘷𝘦. 𝘓𝘦𝘴𝘴 𝘯𝘰𝘪𝘴𝘦. And it really shines in switch expressions: var result = input switch { >= 0 and < 18 => "Child", >= 18 and < 65 => "Adult", >= 65 => "Senior", _ => "Unknown" }; No nested if-else. No mental overhead. Just clear business rules. 𝗪𝗵𝗮𝘁 𝗜 𝗽𝗲𝗿𝘀𝗼𝗻𝗮𝗹𝗹𝘆 𝗹𝗶𝗸𝗲 𝗺𝗼𝘀𝘁: • and / or / not — makes logic readable • relational patterns (<, >, <=, >=) inside conditions • combining type checks + conditions in one place • ability to express complex rules without losing clarity This is one of those features that pushes code toward a more declarative style — you describe what you want, not how to check it. 𝗔𝗻𝗱 𝗵𝗲𝗿𝗲’𝘀 𝘁𝗵𝗲 𝘁𝗵𝗼𝘂𝗴𝗵𝘁 Imagine having this in JavaScript: if (status is "pending" or "processing") Yes, there are proposals for pattern matching in JavaScript, but they are still not part of the everyday developer experience. And honestly — after working with this in C#, going back feels… a bit like losing a powerful abstraction. Pattern matching in C# is not just about shorter code. It’s about: • readability • fewer bugs in complex conditions • easier maintenance • clearer business logic #csharp #dotnet #softwareengineering #programming #javascript #webdevelopment #cleancode #developers
To view or add a comment, sign in
-
-
If you're still writing if-else chains in C#, you're missing one of the best features added in the last 5 years. Pattern matching with switch expressions makes your code shorter, safer, and faster. Here's the evolution: 𝗦𝘁𝗮𝗴𝗲 𝟭 — Old way (if-else): → 15 lines of nested if-else → Easy to forget a case → Hard to read 𝗦𝘁𝗮𝗴𝗲 𝟮 — Classic switch: → Better than if-else → Still verbose → No way to return values directly 𝗦𝘁𝗮𝗴𝗲 𝟯 — Switch expression: → Single expression → Returns a value → Compiler enforces exhaustiveness → Pattern matching on types, properties, and tuples Real example I use all the time: Instead of this: → if order is null → throw exception → else if order.Status == "Pending" → process → else if order.Status == "Cancelled" → reject → else → log warning You write: var result = order switch { null => throw new ArgumentNullException(), { Status: "Pending" } => Process(order), { Status: "Cancelled" } => Reject(order), _ => LogWarning(order) }; 3 lines instead of 15. Same logic. Clearer intent. What pattern matching unlocks: • Property patterns — match on object properties • Tuple patterns — match on multiple values at once • Relational patterns — use >, <, >= directly in switches • List patterns — match on collections (C# 11+) • Type patterns — combined with deconstruction The compiler also warns you when you miss a case. Your bugs become impossible to ship. If your codebase is full of long if-else chains, you have an easy refactoring win waiting. What C# feature changed how you write code? #dotnet #csharp #patternmatching #cleancode #programming #softwareengineering
To view or add a comment, sign in
-
-
🚀 C# Switch Statement: Old vs New Syntax Explained C# has evolved significantly, and one of the most impactful improvements is the introduction of the switch expression (C# 8.0+) — making code more concise, readable, and safer. 🔹 Old Switch Statement (C# 1.0+) Statement-based structure Requires break to prevent fall-through Doesn’t return a value directly Better for complex logic with multiple operations 🔹 New Switch Expression (C# 8.0+) Expression-based (returns a value) No break needed No fall-through (safer by design) Supports pattern matching, type checks, and conditions Cleaner and more maintainable 💡 When to use what? Use switch statements when handling complex logic blocks Use switch expressions for simple, value-returning conditions ⚡ Why it matters Modern C# encourages writing less code with more clarity. Switch expressions reduce boilerplate and help avoid common bugs like accidental fall-through. ✅ Pro Tip: Prefer switch expressions for most scenarios in modern applications — especially when working with mappings, transformations, or pattern matching. 📌 Final Thought: Write code that’s not just functional, but also readable and maintainable. #CSharp #DotNet #Programming #SoftwareDevelopment #CleanCode #Developers #Coding #Tech
To view or add a comment, sign in
-
-
C#/.NET Performance Tip — String Concatenation in Loops Many .NET developers still use += inside loops without realizing how expensive it really is. Using result += "x" inside a 1,000-iteration loop is 100 times slower than using StringBuilder.Append. When += is used on a string, .NET must: - Allocate a brand new string on every single iteration - Copy all existing characters into the new buffer - Discard the old string for the garbage collector In contrast, when StringBuilder is used, .NET: - Maintains one internal, growable buffer - Appends characters in place with zero extra allocations - Converts to a string only once at the end This pattern applies anywhere you build text dynamically: - Logging and reporting - CSV / JSON generation - Query construction - Any loop-based text assembly A small change can lead to a massive impact at scale. Benchmark results show: 4.21 us (StringBuilder) vs 412.50 us (+=), with 2.1 KB vs 2,500 KB allocated. Pre-size your StringBuilder when you know the capacity — even better. #csharp #dotnet #performance #programming #tips #coding #softwaredevelopment #aspnetcore #developer #cleancode
To view or add a comment, sign in
-
-
𝗦𝗧𝗢𝗣 𝗪𝗥𝗜𝗧𝗜𝗡𝗚 𝟮𝟬-𝗣𝗔𝗥𝗔𝗠𝗘𝗧𝗘𝗥 𝗖𝗢𝗡𝗦𝗧𝗥𝗨𝗖𝗧𝗢𝗥𝗦. 🛑 If you’re still building massive constructors just to ensure your objects aren't "hollow" or invalid, you’re stuck in a boilerplate nightmare. It's time to move to modern C#. Let’s talk about a feature that finally fixed the "Incomplete Object" problem: 𝗿𝗲𝗾𝘂𝗶𝗿𝗲𝗱 members. 𝗧𝗛𝗘 𝗣𝗥𝗢𝗕𝗟𝗘𝗠: 𝗧𝗛𝗘 𝗖𝗢𝗡𝗦𝗧𝗥𝗨𝗖𝗧𝗢𝗥 𝗧𝗥𝗔𝗣 In the old days, you had two bad choices: Constructors: Great for safety, but they become a mess of parameters that are hard to read and maintain. Object Initializers: Look beautiful (new User { Name = "K" }), but they don't force the developer to set anything. You end up with NullReferenceExceptions because someone forgot a field. You were choosing between safety and syntax. 𝗧𝗛𝗘 𝗦𝗢𝗟𝗨𝗧𝗜𝗢𝗡: 𝗧𝗛𝗘 𝗿𝗲𝗾𝘂𝗶𝗿𝗲𝗱 𝗞𝗘𝗬𝗪𝗢𝗥𝗗 Introduced in C# 11, the required modifier gives you the best of both worlds. You get the clean look of object initializers with the "iron-clad" safety of a constructor. 𝗧𝗛𝗘 𝗖𝗢𝗗𝗘: public class User { // The compiler will THROW an error if this isn't set during initialization public required string Email { get; init; } public required string Username { get; init; } public DateTime JoinedDate { get; init; } = DateTime.UtcNow; } // ✅ This works perfectly var user1 = new User { Email = "hello@example.com", Username = "DevGuru" }; // ❌ This won't even COMPILE var user2 = new User { Email = "missing_username@example.com" }; 𝗪𝗛𝗬 𝗧𝗛𝗜𝗦 𝗜𝗦 𝗔 𝗪𝗜𝗡: Compile-Time Safety: If a dev adds a new "required" property to a class, every piece of code that creates that object will break immediately—preventing runtime bugs before they happen. Zero Boilerplate: No more mapping 15 parameters from a constructor to private fields. Readability: Object initializers are much easier to scan than a long list of constructor arguments where you can't tell which string is "FirstName" and which is "LastName." 𝗧𝗛𝗜𝗡𝗞𝗜𝗡𝗚 𝗢𝗨𝗧𝗦𝗜𝗗𝗘 𝗧𝗛𝗘 𝗕𝗢𝗫 📦 Combine required with init properties. This creates Immutable, Mandatory Data Structures. You ensure the data is there when created, and you guarantee it can't be changed accidentally later. It’s the ultimate "defensive programming" move. 𝗧𝗛𝗘 𝗧𝗔𝗞𝗘𝗔𝗪𝗔𝗬: Stop doing the manual labor of checking for nulls or writing endless constructor overloads. Use required to let the compiler be your bodyguard. #CSharp #DotNet #CleanCode #ProgrammingTips #SoftwareArchitecture #CodingLife #Innovation
To view or add a comment, sign in
Explore content categories
- Career
- Productivity
- Finance
- Soft Skills & Emotional Intelligence
- Project Management
- Education
- Technology
- Leadership
- Ecommerce
- User Experience
- Recruitment & HR
- Customer Experience
- Real Estate
- Marketing
- Sales
- Retail & Merchandising
- Science
- Supply Chain Management
- Future Of Work
- Consulting
- Writing
- Economics
- Artificial Intelligence
- Employee Experience
- Workplace Trends
- Fundraising
- Networking
- Corporate Social Responsibility
- Negotiation
- Communication
- Engineering
- Hospitality & Tourism
- Business Strategy
- Change Management
- Organizational Culture
- Design
- Innovation
- Event Planning
- Training & Development