Colour pinstripe/Printer

From Rosetta Code
Task
Colour pinstripe/Printer
You are encouraged to solve this task according to the task description, using any language you may know.

The task is to create 1 point wide colour vertical pinstripes with a sufficient number of pinstripes to span the entire width of the colour graphics printer. The pinstripes should alternate between each individual cartridge ink and ink pair and black and white pinstripes should be included. A typical pinstripe sequence woud be black, red, green, blue, magenta, cyan, yellow, white.

After the first inch of printing, we switch to a wider 2 pixel wide vertical pinstripe pattern. and to 3 point wide vertical for the next inch, and then 4 point wide, etc. This trend continues for the entire length of the page (or for 12 inches of run length in the case of a printer using continuous roll stationery). After printing the test pattern the page is ejected (or the test pattern is rolled clear of the printer enclosure, in the case of continuous roll printers).

Note that it is an acceptable solution to use the smallest marks that the language provides, rather than working at native printer resolution, where this is not achievable from within the language.

Optionally, on systems where the printer resolution cannot be determined, it is permissible to prompt the user for printer resolution, and to calculate point size based on user input, enabling fractional point sizes to be used.

Ada

Library: APDF
with Ada.Text_IO;

with PDF_Out;

procedure Color_Pinstripe_Printer
is
   use PDF_Out;

   package Point_IO
   is new Ada.Text_Io.Float_IO (Real);

   procedure Pinstripe (Doc          : in out Pdf_Out_File;
                        Line_Width   : Real;
                        Line_Height  : Real;
                        Screen_Width : Real;
                        Y            : Real)
   is
      type Color_Range is (Blck, Red, Green, Blue, Magenta, Cyan, Yellow, White);
      Colors : constant array (Color_Range) of Color_Type
        := (Blck    => (0.0, 0.0, 0.0), Red   => (1.0, 0.0, 0.0),
            Green   => (0.0, 1.0, 0.0), Blue  => (0.0, 0.0, 1.0),
            Magenta => (1.0, 0.0, 1.0), Cyan  => (0.0, 1.0, 1.0),
            Yellow  => (1.0, 1.0, 0.0), White => (1.0, 1.0, 1.0));
      Col : Color_Range := Color_Range'First;

      Count  : constant Natural
        := Natural (Real'Floor (Screen_Width / Line_Width));
      Corner      : constant Point := (Doc.Left_Margin, Doc.Bottom_Margin);
      Corner_Box  : constant Point := Corner     + (10.0, 10.0);
      Corner_Text : constant Point := Corner_Box + (10.0, 10.0);
      Light_Gray  : constant Color_Type := (0.9, 0.9, 0.9);
      Image : String (1 .. 4);
   begin
      --  Pinstripes
      for A in 0 .. Count loop
         Doc.Color (Colors (Col));
         Doc.Draw (What => Corner +
                     Rectangle'(X_Min  => Real (A) * Line_Width,
                                Y_Min  => Y,
                                Width  => Line_Width,
                                Height => Line_Height),
                   Rendering => Fill);
         Col := (if Col = Color_Range'Last
                 then Color_Range'First
                 else Color_Range'Succ (Col));
      end loop;

      --  Box
      Doc.Stroking_Color (Black);
      Doc.Color (Light_Gray);
      Doc.Line_Width (3.0);
      Doc.Draw (What => Corner_Box + (0.0, Y, 150.0, 26.0),
                Rendering => Fill_Then_Stroke);
      --  Text
      Doc.Color (Black);
      Doc.Text_Rendering_Mode (Fill);
      Point_Io.Put (Image, Line_Width, Aft => 1, Exp => 0);
      Doc.Put_XY (Corner_Text.X, Corner_Text.Y + Y,
                  Image & " point color pinstripe");
   end Pinstripe;

   Doc : PDF_Out_File;
begin
   Doc.Create ("color-pinstripe.pdf");
   Doc.Page_Setup (A4_Portrait);
   Doc.Margins (Margins_Type'(Left   => Cm_2_5,
                              others => One_cm));
   declare
      Width  : constant Real
        := A4_Portrait.Width - Doc.Left_Margin - Doc.Right_Margin;
      Height : constant Real
        := A4_Portrait.Height - Doc.Top_Margin - Doc.Bottom_Margin;
   begin
      for Point in 1 .. 11 loop
         Pinstripe (Doc,
                    Line_Width   => Real (Point),
                    Line_Height  => One_Inch,
                    Screen_Width => Width,
                    Y            => Height - Real (Point) * One_Inch);
      end loop;
   end;
   Doc.Close;
