Help language development. Donate to The Perl Foundation
This is a Raku module for composing and rendering HTML-5 canvases.
It supports the majority of the HTML Canvas 2D Context API.
A canvas may be constructed via the API, then rendered to JavaScript via the .js
or .to-html
methods, or saved to a Cairo-supported format such as PNG, SVG or PDF.
The module includes classes:
HTML::Canvas
, a Raku implementation of the basic HTML Canvas 2D API.HTML::Canvas::Image
- for image loading in a variety of formatsHTML::Canvas::Gradient
- for image gradientsHTML::Canvas::Path2
- for path objectsHTML::Canvas::To::Cairo
- a built-in renderer, which can output to several formats,
including PNG, SVG and PDF.This package depends on Cairo, Font::FreeType, Text::FriBidi and HarfBuzz. Additional fonts may also be required on your system on your system:
Cairo
library is also needed. See instructions at https://cairographics.org/download/.Fonts are currently found using fontconfig's fc-match utility. For example:
% fc-match 'arial;weight=bold'
DejaVuSans.ttf: "DejaVu Sans" "Book"
If fc-match is unable to find a font. HTML::Canvas currently falls back to using a mono-spaced font (FreeMono).
The font may need to be installed on your system and/or fontconfig may need additional configuration to ensure it finds the correct font.
use v6; # Create a simple Canvas. Save as PNG and HTML use HTML::Canvas; my HTML::Canvas $canvas .= new: :width(150), :height(100); $canvas.context: -> \ctx { ctx.strokeRect(0, 0, 150, 100); ctx.save; { ctx.fillStyle = "orange"; ctx.fillRect(10, 10, 50, 50); ctx.fillStyle = "rgba(0, 0, 200, 0.3)"; ctx.fillRect(35, 35, 50, 50); }; ctx.restore; ctx.font = "18px Arial"; ctx.fillText("Hello World", 40, 75); } # save canvas as PNG use Cairo; my Cairo::Image $img = $canvas.image; $img.write_png: "examples/canvas-demo.png"; # also save canvas as HTML my $html = "<html><body>{ $canvas.to-html }</body></html>"; "examples/canvas-demo.html".IO.spurt: $html;
use v6; use Cairo; use HTML::Canvas; use HTML::Canvas::To::Cairo; # create a 128 X 128 point PDF my Cairo::Surface::PDF $surface .= create("examples/read-me-example.pdf", 128, 128); # create a PDF with two pages # use a common cache for objects shared between pages such as # fonts and images. This reduces both processing times and PDF file sizes. my HTML::Canvas::To::Cairo::Cache $cache .= new; for 1..2 -> $page { my HTML::Canvas $canvas .= new; my HTML::Canvas::To::Cairo $feed .= new: :$surface, :$canvas, :$cache; $canvas.context: { .font = "10pt times-roman bold"; .fillStyle = "blue"; .strokeStyle = "red"; .save; { .fillStyle = "rgba(1.0, 0.2, 0.2, 0.25)"; .rect(15, 20, 50, 50); .fill; .stroke; }; .restore; .fillText("Page $page/2", 12, 12); }; $surface.show_page; } $surface.finish;
The HTML::Canvas::Image
class is used to upload images for inclusion in HTML documents,
and/or rendering by back-ends.
use HTML::Canvas; use HTML::Canvas::Image; my HTML::Canvas $canvas .= new; my @html-body; # add the image, as a hidden DOM item my HTML::Canvas::Image \image .= open("t/images/camelia-logo.png"); @html-body.push: HTML::Canvas.to-html: image, :style("visibility:hidden"); # draw it $canvas.context: -> \ctx { ctx.drawImage(image, 10, 10 ); }; @html-body.push: $canvas.to-html; my $html = "<html><body>" ~ @html-body.join ~ "</body></html>";
HTML::Canvas::Image
can load a variety of image formats. The built-in
HTML::Canvas::To::Cairo
renderer only supports PNG images, as below:
Currently supported image formats are:
Back-end | PNG | GIF | JPEG | BMP |
---|---|---|---|---|
HTML::Canvas (HTML) |
X | X | X | X |
HTML::Canvas::To::Cairo |
X | |||
HTML::Canvas::To::PDF |
X | X | X |
HTML::Canvas::Path2D
can be used to create a re-usable path that can be passed to calls to the fill()
or stroke()
methods:
use HTML::Canvas; use HTML::Canvas::Path2D; my HTML::Canvas $canvas .= new; $canvas.context: -> \ctx { # Create path my HTML::Canvas::Path2D \region .= new; region.moveTo(30, 90); region.lineTo(110, 20); region.lineTo(240, 130); region.lineTo(60, 130); region.lineTo(190, 20); region.lineTo(270, 90); region.closePath(); ctx.fillStyle = 'green'; ctx.fill(region, 'evenodd'); ctx.translate(100, 100); ctx.fillStyle = 'blue'; ctx.fill(region); }
The following methods can be used in path construction:
moveTo(Numeric \x, Numeric \y)
lineTo(Numeric \x, Numeric \y)
quadraticCurveTo(Numeric \cx, Numeric \cy, Numeric \x, Numeric \y)
bezierCurveTo(Numeric \cx1, Numeric \cy1, Numeric \cx2, Numeric \cy2, Numeric \x, Numeric \y)
rect(Numeric \x, Numeric \y, Numeric \w, Numeric \h)
arc(Numeric \x, Numeric \y, Numeric \r, Numeric \startAngle, Numeric \endAngle, Bool \antiClockwise = False)
closePath()
The methods below implement the majority of the W3C HTML Canvas 2D Context API.
has Numeric $.lineWidth = 1.0;
has Numeric $.globalAlpha = 1.0;
subset LineCap of Str where 'butt'|'round'|'square';
has LineCap $.lineCap = 'butt';
subset LineJoin of Str where 'bevel'|'round'|'miter';
has LineJoin $.lineJoin = 'bevel';
has Str $.font = '10pt times-roman';
subset Baseline of Str where 'alphabetic'|'top'|'hanging'|'middle'|'ideographic'|'bottom';
has Baseline $.textBaseline = 'alphabetic';
subset TextAlignment of Str where 'start'|'end'|'left'|'right'|'center';
has TextAlignment $.textAlign = 'start';
subset TextDirection of Str where 'ltr'|'rtl';
has TextDirection $.direction = 'ltr';
subset ColorSpec where Str|HTML::Canvas::Gradient|HTML::Canvas::Pattern;
has ColorSpec $.fillStyle is rw = 'black';
has ColorSpec $.strokeStyle is rw = 'black';
has Numeric @.lineDash;
has Numeric $.lineDashOffset = 0.0;
save()
restore()
scale(Numeric $x, Numeric $y)
rotate(Numeric $rad)
translate(Numeric $x, Numeric $y)
transform(Numeric \a, Numeric \b, Numeric \c, Numeric \d, Numeric \e, Numeric \f)
setTransform(Numeric \a, Numeric \b, Numeric \c, Numeric \d, Numeric \e, Numeric \f)
clearRect(Numeric $x, Numeric $y, Numeric $w, Numeric $h)
fillRect(Numeric $x, Numeric $y, Numeric $w, Numeric $h)
strokeRect(Numeric $x, Numeric $y, Numeric $w, Numeric $h)
beginPath()
fill(FillRule $rule?)
or fill(HTML::Canvas::Path2D $path, FillRule $rule?)
stroke(HTML::Canvas::Path2D $path?)
clip()
fillText(Str $text, Numeric $x, Numeric $y, Numeric $max-width?)
strokeText(Str $text, Numeric $x, Numeric $y, Numeric $max-width?)
measureText(Str $text)
closePath()
moveTo(Numeric \x, Numeric \y)
lineTo(Numeric \x, Numeric \y)
quadraticCurveTo(Numeric \cp1x, Numeric \cp1y, Numeric \x, Numeric \y)
bezierCurveTo(Numeric \cp1x, Numeric \cp1y, Numeric \cp2x, Numeric \cp2y, Numeric \x, Numeric \y)
rect(Numeric $x, Numeric $y, Numeric $w, Numeric $h)
arc(Numeric $x, Numeric $y, Numeric $radius, Numeric $startAngle, Numeric $endAngle, Bool $counterClockwise?)
multi method drawImage( $image, Numeric \sx, Numeric \sy, Numeric \sw, Numeric \sh, Numeric \dx, Numeric \dy, Numeric \dw, Numeric \dh);
multi method drawImage(CanvasOrXObject $image, Numeric $dx, Numeric $dy, Numeric $dw?, Numeric $dh?)
createLinearGradient(Numeric $x0, Numeric $y0, Numeric $x1, Numeric $y1)
createRadialGradient(Numeric $x0, Numeric $y0, Numeric $r0, Numeric $x1, Numeric $y1, Numeric:D $r1)
createPattern($image, HTML::Canvas::Pattern::Repetition $repetition = 'repeat')
Example:
use HTML::Canvas; use HTML::Canvas::Image; my HTML::Canvas \ctx .= new; my @html-body; ## Images ## my HTML::Canvas::Image \image .= open("t/images/crosshair-100x100.jpg"); # save to HTML @html-body.push: HTML::Canvas.to-html: image, :style("visibility:hidden"); # draw on the canvas ctx.drawImage(image, 20, 10, 50, 50); ## Patterns ## my \pat = ctx.createPattern(image,'repeat'); ctx.fillStyle = pat; ctx.translate(10,50); ctx.fillRect(10,10,150,100); ## Gradients with ctx.createRadialGradient(75,50,5,90,60,100) -> $grd { $grd.addColorStop(0,"red"); $grd.addColorStop(0.5,"white"); $grd.addColorStop(1,"blue"); ctx.fillStyle = $grd; ctx.translate(10,200); ctx.fillRect(10, 10, 150, 100); } say ctx.js;
Currently support for getImageData
and putImageData
(3 argument format) only.
getImageData(Numeric sx, Numeric sy, Numeric sw, Numeric sh)
putImageData(image-data, Numeric dx, Numeric dy)