### ----------------------------------------------------
### -- PDF::NameTags
### -- Licenses: Artistic-2.0
### -- Authors: Tom Browder
### -- File: lib/PDF/NameTags.rakumod
### ----------------------------------------------------
### -- Chunk 2 of 3
clip.defined;
    # what if none are defined?
    if $clip {
        # MUST NOT TRANSFORM OR TRANSLATE
        ($fill, $stroke) = 0, 0;
    }
    else {
        # make stroke the default
        $stroke = 1 if not ($fill or $stroke);
    }

    if $debug {
        say "   Drawing a circle...";
        if $fill {
            say "     Filling with color $fill-color...";
        }
        if $stroke {
            say "     Stroking with color $stroke-color...";
        }
        if $clip {
            say "     Clipping the circle";
        }
        else {
            say "     NOT clipping the circle";
        }
    }


    my $g = $page.gfx;
    $g.Save if not $clip; # CRITICAL
    # NO translation

    if not $clip {
        $g.SetLineWidth: $linewidth;
        $g.StrokeColor = $stroke-color;
        $g.FillColor   = $fill-color;
    }

    # draw the path
    $g.MoveTo: $llx, $lly;
    $g.LineTo: $llx+$width, $lly;
    $g.LineTo: $llx+$width, $lly+$height;
    $g.LineTo: $llx       , $lly+$height;
    $g.ClosePath;

    if not $clip {
        if $fill and $stroke {
            $g.FillStroke;
        }
        elsif $fill {
            $g.Fill;
        }
        elsif $stroke {
            $g.Stroke;
        }
        else {
            die "FATAL: Unknown drawing status";
        }

        $g.Restore;
    }
    else {
        $g.Clip;
        $g.EndPath;
    }

} # sub draw-rectangle(

sub make-graph-paper(
    $ofil,
    PDF::GraphPaper :$description,
    :$debug,
    ) is export {

    # short-hand use
    my $p = $description;

    #=========================
    # Determine maximum horizontal grid squares for Letter paper,
    # portrait orientation, and 0.4-inch horizontal margins.
    my $page-width  = 8.5 * 72;
    my $page-height = 11  * 72;
    my $max-graph-size = $page-width - $p.margins * 2;
    my $max-ncells = $max-graph-size div $p.cell-size;

    my $ngrids = $max-ncells div $p.cells-per-grid;
    my $graph-size = $ngrids * $p.cells-per-grid * $p.cell-size;
    my $ncells = $ngrids * $p.cells-per-grid;

    if $debug {
        say qq:to/HERE/;
        Given cells of size {$p.cell-size} x {$p.cell-size}, with margins {$p.margins},
          with grids of {$p.cells-per-grid} cells per grid = $ngrids total grid cells.
        HERE
    }

    my $pdf  = PDF::Lite.new;
    $pdf.media-box = 0, 0, $page-width, $page-height;
    my $page = $pdf.add-page;

    # Translate to the lower-left corner of the grid area
    my $llx = 0 + 0.5 * $page-width - 0.5 * $graph-size;
    my $lly = $page-height - 72 - $graph-size;
    $page.graphics: {
        .transform: :translate($llx, $lly);

        # draw horizontal lines, $y is varying 0 to $twidth
        #   bottom to top
        for 0..$ncells -> $i {
            my $y = $i * $p.cell-size;
            if not $i mod 10 {
                .LineWidth = $p.grid-linewidth;
            }
            elsif not $i mod 5 {
                .LineWidth = $p.mid-grid-linewidth;
            }
            else {
                .LineWidth = $p.cell-linewidth;
            }
            .MoveTo: 0,           $y;
            .LineTo: $graph-size, $y;
            .Stroke;
        }
        # draw vertical lines, $x is varying 0 to $twidth
        #   left to right
        for 0..$ncells -> $i {
            my $x = $i * $p.cell-size;
            if not $i mod 10 {
                .LineWidth = $p.grid-linewidth;
            }
            elsif not $i mod 5 {
                .LineWidth = $p.mid-grid-linewidth;
            }
            else {
                .LineWidth = $p.cell-linewidth;
            }
            .MoveTo: $x, 0;
            .LineTo: $x, $graph-size;
            .Stroke;
        }
    }

    $pdf.save-as: $ofil;
    say "See output file: '$ofil'";
} # sub make-graph-paper($ofil) is export {

