Skip to content

Commit

Permalink
Merge 54a3087 into e7b0c43
Browse files Browse the repository at this point in the history
  • Loading branch information
Gregor Billing committed Sep 29, 2020
2 parents e7b0c43 + 54a3087 commit 3ada24c
Show file tree
Hide file tree
Showing 21 changed files with 113 additions and 130 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ TNoodle-WCA-*.jar
TNoodle-Build-latest.jar
tnoodle_resources/
tnoodle_pruning_cache/
**/logging/*.log
**/tnoodle.log
version.tnoodle
*.hprof

Expand Down
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ buildscript {
exclude(group = "com.android.tools.build")
}
classpath(GOOGLE_APPENGINE_GRADLE)
classpath(KOTLINX_ATOMICFU_GRADLE)
}
}

Expand Down
8 changes: 8 additions & 0 deletions buildSrc/src/main/kotlin/PluginsHack.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import dependencies.Libraries.Buildscript.GOOGLE_APPENGINE_GRADLE_ACTUAL
import dependencies.Libraries.Buildscript.PROGUARD_GRADLE_ACTUAL
import dependencies.Libraries.Buildscript.WCA_I18N_ACTUAL
import dependencies.Libraries.Buildscript.KOTLINX_ATOMICFU_GRADLE_ACTUAL
import dependencies.Plugins.DEPENDENCY_VERSIONS_ACTUAL
import dependencies.Plugins.GIT_VERSION_TAG_ACTUAL
import dependencies.Plugins.GOOGLE_APPENGINE_ACTUAL
import dependencies.Plugins.KOTLESS_ACTUAL
import dependencies.Plugins.KOTLIN_JVM_ACTUAL
import dependencies.Plugins.KOTLIN_MULTIPLATFORM_ACTUAL
import dependencies.Plugins.KOTLIN_SERIALIZATION_ACTUAL
import dependencies.Plugins.KOTLINX_ATOMICFU_ACTUAL
import dependencies.Plugins.SHADOW_ACTUAL
import dependencies.Plugins.NODEJS_ACTUAL

Expand Down Expand Up @@ -47,6 +49,9 @@ inline val PluginDependenciesSpec.GIT_VERSION_TAG: PluginDependencySpec
inline val PluginDependenciesSpec.KOTLESS: PluginDependencySpec
get() = KOTLESS_ACTUAL

inline val PluginDependenciesSpec.KOTLINX_ATOMICFU: PluginDependencySpec
get() = KOTLINX_ATOMICFU_ACTUAL

inline val PROGUARD_GRADLE: String
get() = PROGUARD_GRADLE_ACTUAL

Expand All @@ -55,3 +60,6 @@ inline val WCA_I18N: String

inline val GOOGLE_APPENGINE_GRADLE: String
get() = GOOGLE_APPENGINE_GRADLE_ACTUAL

