-
-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
26 changed files
with
1,245 additions
and
74 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
--- | ||
'@pintora/core': patch | ||
'@pintora/renderer': patch | ||
'@pintora/standalone': patch | ||
'@pintora/target-wintercg': patch | ||
--- | ||
|
||
Be able to inject text-metric calculator in case there is no Canvas impl in the environment. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,52 +1,89 @@ | ||
import type { TSize } from './geometry' | ||
export interface IFont { | ||
fontFamily?: string | ||
fontSize: number | ||
fontWeight?: any | ||
} | ||
|
||
export function calculateTextDimensions(text: string, font?: IFont) { | ||
const lines = text.split('\n') | ||
let width = 0 | ||
let height = 0 | ||
const fontSize = font?.fontSize || 14 | ||
lines.forEach((line, i) => { | ||
const lineMetric = getLineMetric(line, font) | ||
// console.log('line metric', line, lineMetric) | ||
const w = lineMetric.width | ||
width = Math.max(w, width) | ||
// svg renderer antv/g currently adds tspan dy with '1em', which matches fontSize | ||
// so we will calculate height with similar method | ||
// TODO: but it has some differences with canvas | ||
let lineHeight = fontSize | ||
if (i === 0) { | ||
if ('actualBoundingBoxDescent' in lineMetric) { | ||
lineHeight = lineMetric.actualBoundingBoxAscent + lineMetric.actualBoundingBoxDescent | ||
export interface ITextMetricCalculator { | ||
name?: string | ||
calculateTextDimensions(text: string, font?: IFont): TSize | ||
} | ||
|
||
export type TTextMetrics = Pick<TextMetrics, 'actualBoundingBoxAscent' | 'actualBoundingBoxDescent' | 'width'> | ||
|
||
/** | ||
* Use canvas Context2D `measureText()` method to calculate text metrics. | ||
*/ | ||
class CanvasTextMetricCalculator implements ITextMetricCalculator { | ||
name = 'CanvasTextMetricCalculator' | ||
ctx: CanvasRenderingContext2D | undefined = undefined | ||
|
||
calculateTextDimensions(text: string, font?: IFont) { | ||
const lines = text.split('\n') | ||
let width = 0 | ||
let height = 0 | ||
const fontSize = font?.fontSize || 14 | ||
lines.forEach((line, i) => { | ||
const lineMetric = this.getLineMetric(line, font) | ||
// console.log('line metric', line, lineMetric) | ||
const w = lineMetric.width | ||
width = Math.max(w, width) | ||
// svg renderer antv/g currently adds tspan dy with '1em', which matches fontSize | ||
// so we will calculate height with similar method | ||
// TODO: but it has some differences with canvas | ||
let lineHeight = fontSize | ||
if (i === 0) { | ||
if ('actualBoundingBoxDescent' in lineMetric) { | ||
lineHeight = lineMetric.actualBoundingBoxAscent + lineMetric.actualBoundingBoxDescent | ||
} | ||
} | ||
height += lineHeight | ||
}) | ||
// console.log('calculateTextDimensions:', text, width, height) | ||
return { | ||
width, | ||
height, | ||
} | ||
} | ||
|
||
getLineMetric(text: string, font?: IFont) { | ||
const fontSize = font?.fontSize || 14 | ||
const fontFamily = font?.fontFamily || 'sans-serif' | ||
const ctx = this.getCanvasContext() | ||
ctx.font = `${fontSize}px ${fontFamily}` | ||
return ctx.measureText(text) | ||
} | ||
|
||
/** A helper canvas context 2D for measuring text */ | ||
getCanvasContext = () => { | ||
if (!this.ctx) { | ||
const canvas = document.createElement('canvas') | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
this.ctx = canvas.getContext('2d')! | ||
} | ||
height += lineHeight | ||
}) | ||
// console.log('calculateTextDimensions', text, width, height) | ||
return { | ||
width, | ||
height, | ||
return this.ctx | ||
} | ||
} | ||
|
||
let ctx: CanvasRenderingContext2D | ||
/** A helper canvas context 2D for measuring text */ | ||
const getCanvasContext = () => { | ||
if (!ctx) { | ||
const canvas = document.createElement('canvas') | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
ctx = canvas.getContext('2d')! | ||
const canvasTextCalculator = new CanvasTextMetricCalculator() | ||
|
||
/** | ||
* A bridge for text metric calculator, can be set to use different implementation in different environment. | ||
* By default it uses {@link CanvasTextMetricCalculator}. | ||
*/ | ||
class TextMetricBridge implements ITextMetricCalculator { | ||
protected calculator: ITextMetricCalculator = canvasTextCalculator | ||
setImpl(calculator: ITextMetricCalculator) { | ||
this.calculator = calculator | ||
} | ||
calculateTextDimensions(text: string, font?: IFont | undefined) { | ||
return this.calculator.calculateTextDimensions(text, font) | ||
} | ||
return ctx | ||
} | ||
|
||
function getLineMetric(text: string, font?: IFont) { | ||
const fontSize = font?.fontSize || 14 | ||
const fontFamily = font?.fontFamily || 'sans-serif' | ||
const ctx = getCanvasContext() | ||
ctx.font = `${fontSize}px ${fontFamily}` | ||
return ctx.measureText(text) | ||
export const textMetrics = new TextMetricBridge() | ||
|
||
export function calculateTextDimensions(text: string, font?: IFont) { | ||
return textMetrics.calculateTextDimensions(text, font) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
dist/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
Stil at a very early stage. | ||
|
||
Bundle a big JS including pintora and its dependencies and some Node.js module polyfills, so that it can run inside a WinterCG or other lightweight JS runtime. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module.exports = {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
const URL = require('url') | ||
|
||
module.exports = { | ||
...URL, | ||
fileURLToPath(s) { | ||
return s || '' | ||
}, | ||
} |
98 changes: 98 additions & 0 deletions
98
packages/pintora-target-wintercg/build/ESBuildNodePolyfillsPlugin.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import * as path from 'path' | ||
import { Plugin } from 'esbuild' | ||
|
||
const nodeModulesDir = path.resolve(__dirname, '../node_modules') | ||
|
||
export const ESBuildNodePolyfillsPlugin: Plugin = { | ||
name: 'ESBuildNodePolyfillsPlugin', | ||
setup(build) { | ||
const nodeGlobalsToBeIgnored = /^((tls)|(assert)|(fs)|(net))$/ | ||
build.onResolve({ filter: nodeGlobalsToBeIgnored }, args => { | ||
return { path: args.path, namespace: 'do-nothing' } | ||
}) | ||
|
||
// build.onResolve({ filter: /node:path/ }, args => { | ||
// return { namespace: 'path-browserify' } | ||
// }) | ||
|
||
// Resolve Stream Modules | ||
build.onResolve( | ||
{ | ||
filter: /(_stream_duplex)|(_stream_passthrough)|(_stream_readable)|(_stream_transform)|(_stream_writable)/, | ||
}, | ||
args => { | ||
const pPrefix = [nodeModulesDir, 'readable-stream', 'lib'] | ||
let p | ||
if (args.path.includes('_stream_duplex')) p = path.join(...pPrefix, '_stream_duplex.js') | ||
if (args.path.includes('_stream_passthrough')) p = path.join(...pPrefix, '_stream_passthrough.js') | ||
if (args.path.includes('_stream_readable')) p = path.join(...pPrefix, '_stream_readable.js') | ||
if (args.path.includes('_stream_transform')) p = path.join(...pPrefix, '_stream_transform.js') | ||
if (args.path.includes('_stream_writable')) p = path.join(...pPrefix, '_stream_writable.js') | ||
return { path: p } | ||
}, | ||
) | ||
|
||
// // Special Case for the "SAP Cloud SDK for JavaScript" | ||
// build.onResolve({ filter: /.*\/internal\/streams\/stream/ }, (args) => ({ | ||
// path: path.join(__dirname, "../", "stream-browserify", "index.js"), | ||
// })); | ||
|
||
// Resolve other packages | ||
build.onResolve( | ||
{ | ||
filter: | ||
/^((buffer)|(crypto)|(http)|(https)|(os)|(path)|(node:path)|(stream)|(zlib)|(url)|(events)|(process)|(util))$/, | ||
}, | ||
args => { | ||
const pPrefix = [nodeModulesDir] | ||
let p | ||
switch (args.path) { | ||
case 'buffer': | ||
p = path.join(...pPrefix, 'buffer', 'index.js') | ||
break | ||
case 'crypto': | ||
p = path.join(...pPrefix, 'crypto-browserify', 'index.js') | ||
break | ||
case 'http': | ||
p = path.join(...pPrefix, 'stream-http', 'index.js') | ||
break | ||
case 'https': | ||
p = path.join(...pPrefix, 'https-browserify', 'index.js') | ||
break | ||
case 'os': | ||
p = path.join(...pPrefix, 'os-browserify', 'browser.js') | ||
break | ||
case 'node:path': | ||
p = path.join(...pPrefix, 'path-browserify', 'index.js') | ||
break | ||
case 'path': | ||
p = path.join(...pPrefix, 'path-browserify', 'index.js') | ||
break | ||
// case "stream": | ||
// p = path.join(...pPrefix, "stream-browserify", "index.js"); | ||
// break; | ||
case 'zlib': | ||
p = path.join(...pPrefix, 'browserify-zlib', 'lib', 'index.js') | ||
break | ||
case 'events': | ||
p = path.join(...pPrefix, 'events', 'events.js') | ||
break | ||
case 'process': | ||
p = path.join(...pPrefix, 'process', 'browser.js') | ||
break | ||
case 'util': | ||
p = path.join(...pPrefix, 'util', 'util.js') | ||
break | ||
} | ||
if (p) { | ||
return { path: p, external: false } | ||
} | ||
}, | ||
) | ||
|
||
// Do nothing on specified fields | ||
build.onLoad({ filter: /.*/, namespace: 'do-nothing' }, args => ({ | ||
contents: 'export default false;', | ||
})) | ||
}, | ||
} |
21 changes: 21 additions & 0 deletions
21
packages/pintora-target-wintercg/build/ESBuildPintoraRuntimePlugin.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import * as fs from 'fs' | ||
import { Plugin } from 'esbuild' | ||
|
||
const RUNTIME_CODE_NS = 'pintora-runtime-code' | ||
|
||
export function makeESBuildNodePolyfillsPlugin(opts: { runtimeLibPath: string }) { | ||
return { | ||
name: 'ESBuildNodePolyfillsPlugin', | ||
setup(build) { | ||
build.onResolve({ filter: /virtual:pintora/ }, args => { | ||
return { namespace: RUNTIME_CODE_NS, path: opts.runtimeLibPath } | ||
}) | ||
build.onLoad({ filter: /.*/, namespace: RUNTIME_CODE_NS }, args => { | ||
const contents = fs.readFileSync(opts.runtimeLibPath) | ||
return { | ||
contents, | ||
} | ||
}) | ||
}, | ||
} as Plugin | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import * as path from 'path' | ||
import * as fs from 'fs' | ||
import { build, BuildOptions } from 'esbuild' | ||
import { ESBuildNodePolyfillsPlugin } from './ESBuildNodePolyfillsPlugin' | ||
|
||
const packageDir = path.resolve(__dirname, '..') | ||
const aliasDir = path.resolve(__dirname, '../aliases') | ||
|
||
const runtimeOutFilePath = path.join(packageDir, 'dist/runtime.js') | ||
|
||
const options: BuildOptions = { | ||
entryPoints: ['runtime/index.ts'], | ||
bundle: true, | ||
outfile: runtimeOutFilePath, | ||
format: 'iife', | ||
globalName: 'pintoraTarget', | ||
// sourcemap: 'external', | ||
treeShaking: true, | ||
alias: { | ||
canvas: path.join(aliasDir, 'canvas.js'), | ||
fs: path.join(aliasDir, 'canvas.js'), | ||
'node:url': path.join(aliasDir, 'url.js'), | ||
}, | ||
plugins: [ESBuildNodePolyfillsPlugin], | ||
loader: { | ||
'.ttf': 'binary', | ||
}, | ||
write: true, | ||
} | ||
|
||
build(options).then(afterLibEsbuild) | ||
|
||
async function afterLibEsbuild() { | ||
console.log('afterLibEsbuild, generate platform code') | ||
const plugBuild = await build({ | ||
entryPoints: ['src/platforms/edge-handler.ts'], | ||
bundle: true, | ||
format: 'iife', | ||
sourcemap: false, | ||
write: false, | ||
}) | ||
const runtimeLibCode = fs.readFileSync(runtimeOutFilePath, 'utf-8').toString() | ||
const outdir = path.join(packageDir, 'dist/platforms') | ||
if (!fs.existsSync(outdir)) { | ||
fs.mkdirSync(outdir) | ||
} | ||
const handlerCode = ` | ||
${runtimeLibCode} | ||
// separation | ||
${plugBuild.outputFiles[0].text} | ||
` | ||
fs.writeFileSync(path.join(packageDir, 'dist/platforms/edge-handler.js'), handlerCode) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"extends": "../tsconfig.json", | ||
"compilerOptions": { | ||
"rootDir": ".", | ||
"types": ["node"] | ||
}, | ||
"include": ["."] | ||
} |
Binary file not shown.
Oops, something went wrong.