sub deg2rad($degrees) {
    $degrees * pi / 180
}

sub rad2deg($radians) {
    $radians * 180 / pi
}

sub draw-color-wheel(
    :$cx!, :$cy!,
    :$radius!,
    :$page!,
    :$debug,
    ) is export {
    # a hex wheel of different-colored triangles centered
    # on the circle defined with the inputs

    note "DEBUG: in sub draw-color-wheel" if $debug;
    my $g = $page.gfx;
    #$page.gfx: {
    $g.Save;
    my $cnum = 0; # color number in %colors
    #my $stroke-color = color Black;
    my $stroke-color = color White;
    for 0..^6 {
        ++$cnum; # color number in %colors
        my $angle = $_ * 60;
        my $fill-color = color %colors{$cnum};
        note "DEBUG: fill color = '$fill-color'" if $debug;
        draw-hex-wedge :$cx, :$cy, :height($radius), :$angle, :stroke,
                       :fill, :$fill-color, :$stroke-color, :$page;
    }
    $g.Restore;

} # sub draw-color-wheel(

sub draw-hex-wedge(
    :$cx!, :$cy!,
    :$height!, # apex at cx, ch, height is perpendicular at the base
    :$angle!,  # degrees ccw from 3 o'clock
    :$fill is copy,
    :$stroke is copy,
    :$fill-color, #   = (color Red),
    :$stroke-color, # = (color Black),
    :$page!,
    :$debug,
    ) is export {

    $fill   = 0 if not $fill.defined;
    $stroke = 0 if not $stroke.defined;
    unless ($fill or $stroke) {
        $stroke = 1;
    }

    #
    #          0
    #         /|\ equilateral triangle
    #        / | \ c
    #       /  |h \ 60 deg
    #    1 /---+---\ 2      given: h, angles 1 and 2 60 degrees each
    #       -a   +a
    #                    h/a = tan 60 deg
    #                   |a|  = h / tan 60
    #
    my $a = $height / tan(deg2rad(60));
    # point 0 is $cx,$cy --> 0, 0
    # rotate as desired by $angle
    # draw the triangle

note "DEBUG: stroke color: $stroke-color" if $debug;
note "DEBUG: fill   color: $fill-color" if $debug;

    my $g = $page.gfx;
    $g.Save;
    $g.transform :translate($cx,$cy); # now apex is at 0, 0
    $g.transform :rotate(deg2rad($angle)); # h is positive x: 0 to h, y = 0
    $g.LineWidth = 0;
    $g.FillColor   = color $fill-color;
    $g.StrokeColor = color $stroke-color;
    $g.MoveTo: 0, 0;
    $g.LineTo: $height, -$a;
    $g.LineTo: $height, +$a;
    $g.LineTo:   0,   0;
    $g.ClosePath;
    if $fill and $stroke {
        $g.FillStroke;
    }
    elsif $fill {
        $g.Fill;
    }
    elsif $stroke {
        $g.Stroke;
    }
    $g.Restore;

} # sub draw-hex-wedge(

sub simple-clip1(
    :$x is copy,
    :$y is copy,
    :$width  is copy,
    :$height is copy,
    :$stroke-color = (color Black),
    :$fill-color   = (color White),
    :$page!,
    :$debug,
    ) is export {

    # draw a local circle for clipping
    if not ($x.defined and $y.defined) {
        $x = 0.5 * $page.media-box[2];
        $y = 0.5 * $page.media-box[3];
    }
    if not ($width.defined and $height.defined) {
        $width  = 72;
        $height = 72;
    }
    my $radius = 0.5 * $width;
    my $R = $radius;

    my $g = $page.gfx;
    $g.Save;

    $g.transform: :translate($x, $y);

    $g.StrokeColor = color Black;
    $g.FillColor   = color White;

    #=== Begin: define the clipping path ===
    #==    without calling subs
    # define the path per the localized CTM
    $g.Rectangle: -1*72, -1*72, 2*72, 2*72;
    # clip the path
    $g.Clip;
    # end the clipping path definition
    $g.EndPath;
    #=== End: define the clipping path ===

    # show the clipping path
    $g.FillColor = color Red; # White;
    $g.Rectangle: -1*72, -1*72, 2*72, 2*72;
    $g.FillStroke;

    # an offset blue rectangle
    # offset to the lower left so
    # its top-right corner is stiil
    # visible
    $g.FillColor = color Blue; # White;
    $g.Rectangle: -1.5*72, -1.5*72, 2*72, 2*72;
    $g.FillStroke;

    =begin comment
    # use four Bezier curves, counter-clockwise
    # from stack overflow: copyright 2022 by Spencer Mortenson
    #   but reversed direction
    constant c = 0.551915024495;
    $g.MoveTo:   0*$R,  1*$R; # top of the circle
    $g.CurveTo: -1*$R,  c*$R, -c*$R,  1*$R,  0*$R,  1*$R;
    $g.CurveTo: -c*$R, -1*$R, -1*$R, -c*$R, -1*$R,  0*$R;
    $g.CurveTo:  1*$R, -c*$R,  c*$R, -1*$R,  0*$R, -1*$R;
    $g.CurveTo:  c*$R,  1*$R,  1*$R,  c*$R,  1*$R,  0*$R;
    $g.ClosePath;

    $g.Clip;
    $g.EndPath;
    =end comment

    =begin comment
    # draw a local 5-pointed star overflowing the circle
    # first point is at top center
    my $RS = 1.5 * $R;

    my %point;
    # create the proper values for the five points
    my $delta-degrees = 360.0 / 5;
    for 0..^5 -> $i {
        my $degrees = $i * $delta-degrees;
        my $radians = deg2rad($degrees);
        %point{$i}<x> = $RS * sin($radians);
        %point{$i}<y> = $RS * cos($radians);
    }
    $g.MoveTo: %point<0><x>, %point<0><y>; # point 0 (top of the star)
    $g.LineTo: %point<2><x>, %point<2><y>; # point 2
    $g.LineTo: %point<4><x>, %point<4><y>; # point 4
    $g.LineTo: %point<1><x>, %point<1><y>; # point 1
    $g.LineTo: %point<3><x>, %point<3><y>; # point 3
    $g.LineTo: %point<0><x>, %point<0><y>; # point 0
    $g.CloseFillStroke;
    =end comment

    $g.Restore;

} # sub simple-clip1(

sub simple-clip2(
    :$x is copy,
    :$y is copy,
    :$width  is copy,
    :$height is copy,
    :$stroke-color = (color Black),
    :$fill-color   = (color White),
    :$page!,
    :$debug,
    ) is export {

    my $pg-width  = $page.media-box[2];
    my $pg-height = $page.media-box[3];

    # draw a local circle for clipping
    if not ($x.defined and $y.defined) {
        $x = 0.5 * $pg-width;
        $y = 0.5 * $pg-height;
    }
    if not ($width.defined and $height.defined) {
        $width  = 72;
        $height = 72;
    }

    my $radius = 0.5 * $width;
    if $debug {
        say "DEBUG: circle params:";
        say "  x ($x), y ($y), radius ($radius)";
    }

    my $g = $page.gfx;
    $g.Save;

    $g.transform: :translate($x, $y);

    $g.StrokeColor = color Black;
    $g.FillColor   = color White;

    # clip
    draw-circle-clip 0, 0, $radius+60, :clip, :$page, :$debug;

    # stroke it
    draw-circle-clip 0, 0, $radius, :stroke, :$page, :$debug;

    draw-star 0, 0, $radius+30, :stroke, :$page, :$debug;

    $g.Restore;

} # sub simple-clip2(

