react-native-nano-icons 0.1.1 → 0.1.3
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/README.md +20 -164
- package/android/build.gradle +28 -0
- package/android/src/main/java/com/nanoicons/NanoIconView.kt +78 -0
- package/android/src/main/java/com/nanoicons/NanoIconViewManager.kt +84 -0
- package/android/src/main/java/com/nanoicons/NanoIconsPackage.kt +22 -0
- package/ios/NanoIconView.h +4 -0
- package/ios/NanoIconView.mm +246 -0
- package/lib/commonjs/cli/build.js +1 -1
- package/lib/commonjs/cli/config.d.ts +2 -2
- package/lib/commonjs/cli/config.js +7 -6
- package/lib/commonjs/plugin/src/buildFonts.d.ts +1 -0
- package/lib/commonjs/plugin/src/buildFonts.js +9 -0
- package/lib/commonjs/plugin/src/index.js +1 -34
- package/lib/commonjs/plugin/src/withNanoIconsFontLinking.d.ts +6 -6
- package/lib/commonjs/plugin/src/withNanoIconsFontLinking.js +11 -15
- package/lib/commonjs/scripts/cli.js +15 -5
- package/lib/commonjs/src/core/font/compile.d.ts +13 -2
- package/lib/commonjs/src/core/font/compile.js +49 -46
- package/lib/commonjs/src/core/pipeline/managers.js +19 -3
- package/lib/commonjs/src/core/pipeline/run.js +121 -32
- package/lib/commonjs/src/core/svg/layers.d.ts +16 -0
- package/lib/commonjs/src/core/svg/layers.js +27 -0
- package/lib/commonjs/src/core/svg/svg_dom.d.ts +29 -1
- package/lib/commonjs/src/core/svg/svg_dom.js +78 -2
- package/lib/commonjs/src/core/svg/svg_pathops.d.ts +11 -0
- package/lib/commonjs/src/core/svg/svg_pathops.js +209 -19
- package/lib/commonjs/src/core/types.d.ts +30 -15
- package/lib/module/core/font/compile.js +52 -41
- package/lib/module/core/font/compile.js.map +1 -1
- package/lib/module/core/pipeline/managers.js +17 -3
- package/lib/module/core/pipeline/managers.js.map +1 -1
- package/lib/module/core/pipeline/run.js +131 -44
- package/lib/module/core/pipeline/run.js.map +1 -1
- package/lib/module/core/shims/picosvg-0.22.3-py3-none-any.whl +0 -0
- package/lib/module/core/svg/layers.js +34 -0
- package/lib/module/core/svg/layers.js.map +1 -1
- package/lib/module/core/svg/svg_dom.js +91 -4
- package/lib/module/core/svg/svg_dom.js.map +1 -1
- package/lib/module/core/svg/svg_pathops.js +203 -19
- package/lib/module/core/svg/svg_pathops.js.map +1 -1
- package/lib/module/createNanoIconsSet.js +3 -79
- package/lib/module/createNanoIconsSet.js.map +1 -1
- package/lib/module/createNanoIconsSet.native.js +68 -0
- package/lib/module/createNanoIconsSet.native.js.map +1 -0
- package/lib/module/createNanoIconsSet.shared.js +91 -0
- package/lib/module/createNanoIconsSet.shared.js.map +1 -0
- package/lib/module/index.js +1 -2
- package/lib/module/index.js.map +1 -1
- package/lib/module/specs/NanoIconViewNativeComponent.ts +15 -0
- package/lib/module/types.js +4 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/utils/shallowEqualColor.js +15 -0
- package/lib/module/utils/shallowEqualColor.js.map +1 -0
- package/lib/typescript/src/core/font/compile.d.ts +13 -2
- package/lib/typescript/src/core/font/compile.d.ts.map +1 -1
- package/lib/typescript/src/core/pipeline/managers.d.ts.map +1 -1
- package/lib/typescript/src/core/pipeline/run.d.ts.map +1 -1
- package/lib/typescript/src/core/svg/layers.d.ts +16 -0
- package/lib/typescript/src/core/svg/layers.d.ts.map +1 -1
- package/lib/typescript/src/core/svg/svg_dom.d.ts +29 -1
- package/lib/typescript/src/core/svg/svg_dom.d.ts.map +1 -1
- package/lib/typescript/src/core/svg/svg_pathops.d.ts +11 -0
- package/lib/typescript/src/core/svg/svg_pathops.d.ts.map +1 -1
- package/lib/typescript/src/core/types.d.ts +30 -15
- package/lib/typescript/src/core/types.d.ts.map +1 -1
- package/lib/typescript/src/createNanoIconsSet.d.ts +5 -18
- package/lib/typescript/src/createNanoIconsSet.d.ts.map +1 -1
- package/lib/typescript/src/createNanoIconsSet.native.d.ts +7 -0
- package/lib/typescript/src/createNanoIconsSet.native.d.ts.map +1 -0
- package/lib/typescript/src/createNanoIconsSet.shared.d.ts +11 -0
- package/lib/typescript/src/createNanoIconsSet.shared.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/specs/NanoIconViewNativeComponent.d.ts +14 -0
- package/lib/typescript/src/specs/NanoIconViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +19 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/lib/typescript/src/utils/shallowEqualColor.d.ts +4 -0
- package/lib/typescript/src/utils/shallowEqualColor.d.ts.map +1 -0
- package/package.json +22 -5
- package/plugin/src/buildFonts.ts +13 -0
- package/plugin/src/index.ts +3 -50
- package/plugin/src/withNanoIconsFontLinking.ts +22 -24
- package/react-native-nano-icons.podspec +18 -0
- package/scripts/cli.ts +14 -5
- package/src/core/font/compile.ts +65 -61
- package/src/core/pipeline/managers.ts +26 -3
- package/src/core/pipeline/run.ts +156 -38
- package/src/core/shims/picosvg-0.22.3-py3-none-any.whl +0 -0
- package/src/core/svg/layers.ts +44 -0
- package/src/core/svg/svg_dom.ts +96 -4
- package/src/core/svg/svg_pathops.ts +245 -27
- package/src/core/types.ts +21 -10
- package/src/createNanoIconsSet.native.tsx +108 -0
- package/src/createNanoIconsSet.shared.tsx +121 -0
- package/src/createNanoIconsSet.tsx +7 -126
- package/src/index.ts +1 -2
- package/src/specs/NanoIconViewNativeComponent.ts +15 -0
- package/src/types.ts +27 -0
- package/src/utils/shallowEqualColor.ts +17 -0
package/plugin/src/index.ts
CHANGED
|
@@ -1,31 +1,6 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
ConfigPlugin,
|
|
3
|
-
ExportedConfigWithProps,
|
|
4
|
-
} from '@expo/config-plugins';
|
|
5
|
-
import { withDangerousMod } from '@expo/config-plugins';
|
|
6
|
-
import { buildAllFonts } from './buildFonts.js';
|
|
1
|
+
import type { ConfigPlugin } from '@expo/config-plugins';
|
|
7
2
|
import { withNanoIconsFontLinking } from './withNanoIconsFontLinking.js';
|
|
8
|
-
import type {
|
|
9
|
-
NanoIconsPluginOptions,
|
|
10
|
-
IconSetConfig,
|
|
11
|
-
BuiltFont,
|
|
12
|
-
} from './types.js';
|
|
13
|
-
|
|
14
|
-
const BUILT_FONTS_KEY = '_nanoIconsBuilt';
|
|
15
|
-
|
|
16
|
-
// Single Pyodide/PathKit run for the whole prebuild; reused across ios/android mods.
|
|
17
|
-
let _builtFontsCache: BuiltFont[] | null = null;
|
|
18
|
-
|
|
19
|
-
function getOrBuildFonts(
|
|
20
|
-
projectRoot: string,
|
|
21
|
-
iconSets: IconSetConfig[]
|
|
22
|
-
): Promise<BuiltFont[]> {
|
|
23
|
-
if (_builtFontsCache) return Promise.resolve(_builtFontsCache);
|
|
24
|
-
return buildAllFonts(iconSets, projectRoot).then((built: BuiltFont[]) => {
|
|
25
|
-
_builtFontsCache = built;
|
|
26
|
-
return built;
|
|
27
|
-
});
|
|
28
|
-
}
|
|
3
|
+
import type { NanoIconsPluginOptions } from './types.js';
|
|
29
4
|
|
|
30
5
|
const withNanoIcons: ConfigPlugin<NanoIconsPluginOptions> = (
|
|
31
6
|
config,
|
|
@@ -33,29 +8,7 @@ const withNanoIcons: ConfigPlugin<NanoIconsPluginOptions> = (
|
|
|
33
8
|
) => {
|
|
34
9
|
if (!options?.iconSets?.length) return config;
|
|
35
10
|
|
|
36
|
-
|
|
37
|
-
config = withDangerousMod(config, [
|
|
38
|
-
'ios',
|
|
39
|
-
async (config: ExportedConfigWithProps<unknown>) => {
|
|
40
|
-
const projectRoot = config.modRequest.projectRoot;
|
|
41
|
-
const built = await getOrBuildFonts(projectRoot, options.iconSets);
|
|
42
|
-
(config as unknown as Record<string, unknown>)[BUILT_FONTS_KEY] = built;
|
|
43
|
-
return config;
|
|
44
|
-
},
|
|
45
|
-
]);
|
|
46
|
-
|
|
47
|
-
config = withDangerousMod(config, [
|
|
48
|
-
'android',
|
|
49
|
-
async (config: ExportedConfigWithProps<unknown>) => {
|
|
50
|
-
const projectRoot = config.modRequest.projectRoot;
|
|
51
|
-
const built = await getOrBuildFonts(projectRoot, options.iconSets);
|
|
52
|
-
(config as unknown as Record<string, unknown>)[BUILT_FONTS_KEY] = built;
|
|
53
|
-
return config;
|
|
54
|
-
},
|
|
55
|
-
]);
|
|
56
|
-
|
|
57
|
-
// Link built TTFs into native projects (reads _nanoIconsBuilt from config).
|
|
58
|
-
config = withNanoIconsFontLinking(config);
|
|
11
|
+
config = withNanoIconsFontLinking(config, options.iconSets);
|
|
59
12
|
|
|
60
13
|
return config;
|
|
61
14
|
};
|
|
@@ -4,31 +4,25 @@ import {
|
|
|
4
4
|
withXcodeProject,
|
|
5
5
|
withDangerousMod,
|
|
6
6
|
} from '@expo/config-plugins';
|
|
7
|
+
import type { InfoPlist } from '@expo/config-plugins';
|
|
7
8
|
import fs from 'fs/promises';
|
|
8
9
|
import path from 'path';
|
|
9
|
-
import
|
|
10
|
-
type
|
|
10
|
+
import { getOrBuildFonts } from './buildFonts.js';
|
|
11
|
+
import type { IconSetConfig } from './types.js';
|
|
11
12
|
|
|
12
13
|
const ANDROID_ASSETS_FONTS_DIR = 'app/src/main/assets/fonts';
|
|
13
14
|
|
|
14
|
-
const BUILT_FONTS_KEY = '_nanoIconsBuilt' as const;
|
|
15
|
-
|
|
16
|
-
function getBuiltFonts(config: {
|
|
17
|
-
[key: string]: unknown;
|
|
18
|
-
}): BuiltFont[] | undefined {
|
|
19
|
-
return config[BUILT_FONTS_KEY] as BuiltFont[] | undefined;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
15
|
/**
|
|
23
16
|
* Add TTFs to the iOS project (Resources group + UIAppFonts in Info.plist).
|
|
24
|
-
* Reads cached built font paths from config._nanoIconsBuilt (set by the build mod) to prevent redundant builds for iOS and Android.
|
|
25
17
|
*/
|
|
26
18
|
export function withNanoIconsIos(
|
|
27
|
-
config: Parameters<typeof withXcodeProject>[0]
|
|
19
|
+
config: Parameters<typeof withXcodeProject>[0],
|
|
20
|
+
iconSets: IconSetConfig[]
|
|
28
21
|
): ReturnType<typeof withXcodeProject> {
|
|
29
22
|
config = withXcodeProject(config, async (config) => {
|
|
30
|
-
const built =
|
|
31
|
-
config
|
|
23
|
+
const built = await getOrBuildFonts(
|
|
24
|
+
config.modRequest.projectRoot,
|
|
25
|
+
iconSets
|
|
32
26
|
);
|
|
33
27
|
if (!built?.length) return config;
|
|
34
28
|
const ttfPaths = built.map((b) => b.ttfPath);
|
|
@@ -51,8 +45,9 @@ export function withNanoIconsIos(
|
|
|
51
45
|
config = withInfoPlist(
|
|
52
46
|
config as Parameters<typeof withInfoPlist>[0],
|
|
53
47
|
async (config) => {
|
|
54
|
-
const built =
|
|
55
|
-
config
|
|
48
|
+
const built = await getOrBuildFonts(
|
|
49
|
+
config.modRequest.projectRoot,
|
|
50
|
+
iconSets
|
|
56
51
|
);
|
|
57
52
|
if (!built?.length) return config;
|
|
58
53
|
const ttfPaths = built.map((b) => b.ttfPath);
|
|
@@ -80,16 +75,18 @@ function getUIAppFonts(infoPlist: InfoPlist): string[] {
|
|
|
80
75
|
}
|
|
81
76
|
|
|
82
77
|
/**
|
|
83
|
-
* Copy TTFs to Android assets/fonts.
|
|
78
|
+
* Copy TTFs to Android assets/fonts.
|
|
84
79
|
*/
|
|
85
80
|
export function withNanoIconsAndroid(
|
|
86
|
-
config: Parameters<typeof withDangerousMod>[0]
|
|
81
|
+
config: Parameters<typeof withDangerousMod>[0],
|
|
82
|
+
iconSets: IconSetConfig[]
|
|
87
83
|
): ReturnType<typeof withDangerousMod> {
|
|
88
84
|
return withDangerousMod(config, [
|
|
89
85
|
'android',
|
|
90
86
|
async (config) => {
|
|
91
|
-
const built =
|
|
92
|
-
config
|
|
87
|
+
const built = await getOrBuildFonts(
|
|
88
|
+
config.modRequest.projectRoot,
|
|
89
|
+
iconSets
|
|
93
90
|
);
|
|
94
91
|
if (!built?.length) return config;
|
|
95
92
|
const fontsDir = path.join(
|
|
@@ -108,12 +105,13 @@ export function withNanoIconsAndroid(
|
|
|
108
105
|
}
|
|
109
106
|
|
|
110
107
|
/**
|
|
111
|
-
* Apply iOS and Android font linking.
|
|
108
|
+
* Apply iOS and Android font linking.
|
|
112
109
|
*/
|
|
113
110
|
export function withNanoIconsFontLinking(
|
|
114
|
-
config: Parameters<typeof withNanoIconsIos>[0]
|
|
111
|
+
config: Parameters<typeof withNanoIconsIos>[0],
|
|
112
|
+
iconSets: IconSetConfig[]
|
|
115
113
|
): ReturnType<typeof withNanoIconsAndroid> {
|
|
116
|
-
config = withNanoIconsIos(config);
|
|
117
|
-
config = withNanoIconsAndroid(config);
|
|
114
|
+
config = withNanoIconsIos(config, iconSets);
|
|
115
|
+
config = withNanoIconsAndroid(config, iconSets);
|
|
118
116
|
return config;
|
|
119
117
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
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 = "react-native-nano-icons"
|
|
7
|
+
s.version = package["version"]
|
|
8
|
+
s.summary = package["description"]
|
|
9
|
+
s.homepage = package["homepage"]
|
|
10
|
+
s.license = package["license"]
|
|
11
|
+
s.author = package["author"]
|
|
12
|
+
s.source = { :git => package["repository"], :tag => "#{s.version}" }
|
|
13
|
+
s.platforms = { :ios => "15.1" }
|
|
14
|
+
s.source_files = "ios/**/*.{h,m,mm,cpp}"
|
|
15
|
+
s.requires_arc = true
|
|
16
|
+
|
|
17
|
+
install_modules_dependencies(s)
|
|
18
|
+
end
|
package/scripts/cli.ts
CHANGED
|
@@ -2,14 +2,16 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Bare React Native workflow: build icon fonts and link them into the native project.
|
|
4
4
|
*
|
|
5
|
-
* Run from your app root: npx react-native-nano-icons [--verbose]
|
|
5
|
+
* Run from your app root: npx react-native-nano-icons [--verbose] [--path <dir>]
|
|
6
6
|
*
|
|
7
7
|
* Reads .nanoicons.json (same shape as Expo plugin options) so Expo and bare apps
|
|
8
8
|
* share one config format.
|
|
9
9
|
*
|
|
10
10
|
* Flags:
|
|
11
|
-
* --verbose
|
|
11
|
+
* --verbose Show per-SVG processing details and pipeline timing
|
|
12
|
+
* --path <dir> Directory containing .nanoicons.json (default: cwd)
|
|
12
13
|
*/
|
|
14
|
+
import path from 'node:path';
|
|
13
15
|
import {
|
|
14
16
|
createOraLogger,
|
|
15
17
|
loadNanoIconsConfig,
|
|
@@ -21,10 +23,17 @@ async function main(): Promise<void> {
|
|
|
21
23
|
const verbose = process.argv.includes('--verbose');
|
|
22
24
|
const level = verbose ? 'verbose' : 'normal';
|
|
23
25
|
|
|
26
|
+
const pathIdx = process.argv.indexOf('--path');
|
|
27
|
+
const projectRoot = process.cwd();
|
|
28
|
+
const configRoot =
|
|
29
|
+
pathIdx !== -1 && process.argv[pathIdx + 1]
|
|
30
|
+
? path.resolve(projectRoot, process.argv[pathIdx + 1]!)
|
|
31
|
+
: projectRoot;
|
|
32
|
+
|
|
24
33
|
const logger = await createOraLogger(level);
|
|
25
|
-
const config = loadNanoIconsConfig(
|
|
26
|
-
const built = await buildAllFonts(config.iconSets,
|
|
27
|
-
await linkBare(
|
|
34
|
+
const config = loadNanoIconsConfig(configRoot);
|
|
35
|
+
const built = await buildAllFonts(config.iconSets, projectRoot, { logger });
|
|
36
|
+
await linkBare(projectRoot, built, logger);
|
|
28
37
|
}
|
|
29
38
|
|
|
30
39
|
main().catch((err: unknown) => {
|
package/src/core/font/compile.ts
CHANGED
|
@@ -1,38 +1,74 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { once } from 'node:events';
|
|
4
|
-
import { type Transform } from 'node:stream';
|
|
5
3
|
|
|
6
4
|
import { forceTtfMetrics } from './metrics.js';
|
|
7
5
|
import svg2ttf from 'svg2ttf';
|
|
8
|
-
import { parseCodepointFromFilename } from '../../utils/parse.js';
|
|
9
6
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const name = path.basename(filename, '.svg');
|
|
7
|
+
export type FontGlyph = {
|
|
8
|
+
codepoint: number;
|
|
9
|
+
advanceWidth: number;
|
|
10
|
+
/** Path data already in font coordinates (Y-up, placement applied). */
|
|
11
|
+
d: string;
|
|
12
|
+
};
|
|
17
13
|
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
function escapeXml(s: string): string {
|
|
15
|
+
return s.replace(/&/g, '&').replace(/"/g, '"');
|
|
16
|
+
}
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Build an SVG font XML string from pre-transformed glyph data.
|
|
20
|
+
*/
|
|
21
|
+
function buildSvgFontXml(opts: {
|
|
22
|
+
fontName: string;
|
|
23
|
+
glyphs: FontGlyph[];
|
|
24
|
+
upm: number;
|
|
25
|
+
ascent: number;
|
|
26
|
+
descent: number;
|
|
27
|
+
}): string {
|
|
28
|
+
const { fontName, glyphs, upm, ascent, descent } = opts;
|
|
25
29
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
glyphStream.on('end', resolve);
|
|
30
|
+
const glyphLines = glyphs.map((g) => {
|
|
31
|
+
const hex = g.codepoint.toString(16);
|
|
32
|
+
const name = `u${hex.padStart(4, '0')}`;
|
|
33
|
+
return `<glyph glyph-name="${name}" unicode="&#x${hex};" horiz-adv-x="${g.advanceWidth}" d="${escapeXml(g.d)}"/>`;
|
|
31
34
|
});
|
|
35
|
+
|
|
36
|
+
return `<?xml version="1.0" standalone="no"?>
|
|
37
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
38
|
+
<svg xmlns="http://www.w3.org/2000/svg">
|
|
39
|
+
<defs>
|
|
40
|
+
<font id="${escapeXml(fontName)}" horiz-adv-x="${upm}">
|
|
41
|
+
<font-face font-family="${escapeXml(fontName)}" units-per-em="${upm}" ascent="${ascent}" descent="${-Math.abs(descent)}"/>
|
|
42
|
+
<missing-glyph horiz-adv-x="0"/>
|
|
43
|
+
${glyphLines.join('\n')}
|
|
44
|
+
</font>
|
|
45
|
+
</defs>
|
|
46
|
+
</svg>`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function parseCompileTtfFromGlyphsError(
|
|
50
|
+
err: unknown,
|
|
51
|
+
codepointToIcon: Map<number, string>
|
|
52
|
+
) {
|
|
53
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
54
|
+
const cpMatch = msg.match(/glyph\s+"u([0-9a-fA-F]+)"/);
|
|
55
|
+
if (cpMatch) {
|
|
56
|
+
const cp = parseInt(cpMatch[1]!, 16);
|
|
57
|
+
const iconName = codepointToIcon.get(cp);
|
|
58
|
+
const detail = iconName
|
|
59
|
+
? `icon "${iconName}" (codepoint u${cpMatch[1]})`
|
|
60
|
+
: `codepoint u${cpMatch[1]}`;
|
|
61
|
+
throw new Error(`Font compilation failed for ${detail}: ${msg}`);
|
|
62
|
+
}
|
|
63
|
+
throw err;
|
|
32
64
|
}
|
|
33
65
|
|
|
34
|
-
|
|
35
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Compile a TTF font from pre-transformed glyph data.
|
|
68
|
+
* Builds SVG font XML directly (no intermediate files), then converts via svg2ttf.
|
|
69
|
+
*/
|
|
70
|
+
export async function compileTtfFromGlyphs(opts: {
|
|
71
|
+
glyphs: FontGlyph[];
|
|
36
72
|
outTtfPath: string;
|
|
37
73
|
fontName: string;
|
|
38
74
|
upm: number;
|
|
@@ -40,52 +76,20 @@ export async function compileTtfFromGlyphSVGs(opts: {
|
|
|
40
76
|
descent: number;
|
|
41
77
|
lineGap?: number;
|
|
42
78
|
}): Promise<void> {
|
|
43
|
-
const {
|
|
44
|
-
|
|
45
|
-
const { glyphDir, outTtfPath, fontName, upm, ascent, descent } = opts;
|
|
79
|
+
const { glyphs, outTtfPath, fontName, upm, ascent, descent } = opts;
|
|
46
80
|
const lineGap = opts.lineGap ?? 0;
|
|
47
81
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
.filter((f) => /^u[0-9a-fA-F]+\.svg$/.test(f))
|
|
51
|
-
.sort(
|
|
52
|
-
(a, b) => parseCodepointFromFilename(a) - parseCodepointFromFilename(b)
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
if (files.length === 0)
|
|
56
|
-
throw new Error(`No glyph SVGs found in: ${glyphDir}`);
|
|
82
|
+
if (glyphs.length === 0)
|
|
83
|
+
throw new Error('No glyphs to compile');
|
|
57
84
|
|
|
58
|
-
const
|
|
85
|
+
const svgFontString = buildSvgFontXml({
|
|
59
86
|
fontName,
|
|
60
|
-
|
|
61
|
-
|
|
87
|
+
glyphs,
|
|
88
|
+
upm,
|
|
62
89
|
ascent,
|
|
63
90
|
descent,
|
|
64
91
|
});
|
|
65
92
|
|
|
66
|
-
const svgFontChunks: Buffer[] = [];
|
|
67
|
-
fontStream.on('data', (c: Buffer | string) =>
|
|
68
|
-
svgFontChunks.push(Buffer.isBuffer(c) ? c : Buffer.from(c))
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
// Single error listener for the font stream; per-glyph errors are handled in writeGlyphStreamToFont.
|
|
72
|
-
let fontStreamReject: (err: Error) => void;
|
|
73
|
-
const fontStreamErrorPromise = new Promise<never>((_, rej) => {
|
|
74
|
-
fontStreamReject = rej;
|
|
75
|
-
});
|
|
76
|
-
fontStream.on('error', (err: Error) => fontStreamReject(err));
|
|
77
|
-
|
|
78
|
-
for (const f of files) {
|
|
79
|
-
await Promise.race([
|
|
80
|
-
writeGlyphStreamToFont(fontStream, path.join(glyphDir, f), f),
|
|
81
|
-
fontStreamErrorPromise,
|
|
82
|
-
]);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
fontStream.end();
|
|
86
|
-
await once(fontStream, 'end');
|
|
87
|
-
|
|
88
|
-
const svgFontString = Buffer.concat(svgFontChunks).toString('utf8');
|
|
89
93
|
const ttfRaw = svg2ttf(svgFontString);
|
|
90
94
|
const rawBuf = Buffer.from(ttfRaw.buffer);
|
|
91
95
|
|
|
@@ -73,14 +73,37 @@ export class PyodideManager {
|
|
|
73
73
|
|
|
74
74
|
await py.loadPackage(['micropip', 'lxml'], { messageCallback: () => {} });
|
|
75
75
|
|
|
76
|
+
// Resolve local picosvg wheel path for offline-first installation.
|
|
77
|
+
const picosvgWhlDir = path.join(
|
|
78
|
+
getPackageRoot(),
|
|
79
|
+
'src',
|
|
80
|
+
'core',
|
|
81
|
+
'shims'
|
|
82
|
+
);
|
|
83
|
+
const picosvgWhl = (await fs.readdir(picosvgWhlDir))
|
|
84
|
+
.find((f) => f.startsWith('picosvg-') && f.endsWith('.whl'));
|
|
85
|
+
const localWhlUrl = picosvgWhl
|
|
86
|
+
? `file://${path.join(picosvgWhlDir, picosvgWhl)}`
|
|
87
|
+
: null;
|
|
88
|
+
|
|
89
|
+
py.globals.set('_picosvg_local_whl', localWhlUrl);
|
|
90
|
+
|
|
76
91
|
await py.runPythonAsync(`
|
|
77
92
|
import sys
|
|
78
93
|
if "/" not in sys.path:
|
|
79
94
|
sys.path.insert(0, "/")
|
|
80
|
-
|
|
95
|
+
|
|
81
96
|
import micropip
|
|
82
|
-
|
|
83
|
-
|
|
97
|
+
|
|
98
|
+
_whl = _picosvg_local_whl
|
|
99
|
+
if _whl:
|
|
100
|
+
try:
|
|
101
|
+
await micropip.install(_whl, deps=False)
|
|
102
|
+
except Exception:
|
|
103
|
+
await micropip.install("picosvg", deps=False)
|
|
104
|
+
else:
|
|
105
|
+
await micropip.install("picosvg", deps=False)
|
|
106
|
+
|
|
84
107
|
import pathops
|
|
85
108
|
import picosvg
|
|
86
109
|
`);
|