ugcinc-render 1.5.14 → 1.5.16
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 +9 -29
- package/dist/index.d.ts +9 -29
- package/dist/index.js +24 -115
- package/dist/index.mjs +23 -111
- package/package.json +1 -1
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
|
|
@@ -1290,34 +1298,6 @@ declare function generateSegmentId(): string;
|
|
|
1290
1298
|
*/
|
|
1291
1299
|
declare function generateOverlayId(): string;
|
|
1292
1300
|
|
|
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
1301
|
/**
|
|
1322
1302
|
* Hook exports for ugcinc-render
|
|
1323
1303
|
*
|
|
@@ -1377,4 +1357,4 @@ declare function useResolvedPositions(elements: ImageEditorElement[], textValues
|
|
|
1377
1357
|
|
|
1378
1358
|
declare const RenderRoot: React.FC;
|
|
1379
1359
|
|
|
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
|
|
1360
|
+
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, 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
|
|
@@ -1290,34 +1298,6 @@ declare function generateSegmentId(): string;
|
|
|
1290
1298
|
*/
|
|
1291
1299
|
declare function generateOverlayId(): string;
|
|
1292
1300
|
|
|
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
1301
|
/**
|
|
1322
1302
|
* Hook exports for ugcinc-render
|
|
1323
1303
|
*
|
|
@@ -1377,4 +1357,4 @@ declare function useResolvedPositions(elements: ImageEditorElement[], textValues
|
|
|
1377
1357
|
|
|
1378
1358
|
declare const RenderRoot: React.FC;
|
|
1379
1359
|
|
|
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
|
|
1360
|
+
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, 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,
|
|
@@ -56,11 +57,9 @@ __export(index_exports, {
|
|
|
56
57
|
calculateTimelineContentEnd: () => calculateTimelineContentEnd,
|
|
57
58
|
canSetAsReference: () => canSetAsReference,
|
|
58
59
|
defaultOffset: () => defaultOffset,
|
|
59
|
-
emojiToUnified: () => emojiToUnified,
|
|
60
60
|
formatTime: () => formatTime,
|
|
61
61
|
generateOverlayId: () => generateOverlayId,
|
|
62
62
|
generateSegmentId: () => generateSegmentId,
|
|
63
|
-
getAppleEmojiUrl: () => getAppleEmojiUrl,
|
|
64
63
|
getBaseSegments: () => getBaseSegments,
|
|
65
64
|
getBorderRadii: () => getBorderRadii,
|
|
66
65
|
getDependentElements: () => getDependentElements,
|
|
@@ -69,7 +68,6 @@ __export(index_exports, {
|
|
|
69
68
|
getReferenceElementX: () => getReferenceElementX,
|
|
70
69
|
getReferenceElementY: () => getReferenceElementY,
|
|
71
70
|
getSegmentTimelinePosition: () => getSegmentTimelinePosition,
|
|
72
|
-
hasEmoji: () => hasEmoji,
|
|
73
71
|
hexToRgba: () => hexToRgba,
|
|
74
72
|
isDynamicCropEnabled: () => isDynamicCropEnabled,
|
|
75
73
|
isSegmentVisibleAtTime: () => isSegmentVisibleAtTime,
|
|
@@ -77,7 +75,6 @@ __export(index_exports, {
|
|
|
77
75
|
parseTime: () => parseTime,
|
|
78
76
|
preloadFonts: () => preloadFonts,
|
|
79
77
|
resolveElementPositions: () => resolveElementPositions,
|
|
80
|
-
splitTextAndEmojis: () => splitTextAndEmojis,
|
|
81
78
|
useFontsLoaded: () => useFontsLoaded,
|
|
82
79
|
useImageLoader: () => useImageLoader,
|
|
83
80
|
useImagePreloader: () => useImagePreloader,
|
|
@@ -163,10 +160,11 @@ function applyVideoDefaults(segment) {
|
|
|
163
160
|
}
|
|
164
161
|
|
|
165
162
|
// src/utils/fonts.ts
|
|
163
|
+
var APPLE_EMOJI_FONT = '"Apple Color Emoji"';
|
|
166
164
|
var FONT_FAMILIES = {
|
|
167
|
-
tiktok:
|
|
168
|
-
apple:
|
|
169
|
-
arial:
|
|
165
|
+
tiktok: `"TikTok Sans", ${APPLE_EMOJI_FONT}, sans-serif`,
|
|
166
|
+
apple: `"SF Pro", "SF Pro Display", -apple-system, BlinkMacSystemFont, ${APPLE_EMOJI_FONT}, sans-serif`,
|
|
167
|
+
arial: `Arial, ${APPLE_EMOJI_FONT}, sans-serif`
|
|
170
168
|
};
|
|
171
169
|
function getFontFamily(fontType) {
|
|
172
170
|
return FONT_FAMILIES[fontType] ?? FONT_FAMILIES.arial;
|
|
@@ -186,6 +184,9 @@ var FONT_URLS = {
|
|
|
186
184
|
},
|
|
187
185
|
apple: {
|
|
188
186
|
regular: "/SF-Pro.ttf"
|
|
187
|
+
},
|
|
188
|
+
emoji: {
|
|
189
|
+
apple: "/AppleColorEmoji.ttf"
|
|
189
190
|
}
|
|
190
191
|
};
|
|
191
192
|
async function preloadFonts() {
|
|
@@ -209,6 +210,12 @@ async function preloadFonts() {
|
|
|
209
210
|
{ weight: "normal" }
|
|
210
211
|
);
|
|
211
212
|
fontPromises.push(sfPro.load());
|
|
213
|
+
const appleEmoji = new FontFace(
|
|
214
|
+
"Apple Color Emoji",
|
|
215
|
+
`url(${FONT_URLS.emoji.apple})`,
|
|
216
|
+
{ weight: "normal" }
|
|
217
|
+
);
|
|
218
|
+
fontPromises.push(appleEmoji.load());
|
|
212
219
|
const loadedFonts = await Promise.all(fontPromises);
|
|
213
220
|
loadedFonts.forEach((font) => {
|
|
214
221
|
document.fonts.add(font);
|
|
@@ -219,7 +226,7 @@ function areFontsLoaded() {
|
|
|
219
226
|
if (typeof document === "undefined") {
|
|
220
227
|
return false;
|
|
221
228
|
}
|
|
222
|
-
return document.fonts.check('normal 16px "TikTok Sans"') && document.fonts.check('bold 16px "TikTok Sans"') && document.fonts.check('normal 16px "SF Pro"');
|
|
229
|
+
return document.fonts.check('normal 16px "TikTok Sans"') && document.fonts.check('bold 16px "TikTok Sans"') && document.fonts.check('normal 16px "SF Pro"') && document.fonts.check('normal 16px "Apple Color Emoji"');
|
|
223
230
|
}
|
|
224
231
|
|
|
225
232
|
// src/utils/text.ts
|
|
@@ -306,66 +313,6 @@ function hexToRgba(hex, opacity = 100) {
|
|
|
306
313
|
return `rgba(${r}, ${g}, ${b}, ${opacity / 100})`;
|
|
307
314
|
}
|
|
308
315
|
|
|
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.3;
|
|
363
|
-
}
|
|
364
|
-
function stripEmojis(text) {
|
|
365
|
-
EMOJI_REGEX.lastIndex = 0;
|
|
366
|
-
return text.replace(EMOJI_REGEX, "");
|
|
367
|
-
}
|
|
368
|
-
|
|
369
316
|
// src/components/TextElement.tsx
|
|
370
317
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
371
318
|
function calculateAutoWidthAndLines({
|
|
@@ -398,17 +345,9 @@ function calculateAutoWidthAndLines({
|
|
|
398
345
|
white-space: nowrap;
|
|
399
346
|
`;
|
|
400
347
|
document.body.appendChild(measureSpan);
|
|
401
|
-
const
|
|
402
|
-
|
|
403
|
-
|
|
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;
|
|
348
|
+
const measureText = (textToMeasure) => {
|
|
349
|
+
measureSpan.textContent = textToMeasure;
|
|
350
|
+
return measureSpan.getBoundingClientRect().width;
|
|
412
351
|
};
|
|
413
352
|
measureSpan.textContent = "M";
|
|
414
353
|
const charWidth = measureSpan.getBoundingClientRect().width;
|
|
@@ -432,12 +371,12 @@ function calculateAutoWidthAndLines({
|
|
|
432
371
|
continue;
|
|
433
372
|
}
|
|
434
373
|
const testLine = currentLine + word;
|
|
435
|
-
const testWidth =
|
|
374
|
+
const testWidth = measureText(testLine);
|
|
436
375
|
const WRAP_TOLERANCE = 0.5;
|
|
437
376
|
if (testWidth > availableWidth + WRAP_TOLERANCE && currentLine.trim()) {
|
|
438
377
|
lines.push(currentLine.trimEnd());
|
|
439
378
|
currentLine = word;
|
|
440
|
-
const wordWidth =
|
|
379
|
+
const wordWidth = measureText(word);
|
|
441
380
|
if (wordWidth > availableWidth) {
|
|
442
381
|
let remainingWord = word;
|
|
443
382
|
while (remainingWord) {
|
|
@@ -471,7 +410,7 @@ function calculateAutoWidthAndLines({
|
|
|
471
410
|
}
|
|
472
411
|
let widestLineWidth = 0;
|
|
473
412
|
for (const line of lines) {
|
|
474
|
-
const lineWidth =
|
|
413
|
+
const lineWidth = measureText(line);
|
|
475
414
|
widestLineWidth = Math.max(widestLineWidth, lineWidth);
|
|
476
415
|
}
|
|
477
416
|
document.body.removeChild(measureSpan);
|
|
@@ -678,36 +617,9 @@ function TextElement({ segment, scale = 1 }) {
|
|
|
678
617
|
autoWidth,
|
|
679
618
|
verticalAlign
|
|
680
619
|
]);
|
|
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-block",
|
|
698
|
-
margin: "0 0.05em"
|
|
699
|
-
},
|
|
700
|
-
draggable: false
|
|
701
|
-
},
|
|
702
|
-
i
|
|
703
|
-
);
|
|
704
|
-
}
|
|
705
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.default.Fragment, { children: segment2.content }, i);
|
|
706
|
-
});
|
|
707
|
-
};
|
|
708
620
|
if (autoWidth) {
|
|
709
621
|
const textContent = calculatedLines.map((line, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.default.Fragment, { children: [
|
|
710
|
-
|
|
622
|
+
line,
|
|
711
623
|
index < calculatedLines.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("br", {})
|
|
712
624
|
] }, index));
|
|
713
625
|
if (backgroundColor) {
|
|
@@ -726,7 +638,7 @@ function TextElement({ segment, scale = 1 }) {
|
|
|
726
638
|
}
|
|
727
639
|
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 }) }) });
|
|
728
640
|
}
|
|
729
|
-
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:
|
|
641
|
+
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 }) }) });
|
|
730
642
|
}
|
|
731
643
|
|
|
732
644
|
// src/components/ImageElement.tsx
|
|
@@ -2020,6 +1932,7 @@ var RenderRoot = () => {
|
|
|
2020
1932
|
};
|
|
2021
1933
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2022
1934
|
0 && (module.exports = {
|
|
1935
|
+
APPLE_EMOJI_FONT,
|
|
2023
1936
|
DIMENSION_PRESETS,
|
|
2024
1937
|
FONT_FAMILIES,
|
|
2025
1938
|
FONT_URLS,
|
|
@@ -2046,11 +1959,9 @@ var RenderRoot = () => {
|
|
|
2046
1959
|
calculateTimelineContentEnd,
|
|
2047
1960
|
canSetAsReference,
|
|
2048
1961
|
defaultOffset,
|
|
2049
|
-
emojiToUnified,
|
|
2050
1962
|
formatTime,
|
|
2051
1963
|
generateOverlayId,
|
|
2052
1964
|
generateSegmentId,
|
|
2053
|
-
getAppleEmojiUrl,
|
|
2054
1965
|
getBaseSegments,
|
|
2055
1966
|
getBorderRadii,
|
|
2056
1967
|
getDependentElements,
|
|
@@ -2059,7 +1970,6 @@ var RenderRoot = () => {
|
|
|
2059
1970
|
getReferenceElementX,
|
|
2060
1971
|
getReferenceElementY,
|
|
2061
1972
|
getSegmentTimelinePosition,
|
|
2062
|
-
hasEmoji,
|
|
2063
1973
|
hexToRgba,
|
|
2064
1974
|
isDynamicCropEnabled,
|
|
2065
1975
|
isSegmentVisibleAtTime,
|
|
@@ -2067,7 +1977,6 @@ var RenderRoot = () => {
|
|
|
2067
1977
|
parseTime,
|
|
2068
1978
|
preloadFonts,
|
|
2069
1979
|
resolveElementPositions,
|
|
2070
|
-
splitTextAndEmojis,
|
|
2071
1980
|
useFontsLoaded,
|
|
2072
1981
|
useImageLoader,
|
|
2073
1982
|
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:
|
|
80
|
-
apple:
|
|
81
|
-
arial:
|
|
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,6 +99,9 @@ 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() {
|
|
@@ -121,6 +125,12 @@ async function preloadFonts() {
|
|
|
121
125
|
{ weight: "normal" }
|
|
122
126
|
);
|
|
123
127
|
fontPromises.push(sfPro.load());
|
|
128
|
+
const appleEmoji = new FontFace(
|
|
129
|
+
"Apple Color Emoji",
|
|
130
|
+
`url(${FONT_URLS.emoji.apple})`,
|
|
131
|
+
{ weight: "normal" }
|
|
132
|
+
);
|
|
133
|
+
fontPromises.push(appleEmoji.load());
|
|
124
134
|
const loadedFonts = await Promise.all(fontPromises);
|
|
125
135
|
loadedFonts.forEach((font) => {
|
|
126
136
|
document.fonts.add(font);
|
|
@@ -131,7 +141,7 @@ function areFontsLoaded() {
|
|
|
131
141
|
if (typeof document === "undefined") {
|
|
132
142
|
return false;
|
|
133
143
|
}
|
|
134
|
-
return document.fonts.check('normal 16px "TikTok Sans"') && document.fonts.check('bold 16px "TikTok Sans"') && document.fonts.check('normal 16px "SF Pro"');
|
|
144
|
+
return document.fonts.check('normal 16px "TikTok Sans"') && document.fonts.check('bold 16px "TikTok Sans"') && document.fonts.check('normal 16px "SF Pro"') && document.fonts.check('normal 16px "Apple Color Emoji"');
|
|
135
145
|
}
|
|
136
146
|
|
|
137
147
|
// src/utils/text.ts
|
|
@@ -218,66 +228,6 @@ function hexToRgba(hex, opacity = 100) {
|
|
|
218
228
|
return `rgba(${r}, ${g}, ${b}, ${opacity / 100})`;
|
|
219
229
|
}
|
|
220
230
|
|
|
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.3;
|
|
275
|
-
}
|
|
276
|
-
function stripEmojis(text) {
|
|
277
|
-
EMOJI_REGEX.lastIndex = 0;
|
|
278
|
-
return text.replace(EMOJI_REGEX, "");
|
|
279
|
-
}
|
|
280
|
-
|
|
281
231
|
// src/components/TextElement.tsx
|
|
282
232
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
283
233
|
function calculateAutoWidthAndLines({
|
|
@@ -310,17 +260,9 @@ function calculateAutoWidthAndLines({
|
|
|
310
260
|
white-space: nowrap;
|
|
311
261
|
`;
|
|
312
262
|
document.body.appendChild(measureSpan);
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
|
|
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;
|
|
263
|
+
const measureText = (textToMeasure) => {
|
|
264
|
+
measureSpan.textContent = textToMeasure;
|
|
265
|
+
return measureSpan.getBoundingClientRect().width;
|
|
324
266
|
};
|
|
325
267
|
measureSpan.textContent = "M";
|
|
326
268
|
const charWidth = measureSpan.getBoundingClientRect().width;
|
|
@@ -344,12 +286,12 @@ function calculateAutoWidthAndLines({
|
|
|
344
286
|
continue;
|
|
345
287
|
}
|
|
346
288
|
const testLine = currentLine + word;
|
|
347
|
-
const testWidth =
|
|
289
|
+
const testWidth = measureText(testLine);
|
|
348
290
|
const WRAP_TOLERANCE = 0.5;
|
|
349
291
|
if (testWidth > availableWidth + WRAP_TOLERANCE && currentLine.trim()) {
|
|
350
292
|
lines.push(currentLine.trimEnd());
|
|
351
293
|
currentLine = word;
|
|
352
|
-
const wordWidth =
|
|
294
|
+
const wordWidth = measureText(word);
|
|
353
295
|
if (wordWidth > availableWidth) {
|
|
354
296
|
let remainingWord = word;
|
|
355
297
|
while (remainingWord) {
|
|
@@ -383,7 +325,7 @@ function calculateAutoWidthAndLines({
|
|
|
383
325
|
}
|
|
384
326
|
let widestLineWidth = 0;
|
|
385
327
|
for (const line of lines) {
|
|
386
|
-
const lineWidth =
|
|
328
|
+
const lineWidth = measureText(line);
|
|
387
329
|
widestLineWidth = Math.max(widestLineWidth, lineWidth);
|
|
388
330
|
}
|
|
389
331
|
document.body.removeChild(measureSpan);
|
|
@@ -590,36 +532,9 @@ function TextElement({ segment, scale = 1 }) {
|
|
|
590
532
|
autoWidth,
|
|
591
533
|
verticalAlign
|
|
592
534
|
]);
|
|
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-block",
|
|
610
|
-
margin: "0 0.05em"
|
|
611
|
-
},
|
|
612
|
-
draggable: false
|
|
613
|
-
},
|
|
614
|
-
i
|
|
615
|
-
);
|
|
616
|
-
}
|
|
617
|
-
return /* @__PURE__ */ jsx(React.Fragment, { children: segment2.content }, i);
|
|
618
|
-
});
|
|
619
|
-
};
|
|
620
535
|
if (autoWidth) {
|
|
621
536
|
const textContent = calculatedLines.map((line, index) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
622
|
-
|
|
537
|
+
line,
|
|
623
538
|
index < calculatedLines.length - 1 && /* @__PURE__ */ jsx("br", {})
|
|
624
539
|
] }, index));
|
|
625
540
|
if (backgroundColor) {
|
|
@@ -638,7 +553,7 @@ function TextElement({ segment, scale = 1 }) {
|
|
|
638
553
|
}
|
|
639
554
|
return /* @__PURE__ */ jsx("div", { style: positioningContainerStyle, children: /* @__PURE__ */ jsx("div", { style: { width: calculatedWidth, maxWidth: width }, children: /* @__PURE__ */ jsx("div", { style: textStyle, children: textContent }) }) });
|
|
640
555
|
}
|
|
641
|
-
return /* @__PURE__ */ jsx("div", { style: positioningContainerStyle, children: /* @__PURE__ */ jsx("div", { style: backgroundBoxStyle, children: /* @__PURE__ */ jsx("div", { style: textStyle, children:
|
|
556
|
+
return /* @__PURE__ */ jsx("div", { style: positioningContainerStyle, children: /* @__PURE__ */ jsx("div", { style: backgroundBoxStyle, children: /* @__PURE__ */ jsx("div", { style: textStyle, children: segment.text }) }) });
|
|
642
557
|
}
|
|
643
558
|
|
|
644
559
|
// src/components/ImageElement.tsx
|
|
@@ -1931,6 +1846,7 @@ var RenderRoot = () => {
|
|
|
1931
1846
|
] });
|
|
1932
1847
|
};
|
|
1933
1848
|
export {
|
|
1849
|
+
APPLE_EMOJI_FONT,
|
|
1934
1850
|
DIMENSION_PRESETS,
|
|
1935
1851
|
FONT_FAMILIES,
|
|
1936
1852
|
FONT_URLS,
|
|
@@ -1957,11 +1873,9 @@ export {
|
|
|
1957
1873
|
calculateTimelineContentEnd,
|
|
1958
1874
|
canSetAsReference,
|
|
1959
1875
|
defaultOffset,
|
|
1960
|
-
emojiToUnified,
|
|
1961
1876
|
formatTime,
|
|
1962
1877
|
generateOverlayId,
|
|
1963
1878
|
generateSegmentId,
|
|
1964
|
-
getAppleEmojiUrl,
|
|
1965
1879
|
getBaseSegments,
|
|
1966
1880
|
getBorderRadii,
|
|
1967
1881
|
getDependentElements,
|
|
@@ -1970,7 +1884,6 @@ export {
|
|
|
1970
1884
|
getReferenceElementX,
|
|
1971
1885
|
getReferenceElementY,
|
|
1972
1886
|
getSegmentTimelinePosition,
|
|
1973
|
-
hasEmoji,
|
|
1974
1887
|
hexToRgba,
|
|
1975
1888
|
isDynamicCropEnabled,
|
|
1976
1889
|
isSegmentVisibleAtTime,
|
|
@@ -1978,7 +1891,6 @@ export {
|
|
|
1978
1891
|
parseTime,
|
|
1979
1892
|
preloadFonts,
|
|
1980
1893
|
resolveElementPositions,
|
|
1981
|
-
splitTextAndEmojis,
|
|
1982
1894
|
useFontsLoaded,
|
|
1983
1895
|
useImageLoader,
|
|
1984
1896
|
useImagePreloader,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ugcinc-render",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.16",
|
|
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",
|