diff --git a/.gitignore b/.gitignore
index 9f25cc2301ab500393825b61c652ac5751c5dc3b..7cbe242d3e57f29e4c908eaef1e72c996c53f81a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,3 +22,8 @@ src/main/resources/mapping/liblouis
 
 # Exported SVG files
 *.svg
+
+# Native libraries
+*.so
+*.dylib
+*.dll
diff --git a/.gitmodules b/.gitmodules
index c5dd365bd9aec06336502855f51c391593fd54cd..6279666aaacb70d51061cd2eaf45864903353052 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,6 @@
 [submodule "third_party/liblouis"]
 	path = third_party/liblouis
 	url = https://github.com/liblouis/liblouis
+[submodule "third_party/liblouis_bin"]
+	path = third_party/liblouis_bin
+	url = ../../brailleplot/liblouis_bin.git
diff --git a/build.gradle b/build.gradle
index 59cf0b22950c45a026679cd9f96d0232bc297d16..d56141ec73bfe993b742785d3cf4376a3c4085f3 100644
--- a/build.gradle
+++ b/build.gradle
@@ -68,6 +68,23 @@ clean {
     delete "$rootDir/src/main/resources/mapping/liblouis"
 }
 
+// Copy native liblouis libraries for major platforms to the resource folder
+task copyLibLouisBinary(type: Copy) {
+    from "$projectDir/third_party/liblouis_bin/bin/"
+    into "$rootDir/src/main/resources/native/liblouis"
+}
+processResources.dependsOn copyLibLouisBinary
+// Abort if files are missing
+gradle.taskGraph.afterTask { copyLibLouisBinary ->
+    if(copyLibLouisBinary.state.noSource){
+        throw new GradleException("Could not find liblouis native libraries in \"$projectDir/third_party/liblouis_bin/bin/\". Please make sure you updated all git submodules using \"git submodule update --init --recursive\" ")
+    }
+}
+// Delete libraries on "clean" task
+clean {
+    delete "$rootDir/src/main/resources/native/liblouis"
+}
+
 // Define the main class for the application
 mainClassName = 'de.tudresden.inf.mci.brailleplot.App'
 
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/App.java b/src/main/java/de/tudresden/inf/mci/brailleplot/App.java
index 16b15f62e479ef629606156067266a1d5c410a34..78f51686d080f7b769c57e316d3f8a257db82340 100644
--- a/src/main/java/de/tudresden/inf/mci/brailleplot/App.java
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/App.java
@@ -12,6 +12,8 @@ import de.tudresden.inf.mci.brailleplot.csvparser.CsvParser;
 import de.tudresden.inf.mci.brailleplot.csvparser.CsvType;
 import de.tudresden.inf.mci.brailleplot.datacontainers.PointList;
 import de.tudresden.inf.mci.brailleplot.datacontainers.PointListContainer;
+import de.tudresden.inf.mci.brailleplot.datacontainers.SimpleCategoricalPointListContainerImpl;
+import de.tudresden.inf.mci.brailleplot.diagrams.CategoricalBarChart;
 import de.tudresden.inf.mci.brailleplot.diagrams.LineChart;
 
 
@@ -35,6 +37,7 @@ import de.tudresden.inf.mci.brailleplot.commandline.SettingType;
 import de.tudresden.inf.mci.brailleplot.commandline.SettingsReader;
 import de.tudresden.inf.mci.brailleplot.commandline.SettingsWriter;
 
+import de.tudresden.inf.mci.brailleplot.rendering.LiblouisBrailleTextRasterizer;
 import de.tudresden.inf.mci.brailleplot.rendering.MasterRenderer;
 
 import de.tudresden.inf.mci.brailleplot.svgexporter.BoolFloatingPointDataSvgExporter;
@@ -45,7 +48,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.BufferedReader;
-import java.io.File;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
@@ -53,7 +55,7 @@ import java.io.Reader;
 import java.util.Iterator;
 
 import java.net.URL;
-
+import java.nio.file.Path;
 import java.util.Optional;
 import java.util.concurrent.ConcurrentLinkedDeque;
 
@@ -158,8 +160,6 @@ public final class App {
         try {
             // Logging example
             mLogger.info("Application started");
-            // Needed for Windows machines
-            System.setProperty("jna.library.path", System.getProperty("user.dir") + "/third_party");
             // Parse command line parameters
             CommandLineParser cliParser = new CommandLineParser();
             SettingsWriter settings = cliParser.parse(args);
@@ -175,16 +175,17 @@ public final class App {
 
 
             // Config Parsing
-            URL configPath;
+            JavaPropertiesConfigurationParser configParser;
+            URL defaultConfig = getClass().getClassLoader().getResource("config/default.properties");
             if (!settingsReader.isPresent(SettingType.PRINTER_CONFIG_PATH)) { // TODO: exception if missing this argument, until then use default location for test runs
-                configPath = getClass().getResource("/config/index_everest_d_v4.properties");
+                URL configUrl = getClass().getResource("/config/index_everest_d_v4.properties");
+                configParser = new JavaPropertiesConfigurationParser(configUrl, defaultConfig);
                 mLogger.warn("ATTENTION! Using default specific config from resources. Please remove default config behavior before packaging the jar.");
             } else {
-                File configFile = new File(settingsReader.getSetting(SettingType.PRINTER_CONFIG_PATH).get());
-                configPath = configFile.toURL();
+                Path configPath = Path.of(settingsReader.getSetting(SettingType.PRINTER_CONFIG_PATH).get());
+                configParser = new JavaPropertiesConfigurationParser(configPath, defaultConfig);
             }
 
-            JavaPropertiesConfigurationParser configParser = new JavaPropertiesConfigurationParser(configPath, getClass().getClassLoader().getResource("config/default.properties"));
             Printer indexV4Printer = configParser.getPrinter();
             Format a4Format = configParser.getFormat("A4");
             Representation representationParameters = configParser.getRepresentation();
@@ -195,14 +196,24 @@ public final class App {
             InputStream csvStream = classloader.getResourceAsStream("examples/csv/2_line_plot.csv");
             Reader csvReader = new BufferedReader(new InputStreamReader(csvStream));
 
+
             CsvParser csvParser = new CsvParser(csvReader, ',', '\"');
             PointListContainer<PointList> container = csvParser.parse(CsvType.DOTS, CsvOrientation.HORIZONTAL);
             mLogger.debug("Internal data representation:\n {}", container.toString());
 
             LineChart lineChart = new LineChart(container);
-            lineChart.setTitle("Liniendiagramm");
-            lineChart.setXAxisName("X-Achsen Einheit");
-            lineChart.setYAxisName("Y-Achsen Einheit");
+            lineChart.setTitle(settingsReader.getSetting(SettingType.DIAGRAM_TITLE).orElse(""));
+            lineChart.setXAxisName(settingsReader.getSetting(SettingType.X_AXIS_LABEL).orElse(""));
+            lineChart.setYAxisName(settingsReader.getSetting(SettingType.Y_AXIS_LABEL).orElse(""));
+
+            /*
+            CategoricalBarChart barChart = new CategoricalBarChart(new SimpleCategoricalPointListContainerImpl(container));
+            barChart.setTitle(settingsReader.getSetting(SettingType.DIAGRAM_TITLE).orElse(""));
+            barChart.setXAxisName(settingsReader.getSetting(SettingType.X_AXIS_LABEL).orElse(""));
+            barChart.setYAxisName(settingsReader.getSetting(SettingType.Y_AXIS_LABEL).orElse(""));
+             */
+            LiblouisBrailleTextRasterizer.initModule();
+
             MasterRenderer renderer = new MasterRenderer(indexV4Printer, representationParameters, a4Format);
             RasterCanvas canvas = renderer.rasterize(lineChart);
             Iterator<MatrixData<Boolean>> iter = canvas.getPageIterator();
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/CommandLineParser.java b/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/CommandLineParser.java
index 81e77493f2815a72fe27a5da3ea8de51adb7a686..709190192e4f6eed51d28709741fcf3ae87c3a6a 100644
--- a/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/CommandLineParser.java
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/CommandLineParser.java
@@ -24,7 +24,10 @@ public class CommandLineParser {
         mOptions.addOption("h", SettingType.DISPLAY_HELP.toString(), false, "Print help and exit")
                 .addOption("c", SettingType.CSV_LOCATION.toString(), true, "Path to CSV")
                 .addOption("s", SettingType.SEMANTIC_MAPPING.toString(), true, "Literal for semantic mapping")
-                .addOption("p", SettingType.PRINTER_CONFIG_PATH.toString(), true, "Path to printer configuration file");
+                .addOption("p", SettingType.PRINTER_CONFIG_PATH.toString(), true, "Path to printer configuration file")
+                .addOption("t", SettingType.DIAGRAM_TITLE.toString(), true, "Title of the diagram.")
+                .addOption("x", SettingType.X_AXIS_LABEL.toString(), true, "Label of X-axis including unit.")
+                .addOption("y", SettingType.Y_AXIS_LABEL.toString(), true, "Label of Y-axis including unit.");
     }
 
     /**
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/SettingType.java b/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/SettingType.java
index 010c9c528891ffdb81549f715eac363625469e76..7740652be678cd701d7aa24b5e2aa0d98404e48a 100644
--- a/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/SettingType.java
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/SettingType.java
@@ -10,7 +10,10 @@ public enum SettingType {
     DISPLAY_HELP("help"),
     CSV_LOCATION("csv-path"),
     PRINTER_CONFIG_PATH("printer-config-path"),
-    SEMANTIC_MAPPING("semantic-mapping");
+    SEMANTIC_MAPPING("semantic-mapping"),
+    DIAGRAM_TITLE("title"),
+    X_AXIS_LABEL("xLabel"),
+    Y_AXIS_LABEL("yLabel");
 
     private final String mName;
 
@@ -28,6 +31,12 @@ public enum SettingType {
                 return PRINTER_CONFIG_PATH;
             case "semantic-mapping":
                 return SEMANTIC_MAPPING;
+            case "title":
+                return DIAGRAM_TITLE;
+            case "xLabel":
+                return X_AXIS_LABEL;
+            case "yLabel":
+                return Y_AXIS_LABEL;
             default:
                 throw new IllegalArgumentException("Setting not available");
         }
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/configparser/ConfigurationParser.java b/src/main/java/de/tudresden/inf/mci/brailleplot/configparser/ConfigurationParser.java
index b951ece2f3101e6b6f588c597faa639c7d5c7944..7a698aae4d46311daa1ae8a94c6daad17d148f3f 100644
--- a/src/main/java/de/tudresden/inf/mci/brailleplot/configparser/ConfigurationParser.java
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/configparser/ConfigurationParser.java
@@ -1,5 +1,6 @@
 package de.tudresden.inf.mci.brailleplot.configparser;
 
+import de.tudresden.inf.mci.brailleplot.util.UrlHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -172,7 +173,7 @@ public abstract class ConfigurationParser {
         mLogger.debug("Starting parsing properties file from java resources: \"{}\"", resource);
 
         try {
-            parseConfigFile(resource.openStream(), getParentUrl(resource), assertCompleteness);
+            parseConfigFile(resource.openStream(), UrlHelper.getParentUrl(resource), assertCompleteness);
         } catch (IOException e) {
             throw new ConfigurationParsingException("Could not open resource at \"" + resource.toString() + "\"", e);
         }
@@ -191,7 +192,7 @@ public abstract class ConfigurationParser {
         mLogger.debug("Starting parsing properties file from file system: \"{}\"", filePath);
 
         try {
-            parseConfigFile(new FileInputStream(filePath.toFile()), filePath.toFile().toURI().toURL(), assertCompleteness);
+            parseConfigFile(new FileInputStream(filePath.toFile()), UrlHelper.getParentUrl(filePath.toFile().toURI().toURL()), assertCompleteness);
         } catch (FileNotFoundException | MalformedURLException e) {
             throw new ConfigurationParsingException("Configuration file could not be read at \"" + filePath.toString() + "\"");
         }
@@ -211,7 +212,7 @@ public abstract class ConfigurationParser {
         // reset internal property buffer
         mPrinterProperties.clear();
         mFormatProperties.clear();
-        mValidator.setSearchPath(getPath(path));
+        mValidator.setSearchPath(getPathNoFilePrefix(path));
         // load and parse file
         parse(config, path);
         // build printer object from added properties
@@ -240,30 +241,14 @@ public abstract class ConfigurationParser {
         }
     }
 
-    /**
-     * Returns the URL to the parent directory of a File / Resource.
-     * @param resourcePath The URL to analyze.
-     * @return The URL to the parent directory of the specified URL.
-     * @throws ConfigurationParsingException if the generated URL is not a valid URL.
-     */
-    private static URL getParentUrl(final URL resourcePath) throws ConfigurationParsingException {
-        String fileString = resourcePath.getPath();
-        String parentString = fileString.substring(0, fileString.lastIndexOf("/"));
-        try {
-            return new URL(resourcePath.getProtocol(), resourcePath.getHost(), parentString);
-        } catch (MalformedURLException e) {
-            throw new ConfigurationParsingException("Could not create URL to parent path", e);
-        }
-    }
-
     /**
      * Return a String representation of the path of a {@link URL}.
      * Strips the {@literal "}file:{@literal "} prefix from an URL, if it exist.
      * @param url The URL that needs to be stripped
      * @return The String representation of the path of a URL where the leading {@literal "}file:{@literal "} prefix is stripped.
      */
-    private static String getPath(final URL url) {
-        String urlString = url.getPath();
+    private static String getPathNoFilePrefix(final URL url) throws ConfigurationParsingException {
+        String urlString = UrlHelper.getPathString(url);
         return urlString.replaceAll("^file:", "");
     }
 }
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/configparser/JavaPropertiesConfigurationParser.java b/src/main/java/de/tudresden/inf/mci/brailleplot/configparser/JavaPropertiesConfigurationParser.java
index 190f43ca1562b4c62cdf07e39ad82b87263e9df1..01e938d1f5b1958b6fed42f93571e9d84e69dabd 100644
--- a/src/main/java/de/tudresden/inf/mci/brailleplot/configparser/JavaPropertiesConfigurationParser.java
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/configparser/JavaPropertiesConfigurationParser.java
@@ -1,5 +1,7 @@
 package de.tudresden.inf.mci.brailleplot.configparser;
 
+import de.tudresden.inf.mci.brailleplot.util.UrlHelper;
+
 import java.io.BufferedInputStream;
 import java.io.FileInputStream;
 import java.io.IOException;
@@ -19,6 +21,7 @@ import java.util.Properties;
 public final class JavaPropertiesConfigurationParser extends ConfigurationParser {
 
     private static final String INCLUDE_FILE_EXTENSION = ".properties";
+    private static final String CONFIG_RESOURCE_ROOT = "config";
 
     /**
      * Constructor.
@@ -88,7 +91,7 @@ public final class JavaPropertiesConfigurationParser extends ConfigurationParser
             String value = properties.getProperty(key);
             // check for special property key: 'include'
             if (key.equalsIgnoreCase("include")) {
-                includeResource(value, path);
+                includeResources(value, path);
             } else if (key.equalsIgnoreCase("include-file")) {
                 includeFiles(value, path);
             } else {
@@ -121,20 +124,30 @@ public final class JavaPropertiesConfigurationParser extends ConfigurationParser
      */
     private void includeFiles(final String fileList, final URL parentUrl) throws ConfigurationParsingException, ConfigurationValidationException {
         for (String s : fileList.split(",")) {
-
-            Path parentPath = null;
-            try {
-                parentPath = Path.of(parentUrl.toURI());
-            } catch (URISyntaxException e) {
-                throw new ConfigurationParsingException("Could not generate URI", e);
+            s = s.trim() + INCLUDE_FILE_EXTENSION;
+
+            Path p = Path.of(s);
+            String newPathString;
+            Path newPath;
+            if (p.isAbsolute()) {
+                newPath = p;
+                newPathString = newPath.toString();
+            } else {
+                Path parentPath = null;
+                try {
+                    parentPath = Path.of(parentUrl.toURI());
+                } catch (URISyntaxException e) {
+                    throw new ConfigurationParsingException("Could not generate path from URL", e);
+                }
+                newPath = parentPath.resolve(s);
+                newPathString = newPath.toAbsolutePath().toString();
             }
-            Path newPath = parentPath.getParent().resolve(s.trim() + INCLUDE_FILE_EXTENSION);
-            String newPathString = newPath.toAbsolutePath().toString();
 
             mLogger.debug("Prepare recursive parsing of properties file in the file system for file \"{}\"", newPathString);
 
             try (InputStream is = new BufferedInputStream(new FileInputStream(newPathString))) {
-                parse(is, newPath.toUri().toURL());
+                Objects.requireNonNull(is);
+                parse(is, UrlHelper.getParentUrl(newPath.toUri().toURL()));
             } catch (IOException e) {
                 throw new ConfigurationParsingException("Could not open include file", e);
             }
@@ -148,20 +161,30 @@ public final class JavaPropertiesConfigurationParser extends ConfigurationParser
      * @throws ConfigurationParsingException If errors occurred while reading from a resource.
      * @throws ConfigurationValidationException On any error while checking the parsed properties validity.
      */
-    private void includeResource(final String fileList, final URL parentUrl) throws ConfigurationParsingException, ConfigurationValidationException {
+    private void includeResources(final String fileList, final URL parentUrl) throws ConfigurationParsingException, ConfigurationValidationException {
         for (String s : fileList.split(",")) {
+            s = s.trim() + INCLUDE_FILE_EXTENSION;
+            boolean isAbsolutePath = s.startsWith("/");
 
             URL newUrl = null;
-            try {
-                newUrl = new URL(parentUrl + "/" + s.trim() + INCLUDE_FILE_EXTENSION);
-            } catch (MalformedURLException e) {
-                throw new ConfigurationParsingException("Could not generate URI", e);
+            // If the value begins with a "/", treat path as absolute path in resources
+            if (isAbsolutePath) {
+                String urlString = CONFIG_RESOURCE_ROOT + s;
+                newUrl = getClass().getClassLoader().getResource(urlString);
+                // else treat relative
+            } else {
+                try {
+                    newUrl = new URL(parentUrl.getProtocol(), parentUrl.getHost(), UrlHelper.getPathString(parentUrl) + "/" + s);
+                } catch (MalformedURLException e) {
+                    throw new ConfigurationParsingException("Could not create URL to relative resource", e);
+                }
             }
 
-            mLogger.debug("Prepare recursive parsing of properties file in the java resources at \"{}\"", newUrl);
+            mLogger.debug("Prepare recursive parsing of properties file in the java resources at \"{}\"", UrlHelper.getString(newUrl));
 
             try (InputStream is = newUrl.openStream()) {
-                parse(is, newUrl);
+                Objects.requireNonNull(is);
+                parse(is, UrlHelper.getParentUrl(newUrl));
             } catch (IOException e) {
                 throw new ConfigurationParsingException("Could not open include resource", e);
             }
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/LiblouisBrailleTextRasterizer.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/LiblouisBrailleTextRasterizer.java
index 25b9e69487c01e65fbbc41b0d0be09a506a0aad1..08201f0a21e60d78e40478073803bdbb92582d7d 100644
--- a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/LiblouisBrailleTextRasterizer.java
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/LiblouisBrailleTextRasterizer.java
@@ -8,8 +8,11 @@ import de.tudresden.inf.mci.brailleplot.layout.InsufficientRenderingAreaExceptio
 import de.tudresden.inf.mci.brailleplot.layout.RasterCanvas;
 import de.tudresden.inf.mci.brailleplot.layout.Rectangle;
 import de.tudresden.inf.mci.brailleplot.printerbackend.NotSupportedFileExtensionException;
+import de.tudresden.inf.mci.brailleplot.util.NativeLibraryHelper;
+import de.tudresden.inf.mci.brailleplot.util.NoSuchNativeLibraryException;
 import org.liblouis.DisplayException;
 import org.liblouis.DisplayTable;
+import org.liblouis.Louis;
 import org.liblouis.TranslationException;
 import org.liblouis.TranslationResult;
 import org.liblouis.Translator;
@@ -27,6 +30,8 @@ import static java.lang.Math.ceil;
 
 public class LiblouisBrailleTextRasterizer implements Rasterizer<BrailleText> {
 
+    private static boolean mNativeLibInitialized = false;
+
     private AbstractBrailleTableParser mParser;
     // Parameters for rasterizing
     private int x;
@@ -239,4 +244,33 @@ public class LiblouisBrailleTextRasterizer implements Rasterizer<BrailleText> {
         mTranslator = temp;
         return length;
     }
+
+    /**
+     * Initializes the Module.
+     * @throws LibLouisLibraryMissingException If liblouis could not be loaded from neither the jar or the default JNI include path.
+     */
+    public static void initModule() throws LibLouisLibraryMissingException {
+        if (!mNativeLibInitialized) {
+            try {
+                NativeLibraryHelper.loadNativeLibrary("liblouis");
+            } catch (NoSuchNativeLibraryException e) {
+                // Even if the library is not distributed within the jar file, it might be installed on the system.
+            }
+            try {
+                Louis.getVersion();
+            } catch (java.lang.UnsatisfiedLinkError e) {
+                throw new LibLouisLibraryMissingException(e);
+            }
+            mNativeLibInitialized = true;
+        }
+    }
+
+    /**
+     * Indicates, the native liblouis library was not found.
+     */
+    public static class LibLouisLibraryMissingException extends NoSuchNativeLibraryException {
+        LibLouisLibraryMissingException(final Throwable cause) {
+            super(cause);
+        }
+    }
 }
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/util/GeneralResource.java b/src/main/java/de/tudresden/inf/mci/brailleplot/util/GeneralResource.java
index 9ff63c0cdf4bbd20520ab2e02aee0ac5c062fb63..c6ab27d9deb66773c3aec3131c4b9f9f4106b8a9 100644
--- a/src/main/java/de/tudresden/inf/mci/brailleplot/util/GeneralResource.java
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/util/GeneralResource.java
@@ -47,25 +47,26 @@ public final class GeneralResource {
      * @throws IOException If the given path neither determines a valid external file, nor a valid resource.
      */
     public GeneralResource(final String resourcePath, final String searchPath) throws IOException {
+        mLogger.debug("Requested GeneralResource for path \"" + resourcePath + "\" with search path \"" + searchPath + "\"");
         File checkFile = new File(resourcePath);
-        mLogger.debug("Checking referenced path: " + checkFile);
+        mLogger.trace("Checking referenced path: " + checkFile);
         if (checkFile.isFile()) {
-            mLogger.trace("Interpreting path as file: " + checkFile.getCanonicalPath());
+            mLogger.debug("Interpreting path as file: " + checkFile.getCanonicalPath());
             mResourcePath = checkFile.getCanonicalPath();
             mValidExternalFile = true;
         }
         checkFile = checkFile.getAbsoluteFile();
-        mLogger.debug("Checking referenced path as absolute path: " + checkFile);
+        mLogger.trace("Checking referenced path as absolute path: " + checkFile);
         if (checkFile.isFile()) {
-            mLogger.trace("Interpreting path as absolute file: " + checkFile.getCanonicalPath());
+            mLogger.debug("Interpreting path as absolute file: " + checkFile.getCanonicalPath());
             mResourcePath = checkFile.getCanonicalPath();
             mValidExternalFile = true;
         }
         if (Objects.nonNull(searchPath)) {
             checkFile = new File(searchPath + File.separator + resourcePath);
-            mLogger.debug("Looking for referenced path in search path: " + checkFile);
+            mLogger.trace("Looking for referenced path in search path: " + checkFile);
             if (checkFile.isFile()) {
-                mLogger.trace("Interpreting path as search path relative file: " + checkFile.getCanonicalPath());
+                mLogger.debug("Interpreting path as search path relative file: " + checkFile.getCanonicalPath());
                 mResourcePath = checkFile.getCanonicalPath();
                 mValidExternalFile = true;
             }
@@ -77,9 +78,9 @@ public final class GeneralResource {
         String resourceClassPath = stripJarPath(resourcePath);
         resourceClassPath = resourceClassPath.replace(File.separator, "/"); // class paths are always separated by forward slash
         InputStream checkStream = getClass().getClassLoader().getResourceAsStream(resourceClassPath);
-        mLogger.debug("Checking referenced path as resource: " + resourceClassPath);
+        mLogger.trace("Checking referenced path as resource: " + resourceClassPath);
         if (Objects.nonNull(checkStream)) {
-            mLogger.trace("Interpreting path as resource stream: " + resourceClassPath);
+            mLogger.debug("Interpreting path as resource stream: " + resourceClassPath);
             mResourcePath = resourceClassPath;
             mValidPackedResource = true;
         }
@@ -87,9 +88,9 @@ public final class GeneralResource {
             String relativeResourcePath = new File(resourceSearchPath + File.separator + resourceClassPath).toPath().normalize().toString();
             relativeResourcePath = relativeResourcePath.replace("\\", "/");
             checkStream = getClass().getClassLoader().getResourceAsStream(relativeResourcePath);
-            mLogger.debug("Checking referenced path as search path relative resource: " + relativeResourcePath);
+            mLogger.trace("Checking referenced path as search path relative resource: " + relativeResourcePath);
             if (Objects.nonNull(checkStream)) {
-                mLogger.trace("Interpreting path as resource stream: " + relativeResourcePath);
+                mLogger.debug("Interpreting path as resource stream: " + relativeResourcePath);
                 mResourcePath = relativeResourcePath;
                 mValidPackedResource = true;
             }
@@ -141,7 +142,7 @@ public final class GeneralResource {
      * @param path Absolute classpath pointing to jar resource. Omit the leading "/". If this point to a resource directory the directory and all contents are exported.
      * @return A File instance representing the resource on the file system.
      */
-    public static File getOrExportResourceFile(final String path) {
+    public static synchronized File getOrExportResourceFile(final String path) {
         if (isRunFromCompiledJar()) {
             try {
                 JarFile jar = openJarFile();
@@ -178,7 +179,7 @@ public final class GeneralResource {
         } else {
             Class cl = getClassRef();
             URL resource = cl.getResource("/" + path); // preceding slash for absolute classpath reference
-            String directoryPath = resource.getPath();
+            String directoryPath = UrlHelper.getPathString(resource);
             return new File(directoryPath);
         }
     }
@@ -188,7 +189,9 @@ public final class GeneralResource {
             if (!isRunFromCompiledJar()) {
                 throw new IllegalStateException("Not running from jar.");
             }
-            File jarFile = new File(getClassRef().getProtectionDomain().getCodeSource().getLocation().getPath());
+            URL jarUrl = getClassRef().getProtectionDomain().getCodeSource().getLocation();
+            String jarPath = UrlHelper.getPathString(jarUrl);
+            File jarFile = new File(jarPath);
             return new JarFile(jarFile);
         } catch (Exception e) {
             throw new RuntimeException("Error while retrieving JarFile reference.", e);
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/util/NativeLibraryHelper.java b/src/main/java/de/tudresden/inf/mci/brailleplot/util/NativeLibraryHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..fb12d84492efcbfc339696d9e772692ae93417a6
--- /dev/null
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/util/NativeLibraryHelper.java
@@ -0,0 +1,118 @@
+package de.tudresden.inf.mci.brailleplot.util;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+
+/**
+ * This class offers static methods for native library loading purposes.
+ * @author Georg Graßnick
+ * @version 2019.09.26
+ */
+public final class NativeLibraryHelper {
+
+    private static final Logger LOG = LoggerFactory.getLogger(NativeLibraryHelper.class);
+    private static final String LIB_PATH = calculateLibPath();
+
+    private NativeLibraryHelper() { }
+
+    /**
+     * Loads a library from the resources according to the current system architecture and operating system.
+     * @param libName The name of the library. Library prefixes ({@literal "}lib{@literal "}) are not treated separately.
+     *                The system specific file ending is added automatically and must not be included in the parameter.
+     * @return A File object representing the file of the library. The JNI is automatically set up to take of the library,
+     * so there is most likely no need for this return value.
+     * @throws NoSuchNativeLibraryException If the requested library could not be found or read.
+     */
+    public static synchronized File loadNativeLibrary(final String libName) throws NoSuchNativeLibraryException {
+        File libFile;
+        try {
+            String libPath = "native/" + libName + "/" + LIB_PATH + "/" + libName + dynamicLibFileEnding();
+            libFile = GeneralResource.getOrExportResourceFile(libPath).getAbsoluteFile();
+            registerNewSystemLibPath(libFile.getParent());
+        } catch (Exception e) {
+            throw new NoSuchNativeLibraryException("Could not provide native library from java resources", e);
+        }
+        LOG.debug("Found and exported native library \"" + libFile + "\" for requested library \"" + libName + "\"");
+        return libFile;
+    }
+
+    /**
+     * Adds a path in the file system to the search path of the JNI.
+     * @param path The path to add.
+     */
+    private static synchronized void registerNewSystemLibPath(final String path) {
+        String currentLibPath = System.getProperty("jna.library.path");
+        String newLibPath = null;
+        boolean pathExists = false;
+
+        if (currentLibPath == null) {
+            newLibPath = path;
+        } else {
+            // Do not insert path if it already is included
+            String[] existingPaths = currentLibPath.split(File.pathSeparator);
+            for (String s : existingPaths) {
+                if (s.equals(path)) {
+                    pathExists = true;
+                    break;
+                }
+            }
+            newLibPath = currentLibPath + File.pathSeparator + path;
+        }
+
+        if (!pathExists) {
+            LOG.debug("Setting JNI library path property to \"" + newLibPath + "\"");
+            System.setProperty("jna.library.path", newLibPath);
+        }
+    }
+
+    private static String calculateLibPath() {
+        return getArch() + "/" + getOs();
+    }
+
+    private static String getArch() {
+        String arch = System.getProperty("os.arch");
+        // reference: https://stackoverflow.com/a/36926327
+        switch (arch) {
+            case "x86":
+            case "i386":
+            case "i486":
+            case "i586":
+            case "i686":
+                return "x86_32";
+            case "x86_64":
+            case "amd64":
+                return "x86_64";
+            default:
+                throw new RuntimeException("Operating System architecture \"" + arch + "\" is currently not supported");
+        }
+    }
+
+    private static String getOs() {
+        String name = System.getProperty("os.name");
+        String nameLow = name.toLowerCase();
+        if (nameLow.contains("linux")) {
+            return "linux";
+        } else if (nameLow.contains("mac")) {
+            return "osx";
+        } else if (nameLow.contains("win")) {
+            return "win32";
+        } else {
+            throw new RuntimeException("Operating System \"" + name + "\" is currently not supported");
+        }
+    }
+
+    private static String dynamicLibFileEnding() {
+        switch (getOs()) {
+            case "win32":
+                return ".dll";
+            case "linux":
+                return ".so";
+            case "osx":
+                return ".dylib";
+            default:
+                throw new IllegalStateException("If this exception was thrown, something is wrong with your code");
+        }
+    }
+}
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/util/NoSuchNativeLibraryException.java b/src/main/java/de/tudresden/inf/mci/brailleplot/util/NoSuchNativeLibraryException.java
new file mode 100644
index 0000000000000000000000000000000000000000..2f7331e5f542838d14a77027252710f5dd426b50
--- /dev/null
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/util/NoSuchNativeLibraryException.java
@@ -0,0 +1,23 @@
+package de.tudresden.inf.mci.brailleplot.util;
+
+/**
+ * Indicates, some error has occurred while trying to load a native library from the java resources.
+ * @author Georg Graßnick
+ * @version 2019.09.26
+ */
+public class NoSuchNativeLibraryException extends RuntimeException {
+
+        public NoSuchNativeLibraryException() { }
+
+        public NoSuchNativeLibraryException(final String message) {
+            super(message);
+        }
+
+        public NoSuchNativeLibraryException(final Throwable cause) {
+            super(cause);
+        }
+
+        public NoSuchNativeLibraryException(final String message, final Throwable cause) {
+            super(message, cause);
+        }
+}
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/util/UrlHelper.java b/src/main/java/de/tudresden/inf/mci/brailleplot/util/UrlHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..3b923258ce5b4c3310a41ed46f78fca935a7124e
--- /dev/null
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/util/UrlHelper.java
@@ -0,0 +1,48 @@
+package de.tudresden.inf.mci.brailleplot.util;
+
+import de.tudresden.inf.mci.brailleplot.configparser.ConfigurationParsingException;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * This class offers static helper methods for working wth {@link URL} objects.
+ * @author Georg Graßnick
+ * @version 2019.09.27
+ */
+public final class UrlHelper {
+
+    private UrlHelper() { }
+
+    /**
+     * Get the string representation of the path of a {@link URL}.
+     * For example, spaces are taken care of here.
+     * @param url The {@link URL} to analyze.
+     * @return The string representation of the path of the {@link URL}.
+     */
+    public static String getPathString(final URL url) {
+        return URLDecoder.decode(url.getPath(), StandardCharsets.UTF_8);
+    }
+
+    public static String getString(final URL url) {
+        return URLDecoder.decode(url.toString(), StandardCharsets.UTF_8);
+    }
+
+    /**
+     * Returns the URL to the parent directory of a File / Resource.
+     * @param resourcePath The URL to analyze.
+     * @return The URL to the parent directory of the specified URL.
+     * @throws RuntimeException if the generated URL is not a valid URL.
+     */
+    public static URL getParentUrl(final URL resourcePath) throws ConfigurationParsingException {
+        String fileString = getPathString(resourcePath);
+        String parentString = fileString.substring(0, fileString.lastIndexOf("/"));
+        try {
+            return new URL(resourcePath.getProtocol(), resourcePath.getHost(), parentString);
+        } catch (MalformedURLException e) {
+            throw new RuntimeException("Could not create URL to parent path", e);
+        }
+    }
+}
diff --git a/third_party/liblouis_bin b/third_party/liblouis_bin
new file mode 160000
index 0000000000000000000000000000000000000000..24b6908d0b167ce785c0f9ce8295d9845b69a303
--- /dev/null
+++ b/third_party/liblouis_bin
@@ -0,0 +1 @@
+Subproject commit 24b6908d0b167ce785c0f9ce8295d9845b69a303