Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* ReactiveReports - Free Java /Scala Reporting Library.
* Copyright (C) 2017 SysAlto Corporation. All rights reserved.
*
* This program is part of ReactiveReports.
*
* ReactiveReports is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ReactiveReports is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY. Without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with ReactiveReports.
* If not, see https://www.gnu.org/licenses/lgpl-3.0.en.html.
*/
package com.sysalto.render.util.wrapper
import com.sysalto.report.RFontAttribute
import com.sysalto.report.reportTypes.{RFont, ReportTxt}
import com.sysalto.render.util.fonts.parsers.{AfmParser, FontParser, RFontParserFamily, TtfParser}
import scala.annotation.tailrec
import scala.collection.mutable.ListBuffer
class WordWrap(fontFamilyMap: scala.collection.mutable.HashMap[String, RFontParserFamily]) {
private[this] type Cost[T] = (Double, List[T])
@tailrec
private[this] def calculate[T](widthList: Map[T, List[(Double, T)]], accumList: List[Cost[T]], dest: T,
visited: Set[T]): Cost[T] = accumList match {
case (dist, path) :: rest => path match {
case Nil => (0, List())
case key :: _ =>
if (key == dest) (dist, path.reverse)
else {
val paths = widthList(key).flatMap { case (d, key1) => if (!visited.contains(key1)) List((dist + d, key1 :: path)) else Nil }
val sorted = (paths ++ rest).sortWith { case ((d1, _), (d2, _)) => d1 < d2 }
calculate(widthList, sorted, dest, visited + key)
}
}
case Nil => (0, List())
}
class CharF(val char: Char,val font: RFont)
class Word(val charList: List[CharF])
@tailrec
private[this] def stringToWord(ll: List[CharF], accum: ListBuffer[Word]): Unit = {
if (!ll.exists(item => item.char == ' ')) {
accum += new Word(ll)
} else {
val l1 = ll.dropWhile(item => item.char == ' ')
val ll1 = l1.splitAt(l1.indexWhere(item => item.char == ' ') + 1)
val newWordList = ll1._1
val newWord = newWordList.take(newWordList.length - 1)
accum += new Word(newWord)
stringToWord(ll1._2, accum)
}
}
private[this] object FontType extends Enumeration {
val Afm, Ttf = Value
}
private[this] def getFontParser(font: RFont): FontParser = {
val fontFamily = fontFamilyMap(font.fontName)
font.attribute match {
case RFontAttribute.NORMAL => fontFamily.regular
case RFontAttribute.BOLD => fontFamily.bold.get
case RFontAttribute.ITALIC => fontFamily.italic.get
case RFontAttribute.BOLD_ITALIC => fontFamily.boldItalic.get
}
}
private[this] def getWordSizeIncludingSpace(word: Word): Float = {
if (word.charList.isEmpty) {
0f
} else {
val space = new CharF(' ', word.charList.head.font)
(space :: word.charList).foldLeft(0.toFloat)((total, char) => {
total + getCharWidth(char)
})
}
}
def getTextWidth(text: ReportTxt): Float = {
val word = new Word(text.txt.map(char => new CharF(char, text.font)).toList)
word.charList.foldLeft(0.toFloat)((total, char) => {
total + getCharWidth(char)
})
}
def getTextHeight(text: ReportTxt): Float = {
val word = new Word(text.txt.map(char =>new CharF(char, text.font)).toList)
word.charList.map(char => getCharWidth(char)).max
}
private[this] def getCharWidth(char: CharF): Float = {
val font = char.font
val fontParser: FontParser = getFontParser(font)
fontParser.getCharWidth(char.char) * char.font.size
}
private[this] def getCharHeight(char: CharF): Float = {
val font = char.font
val fontParser: FontParser = getFontParser(font)
fontParser.getCharHeight(char.char) * char.font.size
}
private[this] def splitAtMax(item: Word, max: Float): (Word, Word) = {
@tailrec
def getMaxStr(word: Word): Word = {
if (getWordSizeIncludingSpace(word) <= max) {
word
}
else {
getMaxStr(new Word(word.charList.dropRight(1)))
}
}
val maxStr = getMaxStr(item)
(maxStr, new Word(item.charList.drop(maxStr.charList.size)))
}
@tailrec
private[this] def splitWord(word: Word, max: Float, accum: ListBuffer[Word]): Unit = {
if (getWordSizeIncludingSpace(word) <= max) {
accum += word
} else {
val (part1, part2) = splitAtMax(word, max)
accum += part1
splitWord(part2, max, accum)
}
}
@tailrec
private[this] def wordToRTextPos(offset: Float, word: Word, accum: ListBuffer[RTextPos]): Unit = {
if (word.charList.isEmpty) {
return
}
if (word.charList.groupBy(char => char.font).size == 1) {
// one font ->keep it together
val str = word.charList.map(char => char.char)
val rtext = ReportTxt(str.mkString, word.charList(0).font)
accum += new RTextPos(offset, getWordSizeIncludingSpace(word), rtext)
} else {
val firstFont = word.charList(0).font
val i1 = word.charList.indexWhere(char => char.font != firstFont)
val word1 = new Word(word.charList.take(i1))
accum += new RTextPos(offset, getWordSizeIncludingSpace(word1), ReportTxt(word.charList.take(i1).map(char => char.char).mkString, firstFont))
val word2 = new Word(word.charList.drop(i1))
wordToRTextPos(offset + getWordSizeIncludingSpace(word1), word2, accum)
}
}
private[this] def lineToRTextPos(line: List[Word]): List[RTextPos] = {
val result1 = ListBuffer[RTextPos]()
var offset = 0f
line.foreach(word => {
wordToRTextPos(offset, word, result1)
offset += getWordSizeIncludingSpace(word)
})
// val result= ListBuffer[RTextPos]()
// mergeRTextPos(result1.toList,result)
val result = result1
if (!result.isEmpty && result.last.rtext.txt == " ") {
result.dropRight(1).toList
} else {
result.toList
}
}
private[this] def wordWrapInternal(input: List[ReportTxt], max: Float)(implicit wordSeparators: List[Char]): List[List[RTextPos]] = {
// function that calculate the size of a string including spaces
def size(l: List[Float], font: RFont): Float = {
l.sum
}
def calc(list: List[Float], i1: Int, i2: Int, font: RFont): Option[Float] = {
val l1 = list.slice(i1, i2 + 1).toList
val result = size(l1, font)
if (result <= max) {
val dif = max - result
Some(dif * dif)
} else {
None
}
}
val result1 = input.flatMap(item => item.txt.map(cc => new CharF(cc, item.font)))
val result = ListBuffer[Word]()
stringToWord(result1, result)
val wordList = result.flatMap(item => {
val result = ListBuffer[Word]()
splitWord(item, max, result)
result.toList
}).toList
val list = wordList.map(item => getWordSizeIncludingSpace(item))
val l1 = list.indices
val l2 = l1.combinations(2).map(item => (item.head, item.tail.head)) ++ l1.map(item => (item, item))
val mapCost = l2.map(item => item -> calc(list, item._1, item._2, input(0).font)).filter { case (key, value) => value.isDefined }.
map { case (key, value) => key -> value.get }.toMap
val rr = for {i <- list.length - 1 to 0 by -1
j <- list.length to i by -1
key = (i, j - 1) if mapCost.contains(key)
cost = mapCost(key)
}
yield (i, j, cost)
val rr1 = rr.map { case (a, b, c) => a }.toSet.toList
val rr2 = rr1.map(item => {
item -> rr.filter { case (a, b, c) => a == item }.map { case (a, b, c) => (c.toDouble, b) }.toList
}).toMap
val res = calculate[Int](rr2, List((0, List(0))), list.length, Set())
val indiceList = res._2
val lines = for (i <- 0 to indiceList.size - 2) yield {
wordList.slice(indiceList(i), indiceList(i + 1))
}
lines.map(line => lineToRTextPos(line)).toList
}
@tailrec
private[this] def wordWrapT(input: List[ReportTxt], max: Float, accum: ListBuffer[List[RTextPos]])(implicit wordSeparators: List[Char]): Unit = {
val i1 = input.indexWhere(item => item.txt.contains("\n"))
if (i1 == -1) {
accum ++= wordWrapInternal(input, max)
} else {
val l1 = input.take(i1)
val elem = input(i1)
val i2 = elem.txt.indexOf('\n')
val list1 = input.take(i1) ++ List(ReportTxt(elem.txt.substring(0, i2), elem.font))
val list2 = List(ReportTxt(elem.txt.substring(i2 + 1), elem.font)) ++ input.drop(i1 + 1)
accum ++= wordWrapInternal(list1, max)
wordWrapT(list2, max, accum)
}
}
def wordWrap(input: List[ReportTxt], max: Float)
(implicit wordSeparators: List[Char]): List[List[RTextPos]] = {
val result = ListBuffer[List[RTextPos]]()
wordWrapT(input, max, result)
result.toList
}
}
class RTextPos(val x: Float, val textLength: Float,val rtext: ReportTxt)