use super::*;
use crate::hnc8::{C8PageFonts, write_c8_native_page};
use crate::pdf::{FontObject, ImageObject, OpenTypeFont};
use std::io::Write;
pub struct C8FontSources<'a, F> {
pub sources: &'a mut [C8FontSource<F>],
pub roles: C8PageFonts,
}
pub struct C8FontSource<F> {
pub source: F,
pub face: u32,
}
pub fn convert_c8_native_pdf<S, F, W, C>(
source: &mut S,
sink: &mut W,
fonts: C8FontSources<'_, F>,
table: Option<&QmTable>,
options: ComposeOptions,
limits: &Limits,
cancellation: &C,
) -> Result<ComposeReport>
where
S: RangedSource,
F: RangedSource,
W: Write,
C: Cancellation,
{
validate(limits)?;
let roles = fonts.roles;
let count = fonts.sources.len();
if !(1..=8).contains(&count)
|| [roles.cjk, roles.latin]
.into_iter()
.chain(roles.alternate_latin)
.chain(roles.decoration.map(|(index, _)| index))
.chain(roles.symbols)
.chain(roles.latin_state3)
.chain(roles.latin_state28)
.chain(roles.latin_state31)
.any(|index| index >= count)
{
return Err(At::NONE.error(
Hnc8Stage::Preflight,
Error::invalid("C8 font roles require 1..=8 explicit resources with valid indices"),
));
}
let mut input_bytes_read = 0;
let mut counted = CountingSource::new(source, &mut input_bytes_read);
let mut reader = Hnc8Reader::open(&mut counted, limits, cancellation)
.map_err(|error| container(error, Hnc8Stage::Container))?;
let header = reader.header();
let at = At {
variant: Some(header.variant),
offset: Some(0),
..At::NONE
};
if !matches!(header.variant, Variant::C8 | Variant::HnB) {
return Err(at.error(
Hnc8Stage::Preflight,
unsupported("native composition requires C8 or HN-B"),
));
}
let mut document =
PdfDocument::new(sink, limits, cancellation).map_err(at.locator(Hnc8Stage::Pdf))?;
let mut handles: Vec<FontObject> =
page_vector(count, limits, "C8 font handles").map_err(at.locator(Hnc8Stage::Preflight))?;
let mut font_bytes = 0u64;
for C8FontSource { source, face } in fonts.sources.iter_mut() {
let mut counted_font = CountingSource::new(source, &mut font_bytes);
let font = OpenTypeFont::read(&mut counted_font, *face, limits, cancellation)
.map_err(at.locator(Hnc8Stage::Preflight))?;
handles.push(
document
.add_font(&font)
.map_err(at.locator(Hnc8Stage::Pdf))?,
);
}
let references = [
&handles[roles.cjk],
&handles[roles.latin],
&handles[roles.alternate_latin.unwrap_or(roles.cjk)],
&handles[roles.decoration.map_or(roles.cjk, |(index, _)| index)],
&handles[roles.symbols.unwrap_or(roles.cjk)],
&handles[roles.latin_state3.unwrap_or(roles.cjk)],
&handles[roles.latin_state28.unwrap_or(roles.cjk)],
&handles[roles.latin_state31.unwrap_or(roles.cjk)],
];
let page_roles = C8PageFonts {
cjk: 0,
latin: 1,
alternate_latin: roles.alternate_latin.map(|_| 2),
decoration: roles.decoration.map(|(_, alias)| (3, alias)),
symbols: roles.symbols.map(|_| 4),
latin_state3: roles.latin_state3.map(|_| 5),
latin_state28: roles.latin_state28.map(|_| 6),
latin_state31: roles.latin_state31.map(|_| 7),
};
let mut report = ComposeReport::new(header);
report.outline.unverified = options.include_bookmarks;
let mut buffers = ImageBuffers::default();
while let Some(page) = reader
.next_page()
.map_err(|error| container(error, Hnc8Stage::Container))?
{
let at = At::page(header, page);
let count = admit_page_images(
page,
&[size_of::<ImageObject>(), size_of::<bool>()],
at,
limits,
)?;
let mut images = page_vector(count, limits, "native page image handles")
.map_err(at.locator(Hnc8Stage::Preflight))?;
let mut top_first = page_vector(count, limits, "native page image orientation")
.map_err(at.locator(Hnc8Stage::Preflight))?;
while let Some(record) = reader
.next_image()
.map_err(|error| container(error, Hnc8Stage::Container))?
{
let image_at = at.image(record);
let (checked, plan, visible_width, display_width, height) = preflight_image(
reader.source_mut(),
record,
image_at,
table,
options,
limits,
cancellation,
)?;
top_first.push(!matches!(checked, CheckedImage::Jpeg(_)));
let mut image = ComposedImage {
record,
visible_width,
display_width,
height,
checked,
transform: [0.0; 6],
duplicate_of: None,
type3_text_header_anomaly: None,
};
images.push(emit_image(
reader.source_mut(),
&mut document,
&mut image,
plan,
image_at,
&mut buffers,
table,
limits,
cancellation,
&mut report,
)?);
}
report.output_pages = write_c8_native_page(
&mut reader,
&mut document,
&references,
page_roles,
&images,
&top_first,
)
.map_err(|error| container(error, Hnc8Stage::Text))?
+ 1;
report.no_image_pages += u32::from(count == 0);
}
for (handle, C8FontSource { source, face }) in handles.iter().zip(fonts.sources.iter_mut()) {
let mut counted_font = CountingSource::new(source, &mut font_bytes);
let mut font = OpenTypeFont::read(&mut counted_font, *face, limits, cancellation)
.map_err(at.locator(Hnc8Stage::Pdf))?;
document
.embed_font(handle, &mut font)
.map_err(at.locator(Hnc8Stage::Pdf))?;
}
report.conversion = finish_document(&mut reader, document, &mut report, at)?;
report.conversion.input_bytes_read = input_bytes_read.saturating_add(font_bytes);
Ok(report)
}