Pinstripe/Printer

From Rosetta Code
Revision as of 20:00, 7 October 2021 by PureFox (talk | contribs) (Added Wren)
Task
Pinstripe/Printer
You are encouraged to solve this task according to the task description, using any language you may know.

The task is to demonstrate the creation of a series of 1 point wide vertical pinstripes with a sufficient number of pinstripes to span the entire width of the printed page (except for the last pinstripe). The pinstripes should alternate one point white, one point black. (Where the printer does not support producing graphics in terms of points, pixels may be substituted in this task.)

After the first inch of printing, we switch to a wider 2 point wide vertical pinstripe pattern. alternating two points white, two points black. We then switch to 3 points wide for the next inch, and then 4 points 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.

BBC BASIC

<lang bbcbasic> 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%}
     SYS "CreateSolidBrush", 0 TO brush%
     
     VDU 2,1,32,3
     pitch% = 2
     FOR y% = 0 TO dy% STEP dpi%
       FOR x% = 0 TO dx%-pitch% STEP pitch%
         rc.l% = x% : rc.r% = x% + pitch%/2
         rc.t% = y% : rc.b% = y% + dpi%
         SYS "FillRect", @prthdc%, rc{}, brush%
       NEXT
       pitch% += 2
     NEXT y%
     VDU 2,1,12,3</lang>

Go

Library: Go Graphics


The code for this task is basically the same as for 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. <lang go>package main

import (

   "github.com/fogleman/gg"
   "log"
   "os/exec"
   "runtime"

)

var palette = [2]string{

   "FFFFFF", // white
   "000000", // black    

}

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%2])
           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 := "w_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)
   }

}</lang>


Julia

Creates a png file of dimensions per printer DPI and width and sends it to the default printer using the system's print function. <lang julia>using FileIO, ImageIO

function getnumberwithprompt(prompt, t::Type)

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

end

dpi = getnumberwithprompt("Printer DPI (dots per inch)", Int) pwidth = getnumberwithprompt("Printer width (inches)", Float64) plength = 10.0

imgwidth, imgheight = Int(round(pwidth * dpi)), Int(round(plength * dpi))

img = zeros(UInt8, Int(round(imgheight)), Int(round(imgwidth)))

for row in 1:imgheight, col in 1:imgwidth

   stripewidth = div(row, dpi) + 1
   img[row, col] = rem(col, stripewidth * 2) < stripewidth ? 0 : 255

end

save("temp.png", img) run(`print temp.png`) # the run statement may need to be set up for the installed device </lang>

Liberty BASIC

Draws the pattern in a window onto a large graphic box, then dumps to the printer. <lang lb> nomainwin

'paperW = 8.5 ' for US letter paper 'paperH = 11

paperW = 8.2677165 ' for A4 paper paperH = 11.6929134

dpi= 300

prompt "Enter your printer DPI" +chr$( 13) + "(300 is OK for laser one, 360 likely for inkjet)"; dpi

w = paperW *dpi 'pixel size of paper h = paperH *dpi

graphicbox #main.gr, 0, 0, 300, 300 'picture could be bigger

open "Pinstripe/Printer" for window as #main

  1. main "trapclose [quit]"
  2. main.gr "autoresize" 'now we can maximize window with picture
  3. main.gr "down"
  4. main.gr "horizscrollbar on 0 "; w -300 'so we can scroll it
  5. main.gr "vertscrollbar on 0 "; h -300
  6. main.gr "place 0 0"
  7. main.gr "color white"
  8. main.gr "boxfilled "; w; " ";h
  9. main.gr "color black"
  10. main.gr "backcolor black"

for i = 0 to int( paperH)

   ww = i + 1
   yy =( i + 1) * dpi
   if yy > h then yy = h
   for x = ww to w step ww * 2   'start with white strip
       x1 = x + ww
       if x1 >= w then x1 = w
       #main.gr "place "; x; " "; i * dpi
       #main.gr "boxfilled "; x1; " "; yy
   next

next

  1. main.gr "flush"
  2. main.gr "print "; w

wait [quit] close #main end </lang>

Nim

Library: gintro

The code is almost the same as for Pinstripe/Display#Nim task. <lang Nim>import gintro/[glib, gobject, gtk, gio, cairo]

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

  1. ---------------------------------------------------------------------------------------------------

proc beginPrint(op: PrintOperation; printContext: PrintContext; data: pointer) =

 ## Process signal "begin_print", that is set the number of pages to print.
 op.setNPages(1)
  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 = 1 - colorIndex
     x += lineWidth
   y += lineHeight
  1. ---------------------------------------------------------------------------------------------------

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)
  1. ———————————————————————————————————————————————————————————————————————————————————————————————————

let app = newApplication(Application, "Rosetta.Pinstripe") discard app.connect("activate", activate) discard app.run()</lang>

Phix

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

PicoLisp

<lang PicoLisp>(load "@lib/ps.l")

