mirror of
https://github.com/penpot/penpot.git
synced 2025-12-11 22:14:05 +01:00
🎉 Add multiple fonts to text editor WASM playground
This commit is contained in:
958
frontend/text-editor/src/fonts.css
Normal file
958
frontend/text-editor/src/fonts.css
Normal file
@@ -0,0 +1,958 @@
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 100;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTjWacfw6zH4dthXcyms1lPpC8I_b0juU057p-xEJZj11l4.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 100;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTjWacfw6zH4dthXcyms1lPpC8I_b0juU057p-xEJ9j11l4.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 100;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTjWacfw6zH4dthXcyms1lPpC8I_b0juU057p-xEJRj11l4.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 100;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTjWacfw6zH4dthXcyms1lPpC8I_b0juU057p-xEJVj11l4.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 100;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTjWacfw6zH4dthXcyms1lPpC8I_b0juU057p-xEJtj1w.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 200;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p8dAYxJ8mRBkw.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 200;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p8dAYxA8mRBkw.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 200;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p8dAYxL8mRBkw.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 200;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p8dAYxK8mRBkw.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 200;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p8dAYxE8mQ.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p95AoxJ8mRBkw.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p95AoxA8mRBkw.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p95AoxL8mRBkw.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p95AoxK8mRBkw.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p95AoxE8mQ.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFThWacfw6zH4dthXcyms1lPpC8I_b0juU057pffIJl70w.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFThWacfw6zH4dthXcyms1lPpC8I_b0juU057pfWIJl70w.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFThWacfw6zH4dthXcyms1lPpC8I_b0juU057pfdIJl70w.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFThWacfw6zH4dthXcyms1lPpC8I_b0juU057pfcIJl70w.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFThWacfw6zH4dthXcyms1lPpC8I_b0juU057pfSIJk.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p8hA4xJ8mRBkw.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p8hA4xA8mRBkw.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p8hA4xL8mRBkw.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p8hA4xK8mRBkw.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p8hA4xE8mQ.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p8NBIxJ8mRBkw.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p8NBIxA8mRBkw.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p8NBIxL8mRBkw.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p8NBIxK8mRBkw.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p8NBIxE8mQ.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p9pBYxJ8mRBkw.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p9pBYxA8mRBkw.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p9pBYxL8mRBkw.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p9pBYxK8mRBkw.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p9pBYxE8mQ.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 800;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p91BoxJ8mRBkw.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 800;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p91BoxA8mRBkw.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 800;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p91BoxL8mRBkw.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 800;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p91BoxK8mRBkw.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 800;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p91BoxE8mQ.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 900;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p9RB4xJ8mRBkw.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 900;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p9RB4xA8mRBkw.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 900;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p9RB4xL8mRBkw.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 900;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p9RB4xK8mRBkw.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: italic;
|
||||
font-weight: 900;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTkWacfw6zH4dthXcyms1lPpC8I_b0juU057p9RB4xE8mQ.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 100;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFThWacfw6zH4dthXcyms1lPpC8I_b0juU0xiJffIJl70w.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 100;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFThWacfw6zH4dthXcyms1lPpC8I_b0juU0xiJfWIJl70w.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 100;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFThWacfw6zH4dthXcyms1lPpC8I_b0juU0xiJfdIJl70w.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 100;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFThWacfw6zH4dthXcyms1lPpC8I_b0juU0xiJfcIJl70w.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 100;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFThWacfw6zH4dthXcyms1lPpC8I_b0juU0xiJfSIJk.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 200;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xJIbFCrxG6mA.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 200;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xJIbFA7xG6mA.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 200;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xJIbFCLxG6mA.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 200;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xJIbFCbxG6mA.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 200;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xJIbFB7xG.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xQIXFCrxG6mA.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xQIXFA7xG6mA.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xQIXFCLxG6mA.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xQIXFCbxG6mA.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xQIXFB7xG.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTvWacfw6zH4dthXcyms1lPpC8I_b0juU055qfQOJ0.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTvWacfw6zH4dthXcyms1lPpC8I_b0juU0576fQOJ0.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTvWacfw6zH4dthXcyms1lPpC8I_b0juU055KfQOJ0.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTvWacfw6zH4dthXcyms1lPpC8I_b0juU055afQOJ0.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTvWacfw6zH4dthXcyms1lPpC8I_b0juU0566fQ.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xGITFCrxG6mA.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xGITFA7xG6mA.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xGITFCLxG6mA.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xGITFCbxG6mA.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xGITFB7xG.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xNIPFCrxG6mA.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xNIPFA7xG6mA.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xNIPFCLxG6mA.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xNIPFCbxG6mA.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xNIPFB7xG.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xUILFCrxG6mA.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xUILFA7xG6mA.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xUILFCLxG6mA.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xUILFCbxG6mA.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xUILFB7xG.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xTIHFCrxG6mA.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xTIHFA7xG6mA.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xTIHFCLxG6mA.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xTIHFCbxG6mA.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xTIHFB7xG.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xaIDFCrxG6mA.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xaIDFA7xG6mA.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xaIDFCLxG6mA.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xaIDFCbxG6mA.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'MontserratAlternates';
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/montserratalternates/v17/mFTiWacfw6zH4dthXcyms1lPpC8I_b0juU0xaIDFB7xG.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Oswald';
|
||||
font-style: normal;
|
||||
font-weight: 200 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/oswald/v56/TK3iWkUHHAIjg752FD8Ghe4.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Oswald';
|
||||
font-style: normal;
|
||||
font-weight: 200 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/oswald/v56/TK3iWkUHHAIjg752HT8Ghe4.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Oswald';
|
||||
font-style: normal;
|
||||
font-weight: 200 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/oswald/v56/TK3iWkUHHAIjg752Fj8Ghe4.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Oswald';
|
||||
font-style: normal;
|
||||
font-weight: 200 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/oswald/v56/TK3iWkUHHAIjg752Fz8Ghe4.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Oswald';
|
||||
font-style: normal;
|
||||
font-weight: 200 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/oswald/v56/TK3iWkUHHAIjg752GT8G.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* fallback */
|
||||
@font-face {
|
||||
font-family: 'Playwrite MX Guides';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/playwritemxguides/v1/k3kMo9ESPe9dzQ1UGbvoZhnhbtfklWqN0qk.woff2) format('woff2');
|
||||
}
|
||||
@@ -2,225 +2,551 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&family=Playwrite+ES:wght@100..400&family=Playwrite+NZ:wght@100..400&family=Playwrite+US+Trad:wght@100..400&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap" rel="stylesheet">
|
||||
<link rel="icon" type="image/svg+xml" href="/javascript.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Penpot - Text Editor Playground</title>
|
||||
<style>
|
||||
#output {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<form>
|
||||
<fieldset>
|
||||
<legend>Styles</legend>
|
||||
<!-- Font -->
|
||||
<div class="form-group">
|
||||
<label for="font-family">Font family</label>
|
||||
<select id="font-family">
|
||||
<option value="Open+Sans">Open Sans</option>
|
||||
<option value="sourcesanspro">Source Sans Pro</option>
|
||||
<option value="whatever">Whatever</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="font-size">Font size</label>
|
||||
<input id="font-size" type="number" value="14" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="font-weight">Font weight</label>
|
||||
<select id="font-weight">
|
||||
<option value="100">100</option>
|
||||
<option value="200">200</option>
|
||||
<option value="300">300</option>
|
||||
<option value="400">400 (normal)</option>
|
||||
<option value="500">500</option>
|
||||
<option value="600">600</option>
|
||||
<option value="700">700 (bold)</option>
|
||||
<option value="800">800</option>
|
||||
<option value="900">900</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="font-style">Font style</label>
|
||||
<select id="font-style">
|
||||
<option value="normal">normal</option>
|
||||
<option value="italic">italic</option>
|
||||
<option value="oblique">oblique</option>
|
||||
</select>
|
||||
</div>
|
||||
<!-- Text attributes -->
|
||||
<div class="form-group">
|
||||
<label for="line-height">Line height</label>
|
||||
<input id="line-height" type="number" value="1.0" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="letter-spacing">Letter spacing</label>
|
||||
<input id="letter-spacing" type="number" value="0.0" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="direction-ltr">LTR</label>
|
||||
<input id="direction-ltr" type="radio" name="direction" value="ltr" checked />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="direction-rtl">RTL</label>
|
||||
<input id="direction-rtl" type="radio" name="direction" value="rtl" />
|
||||
</div>
|
||||
<!-- Text Align -->
|
||||
<div class="form-group">
|
||||
<label for="text-align-left">Align left</label>
|
||||
<input id="text-align-left" type="radio" name="text-align" value="left" checked />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="text-align-center">Align center</label>
|
||||
<input id="text-align-center" type="radio" name="text-align" value="center" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="text-align-right">Align right</label>
|
||||
<input id="text-align-right" type="radio" name="text-align" value="right" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="text-align-justify">Align justify</label>
|
||||
<input id="text-align-justify" type="radio" name="text-align" value="justify" />
|
||||
</div>
|
||||
<!-- Text Transform -->
|
||||
<div class="form-group">
|
||||
<label for="text-transform-none">None</label>
|
||||
<input id="text-transform-none" type="radio" name="text-transform" value="none" checked />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="text-transform-uppercase">Uppercase</label>
|
||||
<input id="text-transform-uppercase" type="radio" name="text-transform" value="uppercase" checked />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="text-transform-capitalize">Capitalize</label>
|
||||
<input id="text-transform-capitalize" type="radio" name="text-transform" value="capitalize" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="text-transform-lowercase">Lowercase</label>
|
||||
<input id="text-transform-lowercase" type="radio" name="text-transform" value="lowercase" />
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Debug</legend>
|
||||
<div class="form-group">
|
||||
<label for="direction">Direction</label>
|
||||
<input id="direction" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="focus-node">Focus Node</label>
|
||||
<input id="focus-node" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="focus-offset">Focus offset</label>
|
||||
<input id="focus-offset" readonly type="number">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="focus-inline">Focus Inline</label>
|
||||
<input id="focus-inline" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="focus-paragraph">Focus Paragraph</label>
|
||||
<input id="focus-paragraph" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="anchor-node">Anchor Node</label>
|
||||
<input id="anchor-node" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="anchor-offset">Anchor offset</label>
|
||||
<input id="anchor-offset" readonly type="number">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="anchor-inline">Anchor Inline</label>
|
||||
<input id="anchor-inline" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="anchor-paragraph">Anchor Paragraph</label>
|
||||
<input id="anchor-paragraph" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="start-container">Start container</label>
|
||||
<input id="start-container" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="start-offset">Start offset</label>
|
||||
<input id="start-offset" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="end-container">End container</label>
|
||||
<input id="end-container" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="end-offset">End offset</label>
|
||||
<input id="end-offset" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="multi">Multi?</label>
|
||||
<input id="multi" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="multi-inline">Multi inline?</label>
|
||||
<input id="multi-inline" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="multi-paragraph">Multi paragraph?</label>
|
||||
<input id="multi-paragraph" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="is-text-focus">Is text focus?</label>
|
||||
<input id="is-text-focus" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="is-text-anchor">Is text anchor?</label>
|
||||
<input id="is-text-anchor" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="is-paragraph-start">Is paragraph start?</label>
|
||||
<input id="is-paragraph-start" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="is-paragraph-end">Is paragraph end?</label>
|
||||
<input id="is-paragraph-end" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="is-inline-start">Is inline start?</label>
|
||||
<input id="is-inline-start" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="is-inline-end">Is inline end?</label>
|
||||
<input id="is-inline-end" readonly type="checkbox">
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
<!--
|
||||
<div class="playground">
|
||||
<form>
|
||||
<fieldset>
|
||||
<legend>Styles</legend>
|
||||
<!-- Font -->
|
||||
<div class="form-group">
|
||||
<label for="font-family">Font family</label>
|
||||
<select id="font-family">
|
||||
<option value="Open+Sans">Open Sans</option>
|
||||
<option value="sourcesanspro">Source Sans Pro</option>
|
||||
<option value="whatever">Whatever</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="font-size">Font size</label>
|
||||
<input id="font-size" type="number" value="14" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="font-weight">Font weight</label>
|
||||
<select id="font-weight">
|
||||
<option value="100">100</option>
|
||||
<option value="200">200</option>
|
||||
<option value="300">300</option>
|
||||
<option value="400">400 (normal)</option>
|
||||
<option value="500">500</option>
|
||||
<option value="600">600</option>
|
||||
<option value="700">700 (bold)</option>
|
||||
<option value="800">800</option>
|
||||
<option value="900">900</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="font-style">Font style</label>
|
||||
<select id="font-style">
|
||||
<option value="normal">normal</option>
|
||||
<option value="italic">italic</option>
|
||||
<option value="oblique">oblique</option>
|
||||
</select>
|
||||
</div>
|
||||
<!-- Text attributes -->
|
||||
<div class="form-group">
|
||||
<label for="line-height">Line height</label>
|
||||
<input id="line-height" type="number" value="1.0" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="letter-spacing">Letter spacing</label>
|
||||
<input id="letter-spacing" type="number" value="0.0" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="direction-ltr">LTR</label>
|
||||
<input id="direction-ltr" type="radio" name="direction" value="ltr" checked />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="direction-rtl">RTL</label>
|
||||
<input id="direction-rtl" type="radio" name="direction" value="rtl" />
|
||||
</div>
|
||||
<!-- Text Align -->
|
||||
<div class="form-group">
|
||||
<label for="text-align-left">Align left</label>
|
||||
<input id="text-align-left" type="radio" name="text-align" value="left" checked />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="text-align-center">Align center</label>
|
||||
<input id="text-align-center" type="radio" name="text-align" value="center" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="text-align-right">Align right</label>
|
||||
<input id="text-align-right" type="radio" name="text-align" value="right" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="text-align-justify">Align justify</label>
|
||||
<input id="text-align-justify" type="radio" name="text-align" value="justify" />
|
||||
</div>
|
||||
<!-- Text Transform -->
|
||||
<div class="form-group">
|
||||
<label for="text-transform-none">None</label>
|
||||
<input id="text-transform-none" type="radio" name="text-transform" value="none" checked />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="text-transform-uppercase">Uppercase</label>
|
||||
<input id="text-transform-uppercase" type="radio" name="text-transform" value="uppercase" checked />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="text-transform-capitalize">Capitalize</label>
|
||||
<input id="text-transform-capitalize" type="radio" name="text-transform" value="capitalize" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="text-transform-lowercase">Lowercase</label>
|
||||
<input id="text-transform-lowercase" type="radio" name="text-transform" value="lowercase" />
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Debug</legend>
|
||||
<div class="form-group">
|
||||
<label for="direction">Direction</label>
|
||||
<input id="direction" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="focus-node">Focus Node</label>
|
||||
<input id="focus-node" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="focus-offset">Focus offset</label>
|
||||
<input id="focus-offset" readonly type="number">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="focus-inline">Focus Inline</label>
|
||||
<input id="focus-inline" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="focus-paragraph">Focus Paragraph</label>
|
||||
<input id="focus-paragraph" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="anchor-node">Anchor Node</label>
|
||||
<input id="anchor-node" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="anchor-offset">Anchor offset</label>
|
||||
<input id="anchor-offset" readonly type="number">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="anchor-inline">Anchor Inline</label>
|
||||
<input id="anchor-inline" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="anchor-paragraph">Anchor Paragraph</label>
|
||||
<input id="anchor-paragraph" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="start-container">Start container</label>
|
||||
<input id="start-container" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="start-offset">Start offset</label>
|
||||
<input id="start-offset" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="end-container">End container</label>
|
||||
<input id="end-container" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="end-offset">End offset</label>
|
||||
<input id="end-offset" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="multi">Multi?</label>
|
||||
<input id="multi" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="multi-inline">Multi inline?</label>
|
||||
<input id="multi-inline" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="multi-paragraph">Multi paragraph?</label>
|
||||
<input id="multi-paragraph" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="is-text-focus">Is text focus?</label>
|
||||
<input id="is-text-focus" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="is-text-anchor">Is text anchor?</label>
|
||||
<input id="is-text-anchor" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="is-paragraph-start">Is paragraph start?</label>
|
||||
<input id="is-paragraph-start" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="is-paragraph-end">Is paragraph end?</label>
|
||||
<input id="is-paragraph-end" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="is-inline-start">Is inline start?</label>
|
||||
<input id="is-inline-start" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="is-inline-end">Is inline end?</label>
|
||||
<input id="is-inline-end" readonly type="checkbox">
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
<!--
|
||||
|
||||
Editor
|
||||
Editor
|
||||
|
||||
-->
|
||||
<div class="text-editor-container align-top">
|
||||
<div
|
||||
id="text-editor-selection-imposter"
|
||||
class="text-editor-selection-imposter"></div>
|
||||
<div
|
||||
class="text-editor-content"
|
||||
contenteditable="true"
|
||||
role="textbox"
|
||||
aria-multiline="true"
|
||||
aria-autocomplete="none"
|
||||
spellcheck="false"
|
||||
autocapitalize="false"></div>
|
||||
-->
|
||||
<div class="text-editor-container align-top">
|
||||
<div
|
||||
id="text-editor-selection-imposter"
|
||||
class="text-editor-selection-imposter"></div>
|
||||
<div
|
||||
class="text-editor-content"
|
||||
contenteditable="true"
|
||||
role="textbox"
|
||||
aria-multiline="true"
|
||||
aria-autocomplete="none"
|
||||
spellcheck="false"
|
||||
autocapitalize="false"></div>
|
||||
</div>
|
||||
<!--
|
||||
|
||||
Text output
|
||||
|
||||
-->
|
||||
<canvas id="canvas"></canvas>
|
||||
</div>
|
||||
<!--
|
||||
<script type="module">
|
||||
import "./style.css";
|
||||
import "./fonts.css";
|
||||
import "./editor/TextEditor.css";
|
||||
import { TextEditor } from "./editor/TextEditor";
|
||||
import { SelectionControllerDebug } from "./editor/debug/SelectionControllerDebug";
|
||||
import initWasmModule from './wasm/render_wasm.js';
|
||||
import {
|
||||
init, assignCanvas, render, setupInteraction, useShape, setShapeChildren, addTextShape, updateTextShape, hexToU32ARGB,getRandomInt, getRandomColor, getRandomFloat, addShapeSolidFill, addShapeSolidStrokeFill, storeFonts
|
||||
} from './wasm/lib.js';
|
||||
|
||||
Text output
|
||||
async function loadFontList() {
|
||||
const response = await fetch('fonts/fonts.txt')
|
||||
const text = await response.text()
|
||||
const fonts = text.split('\n').filter(l => !!l)
|
||||
return fonts
|
||||
}
|
||||
|
||||
function getFontStyleWeight(fontStyleName) {
|
||||
if (fontStyleName.startsWith('Thin')) {
|
||||
return 100
|
||||
} else if (fontStyleName.startsWith('ExtraLight')) {
|
||||
return 200
|
||||
} else if (fontStyleName.startsWith('Light')) {
|
||||
return 300
|
||||
} else if (fontStyleName.startsWith('Regular')) {
|
||||
return 400
|
||||
} else if (fontStyleName.startsWith('Medium')) {
|
||||
return 500
|
||||
} else if (fontStyleName.startsWith('SemiBold')) {
|
||||
return 600
|
||||
} else if (fontStyleName.startsWith('Bold')) {
|
||||
return 700
|
||||
} else if (fontStyleName.startsWith('ExtraBold')) {
|
||||
return 800
|
||||
} else if (fontStyleName.startsWith('Black')) {
|
||||
return 900
|
||||
} else {
|
||||
return 400
|
||||
}
|
||||
}
|
||||
|
||||
async function loadFonts() {
|
||||
const fontData = new Map()
|
||||
const fonts = await loadFontList()
|
||||
for (const font of fonts) {
|
||||
const response = await fetch(`fonts/${font}`)
|
||||
const arrayBuffer = await response.arrayBuffer()
|
||||
const [fontName, fontStyleNameAndExtension] = font.split('-')
|
||||
const [fontStyleName, extension] = fontStyleNameAndExtension.split('.')
|
||||
if (!fontData.has(fontName)) {
|
||||
fontData.set(fontName, [])
|
||||
}
|
||||
const id = crypto.randomUUID()
|
||||
const weight = getFontStyleWeight(fontStyleName)
|
||||
const isItalic = (fontStyleName.endsWith('Italic'))
|
||||
const currentFontData = fontData.get(fontName)
|
||||
currentFontData.push({
|
||||
id,
|
||||
url: `fonts/${font}`,
|
||||
name: fontName,
|
||||
weight: weight,
|
||||
style: isItalic ? 'italic' : 'normal',
|
||||
arrayBuffer,
|
||||
})
|
||||
}
|
||||
return fontData
|
||||
}
|
||||
|
||||
const searchParams = new URLSearchParams(location.search);
|
||||
const debug = searchParams.has("debug")
|
||||
? searchParams.get("debug").split(",")
|
||||
: [];
|
||||
|
||||
const textEditorSelectionImposterElement = document.getElementById(
|
||||
"text-editor-selection-imposter",
|
||||
);
|
||||
|
||||
const textEditorElement = document.querySelector(".text-editor-content");
|
||||
const textEditor = new TextEditor(textEditorElement, {
|
||||
styleDefaults: {
|
||||
"font-family": "MontserratAlternates",
|
||||
"font-size": "14",
|
||||
"font-weight": "500",
|
||||
"font-style": "normal",
|
||||
"line-height": "1.2",
|
||||
"letter-spacing": "0",
|
||||
direction: "ltr",
|
||||
"text-align": "left",
|
||||
"text-transform": "none",
|
||||
"text-decoration": "none",
|
||||
"--typography-ref-id": '["~#\'",null]',
|
||||
"--typography-ref-file": '["~#\'",null]',
|
||||
"--font-id": '["~#\'","MontserratAlternates"]',
|
||||
"--fills": '[["^ ","~:fill-color","#000000","~:fill-opacity",1]]',
|
||||
},
|
||||
selectionImposterElement: textEditorSelectionImposterElement,
|
||||
debug: new SelectionControllerDebug({
|
||||
direction: document.getElementById("direction"),
|
||||
multiElement: document.getElementById("multi"),
|
||||
multiInlineElement: document.getElementById("multi-inline"),
|
||||
multiParagraphElement: document.getElementById("multi-paragraph"),
|
||||
isParagraphStart: document.getElementById("is-paragraph-start"),
|
||||
isParagraphEnd: document.getElementById("is-paragraph-end"),
|
||||
isInlineStart: document.getElementById("is-inline-start"),
|
||||
isInlineEnd: document.getElementById("is-inline-end"),
|
||||
isTextAnchor: document.getElementById("is-text-anchor"),
|
||||
isTextFocus: document.getElementById("is-text-focus"),
|
||||
focusNode: document.getElementById("focus-node"),
|
||||
focusOffset: document.getElementById("focus-offset"),
|
||||
focusInline: document.getElementById("focus-inline"),
|
||||
focusParagraph: document.getElementById("focus-paragraph"),
|
||||
anchorNode: document.getElementById("anchor-node"),
|
||||
anchorOffset: document.getElementById("anchor-offset"),
|
||||
anchorInline: document.getElementById("anchor-inline"),
|
||||
anchorParagraph: document.getElementById("anchor-paragraph"),
|
||||
startContainer: document.getElementById("start-container"),
|
||||
startOffset: document.getElementById("start-offset"),
|
||||
endContainer: document.getElementById("end-container"),
|
||||
endOffset: document.getElementById("end-offset"),
|
||||
}),
|
||||
});
|
||||
|
||||
const fontFamilyElement = document.getElementById("font-family");
|
||||
const fontSizeElement = document.getElementById("font-size");
|
||||
const fontWeightElement = document.getElementById("font-weight");
|
||||
const fontStyleElement = document.getElementById("font-style");
|
||||
|
||||
const directionLTRElement = document.getElementById("direction-ltr");
|
||||
const directionRTLElement = document.getElementById("direction-rtl");
|
||||
|
||||
const lineHeightElement = document.getElementById("line-height");
|
||||
const letterSpacingElement = document.getElementById("letter-spacing");
|
||||
|
||||
const textAlignLeftElement = document.getElementById("text-align-left");
|
||||
const textAlignCenterElement = document.getElementById("text-align-center");
|
||||
const textAlignRightElement = document.getElementById("text-align-right");
|
||||
const textAlignJustifyElement = document.getElementById("text-align-justify");
|
||||
|
||||
const fonts = await loadFonts()
|
||||
console.log(fonts)
|
||||
const fontFamiliesFragment = document.createDocumentFragment()
|
||||
for (const [font, fontData] of fonts) {
|
||||
const fontFamilyOptionElement = document.createElement('option')
|
||||
fontFamilyOptionElement.value = font
|
||||
fontFamilyOptionElement.textContent = font
|
||||
fontFamiliesFragment.appendChild(fontFamilyOptionElement)
|
||||
}
|
||||
fontFamilyElement.replaceChildren(fontFamiliesFragment)
|
||||
|
||||
function onDirectionChange(e) {
|
||||
if (debug.includes("events")) {
|
||||
console.log(e);
|
||||
}
|
||||
if (e.target.checked) {
|
||||
textEditor.applyStylesToSelection({
|
||||
direction: e.target.value,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
directionLTRElement.addEventListener("change", onDirectionChange);
|
||||
directionRTLElement.addEventListener("change", onDirectionChange);
|
||||
|
||||
function onTextAlignChange(e) {
|
||||
if (debug.includes("events")) {
|
||||
console.log(e);
|
||||
}
|
||||
if (e.target.checked) {
|
||||
textEditor.applyStylesToSelection({
|
||||
"text-align": e.target.value,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
textAlignLeftElement.addEventListener("change", onTextAlignChange);
|
||||
textAlignCenterElement.addEventListener("change", onTextAlignChange);
|
||||
textAlignRightElement.addEventListener("change", onTextAlignChange);
|
||||
textAlignJustifyElement.addEventListener("change", onTextAlignChange);
|
||||
|
||||
fontFamilyElement.addEventListener("change", (e) => {
|
||||
if (debug.includes("events")) {
|
||||
console.log(e);
|
||||
}
|
||||
const fontStyles = fonts.get(e.target.value)
|
||||
console.log('fontStyles', fontStyles)
|
||||
textEditor.applyStylesToSelection({
|
||||
"font-family": e.target.value,
|
||||
});
|
||||
});
|
||||
|
||||
fontWeightElement.addEventListener("change", (e) => {
|
||||
if (debug.includes("events")) {
|
||||
console.log(e);
|
||||
}
|
||||
textEditor.applyStylesToSelection({
|
||||
"font-weight": e.target.value,
|
||||
});
|
||||
});
|
||||
|
||||
fontSizeElement.addEventListener("change", (e) => {
|
||||
if (debug.includes("events")) {
|
||||
console.log(e);
|
||||
}
|
||||
textEditor.applyStylesToSelection({
|
||||
"font-size": e.target.value,
|
||||
});
|
||||
});
|
||||
|
||||
lineHeightElement.addEventListener("change", (e) => {
|
||||
if (debug.includes("events")) {
|
||||
console.log(e);
|
||||
}
|
||||
textEditor.applyStylesToSelection({
|
||||
"line-height": e.target.value,
|
||||
});
|
||||
});
|
||||
|
||||
letterSpacingElement.addEventListener("change", (e) => {
|
||||
if (debug.includes("events")) {
|
||||
console.log(e);
|
||||
}
|
||||
textEditor.applyStylesToSelection({
|
||||
"letter-spacing": e.target.value,
|
||||
});
|
||||
});
|
||||
|
||||
fontStyleElement.addEventListener("change", (e) => {
|
||||
if (debug.includes("events")) {
|
||||
console.log(e);
|
||||
}
|
||||
textEditor.applyStylesToSelection({
|
||||
"font-style": e.target.value,
|
||||
});
|
||||
});
|
||||
|
||||
function formatHTML(html, options) {
|
||||
const spaces = options?.spaces ?? 4;
|
||||
let indent = 0;
|
||||
return html.replace(/<\/?(.*?)>/g, (fullMatch) => {
|
||||
let str = fullMatch + "\n";
|
||||
if (fullMatch.startsWith("</")) {
|
||||
--indent;
|
||||
str = " ".repeat(indent * spaces) + str;
|
||||
} else {
|
||||
str = " ".repeat(indent * spaces) + str;
|
||||
++indent;
|
||||
if (fullMatch === "<br>") --indent;
|
||||
}
|
||||
return str;
|
||||
});
|
||||
}
|
||||
|
||||
const fontSize = 14;
|
||||
const children = [];
|
||||
const uuid = crypto.randomUUID();
|
||||
children.push(uuid);
|
||||
const outputElement = document.getElementById("output");
|
||||
textEditorElement.addEventListener("input", (e) => {
|
||||
if (debug.includes("events")) {
|
||||
console.log(e);
|
||||
}
|
||||
outputElement.textContent = formatHTML(textEditor.element.innerHTML);
|
||||
});
|
||||
|
||||
textEditor.addEventListener("needslayout", (e) => {
|
||||
useShape(uuid);
|
||||
updateTextShape(textEditor.root, fonts);
|
||||
render();
|
||||
})
|
||||
|
||||
textEditor.addEventListener("stylechange", (e) => {
|
||||
if (debug.includes("events")) {
|
||||
console.log(e);
|
||||
}
|
||||
const fontSize = parseInt(e.detail.getPropertyValue("font-size"), 10);
|
||||
const fontWeight = e.detail.getPropertyValue("font-weight");
|
||||
const fontStyle = e.detail.getPropertyValue("font-style");
|
||||
const fontFamily = e.detail.getPropertyValue("font-family");
|
||||
|
||||
fontFamilyElement.value = fontFamily;
|
||||
fontSizeElement.value = fontSize;
|
||||
fontStyleElement.value = fontStyle;
|
||||
fontWeightElement.value = fontWeight;
|
||||
|
||||
const textAlign = e.detail.getPropertyValue("text-align");
|
||||
textAlignLeftElement.checked = textAlign === "left";
|
||||
textAlignCenterElement.checked = textAlign === "center";
|
||||
textAlignRightElement.checked = textAlign === "right";
|
||||
textAlignJustifyElement.checked = textAlign === "justify";
|
||||
|
||||
const direction = e.detail.getPropertyValue("direction");
|
||||
directionLTRElement.checked = direction === "ltr";
|
||||
directionRTLElement.checked = direction === "rtl";
|
||||
});
|
||||
|
||||
const canvas = document.getElementById("canvas");
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
|
||||
const MIN_LINES = 1;
|
||||
const MAX_LINES = 5;
|
||||
const MIN_WORDS = 1;
|
||||
const MAX_WORDS = 10;
|
||||
|
||||
initWasmModule().then(Module => {
|
||||
init(Module);
|
||||
assignCanvas(canvas);
|
||||
Module._set_canvas_background(hexToU32ARGB("#FABADA", 1));
|
||||
Module._set_view(1, 0, 0);
|
||||
Module._init_shapes_pool(1);
|
||||
setupInteraction(canvas);
|
||||
|
||||
storeFonts(fonts)
|
||||
|
||||
useShape(uuid);
|
||||
Module._set_parent(0, 0, 0, 0);
|
||||
Module._set_shape_type(5);
|
||||
|
||||
const x1 = 0;
|
||||
const y1 = 0;
|
||||
const width = canvas.width;
|
||||
const height = canvas.height;
|
||||
Module._set_shape_selrect(x1, y1, x1 + width, y1 + height);
|
||||
|
||||
addTextShape("", fonts);
|
||||
|
||||
useShape("00000000-0000-0000-0000-000000000000");
|
||||
setShapeChildren(children);
|
||||
render()
|
||||
});
|
||||
</script>
|
||||
|
||||
-->
|
||||
<div id="output"></div>
|
||||
<script type="module" src="/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,216 +0,0 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* Copyright (c) KALEIDOS INC
|
||||
*/
|
||||
|
||||
import "./style.css";
|
||||
import "./editor/TextEditor.css";
|
||||
import { TextEditor } from "./editor/TextEditor";
|
||||
import { SelectionControllerDebug } from "./editor/debug/SelectionControllerDebug";
|
||||
|
||||
const searchParams = new URLSearchParams(location.search);
|
||||
const debug = searchParams.has("debug")
|
||||
? searchParams.get("debug").split(",")
|
||||
: [];
|
||||
|
||||
const textEditorSelectionImposterElement = document.getElementById(
|
||||
"text-editor-selection-imposter",
|
||||
);
|
||||
|
||||
const textEditorElement = document.querySelector(".text-editor-content");
|
||||
const textEditor = new TextEditor(textEditorElement, {
|
||||
styleDefaults: {
|
||||
"font-family": "sourcesanspro",
|
||||
"font-size": "14",
|
||||
"font-weight": "500",
|
||||
"font-style": "normal",
|
||||
"line-height": "1.2",
|
||||
"letter-spacing": "0",
|
||||
direction: "ltr",
|
||||
"text-align": "left",
|
||||
"text-transform": "none",
|
||||
"text-decoration": "none",
|
||||
"--typography-ref-id": '["~#\'",null]',
|
||||
"--typography-ref-file": '["~#\'",null]',
|
||||
"--font-id": '["~#\'","sourcesanspro"]',
|
||||
"--fills": '[["^ ","~:fill-color","#000000","~:fill-opacity",1]]',
|
||||
},
|
||||
selectionImposterElement: textEditorSelectionImposterElement,
|
||||
debug: new SelectionControllerDebug({
|
||||
direction: document.getElementById("direction"),
|
||||
multiElement: document.getElementById("multi"),
|
||||
multiInlineElement: document.getElementById("multi-inline"),
|
||||
multiParagraphElement: document.getElementById("multi-paragraph"),
|
||||
isParagraphStart: document.getElementById("is-paragraph-start"),
|
||||
isParagraphEnd: document.getElementById("is-paragraph-end"),
|
||||
isInlineStart: document.getElementById("is-inline-start"),
|
||||
isInlineEnd: document.getElementById("is-inline-end"),
|
||||
isTextAnchor: document.getElementById("is-text-anchor"),
|
||||
isTextFocus: document.getElementById("is-text-focus"),
|
||||
focusNode: document.getElementById("focus-node"),
|
||||
focusOffset: document.getElementById("focus-offset"),
|
||||
focusInline: document.getElementById("focus-inline"),
|
||||
focusParagraph: document.getElementById("focus-paragraph"),
|
||||
anchorNode: document.getElementById("anchor-node"),
|
||||
anchorOffset: document.getElementById("anchor-offset"),
|
||||
anchorInline: document.getElementById("anchor-inline"),
|
||||
anchorParagraph: document.getElementById("anchor-paragraph"),
|
||||
startContainer: document.getElementById("start-container"),
|
||||
startOffset: document.getElementById("start-offset"),
|
||||
endContainer: document.getElementById("end-container"),
|
||||
endOffset: document.getElementById("end-offset"),
|
||||
}),
|
||||
});
|
||||
|
||||
const fontFamilyElement = document.getElementById("font-family");
|
||||
const fontSizeElement = document.getElementById("font-size");
|
||||
const fontWeightElement = document.getElementById("font-weight");
|
||||
const fontStyleElement = document.getElementById("font-style");
|
||||
|
||||
const directionLTRElement = document.getElementById("direction-ltr");
|
||||
const directionRTLElement = document.getElementById("direction-rtl");
|
||||
|
||||
const lineHeightElement = document.getElementById("line-height");
|
||||
const letterSpacingElement = document.getElementById("letter-spacing");
|
||||
|
||||
const textAlignLeftElement = document.getElementById("text-align-left");
|
||||
const textAlignCenterElement = document.getElementById("text-align-center");
|
||||
const textAlignRightElement = document.getElementById("text-align-right");
|
||||
const textAlignJustifyElement = document.getElementById("text-align-justify");
|
||||
|
||||
function onDirectionChange(e) {
|
||||
if (debug.includes("events")) {
|
||||
console.log(e);
|
||||
}
|
||||
if (e.target.checked) {
|
||||
textEditor.applyStylesToSelection({
|
||||
direction: e.target.value,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
directionLTRElement.addEventListener("change", onDirectionChange);
|
||||
directionRTLElement.addEventListener("change", onDirectionChange);
|
||||
|
||||
function onTextAlignChange(e) {
|
||||
if (debug.includes("events")) {
|
||||
console.log(e);
|
||||
}
|
||||
if (e.target.checked) {
|
||||
textEditor.applyStylesToSelection({
|
||||
"text-align": e.target.value,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
textAlignLeftElement.addEventListener("change", onTextAlignChange);
|
||||
textAlignCenterElement.addEventListener("change", onTextAlignChange);
|
||||
textAlignRightElement.addEventListener("change", onTextAlignChange);
|
||||
textAlignJustifyElement.addEventListener("change", onTextAlignChange);
|
||||
|
||||
fontFamilyElement.addEventListener("change", (e) => {
|
||||
if (debug.includes("events")) {
|
||||
console.log(e);
|
||||
}
|
||||
textEditor.applyStylesToSelection({
|
||||
"font-family": e.target.value,
|
||||
});
|
||||
});
|
||||
|
||||
fontWeightElement.addEventListener("change", (e) => {
|
||||
if (debug.includes("events")) {
|
||||
console.log(e);
|
||||
}
|
||||
textEditor.applyStylesToSelection({
|
||||
"font-weight": e.target.value,
|
||||
});
|
||||
});
|
||||
|
||||
fontSizeElement.addEventListener("change", (e) => {
|
||||
if (debug.includes("events")) {
|
||||
console.log(e);
|
||||
}
|
||||
textEditor.applyStylesToSelection({
|
||||
"font-size": e.target.value,
|
||||
});
|
||||
});
|
||||
|
||||
lineHeightElement.addEventListener("change", (e) => {
|
||||
if (debug.includes("events")) {
|
||||
console.log(e);
|
||||
}
|
||||
textEditor.applyStylesToSelection({
|
||||
"line-height": e.target.value,
|
||||
});
|
||||
});
|
||||
|
||||
letterSpacingElement.addEventListener("change", (e) => {
|
||||
if (debug.includes("events")) {
|
||||
console.log(e);
|
||||
}
|
||||
textEditor.applyStylesToSelection({
|
||||
"letter-spacing": e.target.value,
|
||||
});
|
||||
});
|
||||
|
||||
fontStyleElement.addEventListener("change", (e) => {
|
||||
if (debug.includes("events")) {
|
||||
console.log(e);
|
||||
}
|
||||
textEditor.applyStylesToSelection({
|
||||
"font-style": e.target.value,
|
||||
});
|
||||
});
|
||||
|
||||
function formatHTML(html, options) {
|
||||
const spaces = options?.spaces ?? 4;
|
||||
let indent = 0;
|
||||
return html.replace(/<\/?(.*?)>/g, (fullMatch) => {
|
||||
let str = fullMatch + "\n";
|
||||
if (fullMatch.startsWith("</")) {
|
||||
--indent;
|
||||
str = " ".repeat(indent * spaces) + str;
|
||||
} else {
|
||||
str = " ".repeat(indent * spaces) + str;
|
||||
++indent;
|
||||
if (fullMatch === "<br>") --indent;
|
||||
}
|
||||
return str;
|
||||
});
|
||||
}
|
||||
|
||||
const outputElement = document.getElementById("output");
|
||||
textEditorElement.addEventListener("input", (e) => {
|
||||
if (debug.includes("events")) {
|
||||
console.log(e);
|
||||
}
|
||||
outputElement.textContent = formatHTML(textEditor.element.innerHTML);
|
||||
});
|
||||
|
||||
textEditor.addEventListener("stylechange", (e) => {
|
||||
if (debug.includes("events")) {
|
||||
console.log(e);
|
||||
}
|
||||
const fontSize = parseInt(e.detail.getPropertyValue("font-size"), 10);
|
||||
const fontWeight = e.detail.getPropertyValue("font-weight");
|
||||
const fontStyle = e.detail.getPropertyValue("font-style");
|
||||
const fontFamily = e.detail.getPropertyValue("font-family");
|
||||
|
||||
fontFamilyElement.value = fontFamily;
|
||||
fontSizeElement.value = fontSize;
|
||||
fontStyleElement.value = fontStyle;
|
||||
fontWeightElement.value = fontWeight;
|
||||
|
||||
const textAlign = e.detail.getPropertyValue("text-align");
|
||||
textAlignLeftElement.checked = textAlign === "left";
|
||||
textAlignCenterElement.checked = textAlign === "center";
|
||||
textAlignRightElement.checked = textAlign === "right";
|
||||
textAlignJustifyElement.checked = textAlign === "justify";
|
||||
|
||||
const direction = e.detail.getPropertyValue("direction");
|
||||
directionLTRElement.checked = direction === "ltr";
|
||||
directionRTLElement.checked = direction === "rtl";
|
||||
});
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
frontend/text-editor/src/public/fonts/Oswald-Bold.ttf
Normal file
BIN
frontend/text-editor/src/public/fonts/Oswald-Bold.ttf
Normal file
Binary file not shown.
BIN
frontend/text-editor/src/public/fonts/Oswald-ExtraLight.ttf
Normal file
BIN
frontend/text-editor/src/public/fonts/Oswald-ExtraLight.ttf
Normal file
Binary file not shown.
BIN
frontend/text-editor/src/public/fonts/Oswald-Light.ttf
Normal file
BIN
frontend/text-editor/src/public/fonts/Oswald-Light.ttf
Normal file
Binary file not shown.
BIN
frontend/text-editor/src/public/fonts/Oswald-Medium.ttf
Normal file
BIN
frontend/text-editor/src/public/fonts/Oswald-Medium.ttf
Normal file
Binary file not shown.
BIN
frontend/text-editor/src/public/fonts/Oswald-Regular.ttf
Normal file
BIN
frontend/text-editor/src/public/fonts/Oswald-Regular.ttf
Normal file
Binary file not shown.
BIN
frontend/text-editor/src/public/fonts/Oswald-SemiBold.ttf
Normal file
BIN
frontend/text-editor/src/public/fonts/Oswald-SemiBold.ttf
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
26
frontend/text-editor/src/public/fonts/fonts.txt
Normal file
26
frontend/text-editor/src/public/fonts/fonts.txt
Normal file
@@ -0,0 +1,26 @@
|
||||
MontserratAlternates-BlackItalic.ttf
|
||||
MontserratAlternates-Black.ttf
|
||||
MontserratAlternates-BoldItalic.ttf
|
||||
MontserratAlternates-Bold.ttf
|
||||
MontserratAlternates-ExtraBoldItalic.ttf
|
||||
MontserratAlternates-ExtraBold.ttf
|
||||
MontserratAlternates-ExtraLightItalic.ttf
|
||||
MontserratAlternates-ExtraLight.ttf
|
||||
MontserratAlternates-Italic.ttf
|
||||
MontserratAlternates-LightItalic.ttf
|
||||
MontserratAlternates-Light.ttf
|
||||
MontserratAlternates-MediumItalic.ttf
|
||||
MontserratAlternates-Medium.ttf
|
||||
MontserratAlternates-Regular.ttf
|
||||
MontserratAlternates-SemiBoldItalic.ttf
|
||||
MontserratAlternates-SemiBold.ttf
|
||||
MontserratAlternates-ThinItalic.ttf
|
||||
MontserratAlternates-Thin.ttf
|
||||
Oswald-Bold.ttf
|
||||
Oswald-ExtraLight.ttf
|
||||
Oswald-Light.ttf
|
||||
Oswald-Medium.ttf
|
||||
Oswald-Regular.ttf
|
||||
Oswald-SemiBold.ttf
|
||||
Oswald-VariableFont_wght.ttf
|
||||
PlaywriteMXGuides-Regular.ttf
|
||||
|
Before Width: | Height: | Size: 995 B After Width: | Height: | Size: 995 B |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
@@ -1,482 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&family=Playwrite+ES:wght@100..400&family=Playwrite+NZ:wght@100..400&family=Playwrite+US+Trad:wght@100..400&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap" rel="stylesheet">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Penpot - Text Editor Playground</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="playground">
|
||||
<form>
|
||||
<fieldset>
|
||||
<legend>Styles</legend>
|
||||
<!-- Font -->
|
||||
<div class="form-group">
|
||||
<label for="font-family">Font family</label>
|
||||
<select id="font-family">
|
||||
<option value="Open+Sans">Open Sans</option>
|
||||
<option value="sourcesanspro">Source Sans Pro</option>
|
||||
<option value="whatever">Whatever</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="font-size">Font size</label>
|
||||
<input id="font-size" type="number" value="14" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="font-weight">Font weight</label>
|
||||
<select id="font-weight">
|
||||
<option value="100">100</option>
|
||||
<option value="200">200</option>
|
||||
<option value="300">300</option>
|
||||
<option value="400">400 (normal)</option>
|
||||
<option value="500">500</option>
|
||||
<option value="600">600</option>
|
||||
<option value="700">700 (bold)</option>
|
||||
<option value="800">800</option>
|
||||
<option value="900">900</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="font-style">Font style</label>
|
||||
<select id="font-style">
|
||||
<option value="normal">normal</option>
|
||||
<option value="italic">italic</option>
|
||||
<option value="oblique">oblique</option>
|
||||
</select>
|
||||
</div>
|
||||
<!-- Text attributes -->
|
||||
<div class="form-group">
|
||||
<label for="line-height">Line height</label>
|
||||
<input id="line-height" type="number" value="1.0" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="letter-spacing">Letter spacing</label>
|
||||
<input id="letter-spacing" type="number" value="0.0" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="direction-ltr">LTR</label>
|
||||
<input id="direction-ltr" type="radio" name="direction" value="ltr" checked />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="direction-rtl">RTL</label>
|
||||
<input id="direction-rtl" type="radio" name="direction" value="rtl" />
|
||||
</div>
|
||||
<!-- Text Align -->
|
||||
<div class="form-group">
|
||||
<label for="text-align-left">Align left</label>
|
||||
<input id="text-align-left" type="radio" name="text-align" value="left" checked />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="text-align-center">Align center</label>
|
||||
<input id="text-align-center" type="radio" name="text-align" value="center" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="text-align-right">Align right</label>
|
||||
<input id="text-align-right" type="radio" name="text-align" value="right" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="text-align-justify">Align justify</label>
|
||||
<input id="text-align-justify" type="radio" name="text-align" value="justify" />
|
||||
</div>
|
||||
<!-- Text Transform -->
|
||||
<div class="form-group">
|
||||
<label for="text-transform-none">None</label>
|
||||
<input id="text-transform-none" type="radio" name="text-transform" value="none" checked />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="text-transform-uppercase">Uppercase</label>
|
||||
<input id="text-transform-uppercase" type="radio" name="text-transform" value="uppercase" checked />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="text-transform-capitalize">Capitalize</label>
|
||||
<input id="text-transform-capitalize" type="radio" name="text-transform" value="capitalize" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="text-transform-lowercase">Lowercase</label>
|
||||
<input id="text-transform-lowercase" type="radio" name="text-transform" value="lowercase" />
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Debug</legend>
|
||||
<div class="form-group">
|
||||
<label for="direction">Direction</label>
|
||||
<input id="direction" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="focus-node">Focus Node</label>
|
||||
<input id="focus-node" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="focus-offset">Focus offset</label>
|
||||
<input id="focus-offset" readonly type="number">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="focus-inline">Focus Inline</label>
|
||||
<input id="focus-inline" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="focus-paragraph">Focus Paragraph</label>
|
||||
<input id="focus-paragraph" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="anchor-node">Anchor Node</label>
|
||||
<input id="anchor-node" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="anchor-offset">Anchor offset</label>
|
||||
<input id="anchor-offset" readonly type="number">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="anchor-inline">Anchor Inline</label>
|
||||
<input id="anchor-inline" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="anchor-paragraph">Anchor Paragraph</label>
|
||||
<input id="anchor-paragraph" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="start-container">Start container</label>
|
||||
<input id="start-container" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="start-offset">Start offset</label>
|
||||
<input id="start-offset" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="end-container">End container</label>
|
||||
<input id="end-container" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="end-offset">End offset</label>
|
||||
<input id="end-offset" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="multi">Multi?</label>
|
||||
<input id="multi" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="multi-inline">Multi inline?</label>
|
||||
<input id="multi-inline" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="multi-paragraph">Multi paragraph?</label>
|
||||
<input id="multi-paragraph" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="is-text-focus">Is text focus?</label>
|
||||
<input id="is-text-focus" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="is-text-anchor">Is text anchor?</label>
|
||||
<input id="is-text-anchor" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="is-paragraph-start">Is paragraph start?</label>
|
||||
<input id="is-paragraph-start" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="is-paragraph-end">Is paragraph end?</label>
|
||||
<input id="is-paragraph-end" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="is-inline-start">Is inline start?</label>
|
||||
<input id="is-inline-start" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="is-inline-end">Is inline end?</label>
|
||||
<input id="is-inline-end" readonly type="checkbox">
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
<!--
|
||||
|
||||
Editor
|
||||
|
||||
-->
|
||||
<div class="text-editor-container align-top">
|
||||
<div
|
||||
id="text-editor-selection-imposter"
|
||||
class="text-editor-selection-imposter"></div>
|
||||
<div
|
||||
class="text-editor-content"
|
||||
contenteditable="true"
|
||||
role="textbox"
|
||||
aria-multiline="true"
|
||||
aria-autocomplete="none"
|
||||
spellcheck="false"
|
||||
autocapitalize="false"></div>
|
||||
</div>
|
||||
<!--
|
||||
|
||||
Text output
|
||||
|
||||
-->
|
||||
<canvas id="canvas"></canvas>
|
||||
</div>
|
||||
<script type="module">
|
||||
import "./style.css";
|
||||
import "./editor/TextEditor.css";
|
||||
import { TextEditor } from "./editor/TextEditor";
|
||||
import { SelectionControllerDebug } from "./editor/debug/SelectionControllerDebug";
|
||||
import initWasmModule from './wasm/render_wasm.js';
|
||||
import {
|
||||
init, assignCanvas, render, setupInteraction, useShape, setShapeChildren, addTextShape, updateTextShape, hexToU32ARGB,getRandomInt, getRandomColor, getRandomFloat, addShapeSolidFill, addShapeSolidStrokeFill
|
||||
} from './wasm/lib.js';
|
||||
|
||||
const searchParams = new URLSearchParams(location.search);
|
||||
const debug = searchParams.has("debug")
|
||||
? searchParams.get("debug").split(",")
|
||||
: [];
|
||||
|
||||
const textEditorSelectionImposterElement = document.getElementById(
|
||||
"text-editor-selection-imposter",
|
||||
);
|
||||
|
||||
const textEditorElement = document.querySelector(".text-editor-content");
|
||||
const textEditor = new TextEditor(textEditorElement, {
|
||||
styleDefaults: {
|
||||
"font-family": "sourcesanspro",
|
||||
"font-size": "14",
|
||||
"font-weight": "500",
|
||||
"font-style": "normal",
|
||||
"line-height": "1.2",
|
||||
"letter-spacing": "0",
|
||||
direction: "ltr",
|
||||
"text-align": "left",
|
||||
"text-transform": "none",
|
||||
"text-decoration": "none",
|
||||
"--typography-ref-id": '["~#\'",null]',
|
||||
"--typography-ref-file": '["~#\'",null]',
|
||||
"--font-id": '["~#\'","sourcesanspro"]',
|
||||
"--fills": '[["^ ","~:fill-color","#000000","~:fill-opacity",1]]',
|
||||
},
|
||||
selectionImposterElement: textEditorSelectionImposterElement,
|
||||
debug: new SelectionControllerDebug({
|
||||
direction: document.getElementById("direction"),
|
||||
multiElement: document.getElementById("multi"),
|
||||
multiInlineElement: document.getElementById("multi-inline"),
|
||||
multiParagraphElement: document.getElementById("multi-paragraph"),
|
||||
isParagraphStart: document.getElementById("is-paragraph-start"),
|
||||
isParagraphEnd: document.getElementById("is-paragraph-end"),
|
||||
isInlineStart: document.getElementById("is-inline-start"),
|
||||
isInlineEnd: document.getElementById("is-inline-end"),
|
||||
isTextAnchor: document.getElementById("is-text-anchor"),
|
||||
isTextFocus: document.getElementById("is-text-focus"),
|
||||
focusNode: document.getElementById("focus-node"),
|
||||
focusOffset: document.getElementById("focus-offset"),
|
||||
focusInline: document.getElementById("focus-inline"),
|
||||
focusParagraph: document.getElementById("focus-paragraph"),
|
||||
anchorNode: document.getElementById("anchor-node"),
|
||||
anchorOffset: document.getElementById("anchor-offset"),
|
||||
anchorInline: document.getElementById("anchor-inline"),
|
||||
anchorParagraph: document.getElementById("anchor-paragraph"),
|
||||
startContainer: document.getElementById("start-container"),
|
||||
startOffset: document.getElementById("start-offset"),
|
||||
endContainer: document.getElementById("end-container"),
|
||||
endOffset: document.getElementById("end-offset"),
|
||||
}),
|
||||
});
|
||||
|
||||
const fontFamilyElement = document.getElementById("font-family");
|
||||
const fontSizeElement = document.getElementById("font-size");
|
||||
const fontWeightElement = document.getElementById("font-weight");
|
||||
const fontStyleElement = document.getElementById("font-style");
|
||||
|
||||
const directionLTRElement = document.getElementById("direction-ltr");
|
||||
const directionRTLElement = document.getElementById("direction-rtl");
|
||||
|
||||
const lineHeightElement = document.getElementById("line-height");
|
||||
const letterSpacingElement = document.getElementById("letter-spacing");
|
||||
|
||||
const textAlignLeftElement = document.getElementById("text-align-left");
|
||||
const textAlignCenterElement = document.getElementById("text-align-center");
|
||||
const textAlignRightElement = document.getElementById("text-align-right");
|
||||
const textAlignJustifyElement = document.getElementById("text-align-justify");
|
||||
|
||||
function onDirectionChange(e) {
|
||||
if (debug.includes("events")) {
|
||||
console.log(e);
|
||||
}
|
||||
if (e.target.checked) {
|
||||
textEditor.applyStylesToSelection({
|
||||
direction: e.target.value,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
directionLTRElement.addEventListener("change", onDirectionChange);
|
||||
directionRTLElement.addEventListener("change", onDirectionChange);
|
||||
|
||||
function onTextAlignChange(e) {
|
||||
if (debug.includes("events")) {
|
||||
console.log(e);
|
||||
}
|
||||
if (e.target.checked) {
|
||||
textEditor.applyStylesToSelection({
|
||||
"text-align": e.target.value,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
textAlignLeftElement.addEventListener("change", onTextAlignChange);
|
||||
textAlignCenterElement.addEventListener("change", onTextAlignChange);
|
||||
textAlignRightElement.addEventListener("change", onTextAlignChange);
|
||||
textAlignJustifyElement.addEventListener("change", onTextAlignChange);
|
||||
|
||||
fontFamilyElement.addEventListener("change", (e) => {
|
||||
if (debug.includes("events")) {
|
||||
console.log(e);
|
||||
}
|
||||
textEditor.applyStylesToSelection({
|
||||
"font-family": e.target.value,
|
||||
});
|
||||
});
|
||||
|
||||
fontWeightElement.addEventListener("change", (e) => {
|
||||
if (debug.includes("events")) {
|
||||
console.log(e);
|
||||
}
|
||||
textEditor.applyStylesToSelection({
|
||||
"font-weight": e.target.value,
|
||||
});
|
||||
});
|
||||
|
||||
fontSizeElement.addEventListener("change", (e) => {
|
||||
if (debug.includes("events")) {
|
||||
console.log(e);
|
||||
}
|
||||
textEditor.applyStylesToSelection({
|
||||
"font-size": e.target.value,
|
||||
});
|
||||
});
|
||||
|
||||
lineHeightElement.addEventListener("change", (e) => {
|
||||
if (debug.includes("events")) {
|
||||
console.log(e);
|
||||
}
|
||||
textEditor.applyStylesToSelection({
|
||||
"line-height": e.target.value,
|
||||
});
|
||||
});
|
||||
|
||||
letterSpacingElement.addEventListener("change", (e) => {
|
||||
if (debug.includes("events")) {
|
||||
console.log(e);
|
||||
}
|
||||
textEditor.applyStylesToSelection({
|
||||
"letter-spacing": e.target.value,
|
||||
});
|
||||
});
|
||||
|
||||
fontStyleElement.addEventListener("change", (e) => {
|
||||
if (debug.includes("events")) {
|
||||
console.log(e);
|
||||
}
|
||||
textEditor.applyStylesToSelection({
|
||||
"font-style": e.target.value,
|
||||
});
|
||||
});
|
||||
|
||||
function formatHTML(html, options) {
|
||||
const spaces = options?.spaces ?? 4;
|
||||
let indent = 0;
|
||||
return html.replace(/<\/?(.*?)>/g, (fullMatch) => {
|
||||
let str = fullMatch + "\n";
|
||||
if (fullMatch.startsWith("</")) {
|
||||
--indent;
|
||||
str = " ".repeat(indent * spaces) + str;
|
||||
} else {
|
||||
str = " ".repeat(indent * spaces) + str;
|
||||
++indent;
|
||||
if (fullMatch === "<br>") --indent;
|
||||
}
|
||||
return str;
|
||||
});
|
||||
}
|
||||
|
||||
const fontSize = 14;
|
||||
const children = [];
|
||||
const uuid = crypto.randomUUID();
|
||||
children.push(uuid);
|
||||
const outputElement = document.getElementById("output");
|
||||
textEditorElement.addEventListener("input", (e) => {
|
||||
if (debug.includes("events")) {
|
||||
console.log(e);
|
||||
}
|
||||
outputElement.textContent = formatHTML(textEditor.element.innerHTML);
|
||||
});
|
||||
|
||||
textEditor.addEventListener("needslayout", (e) => {
|
||||
useShape(uuid);
|
||||
updateTextShape(fontSize, textEditor.root);
|
||||
render();
|
||||
})
|
||||
|
||||
textEditor.addEventListener("stylechange", (e) => {
|
||||
if (debug.includes("events")) {
|
||||
console.log(e);
|
||||
}
|
||||
const fontSize = parseInt(e.detail.getPropertyValue("font-size"), 10);
|
||||
const fontWeight = e.detail.getPropertyValue("font-weight");
|
||||
const fontStyle = e.detail.getPropertyValue("font-style");
|
||||
const fontFamily = e.detail.getPropertyValue("font-family");
|
||||
|
||||
fontFamilyElement.value = fontFamily;
|
||||
fontSizeElement.value = fontSize;
|
||||
fontStyleElement.value = fontStyle;
|
||||
fontWeightElement.value = fontWeight;
|
||||
|
||||
const textAlign = e.detail.getPropertyValue("text-align");
|
||||
textAlignLeftElement.checked = textAlign === "left";
|
||||
textAlignCenterElement.checked = textAlign === "center";
|
||||
textAlignRightElement.checked = textAlign === "right";
|
||||
textAlignJustifyElement.checked = textAlign === "justify";
|
||||
|
||||
const direction = e.detail.getPropertyValue("direction");
|
||||
directionLTRElement.checked = direction === "ltr";
|
||||
directionRTLElement.checked = direction === "rtl";
|
||||
});
|
||||
|
||||
const canvas = document.getElementById("canvas");
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
|
||||
const MIN_LINES = 1;
|
||||
const MAX_LINES = 5;
|
||||
const MIN_WORDS = 1;
|
||||
const MAX_WORDS = 10;
|
||||
|
||||
initWasmModule().then(Module => {
|
||||
init(Module);
|
||||
assignCanvas(canvas);
|
||||
Module._set_canvas_background(hexToU32ARGB("#FABADA", 1));
|
||||
Module._set_view(1, 0, 0);
|
||||
Module._init_shapes_pool(1);
|
||||
setupInteraction(canvas);
|
||||
|
||||
|
||||
useShape(uuid);
|
||||
Module._set_parent(0, 0, 0, 0);
|
||||
Module._set_shape_type(5);
|
||||
|
||||
const x1 = 0;
|
||||
const y1 = 0;
|
||||
const width = canvas.width;
|
||||
const height = canvas.height;
|
||||
Module._set_shape_selrect(x1, y1, x1 + width, y1 + height);
|
||||
|
||||
addTextShape(fontSize, "");
|
||||
|
||||
useShape("00000000-0000-0000-0000-000000000000");
|
||||
setShapeChildren(children);
|
||||
render()
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -182,7 +182,6 @@ export function set_parent(id) {
|
||||
}
|
||||
|
||||
export function render() {
|
||||
console.log('render')
|
||||
Module._set_view(1, 0, 0);
|
||||
Module._render_from_cache();
|
||||
debouncedRender();
|
||||
@@ -284,9 +283,11 @@ function getFontStyle(fontStyle) {
|
||||
switch (fontStyle) {
|
||||
default:
|
||||
case 'normal':
|
||||
case 'regular':
|
||||
return 0;
|
||||
case 'oblique':
|
||||
case 'italic':
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,6 +317,7 @@ function setParagraphData(dview, { numLeaves, textAlign, textDirection, textDeco
|
||||
}
|
||||
|
||||
function setLeafData(dview, leafOffset, {
|
||||
fontId,
|
||||
fontStyle,
|
||||
fontSize,
|
||||
fontWeight,
|
||||
@@ -331,10 +333,10 @@ function setLeafData(dview, leafOffset, {
|
||||
dview.setFloat32(leafOffset + 4, fontSize, true); // font-size
|
||||
dview.setFloat32(leafOffset + 8, letterSpacing, true); // letter-spacing
|
||||
dview.setInt32(leafOffset + 12, fontWeight, true); // font-weight: normal
|
||||
dview.setUint32(leafOffset + 16, 0, true); // font-id (UUID part 1)
|
||||
dview.setUint32(leafOffset + 20, 0, true); // font-id (UUID part 2)
|
||||
dview.setUint32(leafOffset + 24, 0, true); // font-id (UUID part 3)
|
||||
dview.setUint32(leafOffset + 28, 0, true); // font-id (UUID part 4)
|
||||
dview.setUint32(leafOffset + 16, fontId[0], true); // font-id (UUID part 1)
|
||||
dview.setUint32(leafOffset + 20, fontId[1], true); // font-id (UUID part 2)
|
||||
dview.setUint32(leafOffset + 24, fontId[2], true); // font-id (UUID part 3)
|
||||
dview.setUint32(leafOffset + 28, fontId[3], true); // font-id (UUID part 4)
|
||||
dview.setUint32(leafOffset + 32, 0, true); // font-family hash
|
||||
dview.setUint32(leafOffset + 36, 0, true); // font-variant-id (UUID part 1)
|
||||
dview.setUint32(leafOffset + 40, 0, true); // font-variant-id (UUID part 2)
|
||||
@@ -344,7 +346,15 @@ function setLeafData(dview, leafOffset, {
|
||||
dview.setUint32(leafOffset + 56, totalFills, true); // total fills count
|
||||
}
|
||||
|
||||
export function updateTextShape(fontSize, root) {
|
||||
function getFontFrom(fontFamily, fontWeight, fontStyle, fonts) {
|
||||
const fontList = fonts.get(fontFamily)
|
||||
if (!fontList) {
|
||||
return null
|
||||
}
|
||||
return fontList.find(fontData => fontData.weight === fontWeight && fontStyle === fontData.style)
|
||||
}
|
||||
|
||||
export function updateTextShape(root, fonts) {
|
||||
// Calculate fills
|
||||
const fills = [
|
||||
{
|
||||
@@ -358,7 +368,6 @@ export function updateTextShape(fontSize, root) {
|
||||
const totalFillsSize = totalFills * FILL_SIZE;
|
||||
|
||||
const paragraphs = root.children;
|
||||
console.log("paragraphs", paragraphs.length);
|
||||
|
||||
Module._clear_shape_text();
|
||||
for (const paragraph of paragraphs) {
|
||||
@@ -366,19 +375,16 @@ export function updateTextShape(fontSize, root) {
|
||||
|
||||
const leaves = paragraph.children;
|
||||
const numLeaves = leaves.length;
|
||||
console.log("leaves", numLeaves);
|
||||
|
||||
for (const leaf of leaves) {
|
||||
const text = leaf.textContent;
|
||||
const textBuffer = new TextEncoder().encode(text);
|
||||
const textSize = textBuffer.byteLength;
|
||||
console.log("text", text, textSize);
|
||||
totalSize += LEAF_ATTR_SIZE + totalFillsSize;
|
||||
}
|
||||
|
||||
totalSize += paragraph.textContent.length;
|
||||
|
||||
console.log("Total Size", totalSize);
|
||||
// Allocate buffer
|
||||
const bufferPtr = allocBytes(totalSize);
|
||||
const heap = new Uint8Array(Module.HEAPU8.buffer, bufferPtr, totalSize);
|
||||
@@ -387,39 +393,21 @@ export function updateTextShape(fontSize, root) {
|
||||
const textAlign = getTextAlign(
|
||||
paragraph.style.getPropertyValue("text-align"),
|
||||
);
|
||||
console.log("text-align", textAlign);
|
||||
const textDirection = getTextDirection(
|
||||
paragraph.style.getPropertyValue("text-direction"),
|
||||
);
|
||||
console.log("text-direction", textDirection);
|
||||
const textDecoration = getTextDecoration(
|
||||
paragraph.style.getPropertyValue("text-decoration"),
|
||||
);
|
||||
console.log("text-decoration", textDecoration);
|
||||
const textTransform = getTextTransform(
|
||||
paragraph.style.getPropertyValue("text-transform"),
|
||||
);
|
||||
console.log("text-transform", textTransform);
|
||||
const lineHeight = parseFloat(
|
||||
paragraph.style.getPropertyValue("line-height"),
|
||||
);
|
||||
console.log("line-height", lineHeight);
|
||||
const letterSpacing = parseFloat(
|
||||
paragraph.style.getPropertyValue("letter-spacing"),
|
||||
);
|
||||
console.log("letter-spacing", letterSpacing);
|
||||
|
||||
/*
|
||||
num_leaves: u32,
|
||||
text_align: u8,
|
||||
text_direction: u8,
|
||||
text_decoration: u8,
|
||||
text_transform: u8,
|
||||
line_height: f32,
|
||||
letter_spacing: f32,
|
||||
typography_ref_file: [u32; 4],
|
||||
typography_ref_id: [u32; 4],
|
||||
*/
|
||||
|
||||
setParagraphData(dview, {
|
||||
numLeaves,
|
||||
@@ -432,31 +420,25 @@ export function updateTextShape(fontSize, root) {
|
||||
})
|
||||
let leafOffset = PARAGRAPH_ATTR_SIZE;
|
||||
for (const leaf of leaves) {
|
||||
console.log(
|
||||
"leafOffset",
|
||||
leafOffset,
|
||||
PARAGRAPH_ATTR_SIZE,
|
||||
LEAF_ATTR_SIZE,
|
||||
FILL_SIZE,
|
||||
totalFills,
|
||||
totalFillsSize,
|
||||
);
|
||||
const fontStyle = getFontStyle(leaf.style.getPropertyValue("font-style"));
|
||||
const fontStyle = leaf.style.getPropertyValue("font-style");
|
||||
const fontStyleSerialized = getFontStyle(fontStyle);
|
||||
const fontSize = parseFloat(leaf.style.getPropertyValue("font-size"));
|
||||
const letterSpacing = parseFloat(leaf.style.getPropertyValue("letter-spacing"))
|
||||
console.log("font-size", fontSize, "letter-spacing", letterSpacing);
|
||||
const fontWeight = parseInt(
|
||||
leaf.style.getPropertyValue("font-weight"),
|
||||
10,
|
||||
);
|
||||
console.log("font-weight", fontWeight);
|
||||
|
||||
const text = leaf.textContent;
|
||||
const textBuffer = new TextEncoder().encode(text);
|
||||
const textSize = textBuffer.byteLength;
|
||||
|
||||
const fontFamily = leaf.style.getPropertyValue('font-family');
|
||||
const fontData = getFontFrom(fontFamily, fontWeight, fontStyle, fonts)
|
||||
const defaultFontId = new Uint32Array([0, 0, 0, 0])
|
||||
const fontId = fontData ? getU32(fontData.id) : defaultFontId
|
||||
setLeafData(dview, leafOffset, {
|
||||
fontStyle,
|
||||
fontId,
|
||||
fontStyle: fontStyleSerialized,
|
||||
textDecoration: 0,
|
||||
textTransform: 0,
|
||||
textDirection: 0,
|
||||
@@ -464,8 +446,8 @@ export function updateTextShape(fontSize, root) {
|
||||
fontWeight,
|
||||
letterSpacing,
|
||||
textSize,
|
||||
totalFills
|
||||
})
|
||||
totalFills,
|
||||
});
|
||||
|
||||
// Serialize fills
|
||||
let fillOffset = leafOffset + LEAF_ATTR_SIZE;
|
||||
@@ -485,14 +467,13 @@ export function updateTextShape(fontSize, root) {
|
||||
|
||||
// Add text content
|
||||
const textOffset = leafOffset;
|
||||
console.log('textOffset', textOffset);
|
||||
heap.set(textBuffer, textOffset);
|
||||
|
||||
Module._set_shape_text_content();
|
||||
}
|
||||
}
|
||||
|
||||
export function addTextShape(fontSize, text) {
|
||||
export function addTextShape(text, fonts) {
|
||||
const numLeaves = 1; // Single text leaf for simplicity
|
||||
const textBuffer = new TextEncoder().encode(text);
|
||||
const textSize = textBuffer.byteLength;
|
||||
@@ -529,13 +510,25 @@ export function addTextShape(fontSize, text) {
|
||||
|
||||
// Serialize leaf attributes
|
||||
const leafOffset = PARAGRAPH_ATTR_SIZE;
|
||||
const fontStyle = getFontStyle('normal');
|
||||
const fontStyleSerialized = getFontStyle(fontStyle);
|
||||
const fontSize = 14;
|
||||
const letterSpacing = 0;
|
||||
const fontWeight = 400;
|
||||
const fontFamily = 'MontserratAlternates';
|
||||
const fontData = getFontFrom(fontFamily, fontWeight, fontStyle, fonts);
|
||||
const defaultFontId = new Uint32Array([0, 0, 0, 0]);
|
||||
const fontId = fontData ? getU32(fontData.id) : defaultFontId;
|
||||
|
||||
setLeafData(dview, leafOffset, {
|
||||
fontId,
|
||||
fontSize,
|
||||
fontWeight: 400,
|
||||
fontStyle: fontStyleSerialized,
|
||||
fontWeight,
|
||||
textDecoration: 0,
|
||||
textDirection: 0,
|
||||
textTransform: 0,
|
||||
letterSpacing: 0,
|
||||
letterSpacing,
|
||||
textSize,
|
||||
totalFills,
|
||||
});
|
||||
@@ -558,3 +551,44 @@ export function addTextShape(fontSize, text) {
|
||||
// Call the WebAssembly function
|
||||
Module._set_shape_text_content();
|
||||
}
|
||||
|
||||
export function storeFonts(fonts) {
|
||||
for (const [fontName, fontStyles] of fonts) {
|
||||
for (const font of fontStyles) {
|
||||
const shapeId = getU32('00000000-0000-0000-0000-000000000000');
|
||||
const fontId = getU32(font.id);
|
||||
const weight = font.weight;
|
||||
const style = getFontStyle(font.style);
|
||||
const size = font.arrayBuffer.byteLength;
|
||||
const ptr = Module._alloc_bytes(size);
|
||||
const heap = Module.HEAPU8;
|
||||
const mem = new Uint8Array(heap.buffer, ptr, size);
|
||||
mem.set(new Uint8Array(font.arrayBuffer));
|
||||
const emoji = false
|
||||
const fallback = false
|
||||
Module._store_font(
|
||||
shapeId[0],
|
||||
shapeId[1],
|
||||
shapeId[2],
|
||||
shapeId[3],
|
||||
fontId[0],
|
||||
fontId[1],
|
||||
fontId[2],
|
||||
fontId[3],
|
||||
weight,
|
||||
style,
|
||||
emoji,
|
||||
fallback,
|
||||
);
|
||||
Module._is_font_uploaded(
|
||||
fontId[0],
|
||||
fontId[1],
|
||||
fontId[2],
|
||||
fontId[3],
|
||||
weight,
|
||||
style,
|
||||
emoji,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user