@alfacapital-tech/c4builder 0.3.0-rc.2 → 0.3.0-rc.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 CHANGED
@@ -42,6 +42,11 @@ With `diagramFormat: png`, diagrams are produced by rasterizing their SVG with
42
42
  native binary, no browser). This gives a single deterministic PNG for both PlantUML and
43
43
  D2, using the bundled font. ditaa diagrams stay native PlantUML PNG.
44
44
 
45
+ **Fonts.** Diagrams use the bundled Nimbus Sans (Helvetica metrics, Cyrillic and Greek —
46
+ see `vendor/fonts/README.md`), so diagram geometry is identical on any machine.
47
+ `useSystemFonts: true` in `.c4builder` (or `--system-fonts` for one run) renders with the
48
+ machine's fonts instead — machine-dependent by design.
49
+
45
50
  The easiest way to get started is with the _new project_ template.
46
51
 
47
52
  ![new](docs/images/new.gif)
@@ -92,7 +92,8 @@ function getOptions(c) {
92
92
  WEB_FILE_NAME: c.webFileName,
93
93
  SUPPORT_SEARCH: c.supportSearch,
94
94
  EXECUTE_SCRIPT: c.executeScript,
95
- EXCLUDE_OTHER_FILES: c.excludeOtherFiles
95
+ EXCLUDE_OTHER_FILES: c.excludeOtherFiles,
96
+ USE_SYSTEM_FONTS: c.useSystemFonts
96
97
  };
97
98
  }
98
99
  export default async () => {
@@ -110,6 +111,7 @@ export default async () => {
110
111
  .option('-w, --watch', 'watch for changes and rebuild')
111
112
  .option('-o, --open', 'open the generated site in the browser (with --site)')
112
113
  .option('--docs', 'a brief explanation for the available configuration options')
114
+ .option('--system-fonts', 'render with system fonts instead of the bundled one (not deterministic)')
113
115
  .option('-p, --port <n>', 'port used for serving the generated site', (v) => {
114
116
  // parseInt молча резал бы опечатку ('30OO' → 30) — валидируем строго.
115
117
  if (!isValidPort(v))
@@ -202,6 +204,8 @@ export default async () => {
202
204
  // уводил бы последующие запуски на строгий путь (exit 1 до визарда).
203
205
  conf.set('hasRun', true);
204
206
  const options = getOptions(built.value);
207
+ if (opts.systemFonts)
208
+ options.USE_SYSTEM_FONTS = true; // флаг сильнее ключа конфига
205
209
  const reloadEmitter = new EventEmitter();
206
210
  reloadEmitter.setMaxListeners(0);
207
211
  if (opts.watch) {
@@ -29,6 +29,7 @@ const defaultConfig = {
29
29
  plantumlServerUrl: 'https://www.plantuml.com/plantuml',
30
30
  diagramFormat: 'svg',
31
31
  d2Layout: 'dagre',
32
- charset: 'UTF-8'
32
+ charset: 'UTF-8',
33
+ useSystemFonts: false
33
34
  };
34
35
  export { defaultConfig };
@@ -53,6 +53,7 @@ export const configSchema = z.object({
53
53
  diagramFormat: str(defaultConfig.diagramFormat),
54
54
  d2Layout: str(defaultConfig.d2Layout),
55
55
  charset: str(defaultConfig.charset),
56
+ useSystemFonts: bool(defaultConfig.useSystemFonts),
56
57
  // Задаются явно (флаг/промпт) либо тонкой правкой файла — в defaultConfig не входят.
57
58
  projectName: optStr(),
58
59
  webFileName: optStr(),
@@ -78,6 +78,9 @@ const d2EngineVersion = () => {
78
78
  }
79
79
  return d2VersionCache;
80
80
  };
81
+ // В ключ кеша: смена режима не должна отдавать картинку, отрисованную другим шрифтом
82
+ // (D2 тоже — его PNG растрирует общий resvg).
83
+ export const fontCacheTag = (options) => options.USE_SYSTEM_FONTS ? 'system' : DEFAULT_FONT_NAME;
81
84
  export const getMime = (format) => {
82
85
  if (format === 'svg')
83
86
  return `image/svg+xml`;
@@ -102,22 +105,24 @@ export const httpGet = async (url) => (await httpGetBuffer(url)).toString('base6
102
105
  // расходятся между ОС. С пином шрифт берётся из vendor/fonts (JRE своих не несёт,
103
106
  // freetype у Temurin бандлед), и SVG получается одинаковым везде.
104
107
  // ditaa этим не лечится: его движок берёт AWT-шрифт мимо обеих опций.
105
- export const renderDiagram = (content, { javaBin, jarPath, includePath, format, charset, isDitaa }) => new Promise((resolve, reject) => {
106
- const argv = [
107
- '-Djava.awt.headless=true',
108
- `-Dplantuml.include.path=${includePath}`,
109
- `-Dsun.java2d.fontpath=prepend:${FONTS_DIR}`,
110
- '-jar',
111
- jarPath,
112
- ...(isDitaa ? [] : ['-Playout=smetana']),
113
- `-SdefaultFontName=${DEFAULT_FONT_NAME}`,
114
- `-SCircledCharacterFontName=${DEFAULT_FONT_NAME}`,
115
- '-charset',
116
- charset,
117
- `-t${format}`,
118
- '-pipe'
119
- ];
120
- const child = spawn(javaBin, argv);
108
+ // useSystemFonts убирает все три шрифтовых аргумента — рендер как до пина (машинозависим).
109
+ export const renderArgv = ({ jarPath, includePath, format, charset, isDitaa, useSystemFonts }) => [
110
+ '-Djava.awt.headless=true',
111
+ `-Dplantuml.include.path=${includePath}`,
112
+ ...(useSystemFonts ? [] : [`-Dsun.java2d.fontpath=prepend:${FONTS_DIR}`]),
113
+ '-jar',
114
+ jarPath,
115
+ ...(isDitaa ? [] : ['-Playout=smetana']),
116
+ ...(useSystemFonts
117
+ ? []
118
+ : [`-SdefaultFontName=${DEFAULT_FONT_NAME}`, `-SCircledCharacterFontName=${DEFAULT_FONT_NAME}`]),
119
+ '-charset',
120
+ charset,
121
+ `-t${format}`,
122
+ '-pipe'
123
+ ];
124
+ export const renderDiagram = (content, options) => new Promise((resolve, reject) => {
125
+ const child = spawn(options.javaBin, renderArgv(options));
121
126
  const stdout = [];
122
127
  const stderr = [];
123
128
  // stdio по умолчанию 'pipe' → потоки заведомо не null; берём их деструктуризацией
@@ -253,9 +258,10 @@ export const generateImages = async (tree, options, onImageGenerated, cacheConf,
253
258
  // чексумму → из бэкапа копировалась старая картинка (новые параметры молча
254
259
  // игнорировались, кэш чистился только через --reset). Формат/движок/версия
255
260
  // движка тоже в ключе: их смена обязана приводить к перерендеру.
256
- const renderKey = entryPath !== null
261
+ const renderKey = (entryPath !== null
257
262
  ? `d2\0${d2EngineVersion()}\0layout=${options.D2_LAYOUT}\0fmt=${outFormat}`
258
- : `puml\0${VENDORED_JAR.version}\0charset=${options.CHARSET}\0fmt=${outFormat}\0ditaa=${diagram.isDitaa}\0font=${DEFAULT_FONT_NAME}`;
263
+ : `puml\0${VENDORED_JAR.version}\0charset=${options.CHARSET}\0fmt=${outFormat}\0ditaa=${diagram.isDitaa}`) +
264
+ `\0font=${fontCacheTag(options)}`;
259
265
  const cksum = crypto
260
266
  .createHash('sha256')
261
267
  .update(body + includes + renderKey, 'utf-8')
@@ -305,10 +311,13 @@ export const generateImages = async (tree, options, onImageGenerated, cacheConf,
305
311
  includePath,
306
312
  format: needsRaster ? 'svg' : outFormat,
307
313
  charset: options.CHARSET,
308
- isDitaa
314
+ isDitaa,
315
+ useSystemFonts: options.USE_SYSTEM_FONTS
309
316
  });
310
317
  }
311
- const image = needsRaster ? await rasterizeSvgToPng(rendered) : rendered;
318
+ const image = needsRaster
319
+ ? await rasterizeSvgToPng(rendered, options.USE_SYSTEM_FONTS)
320
+ : rendered;
312
321
  await writeFile(filePath, image);
313
322
  cksumEntry.ok = true;
314
323
  }
@@ -5,4 +5,4 @@ import { VENDOR_DIR } from "../../util/paths.js";
5
5
  // (`fontDirs`, `defaultFontFamily`). Значения обязаны совпадать — иначе метрики
6
6
  // текста разъедутся и PNG-golden покраснеют. НЕ менять без пересбора эталонов.
7
7
  export const FONTS_DIR = path.join(VENDOR_DIR, 'fonts');
8
- export const DEFAULT_FONT_NAME = 'Liberation Sans';
8
+ export const DEFAULT_FONT_NAME = 'Nimbus Sans';
@@ -20,21 +20,18 @@ const getResvg = () => {
20
20
  }
21
21
  return resvgMod;
22
22
  };
23
+ export const resvgFontOptions = (useSystemFonts) => useSystemFonts
24
+ ? { loadSystemFonts: true }
25
+ : { fontDirs: [FONTS_DIR], defaultFontFamily: DEFAULT_FONT_NAME, loadSystemFonts: false };
23
26
  // SVG (Buffer или строка) → PNG (Buffer), масштаб 1:1 к размеру SVG. Вход — валидный
24
27
  // SVG движка: PlantUML ссылается на шрифт по имени (грузим из vendor/fonts), D2 несёт
25
- // шрифт во вшитом @font-face, но loadSystemFonts:false фиксируем в обоих случаях.
28
+ // шрифт во вшитом @font-face, но пин по умолчанию фиксируем в обоих случаях.
26
29
  // renderAsync считает в napi-пуле тредов: JS-поток не блокируется, PlantUML-пул и
27
30
  // D2-очередь продолжают крутиться параллельно растеризации (прежний sync render()
28
31
  // останавливал весь event loop на каждую картинку).
29
- const rasterizeSvgToPng = async (svg) => {
32
+ const rasterizeSvgToPng = async (svg, useSystemFonts = false) => {
30
33
  const { renderAsync } = getResvg();
31
- const rendered = await renderAsync(svg, {
32
- font: {
33
- fontDirs: [FONTS_DIR],
34
- defaultFontFamily: DEFAULT_FONT_NAME,
35
- loadSystemFonts: false
36
- }
37
- });
34
+ const rendered = await renderAsync(svg, { font: resvgFontOptions(useSystemFonts) });
38
35
  return rendered.asPng();
39
36
  };
40
37
  export { rasterizeSvgToPng };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alfacapital-tech/c4builder",
3
- "version": "0.3.0-rc.2",
3
+ "version": "0.3.0-rc.3",
4
4
  "description": "A CLI tool designed to compile a folder structure of markdowns and plant uml files into a site, single file markdown or a collection of markdowns with links",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
Binary file
@@ -1,17 +1,20 @@
1
1
  # Вендорные шрифты
2
2
 
3
- ## Liberation Sans
3
+ ## Nimbus Sans
4
4
 
5
- `LiberationSans-{Regular,Bold,Italic,BoldItalic}.ttf`
5
+ `NimbusSans-{Regular,Bold,Italic,BoldItalic}.otf`
6
6
 
7
7
  - **Назначение:** детерминизм метрик текста в SVG. Ширина текста (`textLength`)
8
8
  считается PlantUML из AWT-метрик шрифта; системный `sans-serif` резолвится
9
9
  по-разному на разных машинах. Шрифт отдаётся JVM через
10
- `-Dsun.java2d.fontpath=prepend:` + `-SdefaultFontName=Liberation Sans`, чтобы
11
- метрики не зависели от машины (см. `build.js`).
12
- - **Происхождение:** пакет `ttf-liberation` / `fonts-liberation`, версия 2.1.5
13
- (font version 2.1). Метрически совместим с Arial; та же версия поставляется на
14
- CI-раннере `ubuntu-24.04`.
15
- - **Лицензия:** SIL Open Font License 1.1 (разрешает вендоринг и распространение).
16
- Полный текст: <https://openfontlicense.org/> · апстрим:
17
- <https://github.com/liberationfonts/liberation-fonts>.
10
+ `-Dsun.java2d.fontpath=prepend:` + `-SdefaultFontName=Nimbus Sans` и resvg через
11
+ `fontDirs` (см. `src/core/render/fonts.ts`). Пин снимается ключом `useSystemFonts`
12
+ в `.c4builder` / флагом `--system-fonts` — тогда рендер идёт системным шрифтом и
13
+ перестаёт быть машинонезависимым.
14
+ - **Происхождение:** URW base35 (Artifex), релиз 20200910 — файлы побайтно совпадают
15
+ с пакетом `gsfonts` этой версии. Метрически совместим с Helvetica, содержит
16
+ кириллицу и греческий. Апстрим:
17
+ <https://github.com/ArtifexSoftware/urw-base35-fonts>.
18
+ - **Лицензия:** AGPL-3.0-only с шрифтовым исключением
19
+ (`PS-or-PDF-font-exception-20170817`): встраивание шрифта в документы не делает их
20
+ производными работами. Полный текст — в апстрим-репозитории (`LICENSE`).