From 97e318d6c12f6842d69925b530831a1b5fddd547 Mon Sep 17 00:00:00 2001 From: Rafael Lins Date: Mon, 11 Aug 2025 18:21:25 -0300 Subject: [PATCH 1/6] feat(wasm-js): WASM JS Compose support, adding caption example --- build.gradle.kts | 1 + examples/android/build.gradle.kts | 7 +- examples/kotlin/build.gradle.kts | 5 +- .../example08-caption-customFont.png | Bin 0 -> 32621 bytes .../examples-results/example08-caption.png | Bin 0 -> 32978 bytes .../src/main/kotlin/Example08-TextCaption.kt | 125 ++++++++++++ .../resources/MozillaHeadline-Regular.ttf | Bin 0 -> 66228 bytes gradle.properties | 2 +- gradle/libs.versions.toml | 21 +- .../qrcode/render/QRCodeGraphics.android.kt | 3 +- .../extensions/AndroidComposeExtensions.kt | 2 +- src/commonMain/kotlin/qrcode/QRCode.kt | 19 +- .../qrcode/render/QRCodeGraphics.wasmjs.kt | 140 ++++---------- .../extensions/WasmJsComposeExtensions.kt | 90 +++++++++ .../render/graphics/DrawScopeGraphics.kt | 179 ++++++++++++++++++ .../render/graphics/HTMLCanvasGraphics.kt | 161 ++++++++++++++++ .../render/graphics/WasmJsDrawingInterface.kt | 24 +++ 17 files changed, 660 insertions(+), 119 deletions(-) create mode 100644 examples/kotlin/examples-results/example08-caption-customFont.png create mode 100644 examples/kotlin/examples-results/example08-caption.png create mode 100644 examples/kotlin/src/main/kotlin/Example08-TextCaption.kt create mode 100644 examples/kotlin/src/main/resources/MozillaHeadline-Regular.ttf create mode 100644 src/wasmJsMain/kotlin/qrcode/render/extensions/WasmJsComposeExtensions.kt create mode 100644 src/wasmJsMain/kotlin/qrcode/render/graphics/DrawScopeGraphics.kt create mode 100644 src/wasmJsMain/kotlin/qrcode/render/graphics/HTMLCanvasGraphics.kt create mode 100644 src/wasmJsMain/kotlin/qrcode/render/graphics/WasmJsDrawingInterface.kt diff --git a/build.gradle.kts b/build.gradle.kts index 20820044..3348f053 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -140,6 +140,7 @@ kotlin { wasmJsMain { dependencies { implementation(libs.kotlinx.browser) + compileOnly(libs.jetbrains.compose.ui) } } } diff --git a/examples/android/build.gradle.kts b/examples/android/build.gradle.kts index 5a8f1912..5cbdda51 100644 --- a/examples/android/build.gradle.kts +++ b/examples/android/build.gradle.kts @@ -6,7 +6,7 @@ plugins { android { namespace = "io.github.g0dkar.qrcode" - compileSdk = 35 + compileSdk = 36 defaultConfig { applicationId = "io.github.g0dkar.qrcodeKotlin" @@ -29,7 +29,10 @@ android { targetCompatibility = JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = "17" + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } } buildFeatures { viewBinding = true diff --git a/examples/kotlin/build.gradle.kts b/examples/kotlin/build.gradle.kts index cb3f4658..4fc7cbac 100644 --- a/examples/kotlin/build.gradle.kts +++ b/examples/kotlin/build.gradle.kts @@ -8,6 +8,7 @@ repositories { } dependencies { - implementation("io.github.g0dkar:qrcode-kotlin:4.5.0") - implementation("org.jfree:org.jfree.svg:5.0.5") + implementation(project(":")) +// implementation("io.github.g0dkar:qrcode-kotlin:4.6.0") + implementation("org.jfree:org.jfree.svg:5.0.7") } diff --git a/examples/kotlin/examples-results/example08-caption-customFont.png b/examples/kotlin/examples-results/example08-caption-customFont.png new file mode 100644 index 0000000000000000000000000000000000000000..a9d7bfcdf2a567fd99073d3edcb2bc32b7e6621e GIT binary patch literal 32621 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 RK(JXmx4CT1*nITj{{g`C+ { + val targetWidth = floor(width * 0.9) + var textSize = graphics.font.size.toFloat() + var textBounds: Rectangle2D + + do { + graphics.font = graphics.font.deriveFont(textSize) + textBounds = graphics.fontMetrics.getStringBounds(text, graphics) + textSize += fontSizeIncrease + } while (textBounds.width <= targetWidth) + + return textBounds.width.toInt() to textBounds.height.toInt() +} diff --git a/examples/kotlin/src/main/resources/MozillaHeadline-Regular.ttf b/examples/kotlin/src/main/resources/MozillaHeadline-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..955455da31e9dde2a8f7ca717a9feab9c6cb88d8 GIT binary patch literal 66228 zcmb@v2YePq{`fz$o1TzDLdWDu=q(8WX`<4*jUq(_0tpZZ35Fnu4eK?}Q}6WD^E|tr zy`f-1Y@mW2L$E*)kP=!*2oUmQ|L@Q2KFJfna^L?i`+Dzeot>Ted_FVtnRzx!E2ZLD zg(x+0*zgf;o3`Ghlr@l}heuv~(S**spY5a6kW8hncyQ!|Nkh7P?0;OT7K4akI&q*-&b7cb&?Jm-@L zBj)B`J!k6bK~E_4AI{I(GcPwgr~j4LSe&29`9AYF(Cqx?ec6}vw4b-Icv;6mm;9(y zIH?VPF28Vg_NRqAs+F?4L+QfoWs9sBc_H1Fm9HRsVQz=57x4Vpmw4PAiwYMPce}hx zZ%&cECRvM$au>}DyZu(KkKy|8Roc1rrI5d}tF+^&I6|e%)nc~(qMFEF&|ipL#ZzB) zSU270*mYMo{iD4n=n6KuTke8Y&&<;^;szd7wtLOa>9e=XwL2Gm{f=Wh-J1R}ddAc{}<0&<{&eDBU4tLpL>7M#dg16ZYu`Xge zl)XP&Q`jHthI+cn(r2?@|M##`hlVKMWq|`1UUcDbwN^E89QXY~yQyz|O8s5VTg|Ok z2z;ukJXyucG5$)~PUT26G*OAHo2z8jKGlwOM;5KRsJ^TRsL`y)sY$FStJ$n`RUYdF z>U!2I)QzlfQMa@Ho4SYfKh=Y*|Dzsd{kVFb^^0mX>$lXqtlwwR>O-}W_2=p<)|=E; z)@5op>wW4tYrhUvT8C>=rJLzitlQ|Gtk2hjSih~;DqFv=zhwQj-b^k^o+2>3k!8** z&VDTUsFKv#YOuOMO(o4&s_WFt>K*loI;petEM1_N>)Z6*`gN}UR99PxR*Kcl>T3

~KNJ3S_}1`!;nfjVL~KOsh>j6i5$8o*60s=a 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 literal 0 HcmV?d00001 diff --git a/gradle.properties b/gradle.properties index fd903547..d4936491 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=4.5.0 +version=4.6.0 kotlin.code.style=official # JS diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 30d7a79a..2d1ed9cd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,21 +1,21 @@ [versions] annotation = "1.9.1" -kotlin = "2.1.21" +kotlin = "2.2.0" dokka = "2.0.0" kotest = "6.0.0.M3" -lifecycleLivedataKtx = "2.9.1" -lifecycleViewmodelKtx = "2.9.1" +lifecycleLivedataKtx = "2.9.2" +lifecycleViewmodelKtx = "2.9.2" mavenNexus = "2.0.0" -androidPlugin = "8.10.1" +androidPlugin = "8.11.1" npmPublish = "3.5.3" core-ktx = "1.16.0" -appcompat = "1.7.0" +appcompat = "1.7.1" material = "1.12.0" constraintlayout = "2.2.1" -navigation-fragment-ktx = "2.9.0" -navigation-ui-ktx = "2.9.0" -androidx-compose = "1.8.2" -kotlinx-browser = "0.3" +navigation-fragment-ktx = "2.9.3" +navigation-ui-ktx = "2.9.3" +jetbrains-compose = "1.8.2" +kotlinx-browser = "0.4" [libraries] androidx-annotation = { module = "androidx.annotation:annotation", version.ref = "annotation" } @@ -32,7 +32,8 @@ material = { group = "com.google.android.material", name = "material", version.r constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } navigation-fragment-ktx = { group = "androidx.navigation", name = "navigation-fragment-ktx", version.ref = "navigation-fragment-ktx" } navigation-ui-ktx = { group = "androidx.navigation", name = "navigation-ui-ktx", version.ref = "navigation-ui-ktx" } -androidx-compose-ui = { group = "androidx.compose.ui", name = "ui", version.ref = "androidx-compose" } +androidx-compose-ui = { group = "androidx.compose.ui", name = "ui", version.ref = "jetbrains-compose" } +jetbrains-compose-ui = { group = "org.jetbrains.compose.ui", name = "ui", version.ref = "jetbrains-compose" } kotlinx-browser = { module = "org.jetbrains.kotlinx:kotlinx-browser", version.ref = "kotlinx-browser" } [plugins] diff --git a/src/androidMain/kotlin/qrcode/render/QRCodeGraphics.android.kt b/src/androidMain/kotlin/qrcode/render/QRCodeGraphics.android.kt index 7875f986..c8481563 100644 --- a/src/androidMain/kotlin/qrcode/render/QRCodeGraphics.android.kt +++ b/src/androidMain/kotlin/qrcode/render/QRCodeGraphics.android.kt @@ -100,7 +100,8 @@ actual open class QRCodeGraphics actual constructor( actual open fun availableFormats(): Array = AVAILABLE_FORMATS /** Returns the [Bitmap] or [DrawScope] (if Jetpack Compose is available) object being worked upon. */ - actual open fun nativeImage(): Any = drawingInterface?.nativeImage() ?: throw NotImplementedError("Native image not supported") + actual open fun nativeImage(): Any = + drawingInterface?.nativeImage() ?: throw NotImplementedError("Native image not supported") /** Draw a straight line from point `(x1,y1)` to `(x2,y2)`. */ actual open fun drawLine(x1: Int, y1: Int, x2: Int, y2: Int, color: Int, thickness: Double) { diff --git a/src/androidMain/kotlin/qrcode/render/extensions/AndroidComposeExtensions.kt b/src/androidMain/kotlin/qrcode/render/extensions/AndroidComposeExtensions.kt index 430716b0..2e6ec5e4 100644 --- a/src/androidMain/kotlin/qrcode/render/extensions/AndroidComposeExtensions.kt +++ b/src/androidMain/kotlin/qrcode/render/extensions/AndroidComposeExtensions.kt @@ -9,7 +9,7 @@ import qrcode.QRCode import qrcode.render.graphics.DrawScopeGraphics /** - * Extension function to make it easier to draw a [QRCode] into a modern Jetpack Compose [Canvas]. + * Extension function to make it easier to draw a [QRCode] into a modern Compose [Canvas]. * * Usage example: * diff --git a/src/commonMain/kotlin/qrcode/QRCode.kt b/src/commonMain/kotlin/qrcode/QRCode.kt index 2a21b720..bee9f9bf 100644 --- a/src/commonMain/kotlin/qrcode/QRCode.kt +++ b/src/commonMain/kotlin/qrcode/QRCode.kt @@ -150,14 +150,25 @@ class QRCode @JvmOverloads constructor( /** * Size of the canvas where the QRCode will be drawn into (the final image will be a square of `canvasSize` by `canvasSize`) - * - * - * - */ + */ var canvasSize: Int = if (canvasSize > DEFAULT_QRCODE_SIZE) canvasSize else qrCodeProcessor.computeImageSize(squareSize, rawData) private set + /** + * Width of the canvas where the QRCode will be drawn into + * + * @see canvasSize + */ + val width: Int = this.canvasSize + + /** + * Height of the canvas where the QRCode will be drawn into + * + * @see canvasSize + */ + val height: Int = this.canvasSize + /** Size of the canvas where the QRCode will be drawn into. */ @Deprecated("Please use canvasSize instead.") val computedSize: Int diff --git a/src/wasmJsMain/kotlin/qrcode/render/QRCodeGraphics.wasmjs.kt b/src/wasmJsMain/kotlin/qrcode/render/QRCodeGraphics.wasmjs.kt index b726364a..8476db46 100644 --- a/src/wasmJsMain/kotlin/qrcode/render/QRCodeGraphics.wasmjs.kt +++ b/src/wasmJsMain/kotlin/qrcode/render/QRCodeGraphics.wasmjs.kt @@ -1,59 +1,21 @@ package qrcode.render -import kotlinx.browser.document -import org.khronos.webgl.Uint8ClampedArray -import org.khronos.webgl.toInt8Array -import org.w3c.dom.CanvasRenderingContext2D import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.ImageData import org.w3c.files.Blob +import qrcode.render.graphics.HTMLCanvasGraphics +import qrcode.render.graphics.WasmJsDrawingInterface @Suppress("MemberVisibilityCanBePrivate") +@OptIn(ExperimentalWasmJsInterop::class) actual open class QRCodeGraphics actual constructor( val width: Int, val height: Int, ) { - companion object { - private const val CANVAS_UNSUPPORTED = "Canvas seems to not be supported :(" - private const val FULL_CIRCLE = 3.141592653589793 * 2.0 // 2 * PI = Full circle - } + var drawingInterface: WasmJsDrawingInterface? = null - private val canvas: HTMLCanvasElement + /** Whether any drawing operations were done or not. */ private var changed: Boolean = false - init { - val canvas = tryGet { document.createElement("canvas") as HTMLCanvasElement } - - canvas.width = width - canvas.height = height - - this.canvas = canvas - } - - private fun rgba(color: Int): String { - val r = (color shr 16) and 0xFF - val g = (color shr 8) and 0xFF - val b = (color shr 0) and 0xFF - val a = ((color shr 24) and 0xFF) / 255.0 - return "rgba($r,$g,$b,$a)" - } - - private fun draw(color: Int, action: CanvasRenderingContext2D.() -> Unit) { - changed = true - - val context = tryGet { canvas.getContext("2d") as CanvasRenderingContext2D } - - val colorString = rgba(color) - context.fillStyle = colorString.toJsString() - context.strokeStyle = colorString.toJsString() - - val lineWidth = context.lineWidth - - action(context) - - context.lineWidth = lineWidth - } - /** Returns `true` if **any** drawing was performed */ actual open fun changed() = changed @@ -61,24 +23,47 @@ actual open class QRCodeGraphics actual constructor( actual fun reset() { if (changed) { changed = false - draw(0) { clearRect(0.0, 0.0, width.toDouble(), height.toDouble()) } } } + /** + * Make sure we can use the [drawingInterface]. Never mind the name. + */ + private fun useCanvas(): WasmJsDrawingInterface { + if (drawingInterface == null) { + drawingInterface = HTMLCanvasGraphics(width, height) + } + + return drawingInterface!! + } + /** Return the dimensions of this Graphics object as a pair of `width, height` */ actual open fun dimensions() = arrayOf(width, height) /** * Returns a Data URL to this can be shown in an `` tag. */ - open fun toDataURL(format: String = "png"): String = canvas.toDataURL(format) + open fun toDataURL(format: String = "png"): String = + nativeImage().let { + when (it) { + is HTMLCanvasElement -> it.toDataURL(format) + else -> throw Error("Unsupported operation") + } + } /** * Direct access to the `.toBlob()` function of the underlying canvas. * * Syntactic sugar for `nativeImage().toBlob(callback)`. */ - open fun toBlob(callback: (Blob?) -> Unit): Unit = canvas.toBlob(callback) + open fun toBlob(callback: (Blob?) -> Unit) { + nativeImage().let { + when (it) { + is HTMLCanvasElement -> it.toBlob(callback) + else -> throw Error("Unsupported operation") + } + } + } /** Returns this image as a [ByteArray] encoded as PNG. */ actual open fun getBytes(): ByteArray = getBytes("png") @@ -86,7 +71,7 @@ actual open class QRCodeGraphics actual constructor( /** Returns this image as a [ByteArray] encoded as the specified format (e.g. `PNG`, `JPG`, `BMP`, ...). */ @JsName("getBytesForFormat") actual open fun getBytes(format: String): ByteArray = - canvas.toDataURL(format).encodeToByteArray() + useCanvas().getBytes(format) /** Returns the available formats to be passed as parameters to [getBytes]. * @@ -95,35 +80,22 @@ actual open class QRCodeGraphics actual constructor( actual open fun availableFormats(): Array = arrayOf("png") /** Returns the native image object this QRCodeGraphics is working upon. */ - actual open fun nativeImage(): Any = canvas + actual open fun nativeImage(): Any = + drawingInterface?.nativeImage() ?: throw NotImplementedError("Native image not supported") /** Draw a straight line from point `(x1,y1)` to `(x2,y2)`. */ actual open fun drawLine(x1: Int, y1: Int, x2: Int, y2: Int, color: Int, thickness: Double) { - draw(color) { - moveTo(x1.toDouble(), y1.toDouble()) - lineTo(x2.toDouble(), y2.toDouble()) - } + useCanvas().drawLine(x1, y1, x2, y2, color, thickness) } /** Draw the edges of a rectangle starting at point `(x,y)` and having `width` by `height`. */ actual open fun drawRect(x: Int, y: Int, width: Int, height: Int, color: Int, thickness: Double) { - draw(color) { - lineWidth = thickness - val halfThickness = thickness / 2.0 - strokeRect( - x.toDouble() + halfThickness, - y.toDouble() + halfThickness, - width.toDouble() - thickness, - height.toDouble() - thickness, - ) - } + useCanvas().drawRect(x, y, width, height, color, thickness) } /** Fills the rectangle starting at point `(x,y)` and having `width` by `height`. */ actual open fun fillRect(x: Int, y: Int, width: Int, height: Int, color: Int) { - draw(color) { - fillRect(x.toDouble(), y.toDouble(), width.toDouble(), height.toDouble()) - } + useCanvas().fillRect(x, y, width, height, color) } /** Fill the whole area of this canvas with the specified [color]. */ @@ -161,7 +133,7 @@ actual open class QRCodeGraphics actual constructor( color: Int, thickness: Double, ) { - drawRect(x, y, width, height, color, 1.0) + useCanvas().drawRoundRect(x, y, width, height, borderRadius, color, thickness) } /** @@ -193,22 +165,14 @@ actual open class QRCodeGraphics actual constructor( borderRadius: Int, color: Int, ) { - fillRect(x, y, width, height, color) + useCanvas().fillRoundRect(x, y, width, height, borderRadius, color) } /** * Draw the edges of an ellipse (aka "a circle") which occupies the area `(x,y,width,height)` */ actual fun drawEllipse(x: Int, y: Int, width: Int, height: Int, color: Int, thickness: Double) { - draw(color) { - val radiusX = width.toDouble() / 2.0 - val radiusY = height.toDouble() / 2.0 - - lineWidth = thickness - beginPath() - ellipse(radiusX + x.toDouble(), radiusY + y.toDouble(), radiusX, radiusY, 0.0, 0.0, FULL_CIRCLE, false) - stroke() - } + useCanvas().drawEllipse(x, y, width, height, color, thickness) } /** @@ -216,14 +180,7 @@ actual open class QRCodeGraphics actual constructor( * */ actual fun fillEllipse(x: Int, y: Int, width: Int, height: Int, color: Int) { - draw(color) { - val radiusX = width.toDouble() / 2.0 - val radiusY = height.toDouble() / 2.0 - - beginPath() - ellipse(radiusX + x.toDouble(), radiusY + y.toDouble(), radiusX, radiusY, 0.0, 0.0, FULL_CIRCLE, false) - fill() - } + useCanvas().fillEllipse(x, y, width, height, color) } /** @@ -234,19 +191,6 @@ actual open class QRCodeGraphics actual constructor( */ @JsName("drawImageFromBytes") actual fun drawImage(rawData: ByteArray?, x: Int, y: Int) { - if (rawData != null && rawData.isNotEmpty()) { - draw(0) { - val imageDataArray: JsArray = rawData.toInt8Array().unsafeCast() - val imageData = ImageData(Uint8ClampedArray(imageDataArray), width) - putImageData(imageData, x.toDouble(), y.toDouble()) - } - } + useCanvas().drawImage(rawData, x, y) } - - private fun tryGet(what: () -> T): T = - try { - what() - } catch (t: Throwable) { - throw Error(CANVAS_UNSUPPORTED, cause = t) - } } diff --git a/src/wasmJsMain/kotlin/qrcode/render/extensions/WasmJsComposeExtensions.kt b/src/wasmJsMain/kotlin/qrcode/render/extensions/WasmJsComposeExtensions.kt new file mode 100644 index 00000000..6907aa2d --- /dev/null +++ b/src/wasmJsMain/kotlin/qrcode/render/extensions/WasmJsComposeExtensions.kt @@ -0,0 +1,90 @@ +package qrcode.render.extensions + +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Canvas +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.decodeToImageBitmap +import androidx.compose.ui.graphics.drawscope.DrawScope +import androidx.compose.ui.graphics.drawscope.translate +import qrcode.QRCode +import qrcode.render.QRCodeGraphics +import qrcode.render.graphics.DrawScopeGraphics + +/** + * Extension function to make it easier to draw a [QRCode] into a modern Compose [Canvas]. + * + * Usage example: + * + * ```kotlin + * import qrcode.render.extensions.drawQRCode + * + * @Composable + * fun QRCodeKotlinQRCode( + * text: String, + * modifier: Modifier = Modifier, + * ) { + * val qrCode = remember(text) { + * QRCode.ofRoundedSquares() + * .build(text) + * } + * + * Canvas( + * modifier = modifier + * .aspectRatio(1f) + * ) { + * drawQRCode(qrCode) // Draw the QRCode at (0, 0) + * } + * } + * ``` + * + * **Code sample by @dgmltn at GitHub**, used with authors' permission and lightly modified :) + * + * Original code: https://github.com/g0dkar/qrcode-kotlin/issues/141#issuecomment-2722041216 + * + * @param qrCode The [QRCode] that will be drawn into the [DrawScope]. + * @param offsetTopLeft The [Offset] of the top-left corner where the QRCode will be drawn. Defaults to [Offset.Zero]. + * @param sizeToFitInto The area in which to fit the QRCode. Defaults to the `size` of the [DrawScope]. + * + */ +fun DrawScope.drawQRCode( + qrCode: QRCode, + offsetTopLeft: Offset = Offset.Zero, + sizeToFitInto: Size = this.size, +) { + val width = sizeToFitInto.width.toInt() + val height = sizeToFitInto.height.toInt() + + qrCode.fitIntoArea(width, height) + + val qrCodeGraphics = qrCode.graphics + val previousDrawingInterface = qrCodeGraphics.drawingInterface + + val drawScopeGraphics = DrawScopeGraphics(this, qrCodeGraphics.width, qrCodeGraphics.height) + qrCodeGraphics.drawingInterface = drawScopeGraphics + + translate(offsetTopLeft.x, offsetTopLeft.y) { + qrCode.render() + } + + qrCodeGraphics.drawingInterface = previousDrawingInterface +} + +/** + * Extension function to generate a QRCode as a Compose [ImageBitmap]. + * + * Code largely "inspired" (read: copied) from [this](https://github.com/g0dkar/qrcode-kotlin/issues/187#issuecomment-3119959105) comment by `bxkr` + * + * All credits to [@bxkr](https://github.com/bxkr) - thanks! + */ +fun QRCode.renderToPNGImageBitmap( + qrCodeGraphics: QRCodeGraphics = graphics, + xOffset: Int = this.xOffset, + yOffset: Int = this.yOffset, + format: String = "PNG", +): ImageBitmap = + render(qrCodeGraphics, xOffset, yOffset) + .toDataURL(format) + .substringAfter("data:image/png;base64,") + .encodeToByteArray() + .decodeToImageBitmap() diff --git a/src/wasmJsMain/kotlin/qrcode/render/graphics/DrawScopeGraphics.kt b/src/wasmJsMain/kotlin/qrcode/render/graphics/DrawScopeGraphics.kt new file mode 100644 index 00000000..f69fba31 --- /dev/null +++ b/src/wasmJsMain/kotlin/qrcode/render/graphics/DrawScopeGraphics.kt @@ -0,0 +1,179 @@ +package qrcode.render.graphics + +import androidx.compose.ui.geometry.CornerRadius +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.drawscope.DrawScope +import androidx.compose.ui.graphics.drawscope.Fill +import androidx.compose.ui.graphics.drawscope.Stroke +import qrcode.color.Colors + +class DrawScopeGraphics( + val drawScope: DrawScope, val width: Int, val height: Int, +) : WasmJsDrawingInterface { + private val paintCache = mutableMapOf() + + private fun composeColor(color: Int): Color { + return paintCache.getOrPut(color) { + val (r, g, b, a) = Colors.getRGBA(color) + Color(r, g, b, a) + } + } + + private fun offset(x: Int, y: Int) = Offset(x.toFloat(), y.toFloat()) + + private fun offsetSize(x: Int, y: Int, width: Int, height: Int): Pair = + Pair(offset(x, y), Size(width.toFloat(), height.toFloat())) + + private fun stroke(thickness: Double) = Stroke(width = thickness.toFloat()) + + override fun drawLine( + x1: Int, + y1: Int, + x2: Int, + y2: Int, + color: Int, + thickness: Double, + ) { + val startXY = offset(x1, y1) + val endXY = offset(x2, y2) + drawScope.drawLine( + color = composeColor(color), + start = startXY, + end = endXY, + strokeWidth = thickness.toFloat(), + ) + } + + override fun drawRect( + x: Int, + y: Int, + width: Int, + height: Int, + color: Int, + thickness: Double, + ) { + val halfThickness = (thickness / 2.0).toInt() + val (topLeft, size) = offsetSize( + x + halfThickness, + y + halfThickness, + width - halfThickness * 2, + height - halfThickness * 2, + ) + + drawScope.drawRect( + color = composeColor(color), + topLeft = topLeft, + size = size, + style = stroke(thickness), + ) + } + + override fun fillRect(x: Int, y: Int, width: Int, height: Int, color: Int) { + val (topLeft, size) = offsetSize(x, y, width, height) + + drawScope.drawRect( + color = composeColor(color), + topLeft = topLeft, + size = size, + style = Fill, + ) + } + + override fun fill(color: Int) { + fillRect(0, 0, width, height, color) + } + + override fun drawRoundRect( + x: Int, + y: Int, + width: Int, + height: Int, + borderRadius: Int, + color: Int, + thickness: Double, + ) { + val halfThickness = (thickness / 2.0).toInt() + val (topLeft, size) = offsetSize( + x + halfThickness, + y + halfThickness, + width - halfThickness * 2, + height - halfThickness * 2, + ) + + drawScope.drawRoundRect( + color = composeColor(color), + topLeft = topLeft, + size = size, + cornerRadius = CornerRadius(borderRadius.toFloat(), borderRadius.toFloat()), + style = stroke(thickness), + ) + } + + override fun fillRoundRect( + x: Int, + y: Int, + width: Int, + height: Int, + borderRadius: Int, + color: Int, + ) { + val (topLeft, size) = offsetSize(x, y, width, height) + val cornerRadius = CornerRadius(borderRadius.toFloat(), borderRadius.toFloat()) + + drawScope.drawRoundRect( + color = composeColor(color), + topLeft = topLeft, + size = size, + cornerRadius = cornerRadius, + style = Fill, + ) + } + + override fun drawEllipse( + x: Int, + y: Int, + width: Int, + height: Int, + color: Int, + thickness: Double, + ) { + val halfThickness = (thickness / 2.0).toInt() + val (topLeft, size) = offsetSize( + x + halfThickness, + y + halfThickness, + width - halfThickness * 2, + height - halfThickness * 2, + ) + + drawScope.drawOval( + color = composeColor(color), + topLeft = topLeft, + size = size, + style = stroke(thickness), + ) + } + + override fun fillEllipse(x: Int, y: Int, width: Int, height: Int, color: Int) { + val (topLeft, size) = offsetSize(x, y, width, height) + + drawScope.drawOval( + color = composeColor(color), + topLeft = topLeft, + size = size, + style = Fill, + ) + } + + override fun drawImage(rawData: ByteArray?, x: Int, y: Int) { + TODO("Unsupported operation") + } + + override fun nativeImage(): Any = drawScope + + override fun getBytes(format: String, quality: Int): ByteArray { + TODO("Unsupported operation") + } + +} diff --git a/src/wasmJsMain/kotlin/qrcode/render/graphics/HTMLCanvasGraphics.kt b/src/wasmJsMain/kotlin/qrcode/render/graphics/HTMLCanvasGraphics.kt new file mode 100644 index 00000000..350ba829 --- /dev/null +++ b/src/wasmJsMain/kotlin/qrcode/render/graphics/HTMLCanvasGraphics.kt @@ -0,0 +1,161 @@ +package qrcode.render.graphics + +import kotlinx.browser.document +import org.khronos.webgl.Uint8ClampedArray +import org.khronos.webgl.toInt8Array +import org.w3c.dom.CanvasRenderingContext2D +import org.w3c.dom.HTMLCanvasElement +import org.w3c.dom.ImageData + +/** + * An [WasmJsDrawingInterface] that uses a [Canvas] (here referred to as "Classic Canvas") to draw into a [Bitmap]. + * + * For a modern Canvas, see [DrawScopeGraphics] which uses Jetpack Compose. + */ +@OptIn(ExperimentalWasmJsInterop::class) +open class HTMLCanvasGraphics( + val width: Int, + val height: Int, +) : WasmJsDrawingInterface { + companion object { + private const val CANVAS_UNSUPPORTED = "Canvas seems to not be supported :(" + private const val FULL_CIRCLE = 3.141592653589793 * 2.0 // 2 * PI = Full circle + } + + private val canvas: HTMLCanvasElement + + init { + val canvas = tryGet { document.createElement("canvas") as HTMLCanvasElement } + + canvas.width = width + canvas.height = height + + this.canvas = canvas + } + + private fun draw(color: Int, action: CanvasRenderingContext2D.() -> Unit) { + val context = tryGet { canvas.getContext("2d") as CanvasRenderingContext2D } + + val colorString = rgba(color) + context.fillStyle = colorString.toJsString() + context.strokeStyle = colorString.toJsString() + + val lineWidth = context.lineWidth + + action(context) + + context.lineWidth = lineWidth + } + + override fun drawLine(x1: Int, y1: Int, x2: Int, y2: Int, color: Int, thickness: Double) { + draw(color) { + moveTo(x1.toDouble(), y1.toDouble()) + lineTo(x2.toDouble(), y2.toDouble()) + } + } + + override fun drawRect(x: Int, y: Int, width: Int, height: Int, color: Int, thickness: Double) { + draw(color) { + lineWidth = thickness + val halfThickness = thickness / 2.0 + strokeRect( + x.toDouble() + halfThickness, + y.toDouble() + halfThickness, + width.toDouble() - thickness, + height.toDouble() - thickness, + ) + } + } + + override fun fillRect(x: Int, y: Int, width: Int, height: Int, color: Int) { + draw(color) { + fillRect(x.toDouble(), y.toDouble(), width.toDouble(), height.toDouble()) + } + } + + override fun fill(color: Int) { + fillRect(0, 0, width, height, color) + } + + override fun drawRoundRect( + x: Int, + y: Int, + width: Int, + height: Int, + borderRadius: Int, + color: Int, + thickness: Double, + ) { + drawRect(x, y, width, height, color, 1.0) + } + + override fun fillRoundRect( + x: Int, + y: Int, + width: Int, + height: Int, + borderRadius: Int, + color: Int, + ) { + fillRect(x, y, width, height, color) + } + + override fun drawEllipse(x: Int, y: Int, width: Int, height: Int, color: Int, thickness: Double) { + draw(color) { + val radiusX = width.toDouble() / 2.0 + val radiusY = height.toDouble() / 2.0 + + lineWidth = thickness + beginPath() + ellipse( + radiusX + x.toDouble(), radiusY + y.toDouble(), radiusX, radiusY, 0.0, 0.0, + FULL_CIRCLE, false, + ) + stroke() + } + } + + override fun fillEllipse(x: Int, y: Int, width: Int, height: Int, color: Int) { + draw(color) { + val radiusX = width.toDouble() / 2.0 + val radiusY = height.toDouble() / 2.0 + + beginPath() + ellipse( + radiusX + x.toDouble(), radiusY + y.toDouble(), radiusX, radiusY, 0.0, 0.0, + FULL_CIRCLE, false, + ) + fill() + } + } + + override fun drawImage(rawData: ByteArray?, x: Int, y: Int) { + if (rawData != null && rawData.isNotEmpty()) { + draw(0) { + val imageDataArray: JsArray = rawData.toInt8Array().unsafeCast() + val imageData = ImageData(Uint8ClampedArray(imageDataArray), width) + putImageData(imageData, x.toDouble(), y.toDouble()) + } + } + } + + override fun nativeImage(): Any = canvas + + override fun getBytes(format: String, quality: Int): ByteArray = + canvas.toDataURL(format).substringAfter("data:image/png;base64,").encodeToByteArray() + + private fun rgba(color: Int): String { + val r = (color shr 16) and 0xFF + val g = (color shr 8) and 0xFF + val b = (color shr 0) and 0xFF + val a = ((color shr 24) and 0xFF) / 255.0 + return "rgba($r,$g,$b,$a)" + } + + private fun tryGet(what: () -> T): T = + try { + what() + } catch (t: Throwable) { + throw Error(CANVAS_UNSUPPORTED, cause = t) + } +} diff --git a/src/wasmJsMain/kotlin/qrcode/render/graphics/WasmJsDrawingInterface.kt b/src/wasmJsMain/kotlin/qrcode/render/graphics/WasmJsDrawingInterface.kt new file mode 100644 index 00000000..860f6fd1 --- /dev/null +++ b/src/wasmJsMain/kotlin/qrcode/render/graphics/WasmJsDrawingInterface.kt @@ -0,0 +1,24 @@ +package qrcode.render.graphics + +interface WasmJsDrawingInterface { + fun drawLine(x1: Int, y1: Int, x2: Int, y2: Int, color: Int, thickness: Double) + fun drawRect(x: Int, y: Int, width: Int, height: Int, color: Int, thickness: Double) + fun fillRect(x: Int, y: Int, width: Int, height: Int, color: Int) + fun fill(color: Int) + fun drawRoundRect( + x: Int, + y: Int, + width: Int, + height: Int, + borderRadius: Int, + color: Int, + thickness: Double, + ) + + fun fillRoundRect(x: Int, y: Int, width: Int, height: Int, borderRadius: Int, color: Int) + fun drawEllipse(x: Int, y: Int, width: Int, height: Int, color: Int, thickness: Double) + fun fillEllipse(x: Int, y: Int, width: Int, height: Int, color: Int) + fun drawImage(rawData: ByteArray?, x: Int, y: Int) + fun nativeImage(): Any + fun getBytes(format: String = "PNG", quality: Int = 100): ByteArray +} From a6082c647a1dffcb87f40b6d0fe5ec668aeaf155 Mon Sep 17 00:00:00 2001 From: Rafael Lins Date: Tue, 14 Oct 2025 09:41:30 -0300 Subject: [PATCH 2/6] feat(wasm-js): Added max auto info density param --- examples/java/build.gradle.kts | 2 +- gradle/libs.versions.toml | 2 +- src/commonMain/kotlin/qrcode/QRCodeBuilder.kt | 13 ++++++++++++- src/commonMain/kotlin/qrcode/raw/QRCodeProcessor.kt | 3 ++- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/examples/java/build.gradle.kts b/examples/java/build.gradle.kts index d77257cd..f35e2ff8 100644 --- a/examples/java/build.gradle.kts +++ b/examples/java/build.gradle.kts @@ -9,5 +9,5 @@ repositories { dependencies { implementation("io.github.g0dkar:qrcode-kotlin:4.5.0") - implementation("org.jfree:org.jfree.svg:5.0.5") + implementation("org.jfree:org.jfree.svg:5.0.7") } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2d1ed9cd..4cfee031 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,7 +6,7 @@ kotest = "6.0.0.M3" lifecycleLivedataKtx = "2.9.2" lifecycleViewmodelKtx = "2.9.2" mavenNexus = "2.0.0" -androidPlugin = "8.11.1" +androidPlugin = "8.12.0" npmPublish = "3.5.3" core-ktx = "1.16.0" appcompat = "1.7.1" diff --git a/src/commonMain/kotlin/qrcode/QRCodeBuilder.kt b/src/commonMain/kotlin/qrcode/QRCodeBuilder.kt index 33ae1401..737afa6b 100644 --- a/src/commonMain/kotlin/qrcode/QRCodeBuilder.kt +++ b/src/commonMain/kotlin/qrcode/QRCodeBuilder.kt @@ -47,6 +47,7 @@ class QRCodeBuilder @JvmOverloads constructor( private var xOffset: Int = 0 private var yOffset: Int = 0 private var margin: Int = 0 + private var maxAutoInformationDensity: Int = MAXIMUM_INFO_DENSITY private fun innerSpace() = when (shape) { @@ -327,6 +328,16 @@ class QRCodeBuilder @JvmOverloads constructor( return this } + /** + * Sets a value for the Maximum Information Density if automatically calculated. + * + * @param margin How many extra pixels to add around the QRCode + */ + fun withMaxAutoInformationDensity(maxAutoInformationDensity: Int): QRCodeBuilder { + this.maxAutoInformationDensity = maxAutoInformationDensity + return this + } + private val beforeFn: QRCode.(QRCodeGraphics, Int, Int) -> Unit get() = { canvas, xOffset, yOffset -> drawLogoBeforeAction(canvas, xOffset, yOffset) @@ -374,7 +385,7 @@ class QRCodeBuilder @JvmOverloads constructor( graphicsFactory = graphicsFactory, errorCorrectionLevel = errorCorrectionLevel, informationDensity = when (informationDensity) { - 0 -> QRCodeProcessor.infoDensityForDataAndECL(data, errorCorrectionLevel) + 0 -> QRCodeProcessor.infoDensityForDataAndECL(data, errorCorrectionLevel, maxInfoDensity = maxAutoInformationDensity) else -> informationDensity }, maskPattern = maskPattern, diff --git a/src/commonMain/kotlin/qrcode/raw/QRCodeProcessor.kt b/src/commonMain/kotlin/qrcode/raw/QRCodeProcessor.kt index 223379e7..83721758 100644 --- a/src/commonMain/kotlin/qrcode/raw/QRCodeProcessor.kt +++ b/src/commonMain/kotlin/qrcode/raw/QRCodeProcessor.kt @@ -105,6 +105,7 @@ class QRCodeProcessor @JvmOverloads constructor( data: String, errorCorrectionLevel: ErrorCorrectionLevel, dataType: QRCodeDataType = QRUtil.getDataType(data), + maxInfoDensity: Int = MAXIMUM_INFO_DENSITY, ): Int { val qrCodeData = when (dataType) { NUMBERS -> QRNumber(data) @@ -119,7 +120,7 @@ class QRCodeProcessor @JvmOverloads constructor( } } - return MAXIMUM_INFO_DENSITY + return maxInfoDensity } } From 5598d75f8cd1983fe55ab8dc1c53ad4c550f764c Mon Sep 17 00:00:00 2001 From: Rafael Lins Date: Tue, 11 Nov 2025 10:20:38 -0300 Subject: [PATCH 3/6] fix(size-2): Size 2 is an Ultrakill reference --- build.gradle.kts | 4 +- .../examples-results/example00-simple.png | Bin 24402 -> 23484 bytes .../example08-caption-customFont.png | Bin 32621 -> 30660 bytes .../examples-results/example08-caption.png | Bin 32978 -> 32674 bytes .../src/main/kotlin/Example08-TextCaption.kt | 104 +++++------------- .../qrcode/shape/DefaultShapeFunction.kt | 4 +- .../qrcode/render/QRCodeGraphics.jvm.kt | 38 +++++-- 7 files changed, 61 insertions(+), 89 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 3348f053..53fa507e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -133,14 +133,14 @@ kotlin { androidTarget { dependencies { - compileOnly(libs.androidx.compose.ui) + api(libs.androidx.compose.ui) } } wasmJsMain { dependencies { implementation(libs.kotlinx.browser) - compileOnly(libs.jetbrains.compose.ui) + api(libs.jetbrains.compose.ui) } } } diff --git a/examples/kotlin/examples-results/example00-simple.png b/examples/kotlin/examples-results/example00-simple.png index 1833b39d2026f51aa115a9e77d633ef4125d3956..8adc3cc77c6e49549717d522e1139fe191e85781 100644 GIT binary patch literal 23484 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 IzjVug0A=H`Y5)KL literal 24402 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 literal 32978 zcmeHv2~?BUy0+RXwG>dXqB5jakz*@RicBKJ*(w6II0Htch#EqOC?JqTMMae1^w=7e zA=Oq;WE2Q9ktwM%IUrMj01=QOgfJy!{P*`|)YH}Dz5l)UzyH0kVwNk}!@J-8KF>Ss zoiA#S%MPtimw!5I)-0`^-);S2)~q==@Q?S&Jn%{HkGkSnv(C@jxpmXtkR!wG-)9)3 z_xqeAZoH>AanbsV^_$~=b^7AP{EzP~{O%B8SFq7#Enf|PI{mOek2!vWR@46XuWM_u zA^TS~=41E4f0GBL*sNEh)3q0i4+YpfN<~Id#hu7-4yufo$rQ$6k&=%(+A=4&zJsiW zCOSDg4v9t(`<_(?`v)y|B!e!RBgZ+(L>Mwje(7 z0nib0i*$$?A?1TLS2QKH(8;fh;y5pc*M&QR)h=nTb7`b%Fv#~25+hMRl5m1OfEOJ~ z7xInNCfo|75+4$DbIOTAR%BCB=U)xd@Gl3EPXDVx-VisTT{A{l_Ur7m$PHj6U~6=_ z%iP0G>xr`SOb+n4hIbOLH24DXFBZ2(@uSln3NM z!y{F@9JIpG8|^xB5lNYPGw4{hMQu|TlEDnY%S+ zF&=B{q!GWvWIfPM8l)zfm2VBFMGZQP7Q3D)N0_AYo^Tvr{8M+Ay_^e7{5?&H?f0b` z1YQ(`5m;+57VQ_3arZ)XL?gOjHS)0_$U&=8DqHt;SXVIl?M0+~EC>*=5@q&O<(u%J z^CPNotDZ(jr;=NpPoVK2f8GkX0QRze`W#m! z^dT3gU=GL`!*=gnf|2=3>rS$AMs@Z%1C0kM05FHl<6%mWN(O^GuaajV#|SCFnoE&I z0E`f#a>jSKYDS?m3jKdzp>ikvr*^&<4s3V-HCkZs%cA(d#;Ob{UH)+VsBq-(@Qs2g zV4Q;Hi6Bh;YgoZ70h)ujTIJ9G6x1_f&S+B>6#khX`QK-oe};QFCzS{1vh-7#zo}B# z@(#Jv1wK!ff+};kR498_KvL!#Az%M)O8dPs7X=Pc785`&`MtDOm5%~yk-2WjbH1C} z?p5Zbz+cNeEEF-km)@%KQV=G06+l^tPy3J`#_nh1ACDL zK2Y2-qpBHIL3ONuvsHmY6dYnFD+*R{Fuq$+*a8*}nLdOO044}^08NT|7-W9HP9cL< zw4(_XWi<6J@^&#iMfELTt&4nUeNMi-dgTwz^6t>qomfRN>pf{eJ4^{vQB!+Q8Za~? zgsNDXVgah5DO|$;)k3dJr>`Rphn+?c_uFvG-d7YTnX(&R>JW7#{Rm|4)P$oX&A$i# zzQ&rArPF*x)w82YsMNMx*mS50>x2|J@EeGt=WVZq{G6gFo>J{{9lC>JGVvyXA2y1R z^6%zBCEc^SocEPUkvOzS)`~}YT}%RpS|;YBKuuD>w-i9FrB=7z(>>h$vrd(o z4!13ekuHZ43bi#42fqai9)KER%Xl0nL4-ZQo(~nN`Ieyh`af;{R7?i?>EKC(j5ujb zy13p^vYP<981gf#VQW)u7J&rhPHJ(LI`DZ+fkuHtN*wY!D3%lTBtsVOSsU{q(3id;8-hr2lthD4xDX|6P9f?_ z&{pq>X)?f!z!aHC9S+J4HK6y&1Ea_>xMU>valoecgO8o~de_wwK4_CtQ&UkD)Pri7 zEz|{!Azmb@KBks&0?SWJO&uhSnlDky1R-HSW9lVh_AOP)3+?Em)Kr17=z6tUDhj!_ zefwUUcca#-<=hbTffR)K(qk5Cx$un0s(kz3p=HPnpug8!Mv)crd`wYffU5$ES~|2T z@JUvA2VBCp*G9<8-(Ux1E#a@_krN~eT>A#`1$_Eo8@^FiI8hr2gcb5YL~eg@)&j>u ztkK!>nuyv!UH*!)yxN@bX+UhHhU`#5eIO8;$%8MoIcJ22P8er2KVvq~VK%5G{09LR zP_z6`@QOv8Km$sEs+M2^S!M(S9dZtxdeXJGUT@)Ufex@C?owtvkR-56;8i0vk5BFq z?E#hw`vpZN1LGNT1cP{P!^J}mi;+KrML^y_nW4Z80eP%Ylql!uew}I^pBYhPyg8$l z{~op?^SI#MFj8vq>mdhS>J1qbWk`Vw{QDcKI6uN}6+#4DA@ut^F8;M8*Ist1Y}C`zx`a~J=w*U!Q1@AcYUJDPq%($ zWCI5-GmYwIR4}6g?Y~q3C?d%JU`MOE8V7q=;9O;Cp?a#SI5v}4{y_MtOZ-M2Un+Vj z<5e|x4SQ;UQ)n#N$M_x{)%`v7uayT1^0}0*U!Jgu4{*FXZb zgd5BSYYyrp`&d|ofa^#KQIuaRHLDm5UM%P&kAcYih?z~4r)|nzn%P7}im3wV3=)AZ zWQOsVNCd@K`5$W(Id7ek3Ex4#DnC(PSh_}6AzVgxvL0$#i{gGydJj%6i4xti9;#FZ zt{y|}ABD6l>vwXFktIK>_)U4g2}D9O;@+OoE^ItA#sER{-(n1k$Y@{QHR-}cwNe2n z?}N0vxXRNLxs#RmfiD*;PxHVT8&rCfN9*#y z8+dG}5A1vm9Dh~h2{1`0L{o+vP#-8|8de_pfrB_Gh=9J3OQ1?XVE)GO&&zm0!31*A zWWkHP51bV#&jo=Yzyu-}O%<^~dV_d|-aVsGIQ4?5bw;5uMr5h!f2xHlBI?xj+EVY0 z{~U!YegLO*nXr3>5Nh7#BoD#m0EN?E`PD#`Q&c3!f;nYdXNCtqhE$IjW|SlUg$U$E zpkIp4DChrgSSjfwH5F+np^Jf^RI-CCM}GH`%cE5C6Jmhs072PYelJ*_sP98wkapct zRtc23`8&4((ozX?q~#XC)yh(KWplY@$>-lE{#X*>Eu&UhXduti(>%@YOWpR(P`MVc?QU^^&5eC4v`+zOgem~ww-6sELrHP{(llrDtsz9}sdG*Z~F+-ha^CeD5Txe$n^e||1R(naZE z==!v*vZe4>GTT(eyWr$OI$^^Urf|f~;Gquj?(yL956~6SIAjD>Uz!JH+p>sT@dE@I z&{T0Sm;yB!%oOs#^C@idD6MpIIk?111-EWLsIL=IS}K)Hv4~sH)Zf%@zbcJIp#|Ae zWXh2b1sg4j%4K_{B3<4_Uu$Rm3t%BLdYq|?7ikasC5Q(WKiU{S*# zTvX%->As6a{Ck3QTe`!;IOJ~(erCCHP{m|2$>k!-bZ6!$Ar3s3nl?f=rlb9(Qo<#0 z*Z4y=rw1YJ0gsjdJM!AO1gp+{iXD|l+{(f9q#`|VHT#YHYPOP$zTb*+DTs zp9-#4o5-(Lf5?tk6D8F;;97hf@=<&U^Mh0%={EjU0=i}YA=1N7zEHPdu=8NoV2wf_zoQ)Z!4~SDC|b&9l6eL>%HYRv zKtBXf?uKHQ{sedMQ#RC*2-K)Bb||@&0avzu_#qnY>YpfD{s}DLM}E8ml>bO2X8_8Q zJfW@vzk2{>g-H#GackQR`QE(~F{nB3CNAoU>_0^{VgIY3o+1@%BCCYk%GP{eq8vz4hIkiqGa; z+&cdsK3D#>V#TeTsU!)neI(`U#1Y;w=QjbqDYQwo=q(-DoQOv`tugtn8B`RK2mZ$Yp0 zOL@QCA=pOl((Qu>=?$2@{lv&eGsDg(Va5_L_y1jRxNAXM>Yen&=IO%NOL@a3zj>7| zT<#rh(KkQ*hC%mt2W#r>mpAD{P?Y2I&Nm0ZtH`E+0HIL*RrgE4iXFC#njVkGUYXo} zP=;Cvva4&M*N9dISq^y_@Glkq5NMq6hiYDk8|28IA9x?U!Xk!Jz_jYGMvwU>r!TGa zGPUQNIEcL@rxCy*Co4R_YoN1Butr7}%6F>+6;U%wnc0<6*!j#D*#7{m0LqF!H;1`O zMK>p+Eoiq)MxE8T0b&m*emEuQHu%a)9H1>HB8ZC?WxV-hN5$cw&KFsopYY~2yPgCL z*EmsWw-;`$e+hDNGTsz^!F$2oa)F!9P|S%tyDL+ff<1vR&T;357U6ywd64 zqlj)~Gu45$p0;MOuS1@Fa|zP79QDeGop@VYdciqi5)x5I#?Vbv(YDjA^% z&p$Au$Qecc7hB|J5O>fvT)?UUX&pd%tuBAHn%P3W9)R_X0uaDrRhY2kcd2l#$TJj% z4p6PK^@gqp6<*UolmdZE!2|%RDl%}0&{5$q3lItfnhrU_0SZ+hpZLttR)wz_L1#27 zC+!&{^Lw+cmul2opdj<1CnzZUl}X4Wq~bd`CB-5jpa4 zR5S@lQ6-Kd`vEk4Sa?f~_hfm2ijl!EigaaQS@`odx}R*Ech{s6N0DEEu&c@OxEePq zi@++5R%BM8^ANdH7Vdz6!8q@(NhJ=HI6+ndkt!jOjmRoR$f8`(N%-$Up>h{LO4M;t|&X>g6%-Ll#mV$naT- zM%>^Uq+hA|^#kTH4|y1YYBke8EL!RedP`Wh(vz%Z|Utw`-BvjSuN$Jc7KY z|5Tj5&mFU&zo(G5fcIpfS^Nj|?AR^(y)xcN^dJHiI%?18J=)ZHY(q*<;pYw6AsPFm z(SI!R*#7}LaS9|-5b?~seUJ{4uS;#btdJvJLW}l4O!0L=1cE&=Rz#0L3eu`K;a*wJl~$5m`)ur#XBQ8o%Q6E z(k@Z=Hva+i)Lfob+;m<|Z+LA@sAltslbwyb;Ia6oBkk5){>9YL?@J}+<|mt9m)_4T zjCIBKkBjU3b<9sYp>oU%GSAg!dYbinA8A4t`k^Cv85YoF-teSQ6!RoLKT zlq>30gA+nz&JHgluSZ)nfCWgphNdj2L4^r%=MH6F9%{~s9nQaR&cF_MM;|(G{vcKF zbM#cB4>hSQnQY_VA1I!bPl)6f;gu713ntT63NZsUQ|^ML-b=bPQwO(I-*GtGnwgS0 z&~KhQUKlH7KKt|8nqM_9U3t;GB2{mNawglbp!Lnz(~{8@kVYdi8FW!w?`g+ApVB26 zsD|~>LcF6tDf{Hnk*jH~ZX|K2;Ev;w?8Js~q66Q%;c#sr(1`0l3phDpMZ4N{J!|mt z9}3}Eh3xPoBulRT`KWxQ`b|BkNoRiq3#Tnz7{1V@bO9>ekmDnG79_gcB*l*+H%v?8 z{OC?Ua^1H$WcBUsuxrUo;VjQK!s15}Mxvw$>3Ek8h2>LDC5)PCOa36`mL;LijK&#? zOf;zCE}|o!*~ZPkbL%>&pMAYp$54b|+a4ee zb!8Hh2NoO|=!&Fxvuz{zVhu8JRL4m4)P#MLZ^7n=PhW+YfSJb{A3)}@Lc{CJb!_=H zIh9&fcGEIDy;(Su!ow^4_cBHirRGy zB}maG;KKUs_;?LzZ+V;@KUo7cXq=9?64s)ZFxFVE<3}fFMm78R-Drs=5d?89f=dK=c*e%=}7RWT|K{Xv23b+d!JeZ~eIv z9h8CZ&|_M>cZppNwxHL1MRa@eaDKI2i~mDR#S|LfGL3e$e!t&YB}h1UWNE$;jaai+_B z-zMoMnjB@AuCC3u)nNEA$f2Y9tBvBvr3Ru(^O3^D{F8LoRR-wm+=pz#wxAZ|aBnNS zggE?3{%H&o*%ljE<3qNrKiqf00HH~`bxPk}U^>a#7=8`aL@_+?vW&s!<+DY(LDO5F zIS~5FNf_~llyuJTt}(7%nY^APQruc%S z3C=#n#?tSHq~|GL+kWX)vN6=rE@8RG=>~sNR)>7sEBF%&QgUu%aRbq{YjB55`mTt> zqFocPt(5XIFLT^_Nv;%SM-2H1Euh64kj`DlC4YO=$gG@#3w89@J?hxYIYlb*XLJ&u znvxPx(_O*2HD#r=WfKQZFwPZu#%5O+gfAxTxfSVt)#g^gR`4Am(7wK7NsEm~Pjg-k zg5o0mV`s75SClmjf3>w~u|DEiX#@;lPq13Y%g&1{x;VSvplLf}4#d^7L!RsVzHK>v z{TGe?IBl2h{C&EI;!q4T&QIR)@~ykf`SvG_Ref7kZ=x|F+`g0aq^x-<=?bNtRY~wN z4>y|dh<(czUSXd;7Z#oKb8Urji?#N=+s@0#i%~_1y7pAtJzflSv>^kXcdjR?1dAq# zGpt5@8Dj~dF+t+nZ@wvOsaV@yk&;(KXkJc=?};nTEGKjua78g;CasR)oEt%bdogYl zMvsACextRQFwfCXUs4*CM$bfhH?$AqQBNGZ87@D%$5>uPc6a5wY@ochjvOe_eyP&;AJInwDsL87Q%T3V}2Ci#z_w+DU|lpMIWBW1ZwAl>*{ zL96{>)wqqsko=Zp#tuK|zS{9;{I=p@Jz7i+hZ!6AIb%8{m!%QUfBEWWP6N8cw|H2W zb|^CrOAia}8Qjzu8!^|mYHYBNr@2?M%*NQTcs0)Y?tn>2?7x%Jk%=b64%rPfbeqO$BamKFRHsR8Aoow z0{1bOJ14{otj26IzG$yLPdg32JUl3#;3e&NwLK!FD6u(O-#LHm%+QeVOtA<3oBFqs zcHcXmqwZ}JqJ*N48Ob=e(9X(jJ?Aa8M_i+7P8h6=u33W(dg*`det%<*Ynw6G>l(Yl z_F!lnMU-$&XFhI^MQ{W2r46fR_g9QaoNLcZ$DTX}E~TE3V#dv3Oo)TIb^Z5?c9zVp z+Qe(D<2d=+&Y_SGH`!pu8_Emr;x%>hoIj%I9^S(G{l&}tD6&5=su0&wC)Lex z>Ohce%8`%rY+{<l`>U&v&C*~qm`Mj|J{77 z$2?a1CEQKS*WBcK$-p8%eVPZhb)hkC_0dy8+WqrJlJH`Kl_O{EjJ0nio4L~N-#XHB zeYJr}6Dx&m9lF`0BB>}>dNUb$|Smy<878H?YPI@uEX?p03Sc>|pdnvAGQ=P-G<{XL^ON<@5 zFv#&Ei7N{xzci!ze>vqaEXDhpj(p;>wP0%gFI8f!!Aci|dHGcL{uWMC8SZ&eZ}jFx z8eh5jHWPR!f71=wqf7XqSW;k9cC8F z$FNUF6B%a2H$gG{kIRCxjCh+L?+<^*6tF5&s`Tp{O(e|Fwzu=KugnK?#eGK^9!)LQZVmainlHz57L;$nyt;&eHs)rTAGIkGQ|ao6 z&aQ9tp%?gUmaS5gmDaVt$0%S*ht+lVYd3_1Wp$1eu%dz)*mNwhV7S_%$8B(tAHx5# z*q_ZPcKbx)S8U)r;&-H7mqzKfF%*5o=KoIEy&*Hvyq})nP*gj97{x3oPZ)n3gzQ~h zVqxuh5mQ+(+*FA=Me^QieUaL3hQ_y_jBx33nr(nR)tb`8bxq1^*}g%~*2R|Ixa{Y` zoDSe2H^=Jnn6>4UZmj<+U-zC_Bc8DxMIjF`cd{@ApHDGB0@5tV!tm>TO`C4Fjo_B- zuWKCo@#F}$`%AKopWak>NR(6iFoIS-xWA89;AA_$`w=&yMr1+LV6>M6n1Bv(o7N!jc~0q&*JE-wdsH8jj+%9^ zx`fl&ZEoO=+1whGE?xr>OlRWe1t@%ylFzW+^hEwK6}q#-hjl_)5UsX_KkYD#AXu zJCb0y+70NFYBKB75^AMdM)gB=(+dpr(h!fMqog6 zUQu&^j_~4nc10(qLgHoM$9p2__N>@h5VXRU)@JKh+$32%Rh)+v-P~)A%lV2Ph1E1& z=*ua4;5o%OCDN|9(nxR^?Yzp91&1#9%E3E23%P4zVMQ18`f9q|E-DGC8^d~?%Aklo zdv4&CziD^J*Nc}iJab}8dD**5zkir`n0X1)Ec!>2RP;|`wg`_S-jULLAe-2tDMVS) z%(_;2*e?W{HK2H+iJuBP+VXkxmsTCPYv&?*+rK>f2C>MH=o~N3UrM5EH~gcz{wM0g z#iS>c01We?%^*5xJD2qfY}Czdt`?J~)##fh4R^C362Iw5keB(ebdUG&9G8kb z-Exa2nwJuI=6C0Eg+CaWn31LyXxI@)y7KdDvTsJCo>Kds1Zs8WEJnpK1rK>`WnA4= z9@Ijn$>9}chZ}H!EWs3uvy%*1cY2v~bpBE4Zg#f>xCe8BcBJTNv0reV&%KaDTtYx> zLE}?Ov*=PqURKaj-;JFkPg!ovtFbm^Ny52^Q}L_)^Z}t!CVZfXw77|&9gAbzI<;4p zeQ~rnOK*z!z#?e2Nq1XR*HSbqi+xz=2C;joO^yamcMW~IEoTY7q^Vrz1f6>1o3p72 zony|w+x7Gfzgq4@ZFA0G3$56vJ2DFM-yV{gtS1_|t$k^O1%Z1=(~$Yx42=YKWf(fo(_o!8%GQGl4p{Nt+e9>qf|{grWwkk+~gXvsb@Qe?Trf zTUs7#Ely^dZ)!yQqXKyC`6-WB6*9L2EDe@(X{HxD)P72nZI2N ztL?e2m8MH*aymT3_hpyR7)i*IKl1P?k~6hUWho^#HagMuS?6V_FiSv?4JQ=A0g{V<#v^h@Uf>zw2`x(fCt-_5Yh1Hn^Bb8!5q_5E62O zvLQIlAwLa=e9_q86z=a%O%p&-kh)9Cn*wx*rXT7tnwmSCak(^0x+NxC({_Hd%g6MC z`jNmK$Q6YADXlQc8+s#3g97IwH2+qRBcY-7sk?w? zlgi&wU&-45yqUqO93lwyp>>U1zayCH=}HZPF$L#16$ozI|tD*1HViIv>#>nCGIT^%tWw*;L$ z#4_3kVzzHzF9NZ>u6k_$uFYZIx4C5hPNv}V0gwowmXn|7hu?U+Pg_C{YL#wr)lXc& z%ge?j#0*VWt((;NBdk6M({0~2&Cevl6+|t^0?!OoyJkVJ-XGra&iG0z5biX$HE^Q< z@(U7{P%oDKV*5qUQm^nLq_NrUEa%|>qVVKzpA&X~kXB@p67z)VPhxu;5(iHHwyfvq zFoWSEJrnhCQj5vUeKD^G`}^tWfPsd{Y1}{Z^ zR@BSu>|unsk^GTexi4a>^tl5q$mg-tV#;NA!D^7lSegOfd>|=zWy7f z%zP1xT`;Umx_(UDf7M;1EZK9){%q6%G>QEx&VPuQY9jIPYZO1~&d=?2^7Nxp(r{VU zle0ONuKLcSz+m@qoSKiS^EMjr_IXU@d-P{HX>TLVts_@B0B{ZOV7a{_`_XOU3Zj=0 zg_iYe@geb!`;5#ID`NF(^7CpOy~20J#e1>JvTQ#`VE@KlV>^<4?o|Y$HCe~MXPv;! zd537@zyP0o2HtjuUVK|`=#}g(!j=ds#gOQ`mhkhpWxJ#o*ahh=NuVTzW|M9S_9oEc z`HVc)CSQZSCA;lA`}r;!)MNMXf>_KQYMU7&8HFG1XyZoXM3*mxXw#|8$*10aWEwUp z-Qw(c;8N_zxQ&yK+PJ~(_l}nkj_D^#ZUu^#S_`kBtI%=ymj%Qz*E|-YE4F!0`^yEH ztGPw+VU)y_#rYDl3ZI&!UezAEe2qC{l29vfy)5zTgqAoFK=H2NweS4hY14g z%7T)oevi2KujU~cpOgGrI7^TZ(kmUi4al$R84mcVx8LSZI2b$nNXCN-Yu&|7QEOp; zVZPA*Db}CF@*GM5HKS>3p78~Abm8@n-dOcKe%lx?*)JX5x+G&E!mljVbE>#H4ywZx zY)^2GGGCX43g>oDFfPBjOL-WIDw+o;W-uk0va-NYMR_N0?y&Aug+#|n75F3+BHaT| zg@^fF119dtAGRhQKKHaBSMUs@k4wHN&g4#GrWft0AapOz*6p&nakJCU1(a(SzX%>V z>i5)k%lhDD-jFR*w}(GVv>$5oHmIeM=HKL>a~|Kmutwh-)0@{eLEhzVIan31KOUfo zdw81tcz8c+w_C#OLlNQ%(~=+J9i2pRF2Fb!F!q+3dqzO1iOe_O#DUWp@{bL|Ia8Wp zUed_r-g*IxG7>+D&9-(CZJucFeY(z!z8mL+qG(3gCUyjVq31h~V)SG#?$G3KZN0V* zwEVH2pZTCfm~fFf9E}~t08hUi?)WgMPb_s8M+b=vD~|M>KR4L<+>V&#FpeGzU)#Q`a&Zx2iVnY(M{u4(horoaR`brN@@Ww+>=M^5anAbiDT^x6xz{Kl$x`|AFGI z2`9sk#7ohipKRYNy&@}l!;49;nk{^VnbyBOp?_Fvt=%)$!N?yVKfY~Q_GH|eZG9|K zIv*pb(N3YU=TV1ulFx%mc@_G^KvL2gYDpsU0Og5A=uImM2wMkv`IA)KA$URk?=L8F z7>9adQSlCQ_!*i6jpD9+rR~?_Y+rw+`oi9X*HQXHQbDpg>8F}y48h&|on0x1HKZi$Qkw3l`_Oho&Bfn|AaoN+Q zsI^z`OdG^XX?U*&49U0Dmf2i?E^SM~wrl3*RruRnnx4jY-y`cq)%Wj)U`mPhgr_~r z3ld6emIscb7`;o0!2itog>j~;?98VW)DRR)_IE9Hr+EoY@qI0?%FYL#t}B6}124kT zikJ~CDx$!%6cYokzZ;g@oTI+rX!sw%x`V^t<2y#3JFyeO3yjwc=o(7v!tfhBeKDac zXp#cmPzm~&`9{3`zpDFx@L4pIbj)wotXVNTw{F_|L1(=G$w%RRkvdWYa&~v{7andp RK(JXmx4CT1*nITj{{g`C+ 0) { - graphics.stroke = BasicStroke(strokeThickness.toFloat(), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND) + graphics.stroke = BasicStroke(strokeThickness.toFloat()) } graphics.color = jdkColor graphics.background = jdkColor graphics.paint = jdkColor - graphics.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON) + + if (width >= 100) { + graphics.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON) + } action(graphics) @@ -153,13 +158,23 @@ actual open class QRCodeGraphics actual constructor( /** Draw the edges of a rectangle starting at point `(x,y)` and having `width` by `height`. */ actual open fun drawRect(x: Int, y: Int, width: Int, height: Int, color: Int, thickness: Double) { draw(color, thickness) { - val halfThickness = (thickness / 2.0).roundToInt().coerceAtLeast(0) - it.drawRect( - x + halfThickness + customImageOffsetX, - y + halfThickness + customImageOffsetY, - width - halfThickness * 2, - height - halfThickness * 2, - ) + if (width >= 100) { + val halfThickness = roundDown(thickness / 2.0) + + it.drawRect( + x + halfThickness + customImageOffsetX, + y + halfThickness + customImageOffsetY, + width - halfThickness * 2, + height - halfThickness * 2, + ) + } else { + it.drawRect( + x + customImageOffsetX, + y + customImageOffsetY, + width, + height, + ) + } } } @@ -204,7 +219,7 @@ actual open class QRCodeGraphics actual constructor( thickness: Double, ) { draw(color, thickness) { - val halfThickness = (thickness / 2.0).roundToInt().coerceAtLeast(0) + val halfThickness = roundDown(thickness / 2.0) it.drawRoundRect( x + halfThickness + customImageOffsetX, y + halfThickness + customImageOffsetY, @@ -305,4 +320,7 @@ actual open class QRCodeGraphics actual constructor( action.accept(it) } } + + private fun roundDown(value: Double): Int = + BigDecimal.valueOf(value).setScale(0, FLOOR).toInt().coerceAtLeast(0) } From 6a7e5f99f7cd76f19f87f7405c1367a08d5e2dd7 Mon Sep 17 00:00:00 2001 From: Rafael Lins Date: Tue, 11 Nov 2025 12:08:42 -0300 Subject: [PATCH 4/6] fix(size-2): Size 2 is an Ultrakill reference --- CHANGELOG.md | 39 ++++- .../example09-2xW-2xH-fitIntoArea.png | Bin 0 -> 54835 bytes .../example09-2xW-2xH-resizeCanvas.png | Bin 0 -> 29384 bytes .../example09-2xW-3xH-fitIntoArea.png | Bin 0 -> 78046 bytes .../example09-3xW-2xH-fitIntoArea.png | Bin 0 -> 80214 bytes .../src/main/kotlin/Example09-Resizing.kt | 37 +++++ src/commonMain/kotlin/qrcode/QRCode.kt | 151 ++++++++++++++++-- .../kotlin/qrcode/QRCodeAlignment.kt | 18 +++ src/commonMain/kotlin/qrcode/QRCodeBuilder.kt | 2 +- 9 files changed, 224 insertions(+), 23 deletions(-) create mode 100644 examples/kotlin/examples-results/example09-2xW-2xH-fitIntoArea.png create mode 100644 examples/kotlin/examples-results/example09-2xW-2xH-resizeCanvas.png create mode 100644 examples/kotlin/examples-results/example09-2xW-3xH-fitIntoArea.png create mode 100644 examples/kotlin/examples-results/example09-3xW-2xH-fitIntoArea.png create mode 100644 examples/kotlin/src/main/kotlin/Example09-Resizing.kt create mode 100644 src/commonMain/kotlin/qrcode/QRCodeAlignment.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 03e7978f..f659afab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,29 @@ > Mostly notable changes from version to version. Some stuff might go undocumented. If you find something that you think > should be documented, please open an [issue](https://github.com/g0dkar/qrcode-kotlin/issues) :) -# 4.5.0 - Latest +# 4.6.0 - Latest ## ✨ New + +- Added a `resizeCanvas()` function to `QRCode` to make it possible to resize only the Canvas. +- You can now have canvas sizes that are NOT squares (e.g.: a `1024x768` canvas) + - By default, they'll be squares. But you can `resizeCanvas()` (or most likely `fitIntoArea()`) with different + `width` and `height` parameters. +- A new example (Example 09) showing this new behaviour. + +## ♻️ Changed + +- After resizing the canvas (either via `resizeCanvas()` or `fitIntoArea()`) the QRCode will be realigned inside the new + canvas. **Default is to stay centered.** + +## 🔧 Fixed + +- _DEVELOPMENT STILL IN PROGRESS_ + +# 4.5.0 + +## ✨ New + - Added (experimental) support for WASM targets (requested via Issues #140 and #167) - Please do let us know if you run into any issues with it <3 @@ -17,15 +37,21 @@ > I'm trying to keep a better CHANGELOG from now on ^^ ## 🔧 Fixed -- **Fixed an issue with rendering the Timing Pattern.** I have known it for a while, but now I finally figured what was the issue and fixed it. + +- **Fixed an issue with rendering the Timing Pattern.** I have known it for a while, but now I finally figured what was + the issue and fixed it. ## ♻️ Changed + - Changed default ECL from `VERY_HIGH` to `LOW` as to stay closer to what other tools seems to use as a default -- Computing the `informationDensity` value now always goes for the **least possible value** _(down from a minimum of 6 set by `QRCodeBuilder`)_ +- Computing the `informationDensity` value now always goes for the **least possible value** _(down from a minimum of 6 + set by `QRCodeBuilder`)_ - Better documentation of methods - this is an ongoing initiative! ## ✨ New -- New `InsufficientInformationDensityException`: instead of an `IllegalArgumentException`, this new exception is thrown with a more helpful message + +- New `InsufficientInformationDensityException`: instead of an `IllegalArgumentException`, this new exception is thrown + with a more helpful message - Added `drawQRCode()` extension function to a Android Compose `DrawScope` to draw QRCodes into modern Android. - Idea/request from Issue #141 by @dgmltn (Thanks!) - Added examples demonstrating what the ECL does (same data, different ECLs) @@ -34,10 +60,13 @@ - Moved example QRCode files to a folder within each language examples, just to reduce clutter :) ## 🚫 Removed -- `forceInformationDensity` was removed. Now the **QRCodeBuilder** class uses `infoDensity = 0` (default value) as a trigger to compute it automatically since it needs to be `>= 1` + +- `forceInformationDensity` was removed. Now the **QRCodeBuilder** class uses `infoDensity = 0` (default value) as a + trigger to compute it automatically since it needs to be `>= 1` - Default value calling `QRCode()` directly is still 6 as to keep a bit of backwards compatibility 😅 ## 👀 Internal + - Renamed "typeNum" to "informationDensity" - Updated dokka and KMP - Fixed dokka always triggering building the whole `docs/dokka/` folder (that is only for GH Pages) diff --git a/examples/kotlin/examples-results/example09-2xW-2xH-fitIntoArea.png b/examples/kotlin/examples-results/example09-2xW-2xH-fitIntoArea.png new file mode 100644 index 0000000000000000000000000000000000000000..2b44f7ffceaf72cdd3f787894caaa3285fefcc8d GIT binary patch literal 54835 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 BRMY?f literal 0 HcmV?d00001 diff --git a/examples/kotlin/examples-results/example09-2xW-2xH-resizeCanvas.png b/examples/kotlin/examples-results/example09-2xW-2xH-resizeCanvas.png new file mode 100644 index 0000000000000000000000000000000000000000..cd43e4132c4c635e36c81dd076a50f4dfa5f951f GIT binary patch literal 29384 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 Fe*pk}Eb;&V literal 0 HcmV?d00001 diff --git a/examples/kotlin/examples-results/example09-3xW-2xH-fitIntoArea.png b/examples/kotlin/examples-results/example09-3xW-2xH-fitIntoArea.png new file mode 100644 index 0000000000000000000000000000000000000000..c78bb4bd90d69e5bf37b2994b13d73a02552a68c GIT binary patch literal 80214 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 DEFAULT_QRCODE_SIZE) canvasSize else qrCodeProcessor.computeImageSize(squareSize, rawData) private set @@ -194,8 +206,8 @@ class QRCode @JvmOverloads constructor( if (!actualSquare.rendered) { when (currentSquare.squareInfo.type) { POSITION_PROBE, POSITION_ADJUST -> shapeFn.renderControlSquare( - xOffset, - yOffset, + xOffset + internalXOffset, + yOffset + internalYOffset, colorFn, actualSquare, canvas, @@ -203,8 +215,8 @@ class QRCode @JvmOverloads constructor( ) else -> shapeFn.renderSquare( - xOffset + x, - yOffset + y, + xOffset + internalXOffset + x, + yOffset + internalYOffset + y, colorFn, currentSquare, canvas, @@ -216,25 +228,130 @@ class QRCode @JvmOverloads constructor( } } - /** - * Computes a [squareSize] to make sure the QRCode can fit into an area of width by height pixels - */ + @Deprecated("Please use resizeCanvas or fitIntoArea instead.") fun resize(size: Int): QRCode { - canvasSize = size - graphics = graphicsFactory.newGraphicsSquare(canvasSize) - - return this + return resizeCanvas( + width = size, + height = size, + resizeCanvasOnly = true, + qrCodeAlignmentAfterResize = MIDDLE_CENTER, + ) } /** - * Computes a [squareSize] to make sure the QRCode can fit into an area of width by height pixels + * Resizes _**the Canvas**_ where the QRCode will be drawn. + * + * By default, it resizes the Canvas to a square of [width] by [width] size. + * + * If the [height] parameter is specified, resizes the canvas to [width] by [height]. + * + * Optionally also resize the QRCode as well, making it as big as possible while + * fitting into the new Canvas Size **(default is `true`, resizing only the canvas)**. + * + * Calling this function with [resizeCanvasOnly] = `false` is the same as calling [fitIntoArea]. + * + * Lastly, [qrCodeAlignmentAfterResize] decides where the QRCode will be placed on the resized canvas. + * Defaults to [MIDDLE_CENTER] (aka "centered"). + * + * You probably want to use [fitIntoArea] instead. + * + * > _Context on why have both: Sometimes you want to resize only the canvas so you'll draw something else + * > there before drawing the QRCode. This isn't the most common use-case. The most common one is resizing + * > the QRCode so it fits into a given area, thus the [fitIntoArea]. Because of the naming confusion and + * > bad documentation (my bad, sorry!) this function was expanded into what it is today (v4.6.0)_ + * + * @param width Width, in pixels, of the canvas + * @param height Height, in pixels, of the canvas (default: same as [width], making it a square) + * @param resizeCanvasOnly If `true` resize ONLY the canvas, leaving the QRCode size intact (default: `true`) + * @param qrCodeAlignmentAfterResize Where will the QRCode be placed after resizing? (default: [MIDDLE_CENTER]) + * + * @see fitIntoArea You likely want this one ;) */ - fun fitIntoArea(width: Int, height: Int): QRCode { + fun resizeCanvas( + width: Int, + height: Int = width, + resizeCanvasOnly: Boolean = true, + qrCodeAlignmentAfterResize: QRCodeAlignment = MIDDLE_CENTER, + ): QRCode = + if (resizeCanvasOnly) { + canvasSize = min(width, height) + graphics = graphicsFactory.newGraphics(width, height) + alignQRCode(width, height, qrCodeAlignmentAfterResize) + } else { + fitIntoArea(width, height, qrCodeAlignmentAfterResize) + } + + /** + * Resizes the Canvas **AND** the QRCode accordingly. + * + * The QRCode will be resized as best as possible to fit into the new Canvas. + * + * After resizing, the QRCode might have to be realigned. The realignment can + * be customized via the optional [qrCodeAlignmentAfterFit] parameter. + * + * By default, the QRCode will be drawn at the middle-center (aka "centered") + * of the Canvas. + */ + @JvmOverloads + fun fitIntoArea(width: Int, height: Int, qrCodeAlignmentAfterFit: QRCodeAlignment = MIDDLE_CENTER): QRCode { val reference = min(width, height) squareSize = floor(reference / rawData.size.toDouble()).toInt() shapeFn.resize(squareSize) canvasSize = reference - graphics = graphicsFactory.newGraphicsSquare(canvasSize) + graphics = graphicsFactory.newGraphics(width, height) + + return alignQRCode(width, height, qrCodeAlignmentAfterFit) + } + + private fun alignQRCode(width: Int, height: Int, qrCodeAlignment: QRCodeAlignment = MIDDLE_CENTER): QRCode { + val qrcodeSize = squareSize * rawData.size + + when (qrCodeAlignment) { + TOP_LEFT -> { + internalXOffset = 0 + internalYOffset = 0 + } + + TOP_RIGHT -> { + internalXOffset = width - qrcodeSize + internalYOffset = 0 + } + + TOP_CENTER -> { + internalXOffset = ((width / 2.0) - (qrcodeSize / 2.0)).toInt() + internalYOffset = 0 + } + + MIDDLE_LEFT -> { + internalXOffset = 0 + internalYOffset = ((height / 2.0) - (qrcodeSize / 2.0)).toInt() + } + + MIDDLE_RIGHT -> { + internalXOffset = width - qrcodeSize + internalYOffset = ((height / 2.0) - (qrcodeSize / 2.0)).toInt() + } + + MIDDLE_CENTER -> { + internalXOffset = ((width / 2.0) - (qrcodeSize / 2.0)).toInt() + internalYOffset = ((height / 2.0) - (qrcodeSize / 2.0)).toInt() + } + + BOTTOM_LEFT -> { + internalXOffset = 0 + internalYOffset = height - qrcodeSize + } + + BOTTOM_RIGHT -> { + internalXOffset = width - qrcodeSize + internalYOffset = height - qrcodeSize + } + + BOTTOM_CENTER -> { + internalXOffset = ((width / 2.0) - (qrcodeSize / 2.0)).toInt() + internalYOffset = height - qrcodeSize + } + } return this } diff --git a/src/commonMain/kotlin/qrcode/QRCodeAlignment.kt b/src/commonMain/kotlin/qrcode/QRCodeAlignment.kt new file mode 100644 index 00000000..31271a4f --- /dev/null +++ b/src/commonMain/kotlin/qrcode/QRCodeAlignment.kt @@ -0,0 +1,18 @@ +package qrcode + +/** + * QRCode alignment inside the Canvas. Used to place the QRCode after calling [QRCode.fitIntoArea]. + */ +enum class QRCodeAlignment { + TOP_LEFT, + TOP_CENTER, + TOP_RIGHT, + + BOTTOM_LEFT, + BOTTOM_CENTER, + BOTTOM_RIGHT, + + MIDDLE_LEFT, + MIDDLE_CENTER, + MIDDLE_RIGHT, +} diff --git a/src/commonMain/kotlin/qrcode/QRCodeBuilder.kt b/src/commonMain/kotlin/qrcode/QRCodeBuilder.kt index 737afa6b..f4c2a20c 100644 --- a/src/commonMain/kotlin/qrcode/QRCodeBuilder.kt +++ b/src/commonMain/kotlin/qrcode/QRCodeBuilder.kt @@ -393,7 +393,7 @@ class QRCodeBuilder @JvmOverloads constructor( doAfter = afterFn, ).apply { if (margin > 0) { - resize(canvasSize + margin * 2) + resizeCanvas(canvasSize + margin * 2) } } } From 39069308443e5c02e6b2ff6ea7cf6e0771f8ee39 Mon Sep 17 00:00:00 2001 From: Rafael Lins Date: Tue, 11 Nov 2025 14:00:43 -0300 Subject: [PATCH 5/6] fix(size-2): Fixed the build issues --- build.gradle.kts | 44 +++++++++---------- .../{values-land => _values-land}/dimens.xml | 0 .../dimens.xml | 0 .../dimens.xml | 0 gradle/libs.versions.toml | 25 ++++++----- gradle/wrapper/gradle-wrapper.properties | 2 +- settings.gradle.kts | 13 ++++-- 7 files changed, 45 insertions(+), 39 deletions(-) rename examples/android/src/main/res/{values-land => _values-land}/dimens.xml (100%) rename examples/android/src/main/res/{values-w1240dp => _values-w1240dp}/dimens.xml (100%) rename examples/android/src/main/res/{values-w600dp => _values-w600dp}/dimens.xml (100%) diff --git a/build.gradle.kts b/build.gradle.kts index 53fa507e..2f791221 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -79,21 +79,21 @@ kotlin { } } - wasmJs { - browser { - commonWebpackConfig { - mode = PRODUCTION - sourceMaps = true - } - - testTask { - enabled = false - } - - binaries.library() - generateTypeScriptDefinitions() - } - } +// wasmJs { +// browser { +// commonWebpackConfig { +// mode = PRODUCTION +// sourceMaps = true +// } +// +// testTask { +// enabled = false +// } +// +// binaries.library() +// generateTypeScriptDefinitions() +// } +// } // This is in place just because my main development machine is NOT a macOS :) // iOS Family of targets... since you can't just "ios()" anymore. @@ -131,18 +131,18 @@ kotlin { } } - androidTarget { + androidMain { dependencies { api(libs.androidx.compose.ui) } } - wasmJsMain { - dependencies { - implementation(libs.kotlinx.browser) - api(libs.jetbrains.compose.ui) - } - } +// wasmJsMain { +// dependencies { +// implementation(libs.kotlinx.browser) +// api(libs.jetbrains.compose.ui) +// } +// } } } diff --git a/examples/android/src/main/res/values-land/dimens.xml b/examples/android/src/main/res/_values-land/dimens.xml similarity index 100% rename from examples/android/src/main/res/values-land/dimens.xml rename to examples/android/src/main/res/_values-land/dimens.xml diff --git a/examples/android/src/main/res/values-w1240dp/dimens.xml b/examples/android/src/main/res/_values-w1240dp/dimens.xml similarity index 100% rename from examples/android/src/main/res/values-w1240dp/dimens.xml rename to examples/android/src/main/res/_values-w1240dp/dimens.xml diff --git a/examples/android/src/main/res/values-w600dp/dimens.xml b/examples/android/src/main/res/_values-w600dp/dimens.xml similarity index 100% rename from examples/android/src/main/res/values-w600dp/dimens.xml rename to examples/android/src/main/res/_values-w600dp/dimens.xml diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4cfee031..e0d613cb 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,21 +1,22 @@ [versions] annotation = "1.9.1" -kotlin = "2.2.0" -dokka = "2.0.0" -kotest = "6.0.0.M3" -lifecycleLivedataKtx = "2.9.2" -lifecycleViewmodelKtx = "2.9.2" +kotlin = "2.2.21" +dokka = "2.1.0" +kotest = "6.0.4" +kotestPlugin = "6.0.0.M3" +lifecycleLivedataKtx = "2.9.4" +lifecycleViewmodelKtx = "2.9.4" mavenNexus = "2.0.0" androidPlugin = "8.12.0" npmPublish = "3.5.3" -core-ktx = "1.16.0" +core-ktx = "1.17.0" appcompat = "1.7.1" -material = "1.12.0" +material = "1.13.0" constraintlayout = "2.2.1" -navigation-fragment-ktx = "2.9.3" -navigation-ui-ktx = "2.9.3" -jetbrains-compose = "1.8.2" -kotlinx-browser = "0.4" +navigation-fragment-ktx = "2.9.6" +navigation-ui-ktx = "2.9.6" +jetbrains-compose = "1.9.4" +kotlinx-browser = "0.5.0" [libraries] androidx-annotation = { module = "androidx.annotation:annotation", version.ref = "annotation" } @@ -39,7 +40,7 @@ kotlinx-browser = { module = "org.jetbrains.kotlinx:kotlinx-browser", version.re [plugins] dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" } kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } -kotest-multiplatform = { id = "io.kotest.multiplatform", version.ref = "kotest" } +kotest-multiplatform = { id = "io.kotest.multiplatform", version.ref = "kotestPlugin" } android-library = { id = "com.android.library", version.ref = "androidPlugin" } nexus = { id = "io.github.gradle-nexus.publish-plugin", version.ref = "mavenNexus" } npmPublish = { id = "dev.petuska.npm.publish", version.ref = "npmPublish" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 002b867c..d4081da4 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/settings.gradle.kts b/settings.gradle.kts index b177acb4..494443d7 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,13 +1,18 @@ -dependencyResolutionManagement { +pluginManagement { repositories { + google { + content { + includeGroupByRegex("com\\.android.*") + includeGroupByRegex("com\\.google.*") + includeGroupByRegex("androidx.*") + } + } mavenCentral() - google() gradlePluginPortal() - mavenLocal() } } -pluginManagement { +dependencyResolutionManagement { repositories { mavenCentral() google() From a65fbd118a1cef6c23af5fefdce297a4d71d4f20 Mon Sep 17 00:00:00 2001 From: Rafael Lins Date: Tue, 11 Nov 2025 15:39:25 -0300 Subject: [PATCH 6/6] fix(size-2): Fixed the build issues --- examples/android/src/main/AndroidManifest.xml | 1 - .../src/main/res/{_values-land => values-land}/dimens.xml | 0 .../src/main/res/{_values-w1240dp => values-w1240dp}/dimens.xml | 0 .../src/main/res/{_values-w600dp => values-w600dp}/dimens.xml | 0 4 files changed, 1 deletion(-) rename examples/android/src/main/res/{_values-land => values-land}/dimens.xml (100%) rename examples/android/src/main/res/{_values-w1240dp => values-w1240dp}/dimens.xml (100%) rename examples/android/src/main/res/{_values-w600dp => values-w600dp}/dimens.xml (100%) diff --git a/examples/android/src/main/AndroidManifest.xml b/examples/android/src/main/AndroidManifest.xml index fa1d52d9..a98fdb88 100644 --- a/examples/android/src/main/AndroidManifest.xml +++ b/examples/android/src/main/AndroidManifest.xml @@ -24,7 +24,6 @@ android:name=".NewQRCodeActivity" /> diff --git a/examples/android/src/main/res/_values-land/dimens.xml b/examples/android/src/main/res/values-land/dimens.xml similarity index 100% rename from examples/android/src/main/res/_values-land/dimens.xml rename to examples/android/src/main/res/values-land/dimens.xml diff --git a/examples/android/src/main/res/_values-w1240dp/dimens.xml b/examples/android/src/main/res/values-w1240dp/dimens.xml similarity index 100% rename from examples/android/src/main/res/_values-w1240dp/dimens.xml rename to examples/android/src/main/res/values-w1240dp/dimens.xml diff --git a/examples/android/src/main/res/_values-w600dp/dimens.xml b/examples/android/src/main/res/values-w600dp/dimens.xml similarity index 100% rename from examples/android/src/main/res/_values-w600dp/dimens.xml rename to examples/android/src/main/res/values-w600dp/dimens.xml