@angadie/chittie-react-native 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ChittieReactNative.podspec +32 -0
- package/LICENSE +24 -0
- package/README.md +80 -0
- package/android/CMakeLists.txt +25 -0
- package/android/build.gradle +100 -0
- package/android/fix-prefab.gradle +37 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/cpp/cpp-adapter.cpp +6 -0
- package/android/src/main/java/com/margelo/nitro/chittie/HybridChittieRasterizer.kt +53 -0
- package/app.plugin.js +5 -0
- package/ios/HybridChittieRasterizer.swift +48 -0
- package/lib/commonjs/adapter.js +33 -0
- package/lib/commonjs/adapter.js.map +1 -0
- package/lib/commonjs/index.js +34 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/specs/ChittieRasterizer.nitro.js +6 -0
- package/lib/commonjs/specs/ChittieRasterizer.nitro.js.map +1 -0
- package/lib/module/adapter.js +29 -0
- package/lib/module/adapter.js.map +1 -0
- package/lib/module/index.js +25 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/specs/ChittieRasterizer.nitro.js +4 -0
- package/lib/module/specs/ChittieRasterizer.nitro.js.map +1 -0
- package/lib/typescript/src/adapter.d.ts +11 -0
- package/lib/typescript/src/adapter.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +15 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/specs/ChittieRasterizer.nitro.d.ts +23 -0
- package/lib/typescript/src/specs/ChittieRasterizer.nitro.d.ts.map +1 -0
- package/nitro.json +23 -0
- package/nitrogen/generated/.gitattributes +1 -0
- package/nitrogen/generated/android/ChittieReactNative+autolinking.cmake +81 -0
- package/nitrogen/generated/android/ChittieReactNative+autolinking.gradle +27 -0
- package/nitrogen/generated/android/ChittieReactNativeOnLoad.cpp +54 -0
- package/nitrogen/generated/android/ChittieReactNativeOnLoad.hpp +34 -0
- package/nitrogen/generated/android/c++/JHybridChittieRasterizerSpec.cpp +58 -0
- package/nitrogen/generated/android/c++/JHybridChittieRasterizerSpec.hpp +63 -0
- package/nitrogen/generated/android/c++/JRasterBitmap.hpp +66 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/chittie/ChittieReactNativeOnLoad.kt +35 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/chittie/HybridChittieRasterizerSpec.kt +54 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/chittie/RasterBitmap.kt +44 -0
- package/nitrogen/generated/ios/ChittieReactNative+autolinking.rb +60 -0
- package/nitrogen/generated/ios/ChittieReactNative-Swift-Cxx-Bridge.cpp +33 -0
- package/nitrogen/generated/ios/ChittieReactNative-Swift-Cxx-Bridge.hpp +54 -0
- package/nitrogen/generated/ios/ChittieReactNative-Swift-Cxx-Umbrella.hpp +48 -0
- package/nitrogen/generated/ios/ChittieReactNativeAutolinking.mm +33 -0
- package/nitrogen/generated/ios/ChittieReactNativeAutolinking.swift +26 -0
- package/nitrogen/generated/ios/c++/HybridChittieRasterizerSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridChittieRasterizerSpecSwift.hpp +88 -0
- package/nitrogen/generated/ios/swift/HybridChittieRasterizerSpec.swift +55 -0
- package/nitrogen/generated/ios/swift/HybridChittieRasterizerSpec_cxx.swift +138 -0
- package/nitrogen/generated/ios/swift/RasterBitmap.swift +39 -0
- package/nitrogen/generated/shared/c++/HybridChittieRasterizerSpec.cpp +21 -0
- package/nitrogen/generated/shared/c++/HybridChittieRasterizerSpec.hpp +64 -0
- package/nitrogen/generated/shared/c++/RasterBitmap.hpp +91 -0
- package/package.json +77 -0
- package/src/adapter.ts +25 -0
- package/src/index.ts +27 -0
- package/src/specs/ChittieRasterizer.nitro.ts +21 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = "ChittieReactNative"
|
|
7
|
+
s.version = package["version"]
|
|
8
|
+
s.summary = package["description"]
|
|
9
|
+
s.homepage = package["homepage"]
|
|
10
|
+
s.license = package["license"]
|
|
11
|
+
s.authors = package["author"]
|
|
12
|
+
|
|
13
|
+
s.platforms = { :ios => min_ios_version_supported }
|
|
14
|
+
s.source = { :git => "https://github.com/octalpixel/chittie.git", :tag => "#{s.version}" }
|
|
15
|
+
|
|
16
|
+
s.source_files = [
|
|
17
|
+
# Implementation (Swift)
|
|
18
|
+
"ios/**/*.{swift}",
|
|
19
|
+
# Autolinking / registration (Objective-C++)
|
|
20
|
+
"ios/**/*.{m,mm}",
|
|
21
|
+
# C++ objects (if any)
|
|
22
|
+
"cpp/**/*.{hpp,cpp}",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
# nitrogen-generated autolinking (produced by `nitro-codegen`).
|
|
26
|
+
load 'nitrogen/generated/ios/ChittieReactNative+autolinking.rb'
|
|
27
|
+
add_nitrogen_files(s)
|
|
28
|
+
|
|
29
|
+
s.dependency 'React-jsi'
|
|
30
|
+
s.dependency 'React-callinvoker'
|
|
31
|
+
install_modules_dependencies(s)
|
|
32
|
+
end
|
package/LICENSE
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Asyncdot Engineering
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
Portions of this software are vendored from MIT-licensed projects and retain
|
|
16
|
+
their original copyright notices; see VENDOR.md.
|
|
17
|
+
|
|
18
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
19
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
20
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
21
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
22
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
23
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# @angadie/chittie-react-native
|
|
2
|
+
|
|
3
|
+
The **React Native backend for chittie** — a [Nitro](https://nitro.margelo.com) module that
|
|
4
|
+
rasterizes **non-Latin text** (Sinhala/Tamil/Arabic) natively (**CoreText** on iOS,
|
|
5
|
+
**android.graphics** on Android) and exposes it as chittie's `TextRasterizer`. No Skia.
|
|
6
|
+
|
|
7
|
+
> **Dev client / bare RN only** — it's a native module, so it can't run in Expo Go. (Your printer
|
|
8
|
+
> transport — BLE/USB — needs a dev client anyway, so this costs you nothing extra.) See the RFC:
|
|
9
|
+
> `docs/rfc/rn-native.md`.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npm i @angadie/chittie-react-native @angadie/chittie react-native-nitro-modules
|
|
15
|
+
cd ios && pod install # iOS
|
|
16
|
+
# Android autolinks; rebuild the dev client.
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The Nitro bindings (`nitrogen/`) are **generated and shipped** — you only re-run `npm run codegen`
|
|
20
|
+
if you change the `.nitro.ts` spec. Built with **react-native-builder-bob** (`lib/commonjs|module|
|
|
21
|
+
typescript`). Requires React Native's **new architecture**.
|
|
22
|
+
|
|
23
|
+
## Use
|
|
24
|
+
|
|
25
|
+
Pass the native rasterizer to `render()` — everything else is the same chittie API:
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
import { render, Printer, Text, Row } from '@angadie/chittie';
|
|
29
|
+
import { createNativeRasterizer } from '@angadie/chittie-react-native';
|
|
30
|
+
|
|
31
|
+
const rasterizer = createNativeRasterizer();
|
|
32
|
+
|
|
33
|
+
const bytes = render(
|
|
34
|
+
<Printer width={32}>
|
|
35
|
+
<Text align="center" bold>ආයුබෝවන්</Text>
|
|
36
|
+
<Row left="තේ" right="Rs. 250" />
|
|
37
|
+
</Printer>,
|
|
38
|
+
{ dotWidth: 384, rasterizer } // 58mm
|
|
39
|
+
);
|
|
40
|
+
// send `bytes` over your transport (BLE/USB/TCP)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The native side returns an RGBA bitmap sized to the text (dims padded to /8), which chittie packs
|
|
44
|
+
to ESC/POS raster unchanged.
|
|
45
|
+
|
|
46
|
+
## Architecture
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
TS: createNativeRasterizer() → makeRasterizer(native) → chittie TextRasterizer
|
|
50
|
+
(src/index.ts) (src/adapter.ts, pure) (ImageData → render())
|
|
51
|
+
Nitro spec: src/specs/ChittieRasterizer.nitro.ts (rasterize → RasterBitmap{data,width,height})
|
|
52
|
+
Native: ios/HybridChittieRasterizer.swift (UIKit/CoreText)
|
|
53
|
+
android/.../HybridChittieRasterizer.kt (StaticLayout/Canvas)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`makeRasterizer` is pure and dependency-free (no Nitro import) so it's unit-testable off-device;
|
|
57
|
+
`createNativeRasterizer` wires the real Nitro HybridObject.
|
|
58
|
+
|
|
59
|
+
## What's verified vs gated
|
|
60
|
+
|
|
61
|
+
- **Verified (off-device):** `nitro-codegen` runs and the bindings are committed under `nitrogen/`;
|
|
62
|
+
the TS adapter spike (native bitmap → `ImageData` → chittie's `padTo8`, `spikes/adapter.spike.mts`);
|
|
63
|
+
`bob build` (commonjs/module/typescript) + typecheck in the monorepo gate.
|
|
64
|
+
- **Gated (needs Xcode/Android Studio + a device):** the native **Swift/Kotlin compile** and an
|
|
65
|
+
on-device snapshot of the rendered glyphs. The iOS/Android build files mirror the proven
|
|
66
|
+
`react-native-nitro-haptics` template; the device build validates them.
|
|
67
|
+
|
|
68
|
+
## Roadmap (per the RFC)
|
|
69
|
+
|
|
70
|
+
- Phase 1 — **rasterizer** (this).
|
|
71
|
+
- Phase 2 — **read-capable BLE transport** (status via `DLE EOT` notify; see `chittie-transport`'s
|
|
72
|
+
`queryStatus`).
|
|
73
|
+
- Phase 3 — **USB (Android host) + TCP** transports.
|
|
74
|
+
|
|
75
|
+
## Notes
|
|
76
|
+
|
|
77
|
+
- Complex shaping + bidi come from the platform engines for free (UIKit/StaticLayout), so Arabic
|
|
78
|
+
joins and reorders correctly. For RTL *rows*, also pass `rtl` to `<Row>` (see `docs/i18n.md`).
|
|
79
|
+
- For font selection, the native side uses the system font (which has Sinhala/Tamil/Arabic
|
|
80
|
+
fallbacks); custom-font support is a future option.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
project(ChittieReactNative)
|
|
2
|
+
cmake_minimum_required(VERSION 3.9.0)
|
|
3
|
+
|
|
4
|
+
set (PACKAGE_NAME ChittieReactNative)
|
|
5
|
+
set (CMAKE_VERBOSE_MAKEFILE ON)
|
|
6
|
+
set (CMAKE_CXX_STANDARD 20)
|
|
7
|
+
|
|
8
|
+
# Define C++ library and add all sources
|
|
9
|
+
add_library(${PACKAGE_NAME} SHARED
|
|
10
|
+
src/main/cpp/cpp-adapter.cpp
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
# Add Nitrogen specs :)
|
|
14
|
+
include(${CMAKE_SOURCE_DIR}/../nitrogen/generated/android/ChittieReactNative+autolinking.cmake)
|
|
15
|
+
|
|
16
|
+
# Set up local includes
|
|
17
|
+
include_directories("src/main/cpp")
|
|
18
|
+
|
|
19
|
+
find_library(LOG_LIB log)
|
|
20
|
+
|
|
21
|
+
target_link_libraries(
|
|
22
|
+
${PACKAGE_NAME}
|
|
23
|
+
${LOG_LIB}
|
|
24
|
+
android
|
|
25
|
+
)
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
repositories {
|
|
3
|
+
google()
|
|
4
|
+
mavenCentral()
|
|
5
|
+
}
|
|
6
|
+
dependencies {
|
|
7
|
+
classpath "com.android.tools.build:gradle:8.8.0"
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
def reactNativeArchitectures() {
|
|
12
|
+
def value = rootProject.getProperties().get("reactNativeArchitectures")
|
|
13
|
+
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
def isNewArchitectureEnabled() {
|
|
17
|
+
return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
apply plugin: "com.android.library"
|
|
21
|
+
apply plugin: "org.jetbrains.kotlin.android"
|
|
22
|
+
apply from: '../nitrogen/generated/android/ChittieReactNative+autolinking.gradle'
|
|
23
|
+
apply from: "./fix-prefab.gradle"
|
|
24
|
+
|
|
25
|
+
if (isNewArchitectureEnabled()) {
|
|
26
|
+
apply plugin: "com.facebook.react"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
def getExtOrDefault(name) {
|
|
30
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["ChittieReactNative_" + name]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
def getExtOrIntegerDefault(name) {
|
|
34
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["ChittieReactNative_" + name]).toInteger()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
android {
|
|
38
|
+
namespace "com.chittie"
|
|
39
|
+
|
|
40
|
+
ndkVersion getExtOrDefault("ndkVersion")
|
|
41
|
+
compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
|
|
42
|
+
|
|
43
|
+
defaultConfig {
|
|
44
|
+
minSdkVersion getExtOrIntegerDefault("minSdkVersion")
|
|
45
|
+
targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
|
|
46
|
+
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
|
|
47
|
+
|
|
48
|
+
externalNativeBuild {
|
|
49
|
+
cmake {
|
|
50
|
+
cppFlags "-frtti -fexceptions -Wall -Wextra -fstack-protector-all"
|
|
51
|
+
arguments "-DANDROID_STL=c++_shared", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
|
|
52
|
+
abiFilters (*reactNativeArchitectures())
|
|
53
|
+
|
|
54
|
+
buildTypes {
|
|
55
|
+
debug { cppFlags "-O1 -g" }
|
|
56
|
+
release { cppFlags "-O2" }
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
externalNativeBuild {
|
|
63
|
+
cmake { path "CMakeLists.txt" }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
buildFeatures {
|
|
67
|
+
buildConfig true
|
|
68
|
+
prefab true
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
buildTypes {
|
|
72
|
+
release { minifyEnabled false }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
lintOptions { disable "GradleCompatible" }
|
|
76
|
+
|
|
77
|
+
compileOptions {
|
|
78
|
+
sourceCompatibility JavaVersion.VERSION_1_8
|
|
79
|
+
targetCompatibility JavaVersion.VERSION_1_8
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
repositories {
|
|
84
|
+
mavenCentral()
|
|
85
|
+
google()
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
dependencies {
|
|
89
|
+
//noinspection GradleDynamicVersion
|
|
90
|
+
implementation "com.facebook.react:react-native:+"
|
|
91
|
+
implementation project(":react-native-nitro-modules")
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (isNewArchitectureEnabled()) {
|
|
95
|
+
react {
|
|
96
|
+
jsRootDir = file("../src/")
|
|
97
|
+
libraryName = "ChittieReactNative"
|
|
98
|
+
codegenJavaPackageName = "com.chittie"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
tasks.configureEach { task ->
|
|
2
|
+
// Generate our prefab publication file only after building the native library so a full
|
|
3
|
+
// configuration publication (including the .so) is generated, not just a header publication.
|
|
4
|
+
def prefabConfigurePattern = ~/^prefab(.+)ConfigurePackage$/
|
|
5
|
+
def matcher = task.name =~ prefabConfigurePattern
|
|
6
|
+
if (matcher.matches()) {
|
|
7
|
+
def variantName = matcher[0][1]
|
|
8
|
+
task.outputs.upToDateWhen { false }
|
|
9
|
+
task.dependsOn("externalNativeBuild${variantName}")
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
afterEvaluate {
|
|
14
|
+
def abis = rootProject.getProperties().get("reactNativeArchitectures")?.split(",") ?: ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
|
|
15
|
+
rootProject.allprojects.each { proj ->
|
|
16
|
+
if (proj === rootProject) return
|
|
17
|
+
|
|
18
|
+
def dependsOnThisLib = proj.configurations.findAll { it.canBeResolved }.any { config ->
|
|
19
|
+
config.dependencies.any { dep -> dep.group == project.group && dep.name == project.name }
|
|
20
|
+
}
|
|
21
|
+
if (!dependsOnThisLib && proj != project) return
|
|
22
|
+
if (!proj.plugins.hasPlugin('com.android.application') && !proj.plugins.hasPlugin('com.android.library')) return
|
|
23
|
+
|
|
24
|
+
def variants = proj.android.hasProperty('applicationVariants') ? proj.android.applicationVariants : proj.android.libraryVariants
|
|
25
|
+
variants.all { variant ->
|
|
26
|
+
def variantName = variant.name
|
|
27
|
+
abis.each { abi ->
|
|
28
|
+
def searchDir = new File(proj.projectDir, ".cxx/${variantName}")
|
|
29
|
+
if (!searchDir.exists()) return
|
|
30
|
+
searchDir.eachDir { randomDir ->
|
|
31
|
+
def prefabFile = new File(randomDir, "${abi}/prefab_config.json")
|
|
32
|
+
if (prefabFile.exists()) prefabFile.setLastModified(System.currentTimeMillis())
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
package com.margelo.nitro.chittie
|
|
2
|
+
|
|
3
|
+
import android.graphics.Bitmap
|
|
4
|
+
import android.graphics.Canvas
|
|
5
|
+
import android.graphics.Color
|
|
6
|
+
import android.graphics.Typeface
|
|
7
|
+
import android.text.Layout
|
|
8
|
+
import android.text.StaticLayout
|
|
9
|
+
import android.text.TextPaint
|
|
10
|
+
import com.margelo.nitro.core.ArrayBuffer
|
|
11
|
+
import java.nio.ByteBuffer
|
|
12
|
+
import kotlin.math.ceil
|
|
13
|
+
|
|
14
|
+
// Implements the nitrogen-generated HybridChittieRasterizerSpec.
|
|
15
|
+
// StaticLayout shapes complex scripts (Sinhala/Tamil joining), bidi (Arabic) and
|
|
16
|
+
// wrapping. We size the bitmap to the TEXT width (so chittie's ESC alignment can
|
|
17
|
+
// center the image) and pad width/height to multiples of 8.
|
|
18
|
+
class HybridChittieRasterizer : HybridChittieRasterizerSpec() {
|
|
19
|
+
override fun rasterize(text: String, fontSize: Double, maxWidth: Double, bold: Boolean): RasterBitmap {
|
|
20
|
+
val paint = TextPaint().apply {
|
|
21
|
+
isAntiAlias = true
|
|
22
|
+
color = Color.BLACK
|
|
23
|
+
textSize = fontSize.toFloat()
|
|
24
|
+
typeface = if (bold) Typeface.DEFAULT_BOLD else Typeface.DEFAULT
|
|
25
|
+
}
|
|
26
|
+
val maxW = maxWidth.toInt().coerceAtLeast(8)
|
|
27
|
+
|
|
28
|
+
val layout = StaticLayout.Builder
|
|
29
|
+
.obtain(text, 0, text.length, paint, maxW)
|
|
30
|
+
.setAlignment(Layout.Alignment.ALIGN_NORMAL)
|
|
31
|
+
.setIncludePad(false)
|
|
32
|
+
.build()
|
|
33
|
+
|
|
34
|
+
fun pad8(v: Int) = (v + 7) and 7.inv()
|
|
35
|
+
var used = 0f
|
|
36
|
+
for (i in 0 until layout.lineCount) used = maxOf(used, layout.getLineWidth(i))
|
|
37
|
+
val w = pad8(ceil(used.toDouble()).toInt().coerceIn(8, maxW))
|
|
38
|
+
val h = pad8(layout.height.coerceAtLeast(8))
|
|
39
|
+
|
|
40
|
+
val bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
|
|
41
|
+
Canvas(bmp).apply {
|
|
42
|
+
drawColor(Color.WHITE)
|
|
43
|
+
layout.draw(this)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
val bb = ByteBuffer.allocateDirect(w * h * 4)
|
|
47
|
+
bmp.copyPixelsToBuffer(bb)
|
|
48
|
+
bb.rewind()
|
|
49
|
+
bmp.recycle()
|
|
50
|
+
|
|
51
|
+
return RasterBitmap(data = ArrayBuffer.copy(bb), width = w.toDouble(), height = h.toDouble())
|
|
52
|
+
}
|
|
53
|
+
}
|
package/app.plugin.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// Expo config plugin (pass-through). chittie-react-native is a Nitro native module
|
|
2
|
+
// picked up by autolinking on `expo prebuild` / in a dev client — no extra native
|
|
3
|
+
// config is required. This exists so it can be listed in app.json `plugins` without
|
|
4
|
+
// error if a project prefers to be explicit.
|
|
5
|
+
module.exports = (config) => config;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import NitroModules
|
|
3
|
+
import UIKit
|
|
4
|
+
|
|
5
|
+
// Implements the nitrogen-generated HybridChittieRasterizerSpec.
|
|
6
|
+
// Shapes one line of text with UIKit/CoreText (complex-script shaping + bidi for
|
|
7
|
+
// free) into an RGBA bitmap sized to the TEXT (so chittie's ESC alignment can
|
|
8
|
+
// center the image), with width/height padded to multiples of 8.
|
|
9
|
+
class HybridChittieRasterizer: HybridChittieRasterizerSpec {
|
|
10
|
+
func rasterize(text: String, fontSize: Double, maxWidth: Double, bold: Bool) throws -> RasterBitmap {
|
|
11
|
+
let font = bold
|
|
12
|
+
? UIFont.boldSystemFont(ofSize: CGFloat(fontSize))
|
|
13
|
+
: UIFont.systemFont(ofSize: CGFloat(fontSize))
|
|
14
|
+
let attrs: [NSAttributedString.Key: Any] = [.font: font, .foregroundColor: UIColor.black]
|
|
15
|
+
let attr = NSAttributedString(string: text, attributes: attrs)
|
|
16
|
+
|
|
17
|
+
let maxW = CGFloat(max(8, maxWidth))
|
|
18
|
+
let opts: NSStringDrawingOptions = [.usesLineFragmentOrigin, .usesFontLeading]
|
|
19
|
+
let bounds = attr.boundingRect(
|
|
20
|
+
with: CGSize(width: maxW, height: .greatestFiniteMagnitude), options: opts, context: nil)
|
|
21
|
+
|
|
22
|
+
func pad8(_ v: Int) -> Int { (v + 7) & ~7 }
|
|
23
|
+
let w = max(8, pad8(Int(ceil(bounds.width)) + 2))
|
|
24
|
+
let h = max(8, pad8(Int(ceil(bounds.height)) + 2))
|
|
25
|
+
|
|
26
|
+
let bytesPerRow = w * 4
|
|
27
|
+
var pixels = [UInt8](repeating: 255, count: w * h * 4) // white, opaque
|
|
28
|
+
guard
|
|
29
|
+
let ctx = CGContext(
|
|
30
|
+
data: &pixels, width: w, height: h, bitsPerComponent: 8, bytesPerRow: bytesPerRow,
|
|
31
|
+
space: CGColorSpaceCreateDeviceRGB(),
|
|
32
|
+
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)
|
|
33
|
+
else {
|
|
34
|
+
throw NSError(domain: "chittie", code: 1, userInfo: [NSLocalizedDescriptionKey: "CGContext failed"])
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// UIKit draws top-left origin; flip the CG context to match.
|
|
38
|
+
UIGraphicsPushContext(ctx)
|
|
39
|
+
ctx.translateBy(x: 0, y: CGFloat(h))
|
|
40
|
+
ctx.scaleBy(x: 1, y: -1)
|
|
41
|
+
attr.draw(with: CGRect(x: 1, y: 1, width: CGFloat(w) - 2, height: CGFloat(h) - 2),
|
|
42
|
+
options: opts, context: nil)
|
|
43
|
+
UIGraphicsPopContext()
|
|
44
|
+
|
|
45
|
+
let buffer = ArrayBuffer.copy(data: Data(pixels))
|
|
46
|
+
return RasterBitmap(data: buffer, width: Double(w), height: Double(h))
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.makeRasterizer = makeRasterizer;
|
|
7
|
+
/**
|
|
8
|
+
* Adapt a native `ChittieRasterizer` HybridObject to chittie's `TextRasterizer`.
|
|
9
|
+
* The native side returns an RGBA bitmap (dims already padded to /8); we wrap it
|
|
10
|
+
* as the structural `ImageData` chittie's raster pipeline consumes. Pure and
|
|
11
|
+
* dependency-free at runtime (no Nitro import) — inject `native` so it's testable
|
|
12
|
+
* off-device.
|
|
13
|
+
*/
|
|
14
|
+
function makeRasterizer(native) {
|
|
15
|
+
return {
|
|
16
|
+
rasterize(text, options = {}) {
|
|
17
|
+
const {
|
|
18
|
+
fontSize = 24,
|
|
19
|
+
maxWidth = 576,
|
|
20
|
+
bold = false
|
|
21
|
+
} = options;
|
|
22
|
+
const b = native.rasterize(text, fontSize, maxWidth, !!bold);
|
|
23
|
+
// RGBA, structural ImageData — chittie reads { data, width, height }.
|
|
24
|
+
return {
|
|
25
|
+
data: new Uint8ClampedArray(b.data),
|
|
26
|
+
width: b.width,
|
|
27
|
+
height: b.height,
|
|
28
|
+
colorSpace: 'srgb'
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["makeRasterizer","native","rasterize","text","options","fontSize","maxWidth","bold","b","data","Uint8ClampedArray","width","height","colorSpace"],"sourceRoot":"../../src","sources":["adapter.ts"],"mappings":";;;;;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASA,cAAcA,CAACC,MAAyB,EAAkB;EACxE,OAAO;IACLC,SAASA,CAACC,IAAY,EAAEC,OAAsB,GAAG,CAAC,CAAC,EAAa;MAC9D,MAAM;QAAEC,QAAQ,GAAG,EAAE;QAAEC,QAAQ,GAAG,GAAG;QAAEC,IAAI,GAAG;MAAM,CAAC,GAAGH,OAAO;MAC/D,MAAMI,CAAC,GAAGP,MAAM,CAACC,SAAS,CAACC,IAAI,EAAEE,QAAQ,EAAEC,QAAQ,EAAE,CAAC,CAACC,IAAI,CAAC;MAC5D;MACA,OAAO;QACLE,IAAI,EAAE,IAAIC,iBAAiB,CAACF,CAAC,CAACC,IAAI,CAAC;QACnCE,KAAK,EAAEH,CAAC,CAACG,KAAK;QACdC,MAAM,EAAEJ,CAAC,CAACI,MAAM;QAChBC,UAAU,EAAE;MACd,CAAC;IACH;EACF,CAAC;AACH","ignoreList":[]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.createNativeRasterizer = createNativeRasterizer;
|
|
7
|
+
Object.defineProperty(exports, "makeRasterizer", {
|
|
8
|
+
enumerable: true,
|
|
9
|
+
get: function () {
|
|
10
|
+
return _adapter.makeRasterizer;
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
var _reactNativeNitroModules = require("react-native-nitro-modules");
|
|
14
|
+
var _adapter = require("./adapter.js");
|
|
15
|
+
let cached;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The native chittie rasterizer (CoreText / android.graphics) as a chittie
|
|
19
|
+
* `TextRasterizer` — pass it to `render()` so Sinhala/Tamil/Arabic print on a
|
|
20
|
+
* device without Skia. Requires a dev client / bare RN (native module).
|
|
21
|
+
*
|
|
22
|
+
* ```ts
|
|
23
|
+
* import { createNativeRasterizer } from '@angadie/chittie-react-native';
|
|
24
|
+
* const bytes = render(<Receipt/>, { dotWidth: 384, rasterizer: createNativeRasterizer() });
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
function createNativeRasterizer() {
|
|
28
|
+
if (!cached) {
|
|
29
|
+
const native = _reactNativeNitroModules.NitroModules.createHybridObject('ChittieRasterizer');
|
|
30
|
+
cached = (0, _adapter.makeRasterizer)(native);
|
|
31
|
+
}
|
|
32
|
+
return cached;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_reactNativeNitroModules","require","_adapter","cached","createNativeRasterizer","native","NitroModules","createHybridObject","makeRasterizer"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;;;;;;;;;;;AAAA,IAAAA,wBAAA,GAAAC,OAAA;AAGA,IAAAC,QAAA,GAAAD,OAAA;AAKA,IAAIE,MAAkC;;AAEtC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASC,sBAAsBA,CAAA,EAAmB;EACvD,IAAI,CAACD,MAAM,EAAE;IACX,MAAME,MAAM,GAAGC,qCAAY,CAACC,kBAAkB,CAAoB,mBAAmB,CAAC;IACtFJ,MAAM,GAAG,IAAAK,uBAAc,EAACH,MAAM,CAAC;EACjC;EACA,OAAOF,MAAM;AACf","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"commonjs"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sourceRoot":"../../../src","sources":["specs/ChittieRasterizer.nitro.ts"],"mappings":"","ignoreList":[]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Adapt a native `ChittieRasterizer` HybridObject to chittie's `TextRasterizer`.
|
|
5
|
+
* The native side returns an RGBA bitmap (dims already padded to /8); we wrap it
|
|
6
|
+
* as the structural `ImageData` chittie's raster pipeline consumes. Pure and
|
|
7
|
+
* dependency-free at runtime (no Nitro import) — inject `native` so it's testable
|
|
8
|
+
* off-device.
|
|
9
|
+
*/
|
|
10
|
+
export function makeRasterizer(native) {
|
|
11
|
+
return {
|
|
12
|
+
rasterize(text, options = {}) {
|
|
13
|
+
const {
|
|
14
|
+
fontSize = 24,
|
|
15
|
+
maxWidth = 576,
|
|
16
|
+
bold = false
|
|
17
|
+
} = options;
|
|
18
|
+
const b = native.rasterize(text, fontSize, maxWidth, !!bold);
|
|
19
|
+
// RGBA, structural ImageData — chittie reads { data, width, height }.
|
|
20
|
+
return {
|
|
21
|
+
data: new Uint8ClampedArray(b.data),
|
|
22
|
+
width: b.width,
|
|
23
|
+
height: b.height,
|
|
24
|
+
colorSpace: 'srgb'
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["makeRasterizer","native","rasterize","text","options","fontSize","maxWidth","bold","b","data","Uint8ClampedArray","width","height","colorSpace"],"sourceRoot":"../../src","sources":["adapter.ts"],"mappings":";;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASA,cAAcA,CAACC,MAAyB,EAAkB;EACxE,OAAO;IACLC,SAASA,CAACC,IAAY,EAAEC,OAAsB,GAAG,CAAC,CAAC,EAAa;MAC9D,MAAM;QAAEC,QAAQ,GAAG,EAAE;QAAEC,QAAQ,GAAG,GAAG;QAAEC,IAAI,GAAG;MAAM,CAAC,GAAGH,OAAO;MAC/D,MAAMI,CAAC,GAAGP,MAAM,CAACC,SAAS,CAACC,IAAI,EAAEE,QAAQ,EAAEC,QAAQ,EAAE,CAAC,CAACC,IAAI,CAAC;MAC5D;MACA,OAAO;QACLE,IAAI,EAAE,IAAIC,iBAAiB,CAACF,CAAC,CAACC,IAAI,CAAC;QACnCE,KAAK,EAAEH,CAAC,CAACG,KAAK;QACdC,MAAM,EAAEJ,CAAC,CAACI,MAAM;QAChBC,UAAU,EAAE;MACd,CAAC;IACH;EACF,CAAC;AACH","ignoreList":[]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { NitroModules } from 'react-native-nitro-modules';
|
|
4
|
+
import { makeRasterizer } from './adapter.js';
|
|
5
|
+
export { makeRasterizer } from './adapter.js';
|
|
6
|
+
let cached;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The native chittie rasterizer (CoreText / android.graphics) as a chittie
|
|
10
|
+
* `TextRasterizer` — pass it to `render()` so Sinhala/Tamil/Arabic print on a
|
|
11
|
+
* device without Skia. Requires a dev client / bare RN (native module).
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { createNativeRasterizer } from '@angadie/chittie-react-native';
|
|
15
|
+
* const bytes = render(<Receipt/>, { dotWidth: 384, rasterizer: createNativeRasterizer() });
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export function createNativeRasterizer() {
|
|
19
|
+
if (!cached) {
|
|
20
|
+
const native = NitroModules.createHybridObject('ChittieRasterizer');
|
|
21
|
+
cached = makeRasterizer(native);
|
|
22
|
+
}
|
|
23
|
+
return cached;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["NitroModules","makeRasterizer","cached","createNativeRasterizer","native","createHybridObject"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;AAAA,SAASA,YAAY,QAAQ,4BAA4B;AAGzD,SAASC,cAAc,QAAQ,cAAc;AAE7C,SAASA,cAAc,QAAQ,cAAc;AAG7C,IAAIC,MAAkC;;AAEtC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,sBAAsBA,CAAA,EAAmB;EACvD,IAAI,CAACD,MAAM,EAAE;IACX,MAAME,MAAM,GAAGJ,YAAY,CAACK,kBAAkB,CAAoB,mBAAmB,CAAC;IACtFH,MAAM,GAAGD,cAAc,CAACG,MAAM,CAAC;EACjC;EACA,OAAOF,MAAM;AACf","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sourceRoot":"../../../src","sources":["specs/ChittieRasterizer.nitro.ts"],"mappings":"","ignoreList":[]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { TextRasterizer } from '@angadie/chittie-text';
|
|
2
|
+
import type { ChittieRasterizer } from './specs/ChittieRasterizer.nitro.js';
|
|
3
|
+
/**
|
|
4
|
+
* Adapt a native `ChittieRasterizer` HybridObject to chittie's `TextRasterizer`.
|
|
5
|
+
* The native side returns an RGBA bitmap (dims already padded to /8); we wrap it
|
|
6
|
+
* as the structural `ImageData` chittie's raster pipeline consumes. Pure and
|
|
7
|
+
* dependency-free at runtime (no Nitro import) — inject `native` so it's testable
|
|
8
|
+
* off-device.
|
|
9
|
+
*/
|
|
10
|
+
export declare function makeRasterizer(native: ChittieRasterizer): TextRasterizer;
|
|
11
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC3E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAE5E;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,iBAAiB,GAAG,cAAc,CAcxE"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { TextRasterizer } from '@angadie/chittie-text';
|
|
2
|
+
export { makeRasterizer } from './adapter.js';
|
|
3
|
+
export type { ChittieRasterizer, RasterBitmap } from './specs/ChittieRasterizer.nitro.js';
|
|
4
|
+
/**
|
|
5
|
+
* The native chittie rasterizer (CoreText / android.graphics) as a chittie
|
|
6
|
+
* `TextRasterizer` — pass it to `render()` so Sinhala/Tamil/Arabic print on a
|
|
7
|
+
* device without Skia. Requires a dev client / bare RN (native module).
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { createNativeRasterizer } from '@angadie/chittie-react-native';
|
|
11
|
+
* const bytes = render(<Receipt/>, { dotWidth: 384, rasterizer: createNativeRasterizer() });
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export declare function createNativeRasterizer(): TextRasterizer;
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAI5D,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,YAAY,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAI1F;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,IAAI,cAAc,CAMvD"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { HybridObject } from 'react-native-nitro-modules';
|
|
2
|
+
/**
|
|
3
|
+
* An RGBA bitmap. `width`/`height` are padded to multiples of 8 on the native
|
|
4
|
+
* side so chittie's ESC/POS raster packer (`padTo8`) is a no-op.
|
|
5
|
+
*/
|
|
6
|
+
export interface RasterBitmap {
|
|
7
|
+
data: ArrayBuffer;
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Native text rasterizer — shapes a line with the platform text engine
|
|
13
|
+
* (CoreText on iOS, android.graphics on Android), which handles complex-script
|
|
14
|
+
* shaping (Sinhala/Tamil joining) and bidi (Arabic/Hebrew) for free.
|
|
15
|
+
*/
|
|
16
|
+
export interface ChittieRasterizer extends HybridObject<{
|
|
17
|
+
ios: 'swift';
|
|
18
|
+
android: 'kotlin';
|
|
19
|
+
}> {
|
|
20
|
+
/** Render `text` to an RGBA bitmap (black on white), wrapped at `maxWidth` dots. */
|
|
21
|
+
rasterize(text: string, fontSize: number, maxWidth: number, bold: boolean): RasterBitmap;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=ChittieRasterizer.nitro.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChittieRasterizer.nitro.d.ts","sourceRoot":"","sources":["../../../../src/specs/ChittieRasterizer.nitro.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE/D;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAkB,SAAQ,YAAY,CAAC;IAAE,GAAG,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,QAAQ,CAAA;CAAE,CAAC;IAC1F,oFAAoF;IACpF,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,YAAY,CAAC;CAC1F"}
|