Repository: J-F-Liu/lopdf (language: rust)

Issue:
lopdf 0.28.0 breaks something.
So I was updating and trying out 0.28.0 of lopdf the newest update and noticed when i merge a ton of pdf's together that I use to merge with 0.27 that it now is not a workable PDF.  IDK exactly what caused the issue but i can try and look at the changes to see what it could be. But this is a major problem if it can no longer merge adobe PDF files together.

I also saw a noticeable size difference between the PDFs as well will diff them to see what might of changed.
old 27,448kb
new 27,433kb


Discussion / hints:
Ok i have figured it out
![image](https://user-images.githubusercontent.com/1034709/181289443-333c460f-f61f-4759-a59b-4a0980cc88e4.png)

the PDF is missing the xref which should be near the end of the file.

```
xref
0 1518
0000000000 65535 f 
.........................more here truncating for readability
0028076214 00000 n 
trailer
<</Root 8 0 R/Size 1518>>
```
Also the Root is wrong too in the new

Root in the new is 
```
1518 0 obj
<</Root 8 0 R/Size 1519/W[1 4 2]/Index[1 9 11 13 25 14 40 19 60 19 80 13 94 28 123 13 137 14 152 19 172 19 192 13 206 22 229 1 233 1 235 2 238 4 243 27 271 57 329 18 348 17 366 18 385 45 431 18 450 29 480 21 502 64 567 18 586 43 630 18 649 43 693 18 712 45 758 18 777 43 821 18 840 31 872 18 891 31 923 18 942 31 974 18 993 17 1011 18 1030 17 1048 18 1067 22 1090 18 1109 22 1132 18 1151 36 1188 18 1207 17 1225 18 1244 31 1276 18 1295 57 1353 18 1372 22 1395 124]/Length 10199>>stream
   	    ó    #    ·   º         3   ‡   É   ú   ¦      i   Â   l   -	   -    <>   =r   =‰   >G   ?’   ?Ã   @z   @ß   A=   A–   ˜§   Q    Ô    ê   °   ±<   ±S   ²   ³Œ   ³½   ´L   Ç   V   ‡   ¸   o   Ô   	2   	‹   ª   ®Á   ³~   ³•   Â³   Ãç   Ãþ   Ä¼   Å÷   Æ(   ÆÔ   Ç9   Ç—   Çð   Ìš   Ðç   Ðþ   Ñª   Õ¿   ÕÕ   Ö   Ù½   ÙÓ   èñ   ê%   ê<   êú   ì   ì²   í^   íÃ   î!   îz   ó$   ý]   ýt   ’   
Æ   
Ý   ›   æ      Ä   )   ‡   à   ‹   #ü   $   $Â   2¯   2Ç   3u   AŠ   A¢   BP   Pr   PŠ   Q8   [e   [}   \+   i—   i¯   xÎ   z   z   zÛ   |K   |}   }0   }–   }õ   ~O   ‚û   ’Ë   ’ã   ¢   £7   £O   ¤   ¥G   ¥y   ¦8   ¦ž   ¦ý   §W   þi   
   
™   
°   
Ï   
   
   
Ü   
D   
v   
	   k…   l   mG   my   n8   nž   ný   oW   Ö   ‚   <   T   (s   )¨   )À   *€   +¨   +Ú   ,   ,ó   -R   -¬   2X   6w   6Ž   7A   ;L   ;c   <   ?U   ?l   N‹   OÀ   OØ   P˜   R   R>   Rñ   SW   S¶   T   X¼   bû   c   r2   sg   s   t?   uw   u©   v\   vÂ   w!   w{   |'   C   [   Ž   žJ   žb   Ÿ   ®Ï   ®ç   ¯š   ¾ç   ¾ÿ   Î   ÏS   Ïk   Ð+   Ñ‡   Þ    ß&   ßî   àó   áJ   %µ   '    6%   6k   7x   €   €   €¦   :   l   ‚7   ‚   ‚ü   St   SÎ   ˜n      ¡È   ¡à   ¢“   ©v   ©Ž   ªA   ­#   ­:   ¼Y   ½Ž   ½¦   ¾f   ¿z   ¿¬   À‘   À÷   ÁV   ‘Î   ’(   “   §S   ûf         -    D    ,   !´ù   #Ò!   $ß2   %ÿ   &5   &L   &4   '	   ('‹   )i€   *°I   *³W   *³n   *´V   +õä   -   .Ô[   10¼   13É   13à   14È   3	r   4e€   5vO   6añ   6e=   6eT   6f<   7ÿ
   8½@   9èí   <.[   <1Œ   <1£   <2q   >''   >'Ý   >*t   >*‹   >9ª   >:ß   >:÷   >;·   ><Ó   >=   >=Ð   >>6   >>•   ?
~   ?Mä   ?N>   ?Rê   ?V™   ?V°   ?Wc   ?ZF   ?Z]   ?i|   ?j±   ?jÉ   ?k‰   ?l   ?lÏ   ?m´   ?n   ?ny   @:b   @:¼   AjÖ   B¦½   CÜ€   Cá,   CäB   CäY   Cóx   Cô­   CôÅ   Cõ…   Cö¡   CöÓ   C÷ž   Cø   Cøc   DŽ¿   D   E9?   E=ë   EB@   EBX   EC   EJT   EJl   EY‹   EZÀ   EZØ   E[˜   E\¬   E\Þ   E]Ã   E^)   E^ˆ   Eôä   Eõ>   G!\   H   Jz]   J	   J‚   J‚1   Jƒ   MÈJ   P2-   P¾<   R?   RB   RB&   RC   S$   V)   XŸm   Z&!   Z)6   Z)M   Z*5   [   [æ÷   ]á”   ^£D   ^¦°   ^¦Ç   ^§¯   a/ì   d<   eËÅ   eÌ{   eÏm   eÏ„   eÞ£   eßØ   eßð   eà°   eáÌ   eáþ   eâÉ   eã/   eãŽ   f¢   f¢÷   fæ¿   fëk   fïÕ   fïí   fð    föq   fö‰   g¨   gÝ   gõ   gµ   gÉ   gû   g	à   g
F   g
¥   gÉ´   gÊ   hù:   jKÃ   kI   kN-   kQX   kQo   kRW   l`•   n°¸   pÝT   q–š   q™£   q™º   qšˆ   tŠ«   v ¶   vh   v   vž   vÓ   vë   v«   vÇ   vù   vÄ   v*   v‰   wCn   wCÈ   wˆú   w¦   w’u   w’   w“@   wš¨   wšÀ   w›s   wžk   wž‚   w­¡   w®Ö   w®î   w¯®   w°Â   w°ô   w±Ù   w²?   w²ž   xÞƒ   xÞÝ   yó+   {!C   |É   |ÎI   |ÑZ   |Ñq   |ÒY   }Ñg   ~—¡   à9   €’   €•/   €•F   €–.   §“   ƒHû   …[Ì   ‡^±   ‡a´   ‡aË   ‡b³   ŠUO   ‹m<   ?@   s   v   v0   w   ’X“   “¹Â   ”£/   •¨U   •«j   •«   •¬i   ˜!   ˜Ä&   š¸o   œ6   œ90   œ9G   œ:/   ÉC   žïš    "    ¡g“   ¡jÎ   ¡jå   ¡k³   ¢ýÄ   ¤+   ¤Ó   ¤ê   ¤.	   ¤/>   ¤/V   ¤0   ¤12   ¤1d   ¤2/   ¤2•   ¤2ô   ¥   ¥Ù   ¥`   ¥d´   ¥j;   ¥jS   ¥k   ¥q   ¥q)   ¥€H   ¥}   ¥•   ¥‚U   ¥ƒi   ¥ƒ›   ¥„€   ¥„æ   ¥…E   ¦mÐ   ¦n*   §õ¶   ©bË   ªÙ­   ªÞY   ªák   ªá‚   ªâj   ­¶¾   °{+   ³6¥   ´Óü   ´Öó   ´×
   ´×ò   ¶¹%   ¹fÇ   »@Ð   ½%-   ½(Y   ½(p   ½)X   ¾.r   ¿C   Á<\   Ã^Û   Ãb   Ãb3   Ãc   Å¶õ   Å·«   ÅºB   ÅºY   ÅÉx   ÅÊ­   ÅÊÅ   ÅË…   ÅÌ¡   ÅÌÓ   ÅÍž   ÅÎ   ÅÎc   Æ¨r   Æ¨Ì   ÆìU   Æñ   ÆõÌ   Æõä   Æö—   Æý   Æý*   ÇI   Ç
~   Ç
–   ÇV   Çj   Çœ   Ç   Çç   ÇF   ÇëU   Çë¯   Êi   ËàŒ   Í“   Í—®   ÍšÓ   Íšê   Í›Ò   Îð7   Ðó   Ño<   ÓÁ(   ÓÄW   ÓÄn   ÓÅV   ÕÉÃ   ×*¤   Øæ‚   Ú~à   Úï   Ú‚   Ú‚î   ÜVº   Þˆ   àn»   âLÖ   âP	   âP    âPî   ä?Î   ä@„   äC    äC7   äRV   äS‹   äS£   äTc   äU   äU±   äV|   äVâ   äWA   å˜   åò   å]Ø   åb„   ågc   åg{   åh.   åo   åo—   å~¶   åë   å€   å€Ã   å×   å‚	   å‚î   åƒT   åƒ³   æF
   æFd   çN   è‚º   é4X   é9   é<   é<0   é=   êI…   ëm   ì)4   ïS²   ïW   ïW%   ïX
   ñ:V   òÒÝ   óè“   õ
K   õ`   õw   õ_   ör   ÷šÍ   ùüƒ   úËI   úÎz   úÎ‘   úÏy   üv²   ýÂ    ÿì   ››   žÉ   žà   ­ÿ   ¯4   ¯L   °   ±(   ±Z   ²%   ²‹   ²ê  Ä–  Äð  	  
Ç  í    ¸  ’  ª  )É  *þ  +  +Ö  ,ê  -  .  .g  .Æ  @r  @Ì  W%  ]^  ',  +Ø  /  /0  0  
  àâ  ÊM  	Ñš  	ÔÂ  	ÔÙ  	ÕÁ  
À  ¼t  Õz  ž  Æ  Ý  Å  ž¶  –&  öK  Ô{  ×š  ×±  Ø  Èã  ‰™  Œ]  Œt  ›“  œÈ  œà     ž¼  žî  Ÿ¹      ~  P¹  Q  ”Ú  ™†  ŸX  Ÿp   #  ¥©  ¥Á  ´à  ¶  ¶-  ¶í  ¸  ¸3  ¹  ¹~  ¹Ý  j  jr  _0  —¯  F  J½  MÖ  Mí  NÕ  là  ôU  µ   {c   ~   ~¤   Œ  !Ç  "ˆ@  #"ö  %Hh  %K›  %K²  %ZÑ  %\  %\  %\Þ  %]ú  %^,  %^÷  %_]  %_¼  &¿  &  &^ß  &c‹  &hÿ  &i  &iÊ  &oŒ  &o¤  &~Ã  &ø  &€  &€Ð  &ä  &‚  &‚û  &ƒa  &ƒÀ  '>Ã  '?  (]ë  )¦W  +.ì  +3˜  +6Ú  +6ñ  +7Ù  ,¢W  -’0  /G  0 £  0£Ê  0£á  0¤É  2IÕ  3Ñ  4‹g  6/
  62   627  6AV  6B‹  6B£  6Cc  6D  6D±  6E|  6Eâ  6FA  7u  7Ï  7HA  7Lí  7Rz  7R’  7SE  7Y2  7YJ  7hi  7iž  7i¶  7jv  7kŠ  7k¼  7l¡  7m  7mf  8+š  8+ô  9#’  :0  ;™  ;E  ;f  ;}  ;e  =ŽÄ  ?Ïg  @Ü0  Aƒ–  A†®  A†Å  A‡­  BÐF  D¤Å  Eÿ  EÿÑ  Fë  F  F!  FV  Fn  F.  FJ  F|  FG  F­  F  F€î  FH  FÄÈ  FÉt  FÎÑ  FÎé  FÏœ  FÓ[  FÓr  Fâ‘  FãÆ  FãÞ  Fäž  Få²  Fåä  FæÍ  Fç3  Fç’  GQt  GQÎ  HC   I*  K'á  K,  K/³  K/Ë  K>ë  K@!  K@:  K@ü  KB  KBL  KC   KC‡  KCç  KÿÉ  L $  LC   LHN  LM]  LMv  LN0  LRÆ  LRß  Laÿ  Lc5  LcN  Ld  Le%  LeX  LfH  Lf¯  Lg  M"ñ  M#L  M¢ä  OD  PÆ„  PË2  PÎw  PÎ  PÝ¯  PÞå  PÞþ  PßÀ  PàÝ  Pá  Páä  PâK  Pâ«  Pãb  Pã½  Q'  Q+À  Q0  Q00  Q0ê  Q3²  Q3Ê  QBê  QD   QD9  QDû  QF  QFC  QG3  QGš  QGú  R  Rp  TdÌ  UH“  W1Z  W6  W9=  W9U  W:,  Y5ª  Y6a  Y8ô  Y9  YH,  YIb  YI{  YJ=  YKZ  YK  YLa  YLÈ  YM(  ZQè  Z•L  Z•§  ZšU  Zž(  Zž@  Zžú  Z¡Ý  Z¡õ  Z±  Z²K  Z²d  Z³&  Z´;  Z´n  Zµ^  ZµÅ  Z¶%  [ºå  [»@  \¾Ô  ]±&  ^…  ^‰Ä  ^Œç  ^Œÿ  ^Ö  _H\  _I  _K¯  _KÇ  _Zç  _\  _\6  _\ø  _^  _^H  __  __ƒ  __ã  `w?  `wš  `»  `¿¶  `Å"  `Å;  `Åõ  `Ë®  `ËÇ  `Úç  `Ü  `Ü6  `Üø  `Þ
  `Þ@  `ß0  `ß—  `ß÷  a÷S  a÷®  c
µ  d;œ  e+…  e03  e3J  e3b  e4U  fJõ  gJr  haP  iNº  iQÕ  iQí  iRà  j’Û  kvî  l‘  mÕ  mØ9  mØQ  mÙ(  o:  p|¾  p{  p“  pŽ³  pé  p  pÄ  p‘á  p’  p’è  p“O  p“¯  qBÝ  qC8  q†š  q‹H  qµ  qÎ  q‘ˆ  q•  q•§  q¤Ç  q¥ý  q¦  q¦Ø  q§í  q¨   q©  q©w  q©×  rY  rY`  rêà  sú“  t¿ì  tÄš  tÇÔ  tÇì  t×  tØB  tØ[  tÙ  tÚ:  tÚm  tÛA  tÛ¨  tÜ  uÉš  uÉõ  v
¯  v]  v  v   vÚ  vº  vÓ  v,ó  v.)  v.B  v/  v0  v0L  v1<  v1£  v2  w•  wð  xiì  yÊ“  zŠ³  za  z’‡  z’Ÿ  z“’  |úÛ  `  €Ï:  ‚1«  ‚4®  ‚4Æ  ‚5¹  ƒ¤ò  „>Ö  …¡G  …¡þ  …¥  …¥,  …´L  …µ‚  …µ›  …¶]  …·z  …·­  …¸  …¸è  …¹H  †—¢  †—ý  †Ûõ  †à£  †å•  †å®  †æh  †íá  †íú  †ý  †þP  †þi  †ÿ+  ‡ @  ‡ s  ‡c  ‡Ê  ‡*  ‡à„  ‡àß  ˆŸ?  ‰Æ  Šò  Š÷>  ŠúX  Šúp  Šûc  Œ1u  j³  Ž*Ó  NŸ  Qã  Qû  Rî  ‘&  “X  ”,l  –Ös  –Ù¡  –Ù¹  –Ú¬  ˜:m  ™±‹  ›ã  ›×p  ›Ú¶  ›ÚÎ  ›ÛÁ  œû“  ž  žÕæ  Ÿë³  Ÿî×  Ÿîï  Ÿïâ   Ð{  ¡ºD  £m  ¤¬*  ¤¯n  ¤¯†  ¤°]  ¥Ø$  ¦ø  ¦úÀ  ¦úØ  §	ø  §.  §G  §	  §
&  §
Y  §-  §”  §ô  §Ú’  §Úí  ¨O  ¨"ý  ¨(6  ¨(O  ¨)	  ¨->  ¨-W  ¨<w  ¨=­  ¨=Æ  ¨>ˆ  ¨?  ¨?Ð  ¨@À  ¨A'  ¨A‡  ©
%  ©
€  ©½Ò  ªH­  «B  «ð  «*  «B  «  «šg  ¬8C  ¬:ý  ¬;  ¬J5  ¬Kk  ¬K„  ¬LF  ¬Mc  ¬MŸ  ¬N  ¬NG  ¬NÏ  ¬Nú  ¬Ol  ¬O—  ¬P  ¬P>  ¬PÅ  ¬Pñ  ¬Qt  ¬Q   ¬R(  ¬RT  ¬S  ¬S/  ¬SŽ  ¬Sº  ¬T0  ¬T\  ¬Tê  ¬U  ¬U  ¬U¬  ¬V.  ¬V[  ¬Vã  ¬W  ¬W“  ¬WÀ  ¬XE  ¬Xr  ¬Xè  ¬Y  ¬Y”  ¬YÁ  ¬Z‹  ¬Z¸  ¬["  ¬[O  ¬[Ð  ¬[ý  ¬\v  ¬\£  ¬]%  ¬]R  ¬]Ê  ¬]÷  ¬^x  ¬^¥  ¬_  ¬_J  ¬_Ë  ¬_ø  ¬`p  ¬`  ¬a  ¬aK  ¬aÃ  ¬að  ¬bq  ¬bž  ¬c  ¬cC  ¬cÄ  ¬cñ  ¬di  ¬d–  ¬e  ¬eD  ¬e¼  ¬eé  ¬fj  ¬f—  ¬g  ¬g<  ¬g½  ¬gê  ¬hc  ¬h  ¬i  ¬i?  ¬i¸  ¬iå  ¬jg  ¬j”  ¬k
  ¬k:  ¬k¼  ¬ké  ¬lb  ¬l  ¬m  ¬m@  ¬m¹  ¬mç  ¬ni  ¬n—  ¬o  ¬o>  ¬oÀ  ¬oî  ¬pg  ¬p•  ¬q  ¬qE  ¬q¾  ¬qì  ¬rn  ¬rœ  ¬s  ¬sC  ¬sÅ  ¬só  ¬tl  ¬tš  ¬u  ¬uJ  ¬uÃ  ¬uñ  ¬ve  ¬v“  
endstream 
endobj
```

the old is just

```
<</Root 8 0 R/Size 1518>>
```
after the xref data.

So the break Occurs in commit a3f531b PR by @ralpha 

@J-F-Liu I would Yank the 0.28.0 release from crates.io till this issue is fixed.

This causes Adobe PDF reader, and Envice to not load the pdf displaying a error message that it is broken due to the missing data needed. But works fine in PDF readers that have no features at all.
@genusistimelord could you provide me with some pdf file that I could use for testing?

Not all PDFs need a Cross Reference Table they can also have a Cross Reference Stream. And It looks like this is what is being used here.

Before the commit `lopdf` could only write a Cross Reference Table. In the PR I created to possibility of a Cross Reference Stream.
The PDF being loaded will keep the way it was encoded, a Table if it was a Table and a Stream if it was a Stream.
In the update is set the default (when using `Document::new()`) to the Stream. (Maybe we want to change the default)
https://github.com/J-F-Liu/lopdf/blob/850b150461245cbf7c8dd780b31c76837769a0f5/src/document.rs#L57
(this is a new (since PDF 1.5, in 2003) and more compact way of storing the info and support some other features.)

You can set the type you want to use using: 
```rust
document.reference_table.cross_reference_type = XrefType::CrossReferenceTable;
// or 
document.reference_table.cross_reference_type = XrefType::CrossReferenceStream;
```

But I don't think this is the real issue here. I think something else went wrong.
Maybe the problem is because you are linearizing the PDF, but yet the object id's are not sequential.
I don't know how many file you are merging together but each gap in the object id is exactly 1 (except for one, that claims to have  3 objects, file number 14, which has 1 object in it).
But if you happen to be merging 60 files together and using the `merge.rs` example code. Maybe removing the `+ 1` on this line will partly solve the problem. 
https://github.com/J-F-Liu/lopdf/blob/850b150461245cbf7c8dd780b31c76837769a0f5/examples/merge.rs#L75
Although I think that the file with 3 object, but actually 1, will still give you the same error.

But I think we need to add a way for linearized files to not skip over missing Object Id's. (btw this is wanted behavior in incremental PDFs, hence the reason it does this right now)

If you could provide me with some file I could make sure this is the issue and fix it.
these are PDF files generated with SSRS.  The plus one should not be an issue since he removed one from the count https://github.com/J-F-Liu/lopdf/blob/master/src/reader.rs#L170  which means it is set to the  max_id == last_id. so you need to add 1 in order to reorder the id's for proper merging. 

here are the files i tested to ensure they do break when merged witht he current setup.
[PDF.zip](https://github.com/J-F-Liu/lopdf/files/9203918/PDF.zip)

also the way i currently merge the pdfs are in this PR i canceled because i thought it was a issue i caused and did not know it was a issue with a previous PR.  

https://github.com/J-F-Liu/lopdf/pull/179/files#diff-4a0b312a600584eb42b9515b8af14b5d6cb3f0d2d17bb4b48e87f53089fe9fb8R5


@ralpha I have tested the document.reference_table.cross_reference_type = XrefType::CrossReferenceTable; though I had to make a change to allow xref to be public so I could set the option. Also we should add a Document Version number check for when its set to XrefType::CrossReferenceStream making sure people can only use pdf version 1.5 and greater.

@J-F-Liu Thank you for yanking the current crate for now. that will prevent further issues till @ralpha can get the streaming side fixed. Also please take a look at my PR changes when you get a chance. As i make xref public and have updated the match statement in expand and the merge.rs example.

Currently don't have time to fix it this week, but will take a look soon.

@genusistimelord  when you changed the Xref table make sure that in:
- The Stream case the `Index[1 9 11 13 25 14 40 19 ...]` is shorter (so `ObjId` is sequential)
Index is list of pairs, `1 9` means "starting at id 1 there are 9 objects".
- The Table case that the sections are sequential too. You can see this by the `0 1518` lines (just 2 int values).
They should be similar in both cases and have similar numbers.
Same numbering is used here `0 1518` means "starting at id 0 there are 1518 objects".

The objects are then listed below it in both cases, ether in stream or table.

Fix patch:
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..0a80641
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,5 @@
+*          text eol=lf
+
+*.rs       text diff=rust
+*.toml     text diff=toml
+Cargo.lock text
\ No newline at end of file
diff --git a/examples/merge.rs b/examples/merge.rs
index afeb1ee..977dc17 100644
--- a/examples/merge.rs
+++ b/examples/merge.rs
@@ -1,297 +1,297 @@
-// if you use nightly then you can enable this feature to gain a boost in read speed of PDF's"
-//#![feature(extend_one)]
-
-#[macro_use]
-extern crate lopdf;
-
-use std::collections::BTreeMap;
-
-use lopdf::content::{Content, Operation};
-use lopdf::{Bookmark, Document, Object, ObjectId, Stream};
-
-pub fn generate_fake_document() -> Document {
-    let mut doc = Document::with_version("1.5");
-    let pages_id = doc.new_object_id();
-    let font_id = doc.add_object(dictionary! {
-        "Type" => "Font",
-        "Subtype" => "Type1",
-        "BaseFont" => "Courier",
-    });
-    let resources_id = doc.add_object(dictionary! {
-        "Font" => dictionary! {
-            "F1" => font_id,
-        },
-    });
-    let content = Content {
-        operations: vec![
-            Operation::new("BT", vec![]),
-            Operation::new("Tf", vec!["F1".into(), 48.into()]),
-            Operation::new("Td", vec![100.into(), 600.into()]),
-            Operation::new("Tj", vec![Object::string_literal("Hello World!")]),
-            Operation::new("ET", vec![]),
-        ],
-    };
-    let content_id = doc.add_object(Stream::new(dictionary! {}, content.encode().unwrap()));
-    let page_id = doc.add_object(dictionary! {
-        "Type" => "Page",
-        "Parent" => pages_id,
-        "Contents" => content_id,
-        "Resources" => resources_id,
-        "MediaBox" => vec![0.into(), 0.into(), 595.into(), 842.into()],
-    });
-    let pages = dictionary! {
-        "Type" => "Pages",
-        "Kids" => vec![page_id.into()],
-        "Count" => 1,
-    };
-    doc.objects.insert(pages_id, Object::Dictionary(pages));
-    let catalog_id = doc.add_object(dictionary! {
-        "Type" => "Catalog",
-        "Pages" => pages_id,
-    });
-    doc.trailer.set("Root", catalog_id);
-
-    doc
-}
-
-fn main() {
-    // Generate a stack of Documents to merge
-    // (The Bookmark layer,  Document to merge)
-    let documents = vec![
-        (1u32, generate_fake_document()),
-        (2u32, generate_fake_document()),
-        (2u32, generate_fake_document()),
-        (3u32, generate_fake_document()),
-    ];
-
-    // We use this to keep track of the last Parent per layer depth.
-    let mut layer_parent: [Option<u32>; 4] = [None; 4];
-
-    // This is the last layer ran.
-    let mut last_layer = 0;
-
-    // Define a starting max_id (will be used as start index for object_ids)
-    let mut max_id = 1;
-    let mut pagenum = 1;
-    // Collect all Documents Objects grouped by a map
-    let mut documents_pages = BTreeMap::new();
-    let mut documents_objects = BTreeMap::new();
-    let mut document = Document::with_version("1.5");
-
-    // Lets try to set these to be bigger to avoid multi allocations for faster handling of files.
-    // We are just saying each Document it about 1000 objects in size. can be adjusted for better speeds.
-    // This can only be used if you use nightly or the #![feature(extend_one)] is stablized.
-    // documents_pages.extend_reserve(documents.len() * 1000);
-    // documents_objects.extend_reserve(documents.len() * 1000);
-
-    // Add a Table of Contents
-    // We set the object page to (0,0) which means it will point to the first object after it.
-    layer_parent[0] = Some(document.add_bookmark(
-        Bookmark::new("Table of Contents".to_string(), [0.0, 0.0, 0.0], 0, (0, 0)),
-        None,
-    ));
-
-    // Can set bookmark formatting and color per report bookmark added.
-    // Formating is 1 for italic 2 for bold 3 for bold and italic
-    // Color is RGB 0.0..255.0
-    for (layer, mut doc) in documents {
-        let color = [0.0, 0.0, 0.0];
-        let format = 0;
-        let mut display = String::new();
-
-        doc.renumber_objects_with(max_id);
-
-        max_id = doc.max_id + 1;
-
-        let mut first_object = None;
-
-        let pages = doc.get_pages();
-
-        // This is actually better than extend as we use less allocations and cloning then.
-        pages
-            .into_iter()
-            .map(|(_, object_id)| {
-                // We use this as the return object for Bookmarking to deturmine what it points too.
-                // We only want to do this for the first page though.
-                if first_object.is_none() {
-                    first_object = Some(object_id);
-                    display = format!("Page {}", pagenum);
-                    pagenum += 1;
-                }
-
-                (object_id, doc.get_object(object_id).unwrap().to_owned())
-            })
-            .for_each(|(key, value)| {
-                documents_pages.insert(key, value);
-            });
-
-        documents_objects.extend(doc.objects);
-
-        // Lets shadow our pointer back if nothing then set to (0,0) tto point to the next page
-        let object = first_object.unwrap_or((0, 0));
-
-        // This will use the layering to implement children under Parents in the bookmarks
-        // Example as we are generating it here.
-        // Table of Contents
-        // - Page 1
-        // -- Page 2
-        // -- Page 3
-        // --- Page 4
-
-        if layer == 0 {
-            layer_parent[0] = Some(document.add_bookmark(Bookmark::new(display, color, format, object), None));
-            last_layer = 0;
-        } else if layer == 1 {
-            layer_parent[1] =
-                Some(document.add_bookmark(Bookmark::new(display, color, format, object), layer_parent[0]));
-            last_layer = 1;
-        } else if last_layer >= layer || last_layer == layer - 1 {
-            layer_parent[layer as usize] = Some(document.add_bookmark(
-                Bookmark::new(display, color, format, object),
-                layer_parent[(layer - 1) as usize],
-            ));
-            last_layer = layer;
-        } else if last_layer > 0 {
-            layer_parent[last_layer as usize] = Some(document.add_bookmark(
-                Bookmark::new(display, color, format, object),
-                layer_parent[(last_layer - 1) as usize],
-            ));
-        } else {
-            layer_parent[1] =
-                Some(document.add_bookmark(Bookmark::new(display, color, format, object), layer_parent[0]));
-            last_layer = 1;
-        }
-    }
-
-    // Catalog and Pages are mandatory
-    let mut catalog_object: Option<(ObjectId, Object)> = None;
-    let mut pages_object: Option<(ObjectId, Object)> = None;
-
-    // Process all objects except "Page" type
-    for (object_id, object) in documents_objects.into_iter() {
-        // We have to ignore "Page" (as are processed later), "Outlines" and "Outline" objects
-        // All other objects should be collected and inserted into the main Document
-        match object.type_name().unwrap_or("") {
-            "Catalog" => {
-                // Collect a first "Catalog" object and use it for the future "Pages"
-                catalog_object = Some((
-                    if let Some((id, _)) = catalog_object {
-                        id
-                    } else {
-                        object_id
-                    },
-                    object,
-                ));
-            }
-            "Pages" => {
-                // Collect and update a first "Pages" object and use it for the future "Catalog"
-                // We have also to merge all dictionaries of the old and the new "Pages" object
-                if let Ok(dictionary) = object.as_dict() {
-                    let mut dictionary = dictionary.clone();
-                    if let Some((_, ref object)) = pages_object {
-                        if let Ok(old_dictionary) = object.as_dict() {
-                            dictionary.extend(old_dictionary);
-                        }
-                    }
-
-                    pages_object = Some((
-                        if let Some((id, _)) = pages_object {
-                            id
-                        } else {
-                            object_id
-                        },
-                        Object::Dictionary(dictionary),
-                    ));
-                }
-            }
-            "Page" => {}     // Ignored, processed later and separately
-            "Outlines" => {} // Ignored, not supported yet
-            "Outline" => {}  // Ignored, not supported yet
-            _ => {
-                document.objects.insert(object_id, object);
-            }
-        }
-    }
-
-    // If no "Pages" found abort
-    if pages_object.is_none() {
-        println!("Pages root not found.");
-
-        return;
-    }
-
-    // Iter over all "Page" and collect with the parent "Pages" created before
-    for (object_id, object) in documents_pages.iter() {
-        if let Ok(dictionary) = object.as_dict() {
-            let mut dictionary = dictionary.clone();
-            dictionary.set("Parent", pages_object.as_ref().unwrap().0);
-
-            document.objects.insert(*object_id, Object::Dictionary(dictionary));
-        }
-    }
-
-    // If no "Catalog" found abort
-    if catalog_object.is_none() {
-        println!("Catalog root not found.");
-
-        return;
-    }
-
-    let (catalog_id, catalog_object) = catalog_object.unwrap();
-    let (page_id, page_object) = pages_object.unwrap();
-
-    // Build a new "Pages" with updated fields
-    if let Ok(dictionary) = page_object.as_dict() {
-        let mut dictionary = dictionary.clone();
-
-        // Set new pages count
-        dictionary.set("Count", documents_pages.len() as u32);
-
-        // Set new "Kids" list (collected from documents pages) for "Pages"
-        dictionary.set(
-            "Kids",
-            documents_pages
-                .into_iter()
-                .map(|(object_id, _)| Object::Reference(object_id))
-                .collect::<Vec<_>>(),
-        );
-
-        document.objects.insert(page_id, Object::Dictionary(dictionary));
-    }
-
-    // Build a new "Catalog" with updated fields
-    if let Ok(dictionary) = catalog_object.as_dict() {
-        let mut dictionary = dictionary.clone();
-        dictionary.set("Pages", page_id);
-        dictionary.set("PageMode", "UseOutlines");
-        dictionary.remove(b"Outlines"); // Outlines not supported in merged PDFs
-
-        document.objects.insert(catalog_id, Object::Dictionary(dictionary));
-    }
-
-    document.trailer.set("Root", catalog_id);
-
-    // Update the max internal ID as wasn't updated before due to direct objects insertion
-    document.max_id = document.objects.len() as u32;
-
-    // Reorder all new Document objects
-    document.renumber_objects();
-
-    //Set any Bookmarks to the First child if they are not set to a page
-    document.adjust_zero_pages();
-
-    //Set all bookmarks to the PDF Object tree then set the Outlines to the Bookmark content map.
-    if let Some(n) = document.build_outline() {
-        if let Ok(Object::Dictionary(ref mut dict)) = document.get_object_mut(catalog_id) {
-            dict.set("Outlines", Object::Reference(n));
-        }
-    }
-
-    // Most of the time this does nothing unless there are a lot of streams
-    // Can be disabled to speed up the process.
-    // document.compress();
-
-    // Save the merged PDF
-    // Store file in current working directory.
-    document.save("merged.pdf").unwrap();
-}
+// if you use nightly then you can enable this feature to gain a boost in read speed of PDF's"
+//#![feature(extend_one)]
+
+#[macro_use]
+extern crate lopdf;
+
+use std::collections::BTreeMap;
+
+use lopdf::content::{Content, Operation};
+use lopdf::{Bookmark, Document, Object, ObjectId, Stream};
+
+pub fn generate_fake_document() -> Document {
+    let mut doc = Document::with_version("1.5");
+    let pages_id = doc.new_object_id();
+    let font_id = doc.add_object(dictionary! {
+        "Type" => "Font",
+        "Subtype" => "Type1",
+        "BaseFont" => "Courier",
+    });
+    let resources_id = doc.add_object(dictionary! {
+        "Font" => dictionary! {
+            "F1" => font_id,
+        },
+    });
+    let content = Content {
+        operations: vec![
+            Operation::new("BT", vec![]),
+            Operation::new("Tf", vec!["F1".into(), 48.into()]),
+            Operation::new("Td", vec![100.into(), 600.into()]),
+            Operation::new("Tj", vec![Object::string_literal("Hello World!")]),
+            Operation::new("ET", vec![]),
+        ],
+    };
+    let content_id = doc.add_object(Stream::new(dictionary! {}, content.encode().unwrap()));
+    let page_id = doc.add_object(dictionary! {
+        "Type" => "Page",
+        "Parent" => pages_id,
+        "Contents" => content_id,
+        "Resources" => resources_id,
+        "MediaBox" => vec![0.into(), 0.into(), 595.into(), 842.into()],
+    });
+    let pages = dictionary! {
+        "Type" => "Pages",
+        "Kids" => vec![page_id.into()],
+        "Count" => 1,
+    };
+    doc.objects.insert(pages_id, Object::Dictionary(pages));
+    let catalog_id = doc.add_object(dictionary! {
+        "Type" => "Catalog",
+        "Pages" => pages_id,
+    });
+    doc.trailer.set("Root", catalog_id);
+
+    doc
+}
+
+fn main() {
+    // Generate a stack of Documents to merge
+    // (The Bookmark layer,  Document to merge)
+    let documents = vec![
+        (1u32, generate_fake_document()),
+        (2u32, generate_fake_document()),
+        (2u32, generate_fake_document()),
+        (3u32, generate_fake_document()),
+    ];
+
+    // We use this to keep track of the last Parent per layer depth.
+    let mut layer_parent: [Option<u32>; 4] = [None; 4];
+
+    // This is the last layer ran.
+    let mut last_layer = 0;
+
+    // Define a starting max_id (will be used as start index for object_ids)
+    let mut max_id = 1;
+    let mut pagenum = 1;
+    // Collect all Documents Objects grouped by a map
+    let mut documents_pages = BTreeMap::new();
+    let mut documents_objects = BTreeMap::new();
+    let mut document = Document::with_version("1.5");
+
+    // Lets try to set these to be bigger to avoid multi allocations for faster handling of files.
+    // We are just saying each Document it about 1000 objects in size. can be adjusted for better speeds.
+    // This can only be used if you use nightly or the #![feature(extend_one)] is stablized.
+    // documents_pages.extend_reserve(documents.len() * 1000);
+    // documents_objects.extend_reserve(documents.len() * 1000);
+
+    // Add a Table of Contents
+    // We set the object page to (0,0) which means it will point to the first object after it.
+    layer_parent[0] = Some(document.add_bookmark(
+        Bookmark::new("Table of Contents".to_string(), [0.0, 0.0, 0.0], 0, (0, 0)),
+        None,
+    ));
+
+    // Can set bookmark formatting and color per report bookmark added.
+    // Formating is 1 for italic 2 for bold 3 for bold and italic
+    // Color is RGB 0.0..255.0
+    for (layer, mut doc) in documents {
+        let color = [0.0, 0.0, 0.0];
+        let format = 0;
+        let mut display = String::new();
+
+        doc.renumber_objects_with(max_id);
+
+        max_id = doc.max_id + 1;
+
+        let mut first_object = None;
+
+        let pages = doc.get_pages();
+
+        // This is actually better than extend as we use less allocations and cloning then.
+        pages
+            .into_iter()
+            .map(|(_, object_id)| {
+                // We use this as the return object for Bookmarking to deturmine what it points too.
+                // We only want to do this for the first page though.
+                if first_object.is_none() {
+                    first_object = Some(object_id);
+                    display = format!("Page {}", pagenum);
+                    pagenum += 1;
+                }
+
+                (object_id, doc.get_object(object_id).unwrap().to_owned())
+            })
+            .for_each(|(key, value)| {
+                documents_pages.insert(key, value);
+            });
+
+        documents_objects.extend(doc.objects);
+
+        // Lets shadow our pointer back if nothing then set to (0,0) tto point to the next page
+        let object = first_object.unwrap_or((0, 0));
+
+        // This will use the layering to implement children under Parents in the bookmarks
+        // Example as we are generating it here.
+        // Table of Contents
+        // - Page 1
+        // -- Page 2
+        // -- Page 3
+        // --- Page 4
+
+        if layer == 0 {
+            layer_parent[0] = Some(document.add_bookmark(Bookmark::new(display, color, format, object), None));
+            last_layer = 0;
+        } else if layer == 1 {
+            layer_parent[1] =
+                Some(document.add_bookmark(Bookmark::new(display, color, format, object), layer_parent[0]));
+            last_layer = 1;
+        } else if last_layer >= layer || last_layer == layer - 1 {
+            layer_parent[layer as usize] = Some(document.add_bookmark(
+                Bookmark::new(display, color, format, object),
+                layer_parent[(layer - 1) as usize],
+            ));
+            last_layer = layer;
+        } else if last_layer > 0 {
+            layer_parent[last_layer as usize] = Some(document.add_bookmark(
+                Bookmark::new(display, color, format, object),
+                layer_parent[(last_layer - 1) as usize],
+            ));
+        } else {
+            layer_parent[1] =
+                Some(document.add_bookmark(Bookmark::new(display, color, format, object), layer_parent[0]));
+            last_layer = 1;
+        }
+    }
+
+    // Catalog and Pages are mandatory
+    let mut catalog_object: Option<(ObjectId, Object)> = None;
+    let mut pages_object: Option<(ObjectId, Object)> = None;
+
+    // Process all objects except "Page" type
+    for (object_id, object) in documents_objects.into_iter() {
+        // We have to ignore "Page" (as are processed later), "Outlines" and "Outline" objects
+        // All other objects should be collected and inserted into the main Document
+        match object.type_name().unwrap_or("") {
+            "Catalog" => {
+                // Collect a first "Catalog" object and use it for the future "Pages"
+                catalog_object = Some((
+                    if let Some((id, _)) = catalog_object {
+                        id
+                    } else {
+                        object_id
+                    },
+                    object,
+                ));
+            }
+            "Pages" => {
+                // Collect and update a first "Pages" object and use it for the future "Catalog"
+                // We have also to merge all dictionaries of the old and the new "Pages" object
+                if let Ok(dictionary) = object.as_dict() {
+                    let mut dictionary = dictionary.clone();
+                    if let Some((_, ref object)) = pages_object {
+                        if let Ok(old_dictionary) = object.as_dict() {
+                            dictionary.extend(old_dictionary);
+                        }
+                    }
+
+                    pages_object = Some((
+                        if let Some((id, _)) = pages_object {
+                            id
+                        } else {
+                            object_id
+                        },
+                        Object::Dictionary(dictionary),
+                    ));
+                }
+            }
+            "Page" => {}     // Ignored, processed later and separately
+            "Outlines" => {} // Ignored, not supported yet
+            "Outline" => {}  // Ignored, not supported yet
+            _ => {
+                document.objects.insert(object_id, object);
+            }
+        }
+    }
+
+    // If no "Pages" found abort
+    if pages_object.is_none() {
+        println!("Pages root not found.");
+
+        return;
+    }
+
+    // Iter over all "Page" and collect with the parent "Pages" created before
+    for (object_id, object) in documents_pages.iter() {
+        if let Ok(dictionary) = object.as_dict() {
+            let mut dictionary = dictionary.clone();
+            dictionary.set("Parent", pages_object.as_ref().unwrap().0);
+
+            document.objects.insert(*object_id, Object::Dictionary(dictionary));
+        }
+    }
+
+    // If no "Catalog" found abort
+    if catalog_object.is_none() {
+        println!("Catalog root not found.");
+
+        return;
+    }
+
+    let (catalog_id, catalog_object) = catalog_object.unwrap();
+    let (page_id, page_object) = pages_object.unwrap();
+
+    // Build a new "Pages" with updated fields
+    if let Ok(dictionary) = page_object.as_dict() {
+        let mut dictionary = dictionary.clone();
+
+        // Set new pages count
+        dictionary.set("Count", documents_pages.len() as u32);
+
+        // Set new "Kids" list (collected from documents pages) for "Pages"
+        dictionary.set(
+            "Kids",
+            documents_pages
+                .into_iter()
+                .map(|(object_id, _)| Object::Reference(object_id))
+                .collect::<Vec<_>>(),
+        );
+
+        document.objects.insert(page_id, Object::Dictionary(dictionary));
+    }
+
+    // Build a new "Catalog" with updated fields
+    if let Ok(dictionary) = catalog_object.as_dict() {
+        let mut dictionary = dictionary.clone();
+        dictionary.set("Pages", page_id);
+        dictionary.set("PageMode", "UseOutlines");
+        dictionary.remove(b"Outlines"); // Outlines not supported in merged PDFs
+
+        document.objects.insert(catalog_id, Object::Dictionary(dictionary));
+    }
+
+    document.trailer.set("Root", catalog_id);
+
+    // Update the max internal ID as wasn't updated before due to direct objects insertion
+    document.max_id = document.objects.len() as u32;
+
+    // Reorder all new Document objects
+    document.renumber_objects();
+
+    //Set any Bookmarks to the First child if they are not set to a page
+    document.adjust_zero_pages();
+
+    //Set all bookmarks to the PDF Object tree then set the Outlines to the Bookmark content map.
+    if let Some(n) = document.build_outline() {
+        if let Ok(Object::Dictionary(ref mut dict)) = document.get_object_mut(catalog_id) {
+            dict.set("Outlines", Object::Reference(n));
+        }
+    }
+
+    // Most of the time this does nothing unless there are a lot of streams
+    // Can be disabled to speed up the process.
+    // document.compress();
+
+    // Save the merged PDF
+    // Store file in current working directory.
+    document.save("merged.pdf").unwrap();
+}
diff --git a/src/lib.rs b/src/lib.rs
index bded8c1..959de6f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -10,7 +10,7 @@ pub use crate::object::{Dictionary, Object, ObjectId, Stream, StringFormat};
 mod document;
 mod incremental_document;
 mod object_stream;
-mod xref;
+pub mod xref;
 pub use crate::document::Document;
 pub use crate::incremental_document::IncrementalDocument;
 
diff --git a/src/object.rs b/src/object.rs
index f135725..439fd05 100644
--- a/src/object.rs
+++ b/src/object.rs
@@ -1,651 +1,651 @@
-use crate::{Document, Error, Result};
-use linked_hash_map::{self, Iter, IterMut, LinkedHashMap};
-use log::warn;
-use std::fmt;
-use std::str;
-
-/// Object identifier consists of two parts: object number and generation number.
-pub type ObjectId = (u32, u16);
-
-/// Dictionary object.
-#[derive(Clone, Default)]
-pub struct Dictionary(LinkedHashMap<Vec<u8>, Object>);
-
-/// Stream object
-/// Warning - all streams must be indirect objects, while
-/// the stream dictionary may be a direct object
-#[derive(Debug, Clone)]
-pub struct Stream {
-    /// Associated stream dictionary
-    pub dict: Dictionary,
-    /// Contents of the stream in bytes
-    pub content: Vec<u8>,
-    /// Can the stream be compressed by the `Document::compress()` function?
-    /// Font streams may not be compressed, for example
-    pub allows_compression: bool,
-    /// Stream data's position in PDF file.
-    pub start_position: Option<usize>,
-}
-
-/// Basic PDF object types defined in an enum.
-#[derive(Clone)]
-pub enum Object {
-    Null,
-    Boolean(bool),
-    Integer(i64),
-    Real(f64),
-    Name(Vec<u8>),
-    String(Vec<u8>, StringFormat),
-    Array(Vec<Object>),
-    Dictionary(Dictionary),
-    Stream(Stream),
-    Reference(ObjectId),
-}
-
-/// String objects can be written in two formats.
-#[derive(Debug, Clone)]
-pub enum StringFormat {
-    Literal,
-    Hexadecimal,
-}
-
-impl Default for StringFormat {
-    fn default() -> StringFormat {
-        StringFormat::Literal
-    }
-}
-
-impl From<bool> for Object {
-    fn from(value: bool) -> Self {
-        Object::Boolean(value)
-    }
-}
-
-impl From<i64> for Object {
-    fn from(number: i64) -> Self {
-        Object::Integer(number)
-    }
-}
-
-macro_rules! from_smaller_ints {
-	($( $Int: ty )+) => {
-		$(
-			impl From<$Int> for Object {
-				fn from(number: $Int) -> Self {
-					Object::Integer(i64::from(number))
-				}
-			}
-		)+
-	}
-}
-
-from_smaller_ints! {
-    i8 i16 i32
-    u8 u16 u32
-}
-
-impl From<f64> for Object {
-    fn from(number: f64) -> Self {
-        Object::Real(number)
-    }
-}
-
-impl From<f32> for Object {
-    fn from(number: f32) -> Self {
-        Object::Real(f64::from(number))
-    }
-}
-
-impl From<String> for Object {
-    fn from(name: String) -> Self {
-        Object::Name(name.into_bytes())
-    }
-}
-
-impl<'a> From<&'a str> for Object {
-    fn from(name: &'a str) -> Self {
-        Object::Name(name.as_bytes().to_vec())
-    }
-}
-
-impl From<Vec<Object>> for Object {
-    fn from(array: Vec<Object>) -> Self {
-        Object::Array(array)
-    }
-}
-
-impl From<Dictionary> for Object {
-    fn from(dcit: Dictionary) -> Self {
-        Object::Dictionary(dcit)
-    }
-}
-
-impl From<Stream> for Object {
-    fn from(stream: Stream) -> Self {
-        Object::Stream(stream)
-    }
-}
-
-impl From<ObjectId> for Object {
-    fn from(id: ObjectId) -> Self {
-        Object::Reference(id)
-    }
-}
-
-impl Object {
-    pub fn string_literal<S: Into<Vec<u8>>>(s: S) -> Self {
-        Object::String(s.into(), StringFormat::Literal)
-    }
-
-    pub fn is_null(&self) -> bool {
-        matches!(*self, Object::Null)
-    }
-
-    pub fn as_bool(&self) -> Result<bool> {
-        match *self {
-            Object::Boolean(ref value) => Ok(*value),
-            _ => Err(Error::Type),
-        }
-    }
-
-    pub fn as_i64(&self) -> Result<i64> {
-        match *self {
-            Object::Integer(ref value) => Ok(*value),
-            _ => Err(Error::Type),
-        }
-    }
-
-    pub fn as_f64(&self) -> Result<f64> {
-        match *self {
-            Object::Real(ref value) => Ok(*value),
-            _ => Err(Error::Type),
-        }
-    }
-
-    /// Get the object value as a float.
-    /// Unlike as_f64() this will also cast an Integer to a Real.
-    pub fn as_float(&self) -> Result<f64> {
-        match *self {
-            Object::Integer(ref value) => Ok(*value as f64),
-            Object::Real(ref value) => Ok(*value),
-            _ => Err(Error::Type),
-        }
-    }
-
-    pub fn as_name(&self) -> Result<&[u8]> {
-        match *self {
-            Object::Name(ref name) => Ok(name),
-            _ => Err(Error::Type),
-        }
-    }
-
-    pub fn as_name_str(&self) -> Result<&str> {
-        Ok(str::from_utf8(self.as_name()?)?)
-    }
-
-    pub fn as_str(&self) -> Result<&[u8]> {
-        match self {
-            Object::String(string, _) => Ok(string),
-            _ => Err(Error::Type),
-        }
-    }
-
-    pub fn as_str_mut(&mut self) -> Result<&mut Vec<u8>> {
-        match self {
-            Object::String(string, _) => Ok(string),
-            _ => Err(Error::Type),
-        }
-    }
-
-    pub fn as_reference(&self) -> Result<ObjectId> {
-        match *self {
-            Object::Reference(ref id) => Ok(*id),
-            _ => Err(Error::Type),
-        }
-    }
-
-    pub fn as_array(&self) -> Result<&Vec<Object>> {
-        match *self {
-            Object::Array(ref arr) => Ok(arr),
-            _ => Err(Error::Type),
-        }
-    }
-
-    pub fn as_array_mut(&mut self) -> Result<&mut Vec<Object>> {
-        match *self {
-            Object::Array(ref mut arr) => Ok(arr),
-            _ => Err(Error::Type),
-        }
-    }
-
-    pub fn as_dict(&self) -> Result<&Dictionary> {
-        match *self {
-            Object::Dictionary(ref dict) => Ok(dict),
-            _ => Err(Error::Type),
-        }
-    }
-
-    pub fn as_dict_mut(&mut self) -> Result<&mut Dictionary> {
-        match *self {
-            Object::Dictionary(ref mut dict) => Ok(dict),
-            _ => Err(Error::Type),
-        }
-    }
-
-    pub fn as_stream(&self) -> Result<&Stream> {
-        match *self {
-            Object::Stream(ref stream) => Ok(stream),
-            _ => Err(Error::Type),
-        }
-    }
-
-    pub fn as_stream_mut(&mut self) -> Result<&mut Stream> {
-        match *self {
-            Object::Stream(ref mut stream) => Ok(stream),
-            _ => Err(Error::Type),
-        }
-    }
-
-    pub fn type_name(&self) -> Result<&str> {
-        match *self {
-            Object::Dictionary(ref dict) => dict.type_name(),
-            Object::Stream(ref stream) => stream.dict.type_name(),
-            _ => Err(Error::Type),
-        }
-    }
-}
-
-impl fmt::Debug for Object {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match *self {
-            Object::Null => f.write_str("null"),
-            Object::Boolean(ref value) => {
-                if *value {
-                    f.write_str("true")
-                } else {
-                    f.write_str("false")
-                }
-            }
-            Object::Integer(ref value) => write!(f, "{}", *value),
-            Object::Real(ref value) => write!(f, "{}", *value),
-            Object::Name(ref name) => write!(f, "/{}", String::from_utf8_lossy(name)),
-            Object::String(ref text, _) => write!(f, "({})", String::from_utf8_lossy(text)),
-            Object::Array(ref array) => {
-                let items = array.iter().map(|item| format!("{:?}", item)).collect::<Vec<String>>();
-                write!(f, "[{}]", items.join(" "))
-            }
-            Object::Dictionary(ref dict) => write!(f, "{:?}", dict),
-            Object::Stream(ref stream) => write!(f, "{:?}stream...endstream", stream.dict),
-            Object::Reference(ref id) => write!(f, "{} {} R", id.0, id.1),
-        }
-    }
-}
-
-impl Dictionary {
-    pub fn new() -> Dictionary {
-        Dictionary(LinkedHashMap::new())
-    }
-
-    pub fn has(&self, key: &[u8]) -> bool {
-        self.0.contains_key(key)
-    }
-
-    pub fn get(&self, key: &[u8]) -> Result<&Object> {
-        self.0.get(key).ok_or(Error::DictKey)
-    }
-
-    /// Extract object from dictionary, dereferencing the object if it
-    /// is a reference.
-    pub fn get_deref<'a>(&'a self, key: &[u8], doc: &'a Document) -> Result<&'a Object> {
-        doc.dereference(self.get(key)?).map(|(_, object)| object)
-    }
-
-    pub fn get_mut(&mut self, key: &[u8]) -> Result<&mut Object> {
-        self.0.get_mut(key).ok_or(Error::DictKey)
-    }
-
-    pub fn set<K, V>(&mut self, key: K, value: V)
-    where
-        K: Into<Vec<u8>>,
-        V: Into<Object>,
-    {
-        self.0.insert(key.into(), value.into());
-    }
-
-    pub fn len(&self) -> usize {
-        self.0.len()
-    }
-
-    pub fn is_empty(&self) -> bool {
-        self.0.len() == 0
-    }
-
-    pub fn remove(&mut self, key: &[u8]) -> Option<Object> {
-        self.0.remove(key)
-    }
-
-    pub fn type_name(&self) -> Result<&str> {
-        self.get(b"Type")
-            .and_then(Object::as_name_str)
-            .or_else(|_| self.get(b"Linearized").and(Ok("Linearized")))
-    }
-
-    pub fn type_is(&self, type_name: &[u8]) -> bool {
-        self.get(b"Type").and_then(Object::as_name).ok() == Some(type_name)
-    }
-
-    pub fn iter(&self) -> Iter<'_, Vec<u8>, Object> {
-        self.0.iter()
-    }
-
-    pub fn iter_mut(&mut self) -> IterMut<'_, Vec<u8>, Object> {
-        self.0.iter_mut()
-    }
-
-    pub fn get_font_encoding(&self) -> &str {
-        self.get(b"Encoding")
-            .and_then(Object::as_name_str)
-            .unwrap_or("StandardEncoding")
-    }
-
-    pub fn extend(&mut self, other: &Dictionary) {
-        let keep_both_objects =
-            |new_dict: &mut LinkedHashMap<Vec<u8>, Object>, key: &Vec<u8>, value: &Object, old_value: &Object| {
-                let mut final_array;
-
-                match value {
-                    Object::Array(array) => {
-                        final_array = Vec::with_capacity(array.len() + 1);
-                        final_array.push(old_value.to_owned());
-                        final_array.extend(array.to_owned());
-                    }
-                    _ => {
-                        final_array = vec![value.to_owned(), old_value.to_owned()];
-                    }
-                }
-
-                new_dict.insert(key.to_owned(), Object::Array(final_array));
-            };
-
-        let mut new_dict = LinkedHashMap::with_capacity(other.0.len() + 1);
-
-        for (key, value) in other.0.iter() {
-            if let Some(old_value) = self.0.get(key) {
-                match (old_value, value) {
-                    (Object::Dictionary(old_dict), Object::Dictionary(dict)) => {
-                        let mut replaced_dict = old_dict.to_owned();
-                        replaced_dict.extend(dict);
-
-                        new_dict.insert(key.to_owned(), Object::Dictionary(replaced_dict));
-                    }
-                    (Object::Array(old_array), Object::Array(array)) => {
-                        let mut replaced_array = old_array.to_owned();
-                        replaced_array.extend(array.to_owned());
-
-                        new_dict.insert(key.to_owned(), Object::Array(replaced_array));
-                    }
-                    (Object::Integer(old_id), Object::Integer(id)) => {
-                        let array = vec![Object::Integer(*old_id), Object::Integer(*id)];
-
-                        new_dict.insert(key.to_owned(), Object::Array(array));
-                    }
-                    (Object::Real(old_id), Object::Real(id)) => {
-                        let array = vec![Object::Real(*old_id), Object::Real(*id)];
-
-                        new_dict.insert(key.to_owned(), Object::Array(array));
-                    }
-                    (Object::String(old_ids, old_format), Object::String(ids, format)) => {
-                        let array = vec![
-                            Object::String(old_ids.to_owned(), old_format.to_owned()),
-                            Object::String(ids.to_owned(), format.to_owned()),
-                        ];
-
-                        new_dict.insert(key.to_owned(), Object::Array(array));
-                    }
-                    (Object::Reference(old_object_id), Object::Reference(object_id)) => {
-                        let array = vec![Object::Reference(*old_object_id), Object::Reference(*object_id)];
-
-                        new_dict.insert(key.to_owned(), Object::Array(array));
-                    }
-                    (Object::Null, _) | (Object::Boolean(_), _) | (Object::Name(_), _) | (Object::Stream(_), _) => {
-                        new_dict.insert(key.to_owned(), old_value.to_owned());
-                    }
-                    (_, _) => keep_both_objects(&mut new_dict, key, value, old_value),
-                }
-            } else {
-                new_dict.insert(key.to_owned(), value.to_owned());
-            }
-        }
-
-        self.0 = new_dict;
-    }
-}
-
-#[macro_export]
-macro_rules! dictionary {
-	() => {
-		$crate::Dictionary::new()
-	};
-	($( $key: expr => $value: expr ),+ ,) => {
-		dictionary!( $($key => $value),+ )
-	};
-	($( $key: expr => $value: expr ),*) => {{
-		let mut dict = $crate::Dictionary::new();
-		$(
-			dict.set($key, $value);
-		)*
-		dict
-	}}
-}
-
-impl fmt::Debug for Dictionary {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let entries = self
-            .into_iter()
-            .map(|(key, value)| format!("/{} {:?}", String::from_utf8_lossy(key), value))
-            .collect::<Vec<String>>();
-        write!(f, "<<{}>>", entries.concat())
-    }
-}
-
-impl<'a> IntoIterator for &'a Dictionary {
-    type Item = (&'a Vec<u8>, &'a Object);
-    type IntoIter = linked_hash_map::Iter<'a, Vec<u8>, Object>;
-
-    fn into_iter(self) -> Self::IntoIter {
-        self.0.iter()
-    }
-}
-
-use std::iter::FromIterator;
-impl<K: Into<Vec<u8>>> FromIterator<(K, Object)> for Dictionary {
-    fn from_iter<I: IntoIterator<Item = (K, Object)>>(iter: I) -> Self {
-        let mut dict = Dictionary::new();
-        for (k, v) in iter {
-            dict.set(k, v);
-        }
-        dict
-    }
-}
-
-impl Stream {
-    pub fn new(mut dict: Dictionary, content: Vec<u8>) -> Stream {
-        dict.set("Length", content.len() as i64);
-        Stream {
-            dict,
-            content,
-            allows_compression: true,
-            start_position: None,
-        }
-    }
-
-    pub fn with_position(dict: Dictionary, position: usize) -> Stream {
-        Stream {
-            dict,
-            content: vec![],
-            allows_compression: true,
-            start_position: Some(position),
-        }
-    }
-
-    /// Default is that the stream may be compressed. On font streams,
-    /// set this to false, otherwise the font will be corrupt
-    #[inline]
-    pub fn with_compression(mut self, allows_compression: bool) -> Stream {
-        self.allows_compression = allows_compression;
-        self
-    }
-
-    // Return first filter
-    pub fn filter(&self) -> Result<String> {
-        self.filters()
-            .and_then(|f| f.into_iter().next().ok_or(Error::ObjectNotFound))
-    }
-
-    pub fn filters(&self) -> Result<Vec<String>> {
-        let filter = self.dict.get(b"Filter")?;
-
-        if let Ok(name) = filter.as_name_str() {
-            Ok(vec![name.into()])
-        } else if let Ok(names) = filter.as_array() {
-            // It is an error if a single conversion fails.
-            names
-                .iter()
-                .map(|n| match Object::as_name_str(n) {
-                    Ok(n) => Ok(String::from(n)),
-                    Err(_) => Err(Error::Type),
-                })
-                .collect()
-        } else {
-            Err(Error::Type)
-        }
-    }
-
-    pub fn set_content(&mut self, content: Vec<u8>) {
-        self.content = content;
-        self.dict.set("Length", self.content.len() as i64);
-    }
-
-    pub fn set_plain_content(&mut self, content: Vec<u8>) {
-        self.dict.remove(b"DecodeParms");
-        self.dict.remove(b"Filter");
-        self.dict.set("Length", content.len() as i64);
-        self.content = content;
-    }
-
-    pub fn compress(&mut self) -> Result<()> {
-        use flate2::write::ZlibEncoder;
-        use flate2::Compression;
-        use std::io::prelude::*;
-
-        if self.dict.get(b"Filter").is_err() {
-            let mut encoder = ZlibEncoder::new(Vec::new(), Compression::best());
-            encoder.write_all(self.content.as_slice())?;
-            let compressed = encoder.finish()?;
-            if compressed.len() + 19 < self.content.len() {
-                self.dict.set("Filter", "FlateDecode");
-                self.set_content(compressed);
-            }
-        }
-        Ok(())
-    }
-
-    pub fn decompressed_content(&self) -> Result<Vec<u8>> {
-        let params = self.dict.get(b"DecodeParms").and_then(Object::as_dict).ok();
-        let filters = self.filters()?;
-
-        if self.dict.get(b"Subtype").and_then(Object::as_name_str).ok() == Some("Image") {
-            return Err(Error::Type);
-        }
-
-        let mut input = self.content.as_slice();
-        let mut output = None;
-
-        // Filters are in decoding order.
-        for filter in filters {
-            output = Some(match filter.as_str() {
-                "FlateDecode" => Self::decompress_zlib(input, params)?,
-                "LZWDecode" => Self::decompress_lzw(input, params)?,
-                _ => {
-                    return Err(Error::Type);
-                }
-            });
-            input = output.as_ref().unwrap();
-        }
-
-        output.ok_or(Error::Type)
-    }
-
-    fn decompress_lzw(input: &[u8], params: Option<&Dictionary>) -> Result<Vec<u8>> {
-        use weezl::{decode::Decoder, BitOrder};
-        const MIN_BITS: u8 = 9;
-
-        let early_change = params
-            .and_then(|p| p.get(b"EarlyChange").ok())
-            .and_then(|p| Object::as_i64(p).ok())
-            .map(|v| v != 0)
-            .unwrap_or(true);
-
-        let mut decoder = if early_change {
-            Decoder::with_tiff_size_switch(BitOrder::Msb, MIN_BITS - 1)
-        } else {
-            Decoder::new(BitOrder::Msb, MIN_BITS - 1)
-        };
-
-        let output = Self::decompress_lzw_loop(input, &mut decoder);
-        Self::decompress_predictor(output, params)
-    }
-
-    fn decompress_lzw_loop(input: &[u8], decoder: &mut weezl::decode::Decoder) -> Vec<u8> {
-        let mut output = vec![];
-
-        let result = decoder.into_stream(&mut output).decode_all(input);
-        if let Err(err) = result.status {
-            warn!("{}", err);
-        }
-
-        output
-    }
-
-    fn decompress_zlib(input: &[u8], params: Option<&Dictionary>) -> Result<Vec<u8>> {
-        use flate2::read::ZlibDecoder;
-        use std::io::prelude::*;
-
-        let mut output = Vec::with_capacity(input.len() * 2);
-        let mut decoder = ZlibDecoder::new(input);
-
-        if !input.is_empty() {
-            decoder.read_to_end(&mut output).unwrap_or_else(|err| {
-                warn!("{}", err);
-                0
-            });
-        }
-        Self::decompress_predictor(output, params)
-    }
-
-    fn decompress_predictor(mut data: Vec<u8>, params: Option<&Dictionary>) -> Result<Vec<u8>> {
-        use crate::filters::png;
-
-        if let Some(params) = params {
-            let predictor = params.get(b"Predictor").and_then(Object::as_i64).unwrap_or(1);
-            if (10..=15).contains(&predictor) {
-                let pixels_per_row = params.get(b"Columns").and_then(Object::as_i64).unwrap_or(1) as usize;
-                let colors = params.get(b"Colors").and_then(Object::as_i64).unwrap_or(1) as usize;
-                let bits = params.get(b"BitsPerComponent").and_then(Object::as_i64).unwrap_or(8) as usize;
-                let bytes_per_pixel = colors * bits / 8;
-                data = png::decode_frame(data.as_slice(), bytes_per_pixel, pixels_per_row)?;
-            }
-            Ok(data)
-        } else {
-            Ok(data)
-        }
-    }
-
-    pub fn decompress(&mut self) {
-        if let Ok(data) = self.decompressed_content() {
-            self.dict.remove(b"DecodeParms");
-            self.dict.remove(b"Filter");
-            self.set_content(data);
-        }
-    }
-}
+use crate::{Document, Error, Result};
+use linked_hash_map::{self, Iter, IterMut, LinkedHashMap};
+use log::warn;
+use std::fmt;
+use std::str;
+
+/// Object identifier consists of two parts: object number and generation number.
+pub type ObjectId = (u32, u16);
+
+/// Dictionary object.
+#[derive(Clone, Default)]
+pub struct Dictionary(LinkedHashMap<Vec<u8>, Object>);
+
+/// Stream object
+/// Warning - all streams must be indirect objects, while
+/// the stream dictionary may be a direct object
+#[derive(Debug, Clone)]
+pub struct Stream {
+    /// Associated stream dictionary
+    pub dict: Dictionary,
+    /// Contents of the stream in bytes
+    pub content: Vec<u8>,
+    /// Can the stream be compressed by the `Document::compress()` function?
+    /// Font streams may not be compressed, for example
+    pub allows_compression: bool,
+    /// Stream data's position in PDF file.
+    pub start_position: Option<usize>,
+}
+
+/// Basic PDF object types defined in an enum.
+#[derive(Clone)]
+pub enum Object {
+    Null,
+    Boolean(bool),
+    Integer(i64),
+    Real(f64),
+    Name(Vec<u8>),
+    String(Vec<u8>, StringFormat),
+    Array(Vec<Object>),
+    Dictionary(Dictionary),
+    Stream(Stream),
+    Reference(ObjectId),
+}
+
+/// String objects can be written in two formats.
+#[derive(Debug, Clone)]
+pub enum StringFormat {
+    Literal,
+    Hexadecimal,
+}
+
+impl Default for StringFormat {
+    fn default() -> StringFormat {
+        StringFormat::Literal
+    }
+}
+
+impl From<bool> for Object {
+    fn from(value: bool) -> Self {
+        Object::Boolean(value)
+    }
+}
+
+impl From<i64> for Object {
+    fn from(number: i64) -> Self {
+        Object::Integer(number)
+    }
+}
+
+macro_rules! from_smaller_ints {
+	($( $Int: ty )+) => {
+		$(
+			impl From<$Int> for Object {
+				fn from(number: $Int) -> Self {
+					Object::Integer(i64::from(number))
+				}
+			}
+		)+
+	}
+}
+
+from_smaller_ints! {
+    i8 i16 i32
+    u8 u16 u32
+}
+
+impl From<f64> for Object {
+    fn from(number: f64) -> Self {
+        Object::Real(number)
+    }
+}
+
+impl From<f32> for Object {
+    fn from(number: f32) -> Self {
+        Object::Real(f64::from(number))
+    }
+}
+
+impl From<String> for Object {
+    fn from(name: String) -> Self {
+        Object::Name(name.into_bytes())
+    }
+}
+
+impl<'a> From<&'a str> for Object {
+    fn from(name: &'a str) -> Self {
+        Object::Name(name.as_bytes().to_vec())
+    }
+}
+
+impl From<Vec<Object>> for Object {
+    fn from(array: Vec<Object>) -> Self {
+        Object::Array(array)
+    }
+}
+
+impl From<Dictionary> for Object {
+    fn from(dcit: Dictionary) -> Self {
+        Object::Dictionary(dcit)
+    }
+}
+
+impl From<Stream> for Object {
+    fn from(stream: Stream) -> Self {
+        Object::Stream(stream)
+    }
+}
+
+impl From<ObjectId> for Object {
+    fn from(id: ObjectId) -> Self {
+        Object::Reference(id)
+    }
+}
+
+impl Object {
+    pub fn string_literal<S: Into<Vec<u8>>>(s: S) -> Self {
+        Object::String(s.into(), StringFormat::Literal)
+    }
+
+    pub fn is_null(&self) -> bool {
+        matches!(*self, Object::Null)
+    }
+
+    pub fn as_bool(&self) -> Result<bool> {
+        match *self {
+            Object::Boolean(ref value) => Ok(*value),
+            _ => Err(Error::Type),
+        }
+    }
+
+    pub fn as_i64(&self) -> Result<i64> {
+        match *self {
+            Object::Integer(ref value) => Ok(*value),
+            _ => Err(Error::Type),
+        }
+    }
+
+    pub fn as_f64(&self) -> Result<f64> {
+        match *self {
+            Object::Real(ref value) => Ok(*value),
+            _ => Err(Error::Type),
+        }
+    }
+
+    /// Get the object value as a float.
+    /// Unlike as_f64() this will also cast an Integer to a Real.
+    pub fn as_float(&self) -> Result<f64> {
+        match *self {
+            Object::Integer(ref value) => Ok(*value as f64),
+            Object::Real(ref value) => Ok(*value),
+            _ => Err(Error::Type),
+        }
+    }
+
+    pub fn as_name(&self) -> Result<&[u8]> {
+        match *self {
+            Object::Name(ref name) => Ok(name),
+            _ => Err(Error::Type),
+        }
+    }
+
+    pub fn as_name_str(&self) -> Result<&str> {
+        Ok(str::from_utf8(self.as_name()?)?)
+    }
+
+    pub fn as_str(&self) -> Result<&[u8]> {
+        match self {
+            Object::String(string, _) => Ok(string),
+            _ => Err(Error::Type),
+        }
+    }
+
+    pub fn as_str_mut(&mut self) -> Result<&mut Vec<u8>> {
+        match self {
+            Object::String(string, _) => Ok(string),
+            _ => Err(Error::Type),
+        }
+    }
+
+    pub fn as_reference(&self) -> Result<ObjectId> {
+        match *self {
+            Object::Reference(ref id) => Ok(*id),
+            _ => Err(Error::Type),
+        }
+    }
+
+    pub fn as_array(&self) -> Result<&Vec<Object>> {
+        match *self {
+            Object::Array(ref arr) => Ok(arr),
+            _ => Err(Error::Type),
+        }
+    }
+
+    pub fn as_array_mut(&mut self) -> Result<&mut Vec<Object>> {
+        match *self {
+            Object::Array(ref mut arr) => Ok(arr),
+            _ => Err(Error::Type),
+        }
+    }
+
+    pub fn as_dict(&self) -> Result<&Dictionary> {
+        match *self {
+            Object::Dictionary(ref dict) => Ok(dict),
+            _ => Err(Error::Type),
+        }
+    }
+
+    pub fn as_dict_mut(&mut self) -> Result<&mut Dictionary> {
+        match *self {
+            Object::Dictionary(ref mut dict) => Ok(dict),
+            _ => Err(Error::Type),
+        }
+    }
+
+    pub fn as_stream(&self) -> Result<&Stream> {
+        match *self {
+            Object::Stream(ref stream) => Ok(stream),
+            _ => Err(Error::Type),
+        }
+    }
+
+    pub fn as_stream_mut(&mut self) -> Result<&mut Stream> {
+        match *self {
+            Object::Stream(ref mut stream) => Ok(stream),
+            _ => Err(Error::Type),
+        }
+    }
+
+    pub fn type_name(&self) -> Result<&str> {
+        match *self {
+            Object::Dictionary(ref dict) => dict.type_name(),
+            Object::Stream(ref stream) => stream.dict.type_name(),
+            _ => Err(Error::Type),
+        }
+    }
+}
+
+impl fmt::Debug for Object {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match *self {
+            Object::Null => f.write_str("null"),
+            Object::Boolean(ref value) => {
+                if *value {
+                    f.write_str("true")
+                } else {
+                    f.write_str("false")
+                }
+            }
+            Object::Integer(ref value) => write!(f, "{}", *value),
+            Object::Real(ref value) => write!(f, "{}", *value),
+            Object::Name(ref name) => write!(f, "/{}", String::from_utf8_lossy(name)),
+            Object::String(ref text, _) => write!(f, "({})", String::from_utf8_lossy(text)),
+            Object::Array(ref array) => {
+                let items = array.iter().map(|item| format!("{:?}", item)).collect::<Vec<String>>();
+                write!(f, "[{}]", items.join(" "))
+            }
+            Object::Dictionary(ref dict) => write!(f, "{:?}", dict),
+            Object::Stream(ref stream) => write!(f, "{:?}stream...endstream", stream.dict),
+            Object::Reference(ref id) => write!(f, "{} {} R", id.0, id.1),
+        }
+    }
+}
+
+impl Dictionary {
+    pub fn new() -> Dictionary {
+        Dictionary(LinkedHashMap::new())
+    }
+
+    pub fn has(&self, key: &[u8]) -> bool {
+        self.0.contains_key(key)
+    }
+
+    pub fn get(&self, key: &[u8]) -> Result<&Object> {
+        self.0.get(key).ok_or(Error::DictKey)
+    }
+
+    /// Extract object from dictionary, dereferencing the object if it
+    /// is a reference.
+    pub fn get_deref<'a>(&'a self, key: &[u8], doc: &'a Document) -> Result<&'a Object> {
+        doc.dereference(self.get(key)?).map(|(_, object)| object)
+    }
+
+    pub fn get_mut(&mut self, key: &[u8]) -> Result<&mut Object> {
+        self.0.get_mut(key).ok_or(Error::DictKey)
+    }
+
+    pub fn set<K, V>(&mut self, key: K, value: V)
+    where
+        K: Into<Vec<u8>>,
+        V: Into<Object>,
+    {
+        self.0.insert(key.into(), value.into());
+    }
+
+    pub fn len(&self) -> usize {
+        self.0.len()
+    }
+
+    pub fn is_empty(&self) -> bool {
+        self.0.len() == 0
+    }
+
+    pub fn remove(&mut self, key: &[u8]) -> Option<Object> {
+        self.0.remove(key)
+    }
+
+    pub fn type_name(&self) -> Result<&str> {
+        self.get(b"Type")
+            .and_then(Object::as_name_str)
+            .or_else(|_| self.get(b"Linearized").and(Ok("Linearized")))
+    }
+
+    pub fn type_is(&self, type_name: &[u8]) -> bool {
+        self.get(b"Type").and_then(Object::as_name).ok() == Some(type_name)
+    }
+
+    pub fn iter(&self) -> Iter<'_, Vec<u8>, Object> {
+        self.0.iter()
+    }
+
+    pub fn iter_mut(&mut self) -> IterMut<'_, Vec<u8>, Object> {
+        self.0.iter_mut()
+    }
+
+    pub fn get_font_encoding(&self) -> &str {
+        self.get(b"Encoding")
+            .and_then(Object::as_name_str)
+            .unwrap_or("StandardEncoding")
+    }
+
+    pub fn extend(&mut self, other: &Dictionary) {
+        let keep_both_objects =
+            |new_dict: &mut LinkedHashMap<Vec<u8>, Object>, key: &Vec<u8>, value: &Object, old_value: &Object| {
+                let mut final_array;
+
+                match value {
+                    Object::Array(array) => {
+                        final_array = Vec::with_capacity(array.len() + 1);
+                        final_array.push(old_value.to_owned());
+                        final_array.extend(array.to_owned());
+                    }
+                    _ => {
+                        final_array = vec![value.to_owned(), old_value.to_owned()];
+                    }
+                }
+
+                new_dict.insert(key.to_owned(), Object::Array(final_array));
+            };
+
+        let mut new_dict = LinkedHashMap::with_capacity(other.0.len() + 1);
+
+        for (key, value) in other.0.iter() {
+            if let Some(old_value) = self.0.get(key) {
+                match (old_value, value) {
+                    (Object::Dictionary(old_dict), Object::Dictionary(dict)) => {
+                        let mut replaced_dict = old_dict.to_owned();
+                        replaced_dict.extend(dict);
+
+                        new_dict.insert(key.to_owned(), Object::Dictionary(replaced_dict));
+                    }
+                    (Object::Array(old_array), Object::Array(array)) => {
+                        let mut replaced_array = old_array.to_owned();
+                        replaced_array.extend(array.to_owned());
+
+                        new_dict.insert(key.to_owned(), Object::Array(replaced_array));
+                    }
+                    (Object::Integer(old_id), Object::Integer(id)) => {
+                        let array = vec![Object::Integer(*old_id), Object::Integer(*id)];
+
+                        new_dict.insert(key.to_owned(), Object::Array(array));
+                    }
+                    (Object::Real(old_id), Object::Real(id)) => {
+                        let array = vec![Object::Real(*old_id), Object::Real(*id)];
+
+                        new_dict.insert(key.to_owned(), Object::Array(array));
+                    }
+                    (Object::String(old_ids, old_format), Object::String(ids, format)) => {
+                        let array = vec![
+                            Object::String(old_ids.to_owned(), old_format.to_owned()),
+                            Object::String(ids.to_owned(), format.to_owned()),
+                        ];
+
+                        new_dict.insert(key.to_owned(), Object::Array(array));
+                    }
+                    (Object::Reference(old_object_id), Object::Reference(object_id)) => {
+                        let array = vec![Object::Reference(*old_object_id), Object::Reference(*object_id)];
+
+                        new_dict.insert(key.to_owned(), Object::Array(array));
+                    }
+                    (Object::Null, _) | (Object::Boolean(_), _) | (Object::Name(_), _) | (Object::Stream(_), _) => {
+                        new_dict.insert(key.to_owned(), old_value.to_owned());
+                    }
+                    (_, _) => keep_both_objects(&mut new_dict, key, value, old_value),
+                }
+            } else {
+                new_dict.insert(key.to_owned(), value.to_owned());
+            }
+        }
+
+        self.0 = new_dict;
+    }
+}
+
+#[macro_export]
+macro_rules! dictionary {
+	() => {
+		$crate::Dictionary::new()
+	};
+	($( $key: expr => $value: expr ),+ ,) => {
+		dictionary!( $($key => $value),+ )
+	};
+	($( $key: expr => $value: expr ),*) => {{
+		let mut dict = $crate::Dictionary::new();
+		$(
+			dict.set($key, $value);
+		)*
+		dict
+	}}
+}
+
+impl fmt::Debug for Dictionary {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let entries = self
+            .into_iter()
+            .map(|(key, value)| format!("/{} {:?}", String::from_utf8_lossy(key), value))
+            .collect::<Vec<String>>();
+        write!(f, "<<{}>>", entries.concat())
+    }
+}
+
+impl<'a> IntoIterator for &'a Dictionary {
+    type Item = (&'a Vec<u8>, &'a Object);
+    type IntoIter = linked_hash_map::Iter<'a, Vec<u8>, Object>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        self.0.iter()
+    }
+}
+
+use std::iter::FromIterator;
+impl<K: Into<Vec<u8>>> FromIterator<(K, Object)> for Dictionary {
+    fn from_iter<I: IntoIterator<Item = (K, Object)>>(iter: I) -> Self {
+        let mut dict = Dictionary::new();
+        for (k, v) in iter {
+            dict.set(k, v);
+        }
+        dict
+    }
+}
+
+impl Stream {
+    pub fn new(mut dict: Dictionary, content: Vec<u8>) -> Stream {
+        dict.set("Length", content.len() as i64);
+        Stream {
+            dict,
+            content,
+            allows_compression: true,
+            start_position: None,
+        }
+    }
+
+    pub fn with_position(dict: Dictionary, position: usize) -> Stream {
+        Stream {
+            dict,
+            content: vec![],
+            allows_compression: true,
+            start_position: Some(position),
+        }
+    }
+
+    /// Default is that the stream may be compressed. On font streams,
+    /// set this to false, otherwise the font will be corrupt
+    #[inline]
+    pub fn with_compression(mut self, allows_compression: bool) -> Stream {
+        self.allows_compression = allows_compression;
+        self
+    }
+
+    // Return first filter
+    pub fn filter(&self) -> Result<String> {
+        self.filters()
+            .and_then(|f| f.into_iter().next().ok_or(Error::ObjectNotFound))
+    }
+
+    pub fn filters(&self) -> Result<Vec<String>> {
+        let filter = self.dict.get(b"Filter")?;
+
+        if let Ok(name) = filter.as_name_str() {
+            Ok(vec![name.into()])
+        } else if let Ok(names) = filter.as_array() {
+            // It is an error if a single conversion fails.
+            names
+                .iter()
+                .map(|n| match Object::as_name_str(n) {
+                    Ok(n) => Ok(String::from(n)),
+                    Err(_) => Err(Error::Type),
+                })
+                .collect()
+        } else {
+            Err(Error::Type)
+        }
+    }
+
+    pub fn set_content(&mut self, content: Vec<u8>) {
+        self.content = content;
+        self.dict.set("Length", self.content.len() as i64);
+    }
+
+    pub fn set_plain_content(&mut self, content: Vec<u8>) {
+        self.dict.remove(b"DecodeParms");
+        self.dict.remove(b"Filter");
+        self.dict.set("Length", content.len() as i64);
+        self.content = content;
+    }
+
+    pub fn compress(&mut self) -> Result<()> {
+        use flate2::write::ZlibEncoder;
+        use flate2::Compression;
+        use std::io::prelude::*;
+
+        if self.dict.get(b"Filter").is_err() {
+            let mut encoder = ZlibEncoder::new(Vec::new(), Compression::best());
+            encoder.write_all(self.content.as_slice())?;
+            let compressed = encoder.finish()?;
+            if compressed.len() + 19 < self.content.len() {
+                self.dict.set("Filter", "FlateDecode");
+                self.set_content(compressed);
+            }
+        }
+        Ok(())
+    }
+
+    pub fn decompressed_content(&self) -> Result<Vec<u8>> {
+        let params = self.dict.get(b"DecodeParms").and_then(Object::as_dict).ok();
+        let filters = self.filters()?;
+
+        if self.dict.get(b"Subtype").and_then(Object::as_name_str).ok() == Some("Image") {
+            return Err(Error::Type);
+        }
+
+        let mut input = self.content.as_slice();
+        let mut output = None;
+
+        // Filters are in decoding order.
+        for filter in filters {
+            output = Some(match filter.as_str() {
+                "FlateDecode" => Self::decompress_zlib(input, params)?,
+                "LZWDecode" => Self::decompress_lzw(input, params)?,
+                _ => {
+                    return Err(Error::Type);
+                }
+            });
+            input = output.as_ref().unwrap();
+        }
+
+        output.ok_or(Error::Type)
+    }
+
+    fn decompress_lzw(input: &[u8], params: Option<&Dictionary>) -> Result<Vec<u8>> {
+        use weezl::{decode::Decoder, BitOrder};
+        const MIN_BITS: u8 = 9;
+
+        let early_change = params
+            .and_then(|p| p.get(b"EarlyChange").ok())
+            .and_then(|p| Object::as_i64(p).ok())
+            .map(|v| v != 0)
+            .unwrap_or(true);
+
+        let mut decoder = if early_change {
+            Decoder::with_tiff_size_switch(BitOrder::Msb, MIN_BITS - 1)
+        } else {
+            Decoder::new(BitOrder::Msb, MIN_BITS - 1)
+        };
+
+        let output = Self::decompress_lzw_loop(input, &mut decoder);
+        Self::decompress_predictor(output, params)
+    }
+
+    fn decompress_lzw_loop(input: &[u8], decoder: &mut weezl::decode::Decoder) -> Vec<u8> {
+        let mut output = vec![];
+
+        let result = decoder.into_stream(&mut output).decode_all(input);
+        if let Err(err) = result.status {
+            warn!("{}", err);
+        }
+
+        output
+    }
+
+    fn decompress_zlib(input: &[u8], params: Option<&Dictionary>) -> Result<Vec<u8>> {
+        use flate2::read::ZlibDecoder;
+        use std::io::prelude::*;
+
+        let mut output = Vec::with_capacity(input.len() * 2);
+        let mut decoder = ZlibDecoder::new(input);
+
+        if !input.is_empty() {
+            decoder.read_to_end(&mut output).unwrap_or_else(|err| {
+                warn!("{}", err);
+                0
+            });
+        }
+        Self::decompress_predictor(output, params)
+    }
+
+    fn decompress_predictor(mut data: Vec<u8>, params: Option<&Dictionary>) -> Result<Vec<u8>> {
+        use crate::filters::png;
+
+        if let Some(params) = params {
+            let predictor = params.get(b"Predictor").and_then(Object::as_i64).unwrap_or(1);
+            if (10..=15).contains(&predictor) {
+                let pixels_per_row = params.get(b"Columns").and_then(Object::as_i64).unwrap_or(1) as usize;
+                let colors = params.get(b"Colors").and_then(Object::as_i64).unwrap_or(1) as usize;
+                let bits = params.get(b"BitsPerComponent").and_then(Object::as_i64).unwrap_or(8) as usize;
+                let bytes_per_pixel = colors * bits / 8;
+                data = png::decode_frame(data.as_slice(), bytes_per_pixel, pixels_per_row)?;
+            }
+            Ok(data)
+        } else {
+            Ok(data)
+        }
+    }
+
+    pub fn decompress(&mut self) {
+        if let Ok(data) = self.decompressed_content() {
+            self.dict.remove(b"DecodeParms");
+            self.dict.remove(b"Filter");
+            self.set_content(data);
+        }
+    }
+}
diff --git a/src/writer.rs b/src/writer.rs
index 3e9fce1..279277e 100644
--- a/src/writer.rs
+++ b/src/writer.rs
@@ -78,6 +78,7 @@ impl Document {
                 generation: 0,
             },
         );
+        self.trailer.set("Type", Name(b"XRef".to_vec()));
         // Update `max_id` in trailer
         self.trailer.set("Size", i64::from(self.max_id + 1));
         // Set the size of each entry in bytes (default for PDFs is `[1 2 1]`)
@@ -89,9 +90,13 @@ impl Document {
         let filter = XRefStreamFilter::None;
         let (stream, stream_length, indexes) = Writer::create_xref_steam(xref, filter)?;
         self.trailer.set("Index", indexes);
+
         if filter == XRefStreamFilter::ASCIIHexDecode {
             self.trailer.set("Filter", Name(b"ASCIIHexDecode".to_vec()));
+        } else {
+            self.trailer.remove(b"Filter");
         }
+
         self.trailer.set("Length", stream_length as i64);
 
         let trailer = &self.trailer;
@@ -193,6 +198,7 @@ pub struct Writer;
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
 pub enum XRefStreamFilter {
     ASCIIHexDecode,
+    _FlateDecode, //this is generally a Zlib compressed Stream.
     None,
 }
 


Test patch: