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

com.sysalto.render.serialization.RenderReport.scala Maven / Gradle / Ivy

The newest version!
package com.sysalto.render.serialization

import java.security.MessageDigest

import com.sysalto.render.PdfDraw._
import com.sysalto.render.util.PageTree
import com.sysalto.render.util.fonts.parsers.{FontParser, RFontParserFamily}
import com.sysalto.render.util.wrapper.WordWrap
import com.sysalto.report.ReportTypes.{BoundaryRect, WrapBox}
import com.sysalto.report.{RFontAttribute, ReportTypes, WrapAlign}
import com.sysalto.report.reportTypes._
import com.sysalto.report.util.PersistenceFactory

import scala.collection.mutable
import scala.collection.mutable.ListBuffer
import RenderReportTypes._

class RenderReport(name: String, PAGE_WIDTH: Float, PAGE_HEIGHT: Float, persistenceFactory: PersistenceFactory, pdfCompression: Boolean) {
  implicit val wordSeparators: List[Char] = List(',', '.', ' ')
  private[this] val fontFamilyMap = scala.collection.mutable.HashMap.empty[String, RFontParserFamily]
  private[this] val wordWrap = new WordWrap(fontFamilyMap)
  implicit val persistenceUtil = persistenceFactory.open()
  ImageStore(persistenceFactory)
  private[this] val pdfWriter = new PdfWriter(name)
  private[this] var id: Long = 0
  private[this] var catalog: PdfCatalog = null
  private[this] var currentPage: PdfPage = null
  private[this] var fontMap = scala.collection.mutable.HashMap.empty[String, PdfFont]
  private[this] val txtList = ListBuffer[PdfTxtFragment]()
  private[this] val graphicList = ListBuffer[PdfGraphicFragment]()
  private[this] val pageList = ListBuffer[PdfPage]()
  private[this] var fontId: Long = 0

  private[this] implicit val allItems = mutable.HashMap[Long, PdfBaseItem]()


  def startPdf(): Unit = {
    pdfWriter <<< "%PDF-1.7"
    pdfWriter <<< s"%${128.toChar}${129.toChar}${130.toChar}${131.toChar}"
    catalog = new PdfCatalog(nextId())
    setObject(catalog)
    currentPage = new PdfPage(nextId(), 0, PAGE_WIDTH, PAGE_HEIGHT)
  }

  def newPage(): Unit = {
    saveCurrentPage()
    currentPage = new PdfPage(nextId(), 0, PAGE_WIDTH, PAGE_HEIGHT, fontMap.values.map(fontItem => fontItem.id.asInstanceOf[java.lang.Long]).toList)
  }

  private[this] def saveCurrentPage(): Unit = {
    graphicList.foreach(item => {
      item.updateContent(this)
    })
    val text = new PdfText(txtList.toList, fontMap.toMap)
    val graphic = new PdfGraphic(graphicList.toList)
    val pdfPageContent = new PdfPageContent(nextId(), List(graphic, text), pdfCompression)
    setObject(pdfPageContent)
    currentPage.idContentPageOpt = Some(pdfPageContent.id)
    currentPage.idFontList = fontMap.values.toList.sortBy(font => font.refName).map(font => font.id.asInstanceOf[java.lang.Long])
    setObject(currentPage)
    pageList += currentPage
    txtList.clear()
    graphicList.clear()
  }

  def metaData(): (Long, Long) = {
    val id = nextId()
    val s =
      s"""${id} 0 obj
				 				 |  <<  /Producer (Reactive Reports - Copyright 2018 SysAlto Corporation)
				 				 |  >>
				 				 |endobj
				 				 |""".stripMargin.getBytes(ENCODING)
    val offset = pdfWriter.position
    pdfWriter << s
    (id, offset)
  }

  def md5(s: String) = {
    val result = MessageDigest.getInstance("MD5").digest(s.getBytes(ENCODING))
    result.map(item => item.toHexString).mkString
  }


  def done(): Unit = {
    saveCurrentPage()

    val pageTreeList = PageTree.pageTree(pageList.toList) {
      () => {
        val pg = new PdfPageList(nextId())
        setObject(pg)
        pg
      }
    }.asInstanceOf[PdfPageList]
    catalog.idPdfPageListOpt = Some(pageTreeList.id)
    setObject(catalog)
    val allItems1 = getAllItems
    // set fontMap
    fontMap.foreach { case (fontName, pdfFont) => {
      val charSet = pdfFont.charSet
      if (pdfFont.embeddedDefOpt.isDefined) {
        val embeddedDef = pdfFont.embeddedDefOpt.get
        val idPdfFontStream = embeddedDef.idPdfFontStream
        val pdfFontStream = getObject[PdfFontStream](idPdfFontStream)
        val fontFamily1=pdfFont.font.externalFont.get
        val fontFamily = fontFamilyMap(fontFamily1.name)
        val fontParser = fontFamily.regular
        fontParser.charList = charSet.toList.map(char => char.toInt)

        pdfFontStream.fontMetric = fontParser.getFontMetric()
        setObject(pdfFontStream)
        val pdfFontDescriptor = getObject[PdfFontDescriptor](embeddedDef.idPdfFontDescriptor)
        val toUnicode = getObject[ToUnicode](pdfFontDescriptor.idToUnicode)
        toUnicode.glyphNbr = pdfFontStream.fontMetric.fontGlyphNbr
        setObject(toUnicode)

        val glyphWith = pdfFontStream.fontMetric.fontGlyphNbr.map {
          case (char, glyphNbr) => glyphNbr.asInstanceOf[Integer] -> pdfFontStream.fontMetric.fontWidth(char).toDouble
        }
        val descendantFonts = getObject[DescendantFonts](pdfFont.idDescendantFonts)
        descendantFonts.glyphWidth = glyphWith
        setObject(descendantFonts)
      }
    }
    }
    allItems1.foreach(itemId => {
      val item = getObject[PdfBaseItem](itemId)
      item.persistenceUtil = persistenceUtil
      item.write(pdfWriter)
    })
    val metaDataObj = metaData()
    val metaDataId = metaDataObj._1
    val xrefOffset = pdfWriter.position
    pdfWriter <<< "xref"

    pdfWriter <<< s"0 ${allItems1.length + 2}"
    pdfWriter <<< "0000000000 65535 f "
    allItems1.foreach(itemId => {
      val item = getObject[PdfBaseItem](itemId)
      val offset = item.offset.toString
      val offsetFrmt = "0" * (10 - offset.length) + offset
      pdfWriter <<< s"${offsetFrmt} 00000 n "
    })
    val metaOffset = metaDataObj._2.toString
    val offsetFrmt = "0" * (10 - metaOffset.length) + metaOffset
    pdfWriter <<< s"${offsetFrmt} 00000 n "
    pdfWriter <<< "trailer"
    pdfWriter <<< s"<<${fileId}>]"
    pdfWriter <<< ">>"
    pdfWriter <<< "startxref"
    pdfWriter <<< xrefOffset.toString
    pdfWriter <<< "%%EOF"
  }

  private[serialization] def nextId(): Long = {
    id += 1
    id
  }

  def nextFontId(): String = {
    fontId += 1
    "F" + fontId
  }

  def setExternalFont(externalFont: RFontFamily): Unit = {
    fontFamilyMap += externalFont.name -> new RFontParserFamily(externalFont.name, externalFont, false)
  }

  private[this] def initEmbeddedFonts(): Unit = {
    initEmbeddedFont(RFontFamily("Courier", "Courier", Some("Courier-Bold"), Some("Courier-Oblique"), Some("Courier-BoldOblique")))
    initEmbeddedFont(RFontFamily("Helvetica", "Helvetica", Some("Helvetica-Bold"), Some("Helvetica-Oblique"), Some("Helvetica-BoldOblique")))
    initEmbeddedFont(RFontFamily("Times", "Times-Roman", Some("Times-Bold"), Some("Times-Italic"), Some("Times-BoldItalic")))
  }

  private[this] def initEmbeddedFont(rFontFamily: RFontFamily): Unit = {
    fontFamilyMap += rFontFamily.name -> new RFontParserFamily(rFontFamily.name, rFontFamily, true)
  }

  def close(): Unit = {
    pdfWriter.close()
    RenderReportTypes.close
  }


