diff --git a/.gitignore b/.gitignore
index 5465036af3679d4d7d3de04e780ee41c4ffe58c3..f930235d667b3385e9c0d64eccd425e3f1dc0d7e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,7 @@ out
 *.class
 *.jar
 *.tar
+
+# Miscellaneous
+*.DS_Store
+*.db
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 8c8f4141fd1c7ddac072e3d18ff34ce73899b79c..676341377ea008279797379fb2e1cbcf2fe49632 100644
--- a/build.gradle
+++ b/build.gradle
@@ -30,8 +30,7 @@ dependencies {
     // Use JUnit test framework
     compile group: 'commons-cli', name: 'commons-cli', version: '1.4'
     testImplementation('org.junit.jupiter:junit-jupiter:5.4.2')
-
-
+    
     // Logging
     compile group: 'org.slf4j', name: 'slf4j-api', version: '1.7.26'
     compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3'
@@ -45,8 +44,6 @@ dependencies {
     compile group: 'com.beust', name: 'jcommander', version: '1.64'
     // https://mvnrepository.com/artifact/org.slf4j/slf4j-api
     compile group: 'org.slf4j', name: 'slf4j-api', version: '1.7.24'
-
-
 }
 
 test {
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/AbstractCanvas.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/AbstractCanvas.java
new file mode 100644
index 0000000000000000000000000000000000000000..b2e63738a499590c5855c80cbbb9559aa59ae848
--- /dev/null
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/AbstractCanvas.java
@@ -0,0 +1,122 @@
+package de.tudresden.inf.mci.brailleplot.rendering;
+
+import de.tudresden.inf.mci.brailleplot.configparser.Format;
+import de.tudresden.inf.mci.brailleplot.configparser.Printer;
+import de.tudresden.inf.mci.brailleplot.printabledata.PrintableData;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListIterator;
+
+/**
+ * Representation of a target onto which can be drawn. It wraps a {@link PrintableData} instance and specifies the size of the drawing area (in mm).
+ * @author Leonard Kupper
+ * @version 2019.07.22
+ */
+public abstract class AbstractCanvas {
+
+    Printer mPrinter;
+    Format mFormat;
+
+    Rectangle mPrintableArea;
+
+    List<PrintableData> mPageContainer;
+
+    AbstractCanvas(final Printer printer, final Format format) throws InsufficientRenderingAreaException {
+        mPrinter = printer;
+        mFormat = format;
+        mPageContainer = new ArrayList<>();
+
+        readConfig();
+    }
+
+    private void readConfig() throws InsufficientRenderingAreaException {
+
+        // New approach using a box model:
+
+        // Create a page box
+        int pageWidth = mFormat.getProperty("page.width").toInt();
+        int pageHeight = mFormat.getProperty("page.height").toInt();
+        Rectangle pageBox = new Rectangle(0, 0, pageWidth, pageHeight);
+
+        // Create a margin box
+        int marginTop = mFormat.getProperty("margin.top").toInt();
+        int marginLeft = mFormat.getProperty("margin.left").toInt();
+        int marginBottom = mFormat.getProperty("margin.bottom").toInt();
+        int marginRight = mFormat.getProperty("margin.right").toInt();
+        Rectangle marginBox = new Rectangle(pageBox);
+        try {
+            marginBox.removeFromTop(marginTop);
+            marginBox.removeFromLeft(marginLeft);
+            marginBox.removeFromBottom(marginBottom);
+            marginBox.removeFromRight(marginRight);
+        } catch (Rectangle.OutOfSpaceException e) {
+            throw new InsufficientRenderingAreaException("The sum of the defined margins is bigger than the page size.", e);
+        }
+
+        // Create a constraint box
+        double constraintTop = mPrinter.getProperty("constraint.top").toDouble();
+        double constraintLeft = mPrinter.getProperty("constraint.left").toDouble();
+        double constraintHeight, constraintWidth;
+        if (mPrinter.getPropertyNames().contains("constraint.height")) {
+            constraintHeight = mPrinter.getProperty("constraint.height").toDouble();
+        } else {
+            constraintHeight = Integer.MAX_VALUE;
+        }
+        if (mPrinter.getPropertyNames().contains("constraint.width")) {
+            constraintWidth = mPrinter.getProperty("constraint.width").toDouble();
+        } else {
+            constraintWidth = Integer.MAX_VALUE;
+        }
+        Rectangle constraintBox = new Rectangle(constraintLeft, constraintTop, constraintWidth, constraintHeight);
+
+        mPrintableArea = calculatePrintingArea(marginBox, constraintBox);
+
+    }
+
+    /**
+     * A universal help function to calculate the printable area from original page size, desired minimum margins
+     * and the given area constraints of the printer.
+     * @param marginBox A rectangle representing the page with cropped edges representing the margins.
+     * @param constraintBox A rectangle representing the printer constraint as [x = constraint x, y = constraint y,
+     *                      w = constraint width, h = constraint height]
+     * @return A rectangle representing the valid printing area.
+     */
+    final Rectangle calculatePrintingArea(final Rectangle marginBox, final Rectangle constraintBox) {
+        return marginBox.intersectedWith(constraintBox).translatedBy(-1 * constraintBox.getX(), -1 * constraintBox.getY());
+    }
+
+    /**
+     * This method is supposed to return the full width of the canvas.
+     * @return The width of the canvas in millimeters.
+     */
+    public double getPrintableWidth() {
+        return mPrintableArea.getWidth();
+    }
+
+    /**
+     * This method is supposed to return the full height of the canvas.
+     * @return The height of the canvas in millimeters.
+     */
+    public double getPrintableHeight() {
+        return mPrintableArea.getHeight();
+    }
+
+    /**
+     * Get the number of pages in the canvas.
+     * @return The number of pages.
+     */
+    public int getPageCount() {
+        return mPageContainer.size();
+    }
+
+    /**
+     * Get an Iterator for the PrintableData instances representing the canvas pages. The single instances should be
+     * casted to the regarding concrete type depending on the canvas implementation.
+     * @return A {@link ListIterator}&lt;{@link PrintableData}&gt;.
+     */
+    public ListIterator<PrintableData> getPageIterator() {
+        return mPageContainer.listIterator();
+    }
+
+}
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/Axis.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/Axis.java
new file mode 100644
index 0000000000000000000000000000000000000000..aa6ddc41ad0328221b7ff8981c40cd0d32e420bc
--- /dev/null
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/Axis.java
@@ -0,0 +1,187 @@
+package de.tudresden.inf.mci.brailleplot.rendering;
+
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * The representation of a visible axis with a line, tickmarks and labels.
+ * @author Leonard Kupper
+ * @version 2019.07.09
+ */
+public class Axis implements Renderable {
+
+    private Type mType;
+    private double mOriginX;
+    private double mOriginY;
+    private double mStepWidth;
+    private double mTickSize;
+    private Rectangle mBoundary;
+    private Map<Integer, String> mLabels;
+
+    /**
+     * Constructor. Creates an instance of a new axis. Distances and sizes are stored as double and are used in a generic manner.
+     * The interpretation of the values must be done by the rasterizer or plotter.
+     * @param type Just changes the orientation of the axis. Can be either {@link Axis.Type#X_AXIS} or {@link Axis.Type#Y_AXIS}.
+     * @param originX The x coordinate of the position where the axis line and the tickmark and label corresponding to the value '0' is placed.
+     * @param originY The y coordinate of the position where the axis line and the tickmark and label corresponding to the value '0' is placed.
+     * @param stepWidth The distance between two tickmarks on the axis.
+     * @param tickSize The size and orientation of the tickmarks. The absolute value controls the length, the sign controls on which side they are displayed.
+     */
+    public Axis(final Type type, final double originX, final double originY, final double stepWidth,
+                final double tickSize) {
+        setType(type);
+        setOriginX(originX);
+        setOriginY(originY);
+        setStepWidth(stepWidth);
+        setTickSize(tickSize);
+    }
+
+    /**
+     * Get the type of the axis.
+     * @return The axis type as {@link Axis.Type}.
+     */
+    public Type getType() {
+        return mType;
+    }
+
+    /**
+     * Set the type of the axis.
+     * @param type The axis type as {@link Axis.Type}.
+     */
+    public void setType(final Type type) {
+        mType = Objects.requireNonNull(type);
+    }
+
+    /**
+     * Get the x position of the coordinate origin. The position is not to be mistaken with the coordinate. It
+     * determines where on the canvas the axis should be positioned, not which x value is positioned at the y-axis.
+     * @return The x position of the axis on canvas.
+     */
+    public double getOriginX() {
+        return mOriginX;
+    }
+
+    /**
+     * Set the x position of the coordinate origin. The position is not to be mistaken with the coordinate. It
+     * determines where on the canvas the axis should be positioned, not which x value is positioned at the y-axis.
+     * @param originX The x position of the axis on canvas.
+     */
+    public void setOriginX(final double originX) {
+        mOriginX = originX;
+    }
+
+    /**
+     * Get the y position of the coordinate origin. The position is not to be mistaken with the coordinate. It
+     * determines where on the canvas the axis should be positioned, not which y value is positioned at the x-axis.
+     * @return The y position of the axis on canvas.
+     */
+    public double getOriginY() {
+        return mOriginY;
+    }
+
+    /**
+     * Set the y position of the coordinate origin. The position is not to be mistaken with the coordinate. It
+     * determines where on the canvas the axis should be positioned, not which y value is positioned at the x-axis.
+     * @param originY The y position of the axis on canvas.
+     */
+    public void setOriginY(final double originY) {
+        mOriginY = originY;
+    }
+
+    /**
+     * Get the distance between neighboring axis tickmarks.
+     * @return The tickmark distance.
+     */
+    public double getStepWidth() {
+        return mStepWidth;
+    }
+
+    /**
+     * Set the distance between neighboring axis tickmarks.
+     * @param stepWidth The tickmark distance.
+     */
+    public void setStepWidth(final double stepWidth) {
+        if (stepWidth <= 0) {
+            throw new IllegalArgumentException("Axis step width can't be negative or zero.");
+        }
+        mStepWidth = stepWidth;
+    }
+
+    /**
+     * Get the length of the axis tickmark lines. The values sign determines the tickmark orientation. A value of zero
+     * indicates that no visible tickmarks are set.
+     * @return The tickmark line length.
+     */
+    public double getTickSize() {
+        return mTickSize;
+    }
+
+    /**
+     * Set the length of the axis tickmark lines. The values sign determines the tickmark orientation. A value of zero
+     * indicates that no visible tickmarks are set.
+     * @param tickSize The tickmark line length.
+     */
+    public void setTickSize(final double tickSize) {
+        mTickSize = tickSize;
+    }
+
+    /**
+     * Get the labels that are drawn next to the axis tickmarks as {@link Map}. The key determines the position of the
+     * label (positive values = labels toward positive value range, 0 = at coordinate origin, negative values = labels
+     * toward negative value range). The value is a String representing the label text.
+     * Not every position must be supplied with a label.
+     * @return A {@link Map} containing all labels.
+     */
+    public Map<Integer, String> getLabels() {
+        return mLabels;
+    }
+    /**
+     * Set the labels that are drawn next to the axis tickmarks as {@link Map}. The key determines the position of the
+     * label (positive values = labels toward positive value range, 0 = at coordinate origin, negative values = labels
+     * toward negative value range). The value is a String representing the label text.
+     * Not every position must be supplied with a label.
+     * @param labels A {@link Map} containing all labels.
+     */
+    public void setLabels(final Map<Integer, String> labels) {
+        mLabels = Objects.requireNonNull(labels);
+    }
+
+    /**
+     * Check whether any labels are set. This should be done prior to trying accessing the labels via {@link #getLabels()}.
+     * @return True if labels are set, else False.
+     */
+    public boolean hasLabels() {
+        return !Objects.isNull(mLabels);
+    }
+
+    /**
+     * Get the area of the canvas on which the area is to be drawn.
+     * @return A {@link Rectangle} representing the area.
+     */
+    public Rectangle getBoundary() {
+        return Objects.requireNonNull(mBoundary);
+    }
+
+    /**
+     * Set the area of the canvas on which the area is to be drawn.
+     * @param boundary A {@link Rectangle} representing the area.
+     */
+    public void setBoundary(final Rectangle boundary) {
+        mBoundary = Objects.requireNonNull(boundary);
+    }
+
+    /**
+     * Check whether a boundary is set.
+     * @return True if boundary is set, else False.
+     */
+    public boolean hasBoundary() {
+        return !Objects.isNull(mBoundary);
+    }
+
+    /**
+     * Representation of the axis type / orientation.
+     */
+    enum Type {
+        X_AXIS, Y_AXIS;
+    }
+}
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/BrailleText.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/BrailleText.java
new file mode 100644
index 0000000000000000000000000000000000000000..afa027dec3057ae0c1f148e7a8f7a7ea52d2b037
--- /dev/null
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/BrailleText.java
@@ -0,0 +1,56 @@
+package de.tudresden.inf.mci.brailleplot.rendering;
+
+import java.util.Objects;
+
+/**
+ * Simple representation of a braille text field.
+ * @author Leonard Kupper
+ * @version 2019.07.22
+ */
+public class BrailleText implements Renderable {
+
+    private String mContent;
+    private Rectangle mArea;
+
+    /**
+     * Constructor. Creates a braille text field.
+     * @param content The actual text of the text field.
+     * @param area The desired area for the text to be rendered on.
+     */
+    public BrailleText(final String content, final Rectangle area) {
+        setText(content);
+        setArea(area);
+    }
+
+    /**
+     * Sets a new text content.
+     * @param content The new content for the text field.
+     */
+    public void setText(final String content) {
+        mContent = Objects.requireNonNull(content);
+    }
+
+    /**
+     * Gets the current text content of the text field.
+     * @return A {@link String} containing the text.
+     */
+    public String getText() {
+        return mContent;
+    }
+
+    /**
+     * Sets a new area for the text field.
+     * @param area The new area for the text field.
+     */
+    public void setArea(final Rectangle area) {
+        mArea = Objects.requireNonNull(area);
+    }
+
+    /**
+     * Gets the current area of the text field.
+     * @return The area of the text field.
+     */
+    public Rectangle getArea() {
+        return mArea;
+    }
+}
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/BrailleTextRasterizer.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/BrailleTextRasterizer.java
new file mode 100644
index 0000000000000000000000000000000000000000..eab684ebd70d05f39ec8d485a822d65996ab64b4
--- /dev/null
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/BrailleTextRasterizer.java
@@ -0,0 +1,36 @@
+package de.tudresden.inf.mci.brailleplot.rendering;
+
+/**
+ * A rasterizer for text on braille grids. This class is still a stub and must be implemented!
+ * @version 2019.07.21
+ * @author Leonard Kupper
+ */
+public final class BrailleTextRasterizer implements Rasterizer<BrailleText> {
+    @Override
+    public void rasterize(final BrailleText data, final RasterCanvas canvas) throws InsufficientRenderingAreaException {
+        // TODO: rasterize the text (Take different grids into consideration! 6-dot / 8-dot)
+        // Until then, we just display dummy characters
+        int x = data.getArea().intWrapper().getX();
+        int y = data.getArea().intWrapper().getY();
+        for (int i = 0; i < data.getText().length(); i++) {
+            canvas.getCurrentPage().setValue(y, x, true);
+            canvas.getCurrentPage().setValue(y + 1, x + 1, true);
+            canvas.getCurrentPage().setValue(y + 2, x, true);
+            x += 2;
+        }
+    }
+
+    // TODO: Completely replace with help methods to calculate suited area for left or right alignment of given text.
+    public int calculateRequiredHeight(final String text, final int xPos, final int yPos, final int maxWidth,
+                                           final RasterCanvas canvas) {
+        // TODO: Add calculations for required height to fit the given text into the given canvas. (Linebreaks!)
+        // Until then we use a dummy value assuming one line of text:
+        return canvas.getCellHeight();
+    }
+
+    public int calculateRequiredWidth(final String text, final int xPos, final int yPos, final RasterCanvas canvas) {
+        // TODO: Add calculations for required width to fit the given text into the given canvas. (Extra spacing for equidistant grid!)
+        // Until then we use a dummy value assuming single character on braille grid:
+        return canvas.getCellWidth();
+    }
+}
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/FunctionalRasterizer.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/FunctionalRasterizer.java
new file mode 100644
index 0000000000000000000000000000000000000000..bd9e347ee4d1fb77d5bad6c1831a4cf3367b4c9d
--- /dev/null
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/FunctionalRasterizer.java
@@ -0,0 +1,53 @@
+package de.tudresden.inf.mci.brailleplot.rendering;
+
+/**
+ * FunctionalRasterizer. This class implements a concrete rasterizer via a functional interface.
+ * The rasterizing algorithm to be used is passed to the constructor as lambda function, method reference or rasterizer implementation.
+ * @param <T> The concrete diagram class which can be rasterized with the rasterizer.
+ * @author Leonard Kupper
+ * @version 2019.07.20
+ */
+public class FunctionalRasterizer<T extends Renderable> implements Rasterizer {
+
+    private Class<? extends T> mSupportedRenderableClass;
+    private ThrowingBiConsumer<T, RasterCanvas, InsufficientRenderingAreaException> mRasterizingAlgorithm;
+
+    /**
+     * Constructor. Creates a new rasterizer from either a given rasterizer implementation or (keep in mind that
+     * Rasterizer is a functional interface) from a ThrowingBiConsumer&lt;T, RasterCanvas, InsufficientRenderingAreaException&gt; method reference.
+     * @param supportedRenderableClass A reference to the accepted diagram class (e.g. 'BarChart.class')
+     * @param rasterizer A reference to a Rasterizer instance.
+     */
+    public FunctionalRasterizer(
+            final Class<T> supportedRenderableClass,
+            final Rasterizer<T> rasterizer) {
+        mSupportedRenderableClass = supportedRenderableClass;
+        mRasterizingAlgorithm = rasterizer::rasterize;
+    }
+
+    @Override
+    public void rasterize(final Renderable data, final RasterCanvas canvas) throws InsufficientRenderingAreaException {
+        // invoke the given rasterizing algorithm
+        T safeData = safeCast(data);
+        mRasterizingAlgorithm.accept(safeData, canvas);
+    }
+
+    final Class<? extends T> getSupportedRenderableClass() {
+        return mSupportedRenderableClass;
+    }
+
+    @SuppressWarnings("unchecked")
+    // This is allowed, because the code that calls the rasterize method does a lookup based on the renderable class beforehand
+    // and will only select the appropriate rasterizer. (See FunctionalRenderingBase)
+    // Since the FunctionalRasterizer is package private, there is no way to invoke it with the wrong type from 'outside'.
+    // Should somebody still force this to happen by intentional tampering, we have no choice but to catch this disgrace.
+    private T safeCast(final Renderable data) {
+        try {
+            return getSupportedRenderableClass().cast(data);
+        } catch (ClassCastException e) {
+            // wow
+            throw new IllegalArgumentException("Wrong renderable type! This rasterizer is not meant to be used with '"
+                    + data.getClass().getCanonicalName() + "'", e);
+        }
+    }
+}
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/FunctionalRenderingBase.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/FunctionalRenderingBase.java
new file mode 100644
index 0000000000000000000000000000000000000000..7938933105a95a29757ad23aa27e198f5f728d88
--- /dev/null
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/FunctionalRenderingBase.java
@@ -0,0 +1,75 @@
+package de.tudresden.inf.mci.brailleplot.rendering;
+
+import java.util.HashMap;
+import java.util.Objects;
+
+/**
+ * FunctionalRenderingBase. This class acts as a wrapper for multiple {@link FunctionalRasterizer} instances.
+ * The rasterizer instances can be registered at runtime. The main purpose of the class is to take {@link Renderable}
+ * representations of any type and select the correct concrete rasterizer.
+ * @author Leonard Kupper
+ * @version 2019.07.22
+ */
+public class FunctionalRenderingBase {
+
+    private HashMap<Class<? extends Renderable>, FunctionalRasterizer> mRasterizingAlgorithms;
+    private RasterCanvas mRaster;
+
+    public FunctionalRenderingBase() {
+        mRasterizingAlgorithms = new HashMap<>();
+    }
+
+    /**
+     * Rasterizes any given {@link Renderable} by passing it to the appropriate registered {@link FunctionalRasterizer}.
+     * @param renderData Any instance of a class implementing {@link Renderable}.
+     * @throws InsufficientRenderingAreaException If too few space is available on the currently set {@link RasterCanvas}
+     * to display the amount of data contained in the given renderable representation.
+     * @exception IllegalStateException If no {@link RasterCanvas} is set. Call {@link #setRasterCanvas(RasterCanvas)} beforehand.
+     * @exception IllegalArgumentException If no rasterizer is registered for the given renderable type.
+     */
+    public void rasterize(final Renderable renderData) throws InsufficientRenderingAreaException {
+        // First, check if a raster is set. No rasterizing without raster.
+        if (Objects.isNull(mRaster)) {
+            throw new IllegalStateException("No raster was set. The method 'setRasterCanvas' must be called before invoking the 'rasterize' method.");
+        }
+        // Then, look at the type of the renderData
+        Class<? extends Renderable> diagramClass = renderData.getClass();
+        // Is a rasterizer for the given renderData type available?
+        if (mRasterizingAlgorithms.containsKey(diagramClass)) {
+            // dispatch to concrete rasterizer implementation
+            FunctionalRasterizer selectedRasterizer = mRasterizingAlgorithms.get(diagramClass);
+            selectedRasterizer.rasterize(renderData, mRaster);
+        } else {
+            throw new IllegalArgumentException("No rasterizer registered for renderData class: '"
+                    + diagramClass.getCanonicalName() + "'");
+        }
+    }
+
+    /**
+     * Registers a {@link FunctionalRasterizer} instance to the rendering base. The rendering base can ony hold one rasterizer
+     * per {@link Renderable} type at the same time. This means that any rasterizer that has been registered for the same
+     * type before will be replaced by the new instance.
+     * @param rasterizer The instance of {@link FunctionalRasterizer} to be registered.
+     */
+    public void registerRasterizer(final FunctionalRasterizer<? extends Renderable> rasterizer) {
+        mRasterizingAlgorithms.put(rasterizer.getSupportedRenderableClass(), rasterizer);
+    }
+
+    /**
+     * Sets a new canvas for any rasterizing operations performed by this rendering base. The rasterizing results are
+     * 'drawn' on the currently selected canvas instance. There are no restrictions on the raster canvas. It is also
+     * possible to pass a canvas which already contains data to 'overlay' the new data.
+     * @param raster The {@link AbstractCanvas} instance which will be used for all subsequent rasterizing operations.
+     */
+    public void setRasterCanvas(final RasterCanvas raster) {
+        mRaster = Objects.requireNonNull(raster);
+    }
+
+    /**
+     * Gets the currently set {@link AbstractCanvas} of the rendering base.
+     * @return An instance of {@link AbstractCanvas}.
+     */
+    public RasterCanvas getRaster() {
+        return mRaster;
+    }
+}
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/Image.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/Image.java
new file mode 100644
index 0000000000000000000000000000000000000000..1bbc3ca7a66d4931b8259bba2ee85a2cfb1f2745
--- /dev/null
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/Image.java
@@ -0,0 +1,35 @@
+package de.tudresden.inf.mci.brailleplot.rendering;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.util.Objects;
+
+/**
+ * A representation of an (raster graphic) image. Basically just a wrapper for {@link javax.imageio.ImageIO} and
+ * {@link java.awt.image.BufferedImage}.
+ * @author Leonard Kupper
+ * @version 2019.07.22
+ */
+public class Image implements Renderable {
+
+    private BufferedImage imageData;
+
+    /**
+     * Constructor. Creates a new renderable representation from an image file.
+     * @param imageFile A file containing an raster graphic image. (Different types supported. BMP, PNG, JPEG, ...)
+     * @throws java.io.IOException If an I/O exception of some sort has occurred while reading the image file.
+     */
+    public Image(final File imageFile) throws java.io.IOException {
+        imageData = ImageIO.read(Objects.requireNonNull(imageFile));
+    }
+
+    /**
+     * Get the loaded image as {@link BufferedImage}.
+     * @return An instance of {@link BufferedImage}.
+     */
+    BufferedImage getBufferedImage() {
+        return imageData;
+    }
+
+}
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/ImageRasterizer.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/ImageRasterizer.java
new file mode 100644
index 0000000000000000000000000000000000000000..85031da37879a928d58d8d815ca4328d8f8819b3
--- /dev/null
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/ImageRasterizer.java
@@ -0,0 +1,212 @@
+package de.tudresden.inf.mci.brailleplot.rendering;
+
+import de.tudresden.inf.mci.brailleplot.printabledata.MatrixData;
+
+import java.awt.image.BufferedImage;
+
+import static java.lang.Math.min;
+import static java.lang.Math.round;
+
+/**
+ * A rasterizer that is able to re-raster a raster graphics onto a canvas.
+ * @author Leonard Kupper
+ * @version 2019.07.22
+ */
+public class ImageRasterizer implements Rasterizer<Image> {
+
+    // This Rasterizer is a basic example on how to create a rasterizer.
+    // Each rasterizer is the implementation of the Rasterizer interface. To create a rasterizer, implement the
+    // Rasterizer interface by overriding the rasterize method. Since Rasterizer is a functional interface,
+    // it is technically also possible to implement a rasterizer as a method independent from a class, as long
+    // as it takes a renderable and a canvas as parameters.
+
+    // Switches
+    private boolean mPreventOverStretch;
+    private boolean mPreserveAspectRatio;
+    private boolean mQuantifiedPositions;
+
+    // Output threshold: A dot will be set for gray scale values below (darker than/equal) this threshold.
+    private int mLowThreshold;
+    private final int mDefaultThreshold = 80;
+
+    /**
+     * Constructor. Creates a new {@link Rasterizer} for instances of {@link Image} with default settings.
+     */
+    public ImageRasterizer() {
+        mPreventOverStretch = true;
+        mPreserveAspectRatio = true;
+        mQuantifiedPositions = true;
+        mLowThreshold = mDefaultThreshold;
+    }
+
+    /**
+     * Constructor. Creates a new {@link Rasterizer} for instances of {@link Image}.
+     * @param preventOverStretch In case that the given images resolution is smaller than the grid on at least one
+     *                           dimension this flag prevents it to be 'stretched' on the output, leading to 'cuts' in
+     *                           former solid lines.
+     * @param preserveAspectRatio  This flag will cause the rasterizer to select the smaller of both scaling ratios for
+     *                             both dimensions to keep aspect ratio the same in the output.
+     * @param useQuantifiedPositions Algorithm selection flag:
+     *                               If set to true, output dot positions will be quantified.
+     *                               If set to false, simple linear mapping will be applied instead.
+     * @param threshold Gray scale threshold which determines whether a pixel in the original image sets a dot in the output.
+     */
+    public ImageRasterizer(
+            final boolean preventOverStretch,
+            final boolean preserveAspectRatio,
+            final boolean useQuantifiedPositions,
+            final int threshold) {
+        mPreventOverStretch = preventOverStretch;
+        mPreserveAspectRatio = preserveAspectRatio;
+        mQuantifiedPositions = useQuantifiedPositions;
+        mLowThreshold = threshold;
+    }
+
+    /**
+     * Rasterizes a {@link Image} instance onto a {@link RasterCanvas}.
+     * @param imgData A instance of {@link Image} representing the renderable image.
+     * @param canvas A instance of {@link RasterCanvas} representing the target for the rasterizer output.
+     */
+    @Override
+    public void rasterize(final Image imgData, final RasterCanvas canvas) {
+
+        // Each rasterizer essentially works by taking an instance of a Renderable (in this case Image) and then
+        // creating a graphical representation of the object on the raster canvas.
+
+        // In this example a raster image will be rasterized onto the canvas by two selectable methods:
+        // - linear mapping
+        // - quantified positions
+
+        // Both methods are similar, since the given image is already represented on a raster, which means that the
+        // basic question is just how to map from one raster to the other. This is were the methods differ.
+        // Another question is when to set a dot, which in this example is done by a simple threshold for
+        // the grey scale value of each pixel.
+        // A more sophisticated implementation could utilize an edge finding algorithm.
+
+        // First, a readable representation of the is retrieved.
+        BufferedImage imgBuf = imgData.getBufferedImage();
+
+        // Then the selected method is applied.
+        // Implementing the rasterizer as class comes in handy. Subtasks can be splitted into help
+        // methods and different modular rasterizers can be reused by calling them inside the rasterize method.
+        if (mQuantifiedPositions) {
+            quantifiedPositionMapping(imgBuf, canvas);
+        } else {
+            linearMapping(imgBuf, canvas);
+        }
+    }
+
+    private void linearMapping(final BufferedImage imgBuf, final RasterCanvas canvas) {
+
+        // A canvas is basically a wrapper for multiple representations of printable data, each representing a page.
+        // These representations can be acquired by either requesting the current page or creating a new page.
+        MatrixData<Boolean> data = canvas.getNewPage();
+
+        // The raster canvas delivers meta information about the underlying format and raster geometry.
+        // A vital information are the cell- and dot-rectangle of the canvas. Every rasterizer should take care of at
+        // least these rectangles which represent the portion of the raster that the rasterizer is expected to work on.
+        Rectangle availableArea = canvas.getDotRectangle();
+        // A rasterizer trying to set values outside of the dot rectangle will cause an exception.
+        // Other information about the grid (spacing of cells and dots, ...) is available but must not always be
+        // regarded, depending on the use case.
+
+        // Calculate the ratios between original image and target raster. (resolution 'shrink' factor)
+        double hRatio =  (availableArea.getWidth() - 1) / imgBuf.getWidth();
+        double vRatio = (availableArea.getHeight() - 1) / imgBuf.getHeight();
+
+        if (mPreventOverStretch) {
+            // In case that the given images resolution is smaller than the grid on at least one dimension
+            // this prevents it to be 'stretched' on the output, leading to 'cuts' in former solid lines.
+            // The maximum ratio is 1 for the linear mapping, meaning that the pixel position would be the
+            // exact dot position in the output.
+            // A ratio smaller than 1 would mean that some pixels will be mapped to the same dot to fit the output,
+            // but without any regard to the spacing between the single dots.
+            hRatio = min(hRatio, 1);
+            vRatio = min(hRatio, 1);
+        }
+        if (mPreserveAspectRatio) {
+            // This selects the smaller of both ratios for both dimensions to keep aspect ratio the same in the output.
+            hRatio = min(hRatio, vRatio);
+            vRatio = min(vRatio, vRatio);
+        }
+
+        // Linear Mapping: The pixel position of the original image is linearly mapped to the dot position.
+        // This can lead to distortions because the original pixel raster is equidistant, but the output raster
+        // does not have to be equidistant.
+
+        // Scan through each pixel of the original image
+        for (int x = 0; x < imgBuf.getWidth(); x++) {
+            // Convert from original pixel x-position to braille dot x-position.
+            // Linear mapping: The conversion happens disregarding the grid spacing (dot and cell distances)
+            int column = (int) round(hRatio * (x + 1));
+            for (int y = 0; y < imgBuf.getHeight(); y++) {
+                // Convert from original pixel y-position to braille dot x-position.
+                int row = (int) round(vRatio * (y + 1));
+                // Calculate gray scale value and compare against threshold.
+                int value = toGrayScaleValue(imgBuf.getRGB(x, y));
+                if (value <= mLowThreshold) {
+                    data.setValue(row, column, true);
+                }
+            }
+        }
+    }
+
+    private void quantifiedPositionMapping(final BufferedImage imgBuf, final RasterCanvas canvas) {
+
+        MatrixData<Boolean> data = canvas.getNewPage();
+
+        // Instead of using the dot rectangle a rectangle representing the target printing space in millimeters
+        // is built from the canvas information.
+        Rectangle availableArea = new Rectangle(0, 0, canvas.getPrintableWidth(), canvas.getPrintableHeight());
+
+        // Calculate the ratios between original image and target printable area. (mm / pixel)
+        double hRatio =  (availableArea.getWidth() / imgBuf.getWidth());
+        double vRatio = (availableArea.getHeight() / imgBuf.getHeight());
+
+        if (mPreventOverStretch) {
+            // Here, the maximum ratio is not 1 as in the linear mapping but instead equal to the regarding dot
+            // distances. This is because the ratios are not measured in dots/pixel but mm/pixel.
+            hRatio = min(hRatio, canvas.getHorizontalDotDistance());
+            vRatio = min(vRatio, canvas.getVerticalDotDistance());
+        }
+        if (mPreserveAspectRatio) {
+            hRatio = min(hRatio, vRatio);
+            vRatio = min(hRatio, vRatio);
+        }
+
+        // Quantified Positions: The pixel position of the original image is linearly mapped to the respective
+        // millimeter position on the printed area of the page. This step preserves the original distance ratios.
+        // In a second step, the calculated exact position is quantified to fit a dot position on the raster.
+        // Distortions can still be introduced but are minimized.
+
+        // Scan through all pixels of the original image
+        for (int x = 0; x < imgBuf.getWidth(); x++) {
+            // Convert from original pixel x-position to printed dot x-position in millimeters.
+            // In contrast to the linear mapping, this will try to preserve the original distance ratios.
+            double columnMM = hRatio * (x + 1);
+            for (int y = 0; y < imgBuf.getHeight(); y++) {
+                // Convert from original pixel y-position to printed dot y-position in millimeters.
+                double rowMM = vRatio * (y + 1);
+                // Calculate gray scale value and compare against threshold.
+                int value = toGrayScaleValue(imgBuf.getRGB(x, y));
+                if (value <= mLowThreshold) {
+                    // The target dot position in millimeters has to be quantified regarding the raster.
+                    int row = canvas.quantifyY(rowMM);
+                    int column = canvas.quantifyX(columnMM);
+                    data.setValue(row, column, true);
+                }
+            }
+        }
+    }
+
+
+    private int toGrayScaleValue(final int rgb) {
+        final int colorChannels = 3;
+        final int bitsPerChannel = 8;
+        final int byteMask = 0xff;
+        int r = (rgb >> 2 * bitsPerChannel) & byteMask;
+        int g = (rgb >> bitsPerChannel) & byteMask;
+        int b = (rgb) & byteMask;
+        return ((r + g + b) / colorChannels);
+    }
+}
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/InsufficientRenderingAreaException.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/InsufficientRenderingAreaException.java
new file mode 100644
index 0000000000000000000000000000000000000000..a422a1c69aacc7ecbb2c7123466fbfc37fed55ce
--- /dev/null
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/InsufficientRenderingAreaException.java
@@ -0,0 +1,25 @@
+package de.tudresden.inf.mci.brailleplot.rendering;
+
+/**
+ * Exception that indicates too few space available to display the amount of data contained in the given renderable representation.
+ * Typical circumstances that lead the rasterizer/plotter to throw this exception are that there are simply too much elements to
+ * display them physically in the given raster/area or that the value range cannot be mapped to the given output resolution.
+ * @author Leonard Kupper
+ * @version 2019.07.20
+ */
+public class InsufficientRenderingAreaException extends Exception {
+
+    public InsufficientRenderingAreaException() { }
+
+    public InsufficientRenderingAreaException(final String message) {
+        super(message);
+    }
+
+    public InsufficientRenderingAreaException(final Throwable cause) {
+        super(cause);
+    }
+
+    public InsufficientRenderingAreaException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/LinearMappingAxisRasterizer.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/LinearMappingAxisRasterizer.java
new file mode 100644
index 0000000000000000000000000000000000000000..208fb2b0b0efc28cf9209c5dec05923c57c61e9a
--- /dev/null
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/LinearMappingAxisRasterizer.java
@@ -0,0 +1,122 @@
+package de.tudresden.inf.mci.brailleplot.rendering;
+
+import de.tudresden.inf.mci.brailleplot.printabledata.MatrixData;
+
+import static de.tudresden.inf.mci.brailleplot.rendering.Axis.Type.X_AXIS;
+import static de.tudresden.inf.mci.brailleplot.rendering.Axis.Type.Y_AXIS;
+import static java.lang.Math.abs;
+
+/**
+ * A rasterizer for instances of {@link Axis} which is using a simple approach by linear mapping.
+ * @author Leonard Kupper
+ * @version 2019.07.20
+ */
+
+public class LinearMappingAxisRasterizer implements Rasterizer<Axis> {
+
+    private BrailleTextRasterizer mTextRasterizer = new BrailleTextRasterizer();
+    private RasterCanvas mCanvas;
+
+    /**
+     * Rasterizes a {@link Axis} instance onto a {@link RasterCanvas}.
+     * @param axis A instance of {@link Axis} representing the visual diagram axis.
+     * @param canvas A instance of {@link RasterCanvas} representing the target for the rasterizer output.
+     * @throws InsufficientRenderingAreaException If too few space is available on the {@link RasterCanvas}
+     * to display the given axis.
+     */
+    @Override
+    public void rasterize(final Axis axis, final RasterCanvas canvas) throws InsufficientRenderingAreaException {
+
+        mCanvas = canvas;
+        MatrixData<Boolean> data = mCanvas.getCurrentPage();
+
+        int dotX, startY, endY, dotY, startX, endX;
+        int stepWidth = (int) axis.getStepWidth();
+        int tickSize = (int) axis.getTickSize();
+        boolean setTicks = (abs(tickSize) > 0);
+        boolean hasLabels = axis.hasLabels();
+
+        if (axis.getType() == X_AXIS) {
+            Rectangle bound;
+            dotY = (int) axis.getOriginY();
+            if (axis.hasBoundary()) {
+                bound = axis.getBoundary();
+            } else {
+                bound = mCanvas.getDotRectangle();
+            }
+            startX = bound.intWrapper().getX();
+            endX = bound.intWrapper().getRight();
+            Rasterizer.fill(startX, dotY, endX, dotY, data, true);
+
+            if (setTicks) {
+                int i;
+                startY = dotY;
+                endY = dotY + tickSize;
+                i = 0;
+                for (dotX = (int) axis.getOriginX(); dotX <= endX; dotX += stepWidth) {
+                    Rasterizer.fill(dotX, startY, dotX, endY, data, true);
+                    // TODO: refactor to have labeling functionality in extra method.
+                    if (hasLabels && axis.getLabels().containsKey(i)) {
+                        String label = axis.getLabels().get(i);
+                        Rectangle labelArea = new Rectangle(dotX - 1, endY + 1, stepWidth, mCanvas.getCellHeight());
+                        mTextRasterizer.rasterize(new BrailleText(label, labelArea), mCanvas);
+                    }
+                    i++;
+                }
+                i = -1;
+                for (dotX = (int) axis.getOriginX() - stepWidth; dotX >= startX; dotX -= stepWidth) {
+                    Rasterizer.fill(dotX, startY, dotX, endY, data, true);
+                    if (hasLabels && axis.getLabels().containsKey(i)) {
+                        String label = axis.getLabels().get(i);
+                        Rectangle labelArea = new Rectangle(dotX - 1, endY + 1, stepWidth, mCanvas.getCellHeight());
+                        mTextRasterizer.rasterize(new BrailleText(label, labelArea), mCanvas);
+                    }
+                    i--;
+                }
+            }
+        }
+
+        if (axis.getType() == Y_AXIS) {
+            Rectangle bound;
+            dotX = (int) axis.getOriginX();
+            if (axis.hasBoundary()) {
+                bound = axis.getBoundary();
+            } else {
+                bound = mCanvas.getDotRectangle();
+            }
+            startY = bound.intWrapper().getY();
+            endY = bound.intWrapper().getBottom();
+            Rasterizer.fill(dotX, startY, dotX, endY, data, true);
+
+            if (setTicks) {
+                int i;
+                startX = dotX;
+                endX = dotX + tickSize;
+                i = 0;
+                for (dotY = (int) axis.getOriginY(); dotY <= endY; dotY += stepWidth) {
+                    Rasterizer.fill(startX, dotY, endX, dotY, data, true);
+                    /*
+                    if (hasLabels && axis.getLabels().containsKey(i)) {
+                        String label = axis.getLabels().get(i);
+                        Rectangle labelArea = new Rectangle(endX + Integer.signum(tickSize), dotY, stepWidth, mCanvas.getCellHeight());
+                        mTextRasterizer.rasterize(new BrailleText(label, labelArea), mCanvas);
+                    }
+                    */
+                    i++;
+                }
+                i = -1;
+                for (dotY = (int) axis.getOriginY() - stepWidth; dotY >= startY; dotY -= stepWidth) {
+                    Rasterizer.fill(startX, dotY, endX, dotY, data, true);
+                    /*
+                    if (hasLabels && axis.getLabels().containsKey(i)) {
+                        String label = axis.getLabels().get(i);
+                        Rectangle labelArea = new Rectangle(endX + Integer.signum(tickSize), dotY, stepWidth, mCanvas.getCellHeight());
+                        mTextRasterizer.rasterize(new BrailleText(label, labelArea), mCanvas);
+                    }
+                    */
+                    i--;
+                }
+            }
+        }
+    }
+}
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/MasterRenderer.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/MasterRenderer.java
new file mode 100644
index 0000000000000000000000000000000000000000..241f0a5d64c22244f81986370884a2731001fe13
--- /dev/null
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/MasterRenderer.java
@@ -0,0 +1,92 @@
+package de.tudresden.inf.mci.brailleplot.rendering;
+
+import de.tudresden.inf.mci.brailleplot.configparser.Format;
+import de.tudresden.inf.mci.brailleplot.configparser.Printer;
+
+import java.util.Objects;
+
+/**
+ * MasterRenderer. This class is the main interface for conversion of a diagram representation into a printable format and manages the current rendering context.
+ * The MasterRenderer takes representations of any diagram type, calculates the available raster/area from the given printer and format configuration and dispatches
+ * any calls to the 'rasterize' and 'plot' methods to the given {@link FunctionalRenderingBase}.
+ * @author Leonard Kupper
+ * @version 2019.07.20
+ */
+public final class MasterRenderer {
+
+    Printer mPrinter;
+    Format mFormat;
+    FunctionalRenderingBase mRenderingBase;
+
+    public MasterRenderer(final Printer printer, final Format format) {
+        // if no rendering base is given, create own rendering base with default set of algorithms
+        FunctionalRenderingBase renderingBase = new FunctionalRenderingBase();
+
+        // Default Algorithms:
+
+        // Rasterizer<BarChart> uniformTexture = new UniformTextureBarChartRasterizer();
+        Rasterizer<Image> linearImageMapping = new ImageRasterizer();
+
+        // renderingBase.registerRasterizer(new FunctionalRasterizer<BarChart>(BarChart.class, uniformTexture));
+        renderingBase.registerRasterizer(new FunctionalRasterizer<Image>(Image.class, linearImageMapping));
+        //renderingBase.registerRasterizer(new FunctionalRasterizer<ScatterPlot>(ScatterPlot.class, ScatterPlotRasterizing::fooRasterizing));
+        //...
+
+        setRenderingContext(printer, format, renderingBase);
+    }
+
+    public MasterRenderer(final Printer printer, final Format format, final FunctionalRenderingBase renderingBase) {
+        setRenderingContext(printer, format, renderingBase);
+    }
+
+    public RasterCanvas rasterize(final Renderable data) throws InsufficientRenderingAreaException {
+        RasterCanvas canvas = createCompatibleRasterCanvas();
+        mRenderingBase.setRasterCanvas(canvas);
+        mRenderingBase.rasterize(data);
+        return canvas;
+    }
+
+    private RasterCanvas createCompatibleRasterCanvas() throws InsufficientRenderingAreaException {
+
+        return new SixDotBrailleRasterCanvas(mPrinter, mFormat);
+
+        /*
+        TODO: support 6 and 8 dot layout#
+        String rasterType = mPrinter.getProperty("raster.type").toString();
+        if (rasterType == "6-dot") {
+            return new SixDotBrailleRasterCanvas(mPrinter, mFormat);
+        } else {
+            ...
+        }
+         */
+    }
+
+    // Getter & Setter
+
+    public void setRenderingContext(final Printer printer, final Format format, final FunctionalRenderingBase renderingBase) {
+        setPrinter(printer);
+        setFormat(format);
+        setRenderingBase(renderingBase);
+    }
+
+    public void setPrinter(final Printer printer) {
+        mPrinter = Objects.requireNonNull(printer);
+    }
+    public Printer getPrinter() {
+        return mPrinter;
+    }
+
+    public void setFormat(final Format format) {
+        mFormat = Objects.requireNonNull(format);
+    }
+    public Format getFormat() {
+        return mFormat;
+    }
+
+    public void setRenderingBase(final FunctionalRenderingBase renderingBase) {
+        mRenderingBase = Objects.requireNonNull(renderingBase);
+    }
+    public FunctionalRenderingBase getRenderingBase() {
+        return mRenderingBase;
+    }
+}
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/RasterCanvas.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/RasterCanvas.java
new file mode 100644
index 0000000000000000000000000000000000000000..541345770c4c402273af81ba0f60f406d1fdb6d5
--- /dev/null
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/RasterCanvas.java
@@ -0,0 +1,269 @@
+package de.tudresden.inf.mci.brailleplot.rendering;
+
+import de.tudresden.inf.mci.brailleplot.configparser.Format;
+import de.tudresden.inf.mci.brailleplot.configparser.Printer;
+import de.tudresden.inf.mci.brailleplot.printabledata.MatrixData;
+import de.tudresden.inf.mci.brailleplot.printabledata.SimpleMatrixDataImpl;
+
+import java.util.ArrayList;
+
+import static java.lang.Math.abs;
+import static java.lang.Math.ceil;
+import static java.lang.Math.floor;
+
+/**
+ * Representation of a target onto which an image can be rasterized.
+ * It wraps a {@link de.tudresden.inf.mci.brailleplot.printabledata.MatrixData} instance and describes the raster size and its (not necessarily equidistant) layout.
+ * @author Leonard Kupper
+ * @version 2019.07.22
+ */
+public class RasterCanvas extends AbstractCanvas {
+
+    private ArrayList<Double> mXPositions;
+    private ArrayList<Double> mYPositions;
+
+    // Raster size
+    private int mHorizontalCellCount;
+    private int mVerticalCellCount;
+    private int mColumnCount;
+    private int mRowCount;
+
+    // Printing area rectangle
+    private Rectangle mPrintingAreaCells;
+    private Rectangle mPrintingAreaDots;
+
+    // Cell size
+    private int mCellWidth; // dots
+    private int mCellHeight; // dots
+    private double mCellHorizontalMM; // millimeters
+    private double mCellVerticalMM; // millimeters
+
+    // Spacing
+    private double mHorizontalDotDistance;
+    private double mVerticalDotDistance;
+    private double mHorizontalCellDistance;
+    private double mVerticalCellDistance;
+    private double mDotDiameter;
+
+    /**
+     * Constructor. Creates a new RasterCanvas, which is a canvas that represents it pages as instances of
+     * {@link MatrixData} and holds information about the layout and spacing of the underlying raster grid.
+     * The described grid is build from uniform 'cells' consisting of a variable amount of dots.
+     * It is used as a target on which can be drawn by a {@link Rasterizer}.
+     * @param printer The {@link Printer} configuration to be used.
+     * @param format The {@link Format} configuration to be used.
+     * @param cellWidth The horizontal count of dots in a cell.
+     * @param cellHeight The vertical count of dots in a cell.
+     * @throws InsufficientRenderingAreaException If the given configuration leads to an printable area of negative
+     * size or zero size, e.g. if the sum of defined margins and constraints adds up to be greater than the original page size.
+     */
+    RasterCanvas(final Printer printer, final Format format, final int cellWidth, final int cellHeight)
+            throws InsufficientRenderingAreaException {
+
+        super(printer, format);
+
+        // Cell size in dots
+        mCellWidth = cellWidth;
+        mCellHeight = cellHeight;
+
+        readConfig();
+        calculateRasterSize();
+        calculateSpacing();
+
+    }
+
+    public final MatrixData<Boolean> getNewPage() {
+        mPageContainer.add(new SimpleMatrixDataImpl<Boolean>(mPrinter, mFormat, mRowCount, mColumnCount, false));
+        return getCurrentPage();
+    }
+
+    @SuppressWarnings("unchecked")
+    // This is allowed because the mPageContainer fields are always initialized with the correct type by the page getters,
+    // cannot be accessed from the outside and are never changed anywhere else.
+    public final MatrixData<Boolean> getCurrentPage() {
+        if (mPageContainer.size() < 1) {
+            return getNewPage();
+        }
+        return (MatrixData<Boolean>) mPageContainer.get(mPageContainer.size() - 1);
+    }
+
+    private void readConfig() {
+
+        // What are the dot and cell distances in mm?
+        mHorizontalDotDistance = mPrinter.getProperty("raster.dotDistance.horizontal").toDouble();
+        mVerticalDotDistance = mPrinter.getProperty("raster.dotDistance.vertical").toDouble();
+        mHorizontalCellDistance = mPrinter.getProperty("raster.cellDistance.horizontal").toDouble();
+        mVerticalCellDistance = mPrinter.getProperty("raster.cellDistance.vertical").toDouble();
+        mDotDiameter = mPrinter.getProperty("raster.dotDiameter").toDouble();
+
+        // Calculate cell size in mm
+        mCellHorizontalMM = mHorizontalDotDistance * (mCellWidth - 1) + mHorizontalCellDistance; // Full width of one cell + padding in mm
+        mCellVerticalMM = mVerticalDotDistance * (mCellHeight - 1) + mVerticalCellDistance; // Full height of one cell + padding in mm
+
+    }
+
+    private void calculateRasterSize() throws InsufficientRenderingAreaException {
+
+        // New approach using a box model:
+
+        // Dividing the printable area into cells to create a cell raster box.
+        int cellRasterX = (int) ceil(mPrintableArea.getX() / mCellHorizontalMM);
+        int cellRasterY = (int) ceil(mPrintableArea.getY() / mCellVerticalMM);
+        int cellRasterR = (int) floor((mPrintableArea.getRight() + mHorizontalCellDistance) / mCellHorizontalMM);
+        int cellRasterB = (int) floor((mPrintableArea.getBottom() + mVerticalCellDistance) / mCellVerticalMM);
+        Rectangle cellRasterBox = new Rectangle(
+                cellRasterX, cellRasterY,
+                cellRasterR - cellRasterX,
+                cellRasterB - cellRasterY
+        );
+
+        // The following properties impact the printing area, but are specific to rasterizing. (That's why they weren't read before in the AbstractCanvas)
+        // The abstract parent class (AbstractCanvas) already calculated indentations based on millimeters, but it is
+        // also possible to set a raster.indentation counted in amount of cells and lines. Those must be removed additionally.
+
+        // Create a raster constraint box
+        int rasterConstraintTop = mPrinter.getProperty("raster.constraint.top").toInt();
+        int rasterConstraintLeft = mPrinter.getProperty("raster.constraint.left").toInt();
+        int rasterConstraintHeight, rasterConstraintWidth;
+        if (mPrinter.getPropertyNames().contains("raster.constraint.height")) {
+            rasterConstraintHeight = mPrinter.getProperty("raster.constraint.height").toInt();
+        } else {
+            rasterConstraintHeight = Integer.MAX_VALUE;
+        }
+        if (mPrinter.getPropertyNames().contains("raster.constraint.width")) {
+            rasterConstraintWidth = mPrinter.getProperty("raster.constraint.width").toInt();
+        } else {
+            rasterConstraintWidth = Integer.MAX_VALUE;
+        }
+        Rectangle rasterConstraintBox = new Rectangle(rasterConstraintLeft, rasterConstraintTop,
+                rasterConstraintWidth, rasterConstraintHeight);
+
+        mPrintingAreaCells = calculatePrintingArea(cellRasterBox, rasterConstraintBox);
+
+        // The following values are set to keep track of the 'real' size of the internal data representation, because
+        // the margins are created virtually by printing some empty cells at the pages top / left edge.
+        // Rasterizers are only presented with a sub-area rectangle, representing the valid printing area.
+
+        // How many rows and columns of full cells fit inside the given page area (ignoring margins and raster constraints)
+        mHorizontalCellCount = mPrintingAreaCells.intWrapper().getRight() + 1; // How many full cells fit horizontally?
+        mVerticalCellCount = mPrintingAreaCells.intWrapper().getBottom() + 1; // How many full cells fit vertically?
+
+        // To how many dots does this raster size correspond?
+        mPrintingAreaDots = toDotRectangle(mPrintingAreaCells);
+        mColumnCount = mPrintingAreaDots.intWrapper().getWidth();
+        mRowCount = mPrintingAreaDots.intWrapper().getHeight();
+
+
+    }
+
+    private void calculateSpacing() {
+
+        mXPositions = calculateQuantizedPositions(mHorizontalDotDistance, mHorizontalCellDistance, mCellWidth, mHorizontalCellCount);
+        mYPositions = calculateQuantizedPositions(mVerticalDotDistance, mVerticalCellDistance, mCellHeight, mVerticalCellCount);
+
+    }
+
+    private ArrayList<Double> calculateQuantizedPositions(
+            final double dotSpacing,
+            final double cellSpacing,
+            final int cellSize,
+            final int cellCount
+    ) {
+        ArrayList<Double> positions = new ArrayList<>();
+        double position = 0;
+        for (int i = 0; i < cellCount; i++) {
+            for (int j = 0; j < cellSize; j++) {
+                positions.add(position);
+                if (j < (cellSize - 1)) {
+                    position += dotSpacing;
+                }
+            }
+            position += cellSpacing;
+        }
+        return positions;
+    }
+
+    public final int getCellWidth() {
+        return mCellWidth;
+    }
+    public final int getCellHeight() {
+        return mCellHeight;
+    }
+
+    public final double getHorizontalDotDistance() {
+        return mHorizontalDotDistance;
+    }
+    public final double getVerticalDotDistance() {
+        return mVerticalDotDistance;
+    }
+    public final double getHorizontalCellDistance() {
+        return mHorizontalCellDistance;
+    }
+    public final double getVerticalCellDistance() {
+        return mVerticalCellDistance;
+    }
+    public final double getDotDiameter() {
+        return mDotDiameter;
+    }
+    public final Rectangle getCellRectangle() {
+        return new Rectangle(mPrintingAreaCells);
+    }
+    public final Rectangle getDotRectangle() {
+        return mPrintingAreaDots;
+    }
+    public final Rectangle toDotRectangle(final Rectangle cellRectangle) {
+        return cellRectangle.scaledBy(mCellWidth, mCellHeight);
+    }
+
+    @Override
+    public double getPrintableWidth() {
+        return mXPositions.get(getDotRectangle().intWrapper().getRight()) - mXPositions.get(getDotRectangle().intWrapper().getX());
+    }
+
+    @Override
+    public double getPrintableHeight() {
+        return mYPositions.get(getDotRectangle().intWrapper().getBottom()) - mYPositions.get(getDotRectangle().intWrapper().getY());
+    }
+
+    /**
+     * Returns the x coordinate (counted in cells) of the cell containing the dot with given x coordinate (counted in dots).
+     * @param dotX The dot x coordinate. In other words its columns number.
+     * @return The cell x coordinate. In other words the cells columns number.
+     */
+    public int getCellXFromDotX(final int dotX) {
+        return dotX / mCellWidth;
+    }
+    /**
+     * Returns the y coordinate (counted in cells) of the cell containing the dot with given y coordinate (counted in dots).
+     * @param dotY The dot y coordinate. In other words its rows number.
+     * @return The cell y coordinate. In other words the cells columns number.
+     */
+    public int getCellYFromDotY(final int dotY) {
+        return dotY / mCellHeight;
+    }
+
+
+    public final int quantifyX(final double unquantifiedMillimeterX) {
+        return findClosestValueIndex(unquantifiedMillimeterX, mXPositions);
+    }
+
+    public final int quantifyY(final double unquantifiedMillimeterY) {
+        return findClosestValueIndex(unquantifiedMillimeterY, mYPositions);
+    }
+
+    private int findClosestValueIndex(final Double value, final ArrayList<Double> list) {
+        double minDistance = Double.POSITIVE_INFINITY;
+        for (int index = 0; index < list.size(); index++) {
+            double distance = abs(list.get(index) - value);
+            if (distance < minDistance) {
+                minDistance = distance;
+            } else {
+                // possible, because we know that the positions are sorted.
+                return index - 1;
+            }
+
+        }
+        // last value is the closest.
+        return (list.size() - 1);
+    }
+}
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/Rasterizer.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/Rasterizer.java
new file mode 100644
index 0000000000000000000000000000000000000000..094f03a649868d9cc06171f2fd590351ff0a6909
--- /dev/null
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/Rasterizer.java
@@ -0,0 +1,83 @@
+package de.tudresden.inf.mci.brailleplot.rendering;
+
+
+import de.tudresden.inf.mci.brailleplot.printabledata.MatrixData;
+
+import static java.lang.Math.max;
+import static java.lang.Math.min;
+
+/**
+ * Rasterizer. A functional interface for anything that is able to rasterize renderable data onto a raster.
+ * This interface also defines a static set of tool methods for basic operations on a raster's data container ({@link MatrixData}).
+ * @param <T> The concrete class implementing {@link Renderable} which can be rasterized with the rasterizer.
+ * @author Leonard Kupper
+ * @version 2019.07.22
+ */
+@FunctionalInterface
+public interface Rasterizer<T extends Renderable> {
+
+    /**
+     * Rasterizes a {@link Renderable} instance onto a {@link RasterCanvas}.
+     * @param data The renderable representation.
+     * @param canvas An instance of {@link RasterCanvas} representing the target for the rasterizer output.
+     * @throws InsufficientRenderingAreaException If too few space is available on the {@link RasterCanvas}
+     * to display the given data.
+     */
+    void rasterize(T data, RasterCanvas canvas) throws InsufficientRenderingAreaException;
+
+    // Basic geometric rasterizing toolset:
+
+    /**
+     * Fills the space on the raster between two arbitrary opposite points with a given value.
+     * @param x1 X coordinate of first point.
+     * @param y1 Y coordinate of first point.
+     * @param x2 X coordinate of second point.
+     * @param y2 Y coordinate of second point.
+     * @param data The target raster data container.
+     * @param value The value to fill the area with.
+     */
+    static void fill(int x1, int y1, int x2, int y2, MatrixData<Boolean> data, boolean value) {
+        int xMin = min(x1, x2);
+        int xMax = max(x1, x2);
+        int yMin = min(y1, y2);
+        int yMax = max(y1, y2);
+        for (int y = yMin; y <= yMax; y++) {
+            for (int x = xMin; x <= xMax; x++) {
+                data.setValue(y, x, value);
+            }
+        }
+    }
+
+    /**
+     * Draws a rectangle border with a given value onto the raster. The rectangle is defined by two arbitrary opposite points.
+     * @param x1 X coordinate of first point.
+     * @param y1 Y coordinate of first point.
+     * @param x2 X coordinate of second point.
+     * @param y2 Y coordinate of second point.
+     * @param data The target raster data container.
+     * @param value The value to fill the area with.
+     */
+    static void rectangle(int x1, int y1, int x2, int y2, MatrixData<Boolean> data, boolean value) {
+        int xMin = min(x1, x2);
+        int xMax = max(x1, x2);
+        int yMin = min(y1, y2);
+        int yMax = max(y1, y2);
+        rectangle(new Rectangle(xMin, yMin, xMax - xMin + 1, yMax - yMin + 1), data, value);
+    }
+
+    /**
+     * Draws a rectangle border with a given value onto the raster.
+     * @param rect The {@link Rectangle} instance to draw.
+     * @param data The target raster data container.
+     * @param value The value to fill the area with.
+     */
+    static void rectangle(Rectangle rect, MatrixData<Boolean> data, boolean value) {
+        Rectangle.IntWrapper intRect = rect.intWrapper();
+        int x2 = max(intRect.getX() + intRect.getWidth() - 1, 0);
+        int y2 = max(intRect.getY() + intRect.getHeight() - 1, 0);
+        fill(intRect.getX(), intRect.getY(), intRect.getX(), y2, data, value);
+        fill(intRect.getX(), y2, x2, y2, data, value);
+        fill(x2, intRect.getY(), x2, y2, data, value);
+        fill(intRect.getX(), intRect.getY(), x2, intRect.getY(), data, value);
+    }
+}
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/Rectangle.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/Rectangle.java
new file mode 100644
index 0000000000000000000000000000000000000000..b0289a10b097dfc84f6f5f1038f612d697445c36
--- /dev/null
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/Rectangle.java
@@ -0,0 +1,411 @@
+package de.tudresden.inf.mci.brailleplot.rendering;
+
+import java.util.Objects;
+
+import static java.lang.Math.round;
+import static java.lang.Math.min;
+import static java.lang.Math.max;
+
+/**
+ * Represents a rectangle that can be continuously divided into partitions. Can be used for doing chart layout and as
+ * boundary of renderable objects.
+ * @author Leonard Kupper
+ * @version 2019.07.12
+ */
+public class Rectangle {
+
+    private double mX, mY, mW, mH;
+
+    /**
+     * Constructor. Creates a rectangle with given position and size.
+     * @param x The x coordinate of the upper left corner.
+     * @param y The y coordinate of the upper left corner.
+     * @param w The width of the rectangle, meaning its rightward expanse.
+     * @param h The height of the rectangle, meaning its downward expanse.
+     */
+    public Rectangle(final double x, final double y, final double w, final double h) {
+        setX(x);
+        setY(y);
+        setWidth(w);
+        setHeight(h);
+    }
+
+    /**
+     * Copy constructor. Creates a copy of a rectangle.
+     * @param rect The rectangle to be copied.
+     */
+    public Rectangle(final Rectangle rect) {
+        setX(rect.getX());
+        setY(rect.getY());
+        setWidth(rect.getWidth());
+        setHeight(rect.getHeight());
+    }
+
+    /**
+     * Removes a partition from the rectangles top and returns it.
+     * @param height The height of the partition that will be removed.
+     * @return A rectangle representing the cropped partition.
+     * @throws OutOfSpaceException If the requested partition is greater than the underlying rectangle itself.
+     */
+    public Rectangle removeFromTop(final double height) throws OutOfSpaceException {
+        Rectangle removedPartition = fromTop(height);
+        double newY = (getY() + height);
+        double newHeight = (getHeight() - height);
+        setY(newY);
+        setHeight(newHeight);
+        return removedPartition;
+    }
+
+    /**
+     * Removes a partition from the rectangles bottom and returns it.
+     * @param height The height of the partition that will be removed.
+     * @return A rectangle representing the cropped partition.
+     * @throws OutOfSpaceException If the requested partition is greater than the underlying rectangle itself.
+     */
+    public Rectangle removeFromBottom(final double height) throws OutOfSpaceException {
+        Rectangle removedPartition = fromBottom(height);
+        double newHeight = (getHeight() - height);
+        setHeight(newHeight);
+        return removedPartition;
+    }
+
+    /**
+     * Removes a partition from the rectangles left side and returns it.
+     * @param width The width of the partition that will be removed.
+     * @return A rectangle representing the cropped partition.
+     * @throws OutOfSpaceException If the requested partition is greater than the underlying rectangle itself.
+     */
+    public Rectangle removeFromLeft(final double width) throws OutOfSpaceException {
+        Rectangle removedPartition = fromLeft(width);
+        double newX = (getX() + width);
+        double newWidth = (getWidth() - width);
+        setX(newX);
+        setWidth(newWidth);
+        return removedPartition;
+    }
+
+    /**
+     * Removes a partition from the rectangles right side and returns it.
+     * @param width The width of the partition that will be removed.
+     * @return A rectangle representing the cropped partition.
+     * @throws OutOfSpaceException If the requested partition is greater than the underlying rectangle itself.
+     */
+    public Rectangle removeFromRight(final double width) throws OutOfSpaceException {
+        Rectangle removedPartition = fromRight(width);
+        double newWidth = (getWidth() - width);
+        setWidth(newWidth);
+        return removedPartition;
+    }
+
+    // Methods to get a rectangle partition
+
+    /**
+     * Gets a partition from the rectangles top without removing it from the original instance.
+     * @param height The height of the selected partition.
+     * @return A rectangle representing the selected partition.
+     * @throws OutOfSpaceException If the requested partition is greater than the underlying rectangle itself.
+     */
+    public Rectangle fromTop(final double height) throws OutOfSpaceException {
+        checkHeight(height);
+        return new Rectangle(getX(), getY(), getWidth(), height);
+    }
+
+    /**
+     * Gets a partition from the rectangles left side without removing it from the original instance.
+     * @param width The width of the selected partition.
+     * @return A rectangle representing the selected partition.
+     * @throws OutOfSpaceException If the requested partition is greater than the underlying rectangle itself.
+     */
+    public Rectangle fromLeft(final double width) throws OutOfSpaceException {
+        checkWidth(width);
+        return new Rectangle(getX(), getY(), width, getHeight());
+    }
+
+    /**
+     * Gets a partition from the rectangles bottom without removing it from the original instance.
+     * @param height The height of the selected partition.
+     * @return A rectangle representing the selected partition.
+     * @throws OutOfSpaceException If the requested partition is greater than the underlying rectangle itself.
+     */
+    public Rectangle fromBottom(final double height) throws OutOfSpaceException {
+        checkHeight(height);
+        double newY = (getY() + (getHeight() - height));
+        return new Rectangle(getX(), newY, getWidth(), height);
+    }
+
+    /**
+     * Gets a partition from the rectangles right side without removing it from the original instance.
+     * @param width The width of the selected partition.
+     * @return A rectangle representing the selected partition.
+     * @throws OutOfSpaceException If the requested partition is greater than the underlying rectangle itself.
+     */
+    public Rectangle fromRight(final double width) throws OutOfSpaceException {
+        checkWidth(width);
+        double newX = (getX() + (getWidth() - width));
+        return new Rectangle(newX, getY(), width, getHeight());
+    }
+
+    // Help methods for validity check of requested partition
+
+    private void checkHeight(final double h) throws OutOfSpaceException {
+        if (h > getHeight()) {
+            throw new OutOfSpaceException("The rectangle partition height cannot be greater than its parent rectangle height."
+                    + "(" + h + ">" + getHeight() + ")");
+        }
+    }
+    private void checkWidth(final double w) throws OutOfSpaceException {
+        if (w > getWidth()) {
+            throw new OutOfSpaceException("The rectangle partition width cannot be greater than its parent rectangle width."
+                    + "(" + w + ">" + getWidth() + ")");
+        }
+    }
+
+    // Getters for edge positions and size
+
+    /**
+     * Gets the rectangles x position.
+     * @return The x coordinate of the upper left corner.
+     */
+    public double getX() {
+        return mX;
+    }
+
+    /**
+     * Gets the rectangles y position.
+     * @return The y coordinate of the upper left corner.
+     */
+    public double getY() {
+        return mY;
+    }
+
+    /**
+     * Gets the rectangles width.
+     * @return The distance between the rectangles left and right edge.
+     */
+    public double getWidth() {
+        return mW;
+    }
+
+    /**
+     * Gets the rectangles height.
+     * @return The distance between the rectangles top and bottom edge.
+     */
+    public double getHeight() {
+        return mH;
+    }
+
+    /**
+     * Gets the rectangles right edges position.
+     * @return The x coordinate of the lower right corner.
+     */
+    public double getRight() {
+        return mX + mW;
+    }
+
+    /**
+     * Gets the rectangles bottom edges position.
+     * @return The y coordinate of the lower right corner.
+     */
+    public double getBottom() {
+        return mY + mH;
+    }
+
+    /**
+     * Sets a new x position for the rectangle.
+     * @param x The new x coordinate of the upper left corner.
+     */
+    public void setX(final double x) {
+        mX = x;
+    }
+
+    /**
+     * Sets a new y position for the rectangle.
+     * @param y The new y coordinate of the upper left corner.
+     */
+    public void setY(final double y) {
+        mY = y;
+    }
+
+    /**
+     * Sets a new width for the rectangle.
+     * @param width The new width value.
+     */
+    public void setWidth(final double width) {
+        if (width < 0) {
+            throw new IllegalArgumentException("The width can't be negative.");
+        }
+        mW = width;
+    }
+
+    /**
+     * Sets a new height for the rectangle.
+     * @param height The new height value.
+     */
+    public void setHeight(final double height) {
+        if (height < 0) {
+            throw new IllegalArgumentException("The height can't be negative.");
+        }
+        mH = height;
+    }
+
+    /**
+     * Returns a scaled version of the original rectangle.
+     * @param xScale The x-axis scale factor
+     * @param yScale The y-axis scale factor
+     * @return New rectangle with scaled position and size.
+     */
+    public Rectangle scaledBy(final double xScale, final double yScale) {
+        return new Rectangle(mX * xScale, mY * yScale, mW * xScale, mH * yScale);
+    }
+
+    /**
+     * Returns a new rectangle representing the intersection of this rectangle with another rectangle.
+     * @param otherRectangle The other rectangle to intersect with this.
+     * @return New rectangle representing the intersection.
+     */
+    public Rectangle intersectedWith(final Rectangle otherRectangle) {
+        double itsctX = max(getX(), otherRectangle.getX());
+        double itsctY = max(getY(), otherRectangle.getY());
+        double itsctB = min(getBottom(), otherRectangle.getBottom());
+        double itsctR = min(getRight(), otherRectangle.getRight());
+        return new Rectangle(itsctX, itsctY, max(0, itsctR - itsctX), max(0, itsctB - itsctY));
+    }
+
+    /**
+     * Returns a translated copy of this rectangle.
+     * @param alongX The distance to move the copy along x axis.
+     * @param alongY The distance to move the copy along y axis.
+     * @return A new rectangle representing a translated copy of this rectangle.
+     */
+    public Rectangle translatedBy(final double alongX, final double alongY) {
+        return new Rectangle(getX() + alongX, getY() + alongY, getWidth(), getHeight());
+    }
+
+    @Override
+    public String toString() {
+        return "x:" + getX() + ", y:" + getY() + ", w:" + getWidth() + ", h:" + getHeight();
+    }
+
+    // Wrapper to make it easy to read integer values from rectangle.
+
+    /**
+     * Retrieves a proxy object as integer coordinate representation of the rectangle. This is meant as a shortcut
+     * for otherwise frequent int-casting when using rectangles on a integer based coordinate system.
+     * @return An instance of {@link IntWrapper} proxying this rectangle.
+     */
+    public IntWrapper intWrapper() {
+        return new IntWrapper(this);
+    }
+
+    /**
+     * Wrapper of rectangle for integer coordinates.
+     * @author Leonard Kupper
+     * @version 2019.07.22
+     */
+    public final class IntWrapper {
+
+        private Rectangle mRectangle;
+
+        /**
+         * Constructor. Creates a wrapper as proxy object for an integer coordinate representation of the given rectangle.
+         * @param rectangle The rectangle to be wrapped.
+         */
+        public IntWrapper(final Rectangle rectangle) {
+            this.mRectangle = Objects.requireNonNull(rectangle);
+        }
+
+
+        private int wrapInt(final double value) {
+            return Math.toIntExact(round(value));
+        }
+
+        /**
+         * Gets the rectangles x position.
+         * @return The x coordinate of the upper left corner.
+         */
+        public int getX() {
+            return wrapInt(mRectangle.getX());
+        }
+
+        /**
+         * Gets the rectangles y position.
+         * @return The y coordinate of the upper left corner.
+         */
+        public int getY() {
+            return wrapInt(mRectangle.getY());
+        }
+
+        /**
+         * Gets the rectangles width.
+         * @return The distance between the rectangles left and right edge.
+         */
+        public int getWidth() {
+            return wrapInt(mRectangle.getWidth());
+        }
+
+        /**
+         * Gets the rectangles height.
+         * @return The distance between the rectangles top and bottom edge.
+         */
+        public int getHeight() {
+            return wrapInt(mRectangle.getHeight());
+        }
+
+        /**
+         * Gets the rectangles right edges position (<b>Important:</b> The IntWrapper treats the rectangle as
+         * representation of a 'whole' area composed of single countable units (e.g. dots or cells) so this method will
+         * return the position of the rightmost contained coordinate, which is x+width-1)
+         * @return The x coordinate of the inner contained right edge.
+         */
+        public int getRight() {
+            return wrapInt(mRectangle.getRight()) - 1;
+        }
+
+        /**
+         * Gets the rectangles bottom edges position (<b>Important:</b> The IntWrapper treats the rectangle as
+         * representation of a 'whole' area composed of single countable units (e.g. dots or cells) so this method will
+         * return the position of the bottommost contained coordinate, which is y+height-1)
+         * @return The y coordinate of the inner contained bottom edge.
+         */
+        public int getBottom() {
+            return wrapInt(mRectangle.getBottom()) - 1;
+        }
+
+        /**
+         * Get the original rectangle, wrapped by this IntWrapper.
+         * @return The wrapped instance of {@link Rectangle}.
+         */
+        public Rectangle getRectangle() {
+            return mRectangle;
+        }
+
+        @Override
+        public String toString() {
+            return "x:" + getX() + ", y:" + getY() + ", w:" + getWidth() + ", h:" + getHeight();
+        }
+    }
+
+    /**
+     * Exception that indicates that an operation on the rectangle has failed because there was too few space available.
+     * Typically this is caused by trying to get a partition of the rectangle which is bigger than its parent rectangle itself.
+     * @author Leonard Kupper
+     * @version 2019.07.12
+     */
+    public class OutOfSpaceException extends Exception {
+
+        public OutOfSpaceException() { }
+
+        public OutOfSpaceException(final String message) {
+            super(message);
+        }
+
+        public OutOfSpaceException(final Throwable cause) {
+            super(cause);
+        }
+
+        public OutOfSpaceException(final String message, final Throwable cause) {
+            super(message, cause);
+        }
+    }
+
+}
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/Renderable.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/Renderable.java
new file mode 100644
index 0000000000000000000000000000000000000000..992d9826d40a917af66322710e406681f28d245d
--- /dev/null
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/Renderable.java
@@ -0,0 +1,10 @@
+package de.tudresden.inf.mci.brailleplot.rendering;
+
+/**
+ * An interface for everything that can be rasterized and plotted.
+ * @author Leonard Kupper
+ * @version 2019.07.04
+ */
+public interface Renderable {
+    // Not much going on here...
+}
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/SixDotBrailleRasterCanvas.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/SixDotBrailleRasterCanvas.java
new file mode 100644
index 0000000000000000000000000000000000000000..62b57a9468f1c18c4b966d1ac6d35f518935eb4b
--- /dev/null
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/SixDotBrailleRasterCanvas.java
@@ -0,0 +1,19 @@
+package de.tudresden.inf.mci.brailleplot.rendering;
+
+import de.tudresden.inf.mci.brailleplot.configparser.Format;
+import de.tudresden.inf.mci.brailleplot.configparser.Printer;
+
+/**
+ * Represents a raster consisting of 6-dot braille cells. (May be removed completely in favor of dynamic {@link RasterCanvas})
+ * @author Leonard Kupper
+ * @version 2019.07.20
+ */
+class SixDotBrailleRasterCanvas extends RasterCanvas {
+
+    private static final int CELL_WIDTH = 2;
+    private static final int CELL_HEIGHT = 3;
+
+    SixDotBrailleRasterCanvas(final Printer printer, final Format format) throws InsufficientRenderingAreaException {
+        super(printer, format, CELL_WIDTH, CELL_HEIGHT);
+    }
+}
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/ThrowingBiConsumer.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/ThrowingBiConsumer.java
new file mode 100644
index 0000000000000000000000000000000000000000..8118436deeee1ab8fc52fc5d22d5af98a459e82a
--- /dev/null
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/ThrowingBiConsumer.java
@@ -0,0 +1,15 @@
+package de.tudresden.inf.mci.brailleplot.rendering;
+
+/**
+ * A functional interface representing a void function taking two parameters of types  T and U which can throw an
+ * exception E on failure.
+ * @param <T> First parameter type.
+ * @param <U> Second parameter type.
+ * @param <E> Exception type.
+ * @author Leonard Kupper
+ * @version 2019.07.09
+ */
+@FunctionalInterface
+interface ThrowingBiConsumer<T, U, E extends Exception> {
+    void accept(T t, U u) throws E;
+}
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/UniformTextureBarChartRasterizer.java.txt b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/UniformTextureBarChartRasterizer.java.txt
new file mode 100644
index 0000000000000000000000000000000000000000..06f33fa6b4bd09044cd751b4d7d96901c94668db
--- /dev/null
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/UniformTextureBarChartRasterizer.java.txt
@@ -0,0 +1,312 @@
+package de.tudresden.inf.mci.brailleplot.rendering;
+
+import de.tudresden.inf.mci.brailleplot.printabledata.MatrixData;
+import de.tudresden.inf.mci.brailleplot.diagrams.BarChart;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+import static java.lang.Math.*;
+
+/**
+ * A rasterizer for instances of {@link BarChart} which is based on an algorithm that constructs horizontal category
+ * bars that are filled with a uniform texture. The rasterizer is 'cell' based, working on 6-dot or 8-dot layouts.
+ * @author Leonard Kupper
+ * @version 2019.07.20
+ */
+final class UniformTextureBarChartRasterizer implements Rasterizer<BarChart> {
+
+    BarChart mDiagram;
+    RasterCanvas mCanvas;
+    MatrixData<Boolean> mData;
+
+    // TODO: move some of these into the format config.
+    // algorithm specific constants
+    private final double[] mUnitScalings = {0.1, 0.125, 0.25, 0.5, 1.0};
+    private final int mTextureUnitSize = 2; // dots
+    private final int mBarMinThickness = 5; // dots
+    private final int mBarMaxThickness = 9; // dots
+    private final int mBarDotPadding = 1;
+    private final int mExtraBarCellPadding = 0; // cells
+    private final int mBarInCellPadding = 2; // dots
+    private final boolean mLeftCaption = true;
+    private final int mCaptionLength = 6; // cells
+
+    // associated rasterizers
+    private BrailleTextRasterizer mTextRasterizer;
+    private LinearMappingAxisRasterizer mAxisRasterizer;
+    //private Rasterizer<Legend> mLegendRasterizer;
+
+    /**
+     * Constructor. Create a new rasterizer for instances of {@link BarChart}.
+     */
+    UniformTextureBarChartRasterizer() {
+        mTextRasterizer = new BrailleTextRasterizer();
+        mAxisRasterizer = new LinearMappingAxisRasterizer();
+        //mLegendRasterizer = new LegendRasterizer();
+    }
+
+    /**
+     * Rasterizes a {@link BarChart} instance onto a {@link RasterCanvas}.
+     * @param diagram A instance of {@link BarChart} representing the bar chart diagram.
+     * @param canvas A instance of {@link RasterCanvas} representing the target for the rasterizer output.
+     * @throws InsufficientRenderingAreaException If too few space is available on the {@link RasterCanvas}
+     * to display the given diagram.
+     */
+    @Override
+    public void rasterize(final BarChart diagram, final RasterCanvas canvas)
+            throws InsufficientRenderingAreaException {
+
+        // The comments here can only give a very short overview, please see the wiki for a full explanation.
+
+        mDiagram = Objects.requireNonNull(diagram);
+        mCanvas = Objects.requireNonNull(canvas);
+        mData = mCanvas.getCurrentPage();
+
+        checkValidBrailleRaster();
+
+
+        // Basic chart layout happens in the following steps (1-4):
+
+        String strDiagramTitle = "I am a bar chart"; // TODO: WHERE DO WE GET THIS INFO FROM??
+        Rectangle barArea = mCanvas.getCellRectangle();
+        int titleBarHeight;
+        Rectangle titleArea, xAxisArea;
+
+        try {
+            // 1. Reserve space for the diagram title on the top
+            titleBarHeight = mTextRasterizer.calculateRequiredHeight(strDiagramTitle, 0, 0,
+                    barArea.intWrapper().getWidth() * mCanvas.getCellWidth(), mCanvas);
+            titleArea = barArea.removeFromTop(mCanvas.getCellYFromDotY(titleBarHeight));
+
+            // 2. Reserve space for the x-axis on the bottom
+            xAxisArea = barArea.removeFromBottom(2);
+
+            // 3. Reserve space for bar captions on the left or right side
+            if (mLeftCaption) {
+                barArea.removeFromLeft(mCaptionLength);
+            } else {
+                barArea.removeFromRight(mCaptionLength);
+            }
+
+            // 4. One extra cell is removed, because there must always be one cell of space for the y-axis.
+            barArea.removeFromRight(1);
+        } catch (Rectangle.OutOfSpaceException e) {
+            // If the rectangle throws an OutOfSpaceException, this just means that there is no way to fit all
+            // components of the chart inside the given area.
+            throw new InsufficientRenderingAreaException("Not enough space to build bar chart layout.", e);
+        }
+
+
+        // The remaining rectangle 'barArea' does now represent the available area for the bars to be displayed.
+
+
+        // Now the charts value range and categories are analyzed to figure out where the y-axis (x = 0) shall be
+        // placed and how to scale the bars to still fit the available space.
+
+        double negValueRangeSize = abs(min(diagram.getMinY(), 0));
+        double posValueRangeSize = max(diagram.getMaxY(), 0);
+        // The complete value range is calculated in a way that it always includes zero, even if all category values
+        // are positive or negative with absolute values > 0, because the y axis will always be positioned at x = 0.
+        double valueRangeSize = negValueRangeSize + posValueRangeSize;
+
+        // Calculate the amount of distinguishable units / steps (on the x-axis) that fit into the given space.
+        int availableUnits = (int) floor(barArea.getWidth() * mCanvas.getCellWidth() / mTextureUnitSize);
+
+        // The width of a single x-axis step depends on the value range and the available units that can be displayed.
+        // A little helper algorithm tries to find a good scaling such that the axis divisions correspond to the original
+        // value range in a rational way. (e.g. one step on the x-axis is a half of the order of magnitude of the value range.)
+        double xAxisStepWidth = findAxisScaling(valueRangeSize, availableUnits);
+
+        // Divide the bar area into a negative and a positive fragment, depending on the value range.
+        int negUnits = (int) round((negValueRangeSize / valueRangeSize) * availableUnits);
+        int posUnits = availableUnits - negUnits;
+
+        // The x coordinate of the origin is (logically) set to be exactly between negative and positive range.
+        int xAxisOriginPosition = barArea.intWrapper().getX() + mCanvas.getCellXFromDotX(negUnits * mTextureUnitSize);
+
+        // Now the available vertical space is analyzed to figure out the bar thickness.
+        // Again, this is done by a help algorithm.
+        int availableCells = barArea.intWrapper().getHeight();
+        int barThickness = findBarThickness(availableCells);
+
+
+
+        // Now everything is ready to be rasterized onto the canvas.
+
+        // 1. Rasterize the diagram title
+        BrailleText diagramTitle = new BrailleText(strDiagramTitle, titleArea.scaledBy(mCanvas.getCellWidth(), mCanvas.getCellHeight()));
+        mTextRasterizer.rasterize(diagramTitle, mCanvas);
+
+        // 2. Draw the individual bars for each category.
+        int refCellX = xAxisOriginPosition; // align bars with y-axis.
+        int refCellY = barArea.intWrapper().getBottom(); // start with the bottommost bar.
+        for (int i = 0; i < mDiagram.getCategoryCount(); i++) {
+            double value = mDiagram.getDataSet(0).get(i).getY();
+            // calculate how to represent value with the current scaling
+            int barLength = (int) round(value / xAxisStepWidth) * mTextureUnitSize;
+            // draw the bar including its caption and then move the reference cell y position up
+            String categoryName = "foobar"; // TODO: read real name
+            refCellY = drawHorizontalBar(refCellX, refCellY, barLength, barThickness, categoryName);
+        }
+
+        // 3. Rasterize both axes
+        // First calculate axis positions and bounds
+        // (these are conversions from 'cell-based' rectangles into a 'dot-based' representation, because the axis
+        // rasterizer uses linear mapping, needs 'dot coordinates' and does not care about cell borders)
+        Rectangle yAxisBound = barArea.scaledBy(mCanvas.getCellWidth(), mCanvas.getCellHeight());
+        Rectangle xAxisBound = xAxisArea.scaledBy(mCanvas.getCellWidth(), mCanvas.getCellHeight());
+        int originX = (xAxisOriginPosition + 1) * mCanvas.getCellWidth() - 1; // convert cell position to dot position
+        int originY = xAxisBound.intWrapper().getY();
+
+        // y-axis: no units, no tickmarks
+        Axis yAxis = new Axis(Axis.Type.Y_AXIS, originX, originY, 1, 0);
+        yAxis.setBoundary(yAxisBound);
+        mAxisRasterizer.rasterize(yAxis, mCanvas);
+
+        // x-axis: tickmarks for every second (full) line of the uniform texture.
+        int xAxisStepDots = mTextureUnitSize * 2;
+        Axis xAxis = new Axis(Axis.Type.X_AXIS, originX, originY, xAxisStepDots, 2);
+        xAxis.setBoundary(xAxisBound);
+        // a bit more complicated than y-axis here: building a map for the axis labels
+        Map<Integer, String> xAxisLabels = new HashMap<>();
+        char labelLetter = 'A';
+        for (int axisTick = (negUnits / 2) * -1; axisTick <= (posUnits / 2); axisTick++) {
+            xAxisLabels.put(axisTick, Character.toString(labelLetter));
+            labelLetter++;
+        }
+        xAxis.setLabels(xAxisLabels);
+        mAxisRasterizer.rasterize(xAxis, mCanvas);
+
+        // Finished.
+
+    }
+
+    // Layout help methods
+
+    private double findAxisScaling(final double valueRangeSize, final int availableUnits) {
+        double minRangePerUnit = valueRangeSize / availableUnits; // this range must fit into one 'axis step'
+        double orderOfMagnitude = pow(10, ceil(log10(minRangePerUnit)));
+        double scaledRange = 0;
+        for (double scaling : mUnitScalings) {
+            scaledRange = (scaling * orderOfMagnitude);
+            if (scaledRange >= minRangePerUnit) {
+                break;
+            }
+        }
+        return scaledRange;
+    }
+
+    private int findBarThickness(final int availableCells) throws InsufficientRenderingAreaException {
+
+        int barThickness = mBarMaxThickness;
+        if ((barThickness % 2) == 0) {
+            barThickness++; // Make bar thickness an uneven number. Needed for the uniform texture.
+        }
+
+        // Probe the maximum possible bar thickness
+        int requiredCells;
+        while (availableCells < (requiredCells = requiredCells(barThickness))) {
+            barThickness -= 2;
+            if (barThickness < mBarMinThickness) {
+                throw new InsufficientRenderingAreaException("Not enough space to render given amount of categories in "
+                        + "bar chart. " + mDiagram.getCategoryCount() + " categories given. " + requiredCells
+                        + " cells required but only " + availableCells + " available. "
+                        + "(Minimum bar thickness is set to " + mBarMinThickness + " dots)");
+            }
+        }
+        return barThickness;
+    }
+
+    private int requiredCells(final int barThickness) {
+        int cellHeight = mCanvas.getCellHeight();
+        // basic thickness of single bar
+        int barSize = mBarInCellPadding + barThickness;
+        // additional padding between neighboring bars
+        int sizeInclusive = barSize + (mExtraBarCellPadding * cellHeight) + mBarDotPadding + 1;
+        // how many cells needed? -> Can borders of neighboring cells 'share' a cell?
+        int barCells = (int) ceil(barSize / (double) cellHeight); // important cast, else int division happens
+        int cellsInclusive = (int) ceil(sizeInclusive / (double) cellHeight);
+        // --> Linear equation
+        return barCells + (cellsInclusive - 1) * (mDiagram.getCategoryCount() - 1);
+    }
+
+
+    /**
+     * Draws a horizontal category bar to the canvas.
+     * @param cellX The reference cell x position.
+     * @param cellY The reference cell y position.
+     * @param length The length of the bar in dots (can also be negative).
+     * @param thickness The thickness of the bar in dots.
+     * @param categoryName The caption to be displayed next to the bar.
+     * @return The y position of the next reference cell for the next bar.
+     * @throws InsufficientRenderingAreaException If the underlying text rasterizer throws it while rasterizing the bar caption.
+     */
+    private int drawHorizontalBar(final int cellX, final int cellY, final int length, final int thickness,
+                                  final String categoryName) throws InsufficientRenderingAreaException {
+        // the bar is drawn according to the bottom right dot of the cell as reference
+        int bottomRightDotX = (cellX + 1) * mCanvas.getCellWidth() - 1;
+        int bottomRightDotY = (cellY + 1) * mCanvas.getCellHeight() - 1;
+
+        // first, a rectangle is drawn between two points: 'lower' (at y-axis) and 'upper' (at bars end)
+        int lowerX = bottomRightDotX;
+        int lowerY = bottomRightDotY - mBarInCellPadding; // position relative to reference point
+        int upperX = lowerX + length; // add the horizontal size of the bar (representing the category value)
+        int upperY = lowerY - (thickness - 1); // substract the bar thickness (up = towards smaller y)
+        Rasterizer.rectangle(lowerX, lowerY, upperX, upperY, mData, true);
+
+        // then the rectangle is filled with the uniform texture
+        int textureStep = Integer.signum(upperX - lowerX) * mTextureUnitSize;
+        int i = 0;
+        for (int dotX = lowerX; dotX != upperX; dotX += textureStep) {
+            // alternate between dotted and solid line
+            if ((i % 2) == 0) {
+                // solid line
+                Rasterizer.fill(dotX, lowerY, dotX, upperY, mData, true);
+            } else {
+                // dotted line
+                int j = 0;
+                for (int dotY = lowerY; dotY > upperY; dotY--) {
+                    mData.setValue(dotY, dotX, ((j % 2) == 0));
+                    j++;
+                }
+            }
+            i++;
+        }
+
+        // finally, rasterize the bar caption text
+        int captionCellX, captionCellY;
+        captionCellY = mCanvas.getCellYFromDotY(upperY + (thickness / 2));
+        if (mLeftCaption) {
+            captionCellX = mCanvas.getCellXFromDotX(min(lowerX, upperX) - 1) - mCaptionLength;
+        } else {
+            captionCellX = mCanvas.getCellXFromDotX(max(lowerX, upperX) + 1);
+        }
+        Rectangle captionArea = new Rectangle(captionCellX, captionCellY, mCaptionLength, 1);
+        mTextRasterizer.rasterize(new BrailleText(categoryName,
+                captionArea.scaledBy(mCanvas.getCellWidth(), mCanvas.getCellHeight())), mCanvas);
+
+        return mCanvas.getCellYFromDotY(upperY - (mBarDotPadding + 1)) - mExtraBarCellPadding;
+    }
+
+    private void checkValidBrailleRaster() throws InsufficientRenderingAreaException {
+        boolean isValidBrailleRaster = ((mCanvas.getCellWidth() == 2)
+                && (mCanvas.getCellHeight() >= 3) && (mCanvas.getCellHeight() <= 4));
+        if (!isValidBrailleRaster) {
+            // TODO: Maybe refactor to have different rendering exceptions?
+            throw new InsufficientRenderingAreaException("This rasterizer can only work with a 6-dot or 8-dot "
+                    + "braille raster.");
+        }
+    }
+
+    /*
+    private boolean isEquidistantRaster() {
+        return (
+                (canvas.getHorizontalCellDistance() == canvas.getVerticalCellDistance())
+                && (canvas.getVerticalCellDistance() == canvas.getHorizontalDotDistance())
+                && (canvas.getHorizontalDotDistance() == canvas.getVerticalDotDistance())
+        );
+    }
+     */
+}
\ No newline at end of file
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/package-info.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..2ed44fe9a99fb3a78c6861911d3dc46e98303b1c
--- /dev/null
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * This package contains all the required classes and interfaces to rasterize or plot an internal diagram representation into printable data.
+ */
+package de.tudresden.inf.mci.brailleplot.rendering;
\ No newline at end of file
diff --git a/src/main/resources/Bundle.properties b/src/main/resources/Bundle.properties
deleted file mode 100644
index 85508b57507ad34b4ff4e2b0af3dc0a3c5e86bf5..0000000000000000000000000000000000000000
--- a/src/main/resources/Bundle.properties
+++ /dev/null
@@ -1,163 +0,0 @@
-param.title = Titel der Grafik [--title "Sinus- und Cosinusfunktion"]
-param.size = Gr��e der Grafik in Millimeter [--size 300,500]
-param.xrange = Darstellungsbereich der x-Achse und abweichender Titel. Wird automatisch auf den Datenbereich erweitert, wenn "--autoscale" angegeben ist. Ist "--diagramtype FunctionPlot" gesetzt, wird der Bereich erweitert, so dass er 0 enth�lt. [--xrange "Jahre::-3:5"]
-param.yrange = Darstellungsbereich der y-Achse und abweichender Titel. Wird automatisch auf den Datenbereich erweitert, wenn "--autoscale" angegeben ist. Ist "--diagramtype FunctionPlot" gesetzt, wird der Bereich erweitert, so dass er 0 enth�lt. [--yrange -3:5]
-param.pi = Einteilung der x-Achse in Vielfache von pi
-param.xlines = Hilfslinien auf der x-Achse, durch Leerzeichen getrennt [--xlines "1 3.5"]
-param.ylines = Hilfslinien auf der y-Achse, durch Leerzeichen getrennt [--ylines "1 3.5"]
-param.css = Direkte Angabe von zus�tzlichen CSS-Anweisungen oder Pfad zu einer CSS-Datei [--css "#grid { stroke: #444444 }"] oder [--css stylesheet.css]
-param.gnuplot = Pfad zum Gnuplot-Programm [--gnuplot "C:\gnuplot.exe"]
-param.output = Pfad zur Ausgabedatei [--output "output/sinus.svg"]
-param.help = Hilfe
-param.points = Liste von Punkten die markiert werden sollen. Es k�nnen mehrere Listen angegeben werden, welche duch {} zu grupieren sind. Punkte werden durch Leerzeichen getrennt. X- und Y-Werte jeweils durch ein Komma getrennt. Jeder Liste kann ein Titel gegeben werden ["Liste 1"::{1.2,3 4.5,6}{-1,-2.3}]. Wird ignoriert wenn --csvdata angegeben und ein valider Pfad ist.
-param.integral = Zeichnet eine Integralfl�che zwischen zwei Funktionen oder der X-Achse. Nur wenn "--diagramtype FunctionPlot" gesetzt ist. [--integral "Wahrscheinlichkeit::1,2[-2:2]" ]
-param.device = Ausgabeger�t
-param.csvpath = Pfad zur CSV-Datei, aus der die Punkt-, Linien- oder Balkendaten gelesen werden (�berschreibt --points) [--csvpath data.csv]
-param.csvtype = Aufbau der CSV-Datei, kann abgek�rzt werden [--csvtype x_aligned_categories] oder [--csvtype xac]
-param.csvorientation = Orientierung der CSV-Datei [--csvorientation horizontal] oder [--csvo h]
-param.autoscale = Wenn angegeben, wird das Diagramm automatisch auf den Datenbereich skaliert, wobei eine angegebene --xrange bzw. --yrange den minimal Rahmen darstellen. Wenn "--diagramtype FunctionPlot" gesetzt ist, wird der Parameter ignoriert. [--autoscale]
-param.showhorizontalgrid = Horizontale Gitterlinien zeigen [--hgrid on] oder [--hgrid off]. Wird weder vgrid noch hgrid angegeben, wird je nach Diagrammtyp eine Standarddarstellung gew�hlt.
-param.showverticalgrid = Horizontale Gitterlinien zeigen [--vgrid on] oder [--vgrid off]. Wird weder vgrid noch hgrid angegeben, wird je nach Diagrammtyp eine Standarddarstellung gew�hlt.
-param.diagramtype = Der Typ des Diagramms. Beeinflusst, welche weiteren Parameter ausgewertet werden.
-param.trendline = Eine Funktion zur Berechnung der Trendline. "--diagramtype ScatterPlot" muss gesetzt sein. Parameter werden hinter dem Funktionsnamen angegeben. M�gliche Linientypen (Parameter in Klammern) sind mit Standardwerten: "MovingAverage n" (n: ganzzahlig und gr��er als 0, Filtergr��e ist 2*n+1), "ExponentialSmoothing alpha" (alpha: zwischen 0 und 1), "BrownLES alpha forecast" (alpha: zwischen 0 und 1; forecast: gr��er als 0, wieviele Werte extrapoliert werden), "LinearRegression" (keine Parameter). Beispiel: "--trendline BrownLES 0.2 10"
-param.hideoriginalpoints = Stellt die originalen Datenpunkte nicht dar, wenn "--trendline" gesetzt ist.
-param.showdoubleaxes = Doppelte Achsen zeigen [--daxes on] oder [--daxes off]. Nicht f�r [--diagramtype FunctionPlot] verwendbar. Wird der Parameter nicht angegeben, wird je nach Diagrammtyp eine Wahl vorgegeben.
-param.showlinepoints = Datenpunkte auf den Linien eines Liniendiagramms markieren [--lp on] oder [--lp off]. Nur f�r [--diagramtype LineChart] verwendbar. Wird der Parameter nicht angegeben, werden die Punkte nur angezeigt, wenn genug Platz vorhanden ist.
-param.pointsborderless = Wenn angegeben, erhalten Punktsymbole keinen Rand. Kann die Darstellung von Liniendiagrammen mit Datenpunkten verbessern.
-param.baraccumulation = W�hlt, auf welche Art mehrere Datenreihen in Balkendiagrammen akkumuliert werden [--ba stacked]
-param.colors = Manuelle Farbwahl. Die Farben k�nnen durch Leerzeichen getrennt angegeben werden. Wurden keine oder zu wenige Farben angegeben, wird die Liste aus der Vorgabereihenfolge aufgef�llt [--colors rot gr�n blau]
-param.sorting = Sortieren von nominalen Daten [--sorting <algorithm>]
-param.sortdescending = Wenn [--sorting <algorithm>] verwendet wird, sortiere absteigend [--sortdescending]
-param.xtitle = Titel der X-Achse [--xtitle Titel]. Titel, die zu gro� f�r eine Zeile sind, werden zu Darstellungsfehlern f�hren.
-param.ytitle = Titel der Y-Achse [--ytitle Titel]. Titel, die zu gro� f�r eine Zeile sind, werden zu Darstellungsfehlern f�hren.
-param.xunit = Einheit der x-Achse [--xunit "1000 Menschen"]
-param.yunit = Einheit der y-Achse [--yunit "m^2"]
-
-error.onoffparameter = Ung�ltiger Wert "{0}". Erlaubt sind: "on" und "off". 
-
-xaxis = X-Achse
-yaxis = Y-Achse
-
-legend = Legende
-legend.xrange = X-Bereich{2}: {0} bis {1}
-legend.yrange = Y-Bereich{2}: {0} bis {1}
-legend.xtic = Skaleneinteilung: {0}
-legend.ytic = Skaleneinteilung: {0}
-
-legend.poi_1 = Punkt {0} 
-legend.poi_n = Punkte {0} 
-
-legend.integral_0 = Integral von {0} bis {1} �ber {2}(x)dx
-legend.integral_1 = Integral von {0} bis {1} �ber {2}(x)dx und {3}(x)dx
-
-desc = Beschreibung
-
-desc.intro_0 = Es wird ein leeres Koordinatensystem im x-Bereich{6} von {0} bis {1} (Skaleneinteilung: {2}) und y-Bereich{7} von {3} bis {4} (Skaleneinteilung: {5}) dargestellt.
-desc.intro_1 = Es wird eine�Funktion im x-Bereich{6} von {0} bis {1} (Skaleneinteilung: {2}) und y-Bereich{7} von {3} bis {4} (Skaleneinteilung: {5}) dargestellt:
-desc.intro_n = Es werden {8}�Funktionen im x-Bereich{6} von {0} bis {1} (Skaleneinteilung: {2}) und y-Bereich{7} von {3} bis {4} (Skaleneinteilung: {5}) dargestellt:
-
-desc.intersections_0 = Die Funktionen haben keine Schnittpunkte.
-desc.intersections_1 = Die Funktionen {0} und {1} haben einen Schnittpunkt:
-desc.intersections_n = Die Funktionen {0} und {1} haben {2} Schnittpunkte:
-
-desc.extrema_0 = Die Funktion{0} hat keine Extrempunkte.
-desc.extrema_1 = Die Funktion{0} hat einen Extrempunkt:
-desc.extrema_n = Die Funktion{0} hat {1} Extrempunkte:
-
-desc.roots_0 = Sie hat keine Nullstellen.
-desc.roots_1 = Sie hat eine Nullstelle:
-desc.roots_n = Sie hat {0} Nullstellen:
-
-legend.poi.intro_1 = Folgender Punkt ist eingezeichnet:
-legend.poi.intro_n = Folgende Punkte sind eingezeichnet:
-
-legend.poi.list_1 = Punkt {0}:
-legend.poi.list_n = Punktliste {0}:
-
-desc.integral_0 = Angezeigt wird das Integral von {0} bis {1} �ber {2}(x)dx.
-desc.integral_1 = Angezeigt wird das Integral von {0} bis {1} �ber {2}(x)dx und {3}(x)dx.
-
-desc.note = Die Angaben sind N�herungswerte und beziehen sich nur auf den sichtbaren Bereich.
-
-### Descriptions for diagrams (not graphs)
-
-desc.diagramtype_title = Dieses {0} tr�gt den Titel "{1}".
-desc.diagramtype_notitle = Es wird ein {0} dargestellt.
-desc.diagramtype_barcharttype = Die Balken sind vertikal {0}.
-
-# New diagram types should get their mName setup here.
-# The format is desc.diagramtype.<DiagramType.toString()>
-desc.diagramtype.ScatterPlot = Punktdiagramm
-desc.diagramtype.LineChart = Liniendiagramm
-desc.diagramtype.BarChart = Balkendiagramm
-
-# single is used when only one data set is displayed
-desc.diagramtype_barcharttype.single = ausgerichtet
-desc.diagramtype_barcharttype.stacked = gestapelt
-desc.diagramtype_barcharttype.grouped = gruppiert
-
-desc.axis_position_first = Die Achsen und ihre Beschriftungen befinden sich links des Diagramms und darunter
-desc.axis_position_first.intersect = und schneiden sich im Punkt {0}
-desc.axis_position_second_start.both = Beide Achsen werden
-desc.axis_position_second_start.vertical = Die vertikale Achse wird
-desc.axis_position_second = auch auf der gegen�berliegenden Seite des Diagramms dargestellt.
-
-desc.vertical_det = vertikale
-desc.vertical_neutral = vertikales
-
-desc.horizontal_det = horizontale
-desc.horizontal_neutral = horizontales
-
-# Maybe the following three strings have to be changed to a more
-# complex representation if other languages are added.
-desc.axis_detail = Die {0} Achse ist{1}{2} von {3} bis {4} in Intervallen von {5} markiert.
-desc.axis_detail_title = als "{0}" bezeichnet und
-desc.axis_detail_unit = in der Einheit {0}
-
-desc.axis_grid = Es wird ein {0} Gitter dargestellt.
-
-### Data descriptions
-
-desc.datacount_0 = Es wird ein leeres Diagramm dargestellt.
-desc.datacount_1 = Es wird eine Datenreihe dargestellt
-desc.datacount_n = Es werden {0} Datenreihen dargestellt
-
-desc.data_0 = Keine Punkte
-desc.data_1 = Ein Punkt
-desc.data_n = {0} Punkte
-
-### Line chart specific descriptions
-
-desc.line.minmax = Hinter ihren Namen stehen die Maximal- bzw. Minimalwerte.
-
-### Bar chart specific descriptions
-
-desc.barchart.sorting = Die Balken sind {0} und von links nach rechts folgenderma�en beschriftet:
-
-# {0}: sorting criterium, {1}: ascending/descending, {2}: sorted
-desc.barchart.sorting.sortingstring = {0} {1} {2}
-
-desc.barchart.sorting.sorted = sortiert
-desc.barchart.sorting.notsorted = nicht sortiert
-
-desc.barchart.sorting.MaxFirstDataSet = nach der ersten Datenreihe
-desc.barchart.sorting.Alphabetical = nach ihrem Titel alphabetisch
-desc.barchart.sorting.CategorialSum = nach der Summe jeder Kategorie
-
-desc.barchart.sorting.asc = aufsteigend
-desc.barchart.sorting.desc = absteigend
-
-### Trendline descriptions
-
-#{0}: desc.trendline_points_[1|n] {1}: desc.trendline_algorithm.<mName> {2} TrendlineAlgorithm.getAlgorithmParams()
-desc.trendline_points = Zu {0} wird eine Trendlinie, die mit dem Verfahren "{1}" {2} berechnet wurde, dargestellt.
-desc.trendline_points_1 = der Datenreihe
-desc.trendline_points_n = jeder Datenreihe
-
-desc.trendline_algorithm.BrownLES = lineare exponentielle Gl�ttung nach Brown
-desc.trendline_algorithm.MovingAverage = gleitendes Mittel
-desc.trendline_algorithm.LinearRegression = lineare Regression
-desc.trendline_algorithm.ExponentialSmoothing = exponentielle Gl�ttung
-
-desc.trendline_only_1 = Bei der dargestellten Linien handelt es sich um eine Trendlinie, die mit dem Verfahren {0} {1} berechnet wurde. Sie tr�gt den Titel "{2}".
-desc.trendline_only_n = Bei den dargestellten Linien handelt es sich um Trendlinien, die mit dem Verfahren "{0}" {1} berechnet wurden. Es werden {2} Datenreihen dargestellt:
diff --git a/src/main/resources/parser_bar.csv b/src/main/resources/parser_bar.csv
deleted file mode 100644
index 9de9a79bb52db16bebda4c6594bc4387b0ebc9f3..0000000000000000000000000000000000000000
--- a/src/main/resources/parser_bar.csv
+++ /dev/null
@@ -1,4 +0,0 @@
-,Reihe a,Reihe b,Reihe c
-Kat.1,3,"2,5",1
-Kat.2,4,3,2
-Kat.3,"4,5",3,1
\ No newline at end of file
diff --git a/src/main/resources/parser_line.csv b/src/main/resources/parser_line.csv
deleted file mode 100644
index 73ed49d88f0ea2da2d675d78edeb181f8f35a2d7..0000000000000000000000000000000000000000
--- a/src/main/resources/parser_line.csv
+++ /dev/null
@@ -1,5 +0,0 @@
-Linie1 ,1,7,9,2,10
-       ,1,2,5,4,10
-Linie2 ,0,2,7,9,1,4
-       ,3,9,4,2,5,7
-
diff --git a/src/main/resources/parser_scatter.csv b/src/main/resources/parser_scatter.csv
deleted file mode 100644
index 8a9f0a45859a1a67592e19ec6978d961cc88eebf..0000000000000000000000000000000000000000
--- a/src/main/resources/parser_scatter.csv
+++ /dev/null
@@ -1,6 +0,0 @@
-Erste Gruppe,1,2,3,4,5
-,1,2,3,4,5
-Zweite Gruppe,5,4,3,2,1
-,5,4,3,2,1
-Dritte Gruppe,1,2,3,4,7
-,5,4,3,2,0
\ No newline at end of file
diff --git a/src/resources/Bundle.properties b/src/resources/Bundle.properties
deleted file mode 100644
index 5d686412512320ad16fd6ba2dd2e938b3632997f..0000000000000000000000000000000000000000
--- a/src/resources/Bundle.properties
+++ /dev/null
@@ -1,163 +0,0 @@
-param.title = Titel der Grafik [--title "Sinus- und Cosinusfunktion"]
-param.size = Größe der Grafik in Millimeter [--size 300,500]
-param.xrange = Darstellungsbereich der x-Achse und abweichender Titel. Wird automatisch auf den Datenbereich erweitert, wenn "--autoscale" angegeben ist. Ist "--diagramtype FunctionPlot" gesetzt, wird der Bereich erweitert, so dass er 0 enthält. [--xrange "Jahre::-3:5"]
-param.yrange = Darstellungsbereich der y-Achse und abweichender Titel. Wird automatisch auf den Datenbereich erweitert, wenn "--autoscale" angegeben ist. Ist "--diagramtype FunctionPlot" gesetzt, wird der Bereich erweitert, so dass er 0 enthält. [--yrange -3:5]
-param.pi = Einteilung der x-Achse in Vielfache von pi
-param.xlines = Hilfslinien auf der x-Achse, durch Leerzeichen getrennt [--xlines "1 3.5"]
-param.ylines = Hilfslinien auf der y-Achse, durch Leerzeichen getrennt [--ylines "1 3.5"]
-param.css = Direkte Angabe von zusätzlichen CSS-Anweisungen oder Pfad zu einer CSS-Datei [--css "#grid { stroke: #444444 }"] oder [--css stylesheet.css]
-param.gnuplot = Pfad zum Gnuplot-Programm [--gnuplot "C:\gnuplot.exe"]
-param.output = Pfad zur Ausgabedatei [--output "output/sinus.svg"]
-param.help = Hilfe
-param.points = Liste von Punkten die markiert werden sollen. Es können mehrere Listen angegeben werden, welche duch {} zu grupieren sind. Punkte werden durch Leerzeichen getrennt. X- und Y-Werte jeweils durch ein Komma getrennt. Jeder Liste kann ein Titel gegeben werden ["Liste 1"::{1.2,3 4.5,6}{-1,-2.3}]. Wird ignoriert wenn --csvdata angegeben und ein valider Pfad ist.
-param.integral = Zeichnet eine Integralfläche zwischen zwei Funktionen oder der X-Achse. Nur wenn "--diagramtype FunctionPlot" gesetzt ist. [--integral "Wahrscheinlichkeit::1,2[-2:2]" ]
-param.device = Ausgabegerät
-param.csvpath = Pfad zur CSV-Datei, aus der die Punkt-, Linien- oder Balkendaten gelesen werden (überschreibt --points) [--csvpath data.csv]
-param.csvtype = Aufbau der CSV-Datei, kann abgekürzt werden [--csvtype x_aligned_categories] oder [--csvtype xac]
-param.csvorientation = Orientierung der CSV-Datei [--csvorientation horizontal] oder [--csvo h]
-param.autoscale = Wenn angegeben, wird das Diagramm automatisch auf den Datenbereich skaliert, wobei eine angegebene --xrange bzw. --yrange den minimal Rahmen darstellen. Wenn "--diagramtype FunctionPlot" gesetzt ist, wird der Parameter ignoriert. [--autoscale]
-param.showhorizontalgrid = Horizontale Gitterlinien zeigen [--hgrid on] oder [--hgrid off]. Wird weder vgrid noch hgrid angegeben, wird je nach Diagrammtyp eine Standarddarstellung gewählt.
-param.showverticalgrid = Horizontale Gitterlinien zeigen [--vgrid on] oder [--vgrid off]. Wird weder vgrid noch hgrid angegeben, wird je nach Diagrammtyp eine Standarddarstellung gewählt.
-param.diagramtype = Der Typ des Diagramms. Beeinflusst, welche weiteren Parameter ausgewertet werden.
-param.trendline = Eine Funktion zur Berechnung der Trendline. "--diagramtype ScatterPlot" muss gesetzt sein. Parameter werden hinter dem Funktionsnamen angegeben. Mögliche Linientypen (Parameter in Klammern) sind mit Standardwerten: "MovingAverage n" (n: ganzzahlig und größer als 0, Filtergröße ist 2*n+1), "ExponentialSmoothing alpha" (alpha: zwischen 0 und 1), "BrownLES alpha forecast" (alpha: zwischen 0 und 1; forecast: größer als 0, wieviele Werte extrapoliert werden), "LinearRegression" (keine Parameter). Beispiel: "--trendline BrownLES 0.2 10"
-param.hideoriginalpoints = Stellt die originalen Datenpunkte nicht dar, wenn "--trendline" gesetzt ist.
-param.showdoubleaxes = Doppelte Achsen zeigen [--daxes on] oder [--daxes off]. Nicht für [--diagramtype FunctionPlot] verwendbar. Wird der Parameter nicht angegeben, wird je nach Diagrammtyp eine Wahl vorgegeben.
-param.showlinepoints = Datenpunkte auf den Linien eines Liniendiagramms markieren [--lp on] oder [--lp off]. Nur für [--diagramtype LineChart] verwendbar. Wird der Parameter nicht angegeben, werden die Punkte nur angezeigt, wenn genug Platz vorhanden ist.
-param.pointsborderless = Wenn angegeben, erhalten Punktsymbole keinen Rand. Kann die Darstellung von Liniendiagrammen mit Datenpunkten verbessern.
-param.baraccumulation = Wählt, auf welche Art mehrere Datenreihen in Balkendiagrammen akkumuliert werden [--ba stacked]
-param.colors = Manuelle Farbwahl. Die Farben können durch Leerzeichen getrennt angegeben werden. Wurden keine oder zu wenige Farben angegeben, wird die Liste aus der Vorgabereihenfolge aufgefüllt [--colors rot grün blau]
-param.sorting = Sortieren von nominalen Daten [--sorting <algorithm>]
-param.sortdescending = Wenn [--sorting <algorithm>] verwendet wird, sortiere absteigend [--sortdescending]
-param.xtitle = Titel der X-Achse [--xtitle Titel]. Titel, die zu groß für eine Zeile sind, werden zu Darstellungsfehlern führen.
-param.ytitle = Titel der Y-Achse [--ytitle Titel]. Titel, die zu groß für eine Zeile sind, werden zu Darstellungsfehlern führen.
-param.xunit = Einheit der x-Achse [--xunit "1000 Menschen"]
-param.yunit = Einheit der y-Achse [--yunit "m^2"]
-
-error.onoffparameter = Ungültiger Wert "{0}". Erlaubt sind: "on" und "off". 
-
-xaxis = X-Achse
-yaxis = Y-Achse
-
-legend = Legende
-legend.xrange = X-Bereich{2}: {0} bis {1}
-legend.yrange = Y-Bereich{2}: {0} bis {1}
-legend.xtic = Skaleneinteilung: {0}
-legend.ytic = Skaleneinteilung: {0}
-
-legend.poi_1 = Punkt {0} 
-legend.poi_n = Punkte {0} 
-
-legend.integral_0 = Integral von {0} bis {1} über {2}(x)dx
-legend.integral_1 = Integral von {0} bis {1} über {2}(x)dx und {3}(x)dx
-
-desc = Beschreibung
-
-desc.intro_0 = Es wird ein leeres Koordinatensystem im x-Bereich{6} von {0} bis {1} (Skaleneinteilung: {2}) und y-Bereich{7} von {3} bis {4} (Skaleneinteilung: {5}) dargestellt.
-desc.intro_1 = Es wird eine Funktion im x-Bereich{6} von {0} bis {1} (Skaleneinteilung: {2}) und y-Bereich{7} von {3} bis {4} (Skaleneinteilung: {5}) dargestellt:
-desc.intro_n = Es werden {8} Funktionen im x-Bereich{6} von {0} bis {1} (Skaleneinteilung: {2}) und y-Bereich{7} von {3} bis {4} (Skaleneinteilung: {5}) dargestellt:
-
-desc.intersections_0 = Die Funktionen haben keine Schnittpunkte.
-desc.intersections_1 = Die Funktionen {0} und {1} haben einen Schnittpunkt:
-desc.intersections_n = Die Funktionen {0} und {1} haben {2} Schnittpunkte:
-
-desc.extrema_0 = Die Funktion{0} hat keine Extrempunkte.
-desc.extrema_1 = Die Funktion{0} hat einen Extrempunkt:
-desc.extrema_n = Die Funktion{0} hat {1} Extrempunkte:
-
-desc.roots_0 = Sie hat keine Nullstellen.
-desc.roots_1 = Sie hat eine Nullstelle:
-desc.roots_n = Sie hat {0} Nullstellen:
-
-legend.poi.intro_1 = Folgender Punkt ist eingezeichnet:
-legend.poi.intro_n = Folgende Punkte sind eingezeichnet:
-
-legend.poi.list_1 = Punkt {0}:
-legend.poi.list_n = Punktliste {0}:
-
-desc.integral_0 = Angezeigt wird das Integral von {0} bis {1} über {2}(x)dx.
-desc.integral_1 = Angezeigt wird das Integral von {0} bis {1} über {2}(x)dx und {3}(x)dx.
-
-desc.note = Die Angaben sind Näherungswerte und beziehen sich nur auf den sichtbaren Bereich.
-
-### Descriptions for diagrams (not graphs)
-
-desc.diagramtype_title = Dieses {0} trägt den Titel "{1}".
-desc.diagramtype_notitle = Es wird ein {0} dargestellt.
-desc.diagramtype_barcharttype = Die Balken sind vertikal {0}.
-
-# New diagram types should get their name setup here.
-# The format is desc.diagramtype.<DiagramType.toString()>
-desc.diagramtype.ScatterPlot = Punktdiagramm
-desc.diagramtype.LineChart = Liniendiagramm
-desc.diagramtype.BarChart = Balkendiagramm
-
-# single is used when only one data set is displayed
-desc.diagramtype_barcharttype.single = ausgerichtet
-desc.diagramtype_barcharttype.stacked = gestapelt
-desc.diagramtype_barcharttype.grouped = gruppiert
-
-desc.axis_position_first = Die Achsen und ihre Beschriftungen befinden sich links des Diagramms und darunter
-desc.axis_position_first.intersect = und schneiden sich im Punkt {0}
-desc.axis_position_second_start.both = Beide Achsen werden
-desc.axis_position_second_start.vertical = Die vertikale Achse wird
-desc.axis_position_second = auch auf der gegenüberliegenden Seite des Diagramms dargestellt.
-
-desc.vertical_det = vertikale
-desc.vertical_neutral = vertikales
-
-desc.horizontal_det = horizontale
-desc.horizontal_neutral = horizontales
-
-# Maybe the following three strings have to be changed to a more
-# complex representation if other languages are added.
-desc.axis_detail = Die {0} Achse ist{1}{2} von {3} bis {4} in Intervallen von {5} markiert.
-desc.axis_detail_title = als "{0}" bezeichnet und
-desc.axis_detail_unit = in der Einheit {0}
-
-desc.axis_grid = Es wird ein {0} Gitter dargestellt.
-
-### Data descriptions
-
-desc.datacount_0 = Es wird ein leeres Diagramm dargestellt.
-desc.datacount_1 = Es wird eine Datenreihe dargestellt
-desc.datacount_n = Es werden {0} Datenreihen dargestellt
-
-desc.data_0 = Keine Punkte
-desc.data_1 = Ein Punkt
-desc.data_n = {0} Punkte
-
-### Line chart specific descriptions
-
-desc.line.minmax = Hinter ihren Namen stehen die Maximal- bzw. Minimalwerte.
-
-### Bar chart specific descriptions
-
-desc.barchart.sorting = Die Balken sind {0} und von links nach rechts folgendermaßen beschriftet:
-
-# {0}: sorting criterium, {1}: ascending/descending, {2}: sorted
-desc.barchart.sorting.sortingstring = {0} {1} {2}
-
-desc.barchart.sorting.sorted = sortiert
-desc.barchart.sorting.notsorted = nicht sortiert
-
-desc.barchart.sorting.MaxFirstDataSet = nach der ersten Datenreihe
-desc.barchart.sorting.Alphabetical = nach ihrem Titel alphabetisch
-desc.barchart.sorting.CategorialSum = nach der Summe jeder Kategorie
-
-desc.barchart.sorting.asc = aufsteigend
-desc.barchart.sorting.desc = absteigend
-
-### Trendline descriptions
-
-#{0}: desc.trendline_points_[1|n] {1}: desc.trendline_algorithm.<name> {2} TrendlineAlgorithm.getAlgorithmParams()
-desc.trendline_points = Zu {0} wird eine Trendlinie, die mit dem Verfahren "{1}" {2} berechnet wurde, dargestellt.
-desc.trendline_points_1 = der Datenreihe
-desc.trendline_points_n = jeder Datenreihe
-
-desc.trendline_algorithm.BrownLES = lineare exponentielle Glättung nach Brown
-desc.trendline_algorithm.MovingAverage = gleitendes Mittel
-desc.trendline_algorithm.LinearRegression = lineare Regression
-desc.trendline_algorithm.ExponentialSmoothing = exponentielle Glättung
-
-desc.trendline_only_1 = Bei der dargestellten Linien handelt es sich um eine Trendlinie, die mit dem Verfahren {0} {1} berechnet wurde. Sie trägt den Titel "{2}".
-desc.trendline_only_n = Bei den dargestellten Linien handelt es sich um Trendlinien, die mit dem Verfahren "{0}" {1} berechnet wurden. Es werden {2} Datenreihen dargestellt:
diff --git a/src/test/java/de/tudresden/inf/mci/brailleplot/rendering/FunctionalRasterizerTest.java b/src/test/java/de/tudresden/inf/mci/brailleplot/rendering/FunctionalRasterizerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..61c0cc354167439d77fc7c6e9c4fdc68432794c6
--- /dev/null
+++ b/src/test/java/de/tudresden/inf/mci/brailleplot/rendering/FunctionalRasterizerTest.java
@@ -0,0 +1,56 @@
+package de.tudresden.inf.mci.brailleplot.rendering;
+
+import de.tudresden.inf.mci.brailleplot.configparser.ConfigurationParser;
+import de.tudresden.inf.mci.brailleplot.configparser.Format;
+import de.tudresden.inf.mci.brailleplot.configparser.JavaPropertiesConfigurationParser;
+import de.tudresden.inf.mci.brailleplot.configparser.Printer;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+
+
+public class FunctionalRasterizerTest {
+
+    public static final String mDefaultConfig = getResource("config/rasterizer_test_default.properties").getAbsolutePath();
+    public static final String mBaseConfig = getResource("config/base_format.properties").getAbsolutePath();
+    public static Printer mPrinter;
+    public static Format mFormat;
+
+    public static File getResource(String fileName) {
+        ClassLoader classLoader = ClassLoader.getSystemClassLoader();
+        File resourceFile = new File(classLoader.getResource(fileName).getFile());
+        return resourceFile;
+    }
+
+    @BeforeAll
+    public static void initialize() {
+        Assertions.assertDoesNotThrow(
+                () -> {
+                    ConfigurationParser parser = new JavaPropertiesConfigurationParser(mBaseConfig, mDefaultConfig);
+                    mPrinter = parser.getPrinter();
+                    mFormat = parser.getFormat("test");
+                }
+        );
+    }
+
+    // Invalid use test cases.
+
+    @Test
+    public void testInvalidDirectCall() {
+        // Create FunctionalRasterizer for BrailleText
+        FunctionalRasterizer<BrailleText> textRasterizer = new FunctionalRasterizer<>(BrailleText.class, (data, canvas) -> {
+            // dummy
+        });
+
+        // The FunctionalRasterizer should not be called directly, it is meant to be called by its RenderingBase
+        // which decides which rasterizer to use based on the Renderable type.
+        // Directly passing the wrong Renderable type must cause exception:
+        Assertions.assertThrows(IllegalArgumentException.class, () -> {
+            RasterCanvas testCanvas = new SixDotBrailleRasterCanvas(mPrinter, mFormat);
+            // Pass Image to BrailleText rasterizer.
+            textRasterizer.rasterize(new Image(getResource("examples/img/dummy.bmp")), testCanvas);
+        });
+    }
+}
diff --git a/src/test/java/de/tudresden/inf/mci/brailleplot/rendering/MasterRendererTest.java b/src/test/java/de/tudresden/inf/mci/brailleplot/rendering/MasterRendererTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..5bf15320f9afa71d734b6e80f26de37e400efe7c
--- /dev/null
+++ b/src/test/java/de/tudresden/inf/mci/brailleplot/rendering/MasterRendererTest.java
@@ -0,0 +1,98 @@
+package de.tudresden.inf.mci.brailleplot.rendering;
+
+import de.tudresden.inf.mci.brailleplot.configparser.ConfigurationParser;
+import de.tudresden.inf.mci.brailleplot.configparser.Format;
+import de.tudresden.inf.mci.brailleplot.configparser.JavaPropertiesConfigurationParser;
+import de.tudresden.inf.mci.brailleplot.configparser.Printer;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+
+
+public class MasterRendererTest {
+
+    public static final String mDefaultConfig = getResource("config/rasterizer_test_default.properties").getAbsolutePath();
+    public static final String mBaseConfig = getResource("config/base_format.properties").getAbsolutePath();
+    public static Printer mPrinter;
+    public static Format mFormat;
+
+    public static File getResource(String fileName) {
+        ClassLoader classLoader = ClassLoader.getSystemClassLoader();
+        File resourceFile = new File(classLoader.getResource(fileName).getFile());
+        return resourceFile;
+    }
+
+    @BeforeAll
+    public static void initialize() {
+        Assertions.assertDoesNotThrow(
+                () -> {
+                    ConfigurationParser parser = new JavaPropertiesConfigurationParser(mBaseConfig, mDefaultConfig);
+                    mPrinter = parser.getPrinter();
+                    mFormat = parser.getFormat("test");
+                }
+        );
+    }
+
+    // Valid use test cases.
+
+    @Test
+    public void testRasterizerSelection() {
+        Assertions.assertDoesNotThrow(
+                () -> {
+                    // Create own rendering base
+                    FunctionalRenderingBase renderingBase = new FunctionalRenderingBase();
+
+                    // Register two different rasterizers for two different types.
+                    // The rasterizers are later distinguished by the number of pages they generate.
+                    FunctionalRasterizer<BrailleText> rasterizerRef1 = new FunctionalRasterizer<>(BrailleText.class, (data, canvas) -> {
+                        for (int i = 0; i < 1; i++) {
+                            canvas.getNewPage();
+                        }
+                    });
+                    FunctionalRasterizer<Image> rasterizerRef2 = new FunctionalRasterizer<>(Image.class, (data, canvas) -> {
+                        for (int i = 0; i < 2; i++) {
+                            canvas.getNewPage();
+                        }
+                    });
+                    renderingBase.registerRasterizer(rasterizerRef1);
+                    renderingBase.registerRasterizer(rasterizerRef2);
+
+                    // create renderer from rendering base
+                    MasterRenderer renderer = new MasterRenderer(mPrinter, mFormat, renderingBase);
+
+                    // Test rasterizer selection
+                    RasterCanvas result;
+
+                    result= renderer.rasterize(new BrailleText("dummy text", new Rectangle(0,0,1,1)));
+                    Assertions.assertEquals(1, result.getPageCount());
+
+                    result = renderer.rasterize(new Image(getResource("examples/img/dummy.bmp")));
+                    Assertions.assertEquals(2, result.getPageCount());
+
+                    // Test replacement of rasterizer
+                    FunctionalRasterizer<Image> rasterizerRef3 = new FunctionalRasterizer<>(Image.class, (data, canvas) -> {
+                        for (int i = 0; i < 3; i++) {
+                            canvas.getNewPage();
+                        }
+                    });
+                    renderingBase.registerRasterizer(rasterizerRef3);
+
+                    result = renderer.rasterize(new Image(getResource("examples/img/dummy.bmp")));
+                    Assertions.assertEquals(3, result.getPageCount());
+
+                }
+        );
+    }
+
+    // Invalid use test cases.
+
+    @Test
+    public void testRasterizerNotAvailable() {
+        // Create MasterRenderer with empty rendering base.
+        MasterRenderer empty = new MasterRenderer(mPrinter, mFormat, new FunctionalRenderingBase());
+
+        Assertions.assertThrows(IllegalArgumentException.class, () -> empty.rasterize(new Image(getResource("examples/img/dummy.bmp"))));
+    }
+}
diff --git a/src/test/java/de/tudresden/inf/mci/brailleplot/rendering/RasterCanvasTest.java b/src/test/java/de/tudresden/inf/mci/brailleplot/rendering/RasterCanvasTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..17d31b7376d62e331c9fa6042f7f57b6ddf0320c
--- /dev/null
+++ b/src/test/java/de/tudresden/inf/mci/brailleplot/rendering/RasterCanvasTest.java
@@ -0,0 +1,189 @@
+package de.tudresden.inf.mci.brailleplot.rendering;
+
+import de.tudresden.inf.mci.brailleplot.configparser.ConfigurationParser;
+import de.tudresden.inf.mci.brailleplot.configparser.JavaPropertiesConfigurationParser;
+import de.tudresden.inf.mci.brailleplot.printabledata.MatrixData;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+import java.util.ListIterator;
+
+
+public class RasterCanvasTest {
+
+    public static final String mDefaultConfig = getResource("config/rasterizer_test_default.properties").getAbsolutePath();
+    public static final String mBaseConfig = getResource("config/base_format.properties").getAbsolutePath();
+    public static final String mMarginsOnlyConfig = getResource("config/margins_only.properties").getAbsolutePath();
+    public static final String mConstraintOnlyConfig = getResource("config/constraint_only.properties").getAbsolutePath();
+    public static final String mBothConfig = getResource("config/margins_and_constraint.properties").getAbsolutePath();
+
+    public static File getResource(String fileName) {
+        ClassLoader classLoader = ClassLoader.getSystemClassLoader();
+        File resourceFile = new File(classLoader.getResource(fileName).getFile());
+        return resourceFile;
+    }
+
+    @Test
+    public void testBaseFormat() {
+        Assertions.assertDoesNotThrow(
+                () -> {
+                    ConfigurationParser parser = new JavaPropertiesConfigurationParser(mBaseConfig, mDefaultConfig);
+                    RasterCanvas canvas = new SixDotBrailleRasterCanvas(parser.getPrinter(), parser.getFormat("test"));
+
+                    // pre-calculated and measured correct values:
+                    int x = 0;
+                    int y = 0;
+                    int w = 35;
+                    int h = 30;
+                    double printW = (w * (2.5 + 3.5)) - 3.5; // 206.5 mm
+                    double printH = (h * (2 * 2.5 + 5.0)) - 5.0; // 295.0 mm
+
+                    // Test the calculated raster against the pre-calculated values
+                    Rectangle raster = canvas.getCellRectangle();
+                    Assertions.assertEquals(x, raster.getX());
+                    Assertions.assertEquals(y, raster.getY());
+                    Assertions.assertEquals(w, raster.getWidth());
+                    Assertions.assertEquals(h, raster.getHeight());
+                    Assertions.assertEquals(x, raster.intWrapper().getX());
+                    Assertions.assertEquals(y, raster.intWrapper().getY());
+                    Assertions.assertEquals(w, raster.intWrapper().getWidth());
+                    Assertions.assertEquals(h, raster.intWrapper().getHeight());
+                    Assertions.assertEquals(printW, canvas.getPrintableWidth());
+                    Assertions.assertEquals(printH, canvas.getPrintableHeight());
+
+                    // Test quantification (by equivalence class partitioning testing)
+                    Assertions.assertEquals(0, canvas.quantifyX(-5));
+                    Assertions.assertEquals(0, canvas.quantifyX(0));
+                    Assertions.assertEquals(9, canvas.quantifyX(28.25));
+                    Assertions.assertEquals(10, canvas.quantifyX(28.26));
+                    Assertions.assertEquals(69, canvas.quantifyX(206.5));
+                    Assertions.assertEquals(69, canvas.quantifyX(250));
+
+                    Assertions.assertEquals(0, canvas.quantifyY(-5));
+                    Assertions.assertEquals(0, canvas.quantifyY(0));
+                    Assertions.assertEquals(15, canvas.quantifyY(51.25));
+                    Assertions.assertEquals(16, canvas.quantifyY(51.26));
+                    Assertions.assertEquals(89, canvas.quantifyY(295.0));
+                    Assertions.assertEquals(89, canvas.quantifyY(350.0));
+                }
+        );
+    }
+
+    @Test
+    public void testMarginsOnly() {
+        Assertions.assertDoesNotThrow(
+                () -> {
+                    ConfigurationParser parser = new JavaPropertiesConfigurationParser(mMarginsOnlyConfig, mDefaultConfig);
+                    RasterCanvas canvas = new SixDotBrailleRasterCanvas(parser.getPrinter(), parser.getFormat("test"));
+
+                    // pre-calculated and measured correct values:
+                    // 6 mm left margin -> 1 cell border
+                    // 12 mm top margin -> ~ 1.2 cell sizes -> 2 cell border
+                    // 30 mm bottom margin -> 3 cell border
+                    int x = 1;
+                    int y = 2;
+                    int w = 34; // 35 - 1
+                    int h = 25; // 30 - 2 - 3
+                    double printW = (w * (2.5 + 3.5)) - 3.5; // 200.5 mm
+                    double printH = (h * (2 * 2.5 + 5.0)) - 5.0; // 245.0 mm
+
+                    // Test the calculated raster against the pre-calculated values
+                    Rectangle raster = canvas.getCellRectangle();
+                    Assertions.assertEquals(x, raster.getX());
+                    Assertions.assertEquals(y, raster.getY());
+                    Assertions.assertEquals(w, raster.getWidth());
+                    Assertions.assertEquals(h, raster.getHeight());
+                    Assertions.assertEquals(x, raster.intWrapper().getX());
+                    Assertions.assertEquals(y, raster.intWrapper().getY());
+                    Assertions.assertEquals(w, raster.intWrapper().getWidth());
+                    Assertions.assertEquals(h, raster.intWrapper().getHeight());
+                    Assertions.assertEquals(printW, canvas.getPrintableWidth());
+                    Assertions.assertEquals(printH, canvas.getPrintableHeight());
+                }
+        );
+    }
+
+    @Test
+    public void testConstraintOnly() {
+        Assertions.assertDoesNotThrow(
+                () -> {
+                    ConfigurationParser parser = new JavaPropertiesConfigurationParser(mConstraintOnlyConfig, mDefaultConfig);
+                    RasterCanvas canvas = new SixDotBrailleRasterCanvas(parser.getPrinter(), parser.getFormat("test"));
+
+                    // pre-calculated and measured correct values:
+                    // width-constraint: 190.0 mm -> fits 32 cells h.
+                    // height-constraint: 250.0 mm -> fits 25 cells v. (-1 because top constraint of 1 cell) -> 24 cells v.
+                    int x = 0; // zero because constraint
+                    int y = 0; // moves reference point.
+                    int w = 30; // because raster.constraint.width = 30 < 32 (will pick minimum)
+                    int h = 24; // because 25 < raster.constraint.height = 28
+                    double printW = (w * (2.5 + 3.5)) - 3.5; // 176.5 mm
+                    double printH = (h * (2 * 2.5 + 5.0)) - 5.0; // 245.0 mm
+
+                    // Test the calculated raster against the pre-calculated values
+                    Rectangle raster = canvas.getCellRectangle();
+                    Assertions.assertEquals(x, raster.getX());
+                    Assertions.assertEquals(y, raster.getY());
+                    Assertions.assertEquals(w, raster.getWidth());
+                    Assertions.assertEquals(h, raster.getHeight());
+                    Assertions.assertEquals(x, raster.intWrapper().getX());
+                    Assertions.assertEquals(y, raster.intWrapper().getY());
+                    Assertions.assertEquals(w, raster.intWrapper().getWidth());
+                    Assertions.assertEquals(h, raster.intWrapper().getHeight());
+                    Assertions.assertEquals(printW, canvas.getPrintableWidth());
+                    Assertions.assertEquals(printH, canvas.getPrintableHeight());
+                }
+        );
+    }
+
+    @Test
+    public void testBoth() {
+        Assertions.assertDoesNotThrow(
+                () -> {
+                    ConfigurationParser parser = new JavaPropertiesConfigurationParser(mBothConfig, mDefaultConfig);
+                    RasterCanvas canvas = new SixDotBrailleRasterCanvas(parser.getPrinter(), parser.getFormat("test"));
+
+                    // pre-calculated and measured correct values:
+                    // width-constraint: 190.0 mm -> fits 32 cells h.
+                    // height-constraint: 250.0 mm -> fits 25 cells v.
+                    int x = 0;
+                    int y = 1;
+                    int w = 30;
+                    int h = 24;
+                    double printW = (w * (2.5 + 3.5)) - 3.5; // 176.5 mm
+                    double printH = (h * (2 * 2.5 + 5.0)) - 5.0; // 235.0 mm
+
+                    // Test the calculated raster against the pre-calculated values
+                    Rectangle raster = canvas.getCellRectangle();
+                    Assertions.assertEquals(x, raster.getX());
+                    Assertions.assertEquals(y, raster.getY());
+                    Assertions.assertEquals(w, raster.getWidth());
+                    Assertions.assertEquals(h, raster.getHeight());
+                    Assertions.assertEquals(x, raster.intWrapper().getX());
+                    Assertions.assertEquals(y, raster.intWrapper().getY());
+                    Assertions.assertEquals(w, raster.intWrapper().getWidth());
+                    Assertions.assertEquals(h, raster.intWrapper().getHeight());
+                    Assertions.assertEquals(printW, canvas.getPrintableWidth());
+                    Assertions.assertEquals(printH, canvas.getPrintableHeight());
+                }
+        );
+    }
+
+    @Test @SuppressWarnings("unchecked")
+    // RasterCanvas is guaranteed to create MatrixData instances for its pages
+    public void testPageIterator() {
+        Assertions.assertDoesNotThrow(
+                () -> {
+                    ConfigurationParser parser = new JavaPropertiesConfigurationParser(mBaseConfig, mDefaultConfig);
+                    MasterRenderer renderer = new MasterRenderer(parser.getPrinter(), parser.getFormat("test"));
+                    RasterCanvas result = renderer.rasterize(new Image(getResource("examples/img/dummy.bmp")));
+                    ListIterator iter = result.getPageIterator();
+                    while (iter.hasNext()) {
+                        MatrixData<Boolean> page = (MatrixData<Boolean>) iter.next();
+                        Assertions.assertNotNull(page);
+                    }
+                }
+        );
+    }
+}
diff --git a/src/test/java/de/tudresden/inf/mci/brailleplot/rendering/RectangleTest.java b/src/test/java/de/tudresden/inf/mci/brailleplot/rendering/RectangleTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..126a146a38b45c8728986f5f325aca32530d0240
--- /dev/null
+++ b/src/test/java/de/tudresden/inf/mci/brailleplot/rendering/RectangleTest.java
@@ -0,0 +1,142 @@
+package de.tudresden.inf.mci.brailleplot.rendering;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+
+public class RectangleTest {
+
+    Rectangle rect1 = new Rectangle(4.5, 2.75, 8.3, 16);
+    Rectangle rect2 = new Rectangle(8.5, 6.75, 8.3, 16);
+    Rectangle rect3 = new Rectangle(0, 0, 3, 2);
+
+    @Test
+    public void testDimensions() {
+        Assertions.assertEquals(4.5, rect1.getX());
+        Assertions.assertEquals(2.75, rect1.getY());
+        Assertions.assertEquals(8.3, rect1.getWidth());
+        Assertions.assertEquals(16, rect1.getHeight());
+        Assertions.assertEquals(12.8, rect1.getRight());
+        Assertions.assertEquals(18.75, rect1.getBottom());
+    }
+
+    @Test
+    public void testIntegerCoordinates() {
+        Rectangle.IntWrapper intRect;
+
+        // with rational original coordinates (get rounded)
+        intRect = rect1.intWrapper();
+        Assertions.assertEquals(5, intRect.getX());
+        Assertions.assertEquals(3, intRect.getY());
+        Assertions.assertEquals(8, intRect.getWidth());
+        Assertions.assertEquals(16, intRect.getHeight());
+        Assertions.assertEquals(12, intRect.getRight());
+        Assertions.assertEquals(18, intRect.getBottom());
+
+        // with natural original coordinates
+        intRect = rect3.intWrapper();
+        Assertions.assertEquals(0, intRect.getX());
+        Assertions.assertEquals(0, intRect.getY());
+        Assertions.assertEquals(3, intRect.getWidth());
+        Assertions.assertEquals(2, intRect.getHeight());
+        Assertions.assertEquals(2, intRect.getRight());
+        Assertions.assertEquals(1, intRect.getBottom());
+    }
+
+    @Test
+    public void testScaling() {
+        Rectangle scaledRect = rect1.scaledBy(5.5, 0.4);
+        Assertions.assertEquals(4.5 * 5.5, scaledRect.getX());
+        Assertions.assertEquals(2.75 * 0.4, scaledRect.getY());
+        Assertions.assertEquals(8.3 * 5.5, scaledRect.getWidth());
+        Assertions.assertEquals(16 * 0.4, scaledRect.getHeight());
+        Assertions.assertEquals(12.8 * 5.5, scaledRect.getRight());
+        Assertions.assertEquals(18.75 * 0.4, scaledRect.getBottom());
+    }
+
+    @Test
+    public void testIntersecting() {
+        Rectangle itsct;
+
+        // Non-empty intersection
+        itsct = rect1.intersectedWith(rect2);
+        Assertions.assertEquals(8.5, itsct.getX());
+        Assertions.assertEquals(6.75, itsct.getY());
+        Assertions.assertEquals(12.8 - 8.5, itsct.getWidth());
+        Assertions.assertEquals(18.75 - 6.75, itsct.getHeight());
+        Assertions.assertEquals(12.8, itsct.getRight());
+        Assertions.assertEquals(18.75, itsct.getBottom());
+
+        // Swapping order of rectangles gives equivalent intersection
+        itsct = rect2.intersectedWith(rect1);
+        Assertions.assertEquals(8.5, itsct.getX());
+        Assertions.assertEquals(6.75, itsct.getY());
+        Assertions.assertEquals(12.8 - 8.5, itsct.getWidth());
+        Assertions.assertEquals(18.75 - 6.75, itsct.getHeight());
+        Assertions.assertEquals(12.8, itsct.getRight());
+        Assertions.assertEquals(18.75, itsct.getBottom());
+
+        // Empty intersection
+        itsct = rect1.intersectedWith(rect3);
+        Assertions.assertEquals(4.5, itsct.getX());
+        Assertions.assertEquals(2.75, itsct.getY());
+        Assertions.assertEquals(0, itsct.getWidth());
+        Assertions.assertEquals(0, itsct.getHeight());
+        Assertions.assertEquals(4.5, itsct.getRight());
+        Assertions.assertEquals(2.75, itsct.getBottom());
+    }
+
+    @Test
+    public void testCropping() {
+        Assertions.assertDoesNotThrow(() -> {
+            Rectangle copy, crop;
+
+            // Create copy by selecting whole width / height (should not throw)
+            copy = rect1.fromTop(16);
+            copy = rect1.fromBottom(16);
+            copy = rect1.fromLeft(8.3);
+            copy = rect1.fromBottom(8.3);
+
+            // Create copy with copy constructor
+            copy = new Rectangle(rect1);
+
+            crop = copy.removeFromTop(1);
+            Assertions.assertEquals(2.75, crop.getY());
+            Assertions.assertEquals(1, crop.getHeight());
+
+            crop = copy.removeFromLeft(2);
+            Assertions.assertEquals(4.5, crop.getX());
+            Assertions.assertEquals(2, crop.getWidth());
+
+            crop = copy.removeFromBottom(3);
+            Assertions.assertEquals(18.75, crop.getBottom());
+            Assertions.assertEquals(3, crop.getHeight());
+
+            crop = copy.removeFromRight(4);
+            Assertions.assertEquals(12.8, crop.getRight());
+            Assertions.assertEquals(4, crop.getWidth());
+
+            Assertions.assertEquals(4.5 + 2, copy.getX());
+            Assertions.assertEquals(2.75 + 1, copy.getY());
+            Assertions.assertEquals(8.3 - 2 - 4, copy.getWidth());
+            Assertions.assertEquals(16 - 1 - 3, copy.getHeight());
+            Assertions.assertEquals(12.8 - 4, copy.getRight());
+            Assertions.assertEquals(18.75 - 3, copy.getBottom());
+
+        });
+    }
+
+    @Test
+    public void testOutOfSpace() {
+        Assertions.assertThrows(Rectangle.OutOfSpaceException.class, () -> {
+            Rectangle copy, crop;
+
+            copy = new Rectangle(rect1);
+
+            // selecting more space than available
+            crop = copy.fromTop(16.01);
+
+        });
+    }
+
+}
diff --git a/src/test/resources/concrete.properties b/src/test/resources/concrete.properties
deleted file mode 100644
index 78a56cbb7497b0fbefd5d685d9b822a519286469..0000000000000000000000000000000000000000
--- a/src/test/resources/concrete.properties
+++ /dev/null
@@ -1,50 +0,0 @@
-# JProperties Printer & Format Configuration
-#
-# Embosser: Dummy Printer
-# Test Revision (19-07-18) (DON'T USE FOR PRINTING)
-#
-# Description:
-# This is the main configuration file for use with the braille plot application
-# when embossing with the 'Index Everest-D V4'.
-# The configuration specifies the general printer abilities and defines
-# pre-selectable formats for this embosser.
-#
-# https://gitlab.hrz.tu-chemnitz.de/s9444737--tu-dresden.de/brailleplot/wikis/Software%20Design#configuration-files
-# =============================================================================
-
-### General Printer Properties
-### ==========================
-
-printer.name=Dummy Printer
-printer.mode=normal
-printer.brailletable=src/test/resources/mapping.properties
-printer.floatingDot.support=true
-printer.floatingDot.resolution=0.05
-
-# The following values represent the fixed indentation and maximum technical printing area of the embosser.
-# If the outputs don't fit on the page you might want to tweak this values. (Check the format too.)
-printer.constraint.top=5.0
-printer.constraint.left=0
-
-# The following properties define the exact grid spacing.
-printer.raster.cellDistance.horizontal=3.6
-printer.raster.cellDistance.vertical=4.8
-printer.raster.dotDistance.horizontal=2.5
-printer.raster.dotDistance.vertical=2.5
-printer.raster.dotDiameter=1.5
-
-### Format Definitions
-### ==================
-
-# A4 Format
-format.A4.page.width=210
-format.A4.page.height=297
-format.A4.margin.left=0
-
-# A5 Format
-format.A5.page.width=148
-format.A5.page.height=210
-format.A5.margin.top=0
-format.A5.margin.left=0
-format.A5.margin.bottom=0
-format.A5.margin.right=0
\ No newline at end of file
diff --git a/src/test/resources/default.properties b/src/test/resources/default.properties
deleted file mode 100644
index 9642c43bd28cdc162d3a668e69cab4c0114002d1..0000000000000000000000000000000000000000
--- a/src/test/resources/default.properties
+++ /dev/null
@@ -1,55 +0,0 @@
-# JProperties Printer & Format Configuration
-#
-# Embosser: Dummy Default
-# Test Revision (19-07-18) (DON'T USE FOR PRINTING)
-#
-# Description:
-# This is the default configuration file for the braille plot application.
-# The configuration specifies the default values of required properties.
-#
-# https://gitlab.hrz.tu-chemnitz.de/s9444737--tu-dresden.de/brailleplot/wikis/Software%20Design#configuration-files
-# =============================================================================
-
-# ATTENTION:    Changes to this configuration will affect settings for ALL printer and format definitions which
-#               are not overriding the defaults.
-
-printer.mode=normal
-printer.brailletable=src/test/resources/mapping.properties
-printer.floatingDot.support=false
-
-# The following values represent the fixed indentation and maximum technical printing area of the embosser.
-# If the outputs don't fit on the page you might want to tweak this values. (Check the format too.)
-printer.constraint.top=0
-printer.constraint.left=0
-# The second constraint in the printer.raster namespace helps to limit the available printing area in steps of
-# whole cells, for example if the printer enforces a maximum char per line limit or borders are activated.
-printer.raster.constraint.top=0
-printer.raster.constraint.left=0
-printer.raster.constraint.width=200
-printer.raster.constraint.height=300
-
-# Overall grid layout / type
-printer.raster.type=6-dot
-
-# The following properties define the exact grid spacing. Standard values based on the
-# 'Marburg Medium' publication standard as described in the FFI braille technical guideline:
-# https://www.ffi.de/assets/Uploads/Technische-Richtlinie-Blindenschrift.pdf
-# See also: # https://codes.iccsafe.org/content/ICCA117_12003/chapter-7-communication-elements-and-features#ICCA117.1_2003_Ch07_Sec703
-printer.raster.cellDistance.horizontal=3.5
-printer.raster.cellDistance.vertical=5.0
-printer.raster.dotDistance.horizontal=2.5
-printer.raster.dotDistance.vertical=2.5
-printer.raster.dotDiameter=1.5
-
-
-### Format Definitions
-### ==================
-
-# Default Format Definition
-format.default.page.height=297
-format.default.margin.top=10
-format.default.margin.left=10
-format.default.margin.bottom=10
-format.default.margin.right=10
-
-# This is a template. Do not define concrete formats in this file. Use the specific user config file for this purpose.
\ No newline at end of file
diff --git a/src/test/resources/illegalPropertyNameExample.properties b/src/test/resources/illegalPropertyNameExample.properties
deleted file mode 100644
index fcceefffc126943232cc159f4aa9d24f24b11f44..0000000000000000000000000000000000000000
--- a/src/test/resources/illegalPropertyNameExample.properties
+++ /dev/null
@@ -1,59 +0,0 @@
-# JProperties Printer & Format Configuration
-#
-# Embosser: Dummy Default with illegal property mName
-# Test Revision (19-07-18) (DON'T USE FOR PRINTING)
-#
-# Description:
-# This is the default configuration file for the braille plot application.
-# The configuration specifies the default values of required properties.
-#
-# https://gitlab.hrz.tu-chemnitz.de/s9444737--tu-dresden.de/brailleplot/wikis/Software%20Design#configuration-files
-# =============================================================================
-
-# ATTENTION:    Changes to this configuration will affect settings for ALL printer and format definitions which
-#               are not overriding the defaults.
-
-printer.mode=normal
-printer.brailletable=src/test/resources/mapping.properties
-printer.floatingDot.support=false
-
-# Illegal property mName example
-printer.garbageProperty=illegal
-
-# The following values represent the fixed indentation and maximum technical printing area of the embosser.
-# If the outputs don't fit on the page you might want to tweak this values. (Check the format too.)
-printer.constraint.top=0
-printer.constraint.left=0
-# The second constraint in the printer.raster namespace helps to limit the available printing area in steps of
-# whole cells, for example if the printer enforces a maximum char per line limit or borders are activated.
-printer.raster.constraint.top=0
-printer.raster.constraint.left=0
-printer.raster.constraint.width=200
-printer.raster.constraint.height=300
-
-# Overall grid layout / type
-printer.raster.type=6-dot
-
-# The following properties define the exact grid spacing. Standard values based on the
-# 'Marburg Medium' publication standard as described in the FFI braille technical guideline:
-# https://www.ffi.de/assets/Uploads/Technische-Richtlinie-Blindenschrift.pdf
-# See also: # https://codes.iccsafe.org/content/ICCA117_12003/chapter-7-communication-elements-and-features#ICCA117.1_2003_Ch07_Sec703
-printer.raster.cellDistance.horizontal=3.5
-printer.raster.cellDistance.vertical=5.0
-printer.raster.dotDistance.horizontal=2.5
-printer.raster.dotDistance.vertical=2.5
-printer.raster.dotDiameter=1.5
-
-
-### Format Definitions
-### ==================
-
-# Default Format Definition (assume A4 portrait)
-format.default.page.width=210
-format.default.page.height=297
-format.default.margin.top=10
-format.default.margin.left=10
-format.default.margin.bottom=10
-format.default.margin.right=10
-
-# This is a template. Do not define concrete formats in this file. Use the specific user config file for this purpose.
\ No newline at end of file
diff --git a/src/test/resources/illegalPropertyValueExample.properties b/src/test/resources/illegalPropertyValueExample.properties
deleted file mode 100644
index 9e6f8f2eb6156cffbeb4c126853c390973d437d2..0000000000000000000000000000000000000000
--- a/src/test/resources/illegalPropertyValueExample.properties
+++ /dev/null
@@ -1,59 +0,0 @@
-# JProperties Printer & Format Configuration
-#
-# Embosser: Dummy Default with illegal property value
-# Test Revision (19-07-18) (DON'T USE FOR PRINTING)
-#
-# Description:
-# This is the default configuration file for the braille plot application.
-# The configuration specifies the default values of required properties.
-#
-# https://gitlab.hrz.tu-chemnitz.de/s9444737--tu-dresden.de/brailleplot/wikis/Software%20Design#configuration-files
-# =============================================================================
-
-# ATTENTION:    Changes to this configuration will affect settings for ALL printer and format definitions which
-#               are not overriding the defaults.
-
-printer.mode=normal
-printer.brailletable=src/test/resources/mapping.properties
-printer.floatingDot.support=false
-
-# The following values represent the fixed indentation and maximum technical printing area of the embosser.
-# If the outputs don't fit on the page you might want to tweak this values. (Check the format too.)
-printer.constraint.top=0
-printer.constraint.left=0
-# The second constraint in the printer.raster namespace helps to limit the available printing area in steps of
-# whole cells, for example if the printer enforces a maximum char per line limit or borders are activated.
-printer.raster.constraint.top=0
-printer.raster.constraint.left=0
-printer.raster.constraint.width=200
-printer.raster.constraint.height=300
-
-# Overall grid layout / type
-printer.raster.type=6-dot
-
-# The following properties define the exact grid spacing. Standard values based on the
-# 'Marburg Medium' publication standard as described in the FFI braille technical guideline:
-# https://www.ffi.de/assets/Uploads/Technische-Richtlinie-Blindenschrift.pdf
-# See also: # https://codes.iccsafe.org/content/ICCA117_12003/chapter-7-communication-elements-and-features#ICCA117.1_2003_Ch07_Sec703
-printer.raster.cellDistance.horizontal=3.5
-printer.raster.cellDistance.vertical=5.0
-printer.raster.dotDistance.horizontal=2.5
-printer.raster.dotDistance.vertical=2.5
-printer.raster.dotDiameter=1.5
-
-
-### Format Definitions
-### ==================
-
-# Default Format Definition (assume A4 portrait)
-format.default.page.width=210
-
-# Illegal property value example
-format.default.page.height=two hundred ninety seven
-
-format.default.margin.top=10
-format.default.margin.left=10
-format.default.margin.bottom=10
-format.default.margin.right=10
-
-# This is a template. Do not define concrete formats in this file. Use the specific user config file for this purpose.
\ No newline at end of file
diff --git a/src/test/resources/mapping.properties b/src/test/resources/mapping.properties
deleted file mode 100644
index 8251bb944bf5666048c401e4679c75d3aa990fc3..0000000000000000000000000000000000000000
--- a/src/test/resources/mapping.properties
+++ /dev/null
@@ -1,146 +0,0 @@
-# JProperties Mapping BrailleTable
-#
-# Table: de-chardefs6
-# Version 1 Rev. 2 (19-07-11)
-#
-# Description:
-# This table contains a mapping from 6-bit-strings to decimal ascii byte values.
-# It is used by the printer backend to encode data sent to the embosser.
-# The pairs are ordered by ascending ascii byte value.
-#
-# =============================================================================
-
-# 0-31:NUL-US (non visible characters)
-# Space
-000000=32
-# !
-000010=33
-# "
-000100=34
-# #
-001111=35
-# $
-000101=36
-# %
-111111=37
-# &
-111101=38
-# '
-000001=39
-# (
-011001=40
-# )
-001011=41
-# *
-001010=42
-# +
-011010=43
-# ,
-010000=44
-# -
-001001=45
-# .
-001000=46
-# /
-010011=47
-# 0
-001101=48
-# 1
-100001=49
-# 2
-110001=50
-# 3
-100101=51
-# 4
-100111=52
-# 5
-100011=53
-# 6
-110101=54
-# 7
-110111=55
-# 8
-110011=56
-# 9
-010101=57
-# :
-010010=58
-# ;
-011000=59
-# <
-000011=60
-# =
-011011=61
-# >
-000110=62
-# ?
-010001=63
-# 64-90:@-Z
-# [
-111011=91
-# 92:\
-# 93:]
-# 94:^
-# _
-000111=95
-# `
-001110=96
-# a
-100000=97
-# b
-110000=98
-# c
-100100=99
-# d
-100110=100
-# e
-100010=101
-# f
-110100=102
-# g
-110110=103
-# h
-110010=104
-# i
-010100=105
-# j
-010110=106
-# k
-101000=107
-# l
-111000=108
-# m
-101100=109
-# n
-101110=110
-# o
-101010=111
-# mP
-111100=112
-# q
-111110=113
-# r
-111010=114
-# s
-011100=115
-# t
-011110=116
-# u
-101001=117
-# v
-111001=118
-# w
-010111=119
-# mX
-101101=120
-# mY
-101111=121
-# z
-101011=122
-# {
-011111=123
-# |
-001100=124
-# ~
-011101=126
-# 127:DEL
\ No newline at end of file
diff --git a/src/test/resources/missingRequiredPropertyExample.properties b/src/test/resources/missingRequiredPropertyExample.properties
deleted file mode 100644
index 1c453f75f7362d799ab06ffe7a1ff5dfbd6bd40d..0000000000000000000000000000000000000000
--- a/src/test/resources/missingRequiredPropertyExample.properties
+++ /dev/null
@@ -1,52 +0,0 @@
-# JProperties Printer & Format Configuration
-#
-# Embosser: Dummy Printer
-# Test Revision (19-07-18) (DON'T USE FOR PRINTING)
-#
-# Description:
-# This is the main configuration file for use with the braille plot application
-# when embossing with the 'Index Everest-D V4'.
-# The configuration specifies the general printer abilities and defines
-# pre-selectable formats for this embosser.
-#
-# https://gitlab.hrz.tu-chemnitz.de/s9444737--tu-dresden.de/brailleplot/wikis/Software%20Design#configuration-files
-# =============================================================================
-
-### General Printer Properties
-### ==========================
-
-# Missing required property (not specified by default):
-# printer.mName=Dummy Printer
-printer.mode=normal
-printer.brailletable=src/test/resources/mapping.properties
-printer.floatingDot.support=true
-printer.floatingDot.resolution=0.05
-
-# The following values represent the fixed indentation and maximum technical printing area of the embosser.
-# If the outputs don't fit on the page you might want to tweak this values. (Check the format too.)
-printer.constraint.top=5.0
-printer.constraint.left=0
-
-# The following properties define the exact grid spacing.
-printer.raster.cellDistance.horizontal=3.6
-printer.raster.cellDistance.vertical=4.8
-printer.raster.dotDistance.horizontal=2.5
-printer.raster.dotDistance.vertical=2.5
-printer.raster.dotDiameter=1.5
-
-### Format Definitions
-### ==================
-
-# A4 Format
-format.A4.page.width=210
-format.A4.page.height=297
-format.A4.margin.left=0
-
-# A5 Format
-# Missing required property (not specified by default):
-# format.A5.page.width=148
-format.A5.page.height=210
-format.A5.margin.top=0
-format.A5.margin.left=0
-format.A5.margin.bottom=0
-format.A5.margin.right=0
\ No newline at end of file