From 74441c91ccc7fa197c95628618551dd572bf3fad Mon Sep 17 00:00:00 2001
From: Manuel Friedli <manuel@fritteli.ch>
Date: Sat, 3 Oct 2020 22:33:58 +0200
Subject: [PATCH] Render PDFs in dynamic size

---
 src/main/java/ch/fritteli/labyrinth/Main.java |  4 +-
 .../ch/fritteli/labyrinth/PDFRenderer.java    | 40 +++++++++++--------
 2 files changed, 26 insertions(+), 18 deletions(-)

diff --git a/src/main/java/ch/fritteli/labyrinth/Main.java b/src/main/java/ch/fritteli/labyrinth/Main.java
index e760bb7..1f7c050 100644
--- a/src/main/java/ch/fritteli/labyrinth/Main.java
+++ b/src/main/java/ch/fritteli/labyrinth/Main.java
@@ -7,8 +7,8 @@ import java.nio.file.Paths;
 
 public class Main {
     public static void main(@NonNull final String[] args) {
-        int width = 20;
-        int height = 20;
+        int width = 50;
+        int height = 50;
         final Labyrinth labyrinth = new Labyrinth(width, height);
         final TextRenderer textRenderer = TextRenderer.newInstance();
         final HTMLRenderer htmlRenderer = HTMLRenderer.newInstance();
diff --git a/src/main/java/ch/fritteli/labyrinth/PDFRenderer.java b/src/main/java/ch/fritteli/labyrinth/PDFRenderer.java
index abce7a9..5dad450 100644
--- a/src/main/java/ch/fritteli/labyrinth/PDFRenderer.java
+++ b/src/main/java/ch/fritteli/labyrinth/PDFRenderer.java
@@ -4,6 +4,7 @@ import lombok.NonNull;
 import org.apache.pdfbox.pdmodel.PDDocument;
 import org.apache.pdfbox.pdmodel.PDPage;
 import org.apache.pdfbox.pdmodel.PDPageContentStream;
+import org.apache.pdfbox.pdmodel.common.PDRectangle;
 
 import java.awt.*;
 import java.io.ByteArrayOutputStream;
@@ -22,17 +23,20 @@ public class PDFRenderer implements Renderer<byte[]> {
     @Override
     @NonNull
     public byte[] render(@NonNull final Labyrinth labyrinth) {
+        final float scale = 10;
+        final float margin = 5;
+        final float pageWidth = labyrinth.getWidth() * scale + 2 * margin;
+        final float pageHeight = labyrinth.getHeight() * scale + 2 * margin;
+
         final PDDocument pdDocument = new PDDocument();
-        final PDPage page = new PDPage();
+        final PDPage page = new PDPage(new PDRectangle(pageWidth, pageHeight));
         pdDocument.addPage(page);
         try (PDPageContentStream pdPageContentStream = new PDPageContentStream(pdDocument, page)) {
-            pdPageContentStream.setLineCapStyle(BasicStroke.CAP_BUTT);
+            pdPageContentStream.setLineCapStyle(BasicStroke.CAP_ROUND);
             pdPageContentStream.setLineJoinStyle(BasicStroke.JOIN_MITER);
             pdPageContentStream.setLineWidth(1.0f);
             pdPageContentStream.setStrokingColor(Color.BLACK);
             pdPageContentStream.setNonStrokingColor(Color.BLACK);
-            final float scale = 10;
-            final float margin = 5;
             this.drawHorizonzalLines(labyrinth, pdPageContentStream, margin, scale);
             this.drawVerticalLines(labyrinth, pdPageContentStream, margin, scale);
         } catch (IOException e) {
@@ -49,25 +53,27 @@ public class PDFRenderer implements Renderer<byte[]> {
     }
 
     private void drawHorizonzalLines(@NonNull final Labyrinth labyrinth, @NonNull final PDPageContentStream pdPageContentStream, final float margin, final float scale) throws IOException {
+        // PDF has the origin in the lower left corner. We want it in the upper left corner, hence some magic is required.
+        final float labyrinthHeight = labyrinth.getHeight() * scale;
         for (int y = 0; y < labyrinth.getHeight(); y++) {
             boolean isPainting = false;
             for (int x = 0; x < labyrinth.getWidth(); x++) {
                 final Tile currentTile = labyrinth.getTileAt(x, y);
                 if (currentTile.hasWallAt(Direction.TOP)) {
                     if (!isPainting) {
-                        pdPageContentStream.moveTo(x * scale + margin, y * scale + margin);
+                        pdPageContentStream.moveTo(x * scale + margin, labyrinthHeight - y * scale + margin);
                         isPainting = true;
                     }
                 } else {
                     if (isPainting) {
-                        pdPageContentStream.lineTo(x * scale + margin, y * scale + margin);
+                        pdPageContentStream.lineTo(x * scale + margin, labyrinthHeight - y * scale + margin);
                         pdPageContentStream.stroke();
                         isPainting = false;
                     }
                 }
             }
             if (isPainting) {
-                pdPageContentStream.lineTo(labyrinth.getWidth() * scale + margin, y * scale + margin);
+                pdPageContentStream.lineTo(labyrinth.getWidth() * scale + margin, labyrinthHeight - y * scale + margin);
                 pdPageContentStream.stroke();
             }
         }
@@ -77,43 +83,45 @@ public class PDFRenderer implements Renderer<byte[]> {
             final Tile currentTile = labyrinth.getTileAt(x, y - 1);
             if (currentTile.hasWallAt(Direction.BOTTOM)) {
                 if (!isPainting) {
-                    pdPageContentStream.moveTo(x * scale + margin, y * scale + margin);
+                    pdPageContentStream.moveTo(x * scale + margin, labyrinthHeight - y * scale + margin);
                     isPainting = true;
                 }
             } else {
                 if (isPainting) {
-                    pdPageContentStream.lineTo(x * scale + margin, y * scale + margin);
+                    pdPageContentStream.lineTo(x * scale + margin, labyrinthHeight - y * scale + margin);
                     pdPageContentStream.stroke();
                     isPainting = false;
                 }
             }
         }
         if (isPainting) {
-            pdPageContentStream.lineTo(labyrinth.getWidth() * scale + margin, labyrinth.getHeight() * scale + margin);
+            pdPageContentStream.lineTo(labyrinth.getWidth() * scale + margin, labyrinthHeight - labyrinth.getHeight() * scale + margin);
             pdPageContentStream.stroke();
         }
     }
 
     private void drawVerticalLines(@NonNull final Labyrinth labyrinth, @NonNull final PDPageContentStream pdPageContentStream, final float margin, final float scale) throws IOException {
+        // PDF has the origin in the lower left corner. We want it in the upper left corner, hence some magic is required.
+        final float labyrinthHeight = labyrinth.getHeight() * scale;
         for (int x = 0; x < labyrinth.getWidth(); x++) {
             boolean isPainting = false;
             for (int y = 0; y < labyrinth.getHeight(); y++) {
                 final Tile currentTile = labyrinth.getTileAt(x, y);
                 if (currentTile.hasWallAt(Direction.LEFT)) {
                     if (!isPainting) {
-                        pdPageContentStream.moveTo(x * scale + margin, y * scale + margin);
+                        pdPageContentStream.moveTo(x * scale + margin, labyrinthHeight - y * scale + margin);
                         isPainting = true;
                     }
                 } else {
                     if (isPainting) {
-                        pdPageContentStream.lineTo(x * scale + margin, y * scale + margin);
+                        pdPageContentStream.lineTo(x * scale + margin, labyrinthHeight - y * scale + margin);
                         pdPageContentStream.stroke();
                         isPainting = false;
                     }
                 }
             }
             if (isPainting) {
-                pdPageContentStream.lineTo(x * scale + margin, labyrinth.getHeight() * scale + margin);
+                pdPageContentStream.lineTo(x * scale + margin, labyrinthHeight - labyrinth.getHeight() * scale + margin);
                 pdPageContentStream.stroke();
             }
         }
@@ -123,19 +131,19 @@ public class PDFRenderer implements Renderer<byte[]> {
             final Tile currentTile = labyrinth.getTileAt(x - 1, y);
             if (currentTile.hasWallAt(Direction.RIGHT)) {
                 if (!isPainting) {
-                    pdPageContentStream.moveTo(x * scale + margin, y * scale + margin);
+                    pdPageContentStream.moveTo(x * scale + margin, labyrinthHeight - y * scale + margin);
                     isPainting = true;
                 }
             } else {
                 if (isPainting) {
-                    pdPageContentStream.lineTo(x * scale + margin, y * scale + margin);
+                    pdPageContentStream.lineTo(x * scale + margin, labyrinthHeight - y * scale + margin);
                     pdPageContentStream.stroke();
                     isPainting = false;
                 }
             }
         }
         if (isPainting) {
-            pdPageContentStream.lineTo(labyrinth.getWidth() * scale + margin, labyrinth.getHeight() * scale + margin);
+            pdPageContentStream.lineTo(labyrinth.getWidth() * scale + margin, labyrinthHeight - labyrinth.getHeight() * scale + margin);
             pdPageContentStream.stroke();
         }
     }