@remotion/web-renderer 4.0.391 → 4.0.393
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/can-use-webfs-target.d.ts +1 -0
- package/dist/can-use-webfs-target.js +19 -0
- package/dist/compose.d.ts +6 -1
- package/dist/compose.js +31 -23
- package/dist/drawing/calculate-transforms.js +27 -8
- package/dist/drawing/draw-dom-element.d.ts +2 -0
- package/dist/drawing/draw-dom-element.js +17 -0
- package/dist/drawing/draw-element-to-canvas.d.ts +7 -3
- package/dist/drawing/draw-element-to-canvas.js +40 -48
- package/dist/drawing/draw-element.d.ts +9 -0
- package/dist/drawing/draw-element.js +50 -0
- package/dist/drawing/drawn-fn.d.ts +5 -0
- package/dist/drawing/drawn-fn.js +1 -0
- package/dist/drawing/text/apply-text-transform.d.ts +1 -0
- package/dist/drawing/text/apply-text-transform.js +12 -0
- package/dist/drawing/text/draw-text.d.ts +2 -0
- package/dist/drawing/text/draw-text.js +44 -0
- package/dist/drawing/text/handle-text-node.d.ts +7 -1
- package/dist/drawing/text/handle-text-node.js +7 -66
- package/dist/drawing/transform-in-3d.d.ts +8 -0
- package/dist/drawing/transform-in-3d.js +142 -0
- package/dist/esm/index.mjs +677 -8534
- package/dist/index.d.ts +2 -1
- package/dist/mediabunny-mappings.d.ts +1 -0
- package/dist/mediabunny-mappings.js +10 -0
- package/dist/output-target.d.ts +1 -0
- package/dist/output-target.js +1 -0
- package/dist/render-media-on-web.d.ts +6 -1
- package/dist/render-media-on-web.js +64 -24
- package/dist/render-operations-queue.d.ts +3 -0
- package/dist/render-operations-queue.js +3 -0
- package/dist/render-still-on-web.js +15 -9
- package/dist/take-screenshot.js +1 -1
- package/dist/walk-tree.d.ts +1 -0
- package/dist/walk-tree.js +14 -0
- package/dist/web-fs-target.d.ts +7 -0
- package/dist/web-fs-target.js +41 -0
- package/package.json +6 -6
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const canUseWebFsWriter: () => Promise<boolean>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const canUseWebFsWriter = async () => {
|
|
2
|
+
if (!('storage' in navigator)) {
|
|
3
|
+
return false;
|
|
4
|
+
}
|
|
5
|
+
if (!('getDirectory' in navigator.storage)) {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
try {
|
|
9
|
+
const directoryHandle = await navigator.storage.getDirectory();
|
|
10
|
+
const fileHandle = await directoryHandle.getFileHandle('remotion-probe-web-fs-support', {
|
|
11
|
+
create: true,
|
|
12
|
+
});
|
|
13
|
+
const canUse = fileHandle.createWritable !== undefined;
|
|
14
|
+
return canUse;
|
|
15
|
+
}
|
|
16
|
+
catch (_a) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
};
|
package/dist/compose.d.ts
CHANGED
|
@@ -1 +1,6 @@
|
|
|
1
|
-
export declare const compose: (element
|
|
1
|
+
export declare const compose: ({ element, context, offsetLeft, offsetTop, }: {
|
|
2
|
+
element: HTMLElement | SVGElement;
|
|
3
|
+
context: OffscreenCanvasRenderingContext2D;
|
|
4
|
+
offsetLeft: number;
|
|
5
|
+
offsetTop: number;
|
|
6
|
+
}) => Promise<void>;
|
package/dist/compose.js
CHANGED
|
@@ -1,7 +1,23 @@
|
|
|
1
|
+
import { drawDomElement } from './drawing/draw-dom-element';
|
|
1
2
|
import { drawElementToCanvas } from './drawing/draw-element-to-canvas';
|
|
2
3
|
import { handleTextNode } from './drawing/text/handle-text-node';
|
|
3
|
-
import {
|
|
4
|
-
|
|
4
|
+
import { skipToNextNonDescendant } from './walk-tree';
|
|
5
|
+
const walkOverNode = ({ node, context, offsetLeft, offsetTop, }) => {
|
|
6
|
+
if (node instanceof HTMLElement || node instanceof SVGElement) {
|
|
7
|
+
return drawElementToCanvas({
|
|
8
|
+
element: node,
|
|
9
|
+
context,
|
|
10
|
+
draw: drawDomElement(node),
|
|
11
|
+
offsetLeft,
|
|
12
|
+
offsetTop,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
if (node instanceof Text) {
|
|
16
|
+
return handleTextNode({ node, context, offsetLeft, offsetTop });
|
|
17
|
+
}
|
|
18
|
+
throw new Error('Unknown node type');
|
|
19
|
+
};
|
|
20
|
+
export const compose = async ({ element, context, offsetLeft, offsetTop, }) => {
|
|
5
21
|
const treeWalker = document.createTreeWalker(element, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT, (node) => {
|
|
6
22
|
if (node instanceof Element) {
|
|
7
23
|
// SVG does have children, but we process SVG elements in its
|
|
@@ -16,28 +32,20 @@ export const compose = async (element, context) => {
|
|
|
16
32
|
}
|
|
17
33
|
return NodeFilter.FILTER_ACCEPT;
|
|
18
34
|
});
|
|
19
|
-
while (
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
: node instanceof HTMLCanvasElement
|
|
31
|
-
? node
|
|
32
|
-
: null);
|
|
33
|
-
if (drawable) {
|
|
34
|
-
context.drawImage(drawable, dimensions.left, dimensions.top, dimensions.width, dimensions.height);
|
|
35
|
-
}
|
|
36
|
-
},
|
|
37
|
-
});
|
|
35
|
+
while (true) {
|
|
36
|
+
const val = await walkOverNode({
|
|
37
|
+
node: treeWalker.currentNode,
|
|
38
|
+
context,
|
|
39
|
+
offsetLeft,
|
|
40
|
+
offsetTop,
|
|
41
|
+
});
|
|
42
|
+
if (val === 'skip-children') {
|
|
43
|
+
if (!skipToNextNonDescendant(treeWalker)) {
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
38
46
|
}
|
|
39
|
-
else if (
|
|
40
|
-
|
|
47
|
+
else if (!treeWalker.nextNode()) {
|
|
48
|
+
break;
|
|
41
49
|
}
|
|
42
50
|
}
|
|
43
51
|
};
|
|
@@ -39,17 +39,34 @@ export const calculateTransforms = (element) => {
|
|
|
39
39
|
? undefined
|
|
40
40
|
: computedStyle.transform;
|
|
41
41
|
const matrix = new DOMMatrix(toParse);
|
|
42
|
-
const { transform } = parent.style;
|
|
42
|
+
const { transform, scale, rotate } = parent.style;
|
|
43
|
+
const additionalMatrices = [];
|
|
44
|
+
// The order of transformations is:
|
|
45
|
+
// 1. Translate --> We do not have to consider it since it changes getClientBoundingRect()
|
|
46
|
+
// 2. Rotate
|
|
47
|
+
// 3. Scale
|
|
48
|
+
// 4. CSS "transform"
|
|
49
|
+
if (rotate !== '' && rotate !== 'none') {
|
|
50
|
+
additionalMatrices.push(new DOMMatrix(`rotate(${rotate})`));
|
|
51
|
+
}
|
|
52
|
+
if (scale !== '' && scale !== 'none') {
|
|
53
|
+
additionalMatrices.push(new DOMMatrix(`scale(${scale})`));
|
|
54
|
+
}
|
|
55
|
+
additionalMatrices.push(matrix);
|
|
43
56
|
parent.style.transform = 'none';
|
|
57
|
+
parent.style.scale = 'none';
|
|
58
|
+
parent.style.rotate = 'none';
|
|
44
59
|
transforms.push({
|
|
45
|
-
matrix,
|
|
46
60
|
rect: parent,
|
|
47
61
|
transformOrigin: computedStyle.transformOrigin,
|
|
48
62
|
boundingClientRect: null,
|
|
63
|
+
matrices: additionalMatrices,
|
|
49
64
|
});
|
|
50
65
|
const parentRef = parent;
|
|
51
66
|
toReset.push(() => {
|
|
52
67
|
parentRef.style.transform = transform;
|
|
68
|
+
parentRef.style.scale = scale;
|
|
69
|
+
parentRef.style.rotate = rotate;
|
|
53
70
|
});
|
|
54
71
|
}
|
|
55
72
|
parent = parent.parentElement;
|
|
@@ -64,12 +81,14 @@ export const calculateTransforms = (element) => {
|
|
|
64
81
|
if (!transform.boundingClientRect) {
|
|
65
82
|
throw new Error('Bounding client rect not found');
|
|
66
83
|
}
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
84
|
+
for (const matrix of transform.matrices) {
|
|
85
|
+
const globalTransformOrigin = getGlobalTransformOrigin(transform);
|
|
86
|
+
const transformMatrix = new DOMMatrix()
|
|
87
|
+
.translate(globalTransformOrigin.x, globalTransformOrigin.y)
|
|
88
|
+
.multiply(matrix)
|
|
89
|
+
.translate(-globalTransformOrigin.x, -globalTransformOrigin.y);
|
|
90
|
+
totalMatrix.multiplySelf(transformMatrix);
|
|
91
|
+
}
|
|
73
92
|
}
|
|
74
93
|
if (!elementComputedStyle) {
|
|
75
94
|
throw new Error('Element computed style not found');
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { turnSvgIntoDrawable } from './turn-svg-into-drawable';
|
|
2
|
+
export const drawDomElement = (node) => {
|
|
3
|
+
const domDrawFn = async ({ dimensions, contextToDraw }) => {
|
|
4
|
+
const drawable = await (node instanceof SVGSVGElement
|
|
5
|
+
? turnSvgIntoDrawable(node)
|
|
6
|
+
: node instanceof HTMLImageElement
|
|
7
|
+
? node
|
|
8
|
+
: node instanceof HTMLCanvasElement
|
|
9
|
+
? node
|
|
10
|
+
: null);
|
|
11
|
+
if (!drawable) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
contextToDraw.drawImage(drawable, dimensions.left, dimensions.top, dimensions.width, dimensions.height);
|
|
15
|
+
};
|
|
16
|
+
return domDrawFn;
|
|
17
|
+
};
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
import type { DrawFn } from './drawn-fn';
|
|
2
|
+
export type DrawElementToCanvasReturnValue = 'continue' | 'skip-children';
|
|
3
|
+
export declare const drawElementToCanvas: ({ element, context, draw, offsetLeft, offsetTop, }: {
|
|
2
4
|
element: HTMLElement | SVGElement;
|
|
3
5
|
context: OffscreenCanvasRenderingContext2D;
|
|
4
|
-
draw:
|
|
5
|
-
|
|
6
|
+
draw: DrawFn;
|
|
7
|
+
offsetLeft: number;
|
|
8
|
+
offsetTop: number;
|
|
9
|
+
}) => Promise<DrawElementToCanvasReturnValue>;
|
|
@@ -1,61 +1,53 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { compose } from '../compose';
|
|
2
2
|
import { calculateTransforms } from './calculate-transforms';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
export const drawElementToCanvas = async ({ element, context, draw, }) => {
|
|
3
|
+
import { drawElement } from './draw-element';
|
|
4
|
+
import { transformIn3d } from './transform-in-3d';
|
|
5
|
+
export const drawElementToCanvas = async ({ element, context, draw, offsetLeft, offsetTop, }) => {
|
|
7
6
|
const { totalMatrix, reset, dimensions, opacity, computedStyle } = calculateTransforms(element);
|
|
8
7
|
if (opacity === 0) {
|
|
9
8
|
reset();
|
|
10
|
-
return;
|
|
9
|
+
return 'continue';
|
|
11
10
|
}
|
|
12
11
|
if (dimensions.width <= 0 || dimensions.height <= 0) {
|
|
13
12
|
reset();
|
|
14
|
-
return;
|
|
13
|
+
return 'continue';
|
|
15
14
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
context.fillStyle = background;
|
|
44
|
-
context.fillRect(dimensions.left, dimensions.top, dimensions.width, dimensions.height);
|
|
45
|
-
context.fillStyle = originalFillStyle;
|
|
15
|
+
if (!totalMatrix.is2D) {
|
|
16
|
+
const canvasOffsetLeft = Math.min(dimensions.left, 0);
|
|
17
|
+
const canvasOffsetTop = Math.min(dimensions.top, 0);
|
|
18
|
+
const tempCanvasWidth = Math.max(dimensions.width, dimensions.right);
|
|
19
|
+
const tempCanvasHeight = Math.max(dimensions.height, dimensions.bottom);
|
|
20
|
+
const tempCanvas = new OffscreenCanvas(tempCanvasWidth, tempCanvasHeight);
|
|
21
|
+
const context2 = tempCanvas.getContext('2d');
|
|
22
|
+
if (!context2) {
|
|
23
|
+
throw new Error('Could not get context');
|
|
24
|
+
}
|
|
25
|
+
await compose({
|
|
26
|
+
element,
|
|
27
|
+
context: context2,
|
|
28
|
+
offsetLeft: canvasOffsetLeft,
|
|
29
|
+
offsetTop: canvasOffsetTop,
|
|
30
|
+
});
|
|
31
|
+
const transformed = transformIn3d({
|
|
32
|
+
canvasWidth: tempCanvasWidth,
|
|
33
|
+
canvasHeight: tempCanvasHeight,
|
|
34
|
+
matrix: totalMatrix,
|
|
35
|
+
sourceCanvas: tempCanvas,
|
|
36
|
+
offsetLeft: canvasOffsetLeft,
|
|
37
|
+
offsetTop: canvasOffsetTop,
|
|
38
|
+
});
|
|
39
|
+
context.drawImage(transformed, 0, 0);
|
|
40
|
+
reset();
|
|
41
|
+
return 'skip-children';
|
|
46
42
|
}
|
|
47
|
-
await
|
|
48
|
-
|
|
49
|
-
ctx: context,
|
|
50
|
-
x: dimensions.left,
|
|
51
|
-
y: dimensions.top,
|
|
52
|
-
width: dimensions.width,
|
|
53
|
-
height: dimensions.height,
|
|
54
|
-
borderRadius,
|
|
43
|
+
await drawElement({
|
|
44
|
+
dimensions: new DOMRect(dimensions.left - offsetLeft, dimensions.top - offsetTop, dimensions.width, dimensions.height),
|
|
55
45
|
computedStyle,
|
|
46
|
+
context,
|
|
47
|
+
draw,
|
|
48
|
+
opacity,
|
|
49
|
+
totalMatrix,
|
|
56
50
|
});
|
|
57
|
-
finishOpacity();
|
|
58
|
-
finishBorderRadius();
|
|
59
|
-
finishTransform();
|
|
60
51
|
reset();
|
|
52
|
+
return 'continue';
|
|
61
53
|
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DrawFn } from './drawn-fn';
|
|
2
|
+
export declare const drawElement: ({ dimensions, computedStyle, context, draw, opacity, totalMatrix, }: {
|
|
3
|
+
dimensions: DOMRect;
|
|
4
|
+
computedStyle: CSSStyleDeclaration;
|
|
5
|
+
context: OffscreenCanvasRenderingContext2D;
|
|
6
|
+
opacity: number;
|
|
7
|
+
totalMatrix: DOMMatrix;
|
|
8
|
+
draw: DrawFn;
|
|
9
|
+
}) => Promise<void>;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { parseBorderRadius, setBorderRadius } from './border-radius';
|
|
2
|
+
import { drawBorder } from './draw-border';
|
|
3
|
+
import { setOpacity } from './opacity';
|
|
4
|
+
import { setTransform } from './transform';
|
|
5
|
+
export const drawElement = async ({ dimensions, computedStyle, context, draw, opacity, totalMatrix, }) => {
|
|
6
|
+
const background = computedStyle.backgroundColor;
|
|
7
|
+
const borderRadius = parseBorderRadius({
|
|
8
|
+
borderRadius: computedStyle.borderRadius,
|
|
9
|
+
width: dimensions.width,
|
|
10
|
+
height: dimensions.height,
|
|
11
|
+
});
|
|
12
|
+
const finishTransform = setTransform({
|
|
13
|
+
ctx: context,
|
|
14
|
+
transform: totalMatrix,
|
|
15
|
+
});
|
|
16
|
+
const finishBorderRadius = setBorderRadius({
|
|
17
|
+
ctx: context,
|
|
18
|
+
x: dimensions.left,
|
|
19
|
+
y: dimensions.top,
|
|
20
|
+
width: dimensions.width,
|
|
21
|
+
height: dimensions.height,
|
|
22
|
+
borderRadius,
|
|
23
|
+
});
|
|
24
|
+
const finishOpacity = setOpacity({
|
|
25
|
+
ctx: context,
|
|
26
|
+
opacity,
|
|
27
|
+
});
|
|
28
|
+
if (background &&
|
|
29
|
+
background !== 'transparent' &&
|
|
30
|
+
!(background.startsWith('rgba') &&
|
|
31
|
+
(background.endsWith(', 0)') || background.endsWith(',0')))) {
|
|
32
|
+
const originalFillStyle = context.fillStyle;
|
|
33
|
+
context.fillStyle = background;
|
|
34
|
+
context.fillRect(dimensions.left, dimensions.top, dimensions.width, dimensions.height);
|
|
35
|
+
context.fillStyle = originalFillStyle;
|
|
36
|
+
}
|
|
37
|
+
await draw({ dimensions, computedStyle, contextToDraw: context });
|
|
38
|
+
drawBorder({
|
|
39
|
+
ctx: context,
|
|
40
|
+
x: dimensions.left,
|
|
41
|
+
y: dimensions.top,
|
|
42
|
+
width: dimensions.width,
|
|
43
|
+
height: dimensions.height,
|
|
44
|
+
borderRadius,
|
|
45
|
+
computedStyle,
|
|
46
|
+
});
|
|
47
|
+
finishOpacity();
|
|
48
|
+
finishBorderRadius();
|
|
49
|
+
finishTransform();
|
|
50
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const applyTextTransform: (text: string, transform: string) => string;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const applyTextTransform = (text, transform) => {
|
|
2
|
+
if (transform === 'uppercase') {
|
|
3
|
+
return text.toUpperCase();
|
|
4
|
+
}
|
|
5
|
+
if (transform === 'lowercase') {
|
|
6
|
+
return text.toLowerCase();
|
|
7
|
+
}
|
|
8
|
+
if (transform === 'capitalize') {
|
|
9
|
+
return text.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
10
|
+
}
|
|
11
|
+
return text;
|
|
12
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Internals } from 'remotion';
|
|
2
|
+
import { applyTextTransform } from './apply-text-transform';
|
|
3
|
+
import { findLineBreaks } from './find-line-breaks.text';
|
|
4
|
+
import { getCollapsedText } from './get-collapsed-text';
|
|
5
|
+
export const drawText = (span) => {
|
|
6
|
+
const drawFn = ({ dimensions: rect, computedStyle, contextToDraw }) => {
|
|
7
|
+
const { fontFamily, fontSize, fontWeight, color, lineHeight, direction, writingMode, letterSpacing, textTransform, } = computedStyle;
|
|
8
|
+
const isVertical = writingMode !== 'horizontal-tb';
|
|
9
|
+
if (isVertical) {
|
|
10
|
+
// TODO: Only warn once per render.
|
|
11
|
+
Internals.Log.warn({
|
|
12
|
+
logLevel: 'warn',
|
|
13
|
+
tag: '@remotion/web-renderer',
|
|
14
|
+
}, 'Detected "writing-mode" CSS property. Vertical text is not yet supported in @remotion/web-renderer');
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
contextToDraw.save();
|
|
18
|
+
contextToDraw.font = `${fontWeight} ${fontSize} ${fontFamily}`;
|
|
19
|
+
contextToDraw.fillStyle = color;
|
|
20
|
+
contextToDraw.letterSpacing = letterSpacing;
|
|
21
|
+
const fontSizePx = parseFloat(fontSize);
|
|
22
|
+
// TODO: This is not necessarily correct, need to create text and measure to know for sure
|
|
23
|
+
const lineHeightPx = lineHeight === 'normal' ? 1.2 * fontSizePx : parseFloat(lineHeight);
|
|
24
|
+
const baselineOffset = (lineHeightPx - fontSizePx) / 2;
|
|
25
|
+
const isRTL = direction === 'rtl';
|
|
26
|
+
contextToDraw.textAlign = isRTL ? 'right' : 'left';
|
|
27
|
+
contextToDraw.textBaseline = 'top';
|
|
28
|
+
const originalText = span.textContent;
|
|
29
|
+
const collapsedText = getCollapsedText(span);
|
|
30
|
+
const transformedText = applyTextTransform(collapsedText, textTransform);
|
|
31
|
+
span.textContent = transformedText;
|
|
32
|
+
// For RTL text, fill from the right edge instead of left
|
|
33
|
+
const xPosition = isRTL ? rect.right : rect.left;
|
|
34
|
+
const lines = findLineBreaks(span, isRTL);
|
|
35
|
+
let offsetTop = 0;
|
|
36
|
+
for (const line of lines) {
|
|
37
|
+
contextToDraw.fillText(line.text, xPosition + line.offsetHorizontal, rect.top + baselineOffset + offsetTop);
|
|
38
|
+
offsetTop += line.offsetTop;
|
|
39
|
+
}
|
|
40
|
+
span.textContent = originalText;
|
|
41
|
+
contextToDraw.restore();
|
|
42
|
+
};
|
|
43
|
+
return drawFn;
|
|
44
|
+
};
|
|
@@ -1 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import type { DrawElementToCanvasReturnValue } from '../draw-element-to-canvas';
|
|
2
|
+
export declare const handleTextNode: ({ node, context, offsetLeft, offsetTop, }: {
|
|
3
|
+
node: Text;
|
|
4
|
+
context: OffscreenCanvasRenderingContext2D;
|
|
5
|
+
offsetLeft: number;
|
|
6
|
+
offsetTop: number;
|
|
7
|
+
}) => Promise<DrawElementToCanvasReturnValue>;
|
|
@@ -1,32 +1,6 @@
|
|
|
1
|
-
// Supported:
|
|
2
|
-
// - fontFamily
|
|
3
|
-
// - fontSize
|
|
4
|
-
// - fontWeight
|
|
5
|
-
// - color
|
|
6
|
-
// - lineHeight
|
|
7
|
-
// - direction
|
|
8
|
-
// - letterSpacing
|
|
9
|
-
// - textTransform
|
|
10
|
-
// Not supported:
|
|
11
|
-
// - writingMode
|
|
12
|
-
// - textDecoration
|
|
13
|
-
import { Internals } from 'remotion';
|
|
14
1
|
import { drawElementToCanvas } from '../draw-element-to-canvas';
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
const applyTextTransform = (text, transform) => {
|
|
18
|
-
if (transform === 'uppercase') {
|
|
19
|
-
return text.toUpperCase();
|
|
20
|
-
}
|
|
21
|
-
if (transform === 'lowercase') {
|
|
22
|
-
return text.toLowerCase();
|
|
23
|
-
}
|
|
24
|
-
if (transform === 'capitalize') {
|
|
25
|
-
return text.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
26
|
-
}
|
|
27
|
-
return text;
|
|
28
|
-
};
|
|
29
|
-
export const handleTextNode = async (node, context) => {
|
|
2
|
+
import { drawText } from './draw-text';
|
|
3
|
+
export const handleTextNode = async ({ node, context, offsetLeft, offsetTop, }) => {
|
|
30
4
|
const span = document.createElement('span');
|
|
31
5
|
const parent = node.parentNode;
|
|
32
6
|
if (!parent) {
|
|
@@ -34,48 +8,15 @@ export const handleTextNode = async (node, context) => {
|
|
|
34
8
|
}
|
|
35
9
|
parent.insertBefore(span, node);
|
|
36
10
|
span.appendChild(node);
|
|
37
|
-
await drawElementToCanvas({
|
|
11
|
+
const value = await drawElementToCanvas({
|
|
38
12
|
context,
|
|
39
13
|
element: span,
|
|
40
|
-
draw(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (isVertical) {
|
|
44
|
-
// TODO: Only warn once per render.
|
|
45
|
-
Internals.Log.warn({
|
|
46
|
-
logLevel: 'warn',
|
|
47
|
-
tag: '@remotion/web-renderer',
|
|
48
|
-
}, 'Detected "writing-mode" CSS property. Vertical text is not yet supported in @remotion/web-renderer');
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
context.save();
|
|
52
|
-
context.font = `${fontWeight} ${fontSize} ${fontFamily}`;
|
|
53
|
-
context.fillStyle = color;
|
|
54
|
-
context.letterSpacing = letterSpacing;
|
|
55
|
-
const fontSizePx = parseFloat(fontSize);
|
|
56
|
-
// TODO: This is not necessarily correct, need to create text and measure to know for sure
|
|
57
|
-
const lineHeightPx = lineHeight === 'normal' ? 1.2 * fontSizePx : parseFloat(lineHeight);
|
|
58
|
-
const baselineOffset = (lineHeightPx - fontSizePx) / 2;
|
|
59
|
-
const isRTL = direction === 'rtl';
|
|
60
|
-
context.textAlign = isRTL ? 'right' : 'left';
|
|
61
|
-
context.textBaseline = 'top';
|
|
62
|
-
const originalText = span.textContent;
|
|
63
|
-
const collapsedText = getCollapsedText(span);
|
|
64
|
-
const transformedText = applyTextTransform(collapsedText, textTransform);
|
|
65
|
-
span.textContent = transformedText;
|
|
66
|
-
// For RTL text, fill from the right edge instead of left
|
|
67
|
-
const xPosition = isRTL ? rect.right : rect.left;
|
|
68
|
-
const lines = findLineBreaks(span, isRTL);
|
|
69
|
-
let offsetTop = 0;
|
|
70
|
-
for (const line of lines) {
|
|
71
|
-
context.fillText(line.text, xPosition + line.offsetHorizontal, rect.top + baselineOffset + offsetTop);
|
|
72
|
-
offsetTop += line.offsetTop;
|
|
73
|
-
}
|
|
74
|
-
span.textContent = originalText;
|
|
75
|
-
context.restore();
|
|
76
|
-
},
|
|
14
|
+
draw: drawText(span),
|
|
15
|
+
offsetLeft,
|
|
16
|
+
offsetTop,
|
|
77
17
|
});
|
|
78
18
|
// Undo the layout manipulation
|
|
79
19
|
parent.insertBefore(node, span);
|
|
80
20
|
parent.removeChild(span);
|
|
21
|
+
return value;
|
|
81
22
|
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const transformIn3d: ({ canvasWidth, canvasHeight, matrix, sourceCanvas, offsetLeft, offsetTop, }: {
|
|
2
|
+
canvasWidth: number;
|
|
3
|
+
canvasHeight: number;
|
|
4
|
+
offsetLeft: number;
|
|
5
|
+
offsetTop: number;
|
|
6
|
+
matrix: DOMMatrix;
|
|
7
|
+
sourceCanvas: HTMLCanvasElement | OffscreenCanvas;
|
|
8
|
+
}) => OffscreenCanvas;
|