inline val KOTLINX_ATOMICFU_GRADLE: String
get() = KOTLINX_ATOMICFU_GRADLE_ACTUAL
3 changes: 3 additions & 0 deletions buildSrc/src/main/kotlin/dependencies/Libraries.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ object Libraries {
val ZIP4J = "net.lingala.zip4j:zip4j:${Versions.ZIP4J}"
val ITEXTPDF = "com.itextpdf:itextpdf:${Versions.ITEXTPDF}"
val BATIK_TRANSCODER = "org.apache.xmlgraphics:batik-transcoder:${Versions.BATIK_TRANSCODER}"
val BATIK_CODEC = "org.apache.xmlgraphics:batik-codec:${Versions.BATIK_CODEC}"
val SNAKEYAML = "org.yaml:snakeyaml:${Versions.SNAKEYAML}"
val SYSTEM_TRAY = "com.dorkbox:SystemTray:${Versions.SYSTEM_TRAY}"
val BOUNCYCASTLE = "org.bouncycastle:bcprov-jdk15on:${Versions.BOUNCYCASTLE}"
Expand All @@ -27,10 +28,12 @@ object Libraries {
val APACHE_COMMONS_LANG3 = "org.apache.commons:commons-lang3:${Versions.APACHE_COMMONS_LANG3}"
val KOTLESS_KTOR = "io.kotless:ktor-lang:${Versions.KOTLESS_KTOR}"
val TESTING_MOCKK = "io.mockk:mockk:${Versions.TESTING_MOCKK}"
val KOTLINX_ATOMICFU_GRADLE = "org.jetbrains.kotlinx:atomicfu-gradle-plugin:${Versions.KOTLINX_ATOMICFU_GRADLE}"

object Buildscript {
val PROGUARD_GRADLE_ACTUAL = PROGUARD_GRADLE
val WCA_I18N_ACTUAL = WCA_I18N
val GOOGLE_APPENGINE_GRADLE_ACTUAL = GOOGLE_APPENGINE_GRADLE
val KOTLINX_ATOMICFU_GRADLE_ACTUAL = KOTLINX_ATOMICFU_GRADLE
}
}
7 changes: 5 additions & 2 deletions buildSrc/src/main/kotlin/dependencies/Plugins.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,13 @@ object Plugins {
inline val PluginDependenciesSpec.GIT_VERSION_TAG_ACTUAL: PluginDependencySpec
get() = id("com.palantir.git-version").version(Versions.Plugins.GIT_VERSION_TAG)

inline val PluginDependenciesSpec.KOTLESS_ACTUAL: PluginDependencySpec
get() = id("io.kotless").version(Versions.Plugins.KOTLESS)

// not versioned because the classpath from the buildscript {} block already implies a version
inline val PluginDependenciesSpec.GOOGLE_APPENGINE_ACTUAL: PluginDependencySpec
get() = id("com.google.cloud.tools.appengine")

inline val PluginDependenciesSpec.KOTLESS_ACTUAL: PluginDependencySpec
get() = id("io.kotless").version(Versions.Plugins.KOTLESS)
inline val PluginDependenciesSpec.KOTLINX_ATOMICFU_ACTUAL: PluginDependencySpec
get() = id("kotlinx-atomicfu")
}
2 changes: 2 additions & 0 deletions buildSrc/src/main/kotlin/dependencies/Versions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ object Versions {
val ZIP4J = "2.6.3"
val ITEXTPDF = "5.5.13.2"
val BATIK_TRANSCODER = BATIK
val BATIK_CODEC = BATIK
val SNAKEYAML = "1.27"
val SYSTEM_TRAY = "3.17"
val BOUNCYCASTLE = "1.66"
Expand All @@ -34,6 +35,7 @@ object Versions {
val APACHE_COMMONS_LANG3 = "3.11"
val KOTLESS_KTOR = KOTLESS
val TESTING_MOCKK = "1.10.2"
val KOTLINX_ATOMICFU_GRADLE = "0.14.4"

object Plugins {
val SHADOW = "6.0.0"
Expand Down
3 changes: 3 additions & 0 deletions cloudscrambles/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import configurations.Languages.attachRemoteRepositories
import configurations.ProjectVersions.tNoodleImplOrDefault
import configurations.ProjectVersions.tNoodleVersionOrDefault
import dependencies.Libraries.BATIK_TRANSCODER
import dependencies.Libraries.BATIK_CODEC
import dependencies.Libraries.GOOGLE_CLOUD_STORAGE
import dependencies.Libraries.KOTLESS_KTOR
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
Expand All @@ -26,6 +27,8 @@ dependencies {
//implementation(KOTLESS_KTOR)
implementation(GOOGLE_CLOUD_STORAGE)
implementation(BATIK_TRANSCODER)

runtimeOnly(BATIK_CODEC)
}

tasks.withType<KotlinCompile> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,43 +9,26 @@ import io.ktor.response.respondText
import io.ktor.routing.Route
import io.ktor.routing.get
import io.ktor.routing.route
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.apache.batik.anim.dom.SVGDOMImplementation
import org.apache.batik.transcoder.TranscoderInput
import org.apache.batik.transcoder.TranscoderOutput
import org.apache.batik.transcoder.TranscodingHints
import org.apache.batik.transcoder.image.ImageTranscoder
import org.apache.batik.transcoder.image.PNGTranscoder
import org.apache.batik.util.SVGConstants
import org.worldcubeassociation.tnoodle.scrambles.Puzzle
import org.worldcubeassociation.tnoodle.server.RouteHandler
import org.worldcubeassociation.tnoodle.server.cloudscrambles.serial.PuzzleImageJsonData
import org.worldcubeassociation.tnoodle.server.model.PuzzleData
import org.worldcubeassociation.tnoodle.svglite.Svg
import java.awt.image.BufferedImage
import java.io.ByteArrayOutputStream
import javax.imageio.ImageIO

object ScrambleViewHandler : RouteHandler {
private const val PUZZLE_KEY_PARAM = PuzzleListHandler.PUZZLE_KEY_PARAM

private const val QUERY_SCRAMBLE_PARAM = "scramble"
private const val QUERY_COLOR_SCHEME_PARAM = "scheme"

// Copied from http://bbgen.net/blog/2011/06/java-svg-to-bufferedimage/
internal class BufferedImageTranscoder : ImageTranscoder() {
var bufferedImage: BufferedImage? = null
private set

override fun createImage(w: Int, h: Int): BufferedImage {
return BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB)
}

override fun writeImage(img: BufferedImage, output: TranscoderOutput) {
this.bufferedImage = img
}
}

private suspend fun ApplicationCall.withScramble(handle: suspend ApplicationCall.(Puzzle, Svg) -> Unit) {
val name = parameters[PUZZLE_KEY_PARAM]
?: return respondText("Please specify a puzzle")
Expand All @@ -67,37 +50,32 @@ object ScrambleViewHandler : RouteHandler {
route("{$PUZZLE_KEY_PARAM}") {
get("png") {
call.withScramble { puzzle, svg ->
val svgFile = svg.toString().byteInputStream()

val (width, height) = puzzle.preferredSize.let { it.width to it.height }
val imageTranscoder = BufferedImageTranscoder()

// Copied from http://stackoverflow.com/a/6634963
// with some tweaks.
val impl = SVGDOMImplementation.getDOMImplementation()

val hints = TranscodingHints().apply {
this[ImageTranscoder.KEY_WIDTH] = width.toFloat()
this[ImageTranscoder.KEY_HEIGHT] = height.toFloat()
this[ImageTranscoder.KEY_DOM_IMPLEMENTATION] = impl
this[ImageTranscoder.KEY_DOM_IMPLEMENTATION] = SVGDOMImplementation.getDOMImplementation()
this[ImageTranscoder.KEY_DOCUMENT_ELEMENT_NAMESPACE_URI] = SVGConstants.SVG_NAMESPACE_URI
this[ImageTranscoder.KEY_DOCUMENT_ELEMENT_NAMESPACE_URI] = SVGConstants.SVG_NAMESPACE_URI
this[ImageTranscoder.KEY_DOCUMENT_ELEMENT] = SVGConstants.SVG_SVG_TAG
this[ImageTranscoder.KEY_XML_PARSER_VALIDATING] = false
}

imageTranscoder.transcodingHints = hints
val imageTranscoder = PNGTranscoder().apply {
transcodingHints = hints
}

val svgFile = svg.toString().byteInputStream()
val input = TranscoderInput(svgFile)
imageTranscoder.transcode(input, TranscoderOutput())

val img = imageTranscoder.bufferedImage

val bytes = ByteArrayOutputStream().also {
withContext(Dispatchers.IO) { ImageIO.write(img, "png", it) }
}
val outputBytes = ByteArrayOutputStream()
val output = TranscoderOutput(outputBytes)
imageTranscoder.transcode(input, output)

respondBytes(bytes.toByteArray(), ContentType.Image.PNG)
respondBytes(outputBytes.toByteArray(), ContentType.Image.PNG)
}
}
get("svg") {
Expand Down
1 change: 1 addition & 0 deletions tnoodle-server/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ attachRemoteRepositories()
plugins {
kotlin("jvm")
KOTLIN_SERIALIZATION
KOTLINX_ATOMICFU
}

dependencies {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.worldcubeassociation.tnoodle.server.model.cache

import kotlinx.atomicfu.atomic
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.produce
import org.worldcubeassociation.tnoodle.scrambles.Puzzle
Expand All @@ -8,22 +9,24 @@ import java.util.concurrent.ThreadFactory
import kotlin.coroutines.CoroutineContext

class CoroutineScrambleCacher(val puzzle: Puzzle, capacity: Int) : CoroutineScope {
var available: Int = 0
private set

override val coroutineContext: CoroutineContext
get() = JOB_CONTEXT

@ExperimentalCoroutinesApi
private val buffer = produce(capacity = capacity) {
while (true) {
send(puzzle.generateScramble())
.also { synchronized(available) { available++ } }
.also { size += 1 }
}
}

private val size = atomic(0)

val available: Int
get() = size.value

suspend fun yieldScramble(): String = buffer.receive()
.also { synchronized(available) { available-- } }
.also { size -= 1 }

fun getScramble(): String = runBlocking { yieldScramble() }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ import io.ktor.application.call
import io.ktor.response.respond
import io.ktor.routing.Route
import io.ktor.routing.get
import kotlinx.serialization.json.json

import org.worldcubeassociation.tnoodle.server.RouteHandler
import org.worldcubeassociation.tnoodle.server.crypto.AsymmetricCipher
import org.worldcubeassociation.tnoodle.server.ServerEnvironmentConfig
import org.worldcubeassociation.tnoodle.server.serial.VersionInfo

Expand Down
2 changes: 1 addition & 1 deletion tnoodle-server/src/main/resources/logback.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<configuration>

<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>logging/tnoodle.log</file>
<file>tnoodle.log</file>

<encoder>
<pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class WebscramblesServer(val environmentConfig: ServerEnvironmentConfig) : Appli

val desiredJsEnv by parser.adding("--jsenv", help = "Add entry to global js object TNOODLE_ENV in /env.js. Treated as strings, so FOO=42 will create the entry TNOODLE_ENV['FOO'] = '42';")

val cliPort by parser.storing("-p", "--port", help = "Start TNoodle on given port. Should be numeric. Defaults to ${OfflineJarUtils.TNOODLE_PORT}", transform = { toInt() })
val cliPort by parser.storing("-p", "--port", help = "Start TNoodle on given port. Should be numeric. Defaults to ${OfflineJarUtils.TNOODLE_PORT}", transform = String::toInt)
.default(OfflineJarUtils.TNOODLE_PORT)

val noBrowser by parser.flagging("-n", "--nobrowser", help = "Don't open the browser when starting the server")
Expand All @@ -96,15 +96,11 @@ class WebscramblesServer(val environmentConfig: ServerEnvironmentConfig) : Appli
val port = System.getenv("PORT")?.takeIf { onlineConfig }?.toIntOrNull() ?: cliPort
val offlineHandler = OfflineJarUtils(port)

offlineHandler.setApplicationIcon()

if (!noReexec) {
val isWrapped = if (!noReexec) {
MainLauncher.wrapMain(args, MIN_HEAP_SIZE_MEGS)
} else false

// This second call to setApplicationIcon() is intentional.
// We want different icons for the parent and child processes.
offlineHandler.setApplicationIcon()
}
offlineHandler.setApplicationIcon(isWrapped)

for (jsEnv in desiredJsEnv) {
val (key, strValue) = jsEnv.split("=", limit = 2)
Expand All @@ -113,7 +109,7 @@ class WebscramblesServer(val environmentConfig: ServerEnvironmentConfig) : Appli

if (!noUpgrade) {
try {
val shutdownMaybe = URL("http://localhost:$port${TNoodleServer.KILL_URL}").openStream()
val shutdownMaybe = URL("${offlineHandler.url}${TNoodleServer.KILL_URL}").openStream()
shutdownMaybe.close()
} catch (ignored: IOException) {
// NOOP. This means we couldn't connect to localhost:$PORT
Expand All @@ -129,8 +125,11 @@ class WebscramblesServer(val environmentConfig: ServerEnvironmentConfig) : Appli

LOG.info("${LocalServerEnvironmentConfig.title} started")

val url = offlineHandler.openTabInBrowser(!noBrowser)
LOG.info("Visit $url for a readme and demo.")
if (!noBrowser) {
offlineHandler.openTabInBrowser()
} else {
LOG.info("Visit ${offlineHandler.url} for a readme and demo.")
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,14 @@ import java.io.ByteArrayOutputStream
abstract class BasePdfSheet<W : PdfWriter> : PdfContent {
open fun openDocument() = Document()

private var renderingCache: ByteArray? = null
private val renderingCache by lazy { directRender(null) }

override fun render(password: String?): ByteArray {
return renderingCache?.takeIf { password == null } ?: directRender(password)
if (password == null) {
return renderingCache
}

return directRender(password)
}

private fun directRender(password: String?): ByteArray {
Expand All @@ -29,7 +33,6 @@ abstract class BasePdfSheet<W : PdfWriter> : PdfContent {
pdfDocument.close()

return this.finalise(pdfBytes, password)
.also { if (password == null) renderingCache = it }
}

abstract fun W.writeContents(document: Document)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -362,9 +362,10 @@ open class FmcSolutionSheet(scrambleSet: ScrambleSet, activityCode: ActivityCode

companion object {
const val FMC_LINE_THICKNESS = 0.5f

const val UNDERLINE_THICKNESS = 0.2f

const val LEADING_MULTIPLIER = 1.3f

const val FORM_TEMPLATE_WCA_ID = "WCA ID: __ __ __ __ __ __ __ __ __ __"

const val SHORT_FILL = ": ____"
Expand All @@ -374,7 +375,5 @@ open class FmcSolutionSheet(scrambleSet: ScrambleSet, activityCode: ActivityCode
val WCA_ROTATIONS = arrayOf("x", "y", "z", "", "", "")

val DIRECTION_MODIFIERS = arrayOf("", "'", "2")

var LEADING_MULTIPLIER = 1.3f
}
}
Loading

0 comments on commit 3ada24c

Please sign in to comment.