diff --git a/Base/res/html/misc/canvas-text.html b/Base/res/html/misc/canvas-text.html new file mode 100644 index 00000000000..961375488c8 --- /dev/null +++ b/Base/res/html/misc/canvas-text.html @@ -0,0 +1,66 @@ + + + + +Canvas Text Examples + + + +

Canvas Text Examples

+ +Canvas text-align
+

+ + + +Canvas text-baseline
+

+ + + + + diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index a79bb1be438..039eced5f5b 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/Userland/Libraries/LibWeb/Forward.h @@ -36,6 +36,8 @@ class OptionConstructor; enum class AudioContextLatencyCategory; enum class CanPlayTypeResult; enum class CanvasFillRule; +enum class CanvasTextAlign; +enum class CanvasTextBaseline; enum class DOMParserSupportedType; enum class EndingType; enum class ImageSmoothingQuality; diff --git a/Userland/Libraries/LibWeb/HTML/Canvas/CanvasPathDrawingStyles.idl b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasPathDrawingStyles.idl index 305f7b67a8e..4e6f7e717ef 100644 --- a/Userland/Libraries/LibWeb/HTML/Canvas/CanvasPathDrawingStyles.idl +++ b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasPathDrawingStyles.idl @@ -1,13 +1,6 @@ // https://html.spec.whatwg.org/multipage/canvas.html#canvaslinecap enum CanvasLineCap { "butt", "round", "square" }; enum CanvasLineJoin { "round", "bevel", "miter" }; -enum CanvasTextAlign { "start", "end", "left", "right", "center" }; -enum CanvasTextBaseline { "top", "hanging", "middle", "alphabetic", "ideographic", "bottom" }; -enum CanvasDirection { "ltr", "rtl", "inherit" }; -enum CanvasFontKerning { "auto", "normal", "none" }; -enum CanvasFontStretch { "ultra-condensed", "extra-condensed", "condensed", "semi-condensed", "normal", "semi-expanded", "expanded", "extra-expanded", "ultra-expanded" }; -enum CanvasFontVariantCaps { "normal", "small-caps", "all-small-caps", "petite-caps", "all-petite-caps", "unicase", "titling-caps" }; -enum CanvasTextRendering { "auto", "optimizeSpeed", "optimizeLegibility", "geometricPrecision" }; // https://html.spec.whatwg.org/multipage/canvas.html#canvaspathdrawingstyles interface mixin CanvasPathDrawingStyles { diff --git a/Userland/Libraries/LibWeb/HTML/Canvas/CanvasState.h b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasState.h index 34a70f76085..162d8beae34 100644 --- a/Userland/Libraries/LibWeb/HTML/Canvas/CanvasState.h +++ b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasState.h @@ -80,6 +80,8 @@ public: Bindings::ImageSmoothingQuality image_smoothing_quality { Bindings::ImageSmoothingQuality::Low }; float global_alpha = { 1 }; Optional clip; + Bindings::CanvasTextAlign text_align { Bindings::CanvasTextAlign::Start }; + Bindings::CanvasTextBaseline text_baseline { Bindings::CanvasTextBaseline::Alphabetic }; }; DrawingState& drawing_state() { return m_drawing_state; } DrawingState const& drawing_state() const { return m_drawing_state; } diff --git a/Userland/Libraries/LibWeb/HTML/Canvas/CanvasTextDrawingStyles.h b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasTextDrawingStyles.h new file mode 100644 index 00000000000..70fdd5d3521 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasTextDrawingStyles.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023, Bastiaan van der Plaat + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Web::HTML { + +// https://html.spec.whatwg.org/multipage/canvas.html#canvastextdrawingstyles +template +class CanvasTextDrawingStyles { +public: + ~CanvasTextDrawingStyles() = default; + + void set_text_align(Bindings::CanvasTextAlign text_align) { my_drawing_state().text_align = text_align; } + Bindings::CanvasTextAlign text_align() const { return my_drawing_state().text_align; } + + void set_text_baseline(Bindings::CanvasTextBaseline text_baseline) { my_drawing_state().text_baseline = text_baseline; } + Bindings::CanvasTextBaseline text_baseline() const { return my_drawing_state().text_baseline; } + +protected: + CanvasTextDrawingStyles() = default; + +private: + CanvasState::DrawingState& my_drawing_state() { return reinterpret_cast(*this).drawing_state(); } + CanvasState::DrawingState const& my_drawing_state() const { return reinterpret_cast(*this).drawing_state(); } +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/Canvas/CanvasTextDrawingStyles.idl b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasTextDrawingStyles.idl new file mode 100644 index 00000000000..e6d1f5d8343 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasTextDrawingStyles.idl @@ -0,0 +1,22 @@ +// https://html.spec.whatwg.org/multipage/canvas.html#canvastextalign +// enum CanvasTextAlign { "start", "end", "left", "right", "center" }; +// enum CanvasTextBaseline { "top", "hanging", "middle", "alphabetic", "ideographic", "bottom" }; +enum CanvasDirection { "ltr", "rtl", "inherit" }; +enum CanvasFontKerning { "auto", "normal", "none" }; +enum CanvasFontStretch { "ultra-condensed", "extra-condensed", "condensed", "semi-condensed", "normal", "semi-expanded", "expanded", "extra-expanded", "ultra-expanded" }; +enum CanvasFontVariantCaps { "normal", "small-caps", "all-small-caps", "petite-caps", "all-petite-caps", "unicase", "titling-caps" }; +enum CanvasTextRendering { "auto", "optimizeSpeed", "optimizeLegibility", "geometricPrecision" }; + +// https://html.spec.whatwg.org/multipage/canvas.html#canvastextdrawingstyles +interface mixin CanvasTextDrawingStyles { + // FIXME: attribute DOMString font; // (default 10px sans-serif) + attribute CanvasTextAlign textAlign; // (default: "start") + attribute CanvasTextBaseline textBaseline; // (default: "alphabetic") + // FIXME: attribute CanvasDirection direction; // (default: "inherit") + // FIXME: attribute DOMString letterSpacing; // (default: "0px") + // FIXME: attribute CanvasFontKerning fontKerning; // (default: "auto") + // FIXME: attribute CanvasFontStretch fontStretch; // (default: "normal") + // FIXME: attribute CanvasFontVariantCaps fontVariantCaps; // (default: "normal") + // FIXME: attribute CanvasTextRendering textRendering; // (default: "auto") + // FIXME: attribute DOMString wordSpacing; // (default: "0px") +}; diff --git a/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp index ad89b90e022..205f9906ece 100644 --- a/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp +++ b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp @@ -205,7 +205,32 @@ void CanvasRenderingContext2D::fill_text(DeprecatedString const& text, float x, draw_clipped([&](auto& painter) { auto& drawing_state = this->drawing_state(); auto& base_painter = painter.underlying_painter(); + auto text_rect = Gfx::FloatRect(x, y, max_width.has_value() ? static_cast(max_width.value()) : base_painter.font().width(text), base_painter.font().pixel_size()); + + // Apply text align to text_rect + // FIXME: CanvasTextAlign::Start and CanvasTextAlign::End currently do not nothing for right-to-left languages: + // https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-textalign-start + // Default alignment of draw_text is left so do nothing by CanvasTextAlign::Start and CanvasTextAlign::Left + if (drawing_state.text_align == Bindings::CanvasTextAlign::Center) { + text_rect.translate_by(-text_rect.width() / 2, 0); + } + if (drawing_state.text_align == Bindings::CanvasTextAlign::End || drawing_state.text_align == Bindings::CanvasTextAlign::Right) { + text_rect.translate_by(-text_rect.width(), 0); + } + + // Apply text baseline to text_rect + // FIXME: Implement CanvasTextBasline::Hanging, Bindings::CanvasTextAlign::Alphabetic and Bindings::CanvasTextAlign::Ideographic for real + // right now they are just handled as textBaseline = top or bottom. + // https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-textbaseline-hanging + // Default baseline of draw_text is top so do nothing by CanvasTextBaseline::Top and CanvasTextBasline::Hanging + if (drawing_state.text_baseline == Bindings::CanvasTextBaseline::Middle) { + text_rect.translate_by(0, -base_painter.font().pixel_size() / 2); + } + if (drawing_state.text_baseline == Bindings::CanvasTextBaseline::Alphabetic || drawing_state.text_baseline == Bindings::CanvasTextBaseline::Ideographic || drawing_state.text_baseline == Bindings::CanvasTextBaseline::Bottom) { + text_rect.translate_by(0, -base_painter.font().pixel_size()); + } + auto transformed_rect = drawing_state.transform.map(text_rect); auto color = drawing_state.fill_style.to_color_but_fixme_should_accept_any_paint_style(); base_painter.draw_text(transformed_rect, text, Gfx::TextAlignment::TopLeft, color.with_opacity(drawing_state.global_alpha)); diff --git a/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h index ea7f1e7a6d2..7338bda4e61 100644 --- a/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h +++ b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -53,7 +54,8 @@ class CanvasRenderingContext2D , public CanvasImageData , public CanvasImageSmoothing , public CanvasCompositing - , public CanvasPathDrawingStyles { + , public CanvasPathDrawingStyles + , public CanvasTextDrawingStyles { WEB_PLATFORM_OBJECT(CanvasRenderingContext2D, Bindings::PlatformObject); diff --git a/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.idl b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.idl index 1f2aa5baf1f..73d03efb452 100644 --- a/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.idl +++ b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.idl @@ -7,6 +7,7 @@ #import #import #import +#import #import #import #import @@ -14,6 +15,10 @@ enum ImageSmoothingQuality { "low", "medium", "high" }; +// FIXME: This should be in CanvasTextDrawingStyles.idl but then it is not exported +enum CanvasTextAlign { "start", "end", "left", "right", "center" }; +enum CanvasTextBaseline { "top", "hanging", "middle", "alphabetic", "ideographic", "bottom" }; + // https://html.spec.whatwg.org/multipage/canvas.html#canvasrenderingcontext2d [Exposed=Window] interface CanvasRenderingContext2D { @@ -34,5 +39,5 @@ CanvasRenderingContext2D includes CanvasText; CanvasRenderingContext2D includes CanvasDrawImage; CanvasRenderingContext2D includes CanvasImageData; CanvasRenderingContext2D includes CanvasPathDrawingStyles; -// FIXME: CanvasRenderingContext2D includes CanvasTextDrawingStyles; +CanvasRenderingContext2D includes CanvasTextDrawingStyles; CanvasRenderingContext2D includes CanvasPath;