ugcinc-render 1.5.15 → 1.5.17

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.d.mts CHANGED
@@ -955,9 +955,14 @@ declare function VideoElement({ segment, src, startFrame, durationInFrames, scal
955
955
  * Font utilities for the rendering system
956
956
  */
957
957
 
958
+ /**
959
+ * Apple Color Emoji font name - used as fallback for emoji characters
960
+ */
961
+ declare const APPLE_EMOJI_FONT = "\"Apple Color Emoji\"";
958
962
  /**
959
963
  * Font family CSS strings for each font type
960
964
  * These match the fonts registered in the rendering system
965
+ * Apple Color Emoji is added as a fallback for consistent emoji rendering
961
966
  */
962
967
  declare const FONT_FAMILIES: Record<FontType, string>;
963
968
  /**
@@ -984,6 +989,9 @@ declare const FONT_URLS: {
984
989
  apple: {
985
990
  regular: string;
986
991
  };
992
+ emoji: {
993
+ apple: string;
994
+ };
987
995
  };
988
996
  /**
989
997
  * Preload fonts for rendering
@@ -994,6 +1002,10 @@ declare function preloadFonts(): Promise<void>;
994
1002
  * Check if fonts are loaded
995
1003
  */
996
1004
  declare function areFontsLoaded(): boolean;
1005
+ /**
1006
+ * Debug utility to log current font status
1007
+ */
1008
+ declare function debugFontStatus(): void;
997
1009
 
998
1010
  /**
999
1011
  * Fit calculation utilities for media positioning
@@ -1290,34 +1302,6 @@ declare function generateSegmentId(): string;
1290
1302
  */
1291
1303
  declare function generateOverlayId(): string;
1292
1304
 
1293
- /**
1294
- * Emoji utilities for rendering Apple-style emojis consistently
1295
- * across all platforms using CDN-hosted emoji images.
1296
- */
1297
- /**
1298
- * Convert an emoji character to its unified unicode codepoint format
1299
- * Example: "😀" -> "1f600", "👨‍👩‍👧" -> "1f468-200d-1f469-200d-1f467"
1300
- */
1301
- declare function emojiToUnified(emoji: string): string;
1302
- /**
1303
- * Get the Apple emoji CDN URL for a given emoji character
1304
- */
1305
- declare function getAppleEmojiUrl(emoji: string): string;
1306
- /**
1307
- * Check if a string contains any emoji characters
1308
- */
1309
- declare function hasEmoji(text: string): boolean;
1310
- /**
1311
- * Split text into segments of text and emojis
1312
- * Returns an array of segments with type 'text' or 'emoji'
1313
- */
1314
- interface TextSegmentPart {
1315
- type: 'text' | 'emoji';
1316
- content: string;
1317
- imageUrl?: string;
1318
- }
1319
- declare function splitTextAndEmojis(text: string): TextSegmentPart[];
1320
-
1321
1305
  /**
1322
1306
  * Hook exports for ugcinc-render
1323
1307
  *
@@ -1377,4 +1361,4 @@ declare function useResolvedPositions(elements: ImageEditorElement[], textValues
1377
1361
 
1378
1362
  declare const RenderRoot: React.FC;
1379
1363
 
1380
- export { type AudioSegment, type BaseEditorConfig, type BaseSegment, type BorderRadiusConfig, type Channel, type CropAxisConfig, type CropBoundary, type CropBounds, DIMENSION_PRESETS, type DimensionPreset, type DimensionPresetKey, type DynamicCropConfig, type EditorConfig, type EditorSegment, FONT_FAMILIES, FONT_URLS, type FitDimensions, type FitMode, type FontType, type FontWeight, type HorizontalAnchor, type HorizontalSelfAnchor, type Hyphenation, IMAGE_DEFAULTS, ImageEditorComposition, type ImageEditorCompositionProps, type ImageEditorConfig, type ImageEditorElement, type ImageEditorNodeConfig, ImageElement, type ImageElementProps, type ImageSegment, type PictureSegment, type PositionResolutionError, type PositionResolutionResult, type RelativePositionConfigX, type RelativePositionConfigY, RenderRoot, type Segment, type SegmentTimelinePosition, type SegmentType, type StaticSegment, TEXT_DEFAULTS, type TextAlignment, type TextDirection, TextElement, type TextElementProps, type TextOverflow, type TextSegment, type TextSegmentPart, type TextWrap, type TimeMode, type TimeValue, VIDEO_DEFAULTS, VISUAL_DEFAULTS, type VerticalAlignment, type VerticalAnchor, type VerticalSelfAnchor, type VideoEditorAudioSegment, type VideoEditorBaseSegment, type VideoEditorChannel, VideoEditorComposition, type VideoEditorCompositionProps, type VideoEditorConfig, type VideoEditorImageSegment, type VideoEditorNodeConfig, type VideoEditorSegment, type VideoEditorTextSegment, type VideoEditorVideoSegment, type VideoEditorVisualSegment, VideoElement, type VideoElementProps, type VideoSegment, type VisualSegment, type VisualSegmentUnion, type WordBreak, applyImageDefaults, applyTextDefaults, applyVideoDefaults, areFontsLoaded, buildFontString, calculateAutoWidthDimensions, calculateCropBounds, calculateEstimatedDuration, calculateFitDimensions, calculateLineWidth, calculateTimelineContentEnd, canSetAsReference, defaultOffset, emojiToUnified, formatTime, generateOverlayId, generateSegmentId, getAppleEmojiUrl, getBaseSegments, getBorderRadii, getDependentElements, getFontFamily, getOverlays, getReferenceElementX, getReferenceElementY, getSegmentTimelinePosition, hasEmoji, hexToRgba, isDynamicCropEnabled, isSegmentVisibleAtTime, parseHexColor, parseTime, preloadFonts, resolveElementPositions, splitTextAndEmojis, useFontsLoaded, useImageLoader, useImagePreloader, useResolvedPositions, wrapText };
1364
+ export { APPLE_EMOJI_FONT, type AudioSegment, type BaseEditorConfig, type BaseSegment, type BorderRadiusConfig, type Channel, type CropAxisConfig, type CropBoundary, type CropBounds, DIMENSION_PRESETS, type DimensionPreset, type DimensionPresetKey, type DynamicCropConfig, type EditorConfig, type EditorSegment, FONT_FAMILIES, FONT_URLS, type FitDimensions, type FitMode, type FontType, type FontWeight, type HorizontalAnchor, type HorizontalSelfAnchor, type Hyphenation, IMAGE_DEFAULTS, ImageEditorComposition, type ImageEditorCompositionProps, type ImageEditorConfig, type ImageEditorElement, type ImageEditorNodeConfig, ImageElement, type ImageElementProps, type ImageSegment, type PictureSegment, type PositionResolutionError, type PositionResolutionResult, type RelativePositionConfigX, type RelativePositionConfigY, RenderRoot, type Segment, type SegmentTimelinePosition, type SegmentType, type StaticSegment, TEXT_DEFAULTS, type TextAlignment, type TextDirection, TextElement, type TextElementProps, type TextOverflow, type TextSegment, type TextWrap, type TimeMode, type TimeValue, VIDEO_DEFAULTS, VISUAL_DEFAULTS, type VerticalAlignment, type VerticalAnchor, type VerticalSelfAnchor, type VideoEditorAudioSegment, type VideoEditorBaseSegment, type VideoEditorChannel, VideoEditorComposition, type VideoEditorCompositionProps, type VideoEditorConfig, type VideoEditorImageSegment, type VideoEditorNodeConfig, type VideoEditorSegment, type VideoEditorTextSegment, type VideoEditorVideoSegment, type VideoEditorVisualSegment, VideoElement, type VideoElementProps, type VideoSegment, type VisualSegment, type VisualSegmentUnion, type WordBreak, applyImageDefaults, applyTextDefaults, applyVideoDefaults, areFontsLoaded, buildFontString, calculateAutoWidthDimensions, calculateCropBounds, calculateEstimatedDuration, calculateFitDimensions, calculateLineWidth, calculateTimelineContentEnd, canSetAsReference, debugFontStatus, defaultOffset, formatTime, generateOverlayId, generateSegmentId, getBaseSegments, getBorderRadii, getDependentElements, getFontFamily, getOverlays, getReferenceElementX, getReferenceElementY, getSegmentTimelinePosition, hexToRgba, isDynamicCropEnabled, isSegmentVisibleAtTime, parseHexColor, parseTime, preloadFonts, resolveElementPositions, useFontsLoaded, useImageLoader, useImagePreloader, useResolvedPositions, wrapText };
package/dist/index.d.ts CHANGED
@@ -955,9 +955,14 @@ declare function VideoElement({ segment, src, startFrame, durationInFrames, scal
955
955
  * Font utilities for the rendering system
956
956
  */
957
957
 
958
+ /**
959
+ * Apple Color Emoji font name - used as fallback for emoji characters
960
+ */
961
+ declare const APPLE_EMOJI_FONT = "\"Apple Color Emoji\"";
958
962
  /**
959
963
  * Font family CSS strings for each font type
960
964
  * These match the fonts registered in the rendering system
965
+ * Apple Color Emoji is added as a fallback for consistent emoji rendering
961
966
  */
962
967
  declare const FONT_FAMILIES: Record<FontType, string>;
963
968
  /**
@@ -984,6 +989,9 @@ declare const FONT_URLS: {
984
989
  apple: {
985
990
  regular: string;
986
991
  };
992
+ emoji: {
993
+ apple: string;
994
+ };
987
995
  };
988
996
  /**
989
997
  * Preload fonts for rendering
@@ -994,6 +1002,10 @@ declare function preloadFonts(): Promise<void>;
994
1002
  * Check if fonts are loaded
995
1003
  */
996
1004
  declare function areFontsLoaded(): boolean;
1005
+ /**
1006
+ * Debug utility to log current font status
1007
+ */
1008
+ declare function debugFontStatus(): void;
997
1009
 
998
1010
  /**
999
1011
  * Fit calculation utilities for media positioning
@@ -1290,34 +1302,6 @@ declare function generateSegmentId(): string;
1290
1302
  */
1291
1303
  declare function generateOverlayId(): string;
1292
1304
 
1293
- /**
1294
- * Emoji utilities for rendering Apple-style emojis consistently
1295
- * across all platforms using CDN-hosted emoji images.
1296
- */
1297
- /**
1298
- * Convert an emoji character to its unified unicode codepoint format
1299
- * Example: "😀" -> "1f600", "👨‍👩‍👧" -> "1f468-200d-1f469-200d-1f467"
1300
- */
1301
- declare function emojiToUnified(emoji: string): string;
1302
- /**
1303
- * Get the Apple emoji CDN URL for a given emoji character
1304
- */
1305
- declare function getAppleEmojiUrl(emoji: string): string;
1306
- /**
1307
- * Check if a string contains any emoji characters
1308
- */
1309
- declare function hasEmoji(text: string): boolean;
1310
- /**
1311
- * Split text into segments of text and emojis
1312
- * Returns an array of segments with type 'text' or 'emoji'
1313
- */
1314
- interface TextSegmentPart {
1315
- type: 'text' | 'emoji';
1316
- content: string;
1317
- imageUrl?: string;
1318
- }
1319
- declare function splitTextAndEmojis(text: string): TextSegmentPart[];
1320
-
1321
1305
  /**
1322
1306
  * Hook exports for ugcinc-render
1323
1307
  *
@@ -1377,4 +1361,4 @@ declare function useResolvedPositions(elements: ImageEditorElement[], textValues
1377
1361
 
1378
1362
  declare const RenderRoot: React.FC;
1379
1363
 
1380
- export { type AudioSegment, type BaseEditorConfig, type BaseSegment, type BorderRadiusConfig, type Channel, type CropAxisConfig, type CropBoundary, type CropBounds, DIMENSION_PRESETS, type DimensionPreset, type DimensionPresetKey, type DynamicCropConfig, type EditorConfig, type EditorSegment, FONT_FAMILIES, FONT_URLS, type FitDimensions, type FitMode, type FontType, type FontWeight, type HorizontalAnchor, type HorizontalSelfAnchor, type Hyphenation, IMAGE_DEFAULTS, ImageEditorComposition, type ImageEditorCompositionProps, type ImageEditorConfig, type ImageEditorElement, type ImageEditorNodeConfig, ImageElement, type ImageElementProps, type ImageSegment, type PictureSegment, type PositionResolutionError, type PositionResolutionResult, type RelativePositionConfigX, type RelativePositionConfigY, RenderRoot, type Segment, type SegmentTimelinePosition, type SegmentType, type StaticSegment, TEXT_DEFAULTS, type TextAlignment, type TextDirection, TextElement, type TextElementProps, type TextOverflow, type TextSegment, type TextSegmentPart, type TextWrap, type TimeMode, type TimeValue, VIDEO_DEFAULTS, VISUAL_DEFAULTS, type VerticalAlignment, type VerticalAnchor, type VerticalSelfAnchor, type VideoEditorAudioSegment, type VideoEditorBaseSegment, type VideoEditorChannel, VideoEditorComposition, type VideoEditorCompositionProps, type VideoEditorConfig, type VideoEditorImageSegment, type VideoEditorNodeConfig, type VideoEditorSegment, type VideoEditorTextSegment, type VideoEditorVideoSegment, type VideoEditorVisualSegment, VideoElement, type VideoElementProps, type VideoSegment, type VisualSegment, type VisualSegmentUnion, type WordBreak, applyImageDefaults, applyTextDefaults, applyVideoDefaults, areFontsLoaded, buildFontString, calculateAutoWidthDimensions, calculateCropBounds, calculateEstimatedDuration, calculateFitDimensions, calculateLineWidth, calculateTimelineContentEnd, canSetAsReference, defaultOffset, emojiToUnified, formatTime, generateOverlayId, generateSegmentId, getAppleEmojiUrl, getBaseSegments, getBorderRadii, getDependentElements, getFontFamily, getOverlays, getReferenceElementX, getReferenceElementY, getSegmentTimelinePosition, hasEmoji, hexToRgba, isDynamicCropEnabled, isSegmentVisibleAtTime, parseHexColor, parseTime, preloadFonts, resolveElementPositions, splitTextAndEmojis, useFontsLoaded, useImageLoader, useImagePreloader, useResolvedPositions, wrapText };
1364
+ export { APPLE_EMOJI_FONT, type AudioSegment, type BaseEditorConfig, type BaseSegment, type BorderRadiusConfig, type Channel, type CropAxisConfig, type CropBoundary, type CropBounds, DIMENSION_PRESETS, type DimensionPreset, type DimensionPresetKey, type DynamicCropConfig, type EditorConfig, type EditorSegment, FONT_FAMILIES, FONT_URLS, type FitDimensions, type FitMode, type FontType, type FontWeight, type HorizontalAnchor, type HorizontalSelfAnchor, type Hyphenation, IMAGE_DEFAULTS, ImageEditorComposition, type ImageEditorCompositionProps, type ImageEditorConfig, type ImageEditorElement, type ImageEditorNodeConfig, ImageElement, type ImageElementProps, type ImageSegment, type PictureSegment, type PositionResolutionError, type PositionResolutionResult, type RelativePositionConfigX, type RelativePositionConfigY, RenderRoot, type Segment, type SegmentTimelinePosition, type SegmentType, type StaticSegment, TEXT_DEFAULTS, type TextAlignment, type TextDirection, TextElement, type TextElementProps, type TextOverflow, type TextSegment, type TextWrap, type TimeMode, type TimeValue, VIDEO_DEFAULTS, VISUAL_DEFAULTS, type VerticalAlignment, type VerticalAnchor, type VerticalSelfAnchor, type VideoEditorAudioSegment, type VideoEditorBaseSegment, type VideoEditorChannel, VideoEditorComposition, type VideoEditorCompositionProps, type VideoEditorConfig, type VideoEditorImageSegment, type VideoEditorNodeConfig, type VideoEditorSegment, type VideoEditorTextSegment, type VideoEditorVideoSegment, type VideoEditorVisualSegment, VideoElement, type VideoElementProps, type VideoSegment, type VisualSegment, type VisualSegmentUnion, type WordBreak, applyImageDefaults, applyTextDefaults, applyVideoDefaults, areFontsLoaded, buildFontString, calculateAutoWidthDimensions, calculateCropBounds, calculateEstimatedDuration, calculateFitDimensions, calculateLineWidth, calculateTimelineContentEnd, canSetAsReference, debugFontStatus, defaultOffset, formatTime, generateOverlayId, generateSegmentId, getBaseSegments, getBorderRadii, getDependentElements, getFontFamily, getOverlays, getReferenceElementX, getReferenceElementY, getSegmentTimelinePosition, hexToRgba, isDynamicCropEnabled, isSegmentVisibleAtTime, parseHexColor, parseTime, preloadFonts, resolveElementPositions, useFontsLoaded, useImageLoader, useImagePreloader, useResolvedPositions, wrapText };
package/dist/index.js CHANGED
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ APPLE_EMOJI_FONT: () => APPLE_EMOJI_FONT,
33
34
  DIMENSION_PRESETS: () => DIMENSION_PRESETS,
34
35
  FONT_FAMILIES: () => FONT_FAMILIES,
35
36
  FONT_URLS: () => FONT_URLS,
@@ -55,12 +56,11 @@ __export(index_exports, {
55
56
  calculateLineWidth: () => calculateLineWidth,
56
57
  calculateTimelineContentEnd: () => calculateTimelineContentEnd,
57
58
  canSetAsReference: () => canSetAsReference,
59
+ debugFontStatus: () => debugFontStatus,
58
60
  defaultOffset: () => defaultOffset,
59
- emojiToUnified: () => emojiToUnified,
60
61
  formatTime: () => formatTime,
61
62
  generateOverlayId: () => generateOverlayId,
62
63
  generateSegmentId: () => generateSegmentId,
63
- getAppleEmojiUrl: () => getAppleEmojiUrl,
64
64
  getBaseSegments: () => getBaseSegments,
65
65
  getBorderRadii: () => getBorderRadii,
66
66
  getDependentElements: () => getDependentElements,
@@ -69,7 +69,6 @@ __export(index_exports, {
69
69
  getReferenceElementX: () => getReferenceElementX,
70
70
  getReferenceElementY: () => getReferenceElementY,
71
71
  getSegmentTimelinePosition: () => getSegmentTimelinePosition,
72
- hasEmoji: () => hasEmoji,
73
72
  hexToRgba: () => hexToRgba,
74
73
  isDynamicCropEnabled: () => isDynamicCropEnabled,
75
74
  isSegmentVisibleAtTime: () => isSegmentVisibleAtTime,
@@ -77,7 +76,6 @@ __export(index_exports, {
77
76
  parseTime: () => parseTime,
78
77
  preloadFonts: () => preloadFonts,
79
78
  resolveElementPositions: () => resolveElementPositions,
80
- splitTextAndEmojis: () => splitTextAndEmojis,
81
79
  useFontsLoaded: () => useFontsLoaded,
82
80
  useImageLoader: () => useImageLoader,
83
81
  useImagePreloader: () => useImagePreloader,
@@ -163,10 +161,11 @@ function applyVideoDefaults(segment) {
163
161
  }
164
162
 
165
163
  // src/utils/fonts.ts
164
+ var APPLE_EMOJI_FONT = '"Apple Color Emoji"';
166
165
  var FONT_FAMILIES = {
167
- tiktok: '"TikTok Sans", sans-serif',
168
- apple: '"SF Pro", "SF Pro Display", -apple-system, BlinkMacSystemFont, sans-serif',
169
- arial: "Arial, sans-serif"
166
+ tiktok: `"TikTok Sans", ${APPLE_EMOJI_FONT}, sans-serif`,
167
+ apple: `"SF Pro", "SF Pro Display", -apple-system, BlinkMacSystemFont, ${APPLE_EMOJI_FONT}, sans-serif`,
168
+ arial: `Arial, ${APPLE_EMOJI_FONT}, sans-serif`
170
169
  };
171
170
  function getFontFamily(fontType) {
172
171
  return FONT_FAMILIES[fontType] ?? FONT_FAMILIES.arial;
@@ -186,40 +185,123 @@ var FONT_URLS = {
186
185
  },
187
186
  apple: {
188
187
  regular: "/SF-Pro.ttf"
188
+ },
189
+ emoji: {
190
+ apple: "/AppleColorEmoji.ttf"
189
191
  }
190
192
  };
191
193
  async function preloadFonts() {
194
+ console.log("[ugcinc-render/fonts] preloadFonts() called");
192
195
  if (typeof document !== "undefined") {
193
- const fontPromises = [];
194
- const tiktokRegular = new FontFace(
195
- "TikTok Sans",
196
- `url(${FONT_URLS.tiktok.regular})`,
197
- { weight: "normal" }
198
- );
199
- fontPromises.push(tiktokRegular.load());
200
- const tiktokBold = new FontFace(
201
- "TikTok Sans",
202
- `url(${FONT_URLS.tiktok.bold})`,
203
- { weight: "bold" }
204
- );
205
- fontPromises.push(tiktokBold.load());
206
- const sfPro = new FontFace(
207
- "SF Pro",
208
- `url(${FONT_URLS.apple.regular})`,
209
- { weight: "normal" }
210
- );
211
- fontPromises.push(sfPro.load());
212
- const loadedFonts = await Promise.all(fontPromises);
213
- loadedFonts.forEach((font) => {
214
- document.fonts.add(font);
196
+ console.log("[ugcinc-render/fonts] Browser environment detected, loading fonts...");
197
+ const fontResults = [];
198
+ try {
199
+ const tiktokRegular = new FontFace(
200
+ "TikTok Sans",
201
+ `url(${FONT_URLS.tiktok.regular})`,
202
+ { weight: "normal" }
203
+ );
204
+ console.log(`[ugcinc-render/fonts] Loading TikTok Sans Regular from: ${FONT_URLS.tiktok.regular}`);
205
+ await tiktokRegular.load();
206
+ document.fonts.add(tiktokRegular);
207
+ fontResults.push({ name: "TikTok Sans Regular", status: "success" });
208
+ } catch (err) {
209
+ const errorMsg = err instanceof Error ? err.message : String(err);
210
+ console.error(`[ugcinc-render/fonts] Failed to load TikTok Sans Regular:`, errorMsg);
211
+ fontResults.push({ name: "TikTok Sans Regular", status: "error", error: errorMsg });
212
+ }
213
+ try {
214
+ const tiktokBold = new FontFace(
215
+ "TikTok Sans",
216
+ `url(${FONT_URLS.tiktok.bold})`,
217
+ { weight: "bold" }
218
+ );
219
+ console.log(`[ugcinc-render/fonts] Loading TikTok Sans Bold from: ${FONT_URLS.tiktok.bold}`);
220
+ await tiktokBold.load();
221
+ document.fonts.add(tiktokBold);
222
+ fontResults.push({ name: "TikTok Sans Bold", status: "success" });
223
+ } catch (err) {
224
+ const errorMsg = err instanceof Error ? err.message : String(err);
225
+ console.error(`[ugcinc-render/fonts] Failed to load TikTok Sans Bold:`, errorMsg);
226
+ fontResults.push({ name: "TikTok Sans Bold", status: "error", error: errorMsg });
227
+ }
228
+ try {
229
+ const sfPro = new FontFace(
230
+ "SF Pro",
231
+ `url(${FONT_URLS.apple.regular})`,
232
+ { weight: "normal" }
233
+ );
234
+ console.log(`[ugcinc-render/fonts] Loading SF Pro from: ${FONT_URLS.apple.regular}`);
235
+ await sfPro.load();
236
+ document.fonts.add(sfPro);
237
+ fontResults.push({ name: "SF Pro", status: "success" });
238
+ } catch (err) {
239
+ const errorMsg = err instanceof Error ? err.message : String(err);
240
+ console.error(`[ugcinc-render/fonts] Failed to load SF Pro:`, errorMsg);
241
+ fontResults.push({ name: "SF Pro", status: "error", error: errorMsg });
242
+ }
243
+ try {
244
+ const appleEmoji = new FontFace(
245
+ "Apple Color Emoji",
246
+ `url(${FONT_URLS.emoji.apple})`,
247
+ { weight: "normal" }
248
+ );
249
+ console.log(`[ugcinc-render/fonts] Loading Apple Color Emoji from: ${FONT_URLS.emoji.apple}`);
250
+ await appleEmoji.load();
251
+ document.fonts.add(appleEmoji);
252
+ fontResults.push({ name: "Apple Color Emoji", status: "success" });
253
+ } catch (err) {
254
+ const errorMsg = err instanceof Error ? err.message : String(err);
255
+ console.error(`[ugcinc-render/fonts] Failed to load Apple Color Emoji:`, errorMsg);
256
+ fontResults.push({ name: "Apple Color Emoji", status: "error", error: errorMsg });
257
+ }
258
+ console.log("[ugcinc-render/fonts] Font loading complete. Results:", fontResults);
259
+ const availableFonts = [];
260
+ document.fonts.forEach((font) => {
261
+ availableFonts.push(`${font.family} (${font.weight}, ${font.status})`);
215
262
  });
263
+ console.log("[ugcinc-render/fonts] Available fonts in document:", availableFonts);
264
+ } else {
265
+ console.log("[ugcinc-render/fonts] Not in browser environment, skipping font loading");
216
266
  }
217
267
  }
218
268
  function areFontsLoaded() {
219
269
  if (typeof document === "undefined") {
270
+ console.log("[ugcinc-render/fonts] areFontsLoaded: Not in browser environment");
220
271
  return false;
221
272
  }
222
- return document.fonts.check('normal 16px "TikTok Sans"') && document.fonts.check('bold 16px "TikTok Sans"') && document.fonts.check('normal 16px "SF Pro"');
273
+ const tiktokRegular = document.fonts.check('normal 16px "TikTok Sans"');
274
+ const tiktokBold = document.fonts.check('bold 16px "TikTok Sans"');
275
+ const sfPro = document.fonts.check('normal 16px "SF Pro"');
276
+ const appleEmoji = document.fonts.check('normal 16px "Apple Color Emoji"');
277
+ console.log("[ugcinc-render/fonts] areFontsLoaded check:", {
278
+ tiktokRegular,
279
+ tiktokBold,
280
+ sfPro,
281
+ appleEmoji
282
+ });
283
+ return tiktokRegular && tiktokBold && sfPro && appleEmoji;
284
+ }
285
+ function debugFontStatus() {
286
+ console.log("[ugcinc-render/fonts] === FONT DEBUG STATUS ===");
287
+ if (typeof document === "undefined") {
288
+ console.log("[ugcinc-render/fonts] Not in browser environment");
289
+ return;
290
+ }
291
+ console.log("[ugcinc-render/fonts] Font checks:");
292
+ console.log(" - TikTok Sans (normal):", document.fonts.check('normal 16px "TikTok Sans"'));
293
+ console.log(" - TikTok Sans (bold):", document.fonts.check('bold 16px "TikTok Sans"'));
294
+ console.log(" - SF Pro:", document.fonts.check('normal 16px "SF Pro"'));
295
+ console.log(" - Apple Color Emoji:", document.fonts.check('normal 16px "Apple Color Emoji"'));
296
+ console.log("[ugcinc-render/fonts] All fonts in document.fonts:");
297
+ document.fonts.forEach((font) => {
298
+ console.log(` - ${font.family}: weight=${font.weight}, style=${font.style}, status=${font.status}`);
299
+ });
300
+ console.log("[ugcinc-render/fonts] Font family stacks:");
301
+ console.log(" - tiktok:", FONT_FAMILIES.tiktok);
302
+ console.log(" - apple:", FONT_FAMILIES.apple);
303
+ console.log(" - arial:", FONT_FAMILIES.arial);
304
+ console.log("[ugcinc-render/fonts] === END FONT DEBUG ===");
223
305
  }
224
306
 
225
307
  // src/utils/text.ts
@@ -306,66 +388,6 @@ function hexToRgba(hex, opacity = 100) {
306
388
  return `rgba(${r}, ${g}, ${b}, ${opacity / 100})`;
307
389
  }
308
390
 
309
- // src/utils/emoji.ts
310
- var APPLE_EMOJI_CDN_BASE = "https://cdn.jsdelivr.net/npm/emoji-datasource-apple/img/apple/64/";
311
- function emojiToUnified(emoji) {
312
- const codePoints = [];
313
- for (const codePoint of emoji) {
314
- const hex = codePoint.codePointAt(0)?.toString(16);
315
- if (hex) {
316
- codePoints.push(hex);
317
- }
318
- }
319
- return codePoints.join("-");
320
- }
321
- function getAppleEmojiUrl(emoji) {
322
- const unified = emojiToUnified(emoji);
323
- return `${APPLE_EMOJI_CDN_BASE}${unified}.png`;
324
- }
325
- var EMOJI_REGEX = /(?:\p{Emoji_Presentation}|\p{Emoji}\uFE0F)(?:\p{Emoji_Modifier}|\uFE0F|\u200D(?:\p{Emoji_Presentation}|\p{Emoji}\uFE0F)(?:\p{Emoji_Modifier})?)*|\p{Regional_Indicator}{2}/gu;
326
- function hasEmoji(text) {
327
- return EMOJI_REGEX.test(text);
328
- }
329
- function splitTextAndEmojis(text) {
330
- const segments = [];
331
- let lastIndex = 0;
332
- EMOJI_REGEX.lastIndex = 0;
333
- let match;
334
- while ((match = EMOJI_REGEX.exec(text)) !== null) {
335
- if (match.index > lastIndex) {
336
- segments.push({
337
- type: "text",
338
- content: text.slice(lastIndex, match.index)
339
- });
340
- }
341
- segments.push({
342
- type: "emoji",
343
- content: match[0],
344
- imageUrl: getAppleEmojiUrl(match[0])
345
- });
346
- lastIndex = match.index + match[0].length;
347
- }
348
- if (lastIndex < text.length) {
349
- segments.push({
350
- type: "text",
351
- content: text.slice(lastIndex)
352
- });
353
- }
354
- return segments;
355
- }
356
- function countEmojis(text) {
357
- EMOJI_REGEX.lastIndex = 0;
358
- const matches = text.match(EMOJI_REGEX);
359
- return matches ? matches.length : 0;
360
- }
361
- function getEmojiWidth(fontSize) {
362
- return fontSize * 1.2;
363
- }
364
- function stripEmojis(text) {
365
- EMOJI_REGEX.lastIndex = 0;
366
- return text.replace(EMOJI_REGEX, "");
367
- }
368
-
369
391
  // src/components/TextElement.tsx
370
392
  var import_jsx_runtime = require("react/jsx-runtime");
371
393
  function calculateAutoWidthAndLines({
@@ -398,17 +420,9 @@ function calculateAutoWidthAndLines({
398
420
  white-space: nowrap;
399
421
  `;
400
422
  document.body.appendChild(measureSpan);
401
- const emojiWidth = getEmojiWidth(fontSize);
402
- const measureTextWithEmojis = (textToMeasure) => {
403
- const emojiCount = countEmojis(textToMeasure);
404
- if (emojiCount === 0) {
405
- measureSpan.textContent = textToMeasure;
406
- return measureSpan.getBoundingClientRect().width;
407
- }
408
- const textWithoutEmojis = stripEmojis(textToMeasure);
409
- measureSpan.textContent = textWithoutEmojis;
410
- const textWidth = measureSpan.getBoundingClientRect().width;
411
- return textWidth + emojiCount * emojiWidth;
423
+ const measureText = (textToMeasure) => {
424
+ measureSpan.textContent = textToMeasure;
425
+ return measureSpan.getBoundingClientRect().width;
412
426
  };
413
427
  measureSpan.textContent = "M";
414
428
  const charWidth = measureSpan.getBoundingClientRect().width;
@@ -432,12 +446,12 @@ function calculateAutoWidthAndLines({
432
446
  continue;
433
447
  }
434
448
  const testLine = currentLine + word;
435
- const testWidth = measureTextWithEmojis(testLine);
449
+ const testWidth = measureText(testLine);
436
450
  const WRAP_TOLERANCE = 0.5;
437
451
  if (testWidth > availableWidth + WRAP_TOLERANCE && currentLine.trim()) {
438
452
  lines.push(currentLine.trimEnd());
439
453
  currentLine = word;
440
- const wordWidth = measureTextWithEmojis(word);
454
+ const wordWidth = measureText(word);
441
455
  if (wordWidth > availableWidth) {
442
456
  let remainingWord = word;
443
457
  while (remainingWord) {
@@ -471,7 +485,7 @@ function calculateAutoWidthAndLines({
471
485
  }
472
486
  let widestLineWidth = 0;
473
487
  for (const line of lines) {
474
- const lineWidth = measureTextWithEmojis(line);
488
+ const lineWidth = measureText(line);
475
489
  widestLineWidth = Math.max(widestLineWidth, lineWidth);
476
490
  }
477
491
  document.body.removeChild(measureSpan);
@@ -479,6 +493,15 @@ function calculateAutoWidthAndLines({
479
493
  return { width: calculatedWidth, lines };
480
494
  }
481
495
  function TextElement({ segment, scale = 1 }) {
496
+ import_react.default.useEffect(() => {
497
+ const fontFamily2 = getFontFamily(segment.fontType ?? TEXT_DEFAULTS.fontType);
498
+ console.log("[ugcinc-render/TextElement] Rendering with:", {
499
+ text: segment.text?.substring(0, 50) + (segment.text?.length > 50 ? "..." : ""),
500
+ fontType: segment.fontType ?? TEXT_DEFAULTS.fontType,
501
+ fontFamily: fontFamily2,
502
+ hasEmojis: /[\u{1F300}-\u{1F9FF}]/u.test(segment.text || "")
503
+ });
504
+ }, [segment.text, segment.fontType]);
482
505
  const fontType = segment.fontType ?? TEXT_DEFAULTS.fontType;
483
506
  const fontSize = (segment.fontSize ?? TEXT_DEFAULTS.fontSize) * scale;
484
507
  const fontWeight = segment.fontWeight ?? TEXT_DEFAULTS.fontWeight;
@@ -678,35 +701,9 @@ function TextElement({ segment, scale = 1 }) {
678
701
  autoWidth,
679
702
  verticalAlign
680
703
  ]);
681
- const renderTextWithEmojis = (text) => {
682
- if (!hasEmoji(text)) {
683
- return text;
684
- }
685
- const segments = splitTextAndEmojis(text);
686
- return segments.map((segment2, i) => {
687
- if (segment2.type === "emoji" && segment2.imageUrl) {
688
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
689
- "img",
690
- {
691
- src: segment2.imageUrl,
692
- alt: segment2.content,
693
- style: {
694
- height: "1.2em",
695
- width: "1.2em",
696
- verticalAlign: "-0.25em",
697
- display: "inline"
698
- },
699
- draggable: false
700
- },
701
- i
702
- );
703
- }
704
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.default.Fragment, { children: segment2.content }, i);
705
- });
706
- };
707
704
  if (autoWidth) {
708
705
  const textContent = calculatedLines.map((line, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.default.Fragment, { children: [
709
- renderTextWithEmojis(line),
706
+ line,
710
707
  index < calculatedLines.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("br", {})
711
708
  ] }, index));
712
709
  if (backgroundColor) {
@@ -725,7 +722,7 @@ function TextElement({ segment, scale = 1 }) {
725
722
  }
726
723
  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: textStyle, children: textContent }) }) });
727
724
  }
728
- 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: renderTextWithEmojis(segment.text) }) }) });
725
+ 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 }) }) });
729
726
  }
730
727
 
731
728
  // src/components/ImageElement.tsx
@@ -2019,6 +2016,7 @@ var RenderRoot = () => {
2019
2016
  };
2020
2017
  // Annotate the CommonJS export names for ESM import in node:
2021
2018
  0 && (module.exports = {
2019
+ APPLE_EMOJI_FONT,
2022
2020
  DIMENSION_PRESETS,
2023
2021
  FONT_FAMILIES,
2024
2022
  FONT_URLS,
@@ -2044,12 +2042,11 @@ var RenderRoot = () => {
2044
2042
  calculateLineWidth,
2045
2043
  calculateTimelineContentEnd,
2046
2044
  canSetAsReference,
2045
+ debugFontStatus,
2047
2046
  defaultOffset,
2048
- emojiToUnified,
2049
2047
  formatTime,
2050
2048
  generateOverlayId,
2051
2049
  generateSegmentId,
2052
- getAppleEmojiUrl,
2053
2050
  getBaseSegments,
2054
2051
  getBorderRadii,
2055
2052
  getDependentElements,
@@ -2058,7 +2055,6 @@ var RenderRoot = () => {
2058
2055
  getReferenceElementX,
2059
2056
  getReferenceElementY,
2060
2057
  getSegmentTimelinePosition,
2061
- hasEmoji,
2062
2058
  hexToRgba,
2063
2059
  isDynamicCropEnabled,
2064
2060
  isSegmentVisibleAtTime,
@@ -2066,7 +2062,6 @@ var RenderRoot = () => {
2066
2062
  parseTime,
2067
2063
  preloadFonts,
2068
2064
  resolveElementPositions,
2069
- splitTextAndEmojis,
2070
2065
  useFontsLoaded,
2071
2066
  useImageLoader,
2072
2067
  useImagePreloader,
package/dist/index.mjs CHANGED
@@ -75,10 +75,11 @@ function applyVideoDefaults(segment) {
75
75
  }
76
76
 
77
77
  // src/utils/fonts.ts
78
+ var APPLE_EMOJI_FONT = '"Apple Color Emoji"';
78
79
  var FONT_FAMILIES = {
79
- tiktok: '"TikTok Sans", sans-serif',
80
- apple: '"SF Pro", "SF Pro Display", -apple-system, BlinkMacSystemFont, sans-serif',
81
- arial: "Arial, sans-serif"
80
+ tiktok: `"TikTok Sans", ${APPLE_EMOJI_FONT}, sans-serif`,
81
+ apple: `"SF Pro", "SF Pro Display", -apple-system, BlinkMacSystemFont, ${APPLE_EMOJI_FONT}, sans-serif`,
82
+ arial: `Arial, ${APPLE_EMOJI_FONT}, sans-serif`
82
83
  };
83
84
  function getFontFamily(fontType) {
84
85
  return FONT_FAMILIES[fontType] ?? FONT_FAMILIES.arial;
@@ -98,40 +99,123 @@ var FONT_URLS = {
98
99
  },
99
100
  apple: {
100
101
  regular: "/SF-Pro.ttf"
102
+ },
103
+ emoji: {
104
+ apple: "/AppleColorEmoji.ttf"
101
105
  }
102
106
  };
103
107
  async function preloadFonts() {
108
+ console.log("[ugcinc-render/fonts] preloadFonts() called");
104
109
  if (typeof document !== "undefined") {
105
- const fontPromises = [];
106
- const tiktokRegular = new FontFace(
107
- "TikTok Sans",
108
- `url(${FONT_URLS.tiktok.regular})`,
109
- { weight: "normal" }
110
- );
111
- fontPromises.push(tiktokRegular.load());
112
- const tiktokBold = new FontFace(
113
- "TikTok Sans",
114
- `url(${FONT_URLS.tiktok.bold})`,
115
- { weight: "bold" }
116
- );
117
- fontPromises.push(tiktokBold.load());
118
- const sfPro = new FontFace(
119
- "SF Pro",
120
- `url(${FONT_URLS.apple.regular})`,
121
- { weight: "normal" }
122
- );
123
- fontPromises.push(sfPro.load());
124
- const loadedFonts = await Promise.all(fontPromises);
125
- loadedFonts.forEach((font) => {
126
- document.fonts.add(font);
110
+ console.log("[ugcinc-render/fonts] Browser environment detected, loading fonts...");
111
+ const fontResults = [];
112
+ try {
113
+ const tiktokRegular = new FontFace(
114
+ "TikTok Sans",
115
+ `url(${FONT_URLS.tiktok.regular})`,
116
+ { weight: "normal" }
117
+ );
118
+ console.log(`[ugcinc-render/fonts] Loading TikTok Sans Regular from: ${FONT_URLS.tiktok.regular}`);
119
+ await tiktokRegular.load();
120
+ document.fonts.add(tiktokRegular);
121
+ fontResults.push({ name: "TikTok Sans Regular", status: "success" });
122
+ } catch (err) {
123
+ const errorMsg = err instanceof Error ? err.message : String(err);
124
+ console.error(`[ugcinc-render/fonts] Failed to load TikTok Sans Regular:`, errorMsg);
125
+ fontResults.push({ name: "TikTok Sans Regular", status: "error", error: errorMsg });
126
+ }
127
+ try {
128
+ const tiktokBold = new FontFace(
129
+ "TikTok Sans",
130
+ `url(${FONT_URLS.tiktok.bold})`,
131
+ { weight: "bold" }
132
+ );
133
+ console.log(`[ugcinc-render/fonts] Loading TikTok Sans Bold from: ${FONT_URLS.tiktok.bold}`);
134
+ await tiktokBold.load();
135
+ document.fonts.add(tiktokBold);
136
+ fontResults.push({ name: "TikTok Sans Bold", status: "success" });
137
+ } catch (err) {
138
+ const errorMsg = err instanceof Error ? err.message : String(err);
139
+ console.error(`[ugcinc-render/fonts] Failed to load TikTok Sans Bold:`, errorMsg);
140
+ fontResults.push({ name: "TikTok Sans Bold", status: "error", error: errorMsg });
141
+ }
142
+ try {
143
+ const sfPro = new FontFace(
144
+ "SF Pro",
145
+ `url(${FONT_URLS.apple.regular})`,
146
+ { weight: "normal" }
147
+ );
148
+ console.log(`[ugcinc-render/fonts] Loading SF Pro from: ${FONT_URLS.apple.regular}`);
149
+ await sfPro.load();
150
+ document.fonts.add(sfPro);
151
+ fontResults.push({ name: "SF Pro", status: "success" });
152
+ } catch (err) {
153
+ const errorMsg = err instanceof Error ? err.message : String(err);
154
+ console.error(`[ugcinc-render/fonts] Failed to load SF Pro:`, errorMsg);
155
+ fontResults.push({ name: "SF Pro", status: "error", error: errorMsg });
156
+ }
157
+ try {
158
+ const appleEmoji = new FontFace(
159
+ "Apple Color Emoji",
160
+ `url(${FONT_URLS.emoji.apple})`,
161
+ { weight: "normal" }
162
+ );
163
+ console.log(`[ugcinc-render/fonts] Loading Apple Color Emoji from: ${FONT_URLS.emoji.apple}`);
164
+ await appleEmoji.load();
165
+ document.fonts.add(appleEmoji);
166
+ fontResults.push({ name: "Apple Color Emoji", status: "success" });
167
+ } catch (err) {
168
+ const errorMsg = err instanceof Error ? err.message : String(err);
169
+ console.error(`[ugcinc-render/fonts] Failed to load Apple Color Emoji:`, errorMsg);
170
+ fontResults.push({ name: "Apple Color Emoji", status: "error", error: errorMsg });
171
+ }
172
+ console.log("[ugcinc-render/fonts] Font loading complete. Results:", fontResults);
173
+ const availableFonts = [];
174
+ document.fonts.forEach((font) => {
175
+ availableFonts.push(`${font.family} (${font.weight}, ${font.status})`);
127
176
  });
177
+ console.log("[ugcinc-render/fonts] Available fonts in document:", availableFonts);
178
+ } else {
179
+ console.log("[ugcinc-render/fonts] Not in browser environment, skipping font loading");
128
180
  }
129
181
  }
130
182
  function areFontsLoaded() {
131
183
  if (typeof document === "undefined") {
184
+ console.log("[ugcinc-render/fonts] areFontsLoaded: Not in browser environment");
132
185
  return false;
133
186
  }
134
- return document.fonts.check('normal 16px "TikTok Sans"') && document.fonts.check('bold 16px "TikTok Sans"') && document.fonts.check('normal 16px "SF Pro"');
187
+ const tiktokRegular = document.fonts.check('normal 16px "TikTok Sans"');
188
+ const tiktokBold = document.fonts.check('bold 16px "TikTok Sans"');
189
+ const sfPro = document.fonts.check('normal 16px "SF Pro"');
190
+ const appleEmoji = document.fonts.check('normal 16px "Apple Color Emoji"');
191
+ console.log("[ugcinc-render/fonts] areFontsLoaded check:", {
192
+ tiktokRegular,
193
+ tiktokBold,
194
+ sfPro,
195
+ appleEmoji
196
+ });
197
+ return tiktokRegular && tiktokBold && sfPro && appleEmoji;
198
+ }
199
+ function debugFontStatus() {
200
+ console.log("[ugcinc-render/fonts] === FONT DEBUG STATUS ===");
201
+ if (typeof document === "undefined") {
202
+ console.log("[ugcinc-render/fonts] Not in browser environment");
203
+ return;
204
+ }
205
+ console.log("[ugcinc-render/fonts] Font checks:");
206
+ console.log(" - TikTok Sans (normal):", document.fonts.check('normal 16px "TikTok Sans"'));
207
+ console.log(" - TikTok Sans (bold):", document.fonts.check('bold 16px "TikTok Sans"'));
208
+ console.log(" - SF Pro:", document.fonts.check('normal 16px "SF Pro"'));
209
+ console.log(" - Apple Color Emoji:", document.fonts.check('normal 16px "Apple Color Emoji"'));
210
+ console.log("[ugcinc-render/fonts] All fonts in document.fonts:");
211
+ document.fonts.forEach((font) => {
212
+ console.log(` - ${font.family}: weight=${font.weight}, style=${font.style}, status=${font.status}`);
213
+ });
214
+ console.log("[ugcinc-render/fonts] Font family stacks:");
215
+ console.log(" - tiktok:", FONT_FAMILIES.tiktok);
216
+ console.log(" - apple:", FONT_FAMILIES.apple);
217
+ console.log(" - arial:", FONT_FAMILIES.arial);
218
+ console.log("[ugcinc-render/fonts] === END FONT DEBUG ===");
135
219
  }
136
220
 
137
221
  // src/utils/text.ts
@@ -218,66 +302,6 @@ function hexToRgba(hex, opacity = 100) {
218
302
  return `rgba(${r}, ${g}, ${b}, ${opacity / 100})`;
219
303
  }
220
304
 
221
- // src/utils/emoji.ts
222
- var APPLE_EMOJI_CDN_BASE = "https://cdn.jsdelivr.net/npm/emoji-datasource-apple/img/apple/64/";
223
- function emojiToUnified(emoji) {
224
- const codePoints = [];
225
- for (const codePoint of emoji) {
226
- const hex = codePoint.codePointAt(0)?.toString(16);
227
- if (hex) {
228
- codePoints.push(hex);
229
- }
230
- }
231
- return codePoints.join("-");
232
- }
233
- function getAppleEmojiUrl(emoji) {
234
- const unified = emojiToUnified(emoji);
235
- return `${APPLE_EMOJI_CDN_BASE}${unified}.png`;
236
- }
237
- var EMOJI_REGEX = /(?:\p{Emoji_Presentation}|\p{Emoji}\uFE0F)(?:\p{Emoji_Modifier}|\uFE0F|\u200D(?:\p{Emoji_Presentation}|\p{Emoji}\uFE0F)(?:\p{Emoji_Modifier})?)*|\p{Regional_Indicator}{2}/gu;
238
- function hasEmoji(text) {
239
- return EMOJI_REGEX.test(text);
240
- }
241
- function splitTextAndEmojis(text) {
242
- const segments = [];
243
- let lastIndex = 0;
244
- EMOJI_REGEX.lastIndex = 0;
245
- let match;
246
- while ((match = EMOJI_REGEX.exec(text)) !== null) {
247
- if (match.index > lastIndex) {
248
- segments.push({
249
- type: "text",
250
- content: text.slice(lastIndex, match.index)
251
- });
252
- }
253
- segments.push({
254
- type: "emoji",
255
- content: match[0],
256
- imageUrl: getAppleEmojiUrl(match[0])
257
- });
258
- lastIndex = match.index + match[0].length;
259
- }
260
- if (lastIndex < text.length) {
261
- segments.push({
262
- type: "text",
263
- content: text.slice(lastIndex)
264
- });
265
- }
266
- return segments;
267
- }
268
- function countEmojis(text) {
269
- EMOJI_REGEX.lastIndex = 0;
270
- const matches = text.match(EMOJI_REGEX);
271
- return matches ? matches.length : 0;
272
- }
273
- function getEmojiWidth(fontSize) {
274
- return fontSize * 1.2;
275
- }
276
- function stripEmojis(text) {
277
- EMOJI_REGEX.lastIndex = 0;
278
- return text.replace(EMOJI_REGEX, "");
279
- }
280
-
281
305
  // src/components/TextElement.tsx
282
306
  import { jsx, jsxs } from "react/jsx-runtime";
283
307
  function calculateAutoWidthAndLines({
@@ -310,17 +334,9 @@ function calculateAutoWidthAndLines({
310
334
  white-space: nowrap;
311
335
  `;
312
336
  document.body.appendChild(measureSpan);
313
- const emojiWidth = getEmojiWidth(fontSize);
314
- const measureTextWithEmojis = (textToMeasure) => {
315
- const emojiCount = countEmojis(textToMeasure);
316
- if (emojiCount === 0) {
317
- measureSpan.textContent = textToMeasure;
318
- return measureSpan.getBoundingClientRect().width;
319
- }
320
- const textWithoutEmojis = stripEmojis(textToMeasure);
321
- measureSpan.textContent = textWithoutEmojis;
322
- const textWidth = measureSpan.getBoundingClientRect().width;
323
- return textWidth + emojiCount * emojiWidth;
337
+ const measureText = (textToMeasure) => {
338
+ measureSpan.textContent = textToMeasure;
339
+ return measureSpan.getBoundingClientRect().width;
324
340
  };
325
341
  measureSpan.textContent = "M";
326
342
  const charWidth = measureSpan.getBoundingClientRect().width;
@@ -344,12 +360,12 @@ function calculateAutoWidthAndLines({
344
360
  continue;
345
361
  }
346
362
  const testLine = currentLine + word;
347
- const testWidth = measureTextWithEmojis(testLine);
363
+ const testWidth = measureText(testLine);
348
364
  const WRAP_TOLERANCE = 0.5;
349
365
  if (testWidth > availableWidth + WRAP_TOLERANCE && currentLine.trim()) {
350
366
  lines.push(currentLine.trimEnd());
351
367
  currentLine = word;
352
- const wordWidth = measureTextWithEmojis(word);
368
+ const wordWidth = measureText(word);
353
369
  if (wordWidth > availableWidth) {
354
370
  let remainingWord = word;
355
371
  while (remainingWord) {
@@ -383,7 +399,7 @@ function calculateAutoWidthAndLines({
383
399
  }
384
400
  let widestLineWidth = 0;
385
401
  for (const line of lines) {
386
- const lineWidth = measureTextWithEmojis(line);
402
+ const lineWidth = measureText(line);
387
403
  widestLineWidth = Math.max(widestLineWidth, lineWidth);
388
404
  }
389
405
  document.body.removeChild(measureSpan);
@@ -391,6 +407,15 @@ function calculateAutoWidthAndLines({
391
407
  return { width: calculatedWidth, lines };
392
408
  }
393
409
  function TextElement({ segment, scale = 1 }) {
410
+ React.useEffect(() => {
411
+ const fontFamily2 = getFontFamily(segment.fontType ?? TEXT_DEFAULTS.fontType);
412
+ console.log("[ugcinc-render/TextElement] Rendering with:", {
413
+ text: segment.text?.substring(0, 50) + (segment.text?.length > 50 ? "..." : ""),
414
+ fontType: segment.fontType ?? TEXT_DEFAULTS.fontType,
415
+ fontFamily: fontFamily2,
416
+ hasEmojis: /[\u{1F300}-\u{1F9FF}]/u.test(segment.text || "")
417
+ });
418
+ }, [segment.text, segment.fontType]);
394
419
  const fontType = segment.fontType ?? TEXT_DEFAULTS.fontType;
395
420
  const fontSize = (segment.fontSize ?? TEXT_DEFAULTS.fontSize) * scale;
396
421
  const fontWeight = segment.fontWeight ?? TEXT_DEFAULTS.fontWeight;
@@ -590,35 +615,9 @@ function TextElement({ segment, scale = 1 }) {
590
615
  autoWidth,
591
616
  verticalAlign
592
617
  ]);
593
- const renderTextWithEmojis = (text) => {
594
- if (!hasEmoji(text)) {
595
- return text;
596
- }
597
- const segments = splitTextAndEmojis(text);
598
- return segments.map((segment2, i) => {
599
- if (segment2.type === "emoji" && segment2.imageUrl) {
600
- return /* @__PURE__ */ jsx(
601
- "img",
602
- {
603
- src: segment2.imageUrl,
604
- alt: segment2.content,
605
- style: {
606
- height: "1.2em",
607
- width: "1.2em",
608
- verticalAlign: "-0.25em",
609
- display: "inline"
610
- },
611
- draggable: false
612
- },
613
- i
614
- );
615
- }
616
- return /* @__PURE__ */ jsx(React.Fragment, { children: segment2.content }, i);
617
- });
618
- };
619
618
  if (autoWidth) {
620
619
  const textContent = calculatedLines.map((line, index) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
621
- renderTextWithEmojis(line),
620
+ line,
622
621
  index < calculatedLines.length - 1 && /* @__PURE__ */ jsx("br", {})
623
622
  ] }, index));