end Color_Pinstripe_Printer;

BBC BASIC

This program first displays a Print Dialogue so the printer can be selected.

      PD_RETURNDC = 256
      _LOGPIXELSY = 90
      
      DIM pd{lStructSize%, hwndOwner%, hDevMode%, hDevNames%, \
      \      hdc%, flags%, nFromPage{l&,h&}, nToPage{l&,h&}, \
      \      nMinPage{l&,h&}, nMaxPage{l&,h&}, nCopies{l&,h&}, \
      \      hInstance%, lCustData%, lpfnPrintHook%, lpfnSetupHook%, \
      \      lpPrintTemplateName%, lpSetupTemplateName%, \
      \      hPrintTemplate%, hSetupTemplate%}
      pd.lStructSize% = DIM(pd{})
      pd.hwndOwner% = @hwnd%
      pd.flags% = PD_RETURNDC
      
      SYS "PrintDlg", pd{} TO ok%
      IF ok%=0 THEN QUIT
      SYS "DeleteDC", @prthdc%
      @prthdc% = pd.hdc%
      *MARGINS 0,0,0,0
      
      dx% = @vdu%!236-@vdu%!232
      dy% = @vdu%!244-@vdu%!240
      SYS "GetDeviceCaps", @prthdc%, _LOGPIXELSY TO dpi%
      
      DIM rc{l%,t%,r%,b%}
      DIM colour%(7)
      colour%() = &000000, &0000FF, &00FF00, &FF0000, \
      \           &FF00FF, &FFFF00, &00FFFF, &FFFFFF
      
      VDU 2,1,32,3
      pitch% = 1
      FOR y% = 0 TO dy% STEP dpi%
        col% = 0
        FOR x% = 0 TO dx%-pitch% STEP pitch%
          rc.l% = x% : rc.r% = x% + pitch%
          rc.t% = y% : rc.b% = y% + dpi%
          SYS "CreateSolidBrush", colour%(col% MOD 8) TO brush%
          SYS "FillRect", @prthdc%, rc{}, brush%
          SYS "DeleteObject", brush%
          col% += 1
        NEXT
        pitch% += 1
      NEXT y%
      VDU 2,1,12,3


FreeBASIC

Dim As String exename
#ifdef __FB_WIN32__
    exename = "mspaint.exe /pt"
#endif
#ifdef __FB_LINUX__
    exename = "lp -o media=A4 "
#endif

Dim As Uinteger ps, col, h, w, x, y1, y2

' (A4) # 595 x 842 dots
w = 842 : h = 595
' create display size window, 8bit color (palette), no frame
Screenres w, h, 8,, 8

h \= 7 : y2 = h -1

For ps = 1 To 7
    col = 0
    For x = 0 To (w - ps -1) Step ps
        Line (x, y1) - (x + ps -1, y2), col, bf
        col = (col +1) And 255
    Next x
    y1 += h : y2 += h
Next ps

Dim As String filename = "color_pinstripe.bmp"
If Bsave(filename, 0) <> 0 Then 
    Cls: Print "Error saving: "; fileName : Sleep
Else
    Dim As Integer result = Exec(exename, filename)
    If result = -1 Then Print "Error running "; exename : Sleep
End If
End


FutureBasic

include "NSLog.incl"

void local fn BuildWindow
  CGRect     r = { 0, 0, 612, 791 } // U.S. Letter Portrait
  long       i, j, colorCount, bars = 612
  CGFloat    y = 0.0, w = 1
  CFArrayRef colors = @[¬
  fn ColorBlack,¬
  fn ColorRed,¬
  fn ColorGreen,¬
  fn ColorBlue,¬
  fn ColorMagenta,¬
  fn ColorCyan,¬
  fn ColorYellow,¬
  fn ColorWhite]
  
  window 1, @"Color Pinstripe Printer Page [U.S. Letter Portrait]", r
  
  pen -1
  for j = 1 to 4
    r = fn CGRectMake( 0, y, w, 197.75 )
    colorCount = 0
    for i = 0 to bars - 1
      rect fill r, colors[colorCount]
      r.origin.x += w
      colorCount++
      if colorCount == 8 then colorCount = 0
    next
    bars = bars << 1
    y += 197.75 : w++
  next
end fn

fn BuildWindow

HandleEvents
Output:


Go

Library: Go Graphics


The code for this task is basically the same as for Colour_pinstripe/Display#Go except that the drawing parameters have been tweaked to produce 1 inch bands when printing on A4 paper and some code has been added to dump the image to the default printer.

package main
 
import (
    "github.com/fogleman/gg"
    "log"
    "os/exec"
    "runtime"
)

var palette = [8]string{
    "000000", // black
    "FF0000", // red
    "00FF00", // green
    "0000FF", // blue
    "FF00FF", // magenta
    "00FFFF", // cyan
    "FFFF00", // yellow
    "FFFFFF", // white
}
 
func pinstripe(dc *gg.Context) {
    w := dc.Width()
    h := dc.Height() / 7
    for b := 1; b <= 11; b++ {
        for x, ci := 0, 0; x < w; x, ci = x+b, ci+1 {
            dc.SetHexColor(palette[ci%8])
            y := h * (b - 1)
            dc.DrawRectangle(float64(x), float64(y), float64(b), float64(h))
            dc.Fill()
        }
    }
}
 
func main() {
    dc := gg.NewContext(842, 595)
    pinstripe(dc)
    fileName := "color_pinstripe.png"
    dc.SavePNG(fileName)
    var cmd *exec.Cmd
    if runtime.GOOS == "windows" {
        cmd = exec.Command("mspaint", "/pt", fileName)
    } else {
        cmd = exec.Command("lp", fileName)
    }
    if err := cmd.Run(); err != nil {
        log.Fatal(err)
    }   
}


Julia

using Colors, FileIO

const colors = [colorant"black", colorant"red", colorant"green", colorant"blue",
                colorant"magenta", colorant"cyan", colorant"yellow", colorant"white"]

function getnumberwithprompt(prompt, t::Type)
    s = ""
    while (x = tryparse(t, s)) == nothing
        print("\n", prompt, ": -> ")
        s = strip(readline())
    end
    return x
end

function colorstripepng(filename)
    dpi = getnumberwithprompt("Printer DPI (dots per inch)", Int)
    pwidth, plength = getnumberwithprompt("Printer width (inches)", Float64), 10
    imgwidth, imgheight = Int(round(pwidth * dpi)), plength * dpi
    img = fill(colorant"black", imgheight, imgwidth)

    for row in 1:imgheight
        stripenum, stripewidth, colorindex = 1, div(row, dpi) + 1, 1
        for col in 1:imgwidth
            img[row, col] = colors[colorindex]
            if (stripenum += 1) % stripewidth == 0
                colorindex = mod1(colorindex + 1, length(colors))
            end
        end
    end
    save(filename, img)
end

colorstripepng("temp.png")
run(`print temp.png`)  # the run statement may need to be set up for the installed device

Nim

Library: gintro

The code for drawing is the same that in the task Colour_pinstripe/Display but the context is different. We have chosen to display a dialog to give the user a way to choose the destination (which may be a printer or a file). Instead of the “draw” signal, we have to process the “begin_print” signal to set the number of pages and the “draw_page” signal to render the page.

import gintro/[glib, gobject, gtk, gio, cairo]

const Colors = [[0.0, 0.0, 0.0], [255.0, 0.0, 0.0],
                [0.0, 255.0, 0.0], [0.0, 0.0, 255.0],
                [255.0, 0.0, 255.0], [0.0, 255.0, 255.0],
                [255.0, 255.0, 0.0], [255.0, 255.0, 255.0]]

#---------------------------------------------------------------------------------------------------

proc beginPrint(op: PrintOperation; printContext: PrintContext; data: pointer) =
  ## Process signal "begin_print", that is set the number of pages to print.
  op.setNPages(1)

#---------------------------------------------------------------------------------------------------

proc drawPage(op: PrintOperation; printContext: PrintContext; pageNum: int; data: pointer) =
  ## Draw a page.

  let context = printContext.getCairoContext()
  let lineHeight = printContext.height / 4

  var y = 0.0
  for lineWidth in [1.0, 2.0, 3.0, 4.0]:
    context.setLineWidth(lineWidth)
    var x = 0.0
    var colorIndex = 0
    while x < printContext.width:
      context.setSource(Colors[colorIndex])
      context.moveTo(x, y)
      context.lineTo(x, y + lineHeight)
      context.stroke()
      colorIndex = (colorIndex + 1) mod Colors.len
      x += lineWidth
    y += lineHeight

