Skip to content
Merged

D2M #13

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/architecture/11-element-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,15 @@ CanvasWorkspace 遍历 elements
每个物料包可提供 Viewer 渲染扩展:

```typescript
interface ViewerRenderContext {
readonly document: Document
data: Record<string, unknown>
resolvedProps: Record<string, unknown>
pageIndex: number
unit: string
zoom: number
}

interface MaterialViewerExtension {
render(node: MaterialNode, context: ViewerRenderContext): ViewerRenderOutput
measure?(node: MaterialNode, context: ViewerMeasureContext): ViewerMeasureResult
Expand All @@ -666,6 +675,7 @@ interface MaterialViewerExtension {
其中职责边界如下:

- `render()` 只负责生成物料内容 DOM/SVG。
- `context.document` 是当前 Viewer host 的文档;物料返回 DOM/SVG 节点时必须使用它创建节点,不能依赖全局 `document`,以兼容 iframe 和 custom host。
- `measure()` 负责参与运行时测量和回流,典型场景是 table-data 这类动态高度物料。
- `getRenderSize()` 负责声明最终渲染容器尺寸,适用于“运行态视觉尺寸不等于 schema 几何尺寸”的物料;例如线条物料可把 legacy `lineWidth` 或最小可见厚度提升为实际渲染高度。
- `viewer` 的 `RenderSurface` 不允许根据 `node.type` 推断这些差异,必须统一通过 `MaterialRendererRegistry -> MaterialViewerExtension` 获取。
Expand Down
1 change: 0 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,3 @@ Strong success criteria let you loop independently. Weak criteria ("make it work
- Each package room needs to import packages through a pnpm workspace
- Ensure that the projects `pnpm build`, `pnpm lint`, and `pnpm typecheck` pass (in order)
- Centralize dependencies workspace catalog
- The project is still evolving and should publish the current best design only. Do not add compatibility or migration wording, paths, shims, aliases, or documentation unless the user explicitly asks for them.
2 changes: 1 addition & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export default antfu(
type: 'lib',
vue: true,
pnpm: false,
ignores: ['.github/**/*.md', 'docs/**/*.md', '**/bin/**', '**/obj/**', 'lib/EasyInk.Render/host/internal/easyink/runtime/easyink-viewer/assets/**'],
ignores: ['.github/**/*.md', 'docs/**/*.md', '**/bin/**', '**/obj/**', '**/.dart_tool/**', 'lib/**'],
rules: {
'ts/explicit-function-return-type': 'off',
},
Expand Down
4 changes: 3 additions & 1 deletion internal-packages/viewer-runtime/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"private": true,
"description": "Internal EasyInk.Render HTML runtime bundle built from @easyink/viewer",
"sideEffects": [
"./src/polyfills.ts",
"./src/style.css"
],
"scripts": {
Expand All @@ -13,7 +14,8 @@
},
"dependencies": {
"@easyink/builtin": "workspace:*",
"@easyink/viewer": "workspace:*"
"@easyink/viewer": "workspace:*",
"core-js": "catalog:framework"
},
"devDependencies": {
"@types/node": "catalog:types",
Expand Down
1 change: 1 addition & 0 deletions internal-packages/viewer-runtime/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { DocumentSchema, DocumentSchemaInput, ViewerDiagnosticEvent } from '@easyink/viewer'
import { registerBuiltinViewerMaterials } from '@easyink/builtin/all'
import { createViewer, normalizeDocumentSchema } from '@easyink/viewer'
import './polyfills'
import './style.css'

interface RuntimePayload {
Expand Down
21 changes: 21 additions & 0 deletions internal-packages/viewer-runtime/src/polyfills.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import 'core-js/stable'

if (!Element.prototype.replaceChildren) {
Object.defineProperty(Element.prototype, 'replaceChildren', {
configurable: true,
writable: true,
value(this: Element, ...nodes: Array<Node | string>): void {
const fragment = this.ownerDocument.createDocumentFragment()
for (const node of nodes) {
fragment.appendChild(
typeof node === 'string'
? this.ownerDocument.createTextNode(node)
: node,
)
}
while (this.firstChild)
this.removeChild(this.firstChild)
this.appendChild(fragment)
},
})
}
2 changes: 1 addition & 1 deletion internal-packages/viewer-runtime/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default defineConfig({
build: {
outDir: 'dist/runtime/easyink-viewer/assets',
emptyOutDir: true,
target: 'es2022',
target: 'chrome83',
minify: false,
cssCodeSplit: false,
lib: {
Expand Down
15 changes: 15 additions & 0 deletions lib/EasyInk.Android/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ EasyInk Android 是原生 AAR 渲染 SDK。它把 `html` 或 `easyink schema + d
| 技术架构、边界、渲染流程、安全模型 | [ARCHITECTURE.md](ARCHITECTURE.md) |
| 分阶段计划、完成度、剩余工作 | [IMPLEMENTATION_PLAN.md](IMPLEMENTATION_PLAN.md) |
| 最小 Android 示例应用 | [samples/minimal](samples/minimal) |
| Flutter 插件化示例应用 | [samples/flutter](samples/flutter) |

