1#![warn(missing_docs)]
2
3use crate::error::Error;
4use crate::page::Page;
5use crate::outline::Outline;
6use crate::Font;
7use crate::encoder::Encoder;
8use crate::destination::Destination;
9use crate::image::Image;
10
11use bitflags::bitflags;
12
13use std::ffi::CString;
14use std::convert::TryInto;
15
16#[derive(Debug)]
18pub enum PageNumStyle {
19 Decimal,
21
22 UpperRoman,
24
25 LowerRoman,
27
28 UpperLetters,
30
31 LowerLetters,
33}
34
35bitflags! {
36 pub struct CompressionMode: u32 {
38 const NONE = 0x00;
40
41 const TEXT = 0x01;
43
44 const IMAGE = 0x02;
46
47 const METADATA = 0x04;
49
50 const ALL = Self::TEXT.bits | Self::IMAGE.bits | Self::METADATA.bits;
52 }
53}
54
55#[derive(Debug)]
57pub enum PageMode {
58 None,
60
61 Outline,
63
64 Thumbs,
66
67 FullScreen,
69}
70
71
72#[derive(Debug)]
74pub enum PageLayout {
75 Single,
77
78 OneColumn,
80
81 TwoColumnLeft,
83
84 TwoColumnRight,
86}
87struct DocumentInner {
90 onerror: Box<dyn Fn(Error)>,
91 last_errno: libharu_sys::HPDF_STATUS,
92 last_detailno: libharu_sys::HPDF_STATUS,
93}
94
95pub struct Document {
99 doc: libharu_sys::HPDF_Doc,
100
101 #[allow(dead_code)]
102 inner: Box<DocumentInner>,
103}
104
105impl Document {
106 pub fn new(onerror: impl Fn(Error) + 'static) -> anyhow::Result<Self>
108 {
109 let onerror = Box::new(onerror);
110 let mut inner = Box::new(DocumentInner{onerror, last_errno: 0, last_detailno: 0});
111
112 let doc = unsafe {
113 libharu_sys::HPDF_New(
114 onerror_callback,
115 std::mem::transmute(inner.as_mut()),
116 )
117 };
118
119 if doc == std::ptr::null_mut() {
120 anyhow::bail!("HPDF_New() failed");
121 }
122
123 Ok(Self { doc, inner })
124 }
125
126 #[inline]
127 pub(crate) fn handle(&self) -> libharu_sys::HPDF_Doc {
128 self.doc
129 }
130
131 pub fn add_page(&self) -> anyhow::Result<Page> {
133 let page = unsafe {
134 libharu_sys::HPDF_AddPage(self.handle())
135 };
136
137 if page == std::ptr::null_mut() {
138 anyhow::bail!("HPDF_AddPage failed");
139 }
140
141 Ok(Page::new(self, page))
142 }
143
144 pub fn current_page(&self) -> anyhow::Result<Page> {
146 let page = unsafe {
147 libharu_sys::HPDF_GetCurrentPage(self.handle())
148 };
149
150 if page == std::ptr::null_mut() {
151 anyhow::bail!("HPDF_GetCurrentPage failed");
152 }
153
154 Ok(Page::new(self, page))
155 }
156
157 pub fn set_page_mode(&self, mode: PageMode) -> anyhow::Result<()> {
159 let mode = match mode {
160 PageMode::None => libharu_sys::HPDF_PageMode::HPDF_PAGE_MODE_USE_NONE,
161 PageMode::Outline => libharu_sys::HPDF_PageMode::HPDF_PAGE_MODE_USE_OUTLINE,
162 PageMode::Thumbs => libharu_sys::HPDF_PageMode::HPDF_PAGE_MODE_USE_THUMBS,
163 PageMode::FullScreen => libharu_sys::HPDF_PageMode::HPDF_PAGE_MODE_FULL_SCREEN,
164 };
165
166 let status = unsafe {
167 libharu_sys::HPDF_SetPageMode(self.handle(), mode)
168 };
169
170 if status != 0 {
171 anyhow::bail!("HPDF_SetPageMode failed (status={})", status);
172 }
173
174 Ok(())
175 }
176
177 pub fn page_mode(&self) -> anyhow::Result<PageMode> {
179 let mode = unsafe {
180 libharu_sys::HPDF_GetPageMode(self.handle())
181 };
182
183 let mode = match mode {
184 libharu_sys::HPDF_PageMode::HPDF_PAGE_MODE_USE_NONE => PageMode::None,
185 libharu_sys::HPDF_PageMode::HPDF_PAGE_MODE_USE_OUTLINE => PageMode::Outline,
186 libharu_sys::HPDF_PageMode::HPDF_PAGE_MODE_USE_THUMBS => PageMode::Thumbs,
187 libharu_sys::HPDF_PageMode::HPDF_PAGE_MODE_FULL_SCREEN => PageMode::FullScreen,
188 _ => {
189 anyhow::bail!("HPDF_GetPageMode failed");
190 }
191 };
192
193 Ok(mode)
194 }
195
196 pub fn insert_page(&self, target: &Page) -> anyhow::Result<Page> {
198 let page = unsafe {
199 libharu_sys::HPDF_InsertPage(self.handle(), target.handle())
200 };
201
202 if page == std::ptr::null_mut() {
203 anyhow::bail!("HPDF_InsertPage failed");
204 }
205
206 Ok(Page::new(self, page))
207 }
208
209 pub fn font(&self, font_name: &str, encoding_name: Option<&str>) -> anyhow::Result<Font> {
211 let font_name = CString::new(font_name)?;
212 let encoding_name = match encoding_name {
213 Some(s) => Some(CString::new(s)?),
214 None => None,
215 };
216
217 let font = unsafe {
218 libharu_sys::HPDF_GetFont(self.handle(),
219 std::mem::transmute(font_name.as_ptr()),
220 match encoding_name {
221 Some(ref s) => std::mem::transmute(s.as_ptr()),
222 None => std::ptr::null_mut(),
223 })
224 };
225
226 if font == std::ptr::null_mut() {
227 anyhow::bail!("HPDF_GetFont failed");
228 }
229
230 Ok(Font::new(self, font))
231 }
232
233 pub fn add_page_label(&self, page_num: usize, style: PageNumStyle, first_page: usize, prefix: Option<&str>) -> anyhow::Result<()> {
235 let style = match style {
236 PageNumStyle::Decimal => libharu_sys::HPDF_PageNumStyle::HPDF_PAGE_NUM_STYLE_DECIMAL,
237 PageNumStyle::UpperRoman => libharu_sys::HPDF_PageNumStyle::HPDF_PAGE_NUM_STYLE_UPPER_ROMAN,
238 PageNumStyle::LowerRoman => libharu_sys::HPDF_PageNumStyle::HPDF_PAGE_NUM_STYLE_LOWER_ROMAN,
239 PageNumStyle::UpperLetters => libharu_sys::HPDF_PageNumStyle::HPDF_PAGE_NUM_STYLE_UPPER_LETTERS,
240 PageNumStyle::LowerLetters => libharu_sys::HPDF_PageNumStyle::HPDF_PAGE_NUM_STYLE_LOWER_LETTERS,
241 };
242
243 let page_num = page_num.try_into()?;
244 let first_page = first_page.try_into()?;
245
246 let prefix = match prefix {
247 Some(s) => CString::new(s)?,
248 None => CString::new("")?,
249 };
250 let status = unsafe {
251 libharu_sys::HPDF_AddPageLabel(self.handle(), page_num, style, first_page, std::mem::transmute(prefix.as_ptr()))
252 };
253
254 if status != 0 {
255 anyhow::bail!("HPDF_AddPageLabelf failed (status = {})", status);
256 }
257
258 Ok(())
259 }
260
261 pub fn use_jpfonts(&self) -> anyhow::Result<()> {
280 let status = unsafe {
281 libharu_sys::HPDF_UseJPFonts(self.handle())
282 };
283
284 if status != 0 {
285 anyhow::bail!("HPDF_UseJPFonts failed (status = {})", status);
286 }
287
288 Ok(())
289 }
290
291 pub fn use_krfonts(&self) -> anyhow::Result<()> {
309 let status = unsafe {
310 libharu_sys::HPDF_UseKRFonts(self.handle())
311 };
312
313 if status != 0 {
314 anyhow::bail!("HPDF_UseKRFonts failed (status = {})", status);
315 }
316
317 Ok(())
318 }
319
320 pub fn use_cnsfonts(&self) -> anyhow::Result<()> {
330 let status = unsafe {
331 libharu_sys::HPDF_UseCNSFonts(self.handle())
332 };
333
334 if status != 0 {
335 anyhow::bail!("HPDF_UseCNSFonts failed (status = {})", status);
336 }
337
338 Ok(())
339 }
340
341 pub fn use_cntfonts(&self) -> anyhow::Result<()> {
347 let status = unsafe {
348 libharu_sys::HPDF_UseCNTFonts(self.handle())
349 };
350
351 if status != 0 {
352 anyhow::bail!("HPDF_UseCNTFonts failed (status = {})", status);
353 }
354
355 Ok(())
356 }
357
358 pub fn use_jpencodings(&self) -> anyhow::Result<()> {
365 let status = unsafe {
366 libharu_sys::HPDF_UseJPEncodings(self.handle())
367 };
368
369 if status != 0 {
370 anyhow::bail!("HPDF_UseJPEncodings failed (status = {})", status);
371 }
372
373 Ok(())
374 }
375
376 pub fn use_krencodings(&self) -> anyhow::Result<()> {
383 let status = unsafe {
384 libharu_sys::HPDF_UseKREncodings(self.handle())
385 };
386
387 if status != 0 {
388 anyhow::bail!("HPDF_UseKREncodings failed (status = {})", status);
389 }
390
391 Ok(())
392 }
393
394 pub fn use_cnsencodings(&self) -> anyhow::Result<()> {
400 let status = unsafe {
401 libharu_sys::HPDF_UseCNSEncodings(self.handle())
402 };
403
404 if status != 0 {
405 anyhow::bail!("HPDF_UseCNSEncodings failed (status = {})", status);
406 }
407
408 Ok(())
409 }
410
411 pub fn use_cntencodings(&self) -> anyhow::Result<()> {
417 let status = unsafe {
418 libharu_sys::HPDF_UseCNTEncodings(self.handle())
419 };
420
421 if status != 0 {
422 anyhow::bail!("HPDF_UseCNTEncodings failed (status = {})", status);
423 }
424
425 Ok(())
426 }
427
428 pub fn use_utfencodings(&self) -> anyhow::Result<()> {
430 let status = unsafe {
431 libharu_sys::HPDF_UseUTFEncodings(self.handle())
432 };
433
434 if status != 0 {
435 anyhow::bail!("HPDF_UseCNTEncodings failed (status = {})", status);
436 }
437
438 Ok(())
439 }
440
441 pub fn save_to_file(&self, name: &str) -> anyhow::Result<()> {
443 let name = CString::new(name).unwrap();
444 let status = unsafe {
445 libharu_sys::HPDF_SaveToFile(self.handle(), std::mem::transmute(name.as_bytes().as_ptr()))
446 };
447
448 if status != 0 {
449 anyhow::bail!("HPDF_SaveToFile failed (status = {})", status);
450 }
451
452 Ok(())
453 }
454
455 pub fn set_compression_mode(&self, mode: CompressionMode) -> anyhow::Result<()> {
457 let status = unsafe {
458 libharu_sys::HPDF_SetCompressionMode(self.handle(), mode.bits())
459 };
460
461 if status != 0 {
462 anyhow::bail!("HPDF_SetCompressionMode failed (status = {})", status);
463 }
464
465 Ok(())
466 }
467
468 pub fn create_outline(&self, title: &str, parent: Option<&Outline>, enc: Option<&Encoder>) -> anyhow::Result<Outline> {
470 let title = CString::new(title)?;
471
472 let outline = unsafe {
473 libharu_sys::HPDF_CreateOutline(
474 self.handle(),
475 match parent {
476 Some(p) => p.handle(),
477 None => std::ptr::null_mut(),
478 },
479 title.as_ptr() as *const i8,
480 match enc {
481 Some(e) => e.handle(),
482 None => std::ptr::null_mut(),
483 }
484 )
485 };
486
487 if outline == std::ptr::null_mut() {
488 anyhow::bail!("HPDF_CreateOutline failed");
489 }
490
491 Ok(Outline::new(self, outline))
492 }
493
494 pub fn create_outline_bytes(&self, title: &[u8], parent: Option<&Outline>, enc: Option<&Encoder>) -> anyhow::Result<Outline> {
496 let title = CString::new(title)?;
497
498 let outline = unsafe {
499 libharu_sys::HPDF_CreateOutline(
500 self.handle(),
501 match parent {
502 Some(p) => p.handle(),
503 None => std::ptr::null_mut(),
504 },
505 title.as_ptr() as *const i8,
506 match enc {
507 Some(e) => e.handle(),
508 None => std::ptr::null_mut(),
509 }
510 )
511 };
512
513 if outline == std::ptr::null_mut() {
514 anyhow::bail!("HPDF_CreateOutline failed");
515 }
516
517 Ok(Outline::new(self, outline))
518 }
519
520 pub fn find_encoder(&self, encoding_name: &str) -> anyhow::Result<Encoder> {
522 let encoding_name = CString::new(encoding_name)?;
523 let enc = unsafe {
524 libharu_sys::HPDF_GetEncoder(self.handle(), encoding_name.as_ptr())
525 };
526
527 if enc == std::ptr::null_mut() {
528 anyhow::bail!("HPDF_GetEncoder failed");
529 }
530
531 Ok(Encoder::new(self, enc))
532 }
533
534 pub fn current_encoder(&self) -> anyhow::Result<Encoder> {
536 let enc = unsafe {
537 libharu_sys::HPDF_GetCurrentEncoder(self.handle())
538 };
539
540 if enc == std::ptr::null_mut() {
541 anyhow::bail!("HPDF_GetCurrentEncoder failed");
542 }
543
544 Ok(Encoder::new(self, enc))
545 }
546
547 pub fn set_current_encoder(&self, encoding_name: &str) -> anyhow::Result<()> {
549 let encoding_name = CString::new(encoding_name)?;
550 let status = unsafe {
551 libharu_sys::HPDF_SetCurrentEncoder(self.handle(), encoding_name.as_ptr())
552 };
553
554 if status != 0 {
555 anyhow::bail!("HPDF_SetCurrentEncoder failed (status={})", status);
556 }
557
558 Ok(())
559 }
560
561 pub fn page_layout(&self) -> anyhow::Result<PageLayout> {
563 let layout = unsafe {
564 libharu_sys::HPDF_GetPageLayout(self.handle())
565 };
566
567 Ok(match layout {
568 libharu_sys::HPDF_PageLayout::HPDF_PAGE_LAYOUT_SINGLE => PageLayout::Single,
569 libharu_sys::HPDF_PageLayout::HPDF_PAGE_LAYOUT_ONE_COLUMN => PageLayout::OneColumn,
570 libharu_sys::HPDF_PageLayout::HPDF_PAGE_LAYOUT_TWO_COLUMN_LEFT => PageLayout::TwoColumnLeft,
571 libharu_sys::HPDF_PageLayout::HPDF_PAGE_LAYOUT_TWO_COLUMN_RIGHT => PageLayout::TwoColumnRight,
572 _ => anyhow::bail!("HPDF_GetPageLayout failed"),
573 })
574 }
575
576 pub fn set_page_layout(&self, layout: PageLayout) -> anyhow::Result<()> {
578 let layout = match layout {
579 PageLayout::Single => libharu_sys::HPDF_PageLayout::HPDF_PAGE_LAYOUT_SINGLE,
580 PageLayout::OneColumn => libharu_sys::HPDF_PageLayout::HPDF_PAGE_LAYOUT_ONE_COLUMN,
581 PageLayout::TwoColumnLeft => libharu_sys::HPDF_PageLayout::HPDF_PAGE_LAYOUT_TWO_COLUMN_LEFT,
582 PageLayout::TwoColumnRight => libharu_sys::HPDF_PageLayout::HPDF_PAGE_LAYOUT_TWO_COLUMN_RIGHT,
583 };
584
585 let status = unsafe {
586 libharu_sys::HPDF_SetPageLayout(self.handle(), layout)
587 };
588
589 if status != 0 {
590 anyhow::bail!("HPDF_SetPageLayout failed (status={})", status);
591 }
592
593 Ok(())
594 }
595
596 pub fn load_ttf_font(&self, name: &str, embedding: bool) -> anyhow::Result<&str> {
598 let name = CString::new(name)?;
599 let ret = unsafe {
600 libharu_sys::HPDF_LoadTTFontFromFile(self.handle(), name.as_ptr(), if embedding { 1 } else { 0 } )
601 };
602
603 if ret == std::ptr::null_mut() {
604 anyhow::bail!("HPDF_LoadTTFontFromFile failed");
605 }
606
607 let s = unsafe { std::ffi::CStr::from_ptr(ret).to_str()? };
608
609 Ok(s)
611 }
612
613 pub fn load_ttf_font_from_ttc(&self, name: &str, index: usize, embedding: bool) -> anyhow::Result<&str> {
615 let name = CString::new(name)?;
616 let index = index as u32;
617
618 let ret = unsafe {
619 libharu_sys::HPDF_LoadTTFontFromFile2(self.handle(), name.as_ptr(), index, if embedding { 1 } else { 0 } )
620 };
621
622 if ret == std::ptr::null_mut() {
623 anyhow::bail!("HPDF_LoadTTFontFromFile failed");
624 }
625
626 let s = unsafe { std::ffi::CStr::from_ptr(ret).to_str()? };
627
628 Ok(s)
630 }
631
632 pub fn load_png_image(&self, name: &str) -> anyhow::Result<Image> {
634 let name = CString::new(name)?;
635
636 let image = unsafe {
637 libharu_sys::HPDF_LoadPngImageFromFile(self.handle(), name.as_ptr())
638 };
639
640 if image == std::ptr::null_mut() {
641 anyhow::bail!("HPDF_LoadPngImageFromFile failed");
642 }
643
644 Ok(Image::new(self, image))
645 }
646
647 pub fn set_open_action(&self, dst: &Destination) -> anyhow::Result<()> {
649 let status = unsafe {
650 libharu_sys::HPDF_SetOpenAction(self.handle(), dst.handle())
651 };
652
653 if status != 0 {
654 anyhow::bail!("HPDF_SetOpenAction failed (status={})", status);
655 }
656
657 Ok(())
658 }
659}
660
661impl Drop for Document {
662 fn drop(&mut self) {
663 unsafe {
664 libharu_sys::HPDF_Free(self.handle());
665 }
666 }
667}
668
669extern "C" fn onerror_callback(
670 errno: libharu_sys::HPDF_STATUS,
671 detailno: libharu_sys::HPDF_STATUS,
672 userdata: libharu_sys::HPDF_HANDLE)
673{
674 let inner: &mut DocumentInner = unsafe { std::mem::transmute(userdata) };
675 inner.last_errno = errno;
676 inner.last_detailno = detailno;
677
678 (inner.onerror)(Error::from_num(errno));
679}