#---------------------------------------------------------------------------------------------------

proc activate(app: Application) =
  ## Activate the application.

  # Launch a print operation.
  let op = newPrintOperation()
  op.connect("begin_print", beginPrint, pointer(nil))
  op.connect("draw_page", drawPage, pointer(nil))

  # Run the print dialog.
  discard op.run(printDialog)

#———————————————————————————————————————————————————————————————————————————————————————————————————

let app = newApplication(Application, "Rosetta.ColorPinstripe")
discard app.connect("activate", activate)
discard app.run()

Phix

See the print_cb function of Colour_pinstripe/Display#Phix and the final comments of that entry.

PicoLisp

(load "@lib/ps.l")

# Using circular lists for an endless supply of colors
#      (black  red  green blue magenta cyan yellow white)
(setq
   Red   (0    100    0     0    100    0    100   100 .)
   Green (0     0    100    0     0    100   100   100 .)
   Blue  (0     0     0    100   100   100    0    100 .) )

(call 'lpr
   (pdf "pinstripes"
      (a4)  # 595 x 842 dots
      (let (I 0  Step 1)
         (for X 595
            (color (car Red) (car Green) (car Blue)
               (vline X 0 842) )
            (when (= Step (inc 'I))
               (zero I)
               (pop 'Red)
               (pop 'Green)
               (pop 'Blue) )
            (when (=0 (% X 72))  # 1 inch
               (zero I)
               (inc 'Step) ) ) )
      (page) ) )

Python

from turtle import *
from PIL import Image
import time
import subprocess

"""

Only works on Windows. Assumes that you have Ghostscript
installed and in your path.

https://www.ghostscript.com/download/gsdnld.html

Hard coded to 100 pixels per inch.

"""

colors = ["black", "red", "green", "blue", "magenta", "cyan", "yellow", "white"]

screen = getscreen()

# width and height in pixels
# aspect ratio for 11 by 8.5 paper

inch_width = 11.0
inch_height = 8.5

pixels_per_inch = 100

pix_width = int(inch_width*pixels_per_inch)
pix_height = int(inch_height*pixels_per_inch)

screen.setup (width=pix_width, height=pix_height, startx=0, starty=0)

screen.screensize(pix_width,pix_height)

# center is 0,0

# get coordinates of the edges

left_edge = -screen.window_width()//2

right_edge = screen.window_width()//2

bottom_edge = -screen.window_height()//2

top_edge = screen.window_height()//2

# draw quickly

screen.delay(0)
screen.tracer(5)

for inch in range(int(inch_width)-1):
    line_width = inch + 1
    pensize(line_width)
    colornum = 0

    min_x = left_edge + (inch * pixels_per_inch)
    max_x = left_edge + ((inch+1) * pixels_per_inch)
    
    for y in range(bottom_edge,top_edge,line_width):
        penup()
        pencolor(colors[colornum])
        colornum = (colornum + 1) % len(colors)
        setposition(min_x,y)
        pendown()
        setposition(max_x,y)
         
screen.getcanvas().postscript(file="striped.eps")

# convert to jpeg
# won't work without Ghostscript.

im = Image.open("striped.eps")
im.save("striped.jpg")

# Got idea from http://rosettacode.org/wiki/Colour_pinstripe/Printer#Go
    
subprocess.run(["mspaint", "/pt", "striped.jpg"])

Racket

The drawing code is exactly the same code as Colour_pinstripe/Display#Racket, only drawing onto a printer device context now.

#lang racket/gui

(define parts 4)

(define dc (new printer-dc%))
(send* dc (start-doc "Colour Pinstripe") (start-page))

(define-values [W H] (send dc get-size))
(define parts 4)
(define colors
  '("Black" "Red" "Green" "Blue" "Magenta" "Cyan" "Yellow" "White"))
(send dc set-pen "black" 0 'transparent)
(send dc set-brush "black" 'solid)
(define H* (round (/ H parts)))
(for ([row parts])
  (define Y (* row H*))
  (for ([X (in-range 0 W (add1 row))] [c (in-cycle colors)])
    (send dc set-brush c 'solid)
    (send dc draw-rectangle X Y (add1 row) H*)))

(send* dc (end-page) (end-doc))

Raku

(formerly Perl 6)

Works with: Rakudo version 2020.01

Note that Raku does not attempt to be a printer driver. This example allows users to specify the dpi and paper size, then generates an image and passes it to the default printer. Defaults to 300 dpi and US letter paper.

unit sub MAIN ($dpi = 300, $size = 'letter');

my $filename = './Color-pinstripe-printer-perl6.png';

my %paper = (
    'letter' => { :width(8.5),    :height(11.0) }
    'A4'     => { :width(8.2677), :height(11.6929)}
);

my ($w, $h) = %paper{$size}<width height> »*» $dpi;

# ROYGBIVK
my @color = (1,0,0),(1,.598,0),(1,1,0),(0,1,0),(0,0,1),(.294,0,.51),(.58,0,.827),(0,0,0);

my $gap = floor $w % ($dpi * +@color) / 2;

my $rows = (1, * * 2 … * > $dpi).elems;

my $height = $dpi;

use Cairo;

my @colors = @color.map: { Cairo::Pattern::Solid.new.create(|$_) };

given Cairo::Image.create(Cairo::FORMAT_ARGB32, $w, $h) -> $image {
    given Cairo::Context.new($image) {
        my Cairo::Pattern::Solid $bg .= create(1,1,1);
        .rectangle(0, 0, $w, $h);
        .pattern($bg);
        .fill;
        $bg.destroy;

        my $y = $gap;
        for ^$rows -> $row {
            my $x = $gap;
            my $width = $dpi / (2 ** $row);
            for @colors -> $this {
                my $v = 0;
                while $v++ < (2 ** ($row - 1)) {
                    given Cairo::Context.new($image) -> $block {
                        $block.rectangle($x, $y, $width, $height);
                        $block.pattern($this);
                        $block.fill;
                        $block.destroy;
                    }
                    $x += $width;
                    $x += $width if $row;
                }
            }
        $y += $height;
        }
    }
    $image.write_png($filename);
}

# Uncomment next line if you actually want to print it
#run('lp', $filename)

See Color-pinstripe-printer-perl6.png (offsite png image)

Tcl

This code assumes that the page's printable area is 8.5"×11".

Library: Tk
package require Tk
# Allocate a temporary drawing surface
canvas .c
# The cycle of colors we want to use
set colors {black red green blue magenta cyan yellow white}
# Draw the output we want 
for {set y 0;set dx 1} {$y < 11*72} {incr y 72;incr dx} {
    for {set x 0;set c 0} {$x < 8.5*72} {incr x $dx;incr c} {
	.c create rectangle $x $y [expr {$x+$dx+1}] [expr {$y+73}] \
	    -fill [lindex $colors [expr {$c%[llength $colors]}]] -outline {}
    }
}
# Send postscript to default printer, scaled 1 pixel -> 1 point
exec lp - << [.c postscript -height $y -width $x -pageheight $y -pagewidth $x]
# Explicit exit; no GUI desired
exit

Wren

Translation of: Go
Library: DOME


This reuses the plug-in from the Pinstripe/Printer#Wren task to enable DOME to print to the default printer.

import "graphics" for Canvas, Color, ImageData
import "dome" for Window
import "plugin" for Plugin

Plugin.load("printer")

import "printer" for Printer

class Main {
    construct new() {
        Window.title = "Color pinstripe - printer"
        _width = 842
        _height = 595
        Canvas.resize(_width, _height)
        Window.resize(_width, _height)
        var colors = [
            Color.hex("000000"), // black
            Color.hex("FF0000"), // red
            Color.hex("00FF00"), // green
            Color.hex("0000FF"), // blue
            Color.hex("FF00FF"), // magenta
            Color.hex("00FFFF"), // cyan
            Color.hex("FFFF00"), // yellow
            Color.hex("FFFFFF")  // white
        ]
        pinstripe(colors)
    }

    pinstripe(colors) {
        var w = _width
        var h = (_height/7).floor
        for (b in 1..11) {
            var x = 0
            var ci = 0
            while (x < w) {
                var y = h * (b - 1)
                Canvas.rectfill(x, y, b, h, colors[ci%8])
                x = x + b
                ci = ci + 1
            }
        }
    }

    init() {
        var img = ImageData.create("color_pinstripe", _width, _height)
        for (x in 0..._width) {
            for (y in 0..._height) img.pset(x, y, Canvas.pget(x, y))
        }
        img.saveToFile("color_pinstripe.png")
        Printer.printFile("color_pinstripe.png")
    }

    update() {}

    draw(alpha) {}
}

var Game = Main.new()