@effing/canvas 0.31.4 → 0.33.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/README.md CHANGED
@@ -184,14 +184,15 @@ const png = canvas.encodeSync("png");
184
184
 
185
185
  ### Color & Background
186
186
 
187
- | Property | Values / Notes |
188
- | ----------------- | -------------------------------------------------- |
189
- | `color` | Any CSS color (inherited) |
190
- | `backgroundColor` | Any CSS color |
191
- | `opacity` | Number (0–1) |
192
- | `background` | Shorthand → `backgroundColor` or `backgroundImage` |
193
- | `backgroundImage` | `linear-gradient()`, `radial-gradient()`, `url()` |
194
- | `backgroundSize` | `cover`, `contain`, length, percentage |
187
+ | Property | Values / Notes |
188
+ | ------------------ | ------------------------------------------------------------------------------------------------------ |
189
+ | `color` | Any CSS color (inherited) |
190
+ | `backgroundColor` | Any CSS color |
191
+ | `opacity` | Number (0–1) |
192
+ | `background` | Shorthand → decomposed into `backgroundColor`, `backgroundImage`, `backgroundRepeat`, `backgroundSize` |
193
+ | `backgroundImage` | `linear-gradient()`, `radial-gradient()`, `url()` |
194
+ | `backgroundSize` | `cover`, `contain`, length, percentage |
195
+ | `backgroundRepeat` | `repeat` (default), `no-repeat`, `repeat-x`, `repeat-y` |
195
196
 
196
197
  ### Typography
197
198
 
package/dist/index.js CHANGED
@@ -2939,8 +2939,11 @@ async function drawNodeCore(ctx, node, parentX, parentY, offsetX, offsetY, debug
2939
2939
  tileW = image.width;
2940
2940
  tileH = image.height;
2941
2941
  }