SDK 边界:本包只负责渲染 PDF 或图片文件,不负责物理打印、系统打印 UI、蓝牙/USB 打印机协议或厂商打印 SDK。

Expand All @@ -29,6 +30,7 @@ SDK 边界:本包只负责渲染 PDF 或图片文件,不负责物理打印
- PDF 输出使用 `renderedPages` 作为页面尺寸来源。
- 图片输出使用 `.ei-viewer-page` 页面 rect 和 `renderedPages` 控制单页截图尺寸。
- `samples/minimal` 提供最小 Android app,可在 Android Studio 中运行 PDF/图片输出验证。
- `samples/flutter` 提供独立 Flutter plugin 与最小宿主,可分别验证 HTML 和 Schema 图片输出。
- `maven-publish` 和 release publication 已配置,发布仓库地址、凭据和签名流程仍需按目标仓库补齐。

待补齐:
Expand Down Expand Up @@ -57,9 +59,22 @@ pnpm android:render-sdk:build

```text
lib/EasyInk.Android/build/outputs/aar/easyink-android-render-release.aar
lib/EasyInk.Android/build/repository/com/easyink/easyink-android-render/0.1.0/
lib/EasyInk.Android/samples/minimal/build/outputs/apk/debug/sample-minimal-debug.apk
```

Flutter 宿主示例:

```bash
cd lib/EasyInk.Android/samples/flutter
flutter pub get
flutter run
```

Flutter sample 通过仓库内生成的 Maven publication 引用 Android SDK,并要求
Flutter 3.38 或更高版本以匹配当前 AAR 的 Kotlin 2.2 metadata。运行时先点击
`Render HTML images`,再点击 `Render Schema images`。

注意:

