///|
/// PDF whitespace predicate.
pub fn is_whitespace(c : Char) -> Bool {
let v = c.to_int()
v == 0 || v == 9 || v == 10 || v == 12 || v == 13 || v == 32
}
///|
fn is_not_whitespace(c : Char) -> Bool {
!is_whitespace(c)
}
///|
pub fn is_newline(c : Char) -> Bool {
c is ('\n' | '\r')
}
///|
pub fn is_delimiter(c : Char) -> Bool {
c is ('(' | ')' | '<' | '>' | '[' | ']' | '{' | '}' | '%' | '/')
}
///|
pub fn is_whitespace_or_delimiter(c : Char) -> Bool {
is_whitespace(c) || is_delimiter(c)
}
///|
/// Read chars until whitespace or delimiter.
pub fn Input::getuntil_white_or_delimiter_string(self : Input) -> String raise {
self.getuntil_string(true, c => is_whitespace_or_delimiter(c))
}
///|
/// Read chars until predicate is true.
pub fn Input::getuntil(
self : Input,
eof_is_delimiter : Bool,
pred : (Char) -> Bool,
) -> Array[Char] raise {
let out = []
let mut done = false
while !done {
let b = (self.input_byte)()
if b == no_more {
if eof_is_delimiter {
done = true
} else {
fail("End_of_file")
}
} else {
let c = b.to_byte().to_char()
if pred(c) {
self.rewind()
done = true
} else {
out.push(c)
}
}
}
out
}
///|
/// Read chars until predicate is true, returning a string.
pub fn Input::getuntil_string(
self : Input,
eof_is_delimiter : Bool,
pred : (Char) -> Bool,
) -> String raise {
let sb = StringBuilder()
let mut done = false
while !done {
let b = (self.input_byte)()
if b == no_more {
if eof_is_delimiter {
done = true
} else {
fail("End_of_file")
}
} else {
let c = b.to_byte().to_char()
if pred(c) {
self.rewind()
done = true
} else {
sb.write_char(c)
}
}
}
sb.to_string()
}
///|
/// Ignore chars until predicate is true.
pub fn Input::ignoreuntil(
self : Input,
eof_is_delimiter : Bool,
pred : (Char) -> Bool,
) -> Unit raise {
let mut done = false
while !done {
let b = (self.input_byte)()
if b == no_more {
if eof_is_delimiter {
done = true
} else {
fail("End_of_file")
}
} else {
let c = b.to_byte().to_char()
if pred(c) {
self.rewind()
done = true
}
}
}
}
///|
/// Drop PDF whitespace.
pub fn Input::dropwhite(self : Input) -> Unit raise {
self.ignoreuntil(true, is_not_whitespace)
}
///|
/// Read a line from input (newline not included).
///
/// Unlike `Input::read_line`, this returns `""` at EOF instead of raising.
pub fn Input::input_line(self : Input) -> String {
let sb = StringBuilder()
let mut got_eol = false
let mut finished = false
while !finished {
let b = (self.input_byte)()
if b == no_more {
finished = true
} else {
let c = b.to_byte().to_char()
if is_newline(c) {
got_eol = true
} else if got_eol {
self.rewind()
finished = true
} else {
sb.write_char(c)
}
}
}
sb.to_string()
}
///|
/// Read back until predicate is true.
fn read_back_until(pred : (Char) -> Bool, input : Input) -> Unit raise {
match input.read_char_back() {
Some(ch) => if !pred(ch) { read_back_until(pred, input) }
None => fail("End_of_file")
}
}
///|
/// Go back one line.
pub fn Input::backline(self : Input) -> Unit raise {
read_back_until(is_newline, self)
read_back_until(c => !is_newline(c), self)
read_back_until(is_newline, self)
self.nudge()
}
///|
/// Read `count` characters from `input` without advancing the cursor.
pub fn Input::read_chunk(self : Input, count : Int) -> String raise {
let pos = (self.pos_in)()
let sb = StringBuilder()
let mut i = 0
while i < count {
match (self.input_char)() {
Some(ch) => sb.write_char(ch)
None => {
(self.seek_in)(pos)
fail("read_chunk")
}
}
i = i + 1
}
(self.seek_in)(pos)
sb.to_string()
}