(call 'lpr

  (pdf "pinstripes"
     (a4)  # 595 x 842 dots
     (for X 595
        (gray (if (bit? 1 X) 0 100)
           (vline X 0 842) ) )
     (page) ) )</lang>

Racket

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

  1. lang racket/gui

(define parts 4)

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

(define-values [W H] (send dc get-size)) (send dc set-pen "black" 0 'solid) (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) 2))])
   (send dc draw-rectangle X Y (add1 row) H*)))

(send* dc (end-page) (end-doc)) </lang>

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.

<lang perl6>unit sub MAIN ($dpi = 300, $size = 'letter');

my $filename = './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;

  1. Black. It's all black

my @color = (0,0,0),;

my $gap = floor $w % ($dpi * 8) / 2;

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

my $height = $dpi * .8;

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 = 8 * $dpi / (2 ** $row);
           for @colors -> $this {
               my $v = 0;
               while $x < ($dpi * 8) {
                   given Cairo::Context.new($image) -> $block {
                       $block.rectangle($x, $y, $width, $height);
                       $block.pattern($this);
                       $block.fill;
                       $block.destroy;
                   }
                   $x += $width * 2;
               }
           }
           $y += $height;
       }
   }
   $image.write_png($filename);

}

  1. Uncomment next line if you actually want to print it
  2. run('lp', $filename)</lang>

See Pinstripe-printer-perl6.png (offsite png image)

Tcl

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

Library: Tk

<lang tcl>package require Tk

  1. Allocate a temporary drawing surface

canvas .c

  1. 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;set c [expr {!$c}]} {

.c create rectangle $x $y [expr {$x+$dx+1}] [expr {$y+73}] \ -fill [lindex {black white} $c] -outline {}

   }

}

  1. Send postscript to default printer, scaled 1 pixel -> 1 point

exec lp - << [.c postscript -height $y -width $x -pageheight $y -pagewidth $x]

  1. Explicit exit; no GUI desired

exit</lang>

Wren

Translation of: Go
Library: DOME


As DOME doesn't have a method to print an image to a printer, we first need to create a small plug-in in C to add this functionality. <lang c>/* gcc -O3 -std=c11 -shared -o printer.so -fPIC -I./include printer.c */

  1. include <stdlib.h>
  2. include <string.h>
  3. include "dome.h"

static DOME_API_v0* core; static WREN_API_v0* wren;

static const char* source = "" "class Printer {\n"

 "foreign static printFile(name) \n"

"} \n";

void C_printFile(WrenVM* vm) {

   const char *arg = wren->getSlotString(vm, 1);
   char command[strlen(arg) + 4];
   strcpy(command, "lp ");
   strcat(command, arg);
   int res = system(command);

}

DOME_EXPORT DOME_Result PLUGIN_onInit(DOME_getAPIFunction DOME_getAPI, DOME_Context ctx) {

   core = DOME_getAPI(API_DOME, DOME_API_VERSION);
   wren = DOME_getAPI(API_WREN, WREN_API_VERSION);
   core->registerModule(ctx, "printer", source);
   core->registerClass(ctx, "printer", "Printer", NULL, NULL);
   core->registerFn(ctx, "printer", "static Printer.printFile(_)", C_printFile);
   return DOME_RESULT_SUCCESS;

}

DOME_EXPORT DOME_Result PLUGIN_preUpdate(DOME_Context ctx) {

   return DOME_RESULT_SUCCESS;

}

DOME_EXPORT DOME_Result PLUGIN_postUpdate(DOME_Context ctx) {

   return DOME_RESULT_SUCCESS;

}

DOME_EXPORT DOME_Result PLUGIN_preDraw(DOME_Context ctx) {

   return DOME_RESULT_SUCCESS;

}

DOME_EXPORT DOME_Result PLUGIN_postDraw(DOME_Context ctx) {

   return DOME_RESULT_SUCCESS;

}

DOME_EXPORT DOME_Result PLUGIN_onShutdown(DOME_Context ctx) {

   return DOME_RESULT_SUCCESS;

}</lang> This assumes that the dome.h header file is copied to an include sub-directory of the current one and that the resulting printer.so shared library file is created in the latter.

We can now use this plug-in in the following script which draws the image to the canvas, copies it to an ImageData object, saves it as a .png file and prints it to the default printer. <lang ecmascript>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 = "Pinstripe - printer"
       _width = 842
       _height = 595
       Canvas.resize(_width, _height)
       Window.resize(_width, _height)
       var colors = [
           Color.hex("FFFFFF"), // white
           Color.hex("000000")  // black
       ]
       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%2])
               x = x + b
               ci = ci + 1
           }
       }
   }
   init() {
       var img = ImageData.create("pinstripe", _width, _height)
       for (x in 0..._width) {
           for (y in 0..._height) img.pset(x, y, Canvas.pget(x, y))
       }
       img.saveToFile("pinstripe.png")
       Printer.printFile("pinstripe.png")
   }
   update() {}
   draw(alpha) {}

}

var Game = Main.new()</lang>