All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.camel.component.pdf.text.DefaultLineBuilderStrategy Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.camel.component.pdf.text;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;

import org.apache.camel.component.pdf.PdfConfiguration;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.pdmodel.font.Standard14Fonts;

import static org.apache.camel.component.pdf.PdfConstants.MIN_CONTENT_WIDTH;

/**
 * Builds lines from words based on line width and PDF document page size. Built lines then will be written to pdf
 * document.
 */
public class DefaultLineBuilderStrategy implements LineBuilderStrategy {
    private final PdfConfiguration pdfConfiguration;

    public DefaultLineBuilderStrategy(PdfConfiguration pdfConfiguration) {
        this.pdfConfiguration = pdfConfiguration;
    }

    /**
     * Builds lines from words. Utilizes the same behaviour as office software:
     * 
    *
  • If word doesn't fit in current line, and current lines contains other words, then it will be moved to new * line. *
  • Word doesn't fit in the line and line does not contain other words, then word will be slitted, and split * index will be on max amount of characters that fits in the line
  • *
*/ @Override public Collection buildLines(Collection splittedText) throws IOException { LinkedList wordsList = new LinkedList<>(splittedText); Collection lines = new ArrayList<>(); LineBuilder currentLine = new LineBuilder(); float allowedLineWidth = getAllowedLineWidth(); while (!wordsList.isEmpty()) { String word = wordsList.removeFirst(); if (isWordFitInCurrentLine(currentLine, word, allowedLineWidth)) { currentLine.appendWord(word); if (wordsList.isEmpty()) { lines.add(currentLine.buildLine()); } } else if (currentLine.getWordsCount() != 0) { lines.add(currentLine.buildLine()); wordsList.addFirst(word); } else { int splitIndex = findSplitIndex(word, allowedLineWidth); currentLine.appendWord(word.substring(0, splitIndex)); lines.add(currentLine.buildLine()); wordsList.addFirst(word.substring(splitIndex)); } } return lines; } private int findSplitIndex(String word, float allowedLineWidth) throws IOException { // Using binary search algorithm to find max amount of characters that fit int the line. int middle = word.length() >> 1; int end = word.length(); int currentSplitIndex = 0; do { if (isLineFitInLineWidth(word.substring(0, middle), allowedLineWidth)) { currentSplitIndex = middle; middle += word.substring(middle, end).length() >> 1; } else { end = middle; middle = currentSplitIndex + (word.substring(currentSplitIndex, middle).length() >> 1); } } while (currentSplitIndex == -1 || !isSplitIndexFound(word, allowedLineWidth, currentSplitIndex)); return currentSplitIndex; } private boolean isSplitIndexFound(String word, float allowedLineWidth, int currentSplitIndex) throws IOException { return isLineFitInLineWidth(word.substring(0, currentSplitIndex), allowedLineWidth) && !isLineFitInLineWidth(word.substring(0, currentSplitIndex + 1), allowedLineWidth); } private boolean isWordFitInCurrentLine(LineBuilder currentLine, String word, float allowedLineWidth) throws IOException { LineBuilder lineBuilder = currentLine.clone().appendWord(word); return isLineFitInLineWidth(lineBuilder.buildLine(), allowedLineWidth); } private boolean isLineFitInLineWidth(String currentLine, float allowedLineWidth) throws IOException { float fontWidth = PdfUtils.getFontWidth( currentLine, new PDType1Font(Standard14Fonts.FontName.valueOf(pdfConfiguration.getFont())), pdfConfiguration.getFontSize()); return fontWidth <= allowedLineWidth; } public float getAllowedLineWidth() { float result = pdfConfiguration.getPageSize().getWidth() - pdfConfiguration.getMarginLeft() - pdfConfiguration.getMarginRight(); if (result < MIN_CONTENT_WIDTH) { throw new IllegalStateException( String.format("Allowed line width cannot be < %d, make sure" + " (marginLeft + marginRight) < pageSize", MIN_CONTENT_WIDTH)); } return result; } private static final class LineBuilder { private StringBuilder line = new StringBuilder(); private int wordsCount; LineBuilder() { } LineBuilder(String line, int wordsCount) { this.line = new StringBuilder(line); this.wordsCount = wordsCount; } public LineBuilder appendWord(String word) { line.append(word).append(" "); wordsCount++; return this; } public String buildLine() { String savedLine = this.line.toString(); reset(); return savedLine; } public int getWordsCount() { return wordsCount; } private void reset() { line = new StringBuilder(); wordsCount = 0; } @Override public LineBuilder clone() { return new LineBuilder(this.line.toString(), this.wordsCount); } @Override public String toString() { return ToStringBuilder.reflectionToString(this); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy