├── gradle.properties ├── settings.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── .travis.yml ├── src ├── main │ └── kotlin │ │ └── id │ │ └── jasoet │ │ └── funpdf │ │ ├── extension │ │ ├── File.kt │ │ ├── String.kt │ │ ├── URL.kt │ │ └── InputStream.kt │ │ ├── Parameter.kt │ │ ├── HtmlToPdf.kt │ │ └── Config.kt └── test │ └── kotlin │ └── id │ └── jasoet │ └── funpdf │ ├── HtmlToPdfSpec.kt │ └── ExtensionSpec.kt ├── gradlew.bat ├── gradlew ├── README.md └── detekt.yml /gradle.properties: -------------------------------------------------------------------------------- 1 | version=1.0.3 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'fun-pdf' 2 | 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasoet/fun-pdf/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Jul 31 16:19:52 WIB 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-all.zip 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | build 3 | deploy 4 | .idea 5 | .gradle 6 | *.iml 7 | out/ 8 | 9 | logs 10 | *.log 11 | 12 | /commons/build 13 | /api-endpoint/build 14 | /grader-worker/build 15 | /.idea/workspace.xml 16 | /.idea/task.xml 17 | 18 | # Eclipse Setting 19 | /.metadata 20 | /Servers 21 | .settings 22 | .project 23 | .classpath 24 | .buildpath 25 | 26 | # Maven 27 | target 28 | 29 | # Netbeans Ignore 30 | nbproject 31 | dist 32 | private 33 | 34 | #idea project 35 | *.iws 36 | *.ipr 37 | 38 | # OS generated files # 39 | .DS_Store 40 | .DS_Store? 41 | ._* 42 | .Spotlight-V100 43 | .Trashes 44 | ehthumbs.db 45 | Thumbs.db 46 | 47 | #Project Spesific 48 | 49 | !config/libs/*.jar 50 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - openjdk8 4 | 5 | dist: trusty 6 | sudo: required 7 | 8 | branches: 9 | only: 10 | - master 11 | 12 | before_cache: 13 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock 14 | - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ 15 | cache: 16 | directories: 17 | - $HOME/.gradle/caches/ 18 | - $HOME/.gradle/wrapper/ 19 | 20 | before_install: 21 | - sudo apt-get update -q 22 | - sudo apt-get install -yq wget xfonts-base xfonts-75dpi 23 | - curl -sLO https://downloads.wkhtmltopdf.org/0.12/0.12.5/wkhtmltox_0.12.5-1.trusty_amd64.deb 24 | - sudo dpkg -i wkhtmltox_0.12.5-1.trusty_amd64.deb 25 | 26 | script: 27 | - ./gradlew clean build 28 | 29 | after_success: 30 | - ./gradlew bintrayUpload 31 | - bash <(curl -s https://codecov.io/bash) -t b4bb7a32-0fed-4fce-b38b-fff96428d465 -------------------------------------------------------------------------------- /src/main/kotlin/id/jasoet/funpdf/extension/File.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C)2018 - Deny Prasetyo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package id.jasoet.funpdf.extension 18 | 19 | import id.jasoet.funpdf.Config 20 | import id.jasoet.funpdf.HtmlToPdf 21 | import java.io.File 22 | import java.io.InputStream 23 | import java.io.OutputStream 24 | 25 | fun File.convertToPdf(output: OutputStream, executable: String = "", configuration: Config.() -> Unit = {}) { 26 | HtmlToPdf(executable = executable, configuration = configuration).convert(input = this, output = output) 27 | } 28 | 29 | fun File.convertToPdf(output: File, executable: String = "", configuration: Config.() -> Unit = {}) { 30 | HtmlToPdf(executable = executable, configuration = configuration).convert(input = this, output = output) 31 | } 32 | 33 | fun File.convertToPdf(executable: String = "", configuration: Config.() -> Unit = {}): InputStream? { 34 | return HtmlToPdf(executable = executable, configuration = configuration).convert(input = this) 35 | } 36 | 37 | -------------------------------------------------------------------------------- /src/main/kotlin/id/jasoet/funpdf/extension/String.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C)2018 - Deny Prasetyo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package id.jasoet.funpdf.extension 18 | 19 | import id.jasoet.funpdf.Config 20 | import id.jasoet.funpdf.HtmlToPdf 21 | import java.io.File 22 | import java.io.InputStream 23 | import java.io.OutputStream 24 | 25 | fun String.convertToPdf(output: OutputStream, executable: String = "", configuration: Config.() -> Unit = {}) { 26 | HtmlToPdf(executable = executable, configuration = configuration).convert(input = this, output = output) 27 | } 28 | 29 | fun String.convertToPdf(output: File, executable: String = "", configuration: Config.() -> Unit = {}) { 30 | HtmlToPdf(executable = executable, configuration = configuration).convert(input = this, output = output) 31 | } 32 | 33 | fun String.convertToPdf(executable: String = "", configuration: Config.() -> Unit = {}): InputStream? { 34 | return HtmlToPdf(executable = executable, configuration = configuration).convert(input = this) 35 | } 36 | 37 | -------------------------------------------------------------------------------- /src/main/kotlin/id/jasoet/funpdf/extension/URL.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C)2018 - Deny Prasetyo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package id.jasoet.funpdf.extension 18 | 19 | import id.jasoet.funpdf.Config 20 | import id.jasoet.funpdf.HtmlToPdf 21 | import java.io.File 22 | import java.io.InputStream 23 | import java.io.OutputStream 24 | import java.net.URL 25 | 26 | 27 | fun URL.convertToPdf(output: OutputStream, executable: String = "", configuration: Config.() -> Unit = {}) { 28 | HtmlToPdf(executable = executable, configuration = configuration).convert(input = this, output = output) 29 | } 30 | 31 | fun URL.convertToPdf(output: File, executable: String = "", configuration: Config.() -> Unit = {}) { 32 | HtmlToPdf(executable = executable, configuration = configuration).convert(input = this, output = output) 33 | } 34 | 35 | fun URL.convertToPdf(executable: String = "", configuration: Config.() -> Unit = {}): InputStream? { 36 | return HtmlToPdf(executable = executable, configuration = configuration).convert(input = this) 37 | } 38 | 39 | -------------------------------------------------------------------------------- /src/main/kotlin/id/jasoet/funpdf/extension/InputStream.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C)2018 - Deny Prasetyo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package id.jasoet.funpdf.extension 18 | 19 | import id.jasoet.funpdf.Config 20 | import id.jasoet.funpdf.HtmlToPdf 21 | import java.io.File 22 | import java.io.InputStream 23 | import java.io.OutputStream 24 | 25 | 26 | fun InputStream.convertToPdf( 27 | output: OutputStream, 28 | executable: String = "", 29 | configuration: Config.() -> Unit = {} 30 | ) { 31 | HtmlToPdf(executable = executable, configuration = configuration).convert(input = this, output = output) 32 | } 33 | 34 | fun InputStream.convertToPdf( 35 | output: File, 36 | executable: String = "", 37 | configuration: Config.() -> Unit = {} 38 | ) { 39 | HtmlToPdf(executable = executable, configuration = configuration).convert(input = this, output = output) 40 | } 41 | 42 | fun InputStream?.convertToPdf( 43 | executable: String = "", 44 | configuration: Config.() -> Unit = {} 45 | ): InputStream? { 46 | return if (this != null) { 47 | HtmlToPdf(executable = executable, configuration = configuration).convert(input = this) 48 | } else { 49 | null 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/kotlin/id/jasoet/funpdf/Parameter.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C)2018 - Deny Prasetyo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package id.jasoet.funpdf 18 | 19 | enum class PageOrientation { 20 | LANDSCAPE, PORTRAIT 21 | } 22 | 23 | enum class Toggle { 24 | ENABLE, DISABLE 25 | } 26 | 27 | class Parameter(private val name: String, private val defaultValue: T? = null) { 28 | 29 | private var value: T? = null 30 | 31 | operator fun invoke(value: T) { 32 | this.value = value 33 | } 34 | 35 | fun compile(): List { 36 | val value: T? = this.value ?: this.defaultValue 37 | return when (value) { 38 | value == null -> emptyList() 39 | is PageOrientation -> { 40 | val orientation = if (value == PageOrientation.LANDSCAPE) "Landscape" else "Portrait" 41 | listOf("--$name", orientation) 42 | } 43 | is Int -> listOf("--$name", "$value") 44 | is Float -> listOf("--$name", "%.2f".format(value)) 45 | is String -> listOf("--$name", value) 46 | is Boolean -> if (value) listOf("--$name") else emptyList() 47 | is Toggle -> if (value == Toggle.ENABLE) listOf("--$name") else listOf("--no-$name") 48 | is List<*> -> value.filter { it is String }.map { it as String }.map { listOf("--$name", it) }.flatten() 49 | else -> emptyList() 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /src/main/kotlin/id/jasoet/funpdf/HtmlToPdf.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C)2018 - Deny Prasetyo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package id.jasoet.funpdf 18 | 19 | import id.jasoet.funkommand.execute 20 | import id.jasoet.funkommand.executeToString 21 | import org.slf4j.LoggerFactory 22 | import java.io.File 23 | import java.io.InputStream 24 | import java.io.OutputStream 25 | import java.net.URL 26 | 27 | class HtmlToPdf(executable: String = "", configuration: Config.() -> Unit = {}) { 28 | private val log = LoggerFactory.getLogger(HtmlToPdf::class.java) 29 | private val config: Config = Config() 30 | private var executable: String 31 | 32 | init { 33 | configuration(config) 34 | 35 | this.executable = if (executable.isExecutable()) { 36 | executable 37 | } else { 38 | log.debug("$executable is not exists or not executable, use default binary") 39 | which("wkhtmltopdf") 40 | } 41 | 42 | } 43 | 44 | fun convert( 45 | input: Any, 46 | output: Any? = null, 47 | environment: Map = emptyMap(), 48 | directory: String = System.getProperty("user.home"), 49 | processConfig: (ProcessBuilder) -> Unit = {} 50 | ): InputStream? { 51 | 52 | val inputParameter: String = when (input) { 53 | is InputStream, is String -> "-" 54 | is File -> input.absolutePath 55 | is URL -> { 56 | when (input.protocol) { 57 | "https", "http", "file" -> input.toString() 58 | else -> throw ProtocolNotSupportedException("Protocol ${input.protocol} not Supported") 59 | } 60 | } 61 | else -> throw IllegalArgumentException("Input Type ${input::class} is not Supported") 62 | } 63 | 64 | val outputParameter: String = when (output) { 65 | is File -> output.absolutePath 66 | else -> "-" 67 | } 68 | 69 | val command: List = listOf(executable, "-q") + config.parameters() + inputParameter + outputParameter 70 | 71 | log.debug("Command to Execute ${command.joinToString(" ")}") 72 | 73 | val commandInput = when (input) { 74 | is InputStream, is String -> input 75 | else -> null 76 | } 77 | 78 | val commandOutput = when (output) { 79 | is OutputStream -> output 80 | else -> null 81 | } 82 | 83 | return command.execute( 84 | input = commandInput, 85 | output = commandOutput, 86 | environment = environment, 87 | directory = directory, 88 | config = processConfig 89 | ) 90 | } 91 | 92 | } 93 | 94 | private fun which(binaryName: String): String { 95 | val operatingSystem = System.getProperty("os.name")?.toLowerCase() ?: "" 96 | val command = if (operatingSystem.contains("windows")) "where $binaryName" else "which $binaryName" 97 | val binaryLocation = command.executeToString().trim() 98 | 99 | val file = File(binaryLocation) 100 | if (!file.exists()) { 101 | throw ExecutableNotFoundException("Cannot find $binaryName executable") 102 | } else if (file.canExecute()) { 103 | return binaryLocation 104 | } else { 105 | throw NotExecutableException("$binaryLocation is not executable") 106 | } 107 | } 108 | 109 | private fun String.isExecutable(): Boolean { 110 | val file = File(this) 111 | return file.exists() && file.canExecute() 112 | } 113 | 114 | class ExecutableNotFoundException(message: String, cause: Throwable?) : Exception(message, cause) { 115 | constructor(message: String) : this(message, null) 116 | } 117 | 118 | class NotExecutableException(message: String, cause: Throwable?) : Exception(message, cause) { 119 | constructor(message: String) : this(message, null) 120 | } 121 | 122 | class ProtocolNotSupportedException(message: String, cause: Throwable?) : Exception(message, cause) { 123 | constructor(message: String) : this(message, null) 124 | } 125 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Html to Pdf Converter 2 | 3 | [![Build Status](https://travis-ci.org/jasoet/fun-pdf.svg?branch=master)](https://travis-ci.org/jasoet/fun-pdf) 4 | [![codecov](https://codecov.io/gh/jasoet/fun-pdf/branch/master/graph/badge.svg)](https://codecov.io/gh/jasoet/fun-pdf) 5 | [![Download](https://api.bintray.com/packages/jasoet/fun/fun-pdf/images/download.svg) ](https://bintray.com/jasoet/fun/fun-pdf/_latestVersion) 6 | 7 | Library to convert Html to Pdf using `wkhtmltopdf` library. This library only tested on Linux and macOS 8 | 9 | ## Features 10 | - Define custom executable location, if not found will find executable by running `which wkthtmltopdf` 11 | - Accept `File`, `String`, `URL` or `InputStream` as standard input. 12 | - Redirect standard output to `File` or `OutputStream` (including `System.out`). 13 | - Accept `Map` as environment variable. 14 | - Return `BufferedInputStream` if output is not redirected, null if otherwise. 15 | - `InputStream` from a command will be easily to be piped (as input) to other command. 16 | - Available on jcenter() 17 | 18 | ## Install wkhtmltopdf 19 | `wkhtmltopdf` library can be downloaded from [here](https://wkhtmltopdf.org/downloads.html) 20 | 21 | ### Linux 22 | ```bash 23 | sudo apt-get update -q 24 | sudo apt-get install -yq wget xfonts-base xfonts-75dpi curl 25 | curl -sLO https://downloads.wkhtmltopdf.org/0.12/0.12.5/wkhtmltox_0.12.5-1.xenial_amd64.deb 26 | sudo dpkg -i wkhtmltox_0.12.5-1.xenial_amd64.deb 27 | ``` 28 | 29 | ### macOS 30 | ```bash 31 | brew cask install wkhtmltopdf 32 | ``` 33 | 34 | ## Gradle 35 | 36 | ### Add maven repository url 37 | ```groovy 38 | repositories { 39 | jcenter() 40 | } 41 | ``` 42 | 43 | ### Add dependency 44 | ```groovy 45 | compile 'id.jasoet:fun-pdf:' 46 | ``` 47 | 48 | ## Usage 49 | 50 | ### Configurable and Cacheable 51 | ```kotlin 52 | val pdf by lazy { 53 | HtmlToPdf(executable = "/usr/bin/wkhtmltopdf") { 54 | orientation(PageOrientation.LANDSCAPE) 55 | pageSize("Letter") 56 | marginTop("1in") 57 | marginBottom("1in") 58 | marginLeft("1in") 59 | marginRight("1in") 60 | } 61 | } 62 | ``` 63 | 64 | ### Html String as input 65 | ```kotlin 66 | val htmlString = "

Hello World

" 67 | 68 | // convert and save if to file 69 | val outputFile = Paths.get("/home/jasoet/document/destination.pdf").toFile() 70 | val inputStream = pdf.convert(input = htmlString,output = outputFile) // will always return null if output is redirected 71 | 72 | // Convert and return InputStream 73 | val inputStream = pdf.convert(input = htmlString) // InputStream is open and ready to use 74 | ``` 75 | 76 | ### File as input 77 | ```kotlin 78 | val file = File("/home/jasoet/source.html") 79 | 80 | // convert and save if to file 81 | val outputFile = Paths.get("/home/jasoet/document/destination.pdf").toFile() 82 | val inputStream = pdf.convert(input = file,output = outputFile) // will always return null if output is redirected 83 | 84 | // Convert and return InputStream 85 | val inputStream = pdf.convert(input = file) // InputStream is open and ready to use 86 | ``` 87 | 88 | ### Url as input 89 | ```kotlin 90 | val url = URL("https://www.google.com") 91 | 92 | // convert and save if to file 93 | val outputFile = Paths.get("/home/jasoet/document/destination.pdf").toFile() 94 | val inputStream = pdf.convert(input = url,output = outputFile) // will always return null if output is redirected 95 | 96 | // Convert and return InputStream 97 | val inputStream = pdf.convert(input = url) // InputStream is open and ready to use 98 | ``` 99 | 100 | ### InputStream as Input 101 | ```kotlin 102 | // get input stream from other executable 103 | val inputStream = listOf("ssh", "jasoet@10.10.2.3","\"cat /home/jasoet/remotefile.html\"").execute() 104 | 105 | // Save it to file 106 | val outputFile = Paths.get("/home/jasoet/document/destination.pdf").toFile() 107 | val inputStream = pdf.convert(input = url,output = outputFile) // will always return null if output is redirected 108 | 109 | ``` 110 | 111 | ## Using extensions 112 | 113 | ### String extension 114 | ```kotlin 115 | 116 | val outputFile = Paths.get("/home/jasoet/document/destination.pdf").toFile() 117 | val inputStream = "

