@kookyleo/graphviz-anywhere-rn 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/android/build.gradle +72 -0
- package/android/src/main/CMakeLists.txt +25 -0
- package/android/src/main/java/com/graphviznative/GraphvizModule.java +149 -0
- package/android/src/main/java/com/graphviznative/GraphvizPackage.java +37 -0
- package/android/src/main/jni/graphviz_jni.c +151 -0
- package/ios/GraphvizModule.h +23 -0
- package/ios/GraphvizModule.m +160 -0
- package/ios/GraphvizNative.podspec +33 -0
- package/lib/commonjs/NativeGraphviz.js +15 -0
- package/lib/commonjs/NativeGraphviz.js.map +1 -0
- package/lib/commonjs/index.js +95 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/module/NativeGraphviz.js +13 -0
- package/lib/module/NativeGraphviz.js.map +1 -0
- package/lib/module/index.js +89 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/typescript/NativeGraphviz.d.ts +25 -0
- package/lib/typescript/NativeGraphviz.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +60 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/macos/GraphvizModule.h +23 -0
- package/macos/GraphvizModule.m +163 -0
- package/package.json +95 -0
- package/scripts/download-native.js +98 -0
- package/src/NativeGraphviz.ts +27 -0
- package/src/index.ts +123 -0
- package/windows/GraphvizNative/GraphvizModule.cpp +159 -0
- package/windows/GraphvizNative/GraphvizModule.h +51 -0
- package/windows/GraphvizNative/ReactPackageProvider.cpp +20 -0
- package/windows/GraphvizNative/ReactPackageProvider.h +21 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
def kotlinVersion = rootProject.ext.has("kotlinVersion")
|
|
3
|
+
? rootProject.ext.get("kotlinVersion")
|
|
4
|
+
: "1.9.25"
|
|
5
|
+
|
|
6
|
+
repositories {
|
|
7
|
+
google()
|
|
8
|
+
mavenCentral()
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
dependencies {
|
|
12
|
+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
def safeExtGet(prop, fallback) {
|
|
17
|
+
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
apply plugin: "com.android.library"
|
|
21
|
+
|
|
22
|
+
android {
|
|
23
|
+
namespace "com.graphviznative"
|
|
24
|
+
compileSdkVersion safeExtGet("compileSdkVersion", 35)
|
|
25
|
+
|
|
26
|
+
defaultConfig {
|
|
27
|
+
minSdkVersion safeExtGet("minSdkVersion", 24)
|
|
28
|
+
targetSdkVersion safeExtGet("targetSdkVersion", 35)
|
|
29
|
+
|
|
30
|
+
externalNativeBuild {
|
|
31
|
+
cmake {
|
|
32
|
+
cppFlags ""
|
|
33
|
+
arguments "-DANDROID_STL=c++_shared"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
ndk {
|
|
38
|
+
abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
externalNativeBuild {
|
|
43
|
+
cmake {
|
|
44
|
+
path "src/main/CMakeLists.txt"
|
|
45
|
+
version "3.22.1"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
buildFeatures {
|
|
50
|
+
prefab true
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
sourceSets {
|
|
54
|
+
main {
|
|
55
|
+
java.srcDirs = ["src/main/java"]
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
compileOptions {
|
|
60
|
+
sourceCompatibility JavaVersion.VERSION_11
|
|
61
|
+
targetCompatibility JavaVersion.VERSION_11
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
repositories {
|
|
66
|
+
google()
|
|
67
|
+
mavenCentral()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
dependencies {
|
|
71
|
+
implementation "com.facebook.react:react-android:+"
|
|
72
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.22.1)
|
|
2
|
+
project(graphviz_jni)
|
|
3
|
+
|
|
4
|
+
# Build the graphviz_api C library from source
|
|
5
|
+
set(GRAPHVIZ_API_SRC "${CMAKE_CURRENT_SOURCE_DIR}/../../../../src")
|
|
6
|
+
|
|
7
|
+
add_library(graphviz_api SHARED
|
|
8
|
+
"${GRAPHVIZ_API_SRC}/graphviz_api.c"
|
|
9
|
+
jni/graphviz_jni.c
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
target_include_directories(graphviz_api PRIVATE
|
|
13
|
+
"${GRAPHVIZ_API_SRC}"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
# Link against the prebuilt Graphviz libraries for Android
|
|
17
|
+
set(GRAPHVIZ_PREBUILT "${CMAKE_CURRENT_SOURCE_DIR}/../../../../graphviz")
|
|
18
|
+
|
|
19
|
+
target_include_directories(graphviz_api PRIVATE
|
|
20
|
+
"${GRAPHVIZ_PREBUILT}/include"
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
# Link Android log library for JNI
|
|
24
|
+
find_library(log-lib log)
|
|
25
|
+
target_link_libraries(graphviz_api ${log-lib})
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* GraphvizModule.java
|
|
3
|
+
*
|
|
4
|
+
* React Native native module for Android.
|
|
5
|
+
* Manages a singleton Graphviz context and dispatches rendering
|
|
6
|
+
* to a background thread to keep the JS thread responsive.
|
|
7
|
+
*
|
|
8
|
+
* Licensed under the Apache License, Version 2.0
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
package com.graphviznative;
|
|
12
|
+
|
|
13
|
+
import android.util.Base64;
|
|
14
|
+
import android.util.Log;
|
|
15
|
+
|
|
16
|
+
import androidx.annotation.NonNull;
|
|
17
|
+
import androidx.annotation.Nullable;
|
|
18
|
+
|
|
19
|
+
import com.facebook.react.bridge.Promise;
|
|
20
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
21
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|
22
|
+
import com.facebook.react.bridge.ReactMethod;
|
|
23
|
+
|
|
24
|
+
import java.util.Arrays;
|
|
25
|
+
import java.util.HashSet;
|
|
26
|
+
import java.util.Set;
|
|
27
|
+
import java.util.concurrent.ExecutorService;
|
|
28
|
+
import java.util.concurrent.Executors;
|
|
29
|
+
|
|
30
|
+
public class GraphvizModule extends ReactContextBaseJavaModule {
|
|
31
|
+
|
|
32
|
+
private static final String TAG = "GraphvizNative";
|
|
33
|
+
private static final String MODULE_NAME = "GraphvizNative";
|
|
34
|
+
|
|
35
|
+
private static final Set<String> TEXT_FORMATS = new HashSet<>(
|
|
36
|
+
Arrays.asList("svg", "json", "dot", "xdot", "plain")
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
private final ExecutorService executor = Executors.newSingleThreadExecutor();
|
|
40
|
+
private long contextPtr = 0;
|
|
41
|
+
|
|
42
|
+
static {
|
|
43
|
+
System.loadLibrary("graphviz_api");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public GraphvizModule(ReactApplicationContext reactContext) {
|
|
47
|
+
super(reactContext);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@Override
|
|
51
|
+
@NonNull
|
|
52
|
+
public String getName() {
|
|
53
|
+
return MODULE_NAME;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@Override
|
|
57
|
+
public void invalidate() {
|
|
58
|
+
if (contextPtr != 0) {
|
|
59
|
+
nativeContextFree(contextPtr);
|
|
60
|
+
contextPtr = 0;
|
|
61
|
+
}
|
|
62
|
+
executor.shutdown();
|
|
63
|
+
super.invalidate();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Lazily create the native context. Must be called from the executor thread.
|
|
68
|
+
*/
|
|
69
|
+
private synchronized long ensureContext() {
|
|
70
|
+
if (contextPtr == 0) {
|
|
71
|
+
contextPtr = nativeContextNew();
|
|
72
|
+
if (contextPtr == 0) {
|
|
73
|
+
Log.e(TAG, "Failed to create Graphviz context");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return contextPtr;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@ReactMethod
|
|
80
|
+
public void renderDot(String dot, String engine, String format, Promise promise) {
|
|
81
|
+
executor.execute(() -> {
|
|
82
|
+
try {
|
|
83
|
+
long ctx = ensureContext();
|
|
84
|
+
if (ctx == 0) {
|
|
85
|
+
promise.reject("NOT_INITIALIZED", "Failed to initialize Graphviz context");
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
String[] result = new String[1];
|
|
90
|
+
int err = nativeRender(ctx, dot, engine, format, result);
|
|
91
|
+
|
|
92
|
+
if (err != 0) {
|
|
93
|
+
String code = errorCodeToString(err);
|
|
94
|
+
String message = nativeStrerror(err);
|
|
95
|
+
promise.reject(code, message);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (result[0] == null) {
|
|
100
|
+
promise.reject("RENDER_FAILED", "Render returned null output");
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// For text formats, return as-is; for binary formats, already base64 from JNI
|
|
105
|
+
promise.resolve(result[0]);
|
|
106
|
+
|
|
107
|
+
} catch (Exception e) {
|
|
108
|
+
Log.e(TAG, "Render failed with exception", e);
|
|
109
|
+
promise.reject("UNKNOWN", "Unexpected error: " + e.getMessage());
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
@ReactMethod
|
|
115
|
+
public void getVersion(Promise promise) {
|
|
116
|
+
try {
|
|
117
|
+
String version = nativeVersion();
|
|
118
|
+
promise.resolve(version);
|
|
119
|
+
} catch (Exception e) {
|
|
120
|
+
promise.reject("UNKNOWN", "Failed to get version: " + e.getMessage());
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private static String errorCodeToString(int err) {
|
|
125
|
+
switch (err) {
|
|
126
|
+
case -1: return "NULL_INPUT";
|
|
127
|
+
case -2: return "INVALID_DOT";
|
|
128
|
+
case -3: return "LAYOUT_FAILED";
|
|
129
|
+
case -4: return "RENDER_FAILED";
|
|
130
|
+
case -5: return "INVALID_ENGINE";
|
|
131
|
+
case -6: return "INVALID_FORMAT";
|
|
132
|
+
case -7: return "OUT_OF_MEMORY";
|
|
133
|
+
case -8: return "NOT_INITIALIZED";
|
|
134
|
+
default: return "UNKNOWN";
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/*
|
|
139
|
+
* JNI native methods.
|
|
140
|
+
* These are implemented in the CMake-built native library that
|
|
141
|
+
* wraps graphviz_api.h via JNI.
|
|
142
|
+
*/
|
|
143
|
+
private static native long nativeContextNew();
|
|
144
|
+
private static native void nativeContextFree(long ctx);
|
|
145
|
+
private static native int nativeRender(long ctx, String dot, String engine,
|
|
146
|
+
String format, String[] outResult);
|
|
147
|
+
private static native String nativeStrerror(int err);
|
|
148
|
+
private static native String nativeVersion();
|
|
149
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* GraphvizPackage.java
|
|
3
|
+
*
|
|
4
|
+
* React Native package registration for the Graphviz native module.
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
package com.graphviznative;
|
|
10
|
+
|
|
11
|
+
import androidx.annotation.NonNull;
|
|
12
|
+
|
|
13
|
+
import com.facebook.react.ReactPackage;
|
|
14
|
+
import com.facebook.react.bridge.NativeModule;
|
|
15
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
16
|
+
import com.facebook.react.uimanager.ViewManager;
|
|
17
|
+
|
|
18
|
+
import java.util.ArrayList;
|
|
19
|
+
import java.util.Collections;
|
|
20
|
+
import java.util.List;
|
|
21
|
+
|
|
22
|
+
public class GraphvizPackage implements ReactPackage {
|
|
23
|
+
|
|
24
|
+
@Override
|
|
25
|
+
@NonNull
|
|
26
|
+
public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
|
|
27
|
+
List<NativeModule> modules = new ArrayList<>();
|
|
28
|
+
modules.add(new GraphvizModule(reactContext));
|
|
29
|
+
return modules;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@Override
|
|
33
|
+
@NonNull
|
|
34
|
+
public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
|
|
35
|
+
return Collections.emptyList();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* graphviz_jni.c
|
|
3
|
+
*
|
|
4
|
+
* JNI bridge between the Java GraphvizModule and the C graphviz_api.
|
|
5
|
+
* Handles string conversions and memory management across the JNI boundary.
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the Apache License, Version 2.0
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#include <jni.h>
|
|
11
|
+
#include <string.h>
|
|
12
|
+
#include <stdlib.h>
|
|
13
|
+
#include <android/log.h>
|
|
14
|
+
|
|
15
|
+
#include "graphviz_api.h"
|
|
16
|
+
|
|
17
|
+
#define TAG "GraphvizJNI"
|
|
18
|
+
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
|
|
19
|
+
|
|
20
|
+
/* Base64 encoding table */
|
|
21
|
+
static const char b64_table[] =
|
|
22
|
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Simple base64 encoder for binary output (png, pdf, ps).
|
|
26
|
+
* Caller must free the returned string.
|
|
27
|
+
*/
|
|
28
|
+
static char *base64_encode(const char *data, size_t len, size_t *out_len) {
|
|
29
|
+
size_t olen = 4 * ((len + 2) / 3);
|
|
30
|
+
char *out = (char *)malloc(olen + 1);
|
|
31
|
+
if (!out) return NULL;
|
|
32
|
+
|
|
33
|
+
size_t i, j;
|
|
34
|
+
for (i = 0, j = 0; i < len;) {
|
|
35
|
+
uint32_t a = i < len ? (unsigned char)data[i++] : 0;
|
|
36
|
+
uint32_t b = i < len ? (unsigned char)data[i++] : 0;
|
|
37
|
+
uint32_t c = i < len ? (unsigned char)data[i++] : 0;
|
|
38
|
+
uint32_t triple = (a << 16) | (b << 8) | c;
|
|
39
|
+
|
|
40
|
+
out[j++] = b64_table[(triple >> 18) & 0x3F];
|
|
41
|
+
out[j++] = b64_table[(triple >> 12) & 0x3F];
|
|
42
|
+
out[j++] = b64_table[(triple >> 6) & 0x3F];
|
|
43
|
+
out[j++] = b64_table[triple & 0x3F];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Padding */
|
|
47
|
+
size_t mod = len % 3;
|
|
48
|
+
if (mod == 1) {
|
|
49
|
+
out[olen - 1] = '=';
|
|
50
|
+
out[olen - 2] = '=';
|
|
51
|
+
} else if (mod == 2) {
|
|
52
|
+
out[olen - 1] = '=';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
out[olen] = '\0';
|
|
56
|
+
if (out_len) *out_len = olen;
|
|
57
|
+
return out;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Returns non-zero if the format produces text (not binary) output.
|
|
62
|
+
*/
|
|
63
|
+
static int is_text_format(const char *format) {
|
|
64
|
+
return (strcmp(format, "svg") == 0 ||
|
|
65
|
+
strcmp(format, "json") == 0 ||
|
|
66
|
+
strcmp(format, "dot") == 0 ||
|
|
67
|
+
strcmp(format, "xdot") == 0 ||
|
|
68
|
+
strcmp(format, "plain") == 0);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
JNIEXPORT jlong JNICALL
|
|
72
|
+
Java_com_graphviznative_GraphvizModule_nativeContextNew(JNIEnv *env, jclass clazz) {
|
|
73
|
+
gv_context_t *ctx = gv_context_new();
|
|
74
|
+
return (jlong)(intptr_t)ctx;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
JNIEXPORT void JNICALL
|
|
78
|
+
Java_com_graphviznative_GraphvizModule_nativeContextFree(JNIEnv *env, jclass clazz, jlong ptr) {
|
|
79
|
+
gv_context_t *ctx = (gv_context_t *)(intptr_t)ptr;
|
|
80
|
+
if (ctx) {
|
|
81
|
+
gv_context_free(ctx);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
JNIEXPORT jint JNICALL
|
|
86
|
+
Java_com_graphviznative_GraphvizModule_nativeRender(
|
|
87
|
+
JNIEnv *env, jclass clazz,
|
|
88
|
+
jlong ptr, jstring jDot, jstring jEngine, jstring jFormat,
|
|
89
|
+
jobjectArray outResult
|
|
90
|
+
) {
|
|
91
|
+
gv_context_t *ctx = (gv_context_t *)(intptr_t)ptr;
|
|
92
|
+
|
|
93
|
+
const char *dot = (*env)->GetStringUTFChars(env, jDot, NULL);
|
|
94
|
+
const char *engine = (*env)->GetStringUTFChars(env, jEngine, NULL);
|
|
95
|
+
const char *format = (*env)->GetStringUTFChars(env, jFormat, NULL);
|
|
96
|
+
|
|
97
|
+
if (!dot || !engine || !format) {
|
|
98
|
+
if (dot) (*env)->ReleaseStringUTFChars(env, jDot, dot);
|
|
99
|
+
if (engine) (*env)->ReleaseStringUTFChars(env, jEngine, engine);
|
|
100
|
+
if (format) (*env)->ReleaseStringUTFChars(env, jFormat, format);
|
|
101
|
+
return (jint)GV_ERR_NULL_INPUT;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
char *outData = NULL;
|
|
105
|
+
size_t outLength = 0;
|
|
106
|
+
|
|
107
|
+
gv_error_t err = gv_render(ctx, dot, engine, format, &outData, &outLength);
|
|
108
|
+
|
|
109
|
+
if (err == GV_OK && outData) {
|
|
110
|
+
jstring result;
|
|
111
|
+
if (is_text_format(format)) {
|
|
112
|
+
result = (*env)->NewStringUTF(env, outData);
|
|
113
|
+
} else {
|
|
114
|
+
size_t b64Len = 0;
|
|
115
|
+
char *b64 = base64_encode(outData, outLength, &b64Len);
|
|
116
|
+
if (b64) {
|
|
117
|
+
result = (*env)->NewStringUTF(env, b64);
|
|
118
|
+
free(b64);
|
|
119
|
+
} else {
|
|
120
|
+
result = NULL;
|
|
121
|
+
err = GV_ERR_OUT_OF_MEMORY;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (result) {
|
|
126
|
+
(*env)->SetObjectArrayElement(env, outResult, 0, result);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (outData) {
|
|
131
|
+
gv_free_render_data(outData);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
(*env)->ReleaseStringUTFChars(env, jDot, dot);
|
|
135
|
+
(*env)->ReleaseStringUTFChars(env, jEngine, engine);
|
|
136
|
+
(*env)->ReleaseStringUTFChars(env, jFormat, format);
|
|
137
|
+
|
|
138
|
+
return (jint)err;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
JNIEXPORT jstring JNICALL
|
|
142
|
+
Java_com_graphviznative_GraphvizModule_nativeStrerror(JNIEnv *env, jclass clazz, jint err) {
|
|
143
|
+
const char *msg = gv_strerror((gv_error_t)err);
|
|
144
|
+
return (*env)->NewStringUTF(env, msg ? msg : "Unknown error");
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
JNIEXPORT jstring JNICALL
|
|
148
|
+
Java_com_graphviznative_GraphvizModule_nativeVersion(JNIEnv *env, jclass clazz) {
|
|
149
|
+
const char *ver = gv_version();
|
|
150
|
+
return (*env)->NewStringUTF(env, ver ? ver : "unknown");
|
|
151
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* GraphvizModule.h
|
|
3
|
+
*
|
|
4
|
+
* React Native native module for Graphviz rendering.
|
|
5
|
+
* Supports both old architecture (RCTBridgeModule) and
|
|
6
|
+
* new architecture (TurboModules) via RCT_NEW_ARCH_ENABLED.
|
|
7
|
+
*
|
|
8
|
+
* Licensed under the Apache License, Version 2.0
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
#import <React/RCTBridgeModule.h>
|
|
12
|
+
|
|
13
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
14
|
+
#import <GraphvizNativeSpec/GraphvizNativeSpec.h>
|
|
15
|
+
#endif
|
|
16
|
+
|
|
17
|
+
@interface GraphvizModule : NSObject <RCTBridgeModule
|
|
18
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
19
|
+
, NativeGraphvizNativeSpec
|
|
20
|
+
#endif
|
|
21
|
+
>
|
|
22
|
+
|
|
23
|
+
@end
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* GraphvizModule.m
|
|
3
|
+
*
|
|
4
|
+
* React Native native module implementation for iOS / macOS.
|
|
5
|
+
* Manages a singleton Graphviz context and dispatches rendering
|
|
6
|
+
* to a background queue to keep the JS thread responsive.
|
|
7
|
+
*
|
|
8
|
+
* Licensed under the Apache License, Version 2.0
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
#import "GraphvizModule.h"
|
|
12
|
+
#import "graphviz_api.h"
|
|
13
|
+
|
|
14
|
+
#import <React/RCTLog.h>
|
|
15
|
+
|
|
16
|
+
@implementation GraphvizModule {
|
|
17
|
+
gv_context_t *_context;
|
|
18
|
+
dispatch_queue_t _renderQueue;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
RCT_EXPORT_MODULE(GraphvizNative)
|
|
22
|
+
|
|
23
|
+
- (instancetype)init {
|
|
24
|
+
self = [super init];
|
|
25
|
+
if (self) {
|
|
26
|
+
_renderQueue = dispatch_queue_create("com.graphviznative.render", DISPATCH_QUEUE_SERIAL);
|
|
27
|
+
_context = NULL;
|
|
28
|
+
}
|
|
29
|
+
return self;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
- (void)dealloc {
|
|
33
|
+
if (_context) {
|
|
34
|
+
gv_context_free(_context);
|
|
35
|
+
_context = NULL;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
+ (BOOL)requiresMainQueueSetup {
|
|
40
|
+
return NO;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Lazily initializes the Graphviz context on the render queue.
|
|
45
|
+
* Must be called from within a dispatch to _renderQueue.
|
|
46
|
+
*/
|
|
47
|
+
- (gv_context_t *)ensureContext {
|
|
48
|
+
if (!_context) {
|
|
49
|
+
_context = gv_context_new();
|
|
50
|
+
if (!_context) {
|
|
51
|
+
RCTLogError(@"GraphvizNative: failed to create Graphviz context");
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return _context;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Map native error codes to JS-friendly error code strings.
|
|
59
|
+
*/
|
|
60
|
+
static NSString *errorCodeToString(gv_error_t err) {
|
|
61
|
+
switch (err) {
|
|
62
|
+
case GV_ERR_NULL_INPUT: return @"NULL_INPUT";
|
|
63
|
+
case GV_ERR_INVALID_DOT: return @"INVALID_DOT";
|
|
64
|
+
case GV_ERR_LAYOUT_FAILED: return @"LAYOUT_FAILED";
|
|
65
|
+
case GV_ERR_RENDER_FAILED: return @"RENDER_FAILED";
|
|
66
|
+
case GV_ERR_INVALID_ENGINE: return @"INVALID_ENGINE";
|
|
67
|
+
case GV_ERR_INVALID_FORMAT: return @"INVALID_FORMAT";
|
|
68
|
+
case GV_ERR_OUT_OF_MEMORY: return @"OUT_OF_MEMORY";
|
|
69
|
+
case GV_ERR_NOT_INITIALIZED: return @"NOT_INITIALIZED";
|
|
70
|
+
default: return @"UNKNOWN";
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Returns YES for text-based output formats that do not need base64 encoding.
|
|
76
|
+
*/
|
|
77
|
+
static BOOL isTextFormat(NSString *format) {
|
|
78
|
+
static NSSet *textFormats;
|
|
79
|
+
static dispatch_once_t onceToken;
|
|
80
|
+
dispatch_once(&onceToken, ^{
|
|
81
|
+
textFormats = [NSSet setWithArray:@[@"svg", @"json", @"dot", @"xdot", @"plain"]];
|
|
82
|
+
});
|
|
83
|
+
return [textFormats containsObject:format];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
RCT_EXPORT_METHOD(renderDot:(NSString *)dot
|
|
87
|
+
engine:(NSString *)engine
|
|
88
|
+
format:(NSString *)format
|
|
89
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
90
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
91
|
+
dispatch_async(_renderQueue, ^{
|
|
92
|
+
gv_context_t *ctx = [self ensureContext];
|
|
93
|
+
if (!ctx) {
|
|
94
|
+
reject(@"NOT_INITIALIZED", @"Failed to initialize Graphviz context", nil);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
char *outData = NULL;
|
|
99
|
+
size_t outLength = 0;
|
|
100
|
+
|
|
101
|
+
gv_error_t err = gv_render(
|
|
102
|
+
ctx,
|
|
103
|
+
[dot UTF8String],
|
|
104
|
+
[engine UTF8String],
|
|
105
|
+
[format UTF8String],
|
|
106
|
+
&outData,
|
|
107
|
+
&outLength
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
if (err != GV_OK) {
|
|
111
|
+
NSString *code = errorCodeToString(err);
|
|
112
|
+
NSString *message = [NSString stringWithUTF8String:gv_strerror(err)];
|
|
113
|
+
reject(code, message, nil);
|
|
114
|
+
if (outData) {
|
|
115
|
+
gv_free_render_data(outData);
|
|
116
|
+
}
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
NSString *result;
|
|
121
|
+
if (isTextFormat(format)) {
|
|
122
|
+
result = [[NSString alloc] initWithBytes:outData
|
|
123
|
+
length:outLength
|
|
124
|
+
encoding:NSUTF8StringEncoding];
|
|
125
|
+
} else {
|
|
126
|
+
NSData *data = [NSData dataWithBytesNoCopy:outData
|
|
127
|
+
length:outLength
|
|
128
|
+
freeWhenDone:NO];
|
|
129
|
+
result = [data base64EncodedStringWithOptions:0];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
gv_free_render_data(outData);
|
|
133
|
+
|
|
134
|
+
if (!result) {
|
|
135
|
+
reject(@"RENDER_FAILED", @"Failed to convert render output to string", nil);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
resolve(result);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
RCT_EXPORT_METHOD(getVersion:(RCTPromiseResolveBlock)resolve
|
|
144
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
145
|
+
const char *version = gv_version();
|
|
146
|
+
if (version) {
|
|
147
|
+
resolve([NSString stringWithUTF8String:version]);
|
|
148
|
+
} else {
|
|
149
|
+
reject(@"UNKNOWN", @"Failed to get Graphviz version", nil);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
154
|
+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
155
|
+
(const facebook::react::ObjCTurboModule::InitParams &)params {
|
|
156
|
+
return std::make_shared<facebook::react::NativeGraphvizNativeSpecJSI>(params);
|
|
157
|
+
}
|
|
158
|
+
#endif
|
|
159
|
+
|
|
160
|
+
@end
|
|
@@ -0,0 +1,33 @@
|
|
|
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 = "GraphvizNative"
|
|
7
|
+
s.version = package["version"]
|
|
8
|
+
s.summary = package["description"]
|
|
9
|
+
s.homepage = package["repository"]["url"]
|
|
10
|
+
s.license = package["license"]
|
|
11
|
+
s.authors = package["author"]
|
|
12
|
+
s.source = { :git => package["repository"]["url"], :tag => s.version }
|
|
13
|
+
|
|
14
|
+
s.ios.deployment_target = "15.1"
|
|
15
|
+
s.osx.deployment_target = "11.0"
|
|
16
|
+
|
|
17
|
+
s.source_files = "*.{h,m,mm}", "../src/*.{h,c}"
|
|
18
|
+
s.public_header_files = "GraphvizModule.h"
|
|
19
|
+
|
|
20
|
+
# Link against the prebuilt Graphviz static/shared libraries
|
|
21
|
+
s.preserve_paths = "../third_party/graphviz/**"
|
|
22
|
+
s.xcconfig = {
|
|
23
|
+
"HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/../src\"",
|
|
24
|
+
"LIBRARY_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/../third_party/graphviz/lib\"",
|
|
25
|
+
}
|
|
26
|
+
s.libraries = "graphviz_api"
|
|
27
|
+
|
|
28
|
+
if respond_to?(:install_modules_dependencies, true)
|
|
29
|
+
install_modules_dependencies(s)
|
|
30
|
+
else
|
|
31
|
+
s.dependency "React-Core"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _reactNative = require("react-native");
|
|
8
|
+
/**
|
|
9
|
+
* TurboModule spec for the Graphviz native module (New Architecture).
|
|
10
|
+
*
|
|
11
|
+
* This interface defines the contract between JS and native code.
|
|
12
|
+
* For the old architecture, we fall back to NativeModules.
|
|
13
|
+
*/
|
|
14
|
+
var _default = exports.default = _reactNative.TurboModuleRegistry.getEnforcing('GraphvizNative');
|
|
15
|
+
//# sourceMappingURL=NativeGraphviz.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_reactNative","require","_default","exports","default","TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeGraphviz.ts"],"mappings":";;;;;;AACA,IAAAA,YAAA,GAAAC,OAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AALA,IAAAC,QAAA,GAAAC,OAAA,CAAAC,OAAA,GAuBeC,gCAAmB,CAACC,YAAY,CAAO,gBAAgB,CAAC","ignoreList":[]}
|