130 lines
4.0 KiB
Java
130 lines
4.0 KiB
Java
package nl.andrewlalis.blockbookbinder.model.build;
|
|
|
|
import nl.andrewlalis.blockbookbinder.model.Book;
|
|
import nl.andrewlalis.blockbookbinder.model.BookPage;
|
|
import nl.andrewlalis.blockbookbinder.util.CharWidthMapper;
|
|
import nl.andrewlalis.blockbookbinder.util.ApplicationProperties;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
public class BookBuilder {
|
|
/**
|
|
* Builds a full book of pages from the given source text.
|
|
* @param source The source text to convert.
|
|
* @return A book containing the source text formatted for a minecraft book.
|
|
*/
|
|
public Book build(String source) {
|
|
final int maxLines = ApplicationProperties.getIntProp("book.page_max_lines");
|
|
List<String> lines = this.convertSourceToLines(this.cleanSource(source));
|
|
Book book = new Book();
|
|
BookPage page = new BookPage();
|
|
int currentPageLineCount = 0;
|
|
|
|
for (String line : lines) {
|
|
page.addLine(line);
|
|
currentPageLineCount++;
|
|
if (currentPageLineCount == maxLines) {
|
|
book.addPage(page);
|
|
page = new BookPage();
|
|
currentPageLineCount = 0;
|
|
}
|
|
}
|
|
|
|
if (page.hasContent()) {
|
|
book.addPage(page);
|
|
}
|
|
|
|
return book;
|
|
}
|
|
|
|
/**
|
|
* Cleans a given source text, by removing superfluous newlines that won't
|
|
* look too nice in minecraft text, and by erasing manually-imposed single
|
|
* newlines that may be in the source text to format it in a normal editor.
|
|
* @param source The source text.
|
|
* @return The cleaned string.
|
|
*/
|
|
public String cleanSource(String source) {
|
|
return source.trim()
|
|
.replaceAll("(?>\\v)+(\\v)", "\n\n") // Replace large chunks of newline with just two.
|
|
.replaceAll("\\S\n\\S", " ") // Unwrap previously-imposed single-line wrapping.
|
|
.replaceAll("\t", " ") // Replace tabs with single-spaces, due to space constraints.
|
|
.replaceAll(" [ ]+", " "); // Remove any superfluous spaces.
|
|
}
|
|
|
|
/**
|
|
* Converts the given source string into a formatted list of lines that can
|
|
* be copied to a minecraft book.
|
|
* @param source The source string.
|
|
* @return A list of lines.
|
|
*/
|
|
private List<String> convertSourceToLines(String source) {
|
|
List<String> lines = new ArrayList<>();
|
|
final char[] sourceChars = source.toCharArray();
|
|
final int maxLinePixelWidth = ApplicationProperties.getIntProp("book.page_max_width");
|
|
int sourceIndex = 0;
|
|
StringBuilder lineBuilder = new StringBuilder(64);
|
|
int linePixelWidth = 0;
|
|
StringBuilder symbolBuilder = new StringBuilder(64);
|
|
|
|
while (sourceIndex < sourceChars.length) {
|
|
final char c = sourceChars[sourceIndex];
|
|
sourceIndex++;
|
|
symbolBuilder.setLength(0);
|
|
symbolBuilder.append(c);
|
|
int symbolWidth = CharWidthMapper.getInstance().getWidth(c);
|
|
|
|
// Since there's a 1-pixel gap between characters, add it to the width if this isn't the first char.
|
|
if (lineBuilder.length() > 0) {
|
|
symbolWidth++;
|
|
}
|
|
|
|
// If we encounter a non-newline whitespace at the beginning of the line, skip it.
|
|
if (c == ' ' && lineBuilder.length() == 0) {
|
|
continue;
|
|
}
|
|
|
|
// If we encounter a newline, immediately skip to a new line.
|
|
if (c == '\n') {
|
|
lines.add(lineBuilder.toString());
|
|
lineBuilder.setLength(0);
|
|
linePixelWidth = 0;
|
|
continue;
|
|
}
|
|
|
|
// If we encounter a word, keep accepting characters until we reach the end.
|
|
if (Character.isLetterOrDigit(c)) {
|
|
while (
|
|
sourceIndex < sourceChars.length
|
|
&& Character.isLetterOrDigit(sourceChars[sourceIndex])
|
|
) {
|
|
char nextChar = sourceChars[sourceIndex];
|
|
symbolBuilder.append(nextChar);
|
|
symbolWidth += 1 + CharWidthMapper.getInstance().getWidth(nextChar);
|
|
sourceIndex++;
|
|
}
|
|
}
|
|
|
|
final String symbol = symbolBuilder.toString();
|
|
// Check if we need to go to the next line to fit the symbol.
|
|
if (linePixelWidth + symbolWidth > maxLinePixelWidth) {
|
|
lines.add(lineBuilder.toString());
|
|
lineBuilder.setLength(0);
|
|
linePixelWidth = 0;
|
|
}
|
|
|
|
// Finally, append the symbol.
|
|
lineBuilder.append(symbol);
|
|
linePixelWidth += symbolWidth;
|
|
}
|
|
|
|
// Append any remaining text.
|
|
if (lineBuilder.length() > 0) {
|
|
lines.add(lineBuilder.toString());
|
|
}
|
|
|
|
return lines;
|
|
}
|
|
}
|