@harmonyos-arkts/d2h 0.0.0-stage → 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/.claude-plugin/marketplace.json +13 -0
- package/.claude-plugin/plugin.json +7 -0
- package/README.md +168 -2
- package/agents/android-to-hmos-00-orchestrator.md +312 -0
- package/agents/d2h.md +168 -0
- package/bin/install-opencode.mjs +51 -0
- package/install-opencode.sh +160 -0
- package/opencode/agents.json +16 -0
- package/package.json +16 -4
- package/schemas/android-source-manifest.schema.json +25 -0
- package/schemas/checkpoint-provenance.schema.json +67 -0
- package/schemas/final-acceptance.schema.json +29 -0
- package/schemas/managed-evidence-index.schema.json +15 -0
- package/schemas/managed-evidence.schema.json +114 -0
- package/schemas/migration-config.schema.json +51 -0
- package/schemas/migration-report-index.schema.json +39 -0
- package/schemas/migration-report-item.schema.json +51 -0
- package/schemas/migration-report-summary.schema.json +25 -0
- package/schemas/migration-status.schema.json +202 -0
- package/schemas/preflight.schema.json +60 -0
- package/schemas/source-order-audit.schema.json +16 -0
- package/schemas/source-provenance-event.schema.json +19 -0
- package/schemas/spec-app-shard.schema.json +45 -0
- package/schemas/spec-fact-corrections-shard.schema.json +13 -0
- package/schemas/spec-features-shard.schema.json +43 -0
- package/schemas/spec-index.schema.json +26 -0
- package/schemas/spec-interactions-shard.schema.json +40 -0
- package/schemas/spec-page.schema.json +98 -0
- package/schemas/spec-pages-index.schema.json +1 -0
- package/schemas/spec-unresolved-shard.schema.json +1 -0
- package/schemas/task-envelope.schema.json +55 -0
- package/schemas/task-plan.schema.json +123 -0
- package/skills/android2hmos_resources_convert/SKILL.md +162 -0
- package/skills/android2hmos_resources_convert/references/image-conversion-rules.md +230 -0
- package/skills/android2hmos_resources_convert/references/svg-fix-patterns.md +175 -0
- package/skills/android2hmos_resources_convert/references/xml-drawable-to-svg-rules.md +513 -0
- package/skills/appgraph-rule-audit/SKILL.md +58 -0
- package/skills/appgraph-rule-audit/references/audit-contract.md +47 -0
- package/skills/appgraph-rule-audit/references/recommendation-schema.md +41 -0
- package/skills/appgraph-rule-audit/schemas/rule-opportunities.schema.json +125 -0
- package/skills/arkts-app-identity/SKILL.md +238 -0
- package/skills/arkts-i18n/SKILL.md +496 -0
- package/skills/arkts-i18n/evals/evals.json +84 -0
- package/skills/arkts-i18n/references/code-examples.md +302 -0
- package/skills/arkts-i18n/references/common-pitfalls.md +391 -0
- package/skills/arkts-i18n/references/dynamic-language-switch.md +604 -0
- package/skills/arkts-i18n/references/hardcoded-string-scanner.md +348 -0
- package/skills/arkts-i18n/references/language-codes.md +104 -0
- package/skills/arkts-i18n/references/resource-file-structure.md +775 -0
- package/skills/arkts-i18n/references/static-vs-dynamic.md +242 -0
- package/skills/arkts-i18n/references/v1-compat.md +244 -0
- package/skills/arkts-i18n/scripts/audit_i18n_completeness.sh +174 -0
- package/skills/arkts-icon-sizing/SKILL.md +211 -0
- package/skills/arkts-icon-sizing/scripts/icon_audit.py +131 -0
- package/skills/arkts-icon-sizing/scripts/icon_autofix.py +88 -0
- package/skills/arkts-icon-sizing/scripts/icon_dims.py +179 -0
- package/skills/arkts-icon-sizing/scripts/icon_fix.py +119 -0
- package/skills/arkts-mvvm-architecture/SKILL.md +613 -0
- package/skills/harmonyos-migration-playbook/SKILL.md +56 -0
- package/skills/harmonyos-migration-playbook/agents/openai.yaml +7 -0
- package/skills/harmonyos-migration-playbook/references/arkts-compile.md +24 -0
- package/skills/harmonyos-migration-playbook/references/harmony-runtime.md +53 -0
- package/skills/harmonyos-migration-playbook/references/lesson-lifecycle.md +45 -0
- package/skills/harmonyos-migration-playbook/references/protocol-e2e.md +23 -0
- package/skills/harmonyos-migration-playbook/references/ui-automation.md +52 -0
- package/skills/harmonyos-migration-playbook/references/windows-environment.md +38 -0
- package/skills/maintaining-migration-report/SKILL.md +155 -0
- package/skills/native-library-substitution/SKILL.md +385 -0
- package/skills/native-library-substitution/references/native-library-substitution.json +56906 -0
- package/skills/native-library-substitution/references/native-library-substitution.md +163 -0
- package/skills/preparing-migration-workspace/SKILL.md +124 -0
- package/skills/preparing-migration-workspace/toolchain.json +43 -0
- package/skills/reviewing-migration-process/SKILL.md +62 -0
- package/src/install-opencode.mjs +108 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# Android to HarmonyOS Image Resource Conversion Rules
|
|
2
|
+
|
|
3
|
+
Complete rules for converting Android **image resources** — bitmap files, nine-patch images, launcher icons (mipmap), adaptive icons — into HarmonyOS `media/` resources. XML drawables → SVG are covered separately in `xml-drawable-to-svg-rules.md`; this document defines what routes to SVG, plus the qualifier directory algorithm and reference resolution needed by image conversion.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
1. [Target Directory Structure](#target-directory-structure)
|
|
7
|
+
2. [Bitmap Images → media](#bitmap-images--media)
|
|
8
|
+
3. [Nine-Patch Images](#nine-patch-images)
|
|
9
|
+
4. [Mipmap → media](#mipmap--media)
|
|
10
|
+
5. [Adaptive Icon → Layered-Image JSON](#adaptive-icon--layered-image-json)
|
|
11
|
+
6. [Identifying XML vs Image Drawables](#identifying-xml-vs-image-drawables)
|
|
12
|
+
7. [Qualifier Mapping](#qualifier-mapping)
|
|
13
|
+
8. [Reference Resolution for Images](#reference-resolution-for-images)
|
|
14
|
+
9. [Special Cases and Edge Cases](#special-cases-and-edge-cases)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Target Directory Structure
|
|
19
|
+
|
|
20
|
+
HarmonyOS image resources live under the `media/` resource group:
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
entry/src/main/resources/
|
|
24
|
+
├── base/media/ # default images (no qualifier)
|
|
25
|
+
├── ldpi/media/ # density-qualified
|
|
26
|
+
├── dark/media/ # night mode
|
|
27
|
+
├── zh_CN/media/ # locale-qualified
|
|
28
|
+
└── zh_CN-dark-ldpi/media/ # combined qualifiers
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
HarmonyOS supported image formats: PNG, JPG, GIF, SVG, WEBP, BMP — all common Android bitmap formats copy directly.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Bitmap Images → media
|
|
36
|
+
|
|
37
|
+
Copy image files from Android `drawable*/` (and `mipmap*/`) to the qualifier-mapped `media/` directory:
|
|
38
|
+
|
|
39
|
+
- `.png`, `.jpg`, `.jpeg`, `.gif`, `.webp`, `.bmp` → direct copy, filename unchanged
|
|
40
|
+
- Resource names are preserved exactly as Android spells them — they are identifiers
|
|
41
|
+
- Do NOT copy XML files in this step — classify them first (see [Identifying XML vs Image Drawables](#identifying-xml-vs-image-drawables))
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Nine-Patch Images
|
|
46
|
+
|
|
47
|
+
Android nine-patch files (`.9.png`) have no native HarmonyOS equivalent:
|
|
48
|
+
|
|
49
|
+
1. **Rename**: `.9.png` → `<name>_9.png`. HarmonyOS forbids `.` in resource filenames except the final extension separator, so `btn_bg.9.png` would be invalid — it becomes `btn_bg_9.png`.
|
|
50
|
+
2. **Semantics lost**: the 1-px stretch/padding border markers are rendered as ordinary pixels; nine-patch stretching behavior does not carry over. The developer must implement stretching at the ArkUI component level (`.borderRadius` + `.resizable` or 9-slice via `ResizableOptions` where supported).
|
|
51
|
+
3. Always log nine-patch conversions in the report with the rename and the lost-semantics note.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Mipmap → media
|
|
56
|
+
|
|
57
|
+
Launcher icons from `mipmap*/` directories go to the qualifier-mapped `media/` directory:
|
|
58
|
+
|
|
59
|
+
- PNG/WEBP launcher icons (`ic_launcher.png`, `ic_launcher_round.png`, `ic_launcher_foreground.png`, `ic_launcher_background.png`, …) → direct copy
|
|
60
|
+
- **Adaptive icon XML** (root `<adaptive-icon>`, commonly in `mipmap-anydpi-v26/`) → layered-image JSON (see below)
|
|
61
|
+
- **VectorDrawable XML in mipmap** (root `<vector>`) → convert to SVG exactly like drawable vectors
|
|
62
|
+
|
|
63
|
+
**Common pattern**: `mipmap-anydpi-v26/` is extremely common. Parse the qualifier as `anydpi` (→ `base`) + `v26` (→ strip) → target `base/media/`. Classify the files inside by their XML root element, never by the directory name.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Adaptive Icon → Layered-Image JSON
|
|
68
|
+
|
|
69
|
+
Convert `<adaptive-icon>` XML to `<original_name>_layered_image.json` in the target `media/` directory.
|
|
70
|
+
|
|
71
|
+
**Conversion rules:**
|
|
72
|
+
|
|
73
|
+
1. Parse the XML and extract the `<background>` and `<foreground>` elements.
|
|
74
|
+
2. Only `background` and `foreground` are valid keys in the output JSON. Skip `monochrome` (and any other elements) **from the JSON output** — HarmonyOS layered-image has no equivalent. However, the resource *referenced* by `monochrome` is a real asset that must still be converted as a normal image.
|
|
75
|
+
3. Convert references:
|
|
76
|
+
- `@drawable/xxx` → `$media:xxx`
|
|
77
|
+
- `@mipmap/xxx` → `$media:xxx`
|
|
78
|
+
- `@color/xxx` → **generate a solid-color PNG** (below) — layered-image only accepts `$media:` references, never `$color:`
|
|
79
|
+
4. **Color background handling (critical)**:
|
|
80
|
+
a. Resolve `@color/xxx` to its hex value from `values*/colors.xml`, following reference chains to a concrete value.
|
|
81
|
+
b. Generate a solid-color PNG filled entirely with that hex (any small size, e.g. 1×1 or 108×108 px).
|
|
82
|
+
c. Save as `<color_name>_bg.png` in `base/media/`.
|
|
83
|
+
d. Reference it as `$media:<color_name>_bg` in the JSON.
|
|
84
|
+
e. If the color cannot be resolved (library resource, source `res/` fallback), log it as an unresolved dependency in the report.
|
|
85
|
+
5. A layer may carry artwork as a **child element** instead of an `android:drawable` attribute (`<foreground><vector …/></foreground>`). Convert that inline drawable to its own media file named `<icon_name>_<layer>.svg` and reference it as `$media:<icon_name>_<layer>`. Never emit the JSON with the layer simply absent. A `<bitmap android:src="@drawable/x"/>` wrapper is just a reference — point at `$media:x`.
|
|
86
|
+
|
|
87
|
+
**Example:**
|
|
88
|
+
|
|
89
|
+
```xml
|
|
90
|
+
<!-- mipmap-anydpi-v26/ic_launcher.xml -->
|
|
91
|
+
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
92
|
+
<background android:drawable="@color/md_orange_700" />
|
|
93
|
+
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
|
|
94
|
+
<monochrome android:drawable="@drawable/ic_launcher_monochrome" />
|
|
95
|
+
</adaptive-icon>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Suppose `@color/md_orange_700` resolves to `#F57C00`. Generate `base/media/md_orange_700_bg.png` (solid `#F57C00`), then write `base/media/ic_launcher_layered_image.json`:
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"layered-image": {
|
|
103
|
+
"background": "$media:md_orange_700_bg",
|
|
104
|
+
"foreground": "$media:ic_launcher_foreground"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`@drawable/ic_launcher_monochrome` is additionally converted as a normal drawable (SVG or bitmap per its type) even though it never appears in the JSON.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Identifying XML vs Image Drawables
|
|
114
|
+
|
|
115
|
+
In `drawable*/` and `mipmap*/` directories, classify every `.xml` file by its **root element** (never by filename or directory):
|
|
116
|
+
|
|
117
|
+
| Root element | Type | Route |
|
|
118
|
+
|---|---|---|
|
|
119
|
+
| `<vector>` | VectorDrawable | → SVG (`xml-drawable-to-svg-rules.md`) |
|
|
120
|
+
| `<shape>` | ShapeDrawable | → SVG |
|
|
121
|
+
| `<layer-list>` | LayerListDrawable | → SVG |
|
|
122
|
+
| `<selector>` | StateListDrawable | Extract default state → SVG; log dropped state variants |
|
|
123
|
+
| `<adaptive-icon>` | Adaptive icon | → layered-image JSON (above) |
|
|
124
|
+
| `<ripple>`, `<animated-vector>`, `<animated-selector>`, `<transition>`, `<level-list>`, `<scale>`, `<rotate>`, `<inset>` | Runtime behavior | Unmappable — log. Exception: `<inset>`/`<rotate>` with fixed values may be applied statically and the inner drawable converted; log either way |
|
|
125
|
+
|
|
126
|
+
When several drawable directories provide the same resource name (`drawable/x.xml` and `drawable-anydpi/x.xml` both landing in `base/media/`), the source with **fewer qualifiers** wins; record the loser in the report as a collision — never silently drop it.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Qualifier Mapping
|
|
131
|
+
|
|
132
|
+
Android uses qualifier suffixes on resource directories; HarmonyOS uses a different qualifier naming scheme. Images are always placed under the `media/` group of the mapped qualifier directory.
|
|
133
|
+
|
|
134
|
+
### Density Qualifiers
|
|
135
|
+
|
|
136
|
+
The DPI range labels differ between platforms:
|
|
137
|
+
|
|
138
|
+
| Android Qualifier | Approx DPI | HarmonyOS Qualifier | HarmonyOS DPI Range |
|
|
139
|
+
|---|---|---|---|
|
|
140
|
+
| `ldpi` | 120 | `sdpi` | (0, 120] |
|
|
141
|
+
| `mdpi` | 160 | `mdpi` | (120, 160] |
|
|
142
|
+
| `tvdpi` | 213 | `ldpi` | (160, 240] |
|
|
143
|
+
| `hdpi` | 240 | `ldpi` | (160, 240] |
|
|
144
|
+
| `xhdpi` | 320 | `xldpi` | (240, 320] |
|
|
145
|
+
| `xxhdpi` | 480 | `xxldpi` | (320, 480] |
|
|
146
|
+
| `xxxhdpi` | 640 | `xxxldpi` | (480, 640] |
|
|
147
|
+
| `nodpi` | N/A | `base` | No density qualifier |
|
|
148
|
+
| `anydpi` | N/A | `base` | No density qualifier |
|
|
149
|
+
|
|
150
|
+
Note the deliberate mismatch: Android `hdpi` (~240) maps to HarmonyOS **`ldpi`** because HarmonyOS labels the (160, 240] range `ldpi`.
|
|
151
|
+
|
|
152
|
+
### Language/Region Qualifiers
|
|
153
|
+
|
|
154
|
+
| Android | HarmonyOS |
|
|
155
|
+
|---|---|
|
|
156
|
+
| `drawable-ar` | `ar/media/` |
|
|
157
|
+
| `drawable-zh` | `zh/media/` |
|
|
158
|
+
| `drawable-zh-rCN` | `zh_CN/media/` |
|
|
159
|
+
| `drawable-zh-rTW` | `zh_TW/media/` |
|
|
160
|
+
| `drawable-en-rUS` | `en_US/media/` |
|
|
161
|
+
| `drawable-fr-rFR` | `fr_FR/media/` |
|
|
162
|
+
|
|
163
|
+
Parsing: language = 2–3 lowercase letters (ISO 639); region = `r` + 2 uppercase letters, strip the `r`; script = `b+sr+Latn` → `sr_Latn`. Language and region join with `_`.
|
|
164
|
+
|
|
165
|
+
### Orientation Qualifiers
|
|
166
|
+
|
|
167
|
+
| Android | HarmonyOS |
|
|
168
|
+
|---|---|
|
|
169
|
+
| `land` | `horizontal` |
|
|
170
|
+
| `port` | `vertical` |
|
|
171
|
+
|
|
172
|
+
### Night Mode Qualifiers
|
|
173
|
+
|
|
174
|
+
| Android | HarmonyOS |
|
|
175
|
+
|---|---|
|
|
176
|
+
| `night` | `dark` |
|
|
177
|
+
| `notnight` | `light` |
|
|
178
|
+
|
|
179
|
+
### Combining Multiple Qualifiers
|
|
180
|
+
|
|
181
|
+
**Parsing algorithm for any Android drawable/mipmap directory name:**
|
|
182
|
+
|
|
183
|
+
1. Split the directory name by `-`; the first segment is the resource type
|
|
184
|
+
2. Parse remaining qualifiers left-to-right: language/region, density, orientation, night mode, API level (`v<N>`), screen-size constraints
|
|
185
|
+
3. Map each recognized qualifier to its HarmonyOS equivalent
|
|
186
|
+
4. **Strip silently** (do not reject): API level `v<N>`, layout direction (`ldrtl`/`ldltr`), screen shape (`round`/`notround`), HDR (`highdr`/`lowdr`), aspect (`long`/`notlong`)
|
|
187
|
+
5. **Skip the entire directory** (mark all resources unmapped) when screen-size qualifiers remain: `sw<N>dp`, `w<N>dp`, `h<N>dp`, `small`, `normal`, `large`, `xlarge` — HarmonyOS has no equivalent; do NOT convert them to `base/`
|
|
188
|
+
6. If no qualifiers remain after stripping, use `base/`
|
|
189
|
+
7. Reassemble in HarmonyOS order: MCC_MNC-language_script_country/region-orientation-device-colormode-density
|
|
190
|
+
|
|
191
|
+
**Examples:**
|
|
192
|
+
|
|
193
|
+
| Android Directory | Qualifiers | HarmonyOS Target |
|
|
194
|
+
|---|---|---|
|
|
195
|
+
| `drawable-hdpi` | hdpi→ldpi | `ldpi/media/` |
|
|
196
|
+
| `mipmap-anydpi-v26` | anydpi→base, v26 stripped | `base/media/` |
|
|
197
|
+
| `drawable-zh-rCN-night-hdpi` | zh_CN, dark, ldpi | `zh_CN-dark-ldpi/media/` |
|
|
198
|
+
| `drawable-night-v21` | dark, v21 stripped | `dark/media/` |
|
|
199
|
+
| `drawable-hdpi-v4` | ldpi, v4 stripped | `ldpi/media/` |
|
|
200
|
+
| `drawable-sw600dp` | **skip — unmapped** | N/A |
|
|
201
|
+
|
|
202
|
+
Never reject a directory just because one qualifier is unsupported — strip it and continue.
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## Reference Resolution for Images
|
|
207
|
+
|
|
208
|
+
Image conversion encounters value references in three places. Build a lookup table from `values*/colors.xml` and `values*/dimens.xml` before converting drawables.
|
|
209
|
+
|
|
210
|
+
**In converted SVG files:**
|
|
211
|
+
- `@color/name` used as `fillColor`/`strokeColor` → resolve to the hex value during SVG generation (follow chains; fallback `#000000` + log)
|
|
212
|
+
- `@dimen/name` used as a dimension → resolve to the numeric value (fallback: sensible default + log)
|
|
213
|
+
- `@android:color/*` system colors → map known ones (`white`→`#FFFFFF`, `black`→`#000000`, `transparent`→`#00000000`); unknown → `#000000` + log
|
|
214
|
+
|
|
215
|
+
**In layered-image JSON files:**
|
|
216
|
+
- `@drawable/xxx` / `@mipmap/xxx` → keep as `$media:xxx` (file references — the files themselves are converted)
|
|
217
|
+
- `@color/xxx` → resolve to hex and generate the solid-color PNG (see adaptive icon rules)
|
|
218
|
+
|
|
219
|
+
**Reference chain depth**: follow at most 5 levels (`A → B → C → concrete value`). Unresolved references are expected — library resources are absent from source `res/`; record them in the report with likely library provenance (e.g. `@drawable/abc_*` → AndroidX AppCompat, `@drawable/mtrl_*`/`design_*` → Material Components) so the developer can supply them manually.
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Special Cases and Edge Cases
|
|
224
|
+
|
|
225
|
+
- **Same name in multiple density directories** (`drawable-hdpi/x.png`, `drawable-xxhdpi/x.png`) → each copies to its own qualifier directory (`ldpi/media/x.png`, `xxldpi/media/x.png`) — no conflict.
|
|
226
|
+
- **Same name from qualified and unqualified dirs** (`drawable/x.png` and `drawable-anydpi/x.xml` both → `base/media/`) → the source with fewer qualifiers wins; the loser is recorded as a collision.
|
|
227
|
+
- **Existing files in the HarmonyOS project**: merge, do not overwrite — an existing media file with the same name keeps its position; the attempted copy is logged as a collision for the developer to review.
|
|
228
|
+
- **Empty/corrupt image files**: skip and log; do not copy zero-byte files into `media/`.
|
|
229
|
+
- **Filenames illegal in HarmonyOS** (`.` inside the name, leading digits are fine but uppercase is kept as-is): only the `.9.png` rename is automatic; any other illegal name is copied with a logged `unmappable` entry explaining the manual rename needed.
|
|
230
|
+
- **Large image sets**: first inventory, then group by (classification × qualifier), then convert group by group, then verify — never convert while still scanning.
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# SVG Display Issue Fix Patterns
|
|
2
|
+
|
|
3
|
+
Common display abnormalities and fixes when migrating SVG/Vector Drawables from Android projects or manually creating SVGs.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Issue 1: viewBox Does Not Match Path Coordinates
|
|
8
|
+
|
|
9
|
+
### Symptoms
|
|
10
|
+
Icon does not display at all, or only shows a blank area.
|
|
11
|
+
|
|
12
|
+
### Cause
|
|
13
|
+
The coordinate range defined by `viewBox` does not match the coordinate data in the `d` attribute of `<path>`.
|
|
14
|
+
|
|
15
|
+
For example: path data is based on a 512×512 coordinate system (coordinate values range from 85 to 448), but `viewBox` is set to `0 0 24 24`, causing all paths to exceed the visible area.
|
|
16
|
+
|
|
17
|
+
### Fix
|
|
18
|
+
|
|
19
|
+
**Option A: Adjust viewBox to Match Path Coordinates (Recommended for Simple Cases)**
|
|
20
|
+
```xml
|
|
21
|
+
<!-- Wrong: viewBox too small -->
|
|
22
|
+
<svg viewBox="0 0 24 24">
|
|
23
|
+
<path d="M101.51,411.23 Q85.21,395.05 ..." /> <!-- coordinates far exceed 24 -->
|
|
24
|
+
</svg>
|
|
25
|
+
|
|
26
|
+
<!-- Fixed: viewBox matches coordinate system, use width/height to control render size -->
|
|
27
|
+
<svg width="24" height="24" viewBox="0 0 512 512">
|
|
28
|
+
<path d="M101.51,411.23 Q85.21,395.05 ..." />
|
|
29
|
+
</svg>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
> Note: `viewBox` defines the path coordinate space, while `width`/`height` define the render size. They are independent.
|
|
33
|
+
|
|
34
|
+
**Option B: Scale Path Coordinates Proportionally (Recommended for Standardization)**
|
|
35
|
+
|
|
36
|
+
Scale all values in the path proportionally to the target coordinate system. For example, scaling from 512 to 24:
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
import re
|
|
40
|
+
|
|
41
|
+
scale = 24.0 / 512.0 # 0.046875
|
|
42
|
+
|
|
43
|
+
def scale_number(match):
|
|
44
|
+
num = float(match.group(0))
|
|
45
|
+
scaled = round(num * scale, 2)
|
|
46
|
+
return '{:.2f}'.format(scaled).rstrip('0').rstrip('.')
|
|
47
|
+
|
|
48
|
+
path_data = "M101.51,411.23Q85.21,395.05 85.22,371.75..."
|
|
49
|
+
result = re.sub(r'[0-9]+\.?[0-9]*', scale_number, path_data)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
After scaling, you can use the standard `viewBox="0 0 24 24"`, consistent with other Material Design icons.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Issue 2: Android Vector Drawable Syntax Remnants
|
|
57
|
+
|
|
58
|
+
### Symptoms
|
|
59
|
+
The SVG file contains Android-specific XML attributes that standard SVG renderers cannot parse.
|
|
60
|
+
|
|
61
|
+
### Common Remnant Syntax
|
|
62
|
+
|
|
63
|
+
| Android Syntax | Standard SVG Equivalent |
|
|
64
|
+
|---------------|----------------------|
|
|
65
|
+
| `<group android:translateX="24" android:rotation="90">` | `<g transform="translate(24,0) rotate(90)">` |
|
|
66
|
+
| `android:fillColor="#FF000000"` | `fill="#000000"` |
|
|
67
|
+
| `android:pathData="..."` | `d="..."` |
|
|
68
|
+
| `android:viewportWidth="24"` | Part of `viewBox="0 0 24 24"` |
|
|
69
|
+
| `<vector>` root element | `<svg>` root element |
|
|
70
|
+
| `<clip-path android:pathData="..."/>` | `<defs><clipPath id="..."><path d="..."/></clipPath></defs>` |
|
|
71
|
+
|
|
72
|
+
### Fix
|
|
73
|
+
|
|
74
|
+
Fully convert Android `<group>` transform attributes to SVG `transform`:
|
|
75
|
+
|
|
76
|
+
```xml
|
|
77
|
+
<!-- Android Vector Drawable -->
|
|
78
|
+
<group
|
|
79
|
+
android:translateX="24"
|
|
80
|
+
android:translateY="0"
|
|
81
|
+
android:rotation="90"
|
|
82
|
+
android:pivotX="12"
|
|
83
|
+
android:pivotY="12"
|
|
84
|
+
android:scaleX="1.5"
|
|
85
|
+
android:scaleY="1.5">
|
|
86
|
+
<path android:pathData="M18,12l4,-4..." android:fillColor="#FFFFFF"/>
|
|
87
|
+
</group>
|
|
88
|
+
|
|
89
|
+
<!-- Converted to Standard SVG -->
|
|
90
|
+
<g transform="translate(24,0) rotate(90,12,12) scale(1.5,1.5)">
|
|
91
|
+
<path d="M18,12l4,-4..." fill="#FFFFFF"/>
|
|
92
|
+
</g>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Transform order: `translate` → `rotate` (with pivot point) → `scale`
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Issue 3: Fill Color is White Causing Invisible Icon
|
|
100
|
+
|
|
101
|
+
### Symptoms
|
|
102
|
+
SVG file exists and path is correct, but icon does not display (invisible on white background).
|
|
103
|
+
|
|
104
|
+
### Cause
|
|
105
|
+
The original Android drawable's `fillColor` is `#FFFFFF` (white), which is invisible on a white background. Common for icons designed to appear on dark ActionBars.
|
|
106
|
+
|
|
107
|
+
### Fix
|
|
108
|
+
|
|
109
|
+
Change `fill="#FFFFFF"` to `fill="#000000"` (black), or choose a suitable color based on UI needs:
|
|
110
|
+
|
|
111
|
+
```xml
|
|
112
|
+
<!-- Before fix: white fill invisible on white background -->
|
|
113
|
+
<path fill="#FFFFFF" d="M18,12l4,-4..."/>
|
|
114
|
+
|
|
115
|
+
<!-- After fix -->
|
|
116
|
+
<path fill="#000000" d="M18,12l4,-4..."/>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Issue 4: Google Material Symbols Coordinate System
|
|
122
|
+
|
|
123
|
+
### Description
|
|
124
|
+
Material Symbols SVGs downloaded from Google Fonts use a special coordinate system:
|
|
125
|
+
|
|
126
|
+
```xml
|
|
127
|
+
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#000000">
|
|
128
|
+
<path d="M200-120q-33 0-56.5-23.5T120-200v-560..."/>
|
|
129
|
+
</svg>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
- `viewBox="0 -960 960 960"` — Y-axis starts at -960 (positive is upward)
|
|
133
|
+
- Many negative values in path coordinates are normal
|
|
134
|
+
- This format renders correctly in HarmonyOS, **no additional conversion needed**
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Issue 5: HarmonyOS media Directory Does Not Support Subdirectories
|
|
139
|
+
|
|
140
|
+
### Symptoms
|
|
141
|
+
Compilation error: `CompileResource` error "invalid path, not a file"
|
|
142
|
+
|
|
143
|
+
### Cause
|
|
144
|
+
Created subdirectories under `resources/base/media/` (e.g., `media/icons/`, `media/md/`).
|
|
145
|
+
|
|
146
|
+
### Fix
|
|
147
|
+
|
|
148
|
+
Move all files from subdirectories to the `media/` root directory, using file name prefixes to distinguish sources:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
# Wrong: using subdirectories
|
|
152
|
+
media/
|
|
153
|
+
md/
|
|
154
|
+
share.svg
|
|
155
|
+
home.svg
|
|
156
|
+
|
|
157
|
+
# Correct: flat storage with prefixes
|
|
158
|
+
media/
|
|
159
|
+
md_share.svg
|
|
160
|
+
md_home.svg
|
|
161
|
+
ic_custom_icon.svg
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Quick Diagnosis Checklist
|
|
167
|
+
|
|
168
|
+
When SVG icon does not display, check in order:
|
|
169
|
+
|
|
170
|
+
1. **File exists?** — Confirm the file exists under `entry/src/main/resources/base/media/`
|
|
171
|
+
2. **Reference correct?** — `$r('app.media.filename_without_extension')`, filename is case-sensitive
|
|
172
|
+
3. **viewBox matches?** — `viewBox` coordinate range must cover path data coordinate values
|
|
173
|
+
4. **Syntax standard?** — No `android:` namespace attributes, root element is `<svg>` not `<vector>`
|
|
174
|
+
5. **Fill color visible?** — `fill` is not `#FFFFFF` (invisible on white background)
|
|
175
|
+
6. **No subdirectories?** — SVG files are directly under `media/` root directory
|