Skip to content

Commit

Permalink
use Paragraph's get_path for outlineText()
Browse files Browse the repository at this point in the history
- replaces the more basic `Path::from_string` approach and now supports all the same typographic styling options as fillText/strokeText
- added optional `width` argument to `outlineText` to set textWrap cutoff
  • Loading branch information
samizdatco committed Oct 28, 2024
1 parent de6f105 commit a64e15d
Show file tree
Hide file tree
Showing 7 changed files with 25 additions and 34 deletions.
9 changes: 4 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,12 @@

## 🥚 ⟩ [Unreleased]

### Maintenance
- Updated `winit` and replaced the end-of-life’d [skulpin](https://github.com/aclysma/skulpin)-based Vulkan renderer with a new implementation using Vulkano for window-drawing on Windows and Linux.
> It’s a fairly direct adaptation of Vulkano [sample code][vulkano_demo] for device setup with skia-specific rendering routines inspired by [@pragmatrix](https://github.com/pragmatrix)’s renderer for [emergent](pragmatrix_emergent). All of which is to say, if you understand this better than I do I'd love some suggestions for improving the rendering setup.
### New Features
- The [**Window**][window] class now has a [`resizable`](resizable) property which can be set to `false` to prevent the window from being manually resized or maximized (contributed by [@nornagon](https://github.com/nornagon) #124).
- The **Canvas** object has a new `engine` property which describes whether the CPU or GPU is being used, which graphics device was selected, and what (if any) error prevented it from being initialized.
- **FontLibrary.**[use()][FontLibrary.use] now supports dynamically loaded [WOFF & WOFF2][woff_wiki] fonts
- **Canvas**es can now be saved as WEBP images and **Image**s can load WEBPs as well (contributed by [@mpaperno](https://github.com/mpaperno) #177, h/t [@revam](https://github.com/revam) for the initial work on this)
- The `outlineText()` method now takes an optional `width` argument and supports all the context's typographic settings (e.g., `.font`, `.fontVariant`, `.textWrap`, `.textTracking`, etc.)

### Breaking Changes
- The **KeyboardEvent** object returned by the `keyup`/`keydown` and `input` event listeners now has fields and values consistent with browser behavior. In particular, `code` is now a name (e.g., `ShiftLeft` or `KeyS`) rather than a numeric scancode, `key` is a straightforward label for the key (e.g., `Shift` or `s`) and the new [`location`](key_location) field provides a numeric description of which variant of a key was pressed.
Expand All @@ -20,12 +17,14 @@
- The **Image**.onload callback now properly sets `this` to point to the new image (contributed by [@mpaperno](https://github.com/mpaperno) & [@ForkKILLET](https://github.com/ForkKILLET)).
- Creating a **Window** with `fullscreen` set to `true` now takes effect immediately (previously it was failing silently)
- Drawing paths after setting an invalid transform no longer crashes (contributed by [@mpaperno](https://github.com/mpaperno) #175)
- Windows on macOS 14+ no longer [become unresponsive](https://github.com/gfx-rs/gfx/issues/2460) after being fully occluded by other windows
- Windows with `.on("draw")` handlers no longer [become unresponsive](https://github.com/gfx-rs/gfx/issues/2460) on macOS 14+ after being fully occluded by other windows
- Ellipses with certain combinations of positive and negative start- and stop-angles now render correctly—previously they would not appear at all if the total sweep exceeded 360° (contributed by [@mpaperno](https://github.com/mpaperno) #176)

### Misc. Improvements
- Upgraded Skia to milestone 129
- Added TypeScript definitions for the **Window** object’s event types (contributed by [@saantonandre](https://github.com/saantonandre) #163) and the `roundRect` method (contributed by [@sandy85625](https://github.com/sandy85625) & [@santilema](https://github.com/santilema))
- Updated `winit` and replaced the end-of-life’d [skulpin](https://github.com/aclysma/skulpin)-based Vulkan renderer with a new implementation using Vulkano for window-drawing on Windows and Linux.
> It’s a fairly direct adaptation of Vulkano [sample code][vulkano_demo] for device setup with skia-specific rendering routines inspired by [@pragmatrix](https://github.com/pragmatrix)’s renderer for [emergent](pragmatrix_emergent). All of which is to say, if you understand this better than I do I'd love some suggestions for improving the rendering setup.
[resizable]: https://github.com/samizdatco/skia-canvas#resizable
[key_location]: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/location
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -562,11 +562,11 @@ The `baseline` value is a y-axis offset from the text origin to that particular

The `startIndex` and `endIndex` values are the indices into the string of the first and last character that were typeset on that line.

#### `outlineText(str)`
#### `outlineText(str, [width])`

The `outlineText()` method typesets a string and returns a Path2D containing the shapes of its character glyphs. It will use the context’s current [`.font`][font], [`.textAlign`][textAlign], and [`.textBaseline`][textBaseline] settings to style the string and will anchor the text relative to the (0, 0) origin point. As a result, you’ll typically want to use the context’s transform-related methods or Path2D’s [`offset()`][p2d_offset] and [`transform()`][p2d_transform] to position the path before drawing it to the canvas.
The `outlineText()` method typesets a string and returns a Path2D containing the shapes of its character glyphs. It will use the context’s current typography settings (e.g., [`.font`][font], [`.textWrap`](#textWrap), [`.textAlign`][textAlign] [`.textBaseline`][textBaseline], etc.) to style the string and will anchor the text relative to the (0, 0) origin point. As a result, you’ll typically want to use the context’s transform-related methods or Path2D’s [`offset()`][p2d_offset] and [`transform()`][p2d_transform] to position the path before drawing it to the canvas.

Note that path-generation uses a more limited typesetting system than [`fillText()`][drawText] and [`strokeText()`][drawText]. As such, it ignores any settings made using the [`.fontVariant`](#fontvariant) or [`.textTracking`](#texttracking) properties and does not support multi-line text (regardless of the current [`.textWrap`](#textwrap) setting).
As with the [`fillText()`][drawText] and [`strokeText()`][drawText] methods, `outlineText()` will produce a single line of text unless `.textWrap` is enabled and will use the optional `width` argument to determine the maximum line width. If a `width` is specified but `.textWrap` is *not* enabled, any text that doesn't fit within that measure will be omitted from the path.

```js
ctx.textBaseline = 'top'
Expand Down
2 changes: 1 addition & 1 deletion lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ export interface CanvasRenderingContext2D extends CanvasCompositing, CanvasDrawI
fillText(text: string, x: number, y:number, maxWidth?: number): void
strokeText(text: string, x: number, y:number, maxWidth?: number): void
measureText(text: string, maxWidth?: number): TextMetrics
outlineText(text: string): Path2D
outlineText(text: string, maxWidth?: number): Path2D

reset(): void
}
Expand Down
4 changes: 2 additions & 2 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -764,8 +764,8 @@ class CanvasRenderingContext2D extends RustClass{
this.ƒ('strokeText', toString(text), x, y, maxWidth)
}

outlineText(text){
let path = this.ƒ('outlineText', toString(text))
outlineText(text, width){
let path = this.ƒ('outlineText', toString(text), width)
return path ? wrap(Path2D, path) : null
}

Expand Down
8 changes: 3 additions & 5 deletions src/context/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -910,12 +910,10 @@ pub fn measureText(mut cx: FunctionContext) -> JsResult<JsArray> {
pub fn outlineText(mut cx: FunctionContext) -> JsResult<JsValue> {
let this = cx.argument::<BoxedContext2D>(0)?;
let text = string_arg(&mut cx, 1, "text")?;
let width = opt_float_arg(&mut cx, 2);
let mut this = this.borrow_mut();
if let Some(path) = this.outline_text(&text){
Ok(cx.boxed(RefCell::new(Path2D{path})).upcast())
}else{
Ok(cx.undefined().upcast())
}
let path = this.outline_text(&text, width);
Ok(cx.boxed(RefCell::new(Path2D{path})).upcast())
}

// -- type properties ---------------------------------------------------------------
Expand Down
4 changes: 2 additions & 2 deletions src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -508,8 +508,8 @@ impl Context2D{
Typesetter::new(&self.state, text, width).metrics()
}

pub fn outline_text(&self, text:&str) -> Option<Path>{
Typesetter::new(&self.state, text, None).path()
pub fn outline_text(&self, text:&str, width:Option<f32>) -> Path{
Typesetter::new(&self.state, text, width).path()
}

pub fn color_with_alpha(&self, src:&Color) -> Color{
Expand Down
26 changes: 10 additions & 16 deletions src/typography.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,22 +144,16 @@ impl Typesetter{
results
}

pub fn path(&mut self) -> Option<SkPath> {
let families:Vec<String> = self.char_style.font_families().iter().map(|fam| fam.to_string()).collect();
let matches = self.typefaces.find_typefaces(&families, self.char_style.font_style());
if let Some(typeface) = matches.first(){
let font = Font::from_typeface(typeface, self.char_style.font_size());
let (leading, metrics) = font.metrics();
let (width, bounds) = font.measure_str(&self.text, None);
let offset = (
width * get_alignment_factor(&self.graf_style),
get_baseline_offset(&metrics, self.baseline)
);

Some(SkPath::from_str(&self.text, offset, &font))
}else{
None
}
pub fn path(&mut self) -> SkPath {
let (mut paragraph, mut offset) = self.layout(&Paint::default());
offset.y -= self.char_style.font_metrics().ascent + paragraph.alphabetic_baseline();

let mut path = SkPath::new();
for idx in 0..paragraph.line_number(){
let (skipped, line) = paragraph.get_path_at(idx);
path.add_path(&line, offset, None);
};
path
}
}

Expand Down

0 comments on commit a64e15d

Please sign in to comment.