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

com.sysalto.render.util.wrapper.WordWrap.scala Maven / Gradle / Ivy

The newest version!
package com.sysalto.render.util.wrapper

import com.sysalto.render.util.fonts.parsers.{FontParser, RFontParserFamily}
import com.sysalto.report.RFontAttribute
import com.sysalto.report.reportTypes.{RFont, ReportTxt}

import scala.collection.mutable
import scala.collection.mutable.ListBuffer


class WordWrap(fontFamilyMap: scala.collection.mutable.HashMap[String, RFontParserFamily])
              (implicit wordSeparators: List[Char]) {

  class RTextPos(val x: Float, val textLength: Float, val rtext: ReportTxt)

  class CharFN(val char: Char, val font: RFont)

  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 splitNewLines(input: List[ReportTxt]): Seq[Seq[ReportTxt]] = {
    val r1 = ListBuffer[List[ReportTxt]]()
    val crtLine = ListBuffer[ReportTxt]()
    input.foreach(reportTxt => {
      if (reportTxt.txt.contains("\n")) {
        val l1 = reportTxt.txt.split("\n").toList.map(txt => ReportTxt(txt, reportTxt.font))
        val lastItem = l1.last
        l1.foreach(item => {
          crtLine += item
          if (item != lastItem) {
            r1 += crtLine.toList
            crtLine.clear()
          }
        })
      } else {
        crtLine += reportTxt
      }
    })
    r1 += crtLine.toList
    r1.toSeq
  }

  private[this] def getCharWidth(char: CharFN): Float = {
    val map=new mutable.HashMap[CharFN,Float]()

    def calcWidth(char: CharFN) = {
      val font = char.font
      val fontParser: FontParser = getFontParser(font)
      fontParser.getCharWidth(char.char) * char.font.size
    }
    val result=map.get(char)
    if (result.isDefined) {
      result.get
    } else {
      val width= calcWidth(char)
      map.put(char,width)
      width
    }
  }


  private[this] def split(input: Seq[CharFN], max: Float): Seq[Seq[CharFN]] = {
    val result = ListBuffer[Seq[CharFN]]()
    val resultCrtLine = ListBuffer[CharFN]()
    var workingInput = ListBuffer[CharFN]()
    workingInput ++= input

    while (workingInput.nonEmpty) {
      val list = workingInput.map(charFN => (charFN, getCharWidth(charFN)))
      val idx1 = list.zipWithIndex.indexWhere {
        case ((charFN, width), index) => {
          list.take(index + 1).map(item => item._2).sum > max
        }
      }
      if (idx1 == -1) {
        // all items fit
        result += workingInput.clone().toSeq
        workingInput.clear()
      } else {
        val idx2 = workingInput.take(idx1 - 1).lastIndexWhere(charFN => wordSeparators.contains(charFN.char))
        if (idx2 >= 0) {
          // found
          resultCrtLine ++= workingInput.take(idx2 + 1)
          result += resultCrtLine.clone().toSeq
          resultCrtLine.clear()
          workingInput = workingInput.drop(idx2 + 1)
        } else {
          // cut the current word
          resultCrtLine ++= workingInput.take(idx1 - 1)
          result += resultCrtLine.clone.toSeq
          resultCrtLine.clear()
          workingInput = workingInput.drop(idx1 - 1)
        }
      }
    }
    result.toSeq
  }

  private[this] def convertToTextPos(input: Seq[CharFN]): Seq[RTextPos] = {
    val result = ListBuffer[RTextPos]()
    var position = 0f
    var textLength = 0f
    var currentFont: RFont = null
    var currentText = new StringBuilder()
    input.foreach(charFN => {
      if (currentFont == null || charFN.font == currentFont) {
        if (currentFont == null) {
          currentFont = charFN.font
        }
        currentText.append(charFN.char)
        textLength += getCharWidth(charFN)
      }
      if (charFN.font != currentFont) {
        result += new RTextPos(position, textLength, ReportTxt(currentText.toString(), currentFont))
        position += textLength
        currentText.clear()
        currentFont = charFN.font
        currentText.append(charFN.char)
        textLength += getCharWidth(charFN)
      }
    })
    result += new RTextPos(position, textLength, ReportTxt(currentText.toString(), currentFont))
    result.toSeq
  }

  private[this] def wrapLine(line: Seq[ReportTxt], max: Float): Seq[Seq[RTextPos]] = {
    val l1 = split(line.flatMap(item => item.txt.map(char => new CharFN(char, item.font))), max)
    l1.map(line => convertToTextPos(line))
  }


  def getTextWidth(text: ReportTxt): Float = {
    text.txt.map(char => getCharWidth(new CharFN(char, text.font))).sum
  }

  def wordWrap(input: List[ReportTxt], max: Float): List[List[RTextPos]] = {
    val l1 = splitNewLines(input)

    val result = ListBuffer[Seq[RTextPos]]()
    val resultCrtLine = ListBuffer[Seq[RTextPos]]()
    l1.foreach(line => {
      result ++= wrapLine(line, max)
    })
    result.map(item => item.toList).toList
  }
}







© 2015 - 2024 Weber Informatics LLC | Privacy Policy