Skip to content

Commit

Permalink
Add ASA carriage control support for -printfile
Browse files Browse the repository at this point in the history
  • Loading branch information
racingmars committed Sep 15, 2022
1 parent 5b0731b commit c6aec14
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 2 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.4.1
0.5.0
8 changes: 8 additions & 0 deletions agent/README
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ This could be used to combine multiple text files into one PDF, for example:

`cat file1.txt file2.txt file3.txt | ./agent -printfile -`

If your local file uses ASA carriage control characters in the first byte of
each line, you may use the `-asa` flag in conjunction with `-printfile`. In
this case, page breaks ("1"), overstrike ("+"), and multiple line skipping
("0" and "-") will be controlled by the first character of the line in the
input file. Regular lines must start with a single space character (" ").
Handling of ASCII FF is disabled in -asa mode. The ASA characters 2–9, A, B,
and C are not supported (these typically position to vertical tab stops).

Acknowledgements
----------------

Expand Down
13 changes: 12 additions & 1 deletion agent/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ var configFile = flag.String("config", "config.yaml", "name of config file")
var output = flag.String("output", "default", "profile to use for -printfile")
var printFile = flag.String("printfile", "",
"print a single UTF-8 text file. Use filename \"-\" for stdin")
var useASA = flag.Bool("asa", false, "When using -printfile, file has ASA "+
"carriage control characters in first position of each line")
var trace = flag.Bool("trace", false, "enable trace logging")
var displayVersion = flag.Bool("version", false, "display version and quit")

Expand All @@ -58,6 +60,11 @@ func main() {
log.Printf("TRACE: trace logging enabled")
}

if *useASA && *printFile == "" {
log.Fatalf("FATAL: the -asa flag is only used with the -printFile " +
"parameter.")
}

// Load configuration file
inputs, outputs, err := loadConfig(*configFile)
if err != nil {
Expand Down Expand Up @@ -230,7 +237,11 @@ func runFilePrinter(output OutputConfig, filename string) {
output.Profile, "fileReader")
}

err = scanner.ScanUTF8Single(r, jobname, handler, *trace)
if *useASA {
err = scanner.ScanASAUTF8Single(r, jobname, handler, *trace)
} else {
err = scanner.ScanUTF8Single(r, jobname, handler, *trace)
}
if err != nil {
log.Fatalf("FATAL: %v", err)
}
Expand Down
131 changes: 131 additions & 0 deletions scanner/asascanner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// Copyright 2022 Matthew R. Wilson <[email protected]>
//
// This file is part of virtual1403
// <https://github.com/racingmars/virtual1403>.
//
// virtual1403 is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// virtual1403 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with virtual1403. If not, see <https://www.gnu.org/licenses/>.

package scanner

import (
"bufio"
"io"
"log"
"unicode/utf8"
)

// ScanASAUTF8Single reads input from a reader (typically local file) and
// prints the entire contents to the handler. No job separation is attempted.
// The input file is assumed to be UTF-8 (compatible with US-ASCII) encoded,
// with the first character of each line being an ASA carriage control
// instructions (' ', '1', '0', '-', and '+' are supported).
func ScanASAUTF8Single(r io.Reader, jobname string, handler PrinterHandler,
trace bool) error {

linenum := 0
var prevline string
scanner := bufio.NewScanner(r)

for scanner.Scan() {
linenum++
line := scanner.Text()
if len(line) == 0 {
// This is blank line that doesn't even include a carriage
// control character. Technically this is incorrect, but we'll
// be lenient and just treat it as a blank line with the regular
// " " carriage control character
line = " "
}
control, size := utf8.DecodeRuneInString(line)
if control == utf8.RuneError {
// If the user is providing an input file with an invalid UTF-8
// byte sequence in the first position of a line, I have serous
// doubts as to whether they really want ASA carriage control
// treatment overall and maybe they should reconsider their life
// choices (or at least reconsider their input file), but again,
// we'll be generous and just ignore it and try to carry on
// assuming this is a regular line.
log.Printf("ERROR: invalid UTF-8 byte sequence at beginning "+
"of line %d", linenum)
control = rune(' ')
}
rest := line[size:]

// If this is the first line, it may start with a "1" carriage control
// to advance the printer to the beginning of the page. We're already
// at the beginning of a new page, so we'll just prime prevLine with
// the text of the current line and ignore the 1. Regular " " control
// is also easy to handle here. '+' is meaningless since we don't have
// a previous line to overstrike.
//
// We will also allow for the case where the first instruction is to
// skip 1 or 2 lines.
if linenum == 1 {
switch control {
case ' ', '1', '+':
// no special handling
case '0':
handler.AddLine("", true)
case '-':
handler.AddLine("", true)
handler.AddLine("", true)
default:
log.Printf("ERROR: unknown/unimplemented control "+
"character '%s' on line %d", string(control), linenum)
}
prevline = rest
continue
}

// Before sending the previous line to the printer, we need to read
// this line's carriage control character so we know whether to tell
// the printer to perform a line feed after printing the previous
// line or if we'll overstrike.
switch control {
case ' ':
handler.AddLine(prevline, true)
case '1':
handler.AddLine(prevline, true)
handler.PageBreak()
case '0':
handler.AddLine(prevline, true)
handler.AddLine("", true)
case '-':
handler.AddLine(prevline, true)
handler.AddLine("", true)
handler.AddLine("", true)
case '+':
handler.AddLine(prevline, false)
default:
handler.AddLine(prevline, true)
log.Printf("ERROR: unknown/unimplemented control "+
"character '%s' on line %d", string(control), linenum)
}

// The line we just scanned becomes the new previous line for the next
// iteration through the loop.
prevline = rest
}

if err := scanner.Err(); err != nil {
return err
}

// We always need to finish by writing the last line in the prevline
// buffer
handler.AddLine(prevline, true)
handler.EndOfJob(jobname)

return nil
}

0 comments on commit c6aec14

Please sign in to comment.