- Gradle 下载和缓存放在 `lib/EasyInk.Android/.gradle/` 下,不在仓库根目录生成 `.gradle/`。
Expand Down
6 changes: 6 additions & 0 deletions lib/EasyInk.Android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ tasks.register("verifyAarRuntimeAssets") {

afterEvaluate {
publishing {
repositories {
maven {
name = "flutterSample"
url = layout.buildDirectory.dir("repository").get().asFile.toURI()
}
}
publications {
create<MavenPublication>("release") {
from(components["release"])
Expand Down
44 changes: 44 additions & 0 deletions lib/EasyInk.Android/samples/flutter/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/

# IntelliJ related
*.iml
*.ipr
*.iws
.idea/

# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/

# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/

# Symbolication related
app.*.symbols

# Obfuscation related
app.*.map.json

# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
30 changes: 30 additions & 0 deletions lib/EasyInk.Android/samples/flutter/.metadata
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled.

version:
revision: "8b872868494e429d94fa06dca855c306438b22c0"
channel: "stable"

project_type: app

# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 8b872868494e429d94fa06dca855c306438b22c0
base_revision: 8b872868494e429d94fa06dca855c306438b22c0
- platform: android
create_revision: 8b872868494e429d94fa06dca855c306438b22c0
base_revision: 8b872868494e429d94fa06dca855c306438b22c0

# User provided section

# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'
67 changes: 67 additions & 0 deletions lib/EasyInk.Android/samples/flutter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# EasyInk Flutter Sample

This sample exercises the Android SDK through the local `easyink_android`
Flutter plugin.
It renders both a minimal HTML document and the bundled supermarket Schema/Data
fixture to PNG files.

The project uses Flutter 3.38 or newer because the current AAR is compiled with
Kotlin 2.2 metadata. Older Flutter Android templates need to be upgraded before
they can compile this sample's Kotlin bridge.

## Run

Build the AAR and repository-local Maven publication first from the repository
root:

```bash
pnpm android:render-sdk:build
```

Then run the Flutter app on an Android device or emulator:

```bash
cd lib/EasyInk.Android/samples/flutter
flutter pub get
flutter run
```

The plugin resolves `com.easyink:easyink-android-render:0.1.0` from the local
Maven repository generated at `lib/EasyInk.Android/build/repository`.
The sample Android root declares that repository in `android/build.gradle.kts`;
production hosts should replace it with the configured remote Maven repository.
The Flutter app manifest enables Internet access and the SDK loopback network
security resource. The sample uses a 120-second readiness timeout for diagnosis.

Use `Render HTML images` first. It tests the local HTML loading path without the
viewer Schema runtime. `Render Schema images` then tests Flutter asset loading,
MethodChannel string transport, and the embedded viewer runtime.

Successful images are published to the public `Download/EasyInk` directory. On
Android 10 and newer the sample uses MediaStore; Android 9 and older request the
legacy storage permission before rendering.

For focused logs:

```bash
adb logcat -s EasyInkAndroidPlugin chromium cr_AwContents
```

The Android side logs only request sizes and the WebView package version; it does
not print the full Schema or Data payload.

## Older System WebView

The Schema path executes the viewer JavaScript embedded in the Android AAR. The
runtime avoids logical assignment syntax such as `||=` so it can be parsed by
older System WebView versions. If readiness still times out, inspect the WebView
console before increasing the timeout: a parse error prevents
`window.easyinkReady` from ever being set.

## Structure

- `lib/` contains only sample UI and calls the typed plugin API.
- `plugins/easyink_android/lib/` owns the public Dart API.
- `plugins/easyink_android/android/` owns MethodChannel, Activity permissions,
EasyInk SDK calls, task cleanup, and Downloads publishing.
- `MainActivity` is intentionally empty so plugin auto-registration is covered.
27 changes: 27 additions & 0 deletions lib/EasyInk.Android/samples/flutter/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.

# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml

linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at
# https://dart-lang.github.io/linter/lints/index.html.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules: {}

# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
13 changes: 13 additions & 0 deletions lib/EasyInk.Android/samples/flutter/android/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java

# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
key.properties
**/*.keystore
**/*.jks
38 changes: 38 additions & 0 deletions lib/EasyInk.Android/samples/flutter/android/app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
plugins {
id("com.android.application")
id("kotlin-android")
id("dev.flutter.flutter-gradle-plugin")
}

android {
namespace = "com.easyink.sample.easyink_flutter_sample"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion

compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
}

defaultConfig {
applicationId = "com.easyink.sample.easyink_flutter_sample"
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
}

buildTypes {
release {
signingConfig = signingConfigs.getByName("debug")
}
}
}

flutter {
source = "../.."
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.easyink.sample.easyink_flutter_sample">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
Loading
Loading