Hello World

".convertToPdf(output = outputFile) // will always return null if output is redirected 118 | 119 | // Convert and return InputStream 120 | val inputStream = "

Hello World

".convertToPdf { 121 | orientation(PageOrientation.LANDSCAPE) 122 | pageSize("Letter") 123 | marginTop("1in") 124 | marginBottom("1in") 125 | marginLeft("1in") 126 | marginRight("1in") 127 | } 128 | // InputStream is open and ready to use 129 | ``` 130 | 131 | ### File extension 132 | ```kotlin 133 | val outputFile = Paths.get("/home/jasoet/document/destination.pdf").toFile() 134 | val inputStream = File("/home/jasoet/source.html").convertToPdf(output = outputFile) // will always return null if output is redirected 135 | 136 | // Convert and return InputStream 137 | val inputStream = File("/home/jasoet/source.html").convertToPdf { 138 | orientation(PageOrientation.LANDSCAPE) 139 | pageSize("Letter") 140 | marginTop("1in") 141 | marginBottom("1in") 142 | marginLeft("1in") 143 | marginRight("1in") 144 | } 145 | // InputStream is open and ready to use 146 | ``` 147 | 148 | ### Url extension 149 | ```kotlin 150 | // convert and save if to file 151 | val outputFile = Paths.get("/home/jasoet/document/destination.pdf").toFile() 152 | val inputStream = URL("https://www.google.com").convertToPdf(output = outputFile) // will always return null if output is redirected 153 | 154 | // Convert and return InputStream 155 | val inputStream = URL("https://www.google.com").convertToPdf { 156 | orientation(PageOrientation.LANDSCAPE) 157 | pageSize("Letter") 158 | marginTop("1in") 159 | marginBottom("1in") 160 | marginLeft("1in") 161 | marginRight("1in") 162 | } 163 | // InputStream is open and ready to use 164 | ``` 165 | 166 | ### InputStream as Input 167 | ```kotlin 168 | val outputFile = Paths.get("/home/jasoet/document/destination.pdf").toFile() 169 | 170 | // get input stream from other executable and save if to file 171 | val inputStream = listOf("ssh", "jasoet@10.10.2.3","\"cat /home/jasoet/remotefile.html\"").execute() 172 | .convertToPdf(output = outputFile) { 173 | orientation(PageOrientation.LANDSCAPE) 174 | pageSize("Letter") 175 | marginTop("1in") 176 | marginBottom("1in") 177 | marginLeft("1in") 178 | marginRight("1in") 179 | } 180 | 181 | ``` 182 | 183 | See [unit test files](https://github.com/jasoet/fun-pdf/blob/master/src/test/kotlin/id/jasoet/funpdf/). 184 | -------------------------------------------------------------------------------- /src/test/kotlin/id/jasoet/funpdf/HtmlToPdfSpec.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C)2018 - Deny Prasetyo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package id.jasoet.funpdf 18 | 19 | import id.jasoet.funkommand.execute 20 | import id.jasoet.funpdf.extension.convertToPdf 21 | import org.amshove.kluent.shouldBe 22 | import org.amshove.kluent.shouldNotBeNull 23 | import org.apache.commons.io.FileUtils 24 | import org.apache.commons.io.IOUtils 25 | import org.jetbrains.spek.api.Spek 26 | import org.jetbrains.spek.api.dsl.given 27 | import org.jetbrains.spek.api.dsl.it 28 | import org.jetbrains.spek.api.dsl.on 29 | import java.io.FileInputStream 30 | import java.io.FileOutputStream 31 | import java.net.URL 32 | import java.nio.file.Paths 33 | import java.util.UUID 34 | 35 | object HtmlToPdfSpec : Spek({ 36 | 37 | val tmpDir: String = System.getProperty("java.io.tmpdir") 38 | 39 | given("HtmlToPdf Converter") { 40 | val pdf by lazy { 41 | HtmlToPdf { 42 | orientation(PageOrientation.LANDSCAPE) 43 | pageSize("Letter") 44 | marginTop("1in") 45 | marginBottom("1in") 46 | marginLeft("1in") 47 | marginRight("1in") 48 | } 49 | } 50 | 51 | on("Handle string input") { 52 | 53 | val page = "

