diff --git a/pom.xml b/pom.xml
index 0d82aea..c2852c8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,5 +21,12 @@
1.18.14
provided
+
+
+
+ com.1stleg
+ jnativehook
+ 2.1.0
+
\ No newline at end of file
diff --git a/src/main/java/nl/andrewlalis/blockbookbinder/BlockBookBinder.java b/src/main/java/nl/andrewlalis/blockbookbinder/BlockBookBinder.java
index 80f0b23..3c5ca22 100644
--- a/src/main/java/nl/andrewlalis/blockbookbinder/BlockBookBinder.java
+++ b/src/main/java/nl/andrewlalis/blockbookbinder/BlockBookBinder.java
@@ -1,8 +1,11 @@
package nl.andrewlalis.blockbookbinder;
import nl.andrewlalis.blockbookbinder.view.MainFrame;
+import org.jnativehook.GlobalScreen;
import javax.swing.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
/**
* The main class for the application.
diff --git a/src/main/java/nl/andrewlalis/blockbookbinder/control/BookExportActionListener.java b/src/main/java/nl/andrewlalis/blockbookbinder/control/BookExportActionListener.java
new file mode 100644
index 0000000..2a2266f
--- /dev/null
+++ b/src/main/java/nl/andrewlalis/blockbookbinder/control/BookExportActionListener.java
@@ -0,0 +1,41 @@
+package nl.andrewlalis.blockbookbinder.control;
+
+import nl.andrewlalis.blockbookbinder.model.Book;
+import nl.andrewlalis.blockbookbinder.view.BookPreviewPanel;
+import org.jnativehook.GlobalScreen;
+import org.jnativehook.NativeHookException;
+
+import java.awt.*;
+import java.awt.datatransfer.Clipboard;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class BookExportActionListener implements ActionListener {
+ private final BookPreviewPanel bookPreviewPanel;
+ private final Clipboard clipboard;
+
+ public BookExportActionListener(BookPreviewPanel bookPreviewPanel) {
+ this.bookPreviewPanel = bookPreviewPanel;
+ this.clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ System.out.println("Starting export.");
+ final Book book = this.bookPreviewPanel.getBook();
+ BookPagePasteListener pasteListener = new BookPagePasteListener(book, clipboard);
+ try {
+ // For catching native events, set logging here.
+ Logger logger = Logger.getLogger(GlobalScreen.class.getPackage().getName());
+ logger.setLevel(Level.WARNING);
+ logger.setUseParentHandlers(false);
+ GlobalScreen.registerNativeHook();
+ GlobalScreen.addNativeKeyListener(pasteListener);
+ } catch (NativeHookException nativeHookException) {
+ System.err.println("Could not register native hook.");
+ nativeHookException.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/nl/andrewlalis/blockbookbinder/control/BookPagePasteListener.java b/src/main/java/nl/andrewlalis/blockbookbinder/control/BookPagePasteListener.java
new file mode 100644
index 0000000..8a2fcec
--- /dev/null
+++ b/src/main/java/nl/andrewlalis/blockbookbinder/control/BookPagePasteListener.java
@@ -0,0 +1,51 @@
+package nl.andrewlalis.blockbookbinder.control;
+
+import nl.andrewlalis.blockbookbinder.model.Book;
+import org.jnativehook.GlobalScreen;
+import org.jnativehook.NativeHookException;
+import org.jnativehook.keyboard.NativeKeyEvent;
+import org.jnativehook.keyboard.NativeKeyListener;
+
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.StringSelection;
+
+public class BookPagePasteListener implements NativeKeyListener {
+ private final Book book;
+ private final Clipboard clipboard;
+ private int nextPage;
+
+ public BookPagePasteListener(Book book, Clipboard clipboard) {
+ this.book = book;
+ this.clipboard = clipboard;
+ this.nextPage = 0;
+ }
+
+ @Override
+ public void nativeKeyTyped(NativeKeyEvent nativeKeyEvent) {}
+
+ @Override
+ public void nativeKeyPressed(NativeKeyEvent nativeKeyEvent) {
+ if (nativeKeyEvent.getKeyCode() == NativeKeyEvent.VC_V && (nativeKeyEvent.getModifiers() & NativeKeyEvent.CTRL_MASK) > 0) {
+ System.out.println("CTRL + V -> Paste!!!");
+ clipboard.setContents(
+ new StringSelection(book.getPages().get(this.nextPage).toString()),
+ null
+ );
+ this.nextPage++;
+ System.out.println("Incremented page.");
+ if (this.nextPage >= this.book.getPageCount()) {
+ try {
+ GlobalScreen.removeNativeKeyListener(this);
+ GlobalScreen.unregisterNativeHook();
+ System.out.println("Done pasting.");
+ } catch (NativeHookException nativeHookException) {
+ System.err.println("Could not unregister a native hook.");
+ nativeHookException.printStackTrace();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void nativeKeyReleased(NativeKeyEvent nativeKeyEvent) {}
+}
diff --git a/src/main/java/nl/andrewlalis/blockbookbinder/control/ConvertToBookActionListener.java b/src/main/java/nl/andrewlalis/blockbookbinder/control/ConvertToBookActionListener.java
new file mode 100644
index 0000000..90343aa
--- /dev/null
+++ b/src/main/java/nl/andrewlalis/blockbookbinder/control/ConvertToBookActionListener.java
@@ -0,0 +1,23 @@
+package nl.andrewlalis.blockbookbinder.control;
+
+import nl.andrewlalis.blockbookbinder.model.BookBuilder;
+import nl.andrewlalis.blockbookbinder.view.BookPreviewPanel;
+import nl.andrewlalis.blockbookbinder.view.SourceTextPanel;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+public class ConvertToBookActionListener implements ActionListener {
+ private final SourceTextPanel sourceTextPanel;
+ private final BookPreviewPanel bookPreviewPanel;
+
+ public ConvertToBookActionListener(SourceTextPanel sourceTextPanel, BookPreviewPanel bookPreviewPanel) {
+ this.sourceTextPanel = sourceTextPanel;
+ this.bookPreviewPanel = bookPreviewPanel;
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ this.bookPreviewPanel.setBook(new BookBuilder().build(this.sourceTextPanel.getSourceText()));
+ }
+}
diff --git a/src/main/java/nl/andrewlalis/blockbookbinder/model/Book.java b/src/main/java/nl/andrewlalis/blockbookbinder/model/Book.java
index 1c3ed61..8d80596 100644
--- a/src/main/java/nl/andrewlalis/blockbookbinder/model/Book.java
+++ b/src/main/java/nl/andrewlalis/blockbookbinder/model/Book.java
@@ -16,4 +16,14 @@ public class Book {
public int getPageCount() {
return this.pages.size();
}
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("Book of " + this.getPageCount() + " pages:\n");
+ for (int i = 0; i < this.getPageCount(); i++) {
+ BookPage page = this.pages.get(i);
+ sb.append("Page ").append(i + 1).append(":\n").append(page).append('\n');
+ }
+ return sb.toString();
+ }
}
diff --git a/src/main/java/nl/andrewlalis/blockbookbinder/model/BookBuilder.java b/src/main/java/nl/andrewlalis/blockbookbinder/model/BookBuilder.java
new file mode 100644
index 0000000..2cadc8f
--- /dev/null
+++ b/src/main/java/nl/andrewlalis/blockbookbinder/model/BookBuilder.java
@@ -0,0 +1,73 @@
+package nl.andrewlalis.blockbookbinder.model;
+
+import nl.andrewlalis.blockbookbinder.util.ApplicationProperties;
+
+/**
+ * Class which helps construct formatted book pages from a source text.
+ */
+public class BookBuilder {
+ public Book build(String source) {
+ Book book = new Book();
+ char[] sourceChars = source.trim().toCharArray();
+
+ final int maxLines = ApplicationProperties.getIntProp("book.page_max_lines");
+ final int maxLineWidth = ApplicationProperties.getIntProp("book.page_max_width");
+ final CharWidthMapper charWidthMapper = new CharWidthMapper();
+
+ BookPage currentPage = new BookPage();
+ StringBuilder lineStringBuilder = new StringBuilder(64);
+ int pageLineCount = 1; // Current line on the page we're on.
+ int lineCharWidth = 0; // Total pixel width of the current line so far.
+ int i = 0;
+ while (i < sourceChars.length) {
+ final char c = sourceChars[i];
+ if (c == '\n') {
+ i++;
+ continue;
+ }
+ final int cWidth = charWidthMapper.getWidth(c);
+ boolean newLineNeeded = lineCharWidth + cWidth + 1 > maxLineWidth;
+ boolean newPageNeeded = pageLineCount == maxLines && newLineNeeded;
+ System.out.println("Current char: " + c + ", Current Line: " + pageLineCount + ", Current Line Char Width: " + lineCharWidth + ", New line needed: " + newLineNeeded + ", New page needed: " + newPageNeeded);
+
+ // Check if the page is full, and append it to the book, and refresh.
+ if (newPageNeeded) {
+ // If necessary, append whatever is left in the last line to the page.
+ if (lineStringBuilder.length() > 0) {
+ currentPage.addLine(lineStringBuilder.toString());
+ }
+ book.getPages().add(currentPage);
+ currentPage = new BookPage();
+ // Reset all buffers and counters for the next page.
+ lineStringBuilder.setLength(0);
+ newLineNeeded = false;
+ pageLineCount = 1;
+ lineCharWidth = 0;
+ }
+
+ // Check if the line is full, and append it to the page and refresh.
+ if (newLineNeeded) {
+ currentPage.addLine(lineStringBuilder.toString());
+ // Reset line status info.
+ lineStringBuilder.setLength(0);
+ pageLineCount++;
+ lineCharWidth = 0;
+ }
+
+ // Finally, append the char to the current line.
+ lineStringBuilder.append(c);
+ lineCharWidth += cWidth + 1;
+ i++;
+ }
+
+ // Append a final page with the remainder of the text.
+ if (currentPage.hasContent()) {
+ if (lineStringBuilder.length() > 0) {
+ currentPage.addLine(lineStringBuilder.toString());
+ }
+ book.getPages().add(currentPage);
+ }
+
+ return book;
+ }
+}
diff --git a/src/main/java/nl/andrewlalis/blockbookbinder/model/BookPage.java b/src/main/java/nl/andrewlalis/blockbookbinder/model/BookPage.java
index f05f87a..17f903f 100644
--- a/src/main/java/nl/andrewlalis/blockbookbinder/model/BookPage.java
+++ b/src/main/java/nl/andrewlalis/blockbookbinder/model/BookPage.java
@@ -1,14 +1,31 @@
package nl.andrewlalis.blockbookbinder.model;
-import lombok.Getter;
-import lombok.Setter;
+import nl.andrewlalis.blockbookbinder.util.ApplicationProperties;
+
+import java.util.ArrayList;
+import java.util.List;
public class BookPage {
- @Getter
- @Setter
- private String content;
+ private final List lines;
public BookPage() {
- this.content = "";
+ this.lines = new ArrayList<>(ApplicationProperties.getIntProp("book.page_max_lines"));
+ }
+
+ public boolean addLine(String line) {
+ if (this.lines.size() == ApplicationProperties.getIntProp("book.page_max_lines")) {
+ return false;
+ }
+ this.lines.add(line);
+ return true;
+ }
+
+ public boolean hasContent() {
+ return !this.lines.isEmpty();
+ }
+
+ @Override
+ public String toString() {
+ return String.join("\n", this.lines);
}
}
diff --git a/src/main/java/nl/andrewlalis/blockbookbinder/model/CharWidthMapper.java b/src/main/java/nl/andrewlalis/blockbookbinder/model/CharWidthMapper.java
new file mode 100644
index 0000000..21fe21d
--- /dev/null
+++ b/src/main/java/nl/andrewlalis/blockbookbinder/model/CharWidthMapper.java
@@ -0,0 +1,56 @@
+package nl.andrewlalis.blockbookbinder.model;
+
+import nl.andrewlalis.blockbookbinder.util.ApplicationProperties;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class CharWidthMapper {
+ private final Map charWidthMap;
+
+ public CharWidthMapper() {
+ this.charWidthMap = new HashMap<>();
+ this.initCharWidthMap();
+ }
+
+ public int getWidth(char c) {
+ return this.charWidthMap.getOrDefault(c, 0);
+ }
+
+ private void initCharWidthMap() {
+ this.charWidthMap.put(' ', 3);
+ this.charWidthMap.put('!', 1);
+ this.charWidthMap.put('"', 3);
+ this.charWidthMap.put('\'', 1);
+ this.charWidthMap.put('(', 3);
+ this.charWidthMap.put(')', 3);
+ this.charWidthMap.put('*', 3);
+ this.charWidthMap.put(',', 1);
+ this.charWidthMap.put('.', 1);
+ this.charWidthMap.put(':', 1);
+ this.charWidthMap.put(';', 1);
+ this.charWidthMap.put('<', 4);
+ this.charWidthMap.put('>', 4);
+ this.charWidthMap.put('@', 6);
+ this.charWidthMap.put('I', 3);
+ this.charWidthMap.put('[', 3);
+ this.charWidthMap.put(']', 3);
+ this.charWidthMap.put('`', 2);
+ this.charWidthMap.put('f', 4);
+ this.charWidthMap.put('i', 1);
+ this.charWidthMap.put('k', 4);
+ this.charWidthMap.put('l', 2);
+ this.charWidthMap.put('t', 3);
+ this.charWidthMap.put('{', 3);
+ this.charWidthMap.put('|', 1);
+ this.charWidthMap.put('}', 3);
+ this.charWidthMap.put('~', 6);
+
+ final int defaultWidth = ApplicationProperties.getIntProp("book.default_char_width");
+ for (char c = 32; c < 127; c++) {
+ if (!this.charWidthMap.containsKey(c)) {
+ this.charWidthMap.put(c, defaultWidth);
+ }
+ }
+ }
+}
diff --git a/src/main/java/nl/andrewlalis/blockbookbinder/util/ApplicationProperties.java b/src/main/java/nl/andrewlalis/blockbookbinder/util/ApplicationProperties.java
index 6e8dc0b..63ad45c 100644
--- a/src/main/java/nl/andrewlalis/blockbookbinder/util/ApplicationProperties.java
+++ b/src/main/java/nl/andrewlalis/blockbookbinder/util/ApplicationProperties.java
@@ -3,6 +3,8 @@ package nl.andrewlalis.blockbookbinder.util;
import lombok.Getter;
import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Properties;
/**
@@ -14,6 +16,8 @@ public class ApplicationProperties {
@Getter
private final Properties properties;
+ private final Map intPropCache;
+
public static ApplicationProperties getInstance() {
if (instance == null) {
try {
@@ -35,8 +39,18 @@ public class ApplicationProperties {
return getInstance().getProperties().getProperty(key);
}
+ public static Integer getIntProp(String key) {
+ Integer value = getInstance().intPropCache.get(key);
+ if (value == null) {
+ value = Integer.parseInt(getProp(key));
+ getInstance().intPropCache.put(key, value);
+ }
+ return value;
+ }
+
private ApplicationProperties() throws IOException {
this.properties = new Properties();
this.properties.load(this.getClass().getClassLoader().getResourceAsStream("application.properties"));
+ this.intPropCache = new HashMap<>();
}
}
diff --git a/src/main/java/nl/andrewlalis/blockbookbinder/view/BookPreviewPanel.java b/src/main/java/nl/andrewlalis/blockbookbinder/view/BookPreviewPanel.java
index 665621f..ca1c237 100644
--- a/src/main/java/nl/andrewlalis/blockbookbinder/view/BookPreviewPanel.java
+++ b/src/main/java/nl/andrewlalis/blockbookbinder/view/BookPreviewPanel.java
@@ -1,30 +1,87 @@
package nl.andrewlalis.blockbookbinder.view;
+import lombok.Getter;
import nl.andrewlalis.blockbookbinder.model.Book;
+import nl.andrewlalis.blockbookbinder.model.BookPage;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
+import java.io.IOException;
+import java.io.InputStream;
/**
* A customized panel that's dedicated to showing a book's contents.
*/
public class BookPreviewPanel extends JPanel {
+ @Getter
private Book book;
+ private int currentPage = 0;
+ private final JTextArea previewPageTextArea;
+ private final JLabel titleLabel;
public BookPreviewPanel() {
super(new BorderLayout());
- this.add(new JLabel("Book Preview"), BorderLayout.NORTH);
+ this.titleLabel = new JLabel("Book Preview");
+ this.add(this.titleLabel, BorderLayout.NORTH);
this.setBorder(new EmptyBorder(5, 5, 5, 5));
- JTextArea previewPageTextArea = new JTextArea();
- JScrollPane previewPageScrollPane = new JScrollPane(previewPageTextArea, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
+ this.previewPageTextArea = new JTextArea();
+ this.previewPageTextArea.setEditable(false);
+ try {
+ InputStream is = this.getClass().getClassLoader().getResourceAsStream("fonts/1_Minecraft-Regular.otf");
+ if (is == null) {
+ throw new IOException("Could not read minecraft font.");
+ }
+ Font mcFont = Font.createFont(Font.TRUETYPE_FONT, is);
+ mcFont = mcFont.deriveFont(24.0f);
+ this.previewPageTextArea.setFont(mcFont);
+ } catch (FontFormatException | IOException e) {
+ e.printStackTrace();
+ }
+ JScrollPane previewPageScrollPane = new JScrollPane(this.previewPageTextArea, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
this.add(previewPageScrollPane, BorderLayout.CENTER);
JPanel previewButtonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
- previewButtonPanel.add(new JButton("<"));
- previewButtonPanel.add(new JButton(">"));
+ JButton previousPageButton = new JButton("Previous Page");
+ previousPageButton.addActionListener(e -> {
+ if (currentPage > 0) {
+ currentPage--;
+ displayCurrentPage();
+ }
+ });
+ JButton nextPageButton = new JButton("Next Page");
+ nextPageButton.addActionListener(e -> {
+ if (currentPage < book.getPageCount() - 1) {
+ currentPage++;
+ displayCurrentPage();
+ }
+ });
+ previewButtonPanel.add(previousPageButton);
+ previewButtonPanel.add(nextPageButton);
this.add(previewButtonPanel, BorderLayout.SOUTH);
+
+ this.setBook(new Book());
+ }
+
+ private void displayCurrentPage() {
+ if (this.book.getPageCount() == 0) {
+ return;
+ }
+ BookPage currentPage = this.book.getPages().get(this.currentPage);
+ this.previewPageTextArea.setText(currentPage.toString());
+ this.titleLabel.setText("Book Preview (Page " + (this.currentPage + 1) + " of " + this.book.getPageCount() + ")");
+ }
+
+ public void setBook(Book book) {
+ this.book = book;
+ this.currentPage = 0;
+ this.displayCurrentPage();
+ }
+
+ public void setCurrentPage(int page) {
+ this.currentPage = page;
+ this.displayCurrentPage();
}
}
diff --git a/src/main/java/nl/andrewlalis/blockbookbinder/view/MainFrame.java b/src/main/java/nl/andrewlalis/blockbookbinder/view/MainFrame.java
index 16b7060..cd591ee 100644
--- a/src/main/java/nl/andrewlalis/blockbookbinder/view/MainFrame.java
+++ b/src/main/java/nl/andrewlalis/blockbookbinder/view/MainFrame.java
@@ -1,5 +1,6 @@
package nl.andrewlalis.blockbookbinder.view;
+import nl.andrewlalis.blockbookbinder.control.BookExportActionListener;
import nl.andrewlalis.blockbookbinder.control.ImportAction;
import nl.andrewlalis.blockbookbinder.util.ApplicationProperties;
@@ -56,17 +57,15 @@ public class MainFrame extends JFrame {
JPanel mainPanel = new JPanel(new BorderLayout());
JPanel doublePanel = new JPanel(new GridLayout(1, 2));
- doublePanel.add(new BookPreviewPanel());
- doublePanel.add(new SourceTextPanel());
+ BookPreviewPanel bookPreviewPanel = new BookPreviewPanel();
+ doublePanel.add(bookPreviewPanel);
+ SourceTextPanel sourceTextPanel = new SourceTextPanel(bookPreviewPanel);
+ doublePanel.add(sourceTextPanel);
mainPanel.add(doublePanel, BorderLayout.CENTER);
JPanel bottomPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
JButton exportButton = new JButton("Export to Book");
- exportButton.addActionListener(e -> {
- System.out.println("Starting export.");
-// final String fullText = mainTextArea.getText().trim();
-// Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(fullText.substring(0, 200)), null);
- });
+ exportButton.addActionListener(new BookExportActionListener(bookPreviewPanel));
bottomPanel.add(exportButton);
mainPanel.add(bottomPanel, BorderLayout.SOUTH);
diff --git a/src/main/java/nl/andrewlalis/blockbookbinder/view/SourceTextPanel.java b/src/main/java/nl/andrewlalis/blockbookbinder/view/SourceTextPanel.java
index df006ae..95ff0a8 100644
--- a/src/main/java/nl/andrewlalis/blockbookbinder/view/SourceTextPanel.java
+++ b/src/main/java/nl/andrewlalis/blockbookbinder/view/SourceTextPanel.java
@@ -1,5 +1,7 @@
package nl.andrewlalis.blockbookbinder.view;
+import nl.andrewlalis.blockbookbinder.control.ConvertToBookActionListener;
+
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
@@ -9,14 +11,18 @@ import java.awt.*;
* a book.
*/
public class SourceTextPanel extends JPanel {
- public SourceTextPanel() {
+ private final JTextArea textArea;
+
+ public SourceTextPanel(BookPreviewPanel bookPreviewPanel) {
super(new BorderLayout());
this.add(new JLabel("Source Text"), BorderLayout.NORTH);
this.setBorder(new EmptyBorder(5, 5, 5, 5));
- JTextArea mainTextArea = new JTextArea();
- JScrollPane scrollWrappedMainTextArea = new JScrollPane(mainTextArea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
+ this.textArea = new JTextArea();
+ this.textArea.setWrapStyleWord(true);
+ this.textArea.setLineWrap(true);
+ JScrollPane scrollWrappedMainTextArea = new JScrollPane(this.textArea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
this.add(scrollWrappedMainTextArea, BorderLayout.CENTER);
JPanel rightPanelButtonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
@@ -24,5 +30,13 @@ public class SourceTextPanel extends JPanel {
JButton importButton = new JButton("Import");
importButton.setActionCommand("importSource");
rightPanelButtonPanel.add(importButton);
+
+ JButton convertButton = new JButton("Convert to Book");
+ convertButton.addActionListener(new ConvertToBookActionListener(this, bookPreviewPanel));
+ rightPanelButtonPanel.add(convertButton);
+ }
+
+ public String getSourceText() {
+ return this.textArea.getText();
}
}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index c28913c..fd49145 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -7,3 +7,5 @@ frame.default_height=600
book.max_pages=100
book.page_max_lines=14
book.page_max_width=113
+book.page_max_chars=255
+book.default_char_width=5