Unverified Commit 537bb123 authored by Aleksandr Veselov's avatar Aleksandr Veselov Committed by GitHub

Implement common test resource loading (#303)

parent ed2e75d3
...@@ -387,17 +387,20 @@ kotlin { ...@@ -387,17 +387,20 @@ kotlin {
val darwinMain by creating { val darwinMain by creating {
dependsOn(nativeMain) dependsOn(nativeMain)
} }
val darwinTest by creating {
dependsOn(nativeTest)
}
val macosMain by creating { val macosMain by creating {
dependsOn(darwinMain) dependsOn(darwinMain)
} }
val macosTest by creating { val macosTest by creating {
dependsOn(nativeTest) dependsOn(darwinTest)
} }
val iosMain by creating { val iosMain by creating {
dependsOn(darwinMain) dependsOn(darwinMain)
} }
val iosTest by creating { val iosTest by creating {
dependsOn(nativeTest) dependsOn(darwinTest)
} }
val macosArch = when (targetArch) { val macosArch = when (targetArch) {
Arch.X64 -> { Arch.X64 -> {
......
...@@ -7,17 +7,38 @@ config.browserConsoleLogOptions.level = "debug"; ...@@ -7,17 +7,38 @@ config.browserConsoleLogOptions.level = "debug";
const basePath = config.basePath; const basePath = config.basePath;
const projectPath = path.resolve(basePath, "..", "..", "..", "..", ".."); const projectPath = path.resolve(basePath, "..", "..", "..", "..", "..");
const wasmPath = path.resolve(projectPath, "build", "out", "link", "Release-wasm-wasm") const wasmPath = path.resolve(projectPath, "build", "out", "link", "Release-wasm-wasm")
const generatedAssetsPath = path.resolve(projectPath, "build", "karma-webpack-out")
const debug = message => console.log(`[karma-config] ${message}`); const debug = message => console.log(`[karma-config] ${message}`);
debug(`karma basePath: ${basePath}`); debug(`karma basePath: ${basePath}`);
debug(`karma wasmPath: ${wasmPath}`); debug(`karma wasmPath: ${wasmPath}`);
debug(`karma generatedAssetsPath: ${generatedAssetsPath}`);
config.proxies = { config.proxies = {
"/wasm/": wasmPath "/wasm/": wasmPath
} }
config.webpack.output = Object.assign(config.webpack.output || {}, {
path: generatedAssetsPath
})
config.webpack.module.rules.push(
{
test: /\.(ttf|woff|woff2)$/,
type: 'asset/resource',
generator: {
filename: "assets/fonts/[name][ext]"
}
},
{
test: /\.txt$/,
type: 'asset/inline'
},
);
config.files = [ config.files = [
path.resolve(wasmPath, "skiko.js"), path.resolve(wasmPath, "skiko.js"),
{pattern: path.resolve(wasmPath, "skiko.wasm"), included: false, served: true, watched: false}, {pattern: path.resolve(wasmPath, "skiko.wasm"), included: false, served: true, watched: false},
{pattern: path.resolve(generatedAssetsPath, "**/*"), included: false, served: true, watched: false},
].concat(config.files); ].concat(config.files);
...@@ -17,15 +17,6 @@ class Data internal constructor(ptr: NativePointer) : Managed(ptr, _FinalizerHol ...@@ -17,15 +17,6 @@ class Data internal constructor(ptr: NativePointer) : Managed(ptr, _FinalizerHol
) )
} }
/**
* Create a new dataref the file with the specified path.
* If the file cannot be opened, this returns null.
*/
fun makeFromFileName(path: String?): Data {
Stats.onNativeCall()
return Data(_nMakeFromFileName(path))
}
/** /**
* Returns a new empty dataref (or a reference to a shared empty dataref). * Returns a new empty dataref (or a reference to a shared empty dataref).
* New or shared, the caller must see that [.close] is eventually called. * New or shared, the caller must see that [.close] is eventually called.
...@@ -139,7 +130,7 @@ private external fun _nToByteBuffer(ptr: NativePointer): ByteBuffer ...@@ -139,7 +130,7 @@ private external fun _nToByteBuffer(ptr: NativePointer): ByteBuffer
private external fun _nMakeFromBytes(bytes: InteropPointer, offset: Int, length: Int): NativePointer private external fun _nMakeFromBytes(bytes: InteropPointer, offset: Int, length: Int): NativePointer
@ExternalSymbolName("org_jetbrains_skia_Data__1nMakeFromFileName") @ExternalSymbolName("org_jetbrains_skia_Data__1nMakeFromFileName")
private external fun _nMakeFromFileName(path: String?): NativePointer internal external fun _nMakeFromFileName(path: InteropPointer): NativePointer
@ExternalSymbolName("org_jetbrains_skia_Data__1nMakeSubset") @ExternalSymbolName("org_jetbrains_skia_Data__1nMakeSubset")
private external fun _nMakeSubset(ptr: NativePointer, offset: Int, length: Int): NativePointer private external fun _nMakeSubset(ptr: NativePointer, offset: Int, length: Int): NativePointer
......
package org.jetbrains.skia package org.jetbrains.skia
import org.jetbrains.skia.impl.*
import org.jetbrains.skia.impl.Library.Companion.staticLoad import org.jetbrains.skia.impl.Library.Companion.staticLoad
import org.jetbrains.skia.impl.RefCnt
import org.jetbrains.skia.impl.Native
import org.jetbrains.skia.impl.Stats
import org.jetbrains.skia.impl.reachabilityBarrier
import org.jetbrains.skia.impl.NativePointer
import org.jetbrains.skia.impl.getPtr
class Typeface internal constructor(ptr: NativePointer) : RefCnt(ptr) { class Typeface internal constructor(ptr: NativePointer) : RefCnt(ptr) {
companion object { companion object {
...@@ -30,24 +25,7 @@ class Typeface internal constructor(ptr: NativePointer) : RefCnt(ptr) { ...@@ -30,24 +25,7 @@ class Typeface internal constructor(ptr: NativePointer) : RefCnt(ptr) {
Stats.onNativeCall() Stats.onNativeCall()
return Typeface(_nMakeFromName(name, style._value)) return Typeface(_nMakeFromName(name, style._value))
} }
/**
* @return a new typeface given a file
* @throws IllegalArgumentException If the file does not exist, or is not a valid font file
*/
/**
* @return a new typeface given a file
* @throws IllegalArgumentException If the file does not exist, or is not a valid font file
*/
fun makeFromFile(path: String, index: Int = 0): Typeface {
Stats.onNativeCall()
val ptr = _nMakeFromFile(path, index)
require(ptr != NullPointer) { "Failed to create Typeface from path=\"$path\" index=$index" }
return Typeface(ptr)
}
/**
* @return a new typeface given a Data
* @throws IllegalArgumentException If the data is null, or is not a valid font file
*/
/** /**
* @return a new typeface given a Data * @return a new typeface given a Data
* @throws IllegalArgumentException If the data is null, or is not a valid font file * @throws IllegalArgumentException If the data is null, or is not a valid font file
...@@ -388,7 +366,7 @@ private external fun _nGetVariationAxes(ptr: NativePointer): Array<FontVariation ...@@ -388,7 +366,7 @@ private external fun _nGetVariationAxes(ptr: NativePointer): Array<FontVariation
private external fun _nMakeFromName(name: String?, fontStyle: Int): NativePointer private external fun _nMakeFromName(name: String?, fontStyle: Int): NativePointer
@ExternalSymbolName("org_jetbrains_skia_Typeface__1nMakeFromFile") @ExternalSymbolName("org_jetbrains_skia_Typeface__1nMakeFromFile")
private external fun _nMakeFromFile(path: String?, index: Int): NativePointer internal external fun _nMakeFromFile(path: InteropPointer, index: Int): NativePointer
@ExternalSymbolName("org_jetbrains_skia_Typeface__1nMakeFromData") @ExternalSymbolName("org_jetbrains_skia_Typeface__1nMakeFromData")
private external fun _nMakeFromData(dataPtr: NativePointer, index: Int): NativePointer private external fun _nMakeFromData(dataPtr: NativePointer, index: Int): NativePointer
......
...@@ -3,4 +3,6 @@ package org.jetbrains.skia.impl ...@@ -3,4 +3,6 @@ package org.jetbrains.skia.impl
expect abstract class RefCnt : Managed { expect abstract class RefCnt : Managed {
protected constructor(ptr: NativePointer) protected constructor(ptr: NativePointer)
protected constructor(ptr: NativePointer, allowClose: Boolean) protected constructor(ptr: NativePointer, allowClose: Boolean)
val refCount: Int
} }
\ No newline at end of file
package org.jetbrains.skiko
expect suspend fun loadBytesFromPath(path: String): ByteArray
...@@ -3,9 +3,6 @@ package org.jetbrains.skia.tests ...@@ -3,9 +3,6 @@ package org.jetbrains.skia.tests
import org.jetbrains.skia.Data import org.jetbrains.skia.Data
import org.jetbrains.skia.Typeface import org.jetbrains.skia.Typeface
private const val RESOURCES_PATH = "src/jvmTest/resources/fonts" expect suspend fun Typeface.Companion.makeFromResource(resourceId: String, index: Int = 0): Typeface
fun Typeface.Companion.makeFromResource(resourceId: String, index: Int = 0): Typeface = expect suspend fun Data.Companion.makeFromResource(resourceId: String): Data
makeFromFile("$RESOURCES_PATH/$resourceId", index)
fun Data.Companion.makeFromResource(resourceId: String) = makeFromFileName("$RESOURCES_PATH/$resourceId")
package org.jetbrains.skiko
import org.jetbrains.skiko.tests.runTest
import kotlin.test.Test
import kotlin.test.assertContentEquals
class ResourceTest {
@Test
fun loadFromPathTest() = runTest {
val bytes = loadBytesFromPath(resourcePath("./hello_world.txt"))
assertContentEquals("Hello, world!".encodeToByteArray(), bytes)
}
@Test
fun loadResourceTest() = runTest {
val bytes = loadResourceAsBytes("./hello_world.txt")
assertContentEquals("Hello, world!".encodeToByteArray(), bytes)
}
@Test
fun loadFontTest() = runTest {
val url = resourcePath("./fonts/FiraCode-Regular.ttf")
val res = loadBytesFromPath(url).sliceArray(0..9)
assertContentEquals(byteArrayOf(
0x00, 0x01, 0x00, 0x00, 0x00, 0x12, 0x01, 0x00, 0x00, 0x04
), res)
}
}
\ No newline at end of file
package org.jetbrains.skiko
import org.jetbrains.skia.*
import org.jetbrains.skia.tests.makeFromResource
import org.jetbrains.skiko.tests.runTest
import kotlin.test.*
class TypefaceTest {
@Test
fun typefaceTest() = runTest {
// TODO Commonize array and string results and uncomment
val inter = Typeface.makeFromResource("./fonts/Inter-Hinted-Regular.ttf")
val interV = Typeface.makeFromResource("./fonts/Inter-V.ttf")
val jbMono = Typeface.makeFromResource("./fonts/JetBrainsMono-Regular.ttf")
val jbMonoBold = Typeface.makeFromData(Data.makeFromResource("./fonts/JetBrainsMono-Bold.ttf"))
assertEquals(FontStyle.NORMAL, inter.fontStyle)
assertFalse(inter.isBold)
assertFalse(inter.isItalic)
assertEquals(FontStyle.BOLD, jbMonoBold.fontStyle)
assertTrue(jbMonoBold.isBold)
assertFalse(jbMonoBold.isItalic)
assertFalse(inter.isFixedPitch)
assertTrue(jbMono.isFixedPitch)
if (kotlinBackend == KotlinBackend.JVM) {
assertNull(inter.variationAxes)
assertNull(inter.variations)
val axes = arrayOf(
FontVariationAxis("wght", 100f, 400f, 900f),
FontVariationAxis("slnt", -10f, 0f, 0f)
)
assertContentEquals(axes, interV.variationAxes)
val inter500: Typeface = interV.makeClone(FontVariation("wght", 500f))
assertNotEquals(inter500, interV)
assertContentEquals(FontVariation.parse("wght=500 slnt=0"), inter500.variations)
val inter400: Typeface = interV.makeClone(FontVariation("wght", 400f))
}
assertNotEquals(inter.uniqueId, interV.uniqueId)
assertNotEquals(inter, interV)
assertNotNull(Typeface.makeDefault())
if (kotlinBackend == KotlinBackend.JVM) {
val Skia = intArrayOf(83, 107, 105, 97)
assertContentEquals(shortArrayOf(394, 713, 677, 503), inter.getUTF32Glyphs(Skia))
assertContentEquals(shortArrayOf(394, 713, 677, 503), inter.getStringGlyphs("Skia"))
}
assertEquals(394, inter.getUTF32Glyph(83))
assertEquals(2548, interV.glyphsCount)
assertEquals(17, inter.tablesCount)
if (kotlinBackend == KotlinBackend.JVM) {
assertContentEquals(
arrayOf(
"GDEF",
"GPOS",
"GSUB",
"OS/2",
"cmap",
"cvt ",
"fpgm",
"gasp",
"glyf",
"head",
"hhea",
"hmtx",
"loca",
"maxp",
"name",
"post",
"prep"
), inter.tableTags
)
}
assertTrue(inter.getTableData("loca")!!.size > 0)
assertEquals(2816, inter.unitsPerEm)
if (kotlinBackend == KotlinBackend.JVM) {
assertNull(jbMono.getKerningPairAdjustments(null))
assertNull(jbMono.getKerningPairAdjustments(jbMono.getStringGlyphs("TAV")))
assertContentEquals(arrayOf(FontFamilyName("Inter", "en-US")), interV.familyNames)
assertEquals("Inter", interV.familyName)
}
}
}
\ No newline at end of file
package org.jetbrains.skiko
expect fun resourcePath(resourceId: String): String
suspend inline fun loadResourceAsBytes(resourcePath: String): ByteArray
= loadBytesFromPath(resourcePath(resourcePath))
\ No newline at end of file
Hello, world!
\ No newline at end of file
package org.jetbrains.skiko
import platform.Foundation.NSBundle
import platform.Foundation.NSURL
private val KEXE_DIR: String = NSBundle.mainBundle.bundlePath
private const val RESOURCES_PATH = "src/commonTest/resources"
actual fun resourcePath(resourceId: String) = run {
val filePath = "$KEXE_DIR/../../../../$RESOURCES_PATH/$resourceId"
// Remove all '..' and '.'
val standardized = NSURL.URLWithString(filePath)?.standardizedURL?.absoluteString
standardized ?: filePath
}
...@@ -6,7 +6,7 @@ actual abstract class RefCnt : Managed { ...@@ -6,7 +6,7 @@ actual abstract class RefCnt : Managed {
actual protected constructor(ptr: NativePointer): super(ptr, _FinalizerHolder.PTR, true) actual protected constructor(ptr: NativePointer): super(ptr, _FinalizerHolder.PTR, true)
actual protected constructor(ptr: NativePointer, allowClose: Boolean): super(ptr, _FinalizerHolder.PTR, allowClose) actual protected constructor(ptr: NativePointer, allowClose: Boolean): super(ptr, _FinalizerHolder.PTR, allowClose)
val refCount: Int actual val refCount: Int
get() { get() {
Stats.onNativeCall() Stats.onNativeCall()
return _nGetRefCount(_ptr) return _nGetRefCount(_ptr)
......
package org.jetbrains.skiko
import kotlinx.browser.window
import kotlinx.coroutines.await
import org.khronos.webgl.Int8Array
actual suspend fun loadBytesFromPath(path: String): ByteArray {
val arrayBuffer = window.fetch(path)
.await()
.arrayBuffer()
.await()
val byteArray = Int8Array(arrayBuffer)
return byteArray.unsafeCast<ByteArray>()
}
package org.jetbrains.skia.tests
import org.jetbrains.skia.Data
import org.jetbrains.skia.Typeface
import org.jetbrains.skiko.loadResourceAsBytes
actual suspend inline fun Typeface.Companion.makeFromResource(resourceId: String, index: Int): Typeface =
makeFromData(Data.makeFromResource(resourceId), index)
actual suspend inline fun Data.Companion.makeFromResource(resourceId: String) =
makeFromBytes(loadResourceAsBytes(resourceId))
package org.jetbrains.skiko
import org.jetbrains.skiko.tests.runTest
import org.khronos.webgl.Int8Array
import kotlin.test.Test
import kotlin.test.assertContentEquals
class ResourcesTest {
@Test
fun loadFromPathInlineTest() = runTest {
val bytes = loadBytesFromPath(resourcePath("./hello_world.txt"))
assertContentEquals("Hello, world!".encodeToByteArray(), bytes)
}
@Test
fun loadFromPathExternTest() = runTest {
val url = resourcePath("./fonts/FiraCode-Regular.ttf")
val res = loadBytesFromPath(url).sliceArray(0..9)
assertContentEquals(byteArrayOf(
0x00, 0x01, 0x00, 0x00, 0x00, 0x12, 0x01, 0x00, 0x00, 0x04
), res)
}
@Test
fun loadResourceInlineTest() = runTest {
val bytes = loadResourceAsBytes("./hello_world.txt")
assertContentEquals("Hello, world!".encodeToByteArray(), bytes)
}
@Test
fun loadResourceExternTest() = runTest {
val res = loadResourceAsBytes("./fonts/FiraCode-Regular.ttf").sliceArray(0..9)
assertContentEquals(byteArrayOf(
0x00, 0x01, 0x00, 0x00, 0x00, 0x12, 0x01, 0x00, 0x00, 0x04
), res)
}
@Test
fun loadFromPathByNonLiteralPathTest() = runTest {
suspend fun loadFont(style: String): ByteArray {
val url = resourcePath("./fonts/FiraCode-$style.ttf")
val res = loadBytesFromPath(url).sliceArray(0..9)
return res
}
assertContentEquals(byteArrayOf(
0x00, 0x01, 0x00, 0x00, 0x00, 0x12, 0x01, 0x00, 0x00, 0x04
), loadFont("Regular"))
}
@Test
fun typedArrayCastTest() {
val data = Int8Array(byteArrayOf(1, 2, 3, 4, 5).toTypedArray())
val casted = data.unsafeCast<ByteArray>()
assertContentEquals(byteArrayOf(1, 2, 3, 4, 5), casted)
}
}
\ No newline at end of file
package org.jetbrains.skiko
import org.jetbrains.skia.ExternalSymbolName
@ExternalSymbolName("require")
actual external fun resourcePath(resourceId: String): String
package org.jetbrains.skia
import org.jetbrains.skia.impl.Stats
import org.jetbrains.skia.impl.interopScope
/**
* Create a new dataref the file with the specified path.
* If the file cannot be opened, this returns null.
*/
fun Data.Companion.makeFromFileName(path: String?): Data {
Stats.onNativeCall()
interopScope {
return Data(_nMakeFromFileName(toInterop(path)))
}
}
\ No newline at end of file
package org.jetbrains.skia
import org.jetbrains.skia.impl.Native
import org.jetbrains.skia.impl.Stats
import org.jetbrains.skia.impl.interopScope
/**
* @return a new typeface given a file
* @throws IllegalArgumentException If the file does not exist, or is not a valid font file
*/
fun Typeface.Companion.makeFromFile(path: String, index: Int = 0): Typeface {
Stats.onNativeCall()
interopScope {
val ptr = _nMakeFromFile(toInterop(path), index)
require(ptr != Native.NullPointer) { "Failed to create Typeface from path=\"$path\" index=$index" }
return Typeface(ptr)
}
}
\ No newline at end of file
...@@ -6,7 +6,7 @@ actual abstract class RefCnt : Managed { ...@@ -6,7 +6,7 @@ actual abstract class RefCnt : Managed {
protected actual constructor(ptr: NativePointer) : super(ptr, _FinalizerHolder.PTR, true) protected actual constructor(ptr: NativePointer) : super(ptr, _FinalizerHolder.PTR, true)
protected actual constructor(ptr: NativePointer, allowClose: Boolean) : super(ptr, _FinalizerHolder.PTR, allowClose) protected actual constructor(ptr: NativePointer, allowClose: Boolean) : super(ptr, _FinalizerHolder.PTR, allowClose)
val refCount: Int actual val refCount: Int
get() = try { get() = try {
Stats.onNativeCall() Stats.onNativeCall()
_nGetRefCount(_ptr) _nGetRefCount(_ptr)
......
package org.jetbrains.skiko
import java.io.File
actual suspend fun loadBytesFromPath(path: String): ByteArray {
return File(path).readBytes()
}
package org.jetbrains.skia.tests
import org.jetbrains.skia.Data
import org.jetbrains.skia.Typeface
import org.jetbrains.skia.makeFromFile
import org.jetbrains.skia.makeFromFileName
import org.jetbrains.skiko.resourcePath
actual suspend fun Typeface.Companion.makeFromResource(resourceId: String, index: Int): Typeface =
makeFromFile(resourcePath(resourceId), index)
actual suspend fun Data.Companion.makeFromResource(resourceId: String) =
makeFromFileName(resourcePath(resourceId))
...@@ -3,24 +3,26 @@ package org.jetbrains.skiko ...@@ -3,24 +3,26 @@ package org.jetbrains.skiko
import org.jetbrains.skia.Data import org.jetbrains.skia.Data
import org.jetbrains.skia.FontStyle import org.jetbrains.skia.FontStyle
import org.jetbrains.skia.Typeface import org.jetbrains.skia.Typeface
import org.jetbrains.skia.makeFromFileName
import org.jetbrains.skia.paragraph.TypefaceFontProvider import org.jetbrains.skia.paragraph.TypefaceFontProvider
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertNull import kotlin.test.assertNull
import org.jetbrains.skia.tests.makeFromResource import org.jetbrains.skia.tests.makeFromResource
import org.jetbrains.skiko.tests.runTest
class FontMgrTest { class FontMgrTest {
@Test @Test
fun fontMgrTest() { fun fontMgrTest() = runTest {
val fontManager = TypefaceFontProvider() val fontManager = TypefaceFontProvider()
val jbMono = Typeface.makeFromResource("JetBrainsMono-Regular.ttf") val jbMono = Typeface.makeFromResource("./fonts/JetBrainsMono-Regular.ttf")
fontManager.registerTypeface(jbMono) fontManager.registerTypeface(jbMono)
val jbMonoBold = Typeface.makeFromResource("JetBrainsMono-Bold.ttf") val jbMonoBold = Typeface.makeFromResource("./fonts/JetBrainsMono-Bold.ttf")
fontManager.registerTypeface(jbMonoBold) fontManager.registerTypeface(jbMonoBold)
val inter: Typeface = Typeface.makeFromResource("InterHinted-Regular.ttf") val inter: Typeface = Typeface.makeFromResource("./fonts/Inter-Hinted-Regular.ttf")
fontManager.registerTypeface(inter, "Interface") fontManager.registerTypeface(inter, "Interface")
assertEquals(2, fontManager.familiesCount) assertEquals(2, fontManager.familiesCount)
...@@ -84,7 +86,7 @@ class FontMgrTest { ...@@ -84,7 +86,7 @@ class FontMgrTest {
fontManager.matchFamilyStyleCharacter("JetBrains Mono", FontStyle.BOLD, arrayOf("en-US"), 65 /* A */) fontManager.matchFamilyStyleCharacter("JetBrains Mono", FontStyle.BOLD, arrayOf("en-US"), 65 /* A */)
) )
Data.makeFromFileName("src/jvmTest/resources/fonts/JetBrainsMono-Italic.ttf").use { data -> Data.makeFromFileName("src/commonTest/resources/fonts/JetBrainsMono-Italic.ttf").use { data ->
fontManager.makeFromData(data).use { fontManager.makeFromData(data).use {
fontManager.matchFamily("JetBrains Mono").use { styleSet -> fontManager.matchFamily("JetBrains Mono").use { styleSet ->
assertEquals(2, fontManager.familiesCount) assertEquals(2, fontManager.familiesCount)
......
...@@ -9,15 +9,22 @@ import kotlin.test.assertEquals ...@@ -9,15 +9,22 @@ import kotlin.test.assertEquals
import kotlin.test.assertNotEquals import kotlin.test.assertNotEquals
import org.jetbrains.skia.tests.makeFromResource import org.jetbrains.skia.tests.makeFromResource
import org.jetbrains.skia.tests.assertCloseEnough import org.jetbrains.skia.tests.assertCloseEnough
import org.jetbrains.skiko.tests.runTest
class TextLineTest { class TextLineTest {
private var inter36: Font = Font(Typeface.makeFromResource("InterHinted-Regular.ttf"), 36f) private val inter36: suspend () -> Font = suspend {
private var firaCode36: Font = Font(Typeface.makeFromResource("FiraCode-Regular.ttf"), 36f) Font(Typeface.makeFromResource("./fonts/Inter-Hinted-Regular.ttf"), 36f)
private var jbMono36: Font = Font(Typeface.makeFromResource("JetBrainsMono-Regular.ttf"), 36f) }
private val firaCode36: suspend () -> Font = suspend {
Font(Typeface.makeFromResource("./fonts/FiraCode-Regular.ttf"), 36f)
}
private val jbMono36: suspend () -> Font = suspend {
Font(Typeface.makeFromResource("./fonts/JetBrainsMono-Regular.ttf"), 36f)
}
@Test @Test
fun getOffsetAtCoordTest() { fun getOffsetAtCoordTest() = runTest {
TextLine.make("abc", inter36).use { line -> TextLine.make("abc", inter36()).use { line ->
assertContentEquals(shortArrayOf(503, 574, 581), line.glyphs) assertContentEquals(shortArrayOf(503, 574, 581), line.glyphs)
assertEquals(0, line.getOffsetAtCoord(-10f)) // before “a” assertEquals(0, line.getOffsetAtCoord(-10f)) // before “a”
assertEquals(0, line.getOffsetAtCoord(0f)) // beginning of “a” assertEquals(0, line.getOffsetAtCoord(0f)) // beginning of “a”
...@@ -38,8 +45,8 @@ class TextLineTest { ...@@ -38,8 +45,8 @@ class TextLineTest {
} }
@Test @Test
fun getLeftOffsetAtCoordTest() { fun getLeftOffsetAtCoordTest() = runTest {
TextLine.make("abc", inter36).use { line -> TextLine.make("abc", inter36()).use { line ->
assertEquals(0, line.getLeftOffsetAtCoord(-10f)) // before “a” assertEquals(0, line.getLeftOffsetAtCoord(-10f)) // before “a”
assertEquals(0, line.getLeftOffsetAtCoord(0f)) // beginning of “a” assertEquals(0, line.getLeftOffsetAtCoord(0f)) // beginning of “a”
assertEquals(0, line.getLeftOffsetAtCoord(5f)) // left half of “a” assertEquals(0, line.getLeftOffsetAtCoord(5f)) // left half of “a”
...@@ -59,8 +66,8 @@ class TextLineTest { ...@@ -59,8 +66,8 @@ class TextLineTest {
} }
@Test @Test
fun getCoordAtOffsetTest() { fun getCoordAtOffsetTest() = runTest {
TextLine.make("abc", inter36).use { line -> TextLine.make("abc", inter36()).use { line ->
assertCloseEnough(0f, line.getCoordAtOffset(0)) assertCloseEnough(0f, line.getCoordAtOffset(0))
assertCloseEnough(20f, line.getCoordAtOffset(1)) assertCloseEnough(20f, line.getCoordAtOffset(1))
assertCloseEnough(42f, line.getCoordAtOffset(2)) assertCloseEnough(42f, line.getCoordAtOffset(2))
...@@ -69,8 +76,8 @@ class TextLineTest { ...@@ -69,8 +76,8 @@ class TextLineTest {
} }
@Test @Test
fun ligaturesTest() { fun ligaturesTest() = runTest {
TextLine.make("<=>->", inter36).use { line -> TextLine.make("<=>->", inter36()).use { line ->
assertContentEquals(shortArrayOf(1712, 1701), line.glyphs) assertContentEquals(shortArrayOf(1712, 1701), line.glyphs)
assertEquals(0, line.getOffsetAtCoord(0f)) assertEquals(0, line.getOffsetAtCoord(0f))
...@@ -90,12 +97,12 @@ class TextLineTest { ...@@ -90,12 +97,12 @@ class TextLineTest {
} }
@Test @Test
fun combiningTest() { fun combiningTest() = runTest {
// u U+0075 LATIN SMALL LETTER U // u U+0075 LATIN SMALL LETTER U
// ̈ U+0308 COMBINING DIAERESIS // ̈ U+0308 COMBINING DIAERESIS
// a U+0061 LATIN SMALL LETTER A // a U+0061 LATIN SMALL LETTER A
// ̧ U+0327 COMBINING CEDILLA // ̧ U+0327 COMBINING CEDILLA
TextLine.make("üa̧", inter36).use { line -> TextLine.make("üa̧", inter36()).use { line ->
assertContentEquals(shortArrayOf(898 /* ü */, 503 /* a */, 1664 /* ̧ */), line.glyphs) assertContentEquals(shortArrayOf(898 /* ü */, 503 /* a */, 1664 /* ̧ */), line.glyphs)
assertEquals(0, line.getOffsetAtCoord(0f)) assertEquals(0, line.getOffsetAtCoord(0f))
...@@ -113,8 +120,8 @@ class TextLineTest { ...@@ -113,8 +120,8 @@ class TextLineTest {
assertCloseEnough(41f, line.getCoordAtOffset(4)) assertCloseEnough(41f, line.getCoordAtOffset(4))
} }
TextLine.make("ă", jbMono36).use { line -> } TextLine.make("ă", jbMono36()).use { line -> }
TextLine.make("aa̧", jbMono36).use { line -> TextLine.make("aa̧", jbMono36()).use { line ->
// JetBrains Mono supports “a” but not “ ̧ ” // JetBrains Mono supports “a” but not “ ̧ ”
// Second grapheme cluster should fall back together, second “a” should resolve to different glyph // Second grapheme cluster should fall back together, second “a” should resolve to different glyph
assertNotEquals(line.glyphs.get(0), line.glyphs.get(1)) assertNotEquals(line.glyphs.get(0), line.glyphs.get(1))
...@@ -122,9 +129,9 @@ class TextLineTest { ...@@ -122,9 +129,9 @@ class TextLineTest {
} }
@Test @Test
fun emojiTest() { fun emojiTest() = runTest {
TextLine.make("☺", firaCode36).use { misc -> TextLine.make("☺", firaCode36()).use { misc ->
TextLine.make("☺️", firaCode36).use { emoji -> TextLine.make("☺️", firaCode36()).use { emoji ->
assertContentEquals(shortArrayOf(1706), misc.glyphs) assertContentEquals(shortArrayOf(1706), misc.glyphs)
assertEquals(1, emoji.glyphs.size) assertEquals(1, emoji.glyphs.size)
assertNotEquals(misc.glyphs.get(0), emoji.glyphs.get(0)) assertNotEquals(misc.glyphs.get(0), emoji.glyphs.get(0))
......
package org.jetbrains.skiko
import org.jetbrains.skia.Data
import org.jetbrains.skia.FontFamilyName
import org.jetbrains.skia.FontStyle
import org.jetbrains.skia.FontVariation
import org.jetbrains.skia.FontVariationAxis
import org.jetbrains.skia.Typeface
import kotlin.test.Test
import kotlin.test.assertContentEquals
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertTrue
import org.jetbrains.skia.tests.makeFromResource
class TypefaceTest {
@Test
fun typefaceTest() {
val inter = Typeface.makeFromResource("InterHinted-Regular.ttf")
val interV = Typeface.makeFromResource("Inter-V.ttf")
val jbMono = Typeface.makeFromResource("JetBrainsMono-Regular.ttf")
val jbMonoBold = Typeface.makeFromData(Data.makeFromResource("JetBrainsMono-Bold.ttf"))
assertEquals(FontStyle.NORMAL, inter.fontStyle)
assertFalse(inter.isBold)
assertFalse(inter.isItalic)
assertEquals(FontStyle.BOLD, jbMonoBold.fontStyle)
assertTrue(jbMonoBold.isBold)
assertFalse(jbMonoBold.isItalic)
assertFalse(inter.isFixedPitch)
assertTrue(jbMono.isFixedPitch)
assertNull(inter.variationAxes)
assertNull(inter.variations)
val axes = arrayOf(
FontVariationAxis("wght", 100f, 400f, 900f),
FontVariationAxis("slnt", -10f, 0f, 0f)
)
assertContentEquals(axes, interV.variationAxes)
val inter500: Typeface = interV.makeClone(FontVariation("wght", 500f))
assertNotEquals(inter500, interV)
assertContentEquals(FontVariation.parse("wght=500 slnt=0"), inter500.variations)
val inter400: Typeface = interV.makeClone(FontVariation("wght", 400f))
assertNotEquals(inter.uniqueId, interV.uniqueId)
assertNotEquals(inter, interV)
assertNotNull(Typeface.makeDefault())
val Skia = intArrayOf(83, 107, 105, 97)
assertContentEquals(shortArrayOf(394, 713, 677, 503), inter.getUTF32Glyphs(Skia))
assertContentEquals(shortArrayOf(394, 713, 677, 503), inter.getStringGlyphs("Skia"))
assertEquals(394, inter.getUTF32Glyph(83))
assertEquals(2548, interV.glyphsCount)
assertEquals(17, inter.tablesCount)
assertContentEquals(
arrayOf(
"GDEF",
"GPOS",
"GSUB",
"OS/2",
"cmap",
"cvt ",
"fpgm",
"gasp",
"glyf",
"head",
"hhea",
"hmtx",
"loca",
"maxp",
"name",
"post",
"prep"
), inter.tableTags
)
assertTrue(inter.getTableData("loca")!!.size > 0)
assertEquals(2816, inter.unitsPerEm)
assertNull(jbMono.getKerningPairAdjustments(null))
assertNull(jbMono.getKerningPairAdjustments(jbMono.getStringGlyphs("TAV")))
assertContentEquals(arrayOf(FontFamilyName("Inter", "en-US")), interV.familyNames)
assertEquals("Inter", interV.familyName)
}
}
\ No newline at end of file
...@@ -6,6 +6,7 @@ import org.jetbrains.skia.Typeface ...@@ -6,6 +6,7 @@ import org.jetbrains.skia.Typeface
import org.jetbrains.skia.paragraph.FontCollection import org.jetbrains.skia.paragraph.FontCollection
import org.jetbrains.skia.paragraph.TypefaceFontProvider import org.jetbrains.skia.paragraph.TypefaceFontProvider
import org.jetbrains.skia.tests.makeFromResource import org.jetbrains.skia.tests.makeFromResource
import org.jetbrains.skiko.tests.runTest
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertContentEquals import kotlin.test.assertContentEquals
import kotlin.test.assertEquals import kotlin.test.assertEquals
...@@ -13,12 +14,12 @@ import kotlin.test.assertEquals ...@@ -13,12 +14,12 @@ import kotlin.test.assertEquals
class FontCollectionTest { class FontCollectionTest {
@Test @Test
fun fontCollectionTest() { fun fontCollectionTest() = runTest {
val fm = TypefaceFontProvider() val fm = TypefaceFontProvider()
val jbMono = Typeface.makeFromResource("JetBrainsMono-Regular.ttf", 0) val jbMono = Typeface.makeFromResource("./fonts/JetBrainsMono-Regular.ttf", 0)
fm.registerTypeface(jbMono) fm.registerTypeface(jbMono)
val inter = Typeface.makeFromResource("InterHinted-Regular.ttf", 0) val inter = Typeface.makeFromResource("./fonts/Inter-Hinted-Regular.ttf", 0)
fm.registerTypeface(inter, "Interface") fm.registerTypeface(inter, "Interface")
// FontCollection // FontCollection
......
package org.jetbrains.skiko
import java.nio.file.Paths
private const val RESOURCES_PATH = "src/commonTest/resources"
actual fun resourcePath(resourceId: String) =
Paths.get("${RESOURCES_PATH}/$resourceId").normalize().toAbsolutePath().toString()
package org.jetbrains.skiko
import platform.posix.realpath
import platform.posix.PATH_MAX
import kotlinx.cinterop.addressOf
import kotlinx.cinterop.toKString
import kotlinx.cinterop.usePinned
private const val RESOURCES_PATH = "src/commonTest/resources"
actual fun resourcePath(resourceId: String) = run {
val filePath = "$RESOURCES_PATH/$resourceId"
// Remove all '..' and '.'
var buffer = ByteArray(PATH_MAX)
val standardized = buffer.usePinned {
realpath(filePath, it.addressOf(0))?.toKString()
}
standardized ?: filePath
}
...@@ -60,7 +60,10 @@ SKIKO_EXPORT KNativePointer org_jetbrains_skia_Data__1nMakeFromBytes ...@@ -60,7 +60,10 @@ SKIKO_EXPORT KNativePointer org_jetbrains_skia_Data__1nMakeFromBytes
SKIKO_EXPORT KNativePointer org_jetbrains_skia_Data__1nMakeFromFileName SKIKO_EXPORT KNativePointer org_jetbrains_skia_Data__1nMakeFromFileName
(KInteropPointer pathStr) { (KInteropPointer pathStr) {
TODO("implement org_jetbrains_skia_Data__1nMakeFromFileName"); SkString path = skString(pathStr);
sk_sp<SkData> instance = SkData::MakeFromFileName(path.c_str());
SkData* ptr = instance.release();
return reinterpret_cast<KNativePointer>(ptr);
} }
SKIKO_EXPORT KNativePointer org_jetbrains_skia_Data__1nMakeSubset SKIKO_EXPORT KNativePointer org_jetbrains_skia_Data__1nMakeSubset
......
...@@ -7,18 +7,11 @@ ...@@ -7,18 +7,11 @@
#include "common.h" #include "common.h"
SKIKO_EXPORT KInt org_jetbrains_skia_Typeface__1nGetFontStyle
(KNativePointer ptr) {
TODO("implement org_jetbrains_skia_Typeface__1nGetFontStyle");
}
#if 0
SKIKO_EXPORT KInt org_jetbrains_skia_Typeface__1nGetFontStyle SKIKO_EXPORT KInt org_jetbrains_skia_Typeface__1nGetFontStyle
(KNativePointer ptr) { (KNativePointer ptr) {
SkTypeface* instance = reinterpret_cast<SkTypeface*>((ptr)); SkTypeface* instance = reinterpret_cast<SkTypeface*>((ptr));
return skija::FontStyle::toJava(instance->fontStyle()); return skija::FontStyle::toKotlin(instance->fontStyle());
} }
#endif
SKIKO_EXPORT KBoolean org_jetbrains_skia_Typeface__1nIsFixedPitch SKIKO_EXPORT KBoolean org_jetbrains_skia_Typeface__1nIsFixedPitch
...@@ -139,18 +132,11 @@ SKIKO_EXPORT KNativePointer org_jetbrains_skia_Typeface__1nMakeFromName ...@@ -139,18 +132,11 @@ SKIKO_EXPORT KNativePointer org_jetbrains_skia_Typeface__1nMakeFromName
SKIKO_EXPORT KNativePointer org_jetbrains_skia_Typeface__1nMakeFromFile SKIKO_EXPORT KNativePointer org_jetbrains_skia_Typeface__1nMakeFromFile
(KInteropPointer pathStr, KInt index) { (KInteropPointer pathStr, KInt index) {
TODO("implement org_jetbrains_skia_Typeface__1nMakeFromFile"); SkString path = skString(pathStr);
}
#if 0
SKIKO_EXPORT KNativePointer org_jetbrains_skia_Typeface__1nMakeFromFile
(KInteropPointer pathStr, KInt index) {
SkString path = skString(env, pathStr);
sk_sp<SkTypeface> instance = SkTypeface::MakeFromFile(path.c_str(), index); sk_sp<SkTypeface> instance = SkTypeface::MakeFromFile(path.c_str(), index);
SkTypeface* ptr = setDefaultVariationCoords(instance).release(); SkTypeface* ptr = setDefaultVariationCoords(instance).release();
return reinterpret_cast<KNativePointer>(ptr); return reinterpret_cast<KNativePointer>(ptr);
} }
#endif
SKIKO_EXPORT KNativePointer org_jetbrains_skia_Typeface__1nMakeFromData SKIKO_EXPORT KNativePointer org_jetbrains_skia_Typeface__1nMakeFromData
......
...@@ -13,13 +13,6 @@ SKIKO_EXPORT KNativePointer org_jetbrains_skia_paragraph_TypefaceFontProvider__1 ...@@ -13,13 +13,6 @@ SKIKO_EXPORT KNativePointer org_jetbrains_skia_paragraph_TypefaceFontProvider__1
return reinterpret_cast<KNativePointer>(instance); return reinterpret_cast<KNativePointer>(instance);
} }
SKIKO_EXPORT void org_jetbrains_skia_paragraph_TypefaceFontProvider__1nRegisterTypeface
(KNativePointer ptr, KNativePointer typefacePtr, KInteropPointer aliasStr) {
TODO("implement org_jetbrains_skia_paragraph_TypefaceFontProvider__1nRegisterTypeface");
}
#if 0
SKIKO_EXPORT void org_jetbrains_skia_paragraph_TypefaceFontProvider__1nRegisterTypeface SKIKO_EXPORT void org_jetbrains_skia_paragraph_TypefaceFontProvider__1nRegisterTypeface
(KNativePointer ptr, KNativePointer typefacePtr, KInteropPointer aliasStr) { (KNativePointer ptr, KNativePointer typefacePtr, KInteropPointer aliasStr) {
TypefaceFontProvider* instance = reinterpret_cast<TypefaceFontProvider*>((ptr)); TypefaceFontProvider* instance = reinterpret_cast<TypefaceFontProvider*>((ptr));
...@@ -27,7 +20,6 @@ SKIKO_EXPORT void org_jetbrains_skia_paragraph_TypefaceFontProvider__1nRegisterT ...@@ -27,7 +20,6 @@ SKIKO_EXPORT void org_jetbrains_skia_paragraph_TypefaceFontProvider__1nRegisterT
if (aliasStr == nullptr) if (aliasStr == nullptr)
instance->registerTypeface(sk_ref_sp(typeface)); instance->registerTypeface(sk_ref_sp(typeface));
else else
instance->registerTypeface(sk_ref_sp(typeface), skString(env, aliasStr)); instance->registerTypeface(sk_ref_sp(typeface), skString(aliasStr));
} }
#endif
package org.jetbrains.skia
import org.jetbrains.skia.impl.Stats
import org.jetbrains.skia.impl.interopScope
/**
* Create a new dataref the file with the specified path.
* If the file cannot be opened, this returns null.
*/
fun Data.Companion.makeFromFileName(path: String?): Data {
Stats.onNativeCall()
interopScope {
return Data(_nMakeFromFileName(toInterop(path)))
}
}
\ No newline at end of file
package org.jetbrains.skia
import org.jetbrains.skia.impl.Native
import org.jetbrains.skia.impl.Stats
import org.jetbrains.skia.impl.interopScope
/**
* @return a new typeface given a file
* @throws IllegalArgumentException If the file does not exist, or is not a valid font file
*/
fun Typeface.Companion.makeFromFile(path: String, index: Int = 0): Typeface {
Stats.onNativeCall()
interopScope {
val ptr = _nMakeFromFile(toInterop(path), index)
require(ptr != Native.NullPointer) { "Failed to create Typeface from path=\"$path\" index=$index" }
return Typeface(ptr)
}
}
\ No newline at end of file
...@@ -6,7 +6,7 @@ actual abstract class RefCnt : Managed { ...@@ -6,7 +6,7 @@ actual abstract class RefCnt : Managed {
protected actual constructor(ptr: NativePointer) : super(ptr, _FinalizerHolder.PTR, true) {} protected actual constructor(ptr: NativePointer) : super(ptr, _FinalizerHolder.PTR, true) {}
protected actual constructor(ptr: NativePointer, allowClose: Boolean) : super(ptr, _FinalizerHolder.PTR, allowClose) protected actual constructor(ptr: NativePointer, allowClose: Boolean) : super(ptr, _FinalizerHolder.PTR, allowClose)
val refCount: Int actual val refCount: Int
get() = try { get() = try {
Stats.onNativeCall() Stats.onNativeCall()
_nGetRefCount(_ptr) _nGetRefCount(_ptr)
......
package org.jetbrains.skiko
import kotlinx.cinterop.addressOf
import kotlinx.cinterop.toKString
import kotlinx.cinterop.usePinned
import platform.posix.*
actual suspend fun loadBytesFromPath(path: String): ByteArray {
val file = fopen(path, "r") ?: run {
val error = strerror(errno)?.toKString() ?: "Unknown error"
throw Error("Can not open file '$path': $error")
}
val size = file.let {
fseek(it, 0, SEEK_END)
val size = ftell(it)
fseek(it, 0, SEEK_SET)
size
}
if (size < 0) {
fclose(file)
throw Error("Can not read file '$path'")
}
if (size > Int.MAX_VALUE.toLong()) {
fclose(file)
throw Error("File '$path' is too long")
}
if (size == 0L) {
fclose(file)
return byteArrayOf()
}
val bytes = ByteArray(size.toInt())
val result = bytes.usePinned {
fread(it.addressOf(0), 1, size.toULong(), file)
}
fclose(file)
if (result != size.toULong()) {
throw Error("Can not read file '$path'")
}
return bytes
}
package org.jetbrains.skia.tests
import org.jetbrains.skia.Data
import org.jetbrains.skia.Typeface
import org.jetbrains.skia.makeFromFile
import org.jetbrains.skia.makeFromFileName
import org.jetbrains.skiko.resourcePath
actual suspend fun Typeface.Companion.makeFromResource(resourceId: String, index: Int): Typeface =
makeFromFile(resourcePath(resourceId), index)
actual suspend fun Data.Companion.makeFromResource(resourceId: String) =
makeFromFileName(resourcePath(resourceId))
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment