use std::io::{Cursor, Write};
use std::num::NonZeroU16;
use super::*;
use crate::color::SeparationInfo;
use crate::object::TextStrLike;
pub struct Catalog<'a> {
dict: Dict<'a>,
}
writer!(Catalog: |obj| {
let mut dict = obj.dict();
dict.pair(Name(b"Type"), Name(b"Catalog"));
Self { dict }
});
impl Catalog<'_> {
pub fn pages(&mut self, id: Ref) -> &mut Self {
self.pair(Name(b"Pages"), id);
self
}
pub fn page_layout(&mut self, layout: PageLayout) -> &mut Self {
self.pair(Name(b"PageLayout"), layout.to_name());
self
}
pub fn page_labels(&mut self) -> NumberTree<'_, Ref> {
self.insert(Name(b"PageLabels")).start()
}
pub fn page_mode(&mut self, mode: PageMode) -> &mut Self {
self.pair(Name(b"PageMode"), mode.to_name());
self
}
pub fn viewer_preferences(&mut self) -> ViewerPreferences<'_> {
self.insert(Name(b"ViewerPreferences")).start()
}
pub fn names(&mut self) -> Names<'_> {
self.insert(Name(b"Names")).start()
}
pub fn destinations(&mut self, id: Ref) -> &mut Self {
self.pair(Name(b"Dests"), id);
self
}
pub fn outlines(&mut self, id: Ref) -> &mut Self {
self.pair(Name(b"Outlines"), id);
self
}
pub fn struct_tree_root(&mut self) -> StructTreeRoot<'_> {
self.insert(Name(b"StructTreeRoot")).start()
}
pub fn mark_info(&mut self) -> MarkInfo<'_> {
self.insert(Name(b"MarkInfo")).start()
}
pub fn lang(&mut self, lang: TextStr) -> &mut Self {
self.pair(Name(b"Lang"), lang);
self
}
pub fn version(&mut self, major: u8, minor: u8) -> &mut Self {
self.pair(Name(b"Version"), Name(format!("{major}.{minor}").as_bytes()));
self
}
pub fn additional_actions(&mut self) -> AdditionalActions<'_> {
self.insert(Name(b"AA")).start()
}
pub fn form(&mut self) -> Form<'_> {
self.insert(Name(b"AcroForm")).start()
}
pub fn metadata(&mut self, id: Ref) -> &mut Self {
self.pair(Name(b"Metadata"), id);
self
}
pub fn extensions(&mut self) -> TypedDict<'_, DeveloperExtension<'_>> {
self.insert(Name(b"Extensions")).dict().typed()
}
pub fn separation_info(&mut self) -> SeparationInfo<'_> {
self.insert(Name(b"SeparationInfo")).start()
}
pub fn output_intents(&mut self) -> TypedArray<'_, OutputIntent<'_>> {
self.insert(Name(b"OutputIntents")).array().typed()
}
pub fn associated_files(&mut self) -> TypedArray<'_, FileSpec<'_>> {
self.insert(Name(b"AF")).array().typed()
}
}
deref!('a, Catalog<'a> => Dict<'a>, dict);
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
pub enum PageLayout {
#[default]
SinglePage,
OneColumn,
TwoColumnLeft,
TwoColumnRight,
TwoPageLeft,
TwoPageRight,
}
impl PageLayout {
pub(crate) fn to_name(self) -> Name<'static> {
match self {
Self::SinglePage => Name(b"SinglePage"),
Self::OneColumn => Name(b"OneColumn"),
Self::TwoColumnLeft => Name(b"TwoColumnLeft"),
Self::TwoColumnRight => Name(b"TwoColumnRight"),
Self::TwoPageLeft => Name(b"TwoPageLeft"),
Self::TwoPageRight => Name(b"TwoPageRight"),
}
}
}
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
pub enum PageMode {
#[default]
UseNone,
UseOutlines,
UseThumbs,
FullScreen,
UseOC,
UseAttachments,
}
impl PageMode {
pub(crate) fn to_name(self) -> Name<'static> {
match self {
Self::UseNone => Name(b"UseNone"),
Self::UseOutlines => Name(b"UseOutlines"),
Self::UseThumbs => Name(b"UseThumbs"),
Self::FullScreen => Name(b"FullScreen"),
Self::UseOC => Name(b"UseOC"),
Self::UseAttachments => Name(b"UseAttachments"),
}
}
}
pub struct DeveloperExtension<'a> {
dict: Dict<'a>,
}
writer!(DeveloperExtension: |obj| {
let mut dict = obj.dict();
dict.pair(Name(b"Type"), Name(b"DeveloperExtensions"));
Self { dict }
});
impl DeveloperExtension<'_> {
pub fn base_version(&mut self, major: u8, minor: u8) -> &mut Self {
self.pair(Name(b"BaseVersion"), Name(format!("{major}.{minor}").as_bytes()));
self
}
pub fn extension_level(&mut self, level: i32) -> &mut Self {
self.pair(Name(b"ExtensionLevel"), level);
self
}
}
deref!('a, DeveloperExtension<'a> => Dict<'a>, dict);
pub struct ViewerPreferences<'a> {
dict: Dict<'a>,
}
writer!(ViewerPreferences: |obj| Self { dict: obj.dict() });
impl ViewerPreferences<'_> {
pub fn hide_toolbar(&mut self, hide: bool) -> &mut Self {
self.pair(Name(b"HideToolbar"), hide);
self
}
pub fn hide_menubar(&mut self, hide: bool) -> &mut Self {
self.pair(Name(b"HideMenubar"), hide);
self
}
pub fn fit_window(&mut self, fit: bool) -> &mut Self {
self.pair(Name(b"FitWindow"), fit);
self
}
pub fn center_window(&mut self, center: bool) -> &mut Self {
self.pair(Name(b"CenterWindow"), center);
self
}
pub fn display_doc_title(&mut self, display: bool) -> &mut Self {
self.pair(Name(b"DisplayDocTitle"), display);
self
}
pub fn non_full_screen_page_mode(&mut self, mode: PageMode) -> &mut Self {
assert!(mode != PageMode::FullScreen, "mode must not full screen");
self.pair(Name(b"NonFullScreenPageMode"), mode.to_name());
self
}
pub fn direction(&mut self, dir: Direction) -> &mut Self {
self.pair(Name(b"Direction"), dir.to_name());
self
}
}
deref!('a, ViewerPreferences<'a> => Dict<'a>, dict);
pub struct StructTreeRoot<'a> {
dict: Dict<'a>,
}
writer!(StructTreeRoot: |obj| {
let mut dict = obj.dict();
dict.pair(Name(b"Type"), Name(b"StructTreeRoot"));
Self { dict }
});
impl StructTreeRoot<'_> {
pub fn child(&mut self, id: Ref) -> &mut Self {
self.dict.pair(Name(b"K"), id);
self
}
pub fn children(&mut self) -> TypedArray<'_, Ref> {
self.dict.insert(Name(b"K")).array().typed()
}
pub fn id_tree(&mut self) -> NameTree<'_, Ref> {
self.dict.insert(Name(b"IDTree")).start()
}
pub fn parent_tree(&mut self) -> NumberTree<'_, Ref> {
self.dict.insert(Name(b"ParentTree")).start()
}
pub fn parent_tree_next_key(&mut self, key: i32) -> &mut Self {
self.dict.pair(Name(b"ParentTreeNextKey"), key);
self
}
pub fn role_map(&mut self) -> RoleMap<'_> {
self.dict.insert(Name(b"RoleMap")).start()
}
pub fn class_map(&mut self) -> ClassMap<'_> {
self.dict.insert(Name(b"ClassMap")).start()
}
pub fn namespaces(&mut self) -> TypedArray<'_, Ref> {
self.dict.insert(Name(b"Namespaces")).array().typed()
}
pub fn pronunciation_lexicon(&mut self) -> TypedArray<'_, Ref> {
self.dict.insert(Name(b"PronunciationLexicon")).array().typed()
}
pub fn associated_files(&mut self) -> TypedArray<'_, FileSpec<'_>> {
self.dict.insert(Name(b"AF")).array().typed()
}
}
deref!('a, StructTreeRoot<'a> => Dict<'a>, dict);
pub struct StructElement<'a> {
dict: Dict<'a>,
}
writer!(StructElement: |obj| {
let mut dict = obj.dict();
dict.pair(Name(b"Type"), Name(b"StructElem"));
Self { dict }
});
impl StructElement<'_> {
pub fn kind(&mut self, role: StructRole) -> &mut Self {
self.dict.pair(Name(b"S"), role.to_name());
self
}
pub fn kind_2(&mut self, role: StructRole2, pdf_2_ns: Ref) -> &mut Self {
self.dict.pair(Name(b"S"), role.to_name(&mut [0; 6]));
self.namespace(pdf_2_ns)
}
pub fn custom_kind(&mut self, name: Name) -> &mut Self {
self.dict.pair(Name(b"S"), name);
self
}
pub fn parent(&mut self, parent: Ref) -> &mut Self {
self.dict.pair(Name(b"P"), parent);
self
}
pub fn id(&mut self, id: Str) -> &mut Self {
self.dict.pair(Name(b"ID"), id);
self
}
pub fn refs(&mut self, refs: impl IntoIterator<Item = Ref>) -> &mut Self {
self.dict.insert(Name(b"Ref")).array().typed().items(refs);
self
}
pub fn page(&mut self, page: Ref) -> &mut Self {
self.dict.pair(Name(b"Pg"), page);
self
}
pub fn child(&mut self, id: Ref) -> &mut Self {
self.dict.pair(Name(b"K"), id);
self
}
pub fn marked_content_child(&mut self) -> MarkedRef<'_> {
self.dict.insert(Name(b"K")).start()
}
pub fn object_child(&mut self) -> ObjectRef<'_> {
self.dict.insert(Name(b"K")).start()
}
pub fn children(&mut self) -> StructChildren<'_> {
self.dict.insert(Name(b"K")).start()
}
pub fn attributes(&mut self) -> TypedArray<'_, Attributes<'_>> {
self.dict.insert(Name(b"A")).array().typed()
}
pub fn attribute_class(&mut self) -> TypedArray<'_, Name<'_>> {
self.dict.insert(Name(b"C")).array().typed()
}
pub fn revision(&mut self, revision: i32) -> &mut Self {
self.dict.pair(Name(b"R"), revision);
self
}
pub fn title(&mut self, title: impl TextStrLike) -> &mut Self {
self.dict.pair(Name(b"T"), title);
self
}
pub fn lang(&mut self, lang: TextStr) -> &mut Self {
self.dict.pair(Name(b"Lang"), lang);
self
}
pub fn alt(&mut self, alt: impl TextStrLike) -> &mut Self {
self.dict.pair(Name(b"Alt"), alt);
self
}
pub fn expanded(&mut self, expanded: impl TextStrLike) -> &mut Self {
self.dict.pair(Name(b"E"), expanded);
self
}
pub fn actual_text(&mut self, actual_text: impl TextStrLike) -> &mut Self {
self.dict.pair(Name(b"ActualText"), actual_text);
self
}
pub fn associated_files(&mut self) -> TypedArray<'_, FileSpec<'_>> {
self.insert(Name(b"AF")).array().typed()
}
pub fn namespace(&mut self, ns: Ref) -> &mut Self {
self.dict.pair(Name(b"NS"), ns);
self
}
pub fn phonetic_alphabet(&mut self, alphabet: PhoneticAlphabet) -> &mut Self {
self.dict.pair(Name(b"PhoneticAlphabet"), alphabet.to_name());
self
}
pub fn phoneme(&mut self, phoneme: TextStr) -> &mut Self {
self.dict.pair(Name(b"Phoneme"), phoneme);
self
}
}
deref!('a, StructElement<'a> => Dict<'a>, dict);
pub struct StructChildren<'a> {
arr: Array<'a>,
}
writer!(StructChildren: |obj| Self { arr: obj.array() });
impl StructChildren<'_> {
pub fn struct_element(&mut self, id: Ref) -> &mut Self {
self.arr.item(id);
self
}
pub fn marked_content_id(&mut self, id: i32) -> &mut Self {
self.arr.item(id);
self
}
pub fn marked_content_ref(&mut self) -> MarkedRef<'_> {
self.arr.push().start()
}
pub fn object_ref(&mut self) -> ObjectRef<'_> {
self.arr.push().start()
}
}
deref!('a, StructChildren<'a> => Array<'a>, arr);
pub struct MarkedRef<'a> {
dict: Dict<'a>,
}
writer!(MarkedRef: |obj| {
let mut dict = obj.dict();
dict.pair(Name(b"Type"), Name(b"MCR"));
Self { dict }
});
impl MarkedRef<'_> {
pub fn page(&mut self, page: Ref) -> &mut Self {
self.dict.pair(Name(b"Pg"), page);
self
}
pub fn stream(&mut self, stream: Ref) -> &mut Self {
self.dict.pair(Name(b"Stm"), stream);
self
}
pub fn stream_owner(&mut self, owner: Ref) -> &mut Self {
self.dict.pair(Name(b"StmOwn"), owner);
self
}
pub fn marked_content_id(&mut self, id: i32) -> &mut Self {
self.dict.pair(Name(b"MCID"), id);
self
}
}
deref!('a, MarkedRef<'a> => Dict<'a>, dict);
pub struct ObjectRef<'a> {
dict: Dict<'a>,
}
writer!(ObjectRef: |obj| {
let mut dict = obj.dict();
dict.pair(Name(b"Type"), Name(b"OBJR"));
Self { dict }
});
impl ObjectRef<'_> {
pub fn page(&mut self, page: Ref) -> &mut Self {
self.dict.pair(Name(b"Pg"), page);
self
}
pub fn object(&mut self, obj: Ref) -> &mut Self {
self.dict.pair(Name(b"Obj"), obj);
self
}
}
deref!('a, ObjectRef<'a> => Dict<'a>, dict);
pub struct RoleMap<'a> {
dict: TypedDict<'a, Name<'a>>,
}
writer!(RoleMap: |obj| Self { dict: obj.dict().typed() });
impl RoleMap<'_> {
pub fn insert(&mut self, name: Name, role: StructRole) -> &mut Self {
self.dict.pair(name, role.to_name());
self
}
}
deref!('a, RoleMap<'a> => TypedDict<'a, Name<'a>>, dict);
pub struct ClassMap<'a> {
dict: Dict<'a>,
}
writer!(ClassMap: |obj| Self { dict: obj.dict() });
impl ClassMap<'_> {
pub fn single(&mut self, name: Name) -> Attributes<'_> {
self.dict.insert(name).start()
}
pub fn multiple(&mut self, name: Name) -> TypedArray<'_, Attributes<'_>> {
self.dict.insert(name).array().typed()
}
}
deref!('a, ClassMap<'a> => Dict<'a>, dict);
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum StructRole {
Document,
Part,
Art,
Sect,
Div,
BlockQuote,
Caption,
TOC,
TOCI,
Index,
NonStruct,
Private,
P,
StructuredHeading,
H1,
H2,
H3,
H4,
H5,
H6,
L,
LI,
Lbl,
LBody,
Table,
TR,
TH,
TD,
THead,
TBody,
TFoot,
Span,
Quote,
Note,
Reference,
BibEntry,
Code,
Link,
Annot,
Ruby,
Warichu,
RB,
RT,
RP,
WT,
WP,
Figure,
Formula,
Form,
}
impl StructRole {
pub fn to_name(self) -> Name<'static> {
match self {
Self::Document => Name(b"Document"),
Self::Part => Name(b"Part"),
Self::Art => Name(b"Art"),
Self::Sect => Name(b"Sect"),
Self::Div => Name(b"Div"),
Self::BlockQuote => Name(b"BlockQuote"),
Self::Caption => Name(b"Caption"),
Self::TOC => Name(b"TOC"),
Self::TOCI => Name(b"TOCI"),
Self::Index => Name(b"Index"),
Self::NonStruct => Name(b"NonStruct"),
Self::Private => Name(b"Private"),
Self::P => Name(b"P"),
Self::StructuredHeading => Name(b"H"),
Self::H1 => Name(b"H1"),
Self::H2 => Name(b"H2"),
Self::H3 => Name(b"H3"),
Self::H4 => Name(b"H4"),
Self::H5 => Name(b"H5"),
Self::H6 => Name(b"H6"),
Self::L => Name(b"L"),
Self::LI => Name(b"LI"),
Self::Lbl => Name(b"Lbl"),
Self::LBody => Name(b"LBody"),
Self::Table => Name(b"Table"),
Self::TR => Name(b"TR"),
Self::TH => Name(b"TH"),
Self::TD => Name(b"TD"),
Self::THead => Name(b"THead"),
Self::TBody => Name(b"TBody"),
Self::TFoot => Name(b"TFoot"),
Self::Span => Name(b"Span"),
Self::Quote => Name(b"Quote"),
Self::Note => Name(b"Note"),
Self::Reference => Name(b"Reference"),
Self::BibEntry => Name(b"BibEntry"),
Self::Code => Name(b"Code"),
Self::Link => Name(b"Link"),
Self::Annot => Name(b"Annot"),
Self::Ruby => Name(b"Ruby"),
Self::Warichu => Name(b"Warichu"),
Self::RB => Name(b"RB"),
Self::RT => Name(b"RT"),
Self::RP => Name(b"RP"),
Self::WT => Name(b"WT"),
Self::WP => Name(b"WP"),
Self::Figure => Name(b"Figure"),
Self::Formula => Name(b"Formula"),
Self::Form => Name(b"Form"),
}
}
pub fn into_pdf_2_0(self) -> Option<StructRole2> {
match self {
Self::Document => Some(StructRole2::Document),
Self::Part => Some(StructRole2::Part),
Self::Art => None,
Self::Sect => Some(StructRole2::Sect),
Self::Div => Some(StructRole2::Div),
Self::BlockQuote => None,
Self::Caption => Some(StructRole2::Caption),
Self::TOC => None,
Self::TOCI => None,
Self::Index => None,
Self::NonStruct => Some(StructRole2::NonStruct),
Self::Private => None,
Self::P => Some(StructRole2::P),
Self::StructuredHeading => Some(StructRole2::StructuredHeading),
Self::H1 => Some(StructRole2::Heading(NonZeroU16::new(1).unwrap())),
Self::H2 => Some(StructRole2::Heading(NonZeroU16::new(2).unwrap())),
Self::H3 => Some(StructRole2::Heading(NonZeroU16::new(3).unwrap())),
Self::H4 => Some(StructRole2::Heading(NonZeroU16::new(4).unwrap())),
Self::H5 => Some(StructRole2::Heading(NonZeroU16::new(5).unwrap())),
Self::H6 => Some(StructRole2::Heading(NonZeroU16::new(6).unwrap())),
Self::L => Some(StructRole2::L),
Self::LI => Some(StructRole2::LI),
Self::Lbl => Some(StructRole2::Lbl),
Self::LBody => Some(StructRole2::LBody),
Self::Table => Some(StructRole2::Table),
Self::TR => Some(StructRole2::TR),
Self::TH => Some(StructRole2::TH),
Self::TD => Some(StructRole2::TD),
Self::THead => Some(StructRole2::THead),
Self::TBody => Some(StructRole2::TBody),
Self::TFoot => Some(StructRole2::TFoot),
Self::Span => Some(StructRole2::Span),
Self::Quote => Some(StructRole2::Em),
Self::Note => Some(StructRole2::FENote),
Self::Reference => Some(StructRole2::Link),
Self::BibEntry => None,
Self::Code => Some(StructRole2::Strong),
Self::Link => Some(StructRole2::Link),
Self::Annot => Some(StructRole2::Annot),
Self::Ruby => Some(StructRole2::Ruby),
Self::Warichu => Some(StructRole2::Warichu),
Self::RB => Some(StructRole2::RB),
Self::RT => Some(StructRole2::RT),
Self::RP => Some(StructRole2::WP),
Self::WT => Some(StructRole2::WT),
Self::WP => Some(StructRole2::WP),
Self::Figure => Some(StructRole2::Figure),
Self::Formula => Some(StructRole2::Formula),
Self::Form => Some(StructRole2::Form),
}
}
pub fn role_type(self) -> StructRoleType {
match self {
Self::Document
| Self::Part
| Self::Art
| Self::Sect
| Self::Div
| Self::BlockQuote
| Self::Caption
| Self::TOC
| Self::TOCI
| Self::Index
| Self::NonStruct
| Self::Private => StructRoleType::Grouping,
Self::P
| Self::StructuredHeading
| Self::H1
| Self::H2
| Self::H3
| Self::H4
| Self::H5
| Self::H6 => {
StructRoleType::BlockLevel(BlockLevelRoleSubtype::ParagraphLike)
}
Self::L | Self::LI | Self::Lbl | Self::LBody => {
StructRoleType::BlockLevel(BlockLevelRoleSubtype::List)
}
Self::Table => StructRoleType::BlockLevel(BlockLevelRoleSubtype::Table),
Self::TR | Self::TH | Self::TD | Self::THead | Self::TBody | Self::TFoot => {
StructRoleType::Table
}
Self::Span
| Self::Quote
| Self::Note
| Self::Reference
| Self::BibEntry
| Self::Code
| Self::Ruby
| Self::Warichu => {
StructRoleType::InlineLevel(InlineLevelRoleSubtype::Generic)
}
Self::Link => StructRoleType::InlineLevel(InlineLevelRoleSubtype::Link),
Self::Annot => {
StructRoleType::InlineLevel(InlineLevelRoleSubtype::Annotation)
}
Self::RB | Self::RT | Self::RP | Self::WT | Self::WP => {
StructRoleType::InlineLevel(InlineLevelRoleSubtype::RubyWarichu)
}
Self::Figure | Self::Formula | Self::Form => StructRoleType::Illustration,
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum StructRoleType {
Grouping,
BlockLevel(BlockLevelRoleSubtype),
InlineLevel(InlineLevelRoleSubtype),
Illustration,
Table,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum BlockLevelRoleSubtype {
ParagraphLike,
List,
Table,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum InlineLevelRoleSubtype {
Generic,
Link,
Annotation,
RubyWarichu,
}
impl TryFrom<StructRole> for StructRole2 {
type Error = ();
fn try_from(value: StructRole) -> Result<Self, Self::Error> {
value.into_pdf_2_0().ok_or(())
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum StructRole2 {
Document,
DocumentFragment,
Part,
Sect,
Div,
Aside,
NonStruct,
P,
Heading(NonZeroU16),
StructuredHeading,
Title,
FENote,
Sub,
Lbl,
Span,
Em,
Strong,
Link,
Annot,
Form,
Ruby,
RB,
RT,
RP,
Warichu,
WT,
WP,
L,
LI,
LBody,
Table,
TR,
TH,
TD,
THead,
TBody,
TFoot,
Caption,
Figure,
Formula,
Artifact,
}
impl StructRole2 {
pub fn to_name(self, buf: &mut [u8; 6]) -> Name<'_> {
Name(match self {
Self::Document => b"Document",
Self::DocumentFragment => b"DocumentFragment",
Self::Part => b"Part",
Self::Sect => b"Sect",
Self::Div => b"Div",
Self::Aside => b"Aside",
Self::NonStruct => b"NonStruct",
Self::P => b"P",
Self::Heading(level) => {
let mut cursor = Cursor::new(buf.as_mut_slice());
write!(&mut cursor, "H{}", level.get()).unwrap();
let pos = cursor.position() as usize;
&buf[..pos]
}
Self::StructuredHeading => b"H",
Self::Title => b"Title",
Self::FENote => b"FENote",
Self::Sub => b"Sub",
Self::Lbl => b"Lbl",
Self::Span => b"Span",
Self::Em => b"Em",
Self::Strong => b"Strong",
Self::Link => b"Link",
Self::Annot => b"Annot",
Self::Form => b"Form",
Self::Ruby => b"Ruby",
Self::RB => b"RB",
Self::RT => b"RT",
Self::RP => b"RP",
Self::Warichu => b"Warichu",
Self::WT => b"WT",
Self::WP => b"WP",
Self::L => b"L",
Self::LI => b"LI",
Self::LBody => b"LBody",
Self::Table => b"Table",
Self::TR => b"TR",
Self::TH => b"TH",
Self::TD => b"TD",
Self::THead => b"THead",
Self::TBody => b"TBody",
Self::TFoot => b"TFoot",
Self::Caption => b"Caption",
Self::Figure => b"Figure",
Self::Formula => b"Formula",
Self::Artifact => b"Artifact",
})
}
pub fn compatibility_1_7(self, opts: RoleMapOpts) -> StructRole2Compat {
match self {
Self::Document => StructRole2Compat::Compatible(StructRole::Document),
Self::DocumentFragment => StructRole2Compat::RoleMapping(StructRole::Div),
Self::Part => StructRole2Compat::Compatible(StructRole::Part),
Self::Sect => StructRole2Compat::Compatible(StructRole::Sect),
Self::Div => StructRole2Compat::Compatible(StructRole::Div),
Self::Aside => StructRole2Compat::RoleMapping(StructRole::Div),
Self::NonStruct => StructRole2Compat::Compatible(StructRole::NonStruct),
Self::P => StructRole2Compat::Compatible(StructRole::P),
Self::Heading(n) if n.get() == 1 => {
StructRole2Compat::Compatible(StructRole::H1)
}
Self::Heading(n) if n.get() == 2 => {
StructRole2Compat::Compatible(StructRole::H2)
}
Self::Heading(n) if n.get() == 3 => {
StructRole2Compat::Compatible(StructRole::H3)
}
Self::Heading(n) if n.get() == 4 => {
StructRole2Compat::Compatible(StructRole::H4)
}
Self::Heading(n) if n.get() == 5 => {
StructRole2Compat::Compatible(StructRole::H5)
}
Self::Heading(n) if n.get() == 6 => {
StructRole2Compat::Compatible(StructRole::H6)
}
Self::Heading(_) => StructRole2Compat::RoleMapping(
if opts.contains(RoleMapOpts::MAP_HN_TO_H6) {
StructRole::H6
} else {
StructRole::P
},
),
Self::StructuredHeading => {
StructRole2Compat::Compatible(StructRole::StructuredHeading)
}
Self::Title => StructRole2Compat::RoleMapping(
if opts.contains(RoleMapOpts::MAP_TITLE_TO_H1) {
StructRole::H1
} else {
StructRole::P
},
),
Self::FENote => StructRole2Compat::RoleMapping(StructRole::Note),
Self::Sub => StructRole2Compat::RoleMapping(
if opts.contains(RoleMapOpts::MAP_SUB_TO_SPAN) {
StructRole::Span
} else {
StructRole::Div
},
),
Self::Lbl => StructRole2Compat::Compatible(StructRole::Lbl),
Self::Span => StructRole2Compat::Compatible(StructRole::Span),
Self::Em => StructRole2Compat::RoleMapping(StructRole::Span),
Self::Strong => StructRole2Compat::RoleMapping(StructRole::Span),
Self::Link => StructRole2Compat::Compatible(StructRole::Link),
Self::Annot => StructRole2Compat::Compatible(StructRole::Annot),
Self::Form => StructRole2Compat::Compatible(StructRole::Form),
Self::Ruby => StructRole2Compat::Compatible(StructRole::Ruby),
Self::RB => StructRole2Compat::Compatible(StructRole::RB),
Self::RT => StructRole2Compat::Compatible(StructRole::RT),
Self::RP => StructRole2Compat::Compatible(StructRole::RP),
Self::Warichu => StructRole2Compat::Compatible(StructRole::Warichu),
Self::WT => StructRole2Compat::Compatible(StructRole::WT),
Self::WP => StructRole2Compat::Compatible(StructRole::WP),
Self::L => StructRole2Compat::Compatible(StructRole::L),
Self::LI => StructRole2Compat::Compatible(StructRole::LI),
Self::LBody => StructRole2Compat::Compatible(StructRole::LBody),
Self::Table => StructRole2Compat::Compatible(StructRole::Table),
Self::TR => StructRole2Compat::Compatible(StructRole::TR),
Self::TH => StructRole2Compat::Compatible(StructRole::TH),
Self::TD => StructRole2Compat::Compatible(StructRole::TD),
Self::THead => StructRole2Compat::Compatible(StructRole::THead),
Self::TBody => StructRole2Compat::Compatible(StructRole::TBody),
Self::TFoot => StructRole2Compat::Compatible(StructRole::TFoot),
Self::Caption => StructRole2Compat::Compatible(StructRole::Caption),
Self::Figure => StructRole2Compat::Compatible(StructRole::Figure),
Self::Formula => StructRole2Compat::Compatible(StructRole::Formula),
Self::Artifact => StructRole2Compat::RoleMapping(StructRole::Private),
}
}
pub fn role_type(self) -> StructRoleType2 {
match self {
Self::Document | Self::DocumentFragment => StructRoleType2::Document,
Self::Part | Self::Sect | Self::Div | Self::Aside | Self::NonStruct => {
StructRoleType2::Grouping
}
Self::P
| Self::Heading(_)
| Self::StructuredHeading
| Self::Title
| Self::FENote => StructRoleType2::BlockLevel,
Self::Sub => StructRoleType2::SubBlockLevel,
Self::Lbl
| Self::Span
| Self::Em
| Self::Strong
| Self::Link
| Self::Annot
| Self::Form => {
StructRoleType2::InlineLevel(InlineLevelRoleSubtype2::Generic)
}
Self::Ruby
| Self::RB
| Self::RP
| Self::RT
| Self::Warichu
| Self::WT
| Self::WP => {
StructRoleType2::InlineLevel(InlineLevelRoleSubtype2::RubyWarichu)
}
Self::L | Self::LI | Self::LBody => StructRoleType2::List,
Self::Table
| Self::TR
| Self::TH
| Self::TD
| Self::THead
| Self::TBody
| Self::TFoot => StructRoleType2::Table,
Self::Caption => StructRoleType2::Caption,
Self::Figure => StructRoleType2::Figure,
Self::Formula => StructRoleType2::Formula,
Self::Artifact => StructRoleType2::Artifact,
}
}
}
bitflags::bitflags! {
pub struct RoleMapOpts: u8 {
const MAP_HN_TO_H6 = 1 << 0;
const MAP_TITLE_TO_H1 = 1 << 1;
const MAP_SUB_TO_SPAN = 1 << 2;
}
}
impl Default for RoleMapOpts {
fn default() -> Self {
Self::empty()
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum StructRole2Compat {
Compatible(StructRole),
RoleMapping(StructRole),
}
impl StructRole2Compat {
pub fn into_pdf_1_7(self) -> Option<StructRole> {
match self {
Self::Compatible(role) => Some(role),
Self::RoleMapping(_) => None,
}
}
pub fn role_mapped_1_7(self) -> Option<StructRole> {
match self {
Self::Compatible(_) => None,
Self::RoleMapping(role) => Some(role),
}
}
pub fn role(self) -> StructRole {
match self {
Self::Compatible(role) => role,
Self::RoleMapping(role) => role,
}
}
}
impl TryFrom<StructRole2> for StructRole {
type Error = ();
fn try_from(value: StructRole2) -> Result<Self, Self::Error> {
value
.compatibility_1_7(RoleMapOpts::default())
.into_pdf_1_7()
.ok_or(())
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum StructRoleType2 {
Document,
Grouping,
BlockLevel,
SubBlockLevel,
InlineLevel(InlineLevelRoleSubtype2),
List,
Table,
Caption,
Figure,
Formula,
Artifact,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum InlineLevelRoleSubtype2 {
Generic,
RubyWarichu,
}
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
pub enum PhoneticAlphabet<'a> {
#[default]
Ipa,
XSampa,
Pinyin,
WadeGiles,
Custom(Name<'a>),
}
impl<'a> PhoneticAlphabet<'a> {
pub(crate) fn to_name(self) -> Name<'a> {
match self {
Self::Ipa => Name(b"ipa"),
Self::XSampa => Name(b"x-sampa"),
Self::Pinyin => Name(b"zh-Latn-pinyin"),
Self::WadeGiles => Name(b"zh-Latn-wadegile"),
Self::Custom(name) => name,
}
}
}
pub struct Namespace<'a> {
dict: Dict<'a>,
}
writer!(Namespace: |obj| {
let mut dict = obj.dict();
dict.pair(Name(b"Type"), Name(b"Namespace"));
Self { dict }
});
impl Namespace<'_> {
pub fn ns(&mut self, identifier: TextStr) -> &mut Self {
self.dict.pair(Name(b"NS"), identifier);
self
}
pub fn schema(&mut self) -> FileSpec<'_> {
self.dict.insert(Name(b"Schema")).start()
}
pub fn role_map_ns(&mut self) -> NamespaceRoleMap<'_> {
self.dict.insert(Name(b"RoleMapNS")).start()
}
pub fn pdf_2_ns(mut self) {
self.ns(TextStr("https://www.iso.org/pdf2/ssn"));
}
pub fn pdf_1_7_ns(mut self) {
self.ns(TextStr("https://www.iso.org/pdf/ssn"));
}
pub fn mathml_3_0_ns(mut self) {
self.ns(TextStr("https://www.w3.org/1998/Math/MathML"));
}
}
deref!('a, Namespace<'a> => Dict<'a>, dict);
pub struct NamespaceRoleMap<'a> {
dict: Dict<'a>,
}
writer!(NamespaceRoleMap: |obj| {
Self { dict: obj.dict() }
});
impl NamespaceRoleMap<'_> {
pub fn to_pdf_1_7(&mut self, name: Name, role: StructRole) -> &mut Self {
self.dict.pair(name, role.to_name());
self
}
pub fn to_namespace(&mut self, name: Name, role: Name, ns_ref: Ref) -> &mut Self {
let mut array = self.dict.insert(name).array();
array.item(role);
array.item(ns_ref);
array.finish();
self
}
pub fn to_pdf_2_0(
&mut self,
name: Name,
role: StructRole2,
pdf_2_ns: Ref,
) -> &mut Self {
self.to_namespace(name, role.to_name(&mut [0; 6]), pdf_2_ns)
}
}
deref!('a, NamespaceRoleMap<'a> => Dict<'a>, dict);
pub struct MarkInfo<'a> {
dict: Dict<'a>,
}
writer!(MarkInfo: |obj| Self { dict: obj.dict() });
impl MarkInfo<'_> {
pub fn marked(&mut self, conformant: bool) -> &mut Self {
self.pair(Name(b"Marked"), conformant);
self
}
pub fn user_properties(&mut self, present: bool) -> &mut Self {
self.pair(Name(b"UserProperties"), present);
self
}
pub fn suspects(&mut self, present: bool) -> &mut Self {
self.pair(Name(b"Suspects"), present);
self
}
}
deref!('a, MarkInfo<'a> => Dict<'a>, dict);
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
pub enum Direction {
#[default]
L2R,
R2L,
}
impl Direction {
pub(crate) fn to_name(self) -> Name<'static> {
match self {
Self::L2R => Name(b"L2R"),
Self::R2L => Name(b"R2L"),
}
}
}
pub struct PageLabel<'a> {
dict: Dict<'a>,
}
writer!(PageLabel: |obj| {
let mut dict = obj.dict();
dict.pair(Name(b"Type"), Name(b"PageLabel"));
Self { dict }
});
impl PageLabel<'_> {
pub fn style(&mut self, style: NumberingStyle) -> &mut Self {
self.pair(Name(b"S"), style.to_name());
self
}
pub fn prefix(&mut self, prefix: impl TextStrLike) -> &mut Self {
self.pair(Name(b"P"), prefix);
self
}
pub fn offset(&mut self, offset: i32) -> &mut Self {
self.pair(Name(b"St"), offset);
self
}
}
deref!('a, PageLabel<'a> => Dict<'a>, dict);
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum NumberingStyle {
Arabic,
LowerRoman,
UpperRoman,
LowerAlpha,
UpperAlpha,
}
impl NumberingStyle {
pub(crate) fn to_name(self) -> Name<'static> {
match self {
NumberingStyle::Arabic => Name(b"D"),
NumberingStyle::LowerRoman => Name(b"r"),
NumberingStyle::UpperRoman => Name(b"R"),
NumberingStyle::LowerAlpha => Name(b"a"),
NumberingStyle::UpperAlpha => Name(b"A"),
}
}
}
pub struct DocumentInfo<'a> {
dict: Dict<'a>,
}
writer!(DocumentInfo: |obj| Self { dict: obj.dict() });
impl DocumentInfo<'_> {
pub fn title(&mut self, title: impl TextStrLike) -> &mut Self {
self.pair(Name(b"Title"), title);
self
}
pub fn author(&mut self, author: impl TextStrLike) -> &mut Self {
self.pair(Name(b"Author"), author);
self
}
pub fn subject(&mut self, subject: impl TextStrLike) -> &mut Self {
self.pair(Name(b"Subject"), subject);
self
}
pub fn keywords(&mut self, keywords: impl TextStrLike) -> &mut Self {
self.pair(Name(b"Keywords"), keywords);
self
}
pub fn creator(&mut self, creator: impl TextStrLike) -> &mut Self {
self.pair(Name(b"Creator"), creator);
self
}
pub fn producer(&mut self, producer: impl TextStrLike) -> &mut Self {
self.pair(Name(b"Producer"), producer);
self
}
pub fn creation_date(&mut self, date: Date) -> &mut Self {
self.pair(Name(b"CreationDate"), date);
self
}
pub fn modified_date(&mut self, date: Date) -> &mut Self {
self.pair(Name(b"ModDate"), date);
self
}
pub fn trapped(&mut self, trapped: TrappingStatus) -> &mut Self {
self.pair(Name(b"Trapped"), trapped.to_name());
self
}
}
deref!('a, DocumentInfo<'a> => Dict<'a>, dict);
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
pub enum TrappingStatus {
Trapped,
NotTrapped,
#[default]
Unknown,
}
impl TrappingStatus {
pub(crate) fn to_name(self) -> Name<'static> {
match self {
TrappingStatus::Trapped => Name(b"True"),
TrappingStatus::NotTrapped => Name(b"False"),
TrappingStatus::Unknown => Name(b"Unknown"),
}
}
}
pub struct Pages<'a> {
dict: Dict<'a>,
}
writer!(Pages: |obj| {
let mut dict = obj.dict();
dict.pair(Name(b"Type"), Name(b"Pages"));
Self { dict }
});
impl Pages<'_> {
pub fn parent(&mut self, parent: Ref) -> &mut Self {
self.pair(Name(b"Parent"), parent);
self
}
pub fn kids(&mut self, kids: impl IntoIterator<Item = Ref>) -> &mut Self {
self.insert(Name(b"Kids")).array().items(kids);
self
}
pub fn count(&mut self, count: i32) -> &mut Self {
self.pair(Name(b"Count"), count);
self
}
pub fn media_box(&mut self, rect: Rect) -> &mut Self {
self.pair(Name(b"MediaBox"), rect);
self
}
pub fn resources(&mut self) -> Resources<'_> {
self.insert(Name(b"Resources")).start()
}
}
deref!('a, Pages<'a> => Dict<'a>, dict);
pub struct Page<'a> {
dict: Dict<'a>,
}
writer!(Page: |obj| {
let mut dict = obj.dict();
dict.pair(Name(b"Type"), Name(b"Page"));
Self { dict }
});
impl Page<'_> {
pub fn parent(&mut self, parent: Ref) -> &mut Self {
self.pair(Name(b"Parent"), parent);
self
}
pub fn last_modified(&mut self, date: Date) -> &mut Self {
self.pair(Name(b"LastModified"), date);
self
}
pub fn media_box(&mut self, rect: Rect) -> &mut Self {
self.pair(Name(b"MediaBox"), rect);
self
}
pub fn crop_box(&mut self, rect: Rect) -> &mut Self {
self.pair(Name(b"CropBox"), rect);
self
}
pub fn bleed_box(&mut self, rect: Rect) -> &mut Self {
self.pair(Name(b"BleedBox"), rect);
self
}
pub fn trim_box(&mut self, rect: Rect) -> &mut Self {
self.pair(Name(b"TrimBox"), rect);
self
}
pub fn art_box(&mut self, rect: Rect) -> &mut Self {
self.pair(Name(b"ArtBox"), rect);
self
}
pub fn resources(&mut self) -> Resources<'_> {
self.insert(Name(b"Resources")).start()
}
pub fn contents(&mut self, id: Ref) -> &mut Self {
self.pair(Name(b"Contents"), id);
self
}
pub fn contents_array(&mut self, ids: impl IntoIterator<Item = Ref>) -> &mut Self {
self.insert(Name(b"Contents")).array().items(ids);
self
}
pub fn rotate(&mut self, degrees: i32) -> &mut Self {
self.pair(Name(b"Rotate"), degrees);
self
}
pub fn group(&mut self) -> Group<'_> {
self.insert(Name(b"Group")).start()
}
pub fn thumbnail(&mut self, id: Ref) -> &mut Self {
self.pair(Name(b"Thumb"), id);
self
}
pub fn duration(&mut self, seconds: f32) -> &mut Self {
self.pair(Name(b"Dur"), seconds);
self
}
pub fn transition(&mut self) -> Transition<'_> {
self.insert(Name(b"Trans")).start()
}
pub fn annotations(&mut self, ids: impl IntoIterator<Item = Ref>) -> &mut Self {
self.insert(Name(b"Annots")).array().items(ids);
self
}
pub fn struct_parents(&mut self, key: i32) -> &mut Self {
self.pair(Name(b"StructParents"), key);
self
}
pub fn tab_order(&mut self, order: TabOrder) -> &mut Self {
self.pair(Name(b"Tabs"), order.to_name());
self
}
pub fn user_unit(&mut self, value: f32) -> &mut Self {
self.pair(Name(b"UserUnit"), value);
self
}
pub fn additional_actions(&mut self) -> AdditionalActions<'_> {
self.insert(Name(b"AA")).start()
}
pub fn metadata(&mut self, id: Ref) -> &mut Self {
self.pair(Name(b"Metadata"), id);
self
}
pub fn associated_files(&mut self) -> TypedArray<'_, FileSpec<'_>> {
self.insert(Name(b"AF")).array().typed()
}
}
deref!('a, Page<'a> => Dict<'a>, dict);
pub struct Outline<'a> {
dict: Dict<'a>,
}
writer!(Outline: |obj| {
let mut dict = obj.dict();
dict.pair(Name(b"Type"), Name(b"Outlines"));
Self { dict }
});
impl Outline<'_> {
pub fn first(&mut self, item: Ref) -> &mut Self {
self.pair(Name(b"First"), item);
self
}
pub fn last(&mut self, item: Ref) -> &mut Self {
self.pair(Name(b"Last"), item);
self
}
pub fn count(&mut self, count: i32) -> &mut Self {
assert!(count >= 0, "visible outline count must not be negative");
self.pair(Name(b"Count"), count);
self
}
}
deref!('a, Outline<'a> => Dict<'a>, dict);
pub struct OutlineItem<'a> {
dict: Dict<'a>,
}
writer!(OutlineItem: |obj| Self { dict: obj.dict() });
impl OutlineItem<'_> {
pub fn title(&mut self, title: impl TextStrLike) -> &mut Self {
self.pair(Name(b"Title"), title);
self
}
pub fn parent(&mut self, outline: Ref) -> &mut Self {
self.pair(Name(b"Parent"), outline);
self
}
pub fn prev(&mut self, outline: Ref) -> &mut Self {
self.pair(Name(b"Prev"), outline);
self
}
pub fn next(&mut self, outline: Ref) -> &mut Self {
self.pair(Name(b"Next"), outline);
self
}
pub fn first(&mut self, outline: Ref) -> &mut Self {
self.pair(Name(b"First"), outline);
self
}
pub fn last(&mut self, outline: Ref) -> &mut Self {
self.pair(Name(b"Last"), outline);
self
}
pub fn count(&mut self, items: i32) -> &mut Self {
self.pair(Name(b"Count"), items);
self
}
pub fn dest(&mut self) -> Destination<'_> {
self.insert(Name(b"Dest")).start()
}
pub fn dest_name(&mut self, name: Name) -> &mut Self {
self.pair(Name(b"Dest"), name);
self
}
pub fn color_rgb(&mut self, r: f32, g: f32, b: f32) -> &mut Self {
self.insert(Name(b"C")).array().items([r, g, b]);
self
}
pub fn flags(&mut self, flags: OutlineItemFlags) -> &mut Self {
self.pair(Name(b"F"), flags.bits() as i32);
self
}
}
deref!('a, OutlineItem<'a> => Dict<'a>, dict);
bitflags::bitflags! {
pub struct OutlineItemFlags: u32 {
const ITALIC = 1 << 0;
const BOLD = 1 << 1;
}
}
pub struct Names<'a> {
dict: Dict<'a>,
}
writer!(Names: |obj| Self { dict: obj.dict() });
impl Names<'_> {
pub fn destinations(&mut self) -> NameTree<'_, Ref> {
self.dict.insert(Name(b"Dests")).start()
}
pub fn appearances(&mut self) -> NameTree<'_, Ref> {
self.dict.insert(Name(b"AP")).start()
}
pub fn javascript(&mut self) -> NameTree<'_, Ref> {
self.dict.insert(Name(b"JavaScript")).start()
}
pub fn pages(&mut self) -> NameTree<'_, Ref> {
self.dict.insert(Name(b"Pages")).start()
}
pub fn templates(&mut self) -> NameTree<'_, Ref> {
self.dict.insert(Name(b"Templates")).start()
}
pub fn capture_ids(&mut self) -> NameTree<'_, Ref> {
self.dict.insert(Name(b"IDS")).start()
}
pub fn capture_urls(&mut self) -> NameTree<'_, Ref> {
self.dict.insert(Name(b"URLS")).start()
}
pub fn embedded_files(&mut self) -> NameTree<'_, Ref> {
self.dict.insert(Name(b"EmbeddedFiles")).start()
}
pub fn alternate_presentations(&mut self) -> NameTree<'_, Ref> {
self.dict.insert(Name(b"AlternatePresentations")).start()
}
pub fn renditions(&mut self) -> NameTree<'_, Ref> {
self.dict.insert(Name(b"Renditions")).start()
}
}
deref!('a, Names<'a> => Dict<'a>, dict);
pub struct Destination<'a> {
array: Array<'a>,
}
writer!(Destination: |obj| Self { array: obj.array() });
impl Destination<'_> {
pub fn page(mut self, page: Ref) -> Self {
self.item(page);
self
}
pub fn xyz(mut self, left: f32, top: f32, zoom: Option<f32>) {
self.item(Name(b"XYZ"));
self.item(left);
self.item(top);
self.item(zoom.unwrap_or_default());
}
pub fn fit(mut self) {
self.item(Name(b"Fit"));
}
pub fn fit_horizontal(mut self, top: f32) {
self.item(Name(b"FitH"));
self.item(top);
}
pub fn fit_vertical(mut self, left: f32) {
self.item(Name(b"FitV"));
self.item(left);
}
pub fn fit_rect(mut self, rect: Rect) {
self.item(Name(b"FitR"));
self.array.items([rect.x1, rect.y1, rect.x2, rect.y2]);
}
pub fn fit_bounding_box(mut self) {
self.item(Name(b"FitB"));
}
pub fn fit_bounding_box_horizontal(mut self, top: f32) {
self.item(Name(b"FitBH"));
self.item(top);
}
pub fn fit_bounding_box_vertical(mut self, left: f32) {
self.item(Name(b"FitBV"));
self.item(left);
}
}
deref!('a, Destination<'a> => Array<'a>, array);
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum TabOrder {
RowOrder,
ColumnOrder,
StructureOrder,
}
impl TabOrder {
pub(crate) fn to_name(self) -> Name<'static> {
match self {
Self::RowOrder => Name(b"R"),
Self::ColumnOrder => Name(b"C"),
Self::StructureOrder => Name(b"S"),
}
}
}
pub struct Metadata<'a> {
stream: Stream<'a>,
}
impl<'a> Metadata<'a> {
pub(crate) fn start(mut stream: Stream<'a>) -> Self {
stream.pair(Name(b"Type"), Name(b"Metadata"));
stream.pair(Name(b"Subtype"), Name(b"XML"));
Self { stream }
}
}
deref!('a, Metadata<'a> => Stream<'a>, stream);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_max_heading_name() {
let mut buf = [0; 6];
let name = StructRole2::Heading(NonZeroU16::MAX).to_name(&mut buf);
assert_eq!(Name(b"H65535"), name);
}
}