aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/xyz/driver/pdsuicommon/pdf/WkHtmlToPdfRenderer.scala
blob: 8141b7f21f08c63527692a3463ab2e6be52c407f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package xyz.driver.pdsuicommon.pdf

import java.io.IOException
import java.nio.file._

import io.github.cloudify.scala.spdf._
import xyz.driver.pdsuicommon.logging._
import xyz.driver.pdsuicommon.pdf.WkHtmlToPdfRenderer.Settings

object WkHtmlToPdfRenderer {

  final case class Settings(downloadsDir: String) {

    lazy val downloadsPath: Path = getPathFrom(downloadsDir)

    private def getPathFrom(x: String): Path = {
      val dirPath =
        if (x.startsWith("/")) Paths.get(x)
        else {
          val workingDir = Paths.get(".")
          workingDir.resolve(x)
        }

      dirPath.toAbsolutePath.normalize()
    }
  }
}

class WkHtmlToPdfRenderer(settings: Settings) extends PdfRenderer with PhiLogging {

  private val pdf = Pdf(new PdfConfig {
    disableJavascript := true
    disableExternalLinks := true
    disableInternalLinks := true
    printMediaType := Some(true)
    orientation := Portrait
    pageSize := "A4"
    lowQuality := true
  })

  override def render(html: String, documentName: String, force: Boolean = false): Path = {
    checkedCreate(html, documentName, force)
  }

  override def delete(documentName: String): Unit = {
    logger.trace(phi"delete(${Unsafe(documentName)})")

    val file = getPath(documentName)
    logger.debug(phi"File: $file")
    if (Files.deleteIfExists(file)) {
      logger.info(phi"Deleted")
    } else {
      logger.warn(phi"Doesn't exist")
    }
  }

  override def getPath(documentName: String): Path = {
    settings.downloadsPath.resolve(s"$documentName.pdf").toAbsolutePath
  }

  protected def checkedCreate[SourceT: SourceDocumentLike](src: SourceT, fileName: String, force: Boolean): Path = {
    logger.trace(phi"checkedCreate(fileName=${Unsafe(fileName)}, force=$force)")

    val dest = getPath(fileName)
    logger.debug(phi"Destination file: $dest")

    if (force || !dest.toFile.exists()) {
      logger.trace(phi"Force refresh the file")
      val newDocPath = forceCreate(src, dest)
      logger.info(phi"Updated")
      newDocPath
    } else if (dest.toFile.exists()) {
      logger.trace(phi"Already exists")
      dest
    } else {
      logger.trace(phi"The file does not exist")
      val newDocPath = forceCreate(src, dest)
      logger.info(phi"Created")
      newDocPath
    }
  }

  protected def forceCreate[SourceT: SourceDocumentLike](src: SourceT, dest: Path): Path = {
    logger.trace(phi"forceCreate[${Unsafe(src.getClass.getName)}](dest=$dest)")

    val destTemp     = Files.createTempFile("driver", ".pdf")
    val destTempFile = destTemp.toFile

    Files.createDirectories(dest.getParent)

    val retCode      = pdf.run(src, destTempFile)
    lazy val pdfSize = destTempFile.length()
    if (retCode != 0) {
      // Try to google "wkhtmltopdf returns {retCode}"
      throw new IOException(s"Can create the document, the return code is $retCode")
    } else if (pdfSize == 0) {
      // Anything could happen, e.g. https://github.com/wkhtmltopdf/wkhtmltopdf/issues/2540
      throw new IOException("The pdf is empty")
    } else {
      logger.debug(phi"Size: ${Unsafe(pdfSize)}B")
      Files.move(destTemp, dest, StandardCopyOption.REPLACE_EXISTING)
      dest
    }
  }
}