///|
/// Decode and parse a list of content stream objects from this document.
///
/// Each input must resolve to a stream object. Stream filters are decoded before
/// parsing, then the decoded streams are concatenated with whitespace. Use
/// `parse_content_ops_with_resources` when inline-image length inference needs
/// page resources.
pub fn PdfDocument::parse_content_ops(
self : PdfDocument,
streams : ArrayView[@syntax.PdfObject],
) -> Array[@content.PdfContentOp] raise @core.PdfError {
let raw_streams = self.pdf_content_raw_streams(streams)
pdf_parse_content_ops_from_view(pdf_content_concat_streams(raw_streams))
}
///|
/// Decode and parse content stream objects with page resources.
///
/// The resource dictionary is used for inline-image color-space component
/// lookup when an unfiltered inline image has no reliable declared length.
pub fn PdfDocument::parse_content_ops_with_resources(
self : PdfDocument,
resources : @syntax.PdfObject,
streams : ArrayView[@syntax.PdfObject],
) -> Array[@content.PdfContentOp] raise @core.PdfError {
let raw_streams = self.pdf_content_raw_streams(streams)
pdf_parse_content_ops_from_view_with_context(
pdf_content_concat_streams(raw_streams),
pdf_content_parse_context(
Some(fn(colour_space) raise @core.PdfError {
self.colour_space_components(resources, colour_space)
}),
),
)
}
///|
/// Parse content bytes with page resources.
///
/// This is the owned-byte wrapper for `parse_content_view_with_resources`.
pub fn PdfDocument::parse_content_bytes_with_resources(
self : PdfDocument,
resources : @syntax.PdfObject,
data : @core.PdfBytes,
) -> Array[@content.PdfContentOp] raise @core.PdfError {
self.parse_content_view_with_resources(resources, data)
}
///|
/// Parse a content byte view with page resources.
///
/// The input view is read directly; returned operators own any byte data they
/// need to keep, such as text strings and inline-image data.
pub fn PdfDocument::parse_content_view_with_resources(
self : PdfDocument,
resources : @syntax.PdfObject,
data : BytesView,
) -> Array[@content.PdfContentOp] raise @core.PdfError {
pdf_parse_content_ops_from_view_with_context(
data,
pdf_content_parse_context(
Some(fn(colour_space) raise @core.PdfError {
self.colour_space_components(resources, colour_space)
}),
),
)
}
///|
fn PdfDocument::pdf_content_raw_streams(
self : PdfDocument,
streams : ArrayView[@syntax.PdfObject],
) -> Array[@core.PdfBytes] raise @core.PdfError {
let raw_streams : Array[@core.PdfBytes] = Array(capacity=streams.length())
for stream_ref in streams {
match self.direct(stream_ref) {
PdfStreamObject(stream) =>
raw_streams.push(
@syntax.pdf_stream_data_bytes(
self.pdf_decode_stream_value_direct(stream).data,
),
)
_ => raise ParseStreamExpected
}
}
raw_streams
}
///|
fn pdf_content_read_complex_object(
cursor : @core.ByteCursor,
first : @syntax.PdfLexeme,
) -> @syntax.PdfObject raise @core.PdfError {
let lexemes : Array[@syntax.PdfLexeme] = [first]
let mut array_depth = 0
let mut dict_depth = 0
match first {
LexLeftSquare => array_depth = 1
LexLeftDict => dict_depth = 1
_ => ()
}
while array_depth > 0 || dict_depth > 0 {
let token = @syntax.pdf_cursor_lex_token(cursor)
match token {
StopLexing | LexNone => raise ParseObjectExpected
LexLeftSquare => array_depth += 1
LexRightSquare => array_depth -= 1
LexLeftDict => dict_depth += 1
LexRightDict => dict_depth -= 1
_ => ()
}
lexemes.push(token)
}
@syntax.pdf_parse_single_lexeme_object(lexemes)
}
///|
fn pdf_content_read_object(
cursor : @core.ByteCursor,
) -> @syntax.PdfObject raise @core.PdfError {
let token = @syntax.pdf_cursor_lex_token(cursor)
match token {
StopLexing | LexNone => raise ParseObjectExpected
LexLeftSquare | LexLeftDict =>
pdf_content_read_complex_object(cursor, token)
_ => @syntax.pdf_parse_single_lexeme_object([token])
}
}
///|
fn pdf_content_operator_or_literal(view : BytesView) -> PdfContentLexeme {
match view {
[116, 114, 117, 101] => ContentOperand(PdfBoolean(true))
[102, 97, 108, 115, 101] => ContentOperand(PdfBoolean(false))
[110, 117, 108, 108] => ContentOperand(PdfNull)
_ => ContentOperator(view)
}
}
///|
fn pdf_content_lex_next(
cursor : @core.ByteCursor,
) -> PdfContentLexeme raise @core.PdfError {
@syntax.pdf_cursor_drop_whitespace(cursor)
let value = cursor.peek_byte()
match value {
value if value == @core.pdf_no_more => ContentEnd
37 => {
ignore(@syntax.pdf_cursor_lex_comment(cursor))
ContentComment
}
47 | 40 | 91 | 60 => ContentOperand(pdf_content_read_object(cursor))
value if @syntax.pdf_is_number_start_byte(value) =>
ContentOperand(pdf_content_read_object(cursor))
116 | 102 | 110 =>
pdf_content_operator_or_literal(
@syntax.pdf_cursor_read_regular_token_view(cursor),
)
_ => ContentOperator(@syntax.pdf_cursor_read_regular_token_view(cursor))
}
}