sub simple-clip3(
    :$x is copy,
    :$y is copy,
    :$width  is copy,
    :$height is copy,
    :$stroke-color = (color Black),
    :$fill-color   = (color White),
    :$page!,
    :$debug,
    ) is export {

    # put the first example on y = 1/4 page height from the top
    # put the second example on y = 2/4 page height from the top
    # put the third example on y = 3/4 page height from the top

    my $pg-width  = $page.media-box[2];
    my $pg-height = $page.media-box[3];

    my $cy1 = 0.75 * $pg-height;
    my $cy2 = 0.50 * $pg-height;
    my $cy3 = 0.20 * $pg-height;

    # title: plain-circle
    $x = 0.5 * $pg-width;
    $y = 0.5 * $pg-height;

    #== first example, no clip
    # draw a colored box
    my $side = 3*72;
    draw-box-clip :llx($x-0.5*$side), :lly($cy1-0.5*$side), :width($side),
                  :height($side), :fill-color(color Blue), :fill, :$page;
    # on top of it draw a white-filled circle
    my $radius = 72;
    draw-circle-clip $x, $cy1, $radius, :fill, :fill-color(color White), :$page;

    $page.gfx.Save;
    #== second example, clip to the circle
    # Note the $page.gfx was NOT saved after the clip so the clipping should be good
    # till the end of the page or after the next .Restore
    draw-circle-clip $x, $cy2, $radius, :clip, :$page;
    draw-box-clip :llx($x-0.5*$side), :lly($cy2-0.5*$side), :width($side),
                  :height($side), :fill-color(color Blue), :fill, :$page;
    $page.gfx.Restore;

    $page.gfx.Save;
    #== third example, clip to the same circle moved down some
    draw-circle-clip $x, $cy3, $radius, :clip, :$page;
    draw-color-wheel :cx($x), :cy($cy3), :radius($radius+10), :$page;

    =begin comment
    draw-hex-wedge :cx($x), :cy($cy3), :height($radius+10), :angle(0),
                   :stroke, :fill, :$page;
    draw-hex-wedge :cx($x), :cy($cy3), :height($radius+10), :angle(60),
                   :stroke, :fill, :$page;
    draw-hex-wedge :cx($x), :cy($cy3), :height($radius+10), :angle(120),
                   :stroke, :fill, :$page;
    draw-hex-wedge :cx($x), :cy($cy3), :height($radius+10), :angle(180),
                   :stroke, :fill, :$page;
    draw-hex-wedge :cx($x), :cy($cy3), :height($radius+10), :angle(240),
                   :stroke, :fill, :$page;
    =end comment

    =begin comment
    draw-box-clip :llx($x-0.5*$side), :lly($cy3-0.5*$side), :width($side), :height($side),
             :fill-color(color Red), :fill, :$page; #, :gfx($page.gfx);
    =end comment

    $page.gfx.Restore;


} # sub simple-clip3(

our %eg-names is export = %(
    # only some have fancy names
    1 => "clipped-box",
    2 => "star-and-circle",
    3 => "plain-circle",
);

sub get-base-name(UInt $N --> Str) is export {
    my $base-name = "example" ~ $N.Str;
    # given an example number, determine a name
    if %eg-names{$N}:exists {
        my $s = %eg-names{$N};
        $base-name = "{$s}-example-$N";
    }
    $base-name
} # sub get-base-name(UInt $N --> Str)

# TODO
# from ps procs in file "boxtext.ps"
# /boxtext { % string location_code [integer: 0-11]
#  (to place--relative to the current point);
# 0 %               center of text bbox positioned at the current point
# 1 %  center of left edge of text bbox positioned at the current point
# 2 %    lower left corner of text bbox positioned at the current point
# 3 % center of lower edge of text bbox positioned at the current point
# 4 %   lower right corner of text bbox positioned at the current point
# 5 % center of right edge of text bbox positioned at the current point
# 6 %   upper right corner of text bbox positioned at the current point
# 7 % center of upper edge of text bbox positioned at the current point
# 8 %    upper left corner of text bbox positioned at the current point
# 9 % on base line (y of current point), left-justified on current point
#10 % on base line (y of current point), centered horizontally
#11 % on base line (y of current point), right-justified on current point

