ugcinc-render 1.5.2 → 1.5.4

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 }) {
@@ -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);
@@ -477,6 +517,34 @@ function TextElement({ segment, scale = 1 }) {
477
517
  autoWidth,
478
518
  verticalAlign
479
519
  ]);
520
+ if (autoWidth && backgroundColor) {
521
+ const bgOffsetX = boxAlign === "center" ? (width - calculatedWidth) / 2 : boxAlign === "right" ? width - calculatedWidth : 0;
522
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: positioningContainerStyle, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { position: "relative", width }, children: [
523
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
524
+ "div",
525
+ {
526
+ style: {
527
+ position: "absolute",
528
+ left: bgOffsetX,
529
+ top: 0,
530
+ width: calculatedWidth,
531
+ height: "100%",
532
+ backgroundColor: hexToRgba(backgroundColor, backgroundOpacity),
533
+ borderRadius: borderRadiusStyle,
534
+ pointerEvents: "none"
535
+ }
536
+ }
537
+ ),
538
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: textStyle, children: segment.text })
539
+ ] }) });
540
+ }
541
+ if (autoWidth) {
542
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: positioningContainerStyle, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { width: calculatedWidth, maxWidth: width }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: {
543
+ ...textStyle,
544
+ width,
545
+ boxSizing: "border-box"
546
+ }, children: segment.text }) }) });
547
+ }
480
548
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: positioningContainerStyle, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: backgroundBoxStyle, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: textStyle, children: segment.text }) }) });
481
549
  }
482
550
 
package/dist/index.mjs CHANGED
@@ -219,7 +219,7 @@ function hexToRgba(hex, opacity = 100) {
219
219
  }
220
220
 
221
221
  // src/components/TextElement.tsx
222
- import { jsx } from "react/jsx-runtime";
222
+ import { jsx, jsxs } from "react/jsx-runtime";
223
223
  function calculateAutoWidth({
224
224
  text,
225
225
  maxWidth,
@@ -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 }) {
@@ -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);
@@ -403,6 +443,34 @@ function TextElement({ segment, scale = 1 }) {
403
443
  autoWidth,
404
444
  verticalAlign
405
445
  ]);
446
+ if (autoWidth && backgroundColor) {
447
+ const bgOffsetX = boxAlign === "center" ? (width - calculatedWidth) / 2 : boxAlign === "right" ? width - calculatedWidth : 0;
448
+ return /* @__PURE__ */ jsx("div", { style: positioningContainerStyle, children: /* @__PURE__ */ jsxs("div", { style: { position: "relative", width }, children: [
449
+ /* @__PURE__ */ jsx(
450
+ "div",
451
+ {
452
+ style: {
453
+ position: "absolute",
454
+ left: bgOffsetX,
455
+ top: 0,
456
+ width: calculatedWidth,
457
+ height: "100%",
458
+ backgroundColor: hexToRgba(backgroundColor, backgroundOpacity),
459
+ borderRadius: borderRadiusStyle,
460
+ pointerEvents: "none"
461
+ }
462
+ }
463
+ ),
464
+ /* @__PURE__ */ jsx("div", { style: textStyle, children: segment.text })
465
+ ] }) });
466
+ }
467
+ if (autoWidth) {
468
+ return /* @__PURE__ */ jsx("div", { style: positioningContainerStyle, children: /* @__PURE__ */ jsx("div", { style: { width: calculatedWidth, maxWidth: width }, children: /* @__PURE__ */ jsx("div", { style: {
469
+ ...textStyle,
470
+ width,
471
+ boxSizing: "border-box"
472
+ }, children: segment.text }) }) });
473
+ }
406
474
  return /* @__PURE__ */ jsx("div", { style: positioningContainerStyle, children: /* @__PURE__ */ jsx("div", { style: backgroundBoxStyle, children: /* @__PURE__ */ jsx("div", { style: textStyle, children: segment.text }) }) });
407
475
  }
408
476
 
@@ -910,7 +978,7 @@ function isDynamicCropEnabled(dynamicCrop) {
910
978
  }
911
979
 
912
980
  // src/compositions/ImageEditorComposition.tsx
913
- import { jsx as jsx3, jsxs } from "react/jsx-runtime";
981
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
914
982
  function getSortedSegments(config) {
915
983
  const allSegments = [];
916
984
  for (const channel of config.channels) {
@@ -1046,7 +1114,7 @@ function ImageEditorComposition({
1046
1114
  return void 0;
1047
1115
  };
1048
1116
  const containerBgColor = backgroundType === "color" && backgroundColor ? backgroundColor : "#000000";
1049
- return /* @__PURE__ */ jsx3(AbsoluteFill, { style: { backgroundColor: containerBgColor }, children: /* @__PURE__ */ jsxs(
1117
+ return /* @__PURE__ */ jsx3(AbsoluteFill, { style: { backgroundColor: containerBgColor }, children: /* @__PURE__ */ jsxs2(
1050
1118
  "div",
1051
1119
  {
1052
1120
  style: {
@@ -1220,7 +1288,7 @@ function VideoElement({
1220
1288
  }
1221
1289
 
1222
1290
  // src/compositions/VideoEditorComposition.tsx
1223
- import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
1291
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
1224
1292
  function calculateSegmentTimings(config, fps) {
1225
1293
  const timings = [];
1226
1294
  const timingsMap = /* @__PURE__ */ new Map();
@@ -1324,7 +1392,7 @@ function VideoEditorComposition({
1324
1392
  const audioTimings = useMemo5(() => {
1325
1393
  return segmentTimings.filter(({ segment }) => segment.type === "audio");
1326
1394
  }, [segmentTimings]);
1327
- return /* @__PURE__ */ jsxs2(AbsoluteFill2, { style: { backgroundColor: "#000000" }, children: [
1395
+ return /* @__PURE__ */ jsxs3(AbsoluteFill2, { style: { backgroundColor: "#000000" }, children: [
1328
1396
  activeVisualSegments.map(({ segment, startFrame, durationInFrames: durationInFrames2 }) => {
1329
1397
  if (segment.type === "text") {
1330
1398
  const textSegment = segment;
@@ -1640,7 +1708,7 @@ function useResolvedPositions(elements, textValues) {
1640
1708
 
1641
1709
  // src/Root.tsx
1642
1710
  import { Composition } from "remotion";
1643
- import { Fragment, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
1711
+ import { Fragment, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
1644
1712
  var defaultImageProps = {
1645
1713
  config: {
1646
1714
  width: 1080,
@@ -1664,7 +1732,7 @@ var defaultVideoProps = {
1664
1732
  var ImageComp = ImageEditorComposition;
1665
1733
  var VideoComp = VideoEditorComposition;
1666
1734
  var RenderRoot = () => {
1667
- return /* @__PURE__ */ jsxs3(Fragment, { children: [
1735
+ return /* @__PURE__ */ jsxs4(Fragment, { children: [
1668
1736
  /* @__PURE__ */ jsx6(
1669
1737
  Composition,
1670
1738
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ugcinc-render",
3
- "version": "1.5.2",
3
+ "version": "1.5.4",
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",