ugcinc-render 1.5.1 → 1.5.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/dist/index.js CHANGED
@@ -302,28 +302,70 @@ function calculateAutoWidth({
302
302
  fontSize,
303
303
  fontWeight,
304
304
  fontFamily,
305
- letterSpacing
305
+ letterSpacing,
306
+ lineHeight
306
307
  }) {
307
308
  if (typeof document === "undefined") {
308
309
  throw new Error("calculateAutoWidth requires a browser environment with document available");
309
310
  }
310
- const canvas = document.createElement("canvas");
311
- const ctx = canvas.getContext("2d");
312
- if (!ctx) {
313
- throw new Error("Failed to get 2D canvas context for text measurement");
311
+ const container = document.createElement("div");
312
+ container.style.cssText = `
313
+ position: absolute;
314
+ visibility: hidden;
315
+ pointer-events: none;
316
+ width: ${maxWidth}px;
317
+ padding: 0 ${paddingRight}px 0 ${paddingLeft}px;
318
+ box-sizing: border-box;
319
+ font-family: ${fontFamily};
320
+ font-size: ${fontSize}px;
321
+ font-weight: ${fontWeight};
322
+ letter-spacing: ${letterSpacing}px;
323
+ line-height: ${lineHeight};
324
+ white-space: pre-wrap;
325
+ word-break: break-word;
326
+ `;
327
+ container.textContent = text;
328
+ document.body.appendChild(container);
329
+ const totalHeight = container.offsetHeight;
330
+ const lineHeightPx = fontSize * lineHeight;
331
+ const numLines = Math.max(1, Math.round(totalHeight / lineHeightPx));
332
+ if (numLines === 1) {
333
+ const span = document.createElement("span");
334
+ span.style.cssText = `
335
+ font-family: ${fontFamily};
336
+ font-size: ${fontSize}px;
337
+ font-weight: ${fontWeight};
338
+ letter-spacing: ${letterSpacing}px;
339
+ white-space: nowrap;
340
+ `;
341
+ span.textContent = text;
342
+ document.body.appendChild(span);
343
+ const textWidth = span.offsetWidth;
344
+ document.body.removeChild(span);
345
+ document.body.removeChild(container);
346
+ return Math.min(textWidth + paddingLeft + paddingRight, maxWidth);
314
347
  }
315
- ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`;
316
- const availableWidth = maxWidth - paddingLeft - paddingRight;
317
348
  const words = text.split(" ");
318
349
  const lines = [];
319
350
  let currentLine = "";
351
+ const measureSpan = document.createElement("span");
352
+ measureSpan.style.cssText = `
353
+ position: absolute;
354
+ visibility: hidden;
355
+ font-family: ${fontFamily};
356
+ font-size: ${fontSize}px;
357
+ font-weight: ${fontWeight};
358
+ letter-spacing: ${letterSpacing}px;
359
+ white-space: nowrap;
360
+ `;
361
+ document.body.appendChild(measureSpan);
362
+ const availableWidth = maxWidth - paddingLeft - paddingRight;
320
363
  for (const word of words) {
321
364
  if (!word) continue;
322
365
  const testLine = currentLine + (currentLine ? " " : "") + word;
323
- const charCount = [...testLine].length;
324
- const extraSpacing = charCount > 1 ? (charCount - 1) * letterSpacing : 0;
325
- const totalWidth = ctx.measureText(testLine).width + extraSpacing;
326
- if (totalWidth > availableWidth && currentLine) {
366
+ measureSpan.textContent = testLine;
367
+ const testWidth = measureSpan.offsetWidth;
368
+ if (testWidth > availableWidth && currentLine) {
327
369
  lines.push(currentLine);
328
370
  currentLine = word;
329
371
  } else {
@@ -333,14 +375,11 @@ function calculateAutoWidth({
333
375
  if (currentLine) lines.push(currentLine);
334
376
  let widestLineWidth = 0;
335
377
  for (const line of lines) {
336
- const chars = [...line];
337
- let lineWidth = 0;
338
- for (const char of chars) {
339
- lineWidth += ctx.measureText(char).width + letterSpacing;
340
- }
341
- if (chars.length > 0) lineWidth -= letterSpacing;
342
- widestLineWidth = Math.max(widestLineWidth, lineWidth);
378
+ measureSpan.textContent = line;
379
+ widestLineWidth = Math.max(widestLineWidth, measureSpan.offsetWidth);
343
380
  }
381
+ document.body.removeChild(measureSpan);
382
+ document.body.removeChild(container);
344
383
  return Math.min(widestLineWidth + paddingLeft + paddingRight, maxWidth);
345
384
  }
346
385
  function TextElement({ segment, scale = 1 }) {
@@ -355,10 +394,10 @@ function TextElement({ segment, scale = 1 }) {
355
394
  const strokeColor = segment.strokeColor;
356
395
  const strokeWidth = (segment.strokeWidth ?? TEXT_DEFAULTS.strokeWidth) * scale;
357
396
  const uniformPadding = (segment.padding ?? TEXT_DEFAULTS.padding) * scale;
358
- const paddingTop = segment.paddingTop ?? uniformPadding;
359
- const paddingRight = segment.paddingRight ?? uniformPadding;
360
- const paddingBottom = segment.paddingBottom ?? uniformPadding;
361
- const paddingLeft = segment.paddingLeft ?? uniformPadding;
397
+ const paddingTop = segment.paddingTop !== void 0 ? segment.paddingTop * scale : uniformPadding;
398
+ const paddingRight = segment.paddingRight !== void 0 ? segment.paddingRight * scale : uniformPadding;
399
+ const paddingBottom = segment.paddingBottom !== void 0 ? segment.paddingBottom * scale : uniformPadding;
400
+ const paddingLeft = segment.paddingLeft !== void 0 ? segment.paddingLeft * scale : uniformPadding;
362
401
  const x = segment.xOffset * scale;
363
402
  const y = segment.yOffset * scale;
364
403
  const width = segment.width * scale;
@@ -380,9 +419,10 @@ function TextElement({ segment, scale = 1 }) {
380
419
  fontSize,
381
420
  fontWeight,
382
421
  fontFamily,
383
- letterSpacing
422
+ letterSpacing,
423
+ lineHeight
384
424
  });
385
- }, [autoWidth, segment.text, width, paddingLeft, paddingRight, fontSize, fontWeight, fontFamily, letterSpacing]);
425
+ }, [autoWidth, segment.text, width, paddingLeft, paddingRight, fontSize, fontWeight, fontFamily, letterSpacing, lineHeight]);
386
426
  const borderRadiusStyle = (0, import_react.useMemo)(() => {
387
427
  if (!backgroundBorderRadius) return void 0;
388
428
  const radii = getBorderRadii(backgroundBorderRadius);
package/dist/index.mjs CHANGED
@@ -228,28 +228,70 @@ function calculateAutoWidth({
228
228
  fontSize,
229
229
  fontWeight,
230
230
  fontFamily,
231
- letterSpacing
231
+ letterSpacing,
232
+ lineHeight
232
233
  }) {
233
234
  if (typeof document === "undefined") {
234
235
  throw new Error("calculateAutoWidth requires a browser environment with document available");
235
236
  }
236
- const canvas = document.createElement("canvas");
237
- const ctx = canvas.getContext("2d");
238
- if (!ctx) {
239
- throw new Error("Failed to get 2D canvas context for text measurement");
237
+ const container = document.createElement("div");
238
+ container.style.cssText = `
239
+ position: absolute;
240
+ visibility: hidden;
241
+ pointer-events: none;
242
+ width: ${maxWidth}px;
243
+ padding: 0 ${paddingRight}px 0 ${paddingLeft}px;
244
+ box-sizing: border-box;
245
+ font-family: ${fontFamily};
246
+ font-size: ${fontSize}px;
247
+ font-weight: ${fontWeight};
248
+ letter-spacing: ${letterSpacing}px;
249
+ line-height: ${lineHeight};
250
+ white-space: pre-wrap;
251
+ word-break: break-word;
252
+ `;
253
+ container.textContent = text;
254
+ document.body.appendChild(container);
255
+ const totalHeight = container.offsetHeight;
256
+ const lineHeightPx = fontSize * lineHeight;
257
+ const numLines = Math.max(1, Math.round(totalHeight / lineHeightPx));
258
+ if (numLines === 1) {
259
+ const span = document.createElement("span");
260
+ span.style.cssText = `
261
+ font-family: ${fontFamily};
262
+ font-size: ${fontSize}px;
263
+ font-weight: ${fontWeight};
264
+ letter-spacing: ${letterSpacing}px;
265
+ white-space: nowrap;
266
+ `;
267
+ span.textContent = text;
268
+ document.body.appendChild(span);
269
+ const textWidth = span.offsetWidth;
270
+ document.body.removeChild(span);
271
+ document.body.removeChild(container);
272
+ return Math.min(textWidth + paddingLeft + paddingRight, maxWidth);
240
273
  }
241
- ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`;
242
- const availableWidth = maxWidth - paddingLeft - paddingRight;
243
274
  const words = text.split(" ");
244
275
  const lines = [];
245
276
  let currentLine = "";
277
+ const measureSpan = document.createElement("span");
278
+ measureSpan.style.cssText = `
279
+ position: absolute;
280
+ visibility: hidden;
281
+ font-family: ${fontFamily};
282
+ font-size: ${fontSize}px;
283
+ font-weight: ${fontWeight};
284
+ letter-spacing: ${letterSpacing}px;
285
+ white-space: nowrap;
286
+ `;
287
+ document.body.appendChild(measureSpan);
288
+ const availableWidth = maxWidth - paddingLeft - paddingRight;
246
289
  for (const word of words) {
247
290
  if (!word) continue;
248
291
  const testLine = currentLine + (currentLine ? " " : "") + word;
249
- const charCount = [...testLine].length;
250
- const extraSpacing = charCount > 1 ? (charCount - 1) * letterSpacing : 0;
251
- const totalWidth = ctx.measureText(testLine).width + extraSpacing;
252
- if (totalWidth > availableWidth && currentLine) {
292
+ measureSpan.textContent = testLine;
293
+ const testWidth = measureSpan.offsetWidth;
294
+ if (testWidth > availableWidth && currentLine) {
253
295
  lines.push(currentLine);
254
296
  currentLine = word;
255
297
  } else {
@@ -259,14 +301,11 @@ function calculateAutoWidth({
259
301
  if (currentLine) lines.push(currentLine);
260
302
  let widestLineWidth = 0;
261
303
  for (const line of lines) {
262
- const chars = [...line];
263
- let lineWidth = 0;
264
- for (const char of chars) {
265
- lineWidth += ctx.measureText(char).width + letterSpacing;
266
- }
267
- if (chars.length > 0) lineWidth -= letterSpacing;
268
- widestLineWidth = Math.max(widestLineWidth, lineWidth);
304
+ measureSpan.textContent = line;
305
+ widestLineWidth = Math.max(widestLineWidth, measureSpan.offsetWidth);
269
306
  }
307
+ document.body.removeChild(measureSpan);
308
+ document.body.removeChild(container);
270
309
  return Math.min(widestLineWidth + paddingLeft + paddingRight, maxWidth);
271
310
  }
272
311
  function TextElement({ segment, scale = 1 }) {
@@ -281,10 +320,10 @@ function TextElement({ segment, scale = 1 }) {
281
320
  const strokeColor = segment.strokeColor;
282
321
  const strokeWidth = (segment.strokeWidth ?? TEXT_DEFAULTS.strokeWidth) * scale;
283
322
  const uniformPadding = (segment.padding ?? TEXT_DEFAULTS.padding) * scale;
284
- const paddingTop = segment.paddingTop ?? uniformPadding;
285
- const paddingRight = segment.paddingRight ?? uniformPadding;
286
- const paddingBottom = segment.paddingBottom ?? uniformPadding;
287
- const paddingLeft = segment.paddingLeft ?? uniformPadding;
323
+ const paddingTop = segment.paddingTop !== void 0 ? segment.paddingTop * scale : uniformPadding;
324
+ const paddingRight = segment.paddingRight !== void 0 ? segment.paddingRight * scale : uniformPadding;
325
+ const paddingBottom = segment.paddingBottom !== void 0 ? segment.paddingBottom * scale : uniformPadding;
326
+ const paddingLeft = segment.paddingLeft !== void 0 ? segment.paddingLeft * scale : uniformPadding;
288
327
  const x = segment.xOffset * scale;
289
328
  const y = segment.yOffset * scale;
290
329
  const width = segment.width * scale;
@@ -306,9 +345,10 @@ function TextElement({ segment, scale = 1 }) {
306
345
  fontSize,
307
346
  fontWeight,
308
347
  fontFamily,
309
- letterSpacing
348
+ letterSpacing,
349
+ lineHeight
310
350
  });
311
- }, [autoWidth, segment.text, width, paddingLeft, paddingRight, fontSize, fontWeight, fontFamily, letterSpacing]);
351
+ }, [autoWidth, segment.text, width, paddingLeft, paddingRight, fontSize, fontWeight, fontFamily, letterSpacing, lineHeight]);
312
352
  const borderRadiusStyle = useMemo(() => {
313
353
  if (!backgroundBorderRadius) return void 0;
314
354
  const radii = getBorderRadii(backgroundBorderRadius);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ugcinc-render",
3
- "version": "1.5.1",
3
+ "version": "1.5.3",
4
4
  "description": "Unified rendering package for UGC Inc - shared types, components, and compositions for pixel-perfect client/server rendering",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",