Hello World

" 54 | 55 | it("Should able to convert to pdf and save it in file") { 56 | val outputFile = Paths.get(tmpDir, "${UUID.randomUUID()}.pdf").toFile() 57 | pdf.convert(page, outputFile) 58 | 59 | outputFile.exists() shouldBe true 60 | } 61 | 62 | it("Should able to convert to pdf and convert it to output stream") { 63 | val outputFile = Paths.get(tmpDir, "${UUID.randomUUID()}.pdf").toFile() 64 | val outputStream = FileOutputStream(outputFile) 65 | pdf.convert(page, outputStream) 66 | 67 | outputFile.exists() shouldBe true 68 | } 69 | 70 | it("Should able to convert to pdf and return InputStream") { 71 | val inputStreamResult = pdf.convert(page) 72 | inputStreamResult.shouldNotBeNull() 73 | 74 | val outputFile = Paths.get(tmpDir, "${UUID.randomUUID()}.pdf").toFile() 75 | val outputStream = FileOutputStream(outputFile) 76 | IOUtils.copy(inputStreamResult, outputStream) 77 | 78 | outputFile.exists() shouldBe true 79 | } 80 | 81 | } 82 | 83 | on("Handle Url input") { 84 | val url = URL("https://www.google.com") 85 | 86 | it("Should able to convert to pdf and save it in file") { 87 | val outputFile = Paths.get(tmpDir, "${UUID.randomUUID()}.pdf").toFile() 88 | pdf.convert(input = url, output = outputFile) 89 | 90 | outputFile.exists() shouldBe true 91 | } 92 | 93 | it("Should able to convert to pdf and convert it to output stream") { 94 | val outputFile = Paths.get(tmpDir, "${UUID.randomUUID()}.pdf").toFile() 95 | val outputStream = FileOutputStream(outputFile) 96 | pdf.convert(input = url, output = outputStream) 97 | 98 | outputFile.exists() shouldBe true 99 | } 100 | 101 | it("Should able to convert to pdf and return InputStream") { 102 | val inputStreamResult = pdf.convert(url) 103 | inputStreamResult.shouldNotBeNull() 104 | 105 | val outputFile = Paths.get(tmpDir, "${UUID.randomUUID()}.pdf").toFile() 106 | val outputStream = FileOutputStream(outputFile) 107 | IOUtils.copy(inputStreamResult, outputStream) 108 | 109 | outputFile.exists() shouldBe true 110 | } 111 | } 112 | 113 | on("Handle File input") { 114 | val page = "

Hello World

" 115 | val inputFile = Paths.get(tmpDir, "${UUID.randomUUID()}.html").toFile() 116 | FileUtils.writeStringToFile(inputFile, page, Charsets.UTF_32) 117 | 118 | 119 | it("Should able to convert to pdf and save it in file") { 120 | val outputFile = Paths.get(tmpDir, "${UUID.randomUUID()}.pdf").toFile() 121 | pdf.convert(input = inputFile, output = outputFile) 122 | 123 | outputFile.exists() shouldBe true 124 | } 125 | 126 | it("Should able to convert to pdf and convert it to output stream") { 127 | val outputFile = Paths.get(tmpDir, "${UUID.randomUUID()}.pdf").toFile() 128 | val outputStream = FileOutputStream(outputFile) 129 | pdf.convert(input = inputFile, output = outputStream) 130 | 131 | outputFile.exists() shouldBe true 132 | } 133 | 134 | it("Should able to convert to pdf and return InputStream") { 135 | val inputStreamResult = pdf.convert(inputFile) 136 | inputStreamResult.shouldNotBeNull() 137 | 138 | val outputFile = Paths.get(tmpDir, "${UUID.randomUUID()}.pdf").toFile() 139 | val outputStream = FileOutputStream(outputFile) 140 | IOUtils.copy(inputStreamResult, outputStream) 141 | 142 | outputFile.exists() shouldBe true 143 | } 144 | } 145 | 146 | on("Handle InputStream input") { 147 | val page = "

Hello World

