/pliant/graphic/filter/escp2.pli
 
 1  # Copyright  Hubert Tonneau  hubert.tonneau@pliant.cx 
 2  # 
 3  # Writing this driver was possible only thanks to the great work done by 
 4  # Robert L Krawitz to gather and document detailed informations about 
 5  # Epson inkjet printers. 
 6  # 
 7  # Things have now changed since Epson is publishing new inkjets printers 
 8  # programming documentation 
 9   
 10   
 11  module "/pliant/language/compiler.pli" 
 12  module "/pliant/language/context.pli" 
 13  module "/pliant/language/stream.pli" 
 14  module "/pliant/math/functions.pli" 
 15  module "prototype.pli" 
 16  module "/pliant/graphic/color/gamut.pli" 
 17  module "/pliant/util/encoding/packbits.pli" 
 18  module "/pliant/graphic/image/pixmap.pli" 
 19  module "/pliant/graphic/color/adjust.pli" 
 20  module "/pliant/graphic/color/spectrum.pli" # defines 'exposure' function 
 21  module "/pliant/graphic/misc/dither.pli" 
 22   
 23  constant packbits true 
 24  constant heads_count 12 
 25  constant top_enhancement true 
 26  constant verbose false 
 27   
 28  gvar Link:Function escp2_generator 
 29   
 30   
 31   
 32 
 
 33  # driving the printer 
 34   
 35   
 36  function generator_prototype x y head level f -> dot 
 37    arg Int head level ; arg Function f ; arg Int dot 
 38    indirect 
 39   
 40  type Escp2Level 
 41    field uInt16 dot0 dot1 
 42    field uInt32 remain 
 43   
 44  type Escp2Lut 
 45    field Int light_threshold 
 46    field Int light_removal 
 47    field Int small 
 48    field Int big_threshold 
 49   
 50  type Escp2Channel 
 51    field (Array Escp2Level 256) level 
 52    field (Array Escp2Lut 256) lut 
 53    field DitherMatrix dither 
 54   
 55  type ImageWriteFilterEscp2 
 56    field Pointer:Stream stream 
 57    field Array:Address input_line 
 58    field Int dim 
 59    field Int size_x size_y 
 60    field Int input_y input_line_size 
 61    field Int dpi_x dpi_y space_x space_y heads jets dot_size bpc hq_dpi dot_levels big_dot 
 62    field Array:Int shift 
 63    field Array:Int pass ; field Int delta 
 64    field CBool old coord4 job_ticket 
 65    field Str command options 
 66    field CBool advanced 
 67    field Address output_lines ; field Int output_line_size 
 68    field Int output_y 
 69    field Int left top 
 70    field Address buffer 
 71    field (Array Escp2Channel heads_count) head 
 72    if top_enhancement 
 73      field (Array List:Str) top_lines 
 74   
 75  ImageWriteFilter maybe ImageWriteFilterEscp2 
 76   
 77   
 78  constant escape character:27 
 79   
 80  function num1 i -> s 
 81    arg Int i ; arg Str s 
 82    := "1" ; s:characters map uInt8 := i 
 83   
 84  function num2 i -> s 
 85    arg Int i ; arg Str s 
 86    := "12" ; s:characters map uInt16_li := i 
 87   
 88  function num4 i -> s 
 89    arg Int i ; arg Str s 
 90    := "1234" ; s:characters map uInt32_li := i 
 91   
 92  function num2_or_4 coord4 i -> s 
 93    arg CBool coord4 ; arg Int i ; arg Str s 
 94    if coord4 
 95      := num4 i 
 96    else 
 97      := num2 i 
 98   
 99  function escp2 command parameters -> s 
 100    arg Str command parameters s 
 101    := escape+"("+command+(num2 parameters:len)+parameters 
 102   
 103  function remote command parameters -> s 
 104    arg Str command parameters s 
 105    := command+(num2 parameters:len)+parameters 
 106   
 107  method f open s options h -> status 
 108    arg_rw ImageWriteFilterEscp2 f ; arg_rw Stream s ; arg Str options ; arg ImagePrototype h ; arg ExtendedStatus status 
 109    var Int dpi_x dpi_y base_unit page_unit 
 110    var Int heads jets dot_size bpc dot_levels space_x space_y 
 111    var Int dpi_shift ; var Array:Int shift 
 112    var Float page_x := undefined ; var Float page_y := undefined 
 113    var Str command 
 114    dpi_x := options option "escp2_dpi_x" Int (cast h:size_x/(abs h:x1-h:x0)*25.4 Int) 
 115    dpi_y := options option "escp2_dpi_y" Int (cast h:size_y/(abs h:y1-h:y0)*25.4 Int) 
 116    if (dpi_x%360<>or dpi_y%360<>0) and not (options option "escp2_force_resolution") 
 117      return (failure "Incorrect resolution "+string:dpi_x+" x "+string:dpi_y+" dpi") 
 118    base_unit := options option "escp2_base_unit" Int (max (max dpi_x dpi_y) 1440) 
 119    page_unit := options option "escp2_page_unit" Int (max (max dpi_x dpi_y) 720) 
 120    var Str model := options option "model" Str 
 121    if model="" 
 122      return failure:"You must specify the printer model" 
 123    if model="Epson C80" or model="Epson C82" 
 124      heads := 0Fh 
 125      jets := shunt h:gamut:dimension=1 180 60 
 126      dot_size := 12h 
 127      if h:gamut:dimension<>1 
 128        dpi_shift := 180 ; shift += 120 ; shift += 60 ; shift += 0 ; shift += 120 
 129      space_x := dpi_x\360 
 130      space_y := dpi_y\180 
 131      page_x := 210 
 132      page_y := 297 
 133      command := "" 
 134    eif model="Epson 750" 
 135      heads := 30Fh 
 136      jets := 48 
 137      dot_size := 10h 
 138      space_x := dpi_x\360 
 139      space_y := dpi_y\120 
 140      page_x := 210 
 141      page_y := 297 
 142      command := "" 
 143    eif model="Epson 1280" or model="Epson 1290" 
 144      heads := 30Fh 
 145      jets := 48 
 146      dot_size := 10h 
 147      space_x := dpi_x\360 
 148      space_y := dpi_y\120 
 149      page_x := 13*25.4 
 150      page_y := 19*25.4 
 151      command := "coord4 zero_margin roll" 
 152    eif model="Epson 2100" or model="Epson 2200" 
 153      heads := 0B0Fh 
 154      jets := 96 
 155      dot_size := 10h 
 156      if h:gamut:dimension<>1 
 157        dpi_shift := 360 ; shift += 0 ; shift += 0 ; shift += 0 ; shift += 0 ; shift += 0 ; shift += 0 ; shift += 0 ; shift += 0 ; shift += 1 ; shift += 1 ; shift += 0 ; shift += 1 
 158      space_x := dpi_x\360 
 159      space_y := dpi_y\180 
 160      page_x := 13*25.4 
 161      page_y := 19*25.4 
 162      command := "coord4 zero_margin roll co_cutter" 
 163    eif model="Epson 3000" 
 164      heads := 0Fh 
 165      jets := shunt h:gamut:dimension=1 128 64 
 166      dot_size := 1 
 167      space_x := dpi_x\360 
 168      space_y := dpi_y\180 
 169      page_x := 17*25.4 
 170      page_y := 44*25.4 
 171      command := "old" 
 172    eif model="Epson 4000" 
 173      heads := 0B0Fh 
 174      jets := 1 
 175      dot_size := 12h 
 176      space_x := 1 
 177      space_y := 1 
 178      page_x := 17*25.4 
 179      page_y := 44*25.4 
 180      command := "coord4 roll ac_cutter" 
 181    eif model="Epson 7600" or model="Epson 9600" or model="Epson 10600" 
 182      heads := shunt model="Epson 10600" 030Fh 0B0Fh 
 183      jets := 1 
 184      dot_size := 10h 
 185      space_x := 1 
 186      space_y := 1 
 187      command := "coord4 roll ac_cutter" 
 188    eif model="Epson R800" 
 189      if h:gamut:dimension<>1 
 190        dpi_shift := 360 ; shift += 0 ; shift += 1 ; shift += 0 ; shift += 0 ; shift += 1 ; shift += 0 
 191      heads := 7Fh 
 192      jets := 180 
 193      dot_size := 13h 
 194      space_x := dpi_x\720 
 195      space_y := dpi_y\180 
 196      page_x := 210 
 197      page_y := 297 
 198      command := "coord4 zero_margin roll co_cutter matte_black" 
 199    else 
 200      return (failure "'"+model+"' is not a valid printer model") 
 201    command := options option "escp2_command" Str command 
 202    var CBool old := command option "old" 
 203    var CBool coord4 := command option "coord4" 
 204    var CBool job_ticket := shunt (options option "escp2_job_ticket"true (options option "escp2_no_job_ticket"false not old 
 205    heads := options option "escp2_heads" Int heads 
 206    jets := options option "escp2_jets" Int jets 
 207    dot_size := options option "escp2_dot_size" Int dot_size 
 208    space_x := options option "escp2_space_x" Int space_x 
 209    space_y := options option "escp2_space_y" Int space_y 
 210    bpc := options option "escp2_bpc" Int (shunt old 1 2) 
 211    dot_levels := options option "escp2_dot_levels" Int (shunt dpi_x>=1440 or dpi_y>=1440 1 2^bpc-1) 
 212    var CBool roll := options option "roll" 
 213    if not ((options (options option_position "margin" 0) options:len) parse word:"margin" (var Float margin_left) (var Float margin_top) (var Float margin_right) (var Float margin_bottom) any) 
 214      margin_right := 0 ; margin_bottom := 0 
 215      if not ((options (options option_position "offset" 0) options:len) parse word:"offset" (var Float margin_left) (var Float margin_top) any) 
 216        margin_left := 0 ; margin_top := 0 
 217    if not ((options (options option_position "page" options:len) options:len) parse word:"page" (var Float page_x) (var Float page_y) any) 
 218      if roll or page_x=undefined or page_y=undefined 
 219        page_x := margin_left+(abs h:x1-h:x0)+margin_right 
 220        page_y := margin_top+(abs h:y1-h:y0)+margin_bottom 
 221    margin_right := page_x-(abs h:x1-h:x0)-margin_left 
 222    margin_bottom := page_y-(abs h:y1-h:y0)-margin_top 
 223    if job_ticket and not (options option "esp2_no_ejl") 
 224      writechars "[0][0][0]" 
 225      writechars escape+character:1+"@EJL 1284.4[lf]@EJL     [lf]" 
 226      writechars escape+"@" 
 227    writechars escape+"@" 
 228    if job_ticket 
 229      writechars (escp2 "R" "[0]REMOTE1") 
 230      if (options option "escp2_paper") 
 231        writechars (remote "SN" "[0][0]"+character:(options option "escp2_paper" Int 0)) # paper 0=default, 1=plain, 3=glossy photo, 5=plain (fast load), 6=heavyweight matte, 7=coated, 8=photo 
 232      if (options option "escp2_load") 
 233        writechars (remote "SN" "[0]"+character:2+character:(options option "escp2_load" Int 0)) # 0=normal, 1=fast, 2=slow 
 234      if (command option "duplex"and ( (options option "front"or (options option "back")) 
 235        writechars (remote "SN" "[0]"+character:7+character:(shunt (options option "front") 1 2)) 
 236      if (command option "zero_margin") 
 237        writechars (remote "FP" "[0]"+character:B0h+character:0FFh) # zero margin 
 238      if (command option "ac_cutter") 
 239        writechars (remote "AC" "[0]"+character:(shunt (options option "cutter") 1 0)) # cutter 
 240      if (command option "co_cutter"and (options option "cutter") 
 241        var Int cutter_unit := shunt page_unit=360 0 page_unit=720 1 page_unit=1440 2 (cast undefined Int) 
 242        if cutter_unit=defined 
 243          if (options option "double_cut") 
 244            writechars (remote "CO" "[0][0]"+character:1+character:cutter_unit+"[0][0][0][0]") 
 245          writechars (remote "CO" "[0][0]"+character:0+character:cutter_unit+num4:(cast page_y*page_unit/25.4 Int)) 
 246      if (options option "escp2_dry") 
 247        writechars (remote "DR" "[0][0]"+num2:(cast (options option "escp_dry" Float 0)*1000 Int)) # dry time between scan lines 
 248      if (options option "escp2_pause") 
 249        writechars (remote "DR" "[0]"+character:1+num2:(cast (options option "escp_dry" Float 0) Int)) # dry time between page 
 250      if (options option "escp2_ink") 
 251        writechars (remote "IK" "[0]"+character:(options option "escp2_ink" Int 0)) 
 252      if (command option "roll") 
 253        writechars (remote "EX"  "[0][0][0][0]"+character:5+character:(shunt roll 1 0)) # roll 
 254      if (options option "escp2_thickness") 
 255        writechars (remote "PH" "[0]"+character:(cast (options option "escp2_thickness" Float 0)*10 Int)) 
 256      writechars escape+"[0][0][0]" 
 257    writechars (escp2 "G" character:1) # graphic mode 
 258    if not old 
 259      writechars (escp2 "U" (num1 base_unit\page_unit)+(num1 base_unit\dpi_y)+(num1 base_unit\dpi_x)+num2:base_unit) 
 260    else 
 261      writechars (escp2 "U" (num1 3600\dpi_y)) 
 262    writechars (escp2 "K" "[0]"+character:2) # color mode 
 263    writechars (escp2 "i" character:(options option "escp2_microweave" Int 0)+(shunt (options option "escp2_microweave2"character:(options option "escp2_microweave2" Int 0)+character:(options option "escp2_microweave3" Int 0) "")) # microweave 
 264    writechars escape+"U"+character:(shunt (options option "unidirectional") 1 0) # unidirectional 
 265    writechars (escp2 "e" "[0]"+character:dot_size) # set dots size 
 266    if not old 
 267      writechars (escp2 "D" num2:14400+(num1 14400\dpi_y*space_y)+(num1 14400\dpi_x*space_x)) # dots spacing 
 268    if not (options option "escp2_no_page") 
 269      writechars (escp2 "C" (num2_or_4 coord4 (cast page_y*page_unit/25.4 Int))) # page length 
 270      # desable vertical margin in order to allow vertical units to start right at the top of the page 
 271      writechars (escp2 "c" (num2_or_4 coord4 0)+(num2_or_4 coord4 (cast page_y*page_unit/25.4 Int))) # vertical margin 
 272    if (options option "escp2_sheet"and ((options (options option_position "sheet" options:len) options:len) parse word:"sheet" (var Float sheet_x) (var Float sheet_y) any) 
 273      writechars (escp2 "S" num4:(cast sheet_x*page_unit/25.4 Int)+num4:(cast sheet_y*page_unit/25.4 Int)) # sheet size 
 274    stream :> s 
 275    f:input_line size := size_y 
 276    for (var Int y) f:input_line:size-1 
 277      f:input_line := null 
 278    dim := h:gamut dimension 
 279    size_x := size_x 
 280    size_y := size_y 
 281    input_y := 0 ; input_line_size := line_size 
 282    dpi_x := dpi_x 
 283    dpi_y := dpi_y 
 284    space_x := space_x 
 285    space_y := space_y 
 286    heads := heads 
 287    jets := jets 
 288    dot_size := dot_size 
 289    dot_levels := dot_levels 
 290    bpc := bpc 
 291    f:shift size := heads_count 
 292    var Int sm := 0 
 293    for (var Int i) 0 heads_count-1 
 294      if i<shift:size 
 295        f:shift := shift:i*dpi_y\dpi_shift 
 296        sm := max sm f:shift:i 
 297      else 
 298        f:shift := 0 
 299    delta := jets*space_y+sm 
 300    old := old 
 301    coord4 := coord4 
 302    job_ticket := job_ticket 
 303    command := command ; options := options 
 304    advanced := options option "escp2_advanced" 
 305    f:pass size := h:size_y+2*f:delta 
 306    for (var Int y) f:pass:size-1 
 307      f:pass := 0 
 308    output_line_size := (h:size_x\space_x*bpc+7)\8 
 309    output_lines := memory_allocate f:output_line_size*jets addressof:f 
 310    output_y := -(delta) 
 311    left := cast margin_left*dpi_x/25.4 Int 
 312    if top_enhancement 
 313      top := cast margin_top*dpi_y/25.4 Int 
 314    else 
 315      f top := max (cast margin_top*dpi_y/25.4 Int) f:delta 
 316    buffer := memory_allocate 2*f:output_line_size*jets+addressof:f 
 317    for (var Int i) 0 heads_count-1 
 318      if (heads .and. 2^i)<>0 
 319        var Pointer:Escp2Channel ch :> f:head i 
 320        if i<8 
 321          ch dither := dither_matrix "dpi_x "+string:dpi_x+" dpi_y "+string:dpi_y 
 322        var CBool light_available := (heads .and. 2^(i%8+8))<>0 
 323        var Int nb := f:head:(i%8):dither:size_x*f:head:(i%8):dither:size_y 
 324        if not f:advanced 
 325          var Float light_gain := options option "escp2_light_gain"+(string i%8) Float (options option "escp2_light_gain" Float 0.25) 
 326          var Float light_removal_start := options option "escp2_light_removal_start"+(string i%8) Float (options option "escp2_light_removal_start" Float 0.0625) 
 327          var Float light_removal_power := options option "escp2_light_removal_power"+(string i%8) Float (options option "escp2_light_removal_power" Float 1.25) 
 328          var Float big_start := options option "escp2_big_start"+(string i%8) Float (options option "escp2_big_start" Float 0.5) 
 329          var Float big_power := options option "escp2_big_power"+(string i%8) Float (options option "escp2_big_power" Float 2) 
 330          var CBool oops := false 
 331          for (var Int j) 0 255 
 332            var Pointer:Escp2Lut ll :> ch:lut j 
 333            var Float := j/255 
 334            := dot_adjust "header [dq]escp2_head"+(string i%8)+"_[dq] header2 [dq]escp2_[dq] "+options 
 335            ll light_threshold := shunt not light_available 0 (cast (exposure light_gain)*nb Int) 
 336            ll light_removal := shunt d<=light_removal_start 0 (cast ((d-light_removal_start)/(1-light_removal_start))^light_removal_power*nb Int) 
 337            ll small := cast d*nb Int 
 338            ll big_threshold := shunt dot_levels=1 0 d<=big_start 0 (cast ((d-big_start)/(1-big_start))^big_power*nb Int) 
 339            if ll:light_threshold>and ll:light_removal<ll:big_threshold 
 340              oops := true 
 341          if oops 
 342            console "Light/big point selection conflict in ESCP2 driver" eol 
 343        else 
 344          var Str bend := string (options option "escp2_bend"+(string i%8) Float (options option "escp2_bend" Float -0.1)) 
 345          var Str ident := shunt (options option "escp2_dot"+(string i%8)) "escp2_dot"+(string i%8) "escp2_dot" 
 346          var Str opt := shunt (options option ident) options bpc="escp2_dot -1 0.5 "+bend+" escp2_dot 1 1 "+bend "escp2_dot -1 0.333 "+bend+" escp2_dot 1 0.667 "+bend+" escp2_dot 3 1 "+bend 
 347          var Array:Int dot ; var Array:Float dot_density ; var Array:Float dot_bend 
 348          dot size := 0 ; dot_density size := 0 ; dot_bend size := 0 
 349          dot += 0 ; dot_density += 0 ; dot_bend += 0 
 350          var Int := 0 
 351          while (opt option_position ident -1)>=0 
 352            if not ((opt (opt option_position ident opt:len) opt:len) parse word:ident (var Int dot0) (var Float density0) (var Float bend0) any) 
 353              return (failure "Icorrect '"+ident+"' parameter") 
 354            if light_available or dot0>=0 
 355              dot += shunt i<and dot0>dot0 i>=and dot0<-dot0 0 ; dot_density += density0 ; dot_bend += bend0 
 356            += 1 
 357          for (var Int j) 0 255 
 358            var Float := j/255 
 359            := dot_adjust "header [dq]escp2_head"+(string i%8)+"_[dq] header2 [dq]escp2_[dq] "+options 
 360            var Int := 0 
 361            while k+2<dot_density:size and d>=(dot_density k+1) 
 362              += 1 
 363            var Pointer:Escp2Level :> ch:level j 
 364            dot0 := dot k 
 365            dot1 := dot k+1 
 366            remain := cast (exposure (d-dot_density:k)/(dot_density:(k+1)-dot_density:k) dot_bend:(k+1))*nb Int 
 367    status := success 
 368   
 369   
 370  method f writeline adr -> status 
 371    arg_rw ImageWriteFilterEscp2 f ; arg Address adr ; arg Status status 
 372    var Pointer:Stream :> stream 
 373    implicit f 
 374   
 375      # record the newly provided line 
 376      input_line input_y := memory_allocate input_line_size addressof:f 
 377      memory_copy adr input_line:input_y input_line_size 
 378      input_y += 1 
 379   
 380      while output_y<size_y 
 381        if (pass output_y+delta)=2^space_x-1 
 382          output_y -= 1 
 383        while (pass output_y+delta)=2^space_x-1 
 384          output_y += 1 
 385        var Int := 0 
 386        while ((pass output_y+delta) .and. 2^p)<>0 
 387          += 1 
 388        # 'first_line' and 'lines_count' is specifying the limits of the pass we are planing to send 
 389        for (var Int h) 0 heads_count-1 
 390          for (var Int j) jets-1 
 391            var Int := output_y+shift:h+j*space_y 
 392            if y>=and y<size_y and input_line:y=null 
 393              # one input line is missing: wait until the input data is received 
 394              return success 
 395        if verbose 
 396          console "escp2 line " output_y "/" size_y "   [cr]" 
 397        # everything is fine for the pass: let's send it to the printer 
 398        var CBool vpos := true 
 399        for (var Int h) 0 heads_count-1 
 400          var Int first_line := output_y+shift:h 
 401          var Int lines_count := min (size_y-first_line+space_y-1)\space_y jets 
 402          if (heads .and. 2^h)<>and lines_count>and (shunt dim=h%8=h%8<dim) 
 403            memory_clear f:output_lines output_line_size*lines_count ; var CBool some := false 
 404            for (var Int j) lines_count-1 
 405              var Int := first_line+j*space_y 
 406              if y>=0 
 407                var Address src := input_line:translate uInt8 p*dim+(shunt dim=1 0 h%8) 
 408                var Int src_step := space_x*dim 
 409                if exists:escp2_generator 
 410                  for (var Int x) size_x-1 step space_x 
 411                    var Int := src map uInt8 
 412                    var Int := generator_prototype escp2_generator 
 413                    if d<>0 
 414                      output_lines map uInt8 j*output_line_size+(x\space_x)*bpc\+= d*2^(8-bpc-(x\space_x)*bpc%8) 
 415                      some := true 
 416                    src := src translate uInt8 src_step 
 417                eif not f:advanced 
 418                  var Address dest := output_lines translate uInt8 j*output_line_size+(p\space_x)*bpc\8 
 419                  var Int shift := 8-bpc-(p\space_x)*bpc%8 
 420                  var Int shift_step := bpc 
 421                  var Pointer:(Array Escp2Lut 256) lut :> f:head:h:lut 
 422                  var Pointer:DitherMatrix dither_matrix :> f:head:(h%8):dither 
 423                  var Address dither := addressof (dither_matrix y%dither_matrix:size_y) 
 424                  var Int modulus := dither_matrix size_x 
 425                  if false # unoptimised version 
 426                    for (var Int x) p size_x-1 step space_x 
 427                      var Int l := src map uInt8 
 428                      # handle pixel 'x' 'y' on head 'h' , which level is 'l' 
 429                      if l<>0 
 430                        var Int d # 'd' will be the dot size 
 431                        var Pointer:Escp2Lut ll :> lut l   
 432                        var Int t := dither map Int x%modulus 
 433                        var CBool light := ll:light_threshold>=t and not ll:light_removal>=t 
 434                        var CBool big := ll:big_threshold>=t 
 435                        var CBool small := ll:small>=t and not light and not big 
 436                        if h<8 
 437                          d := shunt big 3 small 1 0 
 438                        else 
 439                          d := shunt light 1 0 
 440                        if d<>0 
 441                          dest map uInt8 += d*2^shift 
 442                          some := true 
 443                      src := src translate uInt8 src_step 
 444                      if shift>0 
 445                        shift -= shift_step 
 446                      else 
 447                        dest := dest translate uInt8 1 ; shift += 8-shift_step 
 448                  eif h<and (heads .and. 2^(h+8))=0 
 449                    for (var Int x) size_x-1 step space_x 
 450                      var Int := src map uInt8 
 451                      if l<>0 
 452                        var Pointer:Escp2Lut ll :> lut l   
 453                        var Int := dither map Int x%modulus 
 454                        if ll:big_threshold>=t 
 455                          dest map uInt8 += f:dot_levels*2^shift 
 456                          some := true 
 457                        eif ll:small>=t 
 458                          dest map uInt8 += 2^shift 
 459                          some := true 
 460                      src := src translate uInt8 src_step 
 461                      if shift>0 
 462                        shift -= shift_step 
 463                      else 
 464                        dest := dest translate uInt8 1 ; shift += 8-shift_step 
 465                  eif h<8 
 466                    for (var Int x) size_x-1 step space_x 
 467                      var Int := src map uInt8 
 468                      if l<>0 
 469                        var Pointer:Escp2Lut ll :> lut l   
 470                        var Int := dither map Int x%modulus 
 471                        if ll:big_threshold>=t 
 472                          dest map uInt8 += f:dot_levels*2^shift 
 473                          some := true 
 474                        eif ll:small>=and not (ll:light_threshold>=and not ll:light_removal>=t) 
 475                          dest map uInt8 += 2^shift 
 476                          some := true 
 477                      src := src translate uInt8 src_step 
 478                      if shift>0 
 479                        shift -= shift_step 
 480                      else 
 481                        dest := dest translate uInt8 1 ; shift += 8-shift_step 
 482                  else # h>=8 
 483                    for (var Int x) size_x-1 step space_x 
 484                      var Int := src map uInt8 
 485                      if l<>0 
 486                        var Pointer:Escp2Lut ll :> lut l   
 487                        var Int := dither map Int x%modulus 
 488                        if ll:light_threshold>=and not ll:light_removal>=t 
 489                          dest map uInt8 += 2^shift 
 490                          some := true 
 491                      src := src translate uInt8 src_step 
 492                      if shift>0 
 493                        shift -= shift_step 
 494                      else 
 495                        dest := dest translate uInt8 1 ; shift += 8-shift_step 
 496                else # advanced 
 497                  var Address dest := output_lines translate uInt8 j*output_line_size+(p\space_x)*bpc\8 
 498                  var Int shift := 8-bpc-(p\space_x)*bpc%8 
 499                  var Int shift_step := bpc 
 500                  var Pointer:(Array Escp2Level 256) levels :> f:head:h:level 
 501                  var Pointer:DitherMatrix dither_matrix :> f:head:(h%8):dither 
 502                  var Address dither := addressof (dither_matrix y%dither_matrix:size_y) 
 503                  var Int modulus := dither_matrix size_x 
 504                  for (var Int x) size_x-1 step space_x 
 505                    var Int := src map uInt8 
 506                    if l<>0 
 507                      var Pointer:Escp2Level level :> levels l   
 508                      var Int := shunt level:remain>=(dither map Int x%modulus) level:dot1 level:dot0 
 509                      if d<>0 
 510                        dest map uInt8 += d*2^shift 
 511                        some := true 
 512                    src := src translate uInt8 src_step 
 513                    if shift>0 
 514                      shift -= shift_step 
 515                    else 
 516                      dest := dest translate uInt8 1 ; shift += 8-shift_step 
 517            var Address lines_buffer := output_lines 
 518            if top_enhancement 
 519              var Int delta_y := 0 
 520              while top+output_y+delta_y*space_y<0 
 521                delta_y += 1 
 522              lines_buffer := lines_buffer translate Byte delta_y*output_line_size 
 523              lines_count -= delta_y 
 524            if lines_count>and some 
 525              if (options option "escp2_fill"and lines_count<jets 
 526                memory_move lines_buffer output_lines lines_count*output_line_size 
 527                lines_buffer := output_lines 
 528                memory_clear (lines_buffer translate Byte lines_count*output_line_size) (jets-lines_count)*output_line_size 
 529                lines_count := jets 
 530              var Str cmd := "" 
 531              if vpos 
 532                if top_enhancement 
 533                  cmd += escp2 "V" (num2_or_4 coord4 top+output_y+delta_y*space_y) 
 534                else 
 535                  cmd += escp2 "V" (num2_or_4 coord4 top+output_y) 
 536                vpos := false 
 537              if coord4 
 538                cmd += escp2 "$" (num4 left+p) 
 539              else 
 540                cmd += escape+"$"+(num2 left+p) 
 541              if not f:old 
 542                cmd += escape+"i"+character:(shunt h=0 2 h=1 1 h=2 4 h=3 (shunt (command option "matte_black") (shunt (options option "escp2_matte") 0 40h) 0) h<h+h=8 18 h=9 17 h=10 20 h=11 16 0)+character:(shunt packbits 1 0)+character:bpc+num2:output_line_size+num2:lines_count 
 543              else 
 544                cmd += escape+"r"+character:(shunt h=0 2 h=1 1 h=2 4 h=3 0 h=8 18 h=9 17 h=10 20 h=11 16 0) 
 545                cmd += escape+"."+character:(shunt packbits 1 0)+(num1 3600\(dpi_y\space_y))+(num1 3600\(dpi_x\space_x))+num1:lines_count+(num2 output_line_size*8\bpc) 
 546              var Address data ; var Int length 
 547              if packbits 
 548                data := buffer 
 549                length := packbits_encode lines_buffer buffer lines_count*output_line_size 
 550              else 
 551                data := lines_buffer 
 552                length := lines_count*output_line_size 
 553              if top_enhancement and delta_y<>0 
 554                var Int := top+output_y+delta_y*space_y 
 555                if i>=top_lines:size 
 556                  top_lines size := i+1 
 557                top_lines += cmd 
 558                (var Str lbuf) set data length false 
 559                top_lines += lbuf 
 560                top_lines += "[cr]" 
 561              else 
 562                if top_enhancement and top_lines:size>0 
 563                  for (var Int i) 0 (min top+output_y top_lines:size-1) 
 564                    if (exists top_lines:i:first) 
 565                      var Pointer:Str ptr :> top_lines:first 
 566                      while exists:ptr 
 567                        writechars ptr 
 568                        ptr :> top_lines:next ptr 
 569                      top_lines := var List:Str empty_list 
 570                  if top+output_y>=top_lines:size 
 571                    top_lines size := 0 
 572                writechars cmd 
 573                raw_write data length 
 574                writechars "[cr]" 
 575        # ajust the number of pass already done for each line, then drop the input lines we will not use anymore 
 576        for (var Int j) jets-1 
 577          var Int := output_y+j*space_y 
 578          check ((pass y+delta) .and. 2^p)=0 
 579          pass y+delta += 2^p 
 580        for (var Int y) output_y-output_y+jets\space_x-# -2 instead of -1 because output_y may later be decreased by one 
 581          if y>=and y<size_y 
 582            memory_free f:input_line:y 
 583            f:input_line := null 
 584        output_y := output_y+jets\space_x 
 585    status := success 
 586   
 587   
 588  method f close -> status 
 589    arg_rw ImageWriteFilterEscp2 f ; arg ExtendedStatus status 
 590    # send printer termination commands 
 591    implicit f 
 592      for (var Int y) size_y-1 
 593        check (pass y+delta)=2^space_x-1 
 594        if f:input_line:y<>null 
 595          memory_free f:input_line:y 
 596    var Pointer:Stream :> stream 
 597    writechars character:0Ch 
 598    writechars escape+"@" 
 599    if f:job_ticket 
 600      writechars (escp2 "R" "[0]REMOTE1") 
 601      if (f:options option "escp2_je") 
 602        writechars (remote "JE" "[0]"# job end 
 603      writechars (remote "LD" "") 
 604      writechars escape+"[0][0][0]" 
 605    memory_free f:output_lines 
 606    memory_free f:buffer 
 607    if verbose 
 608      console (repeat 60 " ")+"[cr]" 
 609    status := success 
 610   
 611   
 612  image_record_filters ".escp2" Void false ImageWriteFilterEscp2 false 
 613  export escp2_generator