libharu/
document.rs

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/// Page label style.
17#[derive(Debug)]
18pub enum PageNumStyle {
19    /// Page label is displayed by Arabic numerals.
20    Decimal,
21    
22    /// Page label is displayed by Uppercase roman numerals.
23    UpperRoman,
24
25    /// Page label is displayed by Lowercase roman numerals.
26    LowerRoman,
27
28    /// Page label is displayed by Uppercase letters (using A to Z).
29    UpperLetters,
30
31    /// Page label is displayed by Lowercase letters (using a to Z).
32    LowerLetters,
33}
34
35bitflags! {
36    /// The flags specifying which type of contents should be compressed.
37    pub struct CompressionMode: u32 {
38        /// All contents are not compressed.
39        const NONE     = 0x00;
40
41        /// Compress the contents stream of the page.
42        const TEXT     = 0x01;
43
44        /// Compress the streams of the image objects.
45        const IMAGE    = 0x02;
46
47        /// Other stream datas (fonts, cmaps and so on) are compressed.
48        const METADATA = 0x04;
49
50        /// All stream datas are compressed. (The same as `CompressionMode::Text | CompressionMode::Image | CompressionMode::Metadata`)
51        const ALL = Self::TEXT.bits | Self::IMAGE.bits | Self::METADATA.bits;
52    }
53}
54
55/// Page display style.
56#[derive(Debug)]
57pub enum PageMode {
58    /// Display the document with neither outline nor thumbnail.
59    None,
60
61    /// Display the document with outline pain.
62    Outline,
63
64    /// Display the document with thumbnail pain.
65    Thumbs,
66    
67    /// Display the document with full screen mode.
68    FullScreen,
69}
70
71
72/// Page layout style.
73#[derive(Debug)]
74pub enum PageLayout {
75    /// Only one page is displayed.
76    Single,
77    
78    /// Display the pages in one column.
79    OneColumn,
80    
81    /// Display the pages in two column. The page of the odd number is displayed left.
82    TwoColumnLeft,
83    
84    /// Display the pages in two column. The page of the odd number is displayed right.
85    TwoColumnRight,
86}
87// onerrorのクロージャをBoxで持ちたいためInnerを別にしている。
88// TODO: onerrorは必要か?
89struct DocumentInner {
90    onerror: Box<dyn Fn(Error)>,
91    last_errno: libharu_sys::HPDF_STATUS,
92    last_detailno: libharu_sys::HPDF_STATUS,
93}
94
95/// PDF Document handle type.
96/// 
97/// The document handle is a handle to operate a document object. 
98pub struct Document {
99    doc: libharu_sys::HPDF_Doc,
100
101    #[allow(dead_code)]
102    inner: Box<DocumentInner>,
103}
104
105impl Document {
106    /// Create a new instance of document.
107    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    /// Create a new page and adds it after the last page of a document.
132    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    /// Return the current page object.
145    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    /// Set how the document should be displayed.
158    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    /// Get how the document should be displayed.
178    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    /// Create a new page and inserts it just before the specified page.
197    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    /// Gets the handle of a corresponding font object by specified name and encoding.
210    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    /// Add a page labeling range for the document.
234    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    /// Enable Japanese fonts. After the method invoked, an application can use the following Japanese fonts.
262    /// * MS-mincho
263    /// * MS-mincho,Bold
264    /// * MS-mincho,Bold
265    /// * MS-mincho,Italic
266    /// * MS-mincho,BoldItalic
267    /// * MS-Gothic
268    /// * MS-Gothic,Bold
269    /// * MS-Gothic,Italic
270    /// * MS-Gothic,BoldItalic
271    /// * MS-Pmincho
272    /// * MS-Pmincho,Bold
273    /// * MS-Pmincho,Italic
274    /// * MS-Pmincho,BoldItalic
275    /// * MS-PGothic
276    /// * MS-PGothic,Bold
277    /// * MS-PGothic,Italic
278    /// * MS-PGothic,BoldItalic
279    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    /// Enable Korian fonts. After the method invoked, an application can use the following Korean fonts.
292    /// * DotumChe
293    /// * DotumChe,Bold
294    /// * DotumChe,Italic
295    /// * DotumChe,BoldItalic
296    /// * Dotum
297    /// * Dotum,Bold
298    /// * Dotum,Italic
299    /// * Dotum,BoldItalic
300    /// * BatangChe
301    /// * BatangChe,Bold
302    /// * BatangChe,Italic
303    /// * BatangChe,BoldItalic
304    /// * Batang
305    /// * Batang,Bold
306    /// * Batang,Italic
307    /// * Batang,BoldItalic
308    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    /// Enable simplified Chinese fonts. After the method invoked, an application can use the following simplified Chinese fonts.
321    /// * SimSun
322    /// * SimSun,Bold
323    /// * SimSun,Italic
324    /// * SimSun,BoldItalic
325    /// * SimHei
326    /// * SimHei,Bold
327    /// * SimHei,Italic
328    /// * SimHei,BoldItalic
329    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    /// Enable traditional Chinese fonts. After the method invoked, an application can use the following traditional Chinese fonts.
342    /// * MingLiU
343    /// * MingLiU,Bold
344    /// * MingLiU,Italic
345    /// * MingLiU,BoldItalic
346    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    /// Enable Japanese encodings. After the method invoked, an application can use the following Japanese encodings.
359    /// * 90ms-RKSJ-H
360    /// * 90ms-RKSJ-V
361    /// * 90msp-RKSJ-H
362    /// * EUC-H
363    /// * EUC-V
364    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    /// Enable Korean encodings. After the method is invoked, an application can use the following Korean encodings.
377    /// * KSC-EUC-H
378    /// * KSC-EUC-V
379    /// * KSCms-UHC-H
380    /// * KSCms-UHC-HW-H
381    /// * KSCms-UHC-HW-V
382    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    /// Enable simplified Chinese encodings. After the method is invoked, an application can use the following simplified Chinese encodings.
395    /// * GB-EUC-H
396    /// * GB-EUC-V
397    /// * GBK-EUC-H
398    /// * GBK-EUC-V
399    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    /// Enable traditional Chinese encodings. After the method is invoked, an application can use the following traditional Chinese encodings.
412    /// * GB-EUC-H
413    /// * GB-EUC-V
414    /// * GBK-EUC-H
415    /// * GBK-EUC-V
416    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    /// Enable UTF-8 encoding.
429    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    /// Save the current document to a file.
442    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    /// Set the mode of compression.
456    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    /// creates root outline object.
469    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    /// creates root outline object. (raw bytes)
495    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    /// Get the handle of a corresponding encoder object by specified encoding name.
521    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    /// Get the handle of the current encoder of the document object.
535    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    /// Set the handle of the current encoder of the document object.
548    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    /// Get the current setting for page layout.
562    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    /// Set how the page should be displayed. If this attribute is not set, the setting of a viewer application is used.
577    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    /// load a TrueType font from an external file and register it to a document object.
597    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        //let ret = unsafe { CString::from_raw(ret as *mut i8).into_string()? };
610        Ok(s)
611    }
612
613    /// Load a TrueType font from an TrueType collection file and register it to a document object.
614    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        //let ret = unsafe { CString::from_raw(ret as *mut i8).into_string()? };
629        Ok(s)
630    }
631
632    /// Load an external png image file.
633    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    /// Set the first page appears when a document is opened.
648    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}