  def line(x1: Float, y1: Float, x2: Float, y2: Float, lineWidth: Float, color: ReportColor, lineDashType: Option[LineDashType]): Unit = {
    graphicList += new DrawLine(x1, y1, x2, y2, lineWidth, color, lineDashType)
  }

  def rectangle(x1: Float, y1: Float, x2: Float, y2: Float,
                radius: Float, color: Option[ReportColor] = None,
                fillColor: Option[ReportColor] = None, idPaternColor: Option[Long] = None): Unit = {
    graphicList += new PdfRectangle(x1.toLong, y1.toLong, x2.toLong, y2.toLong, radius, color, fillColor, idPaternColor)
  }

  def directDrawMovePoint(x: Float, y: Float): Unit = {
    graphicList += new DirectDrawMovePoint(x, y)
  }

  def directDrawLine(x: Float, y: Float): Unit = {
    graphicList += new DirectDrawLine(x, y)
  }

  def directDraw(code: String): Unit = {
    graphicList += new DirectDraw(code)
  }

  def directDrawCircle(x: Float, y: Float, radius: Float): Unit = {
    graphicList += new DirectDrawCircle(x, y, radius)
  }

  def directDrawArc(x: Float, y: Float, radius: Float, startAngle: Float, endAngle: Float): Unit = {
    graphicList += new DirectDrawArc(x, y, radius, startAngle, endAngle)
  }

  def directDrawStroke(reportColor: ReportColor): Unit = {
    graphicList += new DirectDrawStroke(reportColor)
  }

  def directFillStroke(fill: Boolean, stroke: Boolean): Unit = {
    graphicList += new DirectFillStroke(fill, stroke)
  }

  def directDrawRectangle(x1: Float, y1: Float, x2: Float, y2: Float): Unit = {
    graphicList += new DirectDrawRectangle(x1, y1, x2, y2)
  }


  def directDrawFill(reportColor: ReportColor): Unit = {
    graphicList += new DirectDrawFill(reportColor)
  }

  def directDrawClosePath(): Unit = {
    graphicList += new DirectDrawClosePath()
  }


  def arc(center: DrawPoint, radius: Float, startAngle: Float, endAngle: Float): Unit = {
    graphicList += new DrawArc(center, radius, startAngle, endAngle)
  }

  def circle(center: DrawPoint, radius: Float): Unit = {
    graphicList += new DrawCircle(center, radius)
  }

  def stroke() = {
    graphicList += new DrawStroke()
  }