" 148 | val sourceFile = Paths.get(tmpDir, "${UUID.randomUUID()}.html").toFile() 149 | FileUtils.writeStringToFile(sourceFile, page, Charsets.UTF_32) 150 | 151 | 152 | it("Should able to convert to pdf and save it in file") { 153 | val inputStream = FileInputStream(sourceFile) 154 | val outputFile = Paths.get(tmpDir, "${UUID.randomUUID()}.pdf").toFile() 155 | pdf.convert(input = inputStream, output = outputFile) 156 | 157 | outputFile.exists() shouldBe true 158 | } 159 | 160 | it("Should able to convert to pdf and convert it to output stream") { 161 | val inputStream = FileInputStream(sourceFile) 162 | val outputFile = Paths.get(tmpDir, "${UUID.randomUUID()}.pdf").toFile() 163 | val outputStream = FileOutputStream(outputFile) 164 | pdf.convert(input = inputStream, output = outputStream) 165 | 166 | outputFile.exists() shouldBe true 167 | } 168 | 169 | it("Should able to convert to pdf and return InputStream") { 170 | val inputStream = FileInputStream(sourceFile) 171 | val inputStreamResult = pdf.convert(inputStream) 172 | inputStreamResult.shouldNotBeNull() 173 | 174 | val outputFile = Paths.get(tmpDir, "${UUID.randomUUID()}.pdf").toFile() 175 | val outputStream = FileOutputStream(outputFile) 176 | IOUtils.copy(inputStreamResult, outputStream) 177 | 178 | outputFile.exists() shouldBe true 179 | } 180 | } 181 | } 182 | 183 | }) 184 | -------------------------------------------------------------------------------- /src/main/kotlin/id/jasoet/funpdf/Config.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C)2018 - Deny Prasetyo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package id.jasoet.funpdf 18 | 19 | 20 | class Config { 21 | val allow = Parameter>("allow") 22 | val defaultHeader = Parameter("default-header") 23 | val disableExternalLinks = Parameter("disable-external-links") 24 | val disableInternalLinks = Parameter("disable-internal-links") 25 | val disableJavascript = Parameter("disable-javascript") 26 | val noPdfCompression = Parameter("no-pdf-compression") 27 | val disableSmartShrinking = Parameter("disable-smart-shrinking") 28 | val javascriptDelay = Parameter("javascript-delay") 29 | val convertForms = Parameter("forms") 30 | val encoding = Parameter("encoding", "UTF-8") 31 | val grayScale = Parameter("grayscale") 32 | val lowQuality = Parameter("lowquality") 33 | val marginBottom = Parameter("margin-bottom") 34 | val marginLeft = Parameter("margin-left") 35 | val marginRight = Parameter("margin-right") 36 | val marginTop = Parameter("margin-top") 37 | val minimumFontSize = Parameter("minimum-font-size") 38 | val background = Parameter("background") 39 | val orientation = Parameter("orientation") 40 | val pageHeight = Parameter("page-height") 41 | val pageOffset = Parameter("page-offset") 42 | val pageSize = Parameter("page-size") 43 | val pageWidth = Parameter("page-width") 44 | val title = Parameter("title") 45 | val tableOfContent = Parameter("toc") 46 | val zoom = Parameter("zoom") 47 | val footerCenter = Parameter("footer-center") 48 | val footerFontName = Parameter("footer-font-name") 49 | val footerFontSize = Parameter("footer-font-size") 50 | val footerHtml = Parameter("footer-html") 51 | val footerLeft = Parameter("footer-left") 52 | val footerLine = Parameter("footer-line") 53 | val footerRight = Parameter("footer-right") 54 | val footerSpacing = Parameter("footer-spacing") 55 | val headerCenter = Parameter("header-center") 56 | val headerFontName = Parameter("header-font-name") 57 | val headerFontSize = Parameter("header-font-size") 58 | val headerHtml = Parameter("header-html") 59 | val headerLeft = Parameter("header-left") 60 | val headerLine = Parameter("header-line") 61 | val headerRight = Parameter("header-right") 62 | val headerSpacing = Parameter("header-spacing") 63 | val tableOfContentDepth = Parameter("toc-depth") 64 | val tableOfContentDisableBackLinks = Parameter("toc-disable-back-links") 65 | val tableOfContentDisableLinks = Parameter("toc-disable-links") 66 | val tableOfContentFontName = Parameter("toc-font-name") 67 | val tableOfContentHeaderFontName = Parameter("toc-header-font-name") 68 | val tableOfContentHeaderFontSize = Parameter("toc-header-font-size") 69 | val tableOfContentHeaderText = Parameter("toc-header-text") 70 | val tableOfContentLevel1FontSize = Parameter("toc-l1-font-size") 71 | val tableOfContentLevel1Indentation = Parameter("toc-l1-indentation") 72 | val tableOfContentLevel2FontSize = Parameter("toc-l2-font-size") 73 | val tableOfContentLevel2Indentation = Parameter("toc-l2-indentation") 74 | val tableOfContentLevel3FontSize = Parameter("toc-l3-font-size") 75 | val tableOfContentLevel3Indentation = Parameter("toc-l3-indentation") 76 | val tableOfContentLevel4FontSize = Parameter("toc-l4-font-size") 77 | val tableOfContentLevel4Indentation = Parameter("toc-l4-indentation") 78 | val tableOfContentLevel5FontSize = Parameter("toc-l5-font-size") 79 | val tableOfContentLevel5Indentation = Parameter("toc-l5-indentation") 80 | val tableOfContentLevel6FontSize = Parameter("toc-l6-font-size") 81 | val tableOfContentLevel6Indentation = Parameter("toc-l6-indentation") 82 | val tableOfContentLevel7FontSize = Parameter("toc-l7-font-size") 83 | val tableOfContentLevel7Indentation = Parameter("toc-l7-indentation") 84 | val tableOfContentNoDots = Parameter("toc-no-dots") 85 | val outline = Parameter("outline") 86 | val outlineDepth = Parameter("outline-depth") 87 | val printMediaType = Parameter("print-media-type") 88 | val userStyleSheet = Parameter("user-style-sheet") 89 | val username = Parameter("username") 90 | val password = Parameter("password") 91 | val viewportSize = Parameter("viewport-size") 92 | 93 | 94 | private val listParameters = 95 | listOf(allow, 96 | defaultHeader, 97 | disableExternalLinks, 98 | disableInternalLinks, 99 | disableJavascript, 100 | noPdfCompression, 101 | disableSmartShrinking, 102 | javascriptDelay, 103 | convertForms, 104 | encoding, 105 | grayScale, 106 | lowQuality, 107 | marginBottom, 108 | marginLeft, 109 | marginRight, 110 | marginTop, 111 | minimumFontSize, 112 | background, 113 | orientation, 114 | pageHeight, 115 | pageOffset, 116 | pageSize, 117 | pageWidth, 118 | title, 119 | tableOfContent, 120 | zoom, 121 | footerCenter, 122 | footerFontName, 123 | footerFontSize, 124 | footerHtml, 125 | footerLeft, 126 | footerLine, 127 | footerRight, 128 | footerSpacing, 129 | headerCenter, 130 | headerFontName, 131 | headerFontSize, 132 | headerHtml, 133 | headerLeft, 134 | headerLine, 135 | headerRight, 136 | headerSpacing, 137 | tableOfContentDepth, 138 | tableOfContentDisableBackLinks, 139 | tableOfContentDisableLinks, 140 | tableOfContentFontName, 141 | tableOfContentHeaderFontName, 142 | tableOfContentHeaderFontSize, 143 | tableOfContentHeaderText, 144 | tableOfContentLevel1FontSize, 145 | tableOfContentLevel1Indentation, 146 | tableOfContentLevel2FontSize, 147 | tableOfContentLevel2Indentation, 148 | tableOfContentLevel3FontSize, 149 | tableOfContentLevel3Indentation, 150 | tableOfContentLevel4FontSize, 151 | tableOfContentLevel4Indentation, 152 | tableOfContentLevel5FontSize, 153 | tableOfContentLevel5Indentation, 154 | tableOfContentLevel6FontSize, 155 | tableOfContentLevel6Indentation, 156 | tableOfContentLevel7FontSize, 157 | tableOfContentLevel7Indentation, 158 | tableOfContentNoDots, 159 | outline, 160 | outlineDepth, 161 | printMediaType, 162 | userStyleSheet, 163 | username, 164 | password, 165 | viewportSize) 166 | 167 | fun parameters(): List { 168 | return this.listParameters.map { it.compile() }.flatten() 169 | } 170 | } 171 | 172 | -------------------------------------------------------------------------------- /src/test/kotlin/id/jasoet/funpdf/ExtensionSpec.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C)2018 - Deny Prasetyo 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package id.jasoet.funpdf 18 | 19 | import id.jasoet.funpdf.extension.convertToPdf 20 | import org.amshove.kluent.shouldBe 21 | import org.amshove.kluent.shouldNotBeNull 22 | import org.apache.commons.io.FileUtils 23 | import org.apache.commons.io.IOUtils 24 | import org.jetbrains.spek.api.Spek 25 | import org.jetbrains.spek.api.dsl.given 26 | import org.jetbrains.spek.api.dsl.it 27 | import org.jetbrains.spek.api.dsl.on 28 | import java.io.FileInputStream 29 | import java.io.FileOutputStream 30 | import java.net.URL 31 | import java.nio.file.Paths 32 | import java.util.UUID 33 | 34 | object ExtensionSpec : Spek({ 35 | 36 | val tmpDir: String = System.getProperty("java.io.tmpdir") 37 | 38 | given("HtmlToPdf Extension") { 39 | 40 | on("Handle string input") { 41 | 42 | val page = "