624
623
  if (backgroundColor) {
@@ -637,7 +636,7 @@ function TextElement({ segment, scale = 1 }) {
637
636
  }
638
637
  return /* @__PURE__ */ jsx("div", { style: positioningContainerStyle, children: /* @__PURE__ */ jsx("div", { style: { width: calculatedWidth, maxWidth: width }, children: /* @__PURE__ */ jsx("div", { style: textStyle, children: textContent }) }) });
639
638
  }
640
- return /* @__PURE__ */ jsx("div", { style: positioningContainerStyle, children: /* @__PURE__ */ jsx("div", { style: backgroundBoxStyle, children: /* @__PURE__ */ jsx("div", { style: textStyle, children: renderTextWithEmojis(segment.text) }) }) });
639
+ return /* @__PURE__ */ jsx("div", { style: positioningContainerStyle, children: /* @__PURE__ */ jsx("div", { style: backgroundBoxStyle, children: /* @__PURE__ */ jsx("div", { style: textStyle, children: segment.text }) }) });
641
640
  }
642
641
 
643
642
  // src/components/ImageElement.tsx
@@ -1930,6 +1929,7 @@ var RenderRoot = () => {
1930
1929
  ] });
1931
1930
  };
1932
1931
  export {
1932
+ APPLE_EMOJI_FONT,
1933
1933
  DIMENSION_PRESETS,
1934
1934
  FONT_FAMILIES,
1935
1935
  FONT_URLS,
@@ -1955,12 +1955,11 @@ export {
1955
1955
  calculateLineWidth,
1956
1956
  calculateTimelineContentEnd,
1957
1957
  canSetAsReference,
1958
+ debugFontStatus,
1958
1959
  defaultOffset,
1959
- emojiToUnified,
1960
1960
  formatTime,
1961
1961
  generateOverlayId,
1962
1962
  generateSegmentId,
1963
- getAppleEmojiUrl,
1964
1963
  getBaseSegments,
1965
1964
  getBorderRadii,
1966
1965
  getDependentElements,
@@ -1969,7 +1968,6 @@ export {
1969
1968
  getReferenceElementX,
1970
1969
  getReferenceElementY,
1971
1970
  getSegmentTimelinePosition,
1972
- hasEmoji,
1973
1971
  hexToRgba,
1974
1972
  isDynamicCropEnabled,
1975
1973
  isSegmentVisibleAtTime,
@@ -1977,7 +1975,6 @@ export {
1977
1975
  parseTime,
1978
1976
  preloadFonts,
1979
1977
  resolveElementPositions,
1980
- splitTextAndEmojis,
1981
1978
  useFontsLoaded,
1982
1979
  useImageLoader,
1983
1980
  useImagePreloader,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ugcinc-render",
3
- "version": "1.5.15",
3
+ "version": "1.5.17",
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",