11use super :: * ;
2- use crate :: object:: TextStrLike ;
2+ use crate :: chunk:: Settings ;
3+ use crate :: object:: { is_delimiter_character, TextStrLike } ;
34
45/// A builder for a content stream.
56pub struct Content {
67 buf : Buf ,
8+ settings : Settings ,
79 q_depth : usize ,
810}
911
@@ -16,15 +18,26 @@ impl Content {
1618 Self :: with_capacity ( 1024 )
1719 }
1820
21+ /// Create a new content stream with the given settings.
22+ pub fn with_settings ( settings : Settings ) -> Self {
23+ let mut content = Self :: new ( ) ;
24+ content. settings = settings;
25+ content
26+ }
27+
1928 /// Create a new content stream with the specified initial buffer capacity.
2029 pub fn with_capacity ( capacity : usize ) -> Self {
21- Self { buf : Buf :: with_capacity ( capacity) , q_depth : 0 }
30+ Self {
31+ buf : Buf :: with_capacity ( capacity) ,
32+ q_depth : 0 ,
33+ settings : Default :: default ( ) ,
34+ }
2235 }
2336
2437 /// Start writing an arbitrary operation.
2538 #[ inline]
2639 pub fn op < ' a > ( & ' a mut self , operator : & ' a str ) -> Operation < ' a > {
27- Operation :: start ( & mut self . buf , operator)
40+ Operation :: start ( & mut self . buf , operator, self . settings )
2841 }
2942
3043 /// Return the buffer of the content stream.
@@ -51,12 +64,13 @@ pub struct Operation<'a> {
5164 buf : & ' a mut Buf ,
5265 op : & ' a str ,
5366 first : bool ,
67+ settings : Settings ,
5468}
5569
5670impl < ' a > Operation < ' a > {
5771 #[ inline]
58- pub ( crate ) fn start ( buf : & ' a mut Buf , op : & ' a str ) -> Self {
59- Self { buf, op, first : true }
72+ pub ( crate ) fn start ( buf : & ' a mut Buf , op : & ' a str , settings : Settings ) -> Self {
73+ Self { buf, op, first : true , settings }
6074 }
6175
6276 /// Write a primitive operand.
@@ -79,25 +93,47 @@ impl<'a> Operation<'a> {
7993 self
8094 }
8195
82- /// Start writing an an arbitrary object operand.
96+ /// Start writing an arbitrary object operand.
8397 #[ inline]
8498 pub fn obj ( & mut self ) -> Obj < ' _ > {
85- if !self . first {
86- self . buf . push ( b' ' ) ;
87- }
99+ // In case we are writing the first object, we want a newline to
100+ // separate it from previous operations (looks nicer). Otherwise, a
101+ // space is sufficient.
102+ let pad_byte = if self . first { b'\n' } else { b' ' } ;
103+
104+ // Similarly to how chunks are handled, we always add padding when
105+ // pretty-writing is enabled, and only lazily add padding depending on
106+ // whether it's really necessary if not.
107+ let needs_padding = if self . settings . pretty {
108+ if !self . buf . is_empty ( ) {
109+ self . buf . push ( pad_byte) ;
110+ }
111+
112+ false
113+ } else {
114+ true
115+ } ;
116+
88117 self . first = false ;
89- Obj :: direct ( self . buf , 0 )
118+ Obj :: direct ( self . buf , 0 , self . settings , needs_padding )
90119 }
91120}
92121
93122impl Drop for Operation < ' _ > {
94123 #[ inline]
95124 fn drop ( & mut self ) {
96- if !self . first {
97- self . buf . push ( b' ' ) ;
125+ let pad_byte = if self . first { b'\n' } else { b' ' } ;
126+
127+ // For example, in case we previously wrote a BT operator and then a
128+ // `[]` operand in the next operation, we don't need to pad them.
129+ if ( self . settings . pretty
130+ || self . buf . last ( ) . is_some_and ( |b| !is_delimiter_character ( * b) ) )
131+ && !self . buf . is_empty ( )
132+ {
133+ self . buf . push ( pad_byte) ;
98134 }
135+
99136 self . buf . extend ( self . op . as_bytes ( ) ) ;
100- self . buf . push ( b'\n' ) ;
101137 }
102138}
103139
@@ -1708,4 +1744,44 @@ mod tests {
17081744 b"/F1 12 Tf\n BT\n [] TJ\n [(AB) 2 (CD)] TJ\n ET"
17091745 ) ;
17101746 }
1747+
1748+ #[ test]
1749+ fn test_content_array_no_pretty ( ) {
1750+ let mut content = Content :: with_settings ( Settings { pretty : false } ) ;
1751+
1752+ content. set_font ( Name ( b"F1" ) , 12.0 ) ;
1753+ content. set_font ( Name ( b"F2" ) , 15.0 ) ;
1754+ content. begin_text ( ) ;
1755+ content. show_positioned ( ) . items ( ) ;
1756+ content
1757+ . show_positioned ( )
1758+ . items ( )
1759+ . show ( Str ( b"AB" ) )
1760+ . adjust ( 2.0 )
1761+ . show ( Str ( b"CD" ) )
1762+ . adjust ( 4.0 )
1763+ . show ( Str ( b"EF" ) ) ;
1764+ content. end_text ( ) ;
1765+
1766+ assert_eq ! (
1767+ content. finish( ) . into_vec( ) ,
1768+ b"/F1 12 Tf/F2 15 Tf\n BT[]TJ[(AB)2(CD)4(EF)]TJ\n ET"
1769+ ) ;
1770+ }
1771+
1772+ #[ test]
1773+ fn test_content_dict_no_pretty ( ) {
1774+ let mut content = Content :: with_settings ( Settings { pretty : false } ) ;
1775+
1776+ let mut mc = content. begin_marked_content_with_properties ( Name ( b"Test" ) ) ;
1777+ let mut properties = mc. properties ( ) ;
1778+ properties. actual_text ( TextStr ( "Actual" ) ) . identify ( 1 ) ;
1779+ properties. artifact ( ) . kind ( ArtifactType :: Background ) ;
1780+ mc. finish ( ) ;
1781+
1782+ assert_eq ! (
1783+ content. finish( ) . into_vec( ) ,
1784+ b"/Test<</ActualText(Actual)/MCID 1/Type/Background>>BDC"
1785+ ) ;
1786+ }
17111787}
0 commit comments