subset Loc of UInt where 0 <= $_ < 12;
sub put-text(
    # based on my PostScript function /puttext
    $x is copy, $y is copy,
    :$text!,
    :$page!,
    # defaults
    :$font-size = 12,
    :$fnt = "t", # key to %fonts, value is the loaded font
    # position of the enclosed text bbox in relation to the current point
Loc :$position = 0, #  where {0 <= $_ < 12},
    # optional constraints
    :$width,
    :$height,
    :$debug,
    ) is export {

    my $font = %fonts{$fnt};
    my PDF::Content::Text::Box $bbox;

    #==========================================
    # Determine text bbox size
    if $width and $height {
        # get constrained box size from PDF::Content
        #   define the applicable set of params affected
        if $position (cont) <0 1 2 3 4 5 6 7 8 9 10 11>.Set {
        }
        $bbox .= new: :$text, :$font, :$font-size, :$width, :$height;
    }
    elsif $height {
        # get constrained box size from PDF::Content
        #   define the applicable set of params affected
        if $position (cont) <0 1 2 3 4 5 6 7 8 9 10 11>.Set {
        }
        $bbox .= new: :$text, :$font, :$font-size, :$height;
    }
    elsif $width {
        # get constrained box size from PDF::Content
        #   define the applicable set of params affected
        if $position (cont) <0 1 2 3 4 5 6 7 8 9 10 11>.Set {
        }
        $bbox .= new: :$text, :$font, :$font-size, :$width;
    }
    else {
        # get natural box size from PDF::Content
        #   define the applicable set of params affected
        if $position (cont) <0 1 2 3 4 5 6 7 8 9 10 11>.Set {
        }
        $bbox .= new: :$text, :$font, :$font-size;
    }

    # Determine location of the text box based on calculated bbox above

    # query the bbox
    my $bwidth  = $bbox.content-width;
    my $bheight = $bbox.content-height;
    my $bllx;
    my $blly;

=begin comment
    $page.graphics: {
        my $tx = $cx;
        my $ty = $cy + ($height * 0.5) - $line1Y;
        .transform: :translate($tx, $ty); # where $x/$y is the desired reference point
        .FillColor = color White; #rgb(0, 0, 0); # color Black
        .font = %fonts<hb>, #.core-font('HelveticaBold'),
                 $line1size; # the size
        .print: $gb, :align<center>, :valign<center>;
    }
=end comment

} # sub label(

sub draw-cross-parts(
    :$x,
    :$y,
    :$width!,
    :$height!,
    :$thick  = 2,
    :$xdelta = 0,
    :$ydelta = 0,
    :$fill   = 1, :$fill-color   = (color White),
    :$stroke = 1, :$stroke-color = (color White),
    :$page!,
    :$debug,
    ) is export {

    my $g = $page.gfx;
    $g.Save;

    # move to x,y and draw the arms with thickness
    # horizontal arm
    my ($llx, $lly);
    $llx = ($x - 0.5*$width) + $xdelta;
    $lly = ($y - 0.5*$thick) + $ydelta;
    draw-rectangle-clip  :$llx, :$lly, :$width, :height($thick), :fill, :$page;

    # verical arm
    $llx = ($x - 0.5*$thick)  + $xdelta;
    $lly = ($y - 0.5*$height) + $ydelta;
    draw-rectangle-clip  :$llx, :$lly, :width($thick), :$height, :fill, :$page;

    $g.Restore;

} # sub draw-cross-parts(

sub place-image(
    $cx, $cy,
    :$image-path!,
    :$page!,
    :$method!,
    :$debug,
    ) is export {

    if not $image-path.IO.r {
        die "FATAL:  Image path '$image-path' cannot be opened.";
    }

    # TODO incorporate an XObject form on pages[0] for reuse. See how to
    # do that in file 'xt/1-xform.t'.
    #

    if $debug {
        note "DEBUG: using image path: $image-path";
    }

    if $method == 1 {
        my PDF::Content::XObject $image .= open: $image-path;
        my $w       = $image.width;
        my $h       = $image.height;
        my $hscaled = $h/30;
        my $wscaled = $w/30;
        my $g = $page.gfx: :trace;
        $g.Save;
        $g.do: $image, :position($cx, $cy), :width($wscaled), :height($hscaled),
                       :valign<center>, :align<center>;
        $g.Restore;
    }
    else {
        die "FATAL: Unable to handle method 2 yet.";
    }
} # sub place-image(

sub write-page-data(
    :$printer-name,
    :$cx!,
    :$cy!,
    :$side!,
    :$page!,
    :$debug,
    ) is export {
    #==========================================
    # gbumc text in the blue part
    $page.graphics: {
        .transform: :translate($cx, $cy); # where $x/$y is the desired reference point
        .transform: :rotate(deg2rad(90));
        .FillColor = color Black; #rgb(0, 0, 0); # color Black
        .font = %fonts<h>, #.core-font('HelveticaBold'),
                 9; # the size
        .print: "$printer-name ($side)", :align<center>, :valign<center>;
    }
} #  sub write-page-data(


sub make-printer-test-doc(
    $ofil,
    :$name!,            # printer name data
    :$media! is copy,
    :$debug,
) is export {

    if $media ~~ /^ :i L/ {
        $media = "Letter";
    }
    elsif $media ~~ /^ :i A4/ {
        $media = "A4";
    }
    else {
        die "FATAL: Unhandled media '$media', file an issue for a new media type";
    }

    my ($page);

    my $gp = PDF::GraphPaper.new: :$media, :margins(0);

    my $pdf  = PDF::Lite.new;
    $pdf.media-box = 0, 0, $gp.page-width, $gp.page-height;


    # front page
    $page = $pdf.add-page;
    make-printer-test-page $ofil, :$name, :$page, :obverse(1),
                           :graph-paper($gp), :$debug;

    # back page
    $page = $pdf.add-page;
    make-printer-test-page $ofil, :$name, :$page, :obverse(0),
                           :graph-paper($gp), :$debug;

    $pdf.save-as: $ofil;
    say "See printer test doc: $ofil";

} # sub make-printer-test-doc(

sub make-printer-test-page(
    $ofil,
    :$name!,
    :$graph-paper!,
    :$page!,
    :$obverse!, # front = 1, 0 = back
    :$debug,
) is export {

    my $p = $graph-paper;

    # text chunks to go on each page
    # make as constants later (better yet, constant hash
    # objects with font info, etc., in another module)

    my $para-width = $p.page-width * 0.6;

    # a reusable text box:  with filled text
    my PDF::Content::Text::Box $para .= new:
        :text(""),
        :font(%fonts<t>), :font-size(12), :kern, # <== note font information is rw
        :align<left>, :width($para-width);

    # a reusable text box:  lines preserved and text centered
    my PDF::Content::Text::Box $lines-centered .= new:
        :text(""),
        :font(%fonts<tb>), :font-size(15), :kern, # <== note font information is rw
        :verbatim, :squish,
        :align<center>, :width($para-width);

    # a reusable text box: lines preserved and text aligned left
    my PDF::Content::Text::Box $lines-left .= new:
        :text(""),
        :font(%fonts<tb>), :font-size(15), :kern, # <== note font information is rw
        :verbatim, :squish,
        :align<left>, :width($para-width);

    #=========================
    # Determine maximum horizontal grid squares for the media type
    # portrait orientation, and 0l margins.
    my $max-graph-width  = $p.page-width;
    my $max-graph-height = $p.page-height;

    my $max-ncells-x = ($max-graph-width / $p.cell-size).ceiling;
    my $max-ncells-y = ($max-graph-height / $p.cell-size).ceiling;

    my $ngrids-x = ($max-ncells-x / $p.cells-per-grid).ceiling;
    my $ngrids-y = ($max-ncells-y / $p.cells-per-grid).ceiling;

    my $graph-size-x = $ngrids-x * $p.cells-per-grid * $p.cell-size;
    my $graph-size-y = $ngrids-y * $p.cells-per-grid * $p.cell-size;

    my $ncells-x = $max-ncells-x; #$ngrids-x * $p.cells-per-grid;
    my $ncells-y = $max-ncells-y; #$ngrids-y * $p.cells-per-grid;

    my $ngrids = $ncells-x * $ncells-y;

    if $debug {
        say qq:to/HERE/;
        Given cells of size {$p.cell-size} x {$p.cell-size}, with margins of
          {$p.margins}, with grids of {$p.cells-per-grid} cells per grid
          = $ngrids total grid cells.
        HERE
    }

    # Translate to the lower-left corner of the grid area
    my $llx = 0;
    my $lly = $p.page-height - $graph-size-y;

    my $g = $page.gfx;
    $g.Save;

    $g.transform: :translate($llx, $lly);

    # draw horizontal lines, $y is varying 0 to $twidth
    #   bottom to top, x: 0, $graph-size-x
    for 0..$ncells-y -> $i {
        my $y = $i * $p.cell-size;
        if not $i mod 10 {
            $g.LineWidth = $p.grid-linewidth;
        }
        elsif not $i mod 5 {
            $g.LineWidth = $p.mid-grid-linewidth;
        }
        else {
            $g.LineWidth = $p.cell-linewidth;
        }
        $g.MoveTo: 0,             $y;
        $g.LineTo: $graph-size-x, $y;
        $g.Stroke;
    }

    # draw vertical lines, $x is varying 0 to $twidth
    #   left to right, y: 0, $graph-size-y
    for 0..$ncells-x -> $i {
        my $x = $i * $p.cell-size;
        if not $i mod 10 {
            $g.LineWidth = $p.grid-linewidth;
        }
        elsif not $i mod 5 {
            $g.LineWidth = $p.mid-grid-linewidth;
        }
        else {
            $g.LineWidth = $p.cell-linewidth;
        }
        $g.MoveTo: $x, 0;
        $g.LineTo: $x, $graph-size-y;
        $g.Stroke;
    }

    # now clip a rectangle inside
    # ll corner as origin is 1-inch in and up
    my ($dx, $dy, $cx-width, $cy-height);
    if $p.media ~~ /^ :i L/ {
        $dx = 72;
        $dy = 72;
        $cx-width  = 6.5 * 72;
        $cy-height = 9.0 * 72;
    }
    elsif $p.media ~~ /^ :i A/ {
        die "FATAL: Unable to handle A4 yet";
    }

    # clip it
    draw-rectangle-clip  :llx($dx), :lly($dy), :width($cx-width),
                         :height($cy-height), :clip, :$page;
    # draw it
    draw-rectangle-clip  :llx($dx), :lly($dy), :width($cx-width),
                         :height($cy-height), :fill, :$page;
    =begin comment
    # fill it with:
    #   printer info
    #   arrows and dimension info
    =end comment

    my %text-chunks = get-text-chunks :$name, :media($p.media);

    $lines-centered.text = %text-chunks<1><text>; # $howto;
    #     printer. etc.
    #     instructions
    $page.text: {
        my $lx = 0.5 * ($p.page-width); # - $lines-centered.content-width);
        .text-position = $lx, $p.page-height * 0.8;
        $lines-centered.render: $g;
    }

    # the instructions
    $lines-left.text = %text-chunks<3><text>; # $howto;
    $page.text: {
        my $lx = 0.5 * ($p.page-width - $lines-left.content-width);
        .text-position = $lx, $p.page-height * 0.64;
        $lines-left.render: $g;
    }

    my $otext;
    if $obverse == 1 {
        $otext = "Duplexer page front side (obverse)";
    }
    else {
        $otext = "Duplexer page back side (reverse)";
    }
    # print that as a text box 3/4 from bottom
    my $px = 0.5  * $p.page-width;
    my $py = 0.6 * $p.page-height;
    my $font = %fonts<tb>;
    my $font-size = 20;
    my @lines = $lines-left.text.lines;
    my $lw = 0; # linewidth

    # print the page position info centered but 100 points lower
    $py -= 100;
    $px = 0.5 * $p.page-width;
    $g.print: $otext, :kern, :position[$px, $py], :align<center>, :$font,
              :$font-size;

    # and the page number
    my ($page-number);
    if $obverse == 1 {
        $