Hello World

" 43 | 44 | it("Should able to convert to pdf and save it in file") { 45 | val outputFile = Paths.get(tmpDir, "${UUID.randomUUID()}.pdf").toFile() 46 | 47 | page.convertToPdf(outputFile) { 48 | orientation(PageOrientation.LANDSCAPE) 49 | pageSize("Letter") 50 | marginTop("1in") 51 | marginBottom("1in") 52 | marginLeft("1in") 53 | marginRight("1in") 54 | } 55 | 56 | outputFile.exists() shouldBe true 57 | } 58 | 59 | it("Should able to convert to pdf and convert it to output stream") { 60 | val outputFile = Paths.get(tmpDir, "${UUID.randomUUID()}.pdf").toFile() 61 | val outputStream = FileOutputStream(outputFile) 62 | 63 | page.convertToPdf(outputStream) { 64 | orientation(PageOrientation.LANDSCAPE) 65 | pageSize("Letter") 66 | marginTop("1in") 67 | marginBottom("1in") 68 | marginLeft("1in") 69 | marginRight("1in") 70 | } 71 | 72 | outputFile.exists() shouldBe true 73 | } 74 | 75 | it("Should able to convert to pdf and return InputStream") { 76 | val inputStreamResult = page.convertToPdf { 77 | orientation(PageOrientation.LANDSCAPE) 78 | pageSize("Letter") 79 | marginTop("1in") 80 | marginBottom("1in") 81 | marginLeft("1in") 82 | marginRight("1in") 83 | } 84 | 85 | inputStreamResult.shouldNotBeNull() 86 | 87 | val outputFile = Paths.get(tmpDir, "${UUID.randomUUID()}.pdf").toFile() 88 | val outputStream = FileOutputStream(outputFile) 89 | IOUtils.copy(inputStreamResult, outputStream) 90 | 91 | outputFile.exists() shouldBe true 92 | } 93 | 94 | } 95 | 96 | on("Handle Url input") { 97 | val url = URL("https://www.google.com") 98 | 99 | it("Should able to convert to pdf and save it in file") { 100 | val outputFile = Paths.get(tmpDir, "${UUID.randomUUID()}.pdf").toFile() 101 | url.convertToPdf(outputFile) { 102 | orientation(PageOrientation.LANDSCAPE) 103 | pageSize("Letter") 104 | marginTop("1in") 105 | marginBottom("1in") 106 | marginLeft("1in") 107 | marginRight("1in") 108 | } 109 | 110 | outputFile.exists() shouldBe true 111 | } 112 | 113 | it("Should able to convert to pdf and convert it to output stream") { 114 | val outputFile = Paths.get(tmpDir, "${UUID.randomUUID()}.pdf").toFile() 115 | val outputStream = FileOutputStream(outputFile) 116 | url.convertToPdf(output = outputStream) { 117 | orientation(PageOrientation.LANDSCAPE) 118 | pageSize("Letter") 119 | marginTop("1in") 120 | marginBottom("1in") 121 | marginLeft("1in") 122 | marginRight("1in") 123 | } 124 | 125 | outputFile.exists() shouldBe true 126 | } 127 | 128 | it("Should able to convert to pdf and return InputStream") { 129 | val inputStreamResult = url.convertToPdf { 130 | orientation(PageOrientation.LANDSCAPE) 131 | pageSize("Letter") 132 | marginTop("1in") 133 | marginBottom("1in") 134 | marginLeft("1in") 135 | marginRight("1in") 136 | } 137 | inputStreamResult.shouldNotBeNull() 138 | 139 | val outputFile = Paths.get(tmpDir, "${UUID.randomUUID()}.pdf").toFile() 140 | val outputStream = FileOutputStream(outputFile) 141 | IOUtils.copy(inputStreamResult, outputStream) 142 | 143 | outputFile.exists() shouldBe true 144 | } 145 | } 146 | 147 | on("Handle File input") { 148 | val page = "

Hello World

" 149 | val inputFile = Paths.get(tmpDir, "${UUID.randomUUID()}.html").toFile() 150 | FileUtils.writeStringToFile(inputFile, page, Charsets.UTF_32) 151 | 152 | it("Should able to convert to pdf and save it in file") { 153 | val outputFile = Paths.get(tmpDir, "${UUID.randomUUID()}.pdf").toFile() 154 | inputFile.convertToPdf(output = outputFile) { 155 | orientation(PageOrientation.LANDSCAPE) 156 | pageSize("Letter") 157 | marginTop("1in") 158 | marginBottom("1in") 159 | marginLeft("1in") 160 | marginRight("1in") 161 | } 162 | 163 | outputFile.exists() shouldBe true 164 | } 165 | 166 | it("Should able to convert to pdf and convert it to output stream") { 167 | val outputFile = Paths.get(tmpDir, "${UUID.randomUUID()}.pdf").toFile() 168 | val outputStream = FileOutputStream(outputFile) 169 | inputFile.convertToPdf(output = outputStream) { 170 | orientation(PageOrientation.LANDSCAPE) 171 | pageSize("Letter") 172 | marginTop("1in") 173 | marginBottom("1in") 174 | marginLeft("1in") 175 | marginRight("1in") 176 | } 177 | 178 | outputFile.exists() shouldBe true 179 | } 180 | 181 | it("Should able to convert to pdf and return InputStream") { 182 | val inputStreamResult = inputFile.convertToPdf { 183 | orientation(PageOrientation.LANDSCAPE) 184 | pageSize("Letter") 185 | marginTop("1in") 186 | marginBottom("1in") 187 | marginLeft("1in") 188 | marginRight("1in") 189 | } 190 | inputStreamResult.shouldNotBeNull() 191 | 192 | val outputFile = Paths.get(tmpDir, "${UUID.randomUUID()}.pdf").toFile() 193 | val outputStream = FileOutputStream(outputFile) 194 | IOUtils.copy(inputStreamResult, outputStream) 195 | 196 | outputFile.exists() shouldBe true 197 | } 198 | } 199 | 200 | on("Handle InputStream input") { 201 | val page = "