2942
- for (let ty = y; ty < y + height; ty += tileH) {
2943
- for (let tx = x; tx < x + width; tx += tileW) {
2942
+ const repeat = style.backgroundRepeat ?? "repeat";
2943
+ const stepX = repeat === "repeat" || repeat === "repeat-x" ? tileW : width;
2944
+ const stepY = repeat === "repeat" || repeat === "repeat-y" ? tileH : height;
2945
+ for (let ty = y; ty < y + height; ty += stepY) {
2946
+ for (let tx = x; tx < x + width; tx += stepX) {
2944
2947
  ctx.drawImage(image, tx, ty, tileW, tileH);
2945
2948
  }
2946
2949
  }
@@ -3243,12 +3246,15 @@ function expandStyle(raw, fontFamilies) {
3243
3246
  delete style.gap;
3244
3247
  }
3245
3248
  if (style.background !== void 0) {
3246
- const bg = String(style.background);
3247
- if (bg.includes("gradient(") || bg.includes("url(")) {
3248
- if (style.backgroundImage === void 0) style.backgroundImage = bg;
3249
- } else {
3250
- if (style.backgroundColor === void 0) style.backgroundColor = bg;
3251
- }
3249
+ const parts = parseBackgroundShorthand(String(style.background));
3250
+ if (parts.image !== void 0 && style.backgroundImage === void 0)
3251
+ style.backgroundImage = parts.image;
3252
+ if (parts.repeat !== void 0 && style.backgroundRepeat === void 0)
3253
+ style.backgroundRepeat = parts.repeat;
3254
+ if (parts.size !== void 0 && style.backgroundSize === void 0)
3255
+ style.backgroundSize = parts.size;
3256
+ if (parts.color !== void 0 && style.backgroundColor === void 0)
3257
+ style.backgroundColor = parts.color;
3252
3258
  delete style.background;
3253
3259
  }
3254
3260
  if (style.overflow !== void 0) {
@@ -3294,6 +3300,105 @@ function expandStyle(raw, fontFamilies) {
3294
3300
  }
3295
3301
  return style;
3296
3302
  }
3303
+ var BG_REPEAT_KEYWORDS = /* @__PURE__ */ new Set([
3304
+ "repeat",
3305
+ "no-repeat",
3306
+ "repeat-x",
3307
+ "repeat-y",
3308
+ "space",
3309
+ "round"
3310
+ ]);
3311
+ var BG_POSITION_KEYWORDS = /* @__PURE__ */ new Set([
3312
+ "left",
3313
+ "right",
3314
+ "top",
3315
+ "bottom",
3316
+ "center"
3317
+ ]);
3318
+ var BG_ATTACHMENT_KEYWORDS = /* @__PURE__ */ new Set(["scroll", "fixed", "local"]);
3319
+ var BG_BOX_KEYWORDS = /* @__PURE__ */ new Set(["border-box", "padding-box", "content-box"]);
3320
+ var BG_IMAGE_FN = /^(?:url|image-set|(?:repeating-)?(?:linear|radial|conic)-gradient)\(/;
3321
+ function tokenizeBackground(value) {
3322
+ const tokens = [];
3323
+ let buf = "";
3324
+ for (let i = 0; i < value.length; i++) {
3325
+ const c = value[i];
3326
+ if (c === "(") {
3327
+ let depth = 1;
3328
+ buf += c;
3329
+ i++;
3330
+ while (i < value.length && depth > 0) {
3331
+ const ch = value[i];
3332
+ if (ch === "(") depth++;
3333
+ else if (ch === ")") depth--;
3334
+ buf += ch;
3335
+ i++;
3336
+ }
3337
+ i--;
3338
+ } else if (/\s/.test(c)) {
3339
+ if (buf) {
3340
+ tokens.push(buf);
3341
+ buf = "";
3342
+ }
3343
+ } else if (c === "/") {
3344
+ if (buf) {
3345
+ tokens.push(buf);
3346
+ buf = "";
3347
+ }
3348
+ tokens.push("/");
3349
+ } else {
3350
+ buf += c;
3351
+ }
3352
+ }
3353
+ if (buf) tokens.push(buf);
3354
+ return tokens;
3355
+ }
3356
+ function parseLayer(value) {
3357
+ const out = {};
3358
+ const sizeTokens = [];
3359
+ let afterSlash = false;
3360
+ for (const tok of tokenizeBackground(value)) {
3361
+ if (tok === "/") {
3362
+ afterSlash = true;
3363
+ continue;
3364
+ }
3365
+ if (afterSlash) {
3366
+ sizeTokens.push(tok);
3367
+ continue;
3368
+ }
3369
+ if (BG_IMAGE_FN.test(tok)) {
3370
+ out.image = tok;
3371
+ continue;
3372
+ }
3373
+ if (tok === "none") continue;
3374
+ if (BG_REPEAT_KEYWORDS.has(tok)) {
3375
+ out.repeat = tok;
3376
+ continue;
3377
+ }
3378
+ if (BG_POSITION_KEYWORDS.has(tok) || BG_ATTACHMENT_KEYWORDS.has(tok) || BG_BOX_KEYWORDS.has(tok) || /^-?\d/.test(tok)) {
3379
+ continue;
3380
+ }
3381
+ out.color = tok;
3382
+ }
3383
+ if (sizeTokens.length > 0) out.size = sizeTokens.join(" ");
3384
+ return out;
3385
+ }
3386
+ function parseBackgroundShorthand(value) {
3387
+ const layers = splitGradientArgs(value).map((l) => l.trim());
3388
+ if (layers.length <= 1) return parseLayer(layers[0] ?? "");
3389
+ const parsed = layers.map(parseLayer);
3390
+ const out = {};
3391
+ const images = parsed.map((p) => p.image ?? "none");
3392
+ if (images.some((i) => i !== "none")) out.image = images.join(", ");
3393
+ for (const p of parsed) {
3394
+ if (p.repeat !== void 0 && out.repeat === void 0)
3395
+ out.repeat = p.repeat;
3396
+ if (p.size !== void 0 && out.size === void 0) out.size = p.size;
3397
+ }
3398
+ const finalColor = parsed[parsed.length - 1]?.color;
3399
+ if (finalColor !== void 0) out.color = finalColor;
3400
+ return out;
3401
+ }
3297
3402
 
3298
3403
  // src/jsx/yoga.ts
3299
3404
  import Yoga, {