Skip to content

Commit

Permalink
Created first version of wkhtmltoimage
Browse files Browse the repository at this point in the history
  • Loading branch information
AmazingDreams committed Aug 17, 2017
1 parent ac3f7f3 commit 06e8323
Show file tree
Hide file tree
Showing 3 changed files with 227 additions and 0 deletions.
64 changes: 64 additions & 0 deletions src/main/scala/io/github/cloudify/scala.spdf/Image.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package io.github.cloudify.scala.spdf

import scala.sys.process._
import java.io.File

class Image(executablePath: String, config: ImageConfig) {
validateExecutable_!(executablePath)

/**
* Runs the conversion tool to convert sourceDocument HTML into
* destinationDocument image.
*/
def run[A, B](sourceDocument: A, destinationDocument: B)(implicit sourceDocumentLike: SourceDocumentLike[A], destinationDocumentLike: DestinationDocumentLike[B]): Int = {
val commandLine = toCommandLine(sourceDocument, destinationDocument)
val process = Process(commandLine)
def source = sourceDocumentLike.sourceFrom(sourceDocument) _
def sink = destinationDocumentLike.sinkTo(destinationDocument) _

(sink compose source)(process).!
}

/**
* Generates the command line needed to execute `wkhtmltoimage`
*/
private def toCommandLine[A: SourceDocumentLike, B: DestinationDocumentLike](source: A, destination: B): Seq[String] =
Seq(executablePath) ++
ImageConfig.toParameters(config) ++
Seq(
implicitly[SourceDocumentLike[A]].commandParameter(source),
implicitly[DestinationDocumentLike[B]].commandParameter(destination)
)

/**
* Check whether the executable is actually executable, if it isn't
* a NoExecutableException is thrown.
*/
private def validateExecutable_!(executablePath: String): Unit = {
val executableFile = new File(executablePath)
if(!executableFile.canExecute) throw new NoExecutableException(executableFile.getAbsolutePath)
}

}

object Image {

/**
* Creates a new instance of Image with default configuration
* @return
*/
def apply(config: ImageConfig): Image = {
val executablePath: String = ImageConfig.findExecutable.getOrElse {
throw new NoExecutableException(System.getenv("PATH"))
}

apply(executablePath, config)
}

/**
* Creates a new instance of Image with the passed configuration
*/
def apply(executablePath: String, config: ImageConfig): Image =
new Image(executablePath, config)

}
112 changes: 112 additions & 0 deletions src/main/scala/io/github/cloudify/scala.spdf/ImageConfig.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package io.github.cloudify.scala.spdf

import scala.sys.process._
import ParamShow._

/**
* Holds the configuration parameters of Pdf Kit
*/
trait ImageConfig extends PdfConfig

object ImageConfig {

/**
* An instance of the default configuration
*/
object default extends ImageConfig

/**
* Generates a sequence of command line parameters from a `PdfKitConfig`
*/
def toParameters(config: ImageConfig): Seq[String] = {
import config._
Seq(
allow.toParameter,
background.toParameter,
defaultHeader.toParameter,
disableExternalLinks.toParameter,
disableInternalLinks.toParameter,
disableJavascript.toParameter,
noPdfCompression.toParameter,
disableSmartShrinking.toParameter,
javascriptDelay.toParameter,
enableForms.toParameter,
encoding.toParameter,
footerCenter.toParameter,
footerFontName.toParameter,
footerFontSize.toParameter,
footerHtml.toParameter,
footerLeft.toParameter,
footerLine.toParameter,
footerRight.toParameter,
footerSpacing.toParameter,
grayScale.toParameter,
headerCenter.toParameter,
headerFontName.toParameter,
headerFontSize.toParameter,
headerHtml.toParameter,
headerLeft.toParameter,
headerLine.toParameter,
headerRight.toParameter,
headerSpacing.toParameter,
lowQuality.toParameter,
marginBottom.toParameter,
marginLeft.toParameter,
marginRight.toParameter,
marginTop.toParameter,
minimumFontSize.toParameter,
orientation.toParameter,
outline.toParameter,
outlineDepth.toParameter,
pageHeight.toParameter,
pageOffset.toParameter,
pageSize.toParameter,
pageWidth.toParameter,
password.toParameter,
printMediaType.toParameter,
tableOfContent.toParameter,
tableOfContentDepth.toParameter,
tableOfContentDisableBackLinks.toParameter,
tableOfContentDisableLinks.toParameter,
tableOfContentFontName.toParameter,
tableOfContentHeaderFontName.toParameter,
tableOfContentHeaderFontSize.toParameter,
tableOfContentHeaderText.toParameter,
tableOfContentLevel1FontSize.toParameter,
tableOfContentLevel1Indentation.toParameter,
tableOfContentLevel2FontSize.toParameter,
tableOfContentLevel2Indentation.toParameter,
tableOfContentLevel3FontSize.toParameter,
tableOfContentLevel3Indentation.toParameter,
tableOfContentLevel4FontSize.toParameter,
tableOfContentLevel4Indentation.toParameter,
tableOfContentLevel5FontSize.toParameter,
tableOfContentLevel5Indentation.toParameter,
tableOfContentLevel6FontSize.toParameter,
tableOfContentLevel6Indentation.toParameter,
tableOfContentLevel7FontSize.toParameter,
tableOfContentLevel7Indentation.toParameter,
tableOfContentNoDots.toParameter,
title.toParameter,
userStyleSheet.toParameter,
username.toParameter,
useXServer.toParameter,
viewportSize.toParameter,
zoom.toParameter
).flatten
}

/**
* Attempts to find the `wkhtmltoimage` executable in the system path.
* @return
*/
def findExecutable: Option[String] = try {
val os = System.getProperty("os.name").toLowerCase
val cmd = if(os.contains("windows")) "where wkhtmltoimage" else "which wkhtmltoimage"

Option(cmd.!!.trim).filter(_.nonEmpty)
} catch {
case _: RuntimeException => None
}

}
51 changes: 51 additions & 0 deletions src/test/scala/io/github/cloudify/scala/spdf/ImageSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package io.github.cloudify.scala.spdf

import java.io.File
import scala.sys.process._
import org.scalatest.Matchers
import org.scalatest.WordSpec

class ImageSpec extends WordSpec with Matchers {

"An Image" should {

"require the executionPath config" in {
val file = new File("notexecutable")
val filePath = file.getAbsolutePath

assertThrows[NoExecutableException] {
new Image(filePath, ImageConfig.default)
}

assertThrows[NoExecutableException] {
Image(filePath, ImageConfig.default)
}

}

PdfConfig.findExecutable match {
case Some(_) =>
"generate an image file from an HTML string" in {

val page =
"""
|<html><body><h1>Hello</h1></body></html>
""".stripMargin

val file = File.createTempFile("scala.spdf", "pdf")

val image = Image(ImageConfig.default)

image.run(page, file)

Seq("file", file.getAbsolutePath).!! should include("PDF document")
}

case None =>
"Skipping test, missing wkhtmltopdf binary" in { true should equal(true) }
}


}

}

0 comments on commit 06e8323

Please sign in to comment.