diff --git a/pom.xml b/pom.xml
index aa350a3..8c063a5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -25,6 +25,11 @@
io.vavr
vavr
+
+ org.apache.pdfbox
+ pdfbox
+ 2.0.20
+
org.junit.jupiter
junit-jupiter-api
diff --git a/src/main/java/ch/fritteli/labyrinth/Main.java b/src/main/java/ch/fritteli/labyrinth/Main.java
index ff3821a..e760bb7 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 = 80;
- int height = 40;
+ int width = 20;
+ int height = 20;
final Labyrinth labyrinth = new Labyrinth(width, height);
final TextRenderer textRenderer = TextRenderer.newInstance();
final HTMLRenderer htmlRenderer = HTMLRenderer.newInstance();
@@ -18,6 +18,8 @@ public class Main {
.setTargetSolutionFile(userHome.resolve("labyrinth-solution.txt"));
final HTMLFileRenderer htmlFileRenderer = HTMLFileRenderer.newInstance()
.setTargetFile(userHome.resolve("labyrinth.html"));
+ final PDFFileRenderer pdfFileRenderer = PDFFileRenderer.newInstance().setTargetFile(userHome.resolve("labyrinth.pdf"));
+
// Render Labyrinth to stdout
System.out.println(textRenderer.render(labyrinth));
// Render Labyrinth solution to stdout
@@ -28,5 +30,7 @@ public class Main {
System.out.println(textFileRenderer.render(labyrinth));
// Render HTML to file
System.out.println(htmlFileRenderer.render(labyrinth));
+ // Render PDF to file
+ System.out.println(pdfFileRenderer.render(labyrinth));
}
}
diff --git a/src/main/java/ch/fritteli/labyrinth/PDFFileRenderer.java b/src/main/java/ch/fritteli/labyrinth/PDFFileRenderer.java
new file mode 100644
index 0000000..7772f9b
--- /dev/null
+++ b/src/main/java/ch/fritteli/labyrinth/PDFFileRenderer.java
@@ -0,0 +1,56 @@
+package ch.fritteli.labyrinth;
+
+import lombok.NonNull;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+public class PDFFileRenderer implements Renderer {
+ @NonNull
+ private static final PDFRenderer PDF_RENDERER = PDFRenderer.newInstance();
+ private Path targetFile;
+
+ private PDFFileRenderer() {
+ try {
+ this.targetFile = Files.createTempFile("labyrinth_", ".pdf");
+ } catch (IOException e) {
+ System.err.println("Unable to set default target file.");
+ e.printStackTrace();
+ }
+ }
+
+ @NonNull
+ public static PDFFileRenderer newInstance() {
+ return new PDFFileRenderer();
+ }
+
+ public boolean isTargetFileDefinedAndWritable() {
+ return this.targetFile != null && this.targetFile.toFile().canWrite();
+ }
+
+ @NonNull
+ public PDFFileRenderer setTargetFile(@NonNull final Path targetFile) {
+ this.targetFile = targetFile;
+ return this;
+ }
+
+ @Override
+ public Path render(@NonNull final Labyrinth labyrinth) {
+ if (!this.isTargetFileDefinedAndWritable()) {
+ try {
+ Files.createFile(this.targetFile);
+ } catch (IOException e) {
+ throw new IllegalArgumentException("Cannot write to target file.", e);
+ }
+ }
+ final byte[] bytes = PDF_RENDERER.render(labyrinth);
+ try {
+ Files.write(this.targetFile, bytes);
+ } catch (IOException e) {
+ System.err.println("Failed writing to file " + this.targetFile.normalize().toString());
+ e.printStackTrace();
+ }
+ return this.targetFile;
+ }
+}
diff --git a/src/main/java/ch/fritteli/labyrinth/PDFRenderer.java b/src/main/java/ch/fritteli/labyrinth/PDFRenderer.java
new file mode 100644
index 0000000..abce7a9
--- /dev/null
+++ b/src/main/java/ch/fritteli/labyrinth/PDFRenderer.java
@@ -0,0 +1,142 @@
+package ch.fritteli.labyrinth;
+
+import lombok.NonNull;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDPageContentStream;
+
+import java.awt.*;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+public class PDFRenderer implements Renderer {
+ private PDFRenderer() {
+
+ }
+
+ @NonNull
+ public static PDFRenderer newInstance() {
+ return new PDFRenderer();
+ }
+
+ @Override
+ @NonNull
+ public byte[] render(@NonNull final Labyrinth labyrinth) {
+ final PDDocument pdDocument = new PDDocument();
+ final PDPage page = new PDPage();
+ pdDocument.addPage(page);
+ try (PDPageContentStream pdPageContentStream = new PDPageContentStream(pdDocument, page)) {
+ pdPageContentStream.setLineCapStyle(BasicStroke.CAP_BUTT);
+ 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) {
+ e.printStackTrace();
+ }
+ final ByteArrayOutputStream output = new ByteArrayOutputStream();
+ try {
+ pdDocument.save(output);
+ pdDocument.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return output.toByteArray();
+ }
+
+ private void drawHorizonzalLines(@NonNull final Labyrinth labyrinth, @NonNull final PDPageContentStream pdPageContentStream, final float margin, final float scale) throws IOException {
+ 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);
+ isPainting = true;
+ }
+ } else {
+ if (isPainting) {
+ pdPageContentStream.lineTo(x * scale + margin, y * scale + margin);
+ pdPageContentStream.stroke();
+ isPainting = false;
+ }
+ }
+ }
+ if (isPainting) {
+ pdPageContentStream.lineTo(labyrinth.getWidth() * scale + margin, y * scale + margin);
+ pdPageContentStream.stroke();
+ }
+ }
+ boolean isPainting = false;
+ int y = labyrinth.getHeight();
+ for (int x = 0; x < labyrinth.getWidth(); x++) {
+ final Tile currentTile = labyrinth.getTileAt(x, y - 1);
+ if (currentTile.hasWallAt(Direction.BOTTOM)) {
+ if (!isPainting) {
+ pdPageContentStream.moveTo(x * scale + margin, y * scale + margin);
+ isPainting = true;
+ }
+ } else {
+ if (isPainting) {
+ pdPageContentStream.lineTo(x * scale + margin, y * scale + margin);
+ pdPageContentStream.stroke();
+ isPainting = false;
+ }
+ }
+ }
+ if (isPainting) {
+ pdPageContentStream.lineTo(labyrinth.getWidth() * scale + margin, 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 {
+ 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);
+ isPainting = true;
+ }
+ } else {
+ if (isPainting) {
+ pdPageContentStream.lineTo(x * scale + margin, y * scale + margin);
+ pdPageContentStream.stroke();
+ isPainting = false;
+ }
+ }
+ }
+ if (isPainting) {
+ pdPageContentStream.lineTo(x * scale + margin, labyrinth.getHeight() * scale + margin);
+ pdPageContentStream.stroke();
+ }
+ }
+ boolean isPainting = false;
+ int x = labyrinth.getWidth();
+ for (int y = 0; y < labyrinth.getHeight(); y++) {
+ final Tile currentTile = labyrinth.getTileAt(x - 1, y);
+ if (currentTile.hasWallAt(Direction.RIGHT)) {
+ if (!isPainting) {
+ pdPageContentStream.moveTo(x * scale + margin, y * scale + margin);
+ isPainting = true;
+ }
+ } else {
+ if (isPainting) {
+ pdPageContentStream.lineTo(x * scale + margin, y * scale + margin);
+ pdPageContentStream.stroke();
+ isPainting = false;
+ }
+ }
+ }
+ if (isPainting) {
+ pdPageContentStream.lineTo(labyrinth.getWidth() * scale + margin, labyrinth.getHeight() * scale + margin);
+ pdPageContentStream.stroke();
+ }
+ }
+}