HTML-Canvas-raku
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 toDataURL()
, .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.
Install
This package depends on Cairo, FontConfig, Font::FreeType, Text::FriBidi and HarfBuzz. Additional fonts may also be required on your system:
- the freetype native library needs to be on your system to enable Font::FreeType installation
- Text::FriBidi is required for handling of BiDirectional text.
- the native
Cairo
library is also needed. See instructions at https://cairographics.org/download/. - Installation of the fontconfig native library is also required.
Example
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, source and data URL:
# 1. Save source Javascript
my $html = "<html><body>{ $canvas.to-html }</body></html>";
"examples/canvas-demo-js.html".IO.spurt: $html;
# 2. Save data URL Image
my $data-uri = $canvas.toDataURL();
$html = "<html><body><img src='$data-uri'/></body></html>";
"examples/canvas-demo-url.html".IO.spurt: $html;
Saving as PDF
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: :$surface, :$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;
Images
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 | |
Paths
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()
Font Loading
Font Resources
This module integrates with the CSS::Font::Resources module.
A list of @font-face
CSS font descriptors can be passed to the canvas object to specify font sources. Fonts are resolved
the same way as @font-face
rules in a stylesheet.
For example:
use HTML::Canvas;
use HTML::Canvas::To::Cairo;
use CSS::Font::Descriptor;
use Cairo;
my CSS::Font::Descriptor $ugly .= new: :font-family<arial>, :src<url(resources/font/FreeMono.ttf)>;
my HTML::Canvas $canvas .= new: :font-face[$ugly], :width(650), :height(400);
$canvas<scale>(2.0, 3.0);
$canvas<font> = "30px Arial";
$canvas.fillText("Hello World",10,50);
# save canvas as PNG
my Cairo::Surface $surface = $canvas.image;
$surface.write_png: "tmp/ariel-ugly.png";
Note that supported fonts may be backend dependant.
HTML::Canvas::To::Cairo
supports almost all common font formats via the FreeType/Cairo itegrationHTML::Canvas::To::PDF
supports a smaller set of font formats; Fonts with extensions *.otf
, *.ttf
, *.cff
, *.pfa
, *.pfb
should work. But resources with extensions *.woff
, *.woff2
, and *.eot
are ignored.
Font System Loading
Local fonts and fonts without matching @font-face
font descriptors are resolved using FontConfig.
If FontConfig fails to find a matching font, HTML::Canvas falls back to using a mono-spaced font (FreeMono).
Methods
The methods below implement the majority of the W3C HTML Canvas 2D Context API.
Setters/Getters
lineWidth
has Numeric $.lineWidth = 1.0;
globalAlpha
has Numeric $.globalAlpha = 1.0;
lineCap
subset LineCap of Str where 'butt'|'round'|'square';
has LineCap $.lineCap = 'butt';
lineJoin
subset LineJoin of Str where 'bevel'|'round'|'miter';
has LineJoin $.lineJoin = 'bevel';
font
has Str $.font = '10pt times-roman';
textBaseline
subset Baseline of Str where 'alphabetic'|'top'|'hanging'|'middle'|'ideographic'|'bottom';
has Baseline $.textBaseline = 'alphabetic';
textAlign
subset TextAlignment of Str where 'start'|'end'|'left'|'right'|'center';
has TextAlignment $.textAlign = 'start';
direction
subset TextDirection of Str where 'ltr'|'rtl';
has TextDirection $.direction = 'ltr';
fillStyle
subset ColorSpec where Str|HTML::Canvas::Gradient|HTML::Canvas::Pattern;
has ColorSpec $.fillStyle is rw = 'black';
strokeStyle
has ColorSpec $.strokeStyle is rw = 'black';
setLineDash/getLineDash/lineDash
has Numeric @.lineDash;
lineDashOffset
has Numeric $.lineDashOffset = 0.0;
Graphics State
save()
restore()
scale(Numeric $x, Numeric $y)
rotate(Numeric $rad)
translate(Numeric $x, Numeric $y)
Painting Methods
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)
Path Methods
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?)
Images Patterns and Gradients
drawImage:
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;
Image Data
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)
Additional Rendering Backends