Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=0.13.13
sbt.version=1.0.2
6 changes: 3 additions & 3 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "1.1")
addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "2.0")

addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0")
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.0")

addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.3")
addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.6")
65 changes: 65 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,65 @@
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(
"--quiet",
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)

}
187 changes: 187 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,187 @@
package io.github.cloudify.scala.spdf

import scala.sys.process._

/**
* Holds the configuration parameters of Pdf Kit
*/
trait ImageConfig {

/**
* Options for `wkhtmltoimage` command
* See `wkhtmltoimage --extended-help` for a description of each option
*/

val allow = Parameter[Iterable[String]]("allow")

val bypassProxy = Parameter[Iterable[String]]("bypass-proxy-for")

val cacheDir = Parameter[String]("cache-dir")

val checkboxCheckedSvg = Parameter[String]("checkbox-checked-svg")

val checkboxSvg = Parameter[String]("checkbox-svg")

val cookie = Parameter[Iterable[String]]("cookie")

val cookieJar = Parameter[String]("cookieJar")

val cropH = Parameter[Int]("crop-h")

val cropW = Parameter[Int]("crop-w")

val cropX = Parameter[Int]("crop-x")

val cropY = Parameter[Int]("crop-y")

val customHeader = Parameter[Iterable[String]]("custom-header")

val customHeaderPropagation = Parameter[Boolean]("custom-header-propagation")

val noCustomHeaderPropagation = Parameter[Boolean]("no-custom-header-propagation")

val debugJavascript = Parameter[Boolean]("debug-javascript")

val noDebugJavascript = Parameter[Boolean]("no-debug-javascript")

val encoding = Parameter[String]("encoding")

val format = Parameter[String]("format")

val height = Parameter[Int]("height")

val images = Parameter[Boolean]("images")

val noImages = Parameter[Boolean]("no-images")

val disableJavascript = Parameter[Boolean]("disable-javascript")

val enableJavascript = Parameter[Boolean]("enable-javascript")

val javascriptDelay = Parameter[Int]("javascript-delay")

val loadErrorHandling = Parameter[String]("load-error-handling")

val loadMediaErrorHandling = Parameter[String]("load-media-error-handling")

val disableLocalFileAccess = Parameter[Boolean]("disable-local-file-access")

val enableLocalFileAccess = Parameter[Boolean]("enable-local-file-access")

val minimumFontSize = Parameter[Int]("minimum-font-size")

val password = Parameter[String]("password")

val disablePlugins = Parameter[Boolean]("disable-plugins")

val enablePlugins = Parameter[Boolean]("enable-plugins")

val post = Parameter[Iterable[String]]("post")

val postFile = Parameter[String]("post-file")

val proxy = Parameter[String]("proxy")

val quality = Parameter[Int]("quality")

val quiet = Parameter[Boolean]("quiet")

val radioButtonCheckedSvg = Parameter[String]("radiobuttion-checked-svg")

val radioButtonSvg = Parameter[String]("radiobuttion-svg")

val runScript = Parameter[String]("run-script")

val stopSlowScripts = Parameter[Boolean]("stop-slow-scripts")

val noStopSlowScripts = Parameter[Boolean]("no-stop-slow-scripts")

val userStyleSheet = Parameter[String]("user-style-sheet")

val username = Parameter[String]("username")

val width = Parameter[Int]("width")

val windowStatus = Parameter[String]("window-status")

val zoom = Parameter[Float]("zoom")

}

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,
bypassProxy.toParameter,
cacheDir.toParameter,
checkboxCheckedSvg.toParameter,
checkboxSvg.toParameter,
cookie.toParameter,
cookieJar.toParameter,
cropH.toParameter,
cropW.toParameter,
cropX.toParameter,
cropY.toParameter,
customHeader.toParameter,
customHeaderPropagation.toParameter,
noCustomHeaderPropagation.toParameter,
debugJavascript.toParameter,
noDebugJavascript.toParameter,
encoding.toParameter,
format.toParameter,
height.toParameter,
images.toParameter,
noImages.toParameter,
disableJavascript.toParameter,
enableJavascript.toParameter,
javascriptDelay.toParameter,
loadErrorHandling.toParameter,
loadMediaErrorHandling.toParameter,
disableLocalFileAccess.toParameter,
enableLocalFileAccess.toParameter,
minimumFontSize.toParameter,
password.toParameter,
disablePlugins.toParameter,
enablePlugins.toParameter,
post.toParameter,
postFile.toParameter,
proxy.toParameter,
quality.toParameter,
quiet.toParameter,
radioButtonCheckedSvg.toParameter,
radioButtonSvg.toParameter,
runScript.toParameter,
stopSlowScripts.toParameter,
noStopSlowScripts.toParameter,
userStyleSheet.toParameter,
username.toParameter,
width.toParameter,
windowStatus.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
}

}
8 changes: 7 additions & 1 deletion src/main/scala/io/github/cloudify/scala.spdf/ParamShow.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package io.github.cloudify.scala.spdf

import java.text.{DecimalFormat, DecimalFormatSymbols}
import java.util.{Formatter, Locale}

/**
* Renders a parameter of type T to a sequence of strings that will be
* appended to the command line.
Expand Down Expand Up @@ -39,8 +42,11 @@ object ParamShow {
}

implicit object FloatParamShow extends ParamShow[Float] {
val dformat: DecimalFormatSymbols = DecimalFormatSymbols.getInstance()
dformat.setDecimalSeparator('.')

override def show(name: String, value: Float): Iterable[String] =
formatParam(name, Some("%.2f".format(value)))
formatParam(name, Some(new DecimalFormat("0.00", dformat).format(value)))
}

implicit object PageOrientationParamShow extends ParamShow[PageOrientation] {
Expand Down
Loading