Hello World

" 202 | val sourceFile = Paths.get(tmpDir, "${UUID.randomUUID()}.html").toFile() 203 | FileUtils.writeStringToFile(sourceFile, page, Charsets.UTF_32) 204 | 205 | 206 | it("Should able to convert to pdf and save it in file") { 207 | val inputStream = FileInputStream(sourceFile) 208 | val outputFile = Paths.get(tmpDir, "${UUID.randomUUID()}.pdf").toFile() 209 | inputStream.convertToPdf(output = outputFile) 210 | 211 | outputFile.exists() shouldBe true 212 | } 213 | 214 | it("Should able to convert to pdf and convert it to output stream") { 215 | val inputStream = FileInputStream(sourceFile) 216 | val outputFile = Paths.get(tmpDir, "${UUID.randomUUID()}.pdf").toFile() 217 | val outputStream = FileOutputStream(outputFile) 218 | inputStream.convertToPdf(output = outputStream) 219 | 220 | outputFile.exists() shouldBe true 221 | } 222 | 223 | it("Should able to convert to pdf and return InputStream") { 224 | val inputStream = FileInputStream(sourceFile) 225 | val inputStreamResult = inputStream.convertToPdf() 226 | inputStreamResult.shouldNotBeNull() 227 | 228 | val outputFile = Paths.get(tmpDir, "${UUID.randomUUID()}.pdf").toFile() 229 | val outputStream = FileOutputStream(outputFile) 230 | IOUtils.copy(inputStreamResult, outputStream) 231 | 232 | outputFile.exists() shouldBe true 233 | } 234 | } 235 | } 236 | 237 | }) 238 | -------------------------------------------------------------------------------- /detekt.yml: -------------------------------------------------------------------------------- 1 | autoCorrect: true 2 | failFast: false 3 | 4 | test-pattern: # Configure exclusions for test sources 5 | active: true 6 | patterns: # Test file regexes 7 | - '.*/test/.*' 8 | - '.*Test.kt' 9 | - '.*Spec.kt' 10 | exclude-rule-sets: 11 | - 'comments' 12 | exclude-rules: 13 | - 'NamingRules' 14 | - 'WildcardImport' 15 | - 'MagicNumber' 16 | - 'MaxLineLength' 17 | - 'LateinitUsage' 18 | - 'StringLiteralDuplication' 19 | - 'SpreadOperator' 20 | - 'TooManyFunctions' 21 | 22 | build: 23 | maxIssues: 5 24 | weights: 25 | complexity: 2 26 | formatting: 1 27 | LongParameterList: 1 28 | comments: 1 29 | 30 | processors: 31 | active: true 32 | exclude: 33 | # - 'FunctionCountProcessor' 34 | # - 'PropertyCountProcessor' 35 | # - 'ClassCountProcessor' 36 | # - 'PackageCountProcessor' 37 | # - 'KtFileCountProcessor' 38 | 39 | console-reports: 40 | active: true 41 | exclude: 42 | # - 'ProjectStatisticsReport' 43 | # - 'ComplexityReport' 44 | # - 'NotificationReport' 45 | # - 'FindingsReport' 46 | # - 'BuildFailureReport' 47 | 48 | output-reports: 49 | active: true 50 | exclude: 51 | # - 'HtmlOutputReport' 52 | # - 'PlainOutputReport' 53 | # - 'XmlOutputReport' 54 | 55 | comments: 56 | active: true 57 | CommentOverPrivateFunction: 58 | active: false 59 | CommentOverPrivateProperty: 60 | active: false 61 | EndOfSentenceFormat: 62 | active: false 63 | endOfSentenceFormat: ([.?!][ \t\n\r\f<])|([.?!]$) 64 | UndocumentedPublicClass: 65 | active: false 66 | searchInNestedClass: true 67 | searchInInnerClass: true 68 | searchInInnerObject: true 69 | searchInInnerInterface: true 70 | UndocumentedPublicFunction: 71 | active: false 72 | 73 | complexity: 74 | active: true 75 | ComplexCondition: 76 | active: true 77 | threshold: 3 78 | ComplexInterface: 79 | active: false 80 | threshold: 10 81 | includeStaticDeclarations: false 82 | ComplexMethod: 83 | active: true 84 | threshold: 10 85 | ignoreSingleWhenExpression: false 86 | LabeledExpression: 87 | active: false 88 | LargeClass: 89 | active: true 90 | threshold: 150 91 | LongMethod: 92 | active: true 93 | threshold: 20 94 | LongParameterList: 95 | active: true 96 | threshold: 10 97 | ignoreDefaultParameters: false 98 | MethodOverloading: 99 | active: false 100 | threshold: 5 101 | NestedBlockDepth: 102 | active: true 103 | threshold: 3 104 | StringLiteralDuplication: 105 | active: false 106 | threshold: 2 107 | ignoreAnnotation: true 108 | excludeStringsWithLessThan5Characters: true 109 | ignoreStringsRegex: '$^' 110 | TooManyFunctions: 111 | active: true 112 | thresholdInFiles: 10 113 | thresholdInClasses: 10 114 | thresholdInInterfaces: 10 115 | thresholdInObjects: 10 116 | thresholdInEnums: 10 117 | 118 | empty-blocks: 119 | active: true 120 | EmptyCatchBlock: 121 | active: true 122 | allowedExceptionNameRegex: "^(ignore|expected).*" 123 | EmptyClassBlock: 124 | active: true 125 | EmptyDefaultConstructor: 126 | active: true 127 | EmptyDoWhileBlock: 128 | active: true 129 | EmptyElseBlock: 130 | active: true 131 | EmptyFinallyBlock: 132 | active: true 133 | EmptyForBlock: 134 | active: true 135 | EmptyFunctionBlock: 136 | active: true 137 | EmptyIfBlock: 138 | active: true 139 | EmptyInitBlock: 140 | active: true 141 | EmptyKtFile: 142 | active: true 143 | EmptySecondaryConstructor: 144 | active: true 145 | EmptyWhenBlock: 146 | active: true 147 | EmptyWhileBlock: 148 | active: true 149 | 150 | exceptions: 151 | active: true 152 | ExceptionRaisedInUnexpectedLocation: 153 | active: false 154 | methodNames: 'toString,hashCode,equals,finalize' 155 | InstanceOfCheckForException: 156 | active: false 157 | NotImplementedDeclaration: 158 | active: false 159 | PrintStackTrace: 160 | active: false 161 | RethrowCaughtException: 162 | active: false 163 | ReturnFromFinally: 164 | active: false 165 | SwallowedException: 166 | active: false 167 | ThrowingExceptionFromFinally: 168 | active: false 169 | ThrowingExceptionInMain: 170 | active: false 171 | ThrowingExceptionsWithoutMessageOrCause: 172 | active: false 173 | exceptions: 'IllegalArgumentException,IllegalStateException,IOException' 174 | ThrowingNewInstanceOfSameException: 175 | active: false 176 | TooGenericExceptionCaught: 177 | active: true 178 | exceptions: 179 | - ArrayIndexOutOfBoundsException 180 | - Error 181 | - Exception 182 | - IllegalMonitorStateException 183 | - NullPointerException 184 | - IndexOutOfBoundsException 185 | - RuntimeException 186 | - Throwable 187 | TooGenericExceptionThrown: 188 | active: true 189 | exceptions: 190 | - Error 191 | - Exception 192 | - NullPointerException 193 | - Throwable 194 | - RuntimeException 195 | 196 | naming: 197 | active: true 198 | ClassNaming: 199 | active: true 200 | classPattern: '[A-Z$][a-zA-Z0-9$]*' 201 | EnumNaming: 202 | active: true 203 | enumEntryPattern: '^[A-Z][_a-zA-Z0-9]*' 204 | ForbiddenClassName: 205 | active: false 206 | forbiddenName: '' 207 | FunctionMaxLength: 208 | active: false 209 | maximumFunctionNameLength: 30 210 | FunctionMinLength: 211 | active: false 212 | minimumFunctionNameLength: 3 213 | FunctionNaming: 214 | active: true 215 | functionPattern: '^([a-z$][a-zA-Z$0-9]*)|(`.*`)$' 216 | MatchingDeclarationName: 217 | active: true 218 | MemberNameEqualsClassName: 219 | active: false 220 | ignoreOverriddenFunction: true 221 | ObjectPropertyNaming: 222 | active: true 223 | propertyPattern: '[A-Za-z][_A-Za-z0-9]*' 224 | PackageNaming: 225 | active: true 226 | packagePattern: '^[a-z]+(\.[a-z][a-z0-9]*)*$' 227 | TopLevelPropertyNaming: 228 | active: true 229 | constantPattern: '[A-Z][_A-Z0-9]*' 230 | propertyPattern: '[a-z][A-Za-z\d]*' 231 | privatePropertyPattern: '(_)?[a-z][A-Za-z0-9]*' 232 | VariableMaxLength: 233 | active: false 234 | maximumVariableNameLength: 64 235 | VariableMinLength: 236 | active: false 237 | minimumVariableNameLength: 1 238 | VariableNaming: 239 | active: true 240 | variablePattern: '[a-z][A-Za-z0-9]*' 241 | privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*' 242 | 243 | performance: 244 | active: true 245 | ForEachOnRange: 246 | active: true 247 | SpreadOperator: 248 | active: true 249 | UnnecessaryTemporaryInstantiation: 250 | active: true 251 | 252 | potential-bugs: 253 | active: true 254 | DuplicateCaseInWhenExpression: 255 | active: true 256 | EqualsAlwaysReturnsTrueOrFalse: 257 | active: false 258 | EqualsWithHashCodeExist: 259 | active: true 260 | ExplicitGarbageCollectionCall: 261 | active: true 262 | InvalidRange: 263 | active: false 264 | IteratorHasNextCallsNextMethod: 265 | active: false 266 | IteratorNotThrowingNoSuchElementException: 267 | active: false 268 | LateinitUsage: 269 | active: false 270 | excludeAnnotatedProperties: "" 271 | ignoreOnClassesPattern: "" 272 | UnconditionalJumpStatementInLoop: 273 | active: false 274 | UnreachableCode: 275 | active: true 276 | UnsafeCallOnNullableType: 277 | active: false 278 | UnsafeCast: 279 | active: false 280 | UselessPostfixExpression: 281 | active: false 282 | WrongEqualsTypeParameter: 283 | active: false 284 | 285 | style: 286 | active: true 287 | CollapsibleIfStatements: 288 | active: false 289 | DataClassContainsFunctions: 290 | active: false 291 | conversionFunctionPrefix: 'to' 292 | EqualsNullCall: 293 | active: false 294 | ExpressionBodySyntax: 295 | active: false 296 | ForbiddenComment: 297 | active: true 298 | values: 'TODO:,FIXME:,STOPSHIP:' 299 | ForbiddenImport: 300 | active: false 301 | imports: '' 302 | FunctionOnlyReturningConstant: 303 | active: false 304 | ignoreOverridableFunction: true 305 | excludedFunctions: 'describeContents' 306 | LoopWithTooManyJumpStatements: 307 | active: false 308 | maxJumpCount: 1 309 | MagicNumber: 310 | active: true 311 | ignoreNumbers: '-1,0,1,2' 312 | ignoreHashCodeFunction: false 313 | ignorePropertyDeclaration: false 314 | ignoreConstantDeclaration: true 315 | ignoreCompanionObjectPropertyDeclaration: true 316 | ignoreAnnotation: false 317 | ignoreNamedArgument: true 318 | ignoreEnums: false 319 | MaxLineLength: 320 | active: true 321 | maxLineLength: 120 322 | excludePackageStatements: false 323 | excludeImportStatements: false 324 | MayBeConst: 325 | active: false 326 | ModifierOrder: 327 | active: true 328 | NestedClassesVisibility: 329 | active: false 330 | NewLineAtEndOfFile: 331 | active: true 332 | OptionalAbstractKeyword: 333 | active: true 334 | OptionalUnit: 335 | active: false 336 | OptionalWhenBraces: 337 | active: false 338 | ProtectedMemberInFinalClass: 339 | active: false 340 | RedundantVisibilityModifierRule: 341 | active: false 342 | ReturnCount: 343 | active: true 344 | max: 2 345 | excludedFunctions: "equals" 346 | SafeCast: 347 | active: true 348 | SerialVersionUIDInSerializableClass: 349 | active: false 350 | SpacingBetweenPackageAndImports: 351 | active: false 352 | ThrowsCount: 353 | active: true 354 | max: 2 355 | UnnecessaryAbstractClass: 356 | active: false 357 | UnnecessaryInheritance: 358 | active: false 359 | UnnecessaryParentheses: 360 | active: false 361 | UntilInsteadOfRangeTo: 362 | active: false 363 | UnusedImports: 364 | active: false 365 | UnusedPrivateMember: 366 | active: false 367 | UseDataClass: 368 | active: false 369 | excludeAnnotatedClasses: "" 370 | UtilityClassWithPublicConstructor: 371 | active: false 372 | WildcardImport: 373 | active: true 374 | excludeImports: 'java.util.*,kotlinx.android.synthetic.*' 375 | --------------------------------------------------------------------------------