Units & measurements
Which units does Ydesign use internally?
Ydesign's canvas uses CSS pixels (px) throughout, with 1pt = 1px and 72 dpi as the default baseline. Every size-related value you see in store — width, height, fontSize, left, top, … — is in pixels.
This matches Fabric.js v6's coordinate system and keeps browser-native APIs (getBoundingClientRect, canvas dimensions, etc.) seamless.
Change the unit shown in the UI
While values are stored in pixels, you can tell the UI which unit to display and accept as input via store.setUnit (rulers, the size panel, print bleed, etc. follow along):
store.setUnit({
unit: 'mm', // 'px' | 'pt' | 'mm' | 'cm' | 'in'
dpi: 300,
});
After the switch:
store.unit→'mm'store.dpi→300- The ruler labels in millimeters
- The size panel inputs accept millimeter values
store.width/store.heightstay as pixels (internal storage is unchanged, only the UI converts)
📌 Supported units:
px,pt,mm,cm,in. Currentlyptandpxare treated 1:1.
Display units in custom UI
Ydesign exposes a unit conversion helper set. All built-in panels use the same functions — reuse them in your own components to avoid drift:
import {
pxToUnit,
pxToUnitRounded,
pxToUnitString,
unitToPx,
} from '@ydesign/react-editor/utils/unit';
pxToUnit — pixels → number
pxToUnit({ px: 100, unit: 'mm', dpi: 300 }); // => 8.466...
pxToUnit({ px: 300, unit: 'in', dpi: 300 }); // => 1
pxToUnitRounded — pixels → rounded number
For UI display (avoid long decimals):
pxToUnitRounded({ px: 100, unit: 'mm', dpi: 300 }); // => 8.47
pxToUnitRounded({ px: 100, unit: 'mm', dpi: 300, precious: 0 }); // => 8
pxToUnitString — pixels → string with unit suffix
Drop directly into labels:
pxToUnitString({ px: 2480, unit: 'mm', dpi: 300 }); // => '210mm'
pxToUnitString({ px: 300, unit: 'in', dpi: 300 }); // => '1in'
pxToUnitString({ px: 300, unit: 'px', dpi: 72 }); // => '300px'
unitToPx — number → pixels
Use this when accepting user input (e.g. "A4 width = 210mm" → canvas needs how many pixels?):
const widthPx = unitToPx({ unitVal: 210, unit: 'mm', dpi: 300 });
// => 2480.31...
store.setSize({
width: Math.round(widthPx),
height: Math.round(unitToPx({ unitVal: 297, unit: 'mm', dpi: 300 })),
});
Full example: a size input with unit switching
import { observer } from 'mobx-react-lite';
import { InputNumber, Select } from 'antd';
import { pxToUnitRounded, unitToPx } from '@ydesign/react-editor/utils/unit';
export const SizeInput = observer(({ store }) => {
const widthInCurrentUnit = pxToUnitRounded({
px: store.width,
unit: store.unit as any,
dpi: store.dpi,
precious: 2,
});
const handleChangeWidth = (val: number | null) => {
if (val == null) return;
const px = unitToPx({ unitVal: val, unit: store.unit, dpi: store.dpi });
store.setSize({ width: Math.round(px), height: store.height });
};
return (
<div style={{ display: 'flex', gap: 8 }}>
<InputNumber value={widthInCurrentUnit} onChange={handleChangeWidth} />
<Select
value={store.unit}
onChange={unit => store.setUnit({ unit, dpi: store.dpi })}
style={{ width: 80 }}
options={[
{ value: 'px', label: 'px' },
{ value: 'mm', label: 'mm' },
{ value: 'cm', label: 'cm' },
{ value: 'in', label: 'in' },
]}
/>
</div>
);
});
DPI on export
Client export (store.toDataURL / toBlob / saveAsImage)
Client-side export doesn't read store.dpi. Use the multiplier parameter to control output resolution:
// 1920×1080 canvas with multiplier=2 → 3840×2160 output
await store.saveAsImage({
multiplier: 2,
format: 'png',
});
Two common strategies:
| Goal | Recommended |
|---|---|
| High-res on-screen preview | multiplier: 2 |
| Print-grade 300 dpi | multiplier: 3 ~ 4 |
| Scale by the device pixel ratio | enableRetinaScaling: true |
Cloud render (/api/render/image)
Cloud Render API uses the same multiplier but supports up to 8× (client-side is typically capped at 4× by browser memory). For print:
{
"json": { /* ... */ },
"format": "png",
"multiplier": 4
}
Two approaches to "high-res export"
When your product needs both on-screen previews (72 dpi) and print deliverables (300 dpi), two patterns work:
Approach A: fixed-pixel canvas + multiplier on export (recommended)
store.setSize({ width: 2480, height: 3508 })(A4 @ 300 dpi in pixels)- Screen preview:
multiplier: 1 - Print export:
multiplier: 1withformat: 'png'(the canvas is already at 300 dpi)
Pro: pixel positions match exactly between preview and print — no drift.
Approach B: 72 dpi canvas + scale up at export
store.setSize({ width: 595, height: 842 })(A4 @ 72 dpi)- Preview:
multiplier: 1 - Print:
multiplier: 4(≈ 288 dpi)
Pro: smaller canvas = faster renders + lighter JSON. Con: small text / fine strokes may drift by a pixel or two when scaled.
Pick whichever fits your product.
See also
- 👉 Utility Functions · unit conversion — full API & signatures
- 👉 Size panel — customize size presets & unit picker UI
- 👉 Cloud Render API — server-side high-res rendering
- 👉 Scenes & import/export — client-side export options