  def wrap(txtList: List[ReportTxt], x0: Float, y0: Float, x1: Float, y1: Float,
           wrapAlign: WrapAlign.Value, simulate: Boolean, lineHeight: Float): Option[ReportTypes.WrapBox] = {

    val lines = wordWrap.wordWrap(txtList, x1 - x0)
    var crtY = y0
    if (!simulate) {
      lines.foreach(line => {
        val l1: List[Float] = line.map(item => item.textLength)
        val length = l1.sum
        val newX = wrapAlign match {
          case WrapAlign.WRAP_CENTER => x0 + (x1 - x0 - length) * 0.5f
          case WrapAlign.WRAP_RIGHT => x1 - length
          case _ => x0
        }
        line.zipWithIndex.foreach {
          case (textPos, index) => {
            val offset = line.take(index).map(item => item.textLength).sum
            text(newX + offset, crtY, textPos.rtext)
          }
        }
        crtY -= lineHeight
      })
    } else {
      crtY -= lineHeight * (lines.size - 1)
    }
    val textHeight = 0 //if (l1.isEmpty) 0 else l1.max
    Some(new WrapBox(PAGE_HEIGHT - y0, PAGE_HEIGHT - crtY, lines.size, textHeight))
  }


  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
    }
  }


  def getTextWidth(txt: ReportTxt): Float = wordWrap.getTextWidth(txt)

  def getTextWidth(cell: ReportCell): List[Float] = {
    val lines = wordWrap.wordWrap(cell.txt, cell.margin.right - cell.margin.left)
    lines.map(line => {
      val lastWord = line.last
      line.map(word => word.textLength - (if (word == lastWord) wordWrap.getTextWidth(ReportTxt(" ", word.rtext.font)) else 0)).sum
    })
  }

  def axialShade(x1: Float, y1: Float, x2: Float, y2: Float, rectangle: ReportTypes.DRectangle, from: ReportColor, to: ReportColor): Unit = {

    val colorFct = new PdfShaddingFctColor(nextId(), from, to)
    setObject(colorFct)
    val pdfShadding = new PdfColorShadding(nextId(), x1, y1, x1, y2, colorFct.id)
    setObject(pdfShadding)
    val pattern = new PdfGPattern(nextId(), pdfShadding.id)
    setObject(pattern)
    currentPage.idPdfPatternList ++= List(pattern.id.asInstanceOf[java.lang.Long])
    setObject(currentPage)
    this.rectangle(rectangle.x1, rectangle.y1, rectangle.x2, rectangle.y2, 0, None, None, Some(pattern.id))
    this.stroke()
  }


  def drawImage(file: String, x: Float, y: Float, width: Float, height: Float, opacity: Float): Unit = {
    val pdfImage = ImageStore.getPdfImage(file, nextId())
    setObject(pdfImage)
    val scale = Math.min(width / pdfImage.imageMeta.width, height / pdfImage.imageMeta.height)
    val pdfDrawImage = new PdfDrawImage(pdfImage.id, x, y, scale)
    pdfDrawImage.persistenceUtil = persistenceUtil
    graphicList += pdfDrawImage
    currentPage.idImageList += pdfImage.id
  }


  def text(x: Float, y: Float, txt: ReportTxt): Unit = {
    val font = if (!fontMap.contains(txt.font.fontKeyName)) {
      if (txt.font.externalFont.isDefined) {
        val fontParser = getFontParser(txt.font)
        val fontMetric = fontParser.getFontMetric()
        val fontStream = new PdfFontStream(nextId(), fontParser.fontName, fontMetric, pdfCompression)
        setObject(fontStream)
        val toUnicode = new ToUnicode(nextId(), fontMetric.fontGlyphNbr)
        setObject(toUnicode)
        val fontDescr = new PdfFontDescriptor(nextId(), fontStream.id, toUnicode.id, txt.font.fontKeyName)
        setObject(fontDescr)
        val glyphWith = fontMetric.fontGlyphNbr.map { case (char, glyphNbr) => glyphNbr.asInstanceOf[Integer] -> fontMetric.fontWidth(char).toDouble }
        val descendantFonts = new DescendantFonts(nextId(), fontDescr.id, glyphWith)
        setObject(descendantFonts)
        val font1 = new PdfFont(nextId(), nextFontId(), txt.font,
          Some(new FontEmbeddedDef(fontDescr.id, fontStream.id)), descendantFonts.id)
        setObject(font1)
        fontMap += (txt.font.fontKeyName -> font1)
        font1
      } else {
        val font1 = new PdfFont(nextId(), nextFontId(), txt.font)
        setObject(font1)
        fontMap += (txt.font.fontKeyName -> font1)
        font1
      }
    }
    else fontMap(txt.font.fontKeyName)
    txtList += new PdfTxtFragment(x, y, txt, font.refName)
    if (txt.font.externalFont.isDefined) {
      fontMap(txt.font.fontKeyName).charSet ++= txt.txt.toCharArray
    }
  }


  def linkToPage(boundaryRect: BoundaryRect, pageNbr: Long, left: Int, top: Int): Unit = {
    val goto = new PdfGoToPage(nextId(), pageNbr, left, top)
    setObject(goto)
    val pdfLink = new PdfLink(nextId(), boundaryRect, goto.id)
    setObject(pdfLink)
    currentPage.idAnnotationList = currentPage.idAnnotationList.map(item => item.asInstanceOf[java.lang.Long]) ::: List(pdfLink.id.asInstanceOf[java.lang.Long])
  }

  def linkToUrl(boundaryRect: BoundaryRect, url: String): Unit = {
    val goto = new PdfGoToUrl(nextId(), url)
    setObject(goto)
    val pdfLink = new PdfLink(nextId(), boundaryRect, goto.id)
    setObject(pdfLink)
    currentPage.idAnnotationList = currentPage.idAnnotationList.map(item => item.asInstanceOf[java.lang.Long]) ::: List(pdfLink.id.asInstanceOf[java.lang.Long])
  }


  initEmbeddedFonts()

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy