File: PdfDictionary.cs

package info (click to toggle)
pdfmod 0.8.3-1
  • links: PTS, VCS
  • area: main
  • in suites: squeeze
  • size: 5,196 kB
  • ctags: 9,346
  • sloc: cs: 50,590; xml: 1,177; sh: 709; makefile: 640
file content (1557 lines) | stat: -rw-r--r-- 52,889 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
#region PDFsharp - A .NET library for processing PDF
//
// Authors:
//   Stefan Lange (mailto:Stefan.Lange@pdfsharp.com)
//
// Copyright (c) 2005-2008 empira Software GmbH, Cologne (Germany)
//
// http://www.pdfsharp.com
// http://sourceforge.net/projects/pdfsharp
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
// DEALINGS IN THE SOFTWARE.
#endregion

using System;
using System.Diagnostics;
using System.Collections;
using System.Reflection;
using System.Text;
using System.IO;
using PdfSharp.Drawing;
using PdfSharp.Internal;
using PdfSharp.Pdf.IO;
using PdfSharp.Pdf.Filters;
using PdfSharp.Pdf.Advanced;
using PdfSharp.Pdf.Internal;

namespace PdfSharp.Pdf
{
  /// <summary>
  /// Value creation flags. Specifies whether and how a value that not exists is created.
  /// </summary>
  public enum VCF
  {
    /// <summary>
    /// Don't create the value.
    /// </summary>
    None,

    /// <summary>
    /// Create the value as direct object.
    /// </summary>
    Create,

    /// <summary>
    /// Create the value as indirect object.
    /// </summary>
    CreateIndirect,
  }

  /// <summary>
  /// Represents a PDF dictionary object.
  /// </summary>
  [DebuggerDisplay("(pairs={Elements.Count})")]
  public class PdfDictionary : PdfObject, IEnumerable
  {
    /// <summary>
    /// The elemets of the dictionary.
    /// </summary>
    protected DictionaryElements elements;

    /// <summary>
    /// Initializes a new instance of the <see cref="PdfDictionary"/> class.
    /// </summary>
    public PdfDictionary()
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="PdfDictionary"/> class.
    /// </summary>
    /// <param name="document">The document.</param>
    public PdfDictionary(PdfDocument document)
      : base(document)
    {
    }

    /// <summary>
    /// Initializes a new instance from an existing dictionary. Used for object type transformation.
    /// </summary>
    protected PdfDictionary(PdfDictionary dict)
      : base(dict)
    {
      if (dict.elements != null)
        dict.elements.ChangeOwner(this);
      if (dict.stream != null)
        dict.stream.SetOwner(this);
    }

    /// <summary>
    /// Creates a copy of this dictionary. Direct values are deep copied. Indirect references are not
    /// modified.
    /// </summary>
    public new PdfDictionary Clone()
    {
      return (PdfDictionary)Copy();
    }

    /// <summary>
    /// This function is useful for importing objects from external documents. The returned object is not
    /// yet complete. irefs refer to external objects and directed objects are coloned but their document
    /// property is null. A cloned dictionary or array needs a 'fix-up' to be a valid object.
    /// </summary>
    protected override object Copy()
    {
      PdfDictionary dict = (PdfDictionary)base.Copy();
      if (dict.elements != null)
      {
        dict.elements = dict.elements.Clone();
        dict.elements.ChangeOwner(dict);
        PdfName[] names = dict.elements.KeyNames;
        foreach (PdfName name in names)
        {
          PdfObject obj = dict.elements[name] as PdfObject;
          if (obj != null)
          {
            obj = obj.Clone();
            // Recall that obj.Document is now null
            dict.elements[name] = obj;
          }
        }
      }
      if (dict.stream != null)
      {
        dict.stream = dict.stream.Clone();
        dict.stream.SetOwner(dict);
      }
      return dict;
    }

    /// <summary>
    /// Gets the hashtable containing the elements of this dictionary.
    /// </summary>
    public DictionaryElements Elements
    {
      get
      {
        if (this.elements == null)
          this.elements = new DictionaryElements(this);
        return this.elements;
      }
    }

    /// <summary>
    /// Returns an enumerator that iterates through a collection.
    /// </summary>
    public IEnumerator GetEnumerator()
    {
      return Elements.GetEnumerator();
    }

    /// <summary>
    /// Returns a string with the content of this object in a readable form. Useful for debugging purposes only.
    /// </summary>
    public override string ToString()
    {
      // Get keys and sort
      PdfName[] keys = Elements.KeyNames;
      ArrayList list = new ArrayList(keys);
      list.Sort(PdfName.Comparer);
      list.CopyTo(keys, 0);

      StringBuilder pdf = new StringBuilder();
      pdf.Append("<< ");
      foreach (PdfName key in keys)
        pdf.Append(key.ToString() + " " + Elements[key].ToString() + " ");
      pdf.Append(">>");

      return pdf.ToString();
    }

    internal override void WriteObject(PdfWriter writer)
    {
      writer.WriteBeginObject(this);
      //int count = Elements.Count;
      PdfName[] keys = Elements.KeyNames;

#if DEBUG
      // TODO: automatically set length
      if (this.stream != null)
        Debug.Assert(Elements.Contains(PdfDictionary.PdfStream.Keys.Length), "Dictionary has a stream but no length is set.");
#endif

#if DEBUG
      // Sort keys for debugging purposes. Comparing PDF files with for example programms like
      // Araxis Merge is easier with sorted keys.
      if (writer.Layout == PdfWriterLayout.Verbose)
      {
        ArrayList list = new ArrayList(keys);
        list.Sort(PdfName.Comparer);
        list.CopyTo(keys, 0);
      }
#endif

      foreach (PdfName key in keys)
        WriteDictionaryElement(writer, key);
      if (Stream != null)
        WriteDictionaryStream(writer);
      writer.WriteEndObject();
    }

    /// <summary>
    /// Writes a key/value pair of this dictionary. This function is intended to be overridden
    /// in derived classes.
    /// </summary>
    internal virtual void WriteDictionaryElement(PdfWriter writer, PdfName key)
    {
      if (key == null)
        throw new ArgumentNullException("key");
      PdfItem item = Elements[key];
#if DEBUG
      // TODO: simplify PDFsharp
      if (item is PdfObject && ((PdfObject)item).IsIndirect)
      {
        // Replace an indirect object by its Reference
        item = ((PdfObject)item).Reference;
        Debug.Assert(false, "Check when we come here.");
      }
#endif
      key.WriteObject(writer);
      item.WriteObject(writer);
      writer.NewLine();
    }

    /// <summary>
    /// Writes the stream of this dictionary. This function is intended to be overridden
    /// in a derived class.
    /// </summary>
    internal virtual void WriteDictionaryStream(PdfWriter writer)
    {
      writer.WriteStream(this, (writer.Options & PdfWriterOptions.OmitStream) == PdfWriterOptions.OmitStream);
    }

    /// <summary>
    /// Gets or sets the PDF stream belonging to this dictionary. Returns null if the dictionary has
    /// no stream. To create the stream, call the CreateStream function.
    /// </summary>
    public PdfStream Stream
    {
      get { return this.stream; }
      set { this.stream = value; }
    }
    PdfStream stream;

    /// <summary>
    /// Creates the stream of this dictionary and initializes it with the specified byte array.
    /// The function must not be called if the dictionary already has a strem.
    /// </summary>
    public PdfStream CreateStream(byte[] value)
    {
      if (this.stream != null)
        throw new InvalidOperationException("The dictionary already has a stream.");

      this.stream = new PdfStream(value, this);
      // Always set the length
      Elements[PdfStream.Keys.Length] = new PdfInteger(this.stream.Length);
      return this.stream;
    }

    /// <summary>
    /// When overridden in a derived class, gets the KeysMeta of this dictionary type.
    /// </summary>
    internal virtual DictionaryMeta Meta
    {
      get { return null; }
    }

    /// <summary>
    /// Represents the interface to the elements of a PDF dictionary.
    /// </summary>
    public sealed class DictionaryElements : IDictionary, ICloneable
    {
      Hashtable elements;
      PdfDictionary owner;

      internal DictionaryElements(PdfDictionary dict)
      {
        this.elements = new Hashtable();
        this.owner = dict;
      }

      object ICloneable.Clone()
      {
        DictionaryElements elements = (DictionaryElements)MemberwiseClone();
        elements.elements = (Hashtable)elements.elements.Clone();
        elements.owner = null;
        return elements;
      }

      /// <summary>
      /// Creates a shallow copy of this object. The clone is not owned by a dictionary anymore.
      /// </summary>
      public DictionaryElements Clone()
      {
        return (DictionaryElements)((ICloneable)this).Clone();
      }

      /// <summary>
      /// Moves this instance to another dictionary during object type transformation.
      /// </summary>
      internal void ChangeOwner(PdfDictionary dict)
      {
        this.owner = dict;
        //???
        //if (dict.elements != null)
        //  Debug.Assert(dict.elements == this);
        dict.elements = this;
      }

      [Obsolete("Renamed to ChangeOwner for consistency.")]
      internal void SetOwner(PdfDictionary dict)
      {
        ChangeOwner(dict);
      }

      /// <summary>
      /// Gets the dictionary that this elements object belongs to.
      /// </summary>
      internal PdfDictionary Owner
      {
        get { return this.owner; }
      }

      /// <summary>
      /// Converts the specified value to boolean.
      /// If the value not exists, the function returns false.
      /// If the value is not convertible, the function throws an InvalidCastException.
      /// </summary>
      public bool GetBoolean(string key, bool create)
      {
        object obj = this[key];
        if (obj == null)
        {
          if (create)
            this[key] = new PdfBoolean();
          return false;
        }
        if (obj is PdfReference)
          obj = ((PdfReference)obj).Value;

        if (obj is PdfBoolean)
          return ((PdfBoolean)obj).Value;
        else if (obj is PdfBooleanObject)
          return ((PdfBooleanObject)obj).Value;
        throw new InvalidCastException("GetBoolean: Object is not a boolean.");
      }

      /// <summary>
      /// Converts the specified value to boolean.
      /// If the value not exists, the function returns false.
      /// If the value is not convertible, the function throws an InvalidCastException.
      /// </summary>
      public bool GetBoolean(string key)
      {
        return GetBoolean(key, false);
      }

      /// <summary>
      /// Sets the entry to a direct boolean value.
      /// </summary>
      public void SetBoolean(string key, bool value)
      {
        this[key] = new PdfBoolean(value);
      }

      /// <summary>
      /// Converts the specified value to integer.
      /// If the value not exists, the function returns 0.
      /// If the value is not convertible, the function throws an InvalidCastException.
      /// </summary>
      public int GetInteger(string key, bool create)
      {
        object obj = this[key];
        if (obj == null)
        {
          if (create)
            this[key] = new PdfInteger();
          return 0;
        }
        if (obj is PdfReference)
          obj = ((PdfReference)obj).Value;

        if (obj is PdfInteger)
          return ((PdfInteger)obj).Value;
        if (obj is PdfIntegerObject)
          return ((PdfIntegerObject)obj).Value;
        throw new InvalidCastException("GetInteger: Object is not an integer.");
      }

      /// <summary>
      /// Converts the specified value to integer.
      /// If the value not exists, the function returns 0.
      /// If the value is not convertible, the function throws an InvalidCastException.
      /// </summary>
      public int GetInteger(string key)
      {
        return GetInteger(key, false);
      }

      /// <summary>
      /// Sets the entry to a direct integer value.
      /// </summary>
      public void SetInteger(string key, int value)
      {
        this[key] = new PdfInteger(value);
      }

      /// <summary>
      /// Converts the specified value to double.
      /// If the value not exists, the function returns 0.
      /// If the value is not convertible, the function throws an InvalidCastException.
      /// </summary>
      public double GetReal(string key, bool create)
      {
        object obj = this[key];
        if (obj == null)
        {
          if (create)
            this[key] = new PdfReal();
          return 0;
        }
        if (obj is PdfReference)
          obj = ((PdfReference)obj).Value;

        if (obj is PdfReal)
          return ((PdfReal)obj).Value;
        else if (obj is PdfRealObject)
          return ((PdfRealObject)obj).Value;
        else if (obj is PdfInteger)
          return ((PdfInteger)obj).Value;
        else if (obj is PdfIntegerObject)
          return ((PdfIntegerObject)obj).Value;
        throw new InvalidCastException("GetReal: Object is not a number.");
      }

      /// <summary>
      /// Converts the specified value to double.
      /// If the value not exists, the function returns 0.
      /// If the value is not convertible, the function throws an InvalidCastException.
      /// </summary>
      public double GetReal(string key)
      {
        return GetReal(key, false);
      }

      /// <summary>
      /// Sets the entry to a direct double value.
      /// </summary>
      public void SetReal(string key, double value)
      {
        this[key] = new PdfReal(value);
      }

      /// <summary>
      /// Converts the specified value to String.
      /// If the value not exists, the function returns the empty string.
      /// </summary>
      public string GetString(string key, bool create)
      {
        object obj = this[key];
        if (obj == null)
        {
          if (create)
            this[key] = new PdfString();
          return "";
        }
        if (obj is PdfReference)
          obj = ((PdfReference)obj).Value;

        if (obj is PdfString)
          return ((PdfString)obj).Value;
        else if (obj is PdfStringObject)
          return ((PdfStringObject)obj).Value;
        else if (obj is PdfName)
          return ((PdfName)obj).Value;
        else if (obj is PdfNameObject)
          return ((PdfNameObject)obj).Value;
        throw new InvalidCastException("GetString: Object is not a string.");
      }

      /// <summary>
      /// Converts the specified value to String.
      /// If the value not exists, the function returns the empty string.
      /// </summary>
      public string GetString(string key)
      {
        return GetString(key, false);
      }

      /// <summary>
      /// Sets the entry to a direct string value.
      /// </summary>
      public void SetString(string key, string value)
      {
        this[key] = new PdfString(value);
      }

      /// <summary>
      /// Converts the specified value to a name.
      /// If the value not exists, the function returns the empty string.
      /// </summary>
      public string GetName(string key)
      {
        object obj = this[key];
        if (obj == null)
        {
          //if (create)
          //  this[key] = new Pdf();
          return String.Empty;
        }
        if (obj is PdfReference)
          obj = ((PdfReference)obj).Value;

        if (obj is PdfName)
          return ((PdfName)obj).Value;
        else if (obj is PdfNameObject)
          return ((PdfNameObject)obj).Value;

        throw new InvalidCastException("GetName: Object is not a name.");
      }

      /// <summary>
      /// Sets the specified name value.
      /// If the value doesn't start with a slash, it is added automatically.
      /// </summary>
      public void SetName(string key, string value)
      {
        if (value == null)
          throw new ArgumentNullException("value");

        if (value.Length == 0 || value[0] != '/')
          value = "/" + value;

        this[key] = new PdfName(value);
      }

      /// <summary>
      /// Converts the specified value to PdfRectangle.
      /// If the value not exists, the function returns an empty rectangle.
      /// If the value is not convertible, the function throws an InvalidCastException.
      /// </summary>
      public PdfRectangle GetRectangle(string key, bool create)
      {
        PdfRectangle value = new PdfRectangle();
        object obj = this[key];
        if (obj == null)
        {
          if (create)
            this[key] = value = new PdfRectangle();
          return value;
        }
        if (obj is PdfReference)
          obj = ((PdfReference)obj).Value;

        PdfArray array = obj as PdfArray;
        if (array != null && array.Elements.Count == 4)
        {
          value = new PdfRectangle(array.Elements.GetReal(0), array.Elements.GetReal(1),
            array.Elements.GetReal(2), array.Elements.GetReal(3));
          this[key] = value;
        }
        else
          value = (PdfRectangle)obj;
        return value;
      }

      /// <summary>
      /// Converts the specified value to PdfRectangle.
      /// If the value not exists, the function returns an empty rectangle.
      /// If the value is not convertible, the function throws an InvalidCastException.
      /// </summary>
      public PdfRectangle GetRectangle(string key)
      {
        return GetRectangle(key, false);
      }

      /// <summary>
      /// Sets the entry to a direct rectangle value, represented by an array with four values.
      /// </summary>
      public void SetRectangle(string key, PdfRectangle rect)
      {
        this.elements[key] = rect;
      }

      /// Converts the specified value to XMatrix.
      /// If the value not exists, the function returns an identity matrix.
      /// If the value is not convertible, the function throws an InvalidCastException.
      public XMatrix GetMatrix(string key, bool create)
      {
        XMatrix value = new XMatrix();
        object obj = this[key];
        if (obj == null)
        {
          if (create)
            this[key] = new PdfLiteral("[1 0 0 1 0 0]");  // cannot be parsed, implement a PdfMatrix...
          return value;
        }
        if (obj is PdfReference)
          obj = ((PdfReference)obj).Value;

        PdfArray array = obj as PdfArray;
        if (array != null && array.Elements.Count == 6)
        {
          value = new XMatrix(array.Elements.GetReal(0), array.Elements.GetReal(1), array.Elements.GetReal(2),
            array.Elements.GetReal(3), array.Elements.GetReal(4), array.Elements.GetReal(5));
        }
        else if (obj is PdfLiteral)
        {
          throw new NotImplementedException("Parsing matrix from literal.");
        }
        else
          throw new InvalidCastException("Element is not an array with 6 values.");
        return value;
      }

      /// Converts the specified value to XMatrix.
      /// If the value not exists, the function returns an identity matrix.
      /// If the value is not convertible, the function throws an InvalidCastException.
      public XMatrix GetMatrix(string key)
      {
        return GetMatrix(key, false);
      }

      /// <summary>
      /// Sets the entry to a direct matrix value, represented by an array with six values.
      /// </summary>
      public void SetMatrix(string key, XMatrix matrix)
      {
        this.elements[key] = PdfLiteral.FromMatrix(matrix);
      }

      /// <summary>
      /// Converts the specified value to DateTime.
      /// If the value not exists, the function returns the specified default value.
      /// If the value is not convertible, the function throws an InvalidCastException.
      /// </summary>
      public DateTime GetDateTime(string key, DateTime defaultValue)
      {
        object obj = this[key];
        if (obj == null)
        {
          //if (create)
          //  this[key] = new Pdf();
          return defaultValue;
        }
        if (obj is PdfReference)
          obj = ((PdfReference)obj).Value;

        if (obj is PdfDate)
          return ((PdfDate)obj).Value;

        string date = "";
        if (obj is PdfString)
          date = ((PdfString)obj).Value;
        else if (obj is PdfStringObject)
          date = ((PdfStringObject)obj).Value;
        else
          throw new InvalidCastException("GetName: Object is not a name.");

        if (date != "")
        {
          try
          {
            defaultValue = Parser.ParseDateTime(date, defaultValue);
          }
          catch { }
        }
        return defaultValue;
      }

      /// <summary>
      /// Sets the entry to a direct datetime value.
      /// </summary>
      public void SetDateTime(string key, DateTime value)
      {
        this.elements[key] = new PdfDate(value);
      }

      internal int GetEnumFromName(string key, object defaultValue, bool create)
      {
        if (!(defaultValue is Enum))
          throw new ArgumentException("defaultValue");

        object obj = this[key];
        if (obj == null)
        {
          if (create)
            this[key] = new PdfName(defaultValue.ToString());
          return (int)defaultValue;
        }
        Debug.Assert(obj is Enum);
        return (int)Enum.Parse(defaultValue.GetType(), obj.ToString().Substring(1));
      }

      internal int GetEnumFromName(string key, object defaultValue)
      {
        return GetEnumFromName(key, defaultValue, false);
      }

      internal void SetEnumAsName(string key, object value)
      {
        if (!(value is Enum))
          throw new ArgumentException("value");
        this.elements[key] = new PdfName("/" + value.ToString());
      }

      /// <summary>
      /// Gets the value for the specified key. If the value does not exists, it is optionally created.
      /// </summary>
      public PdfItem GetValue(string key, VCF options)
      {
        PdfObject obj;
        PdfDictionary dict;
        PdfArray array;
        PdfReference iref;
        PdfItem value = this[key];
        if (value == null)
        {
          if (options != VCF.None)
          {
            Type type = GetValueType(key);
            if (type != null)
            {
              Debug.Assert(typeof(PdfItem).IsAssignableFrom(type), "Type not allowed.");
              if (typeof(PdfDictionary).IsAssignableFrom(type))
              {
                value = obj = CreateDictionary(type, null);
              }
              else if (typeof(PdfArray).IsAssignableFrom(type))
              {
                value = obj = CreateArray(type, null);
              }
              else
                throw new NotImplementedException("Type other than array or dictionary.");

              if (options == VCF.CreateIndirect)
              {
                this.owner.Owner.irefTable.Add(obj);
                this[key] = obj.Reference;
              }
              else
                this[key] = obj;
            }
            else
              throw new NotImplementedException("Cannot create value for key: " + key);
          }
        }
        else
        {
          // The value exists and can returned. But for imported documents check for neccessary
          // object type transformation.
          if ((iref = value as PdfReference) != null)
          {
            // Case: value is an indirect reference
            value = iref.Value;
            if (value == null)
            {
              // If we come here PDF file is currupt
              throw new InvalidOperationException("Indirect reference without value.");
            }
            else
            {
              if (true) // || this.owner.Document.IsImported)
              {
                Type type = GetValueType(key);
                Debug.Assert(type != null, "No value type specified in meta information. Please send this file to PDFsharp support.");

                if (type != null && type != value.GetType())
                {
                  if (typeof(PdfDictionary).IsAssignableFrom(type))
                  {
                    Debug.Assert(value is PdfDictionary, "Bug in PDFsharp. Please send this file to PDFsharp support.");
                    value = CreateDictionary(type, (PdfDictionary)value);
                  }
                  else if (typeof(PdfArray).IsAssignableFrom(type))
                  {
                    Debug.Assert(value is PdfArray, "Bug in PDFsharp. Please send this file to PDFsharp support.");
                    value = CreateArray(type, (PdfArray)value);
                  }
                  else
                    throw new NotImplementedException("Type other than array or dictionary.");
                }
              }
            }
            return value;
          }
          // Transformation is only possible after PDF import.
          if (true) // || this.owner.Document.IsImported)
          {
            // Case: value is a direct object
            if ((dict = value as PdfDictionary) != null)
            {
              Type type = GetValueType(key);
              Debug.Assert(type != null, "No value type specified in meta information. Please send this file to PDFsharp support.");
              if (dict.GetType() != type)
                dict = CreateDictionary(type, dict);
              return dict;
            }
            else if ((array = value as PdfArray) != null)
            {
              Type type = GetValueType(key);
              Debug.Assert(type != null, "No value type specified in meta information. Please send this file to PDFsharp support.");
              if (array.GetType() != type)
                array = CreateArray(type, array);
              return array;
            }
          }
        }
        return value;
      }

      /// <summary>
      /// Short cut for GetValue(key, VCF.None).
      /// </summary>
      public PdfItem GetValue(string key)
      {
        return GetValue(key, VCF.None);
      }

      /// <summary>
      /// Returns the type of the object to be created as value of the specified key.
      /// </summary>
      Type GetValueType(string key)  // TODO: move to PdfObject
      {
        Type type = null;
        DictionaryMeta meta = this.owner.Meta;
        if (meta != null)
        {
          KeyDescriptor kd = meta[key];
          if (kd != null)
            type = kd.GetValueType();
          else
            Debug.WriteLine("Warning: Key not desciptor table: " + key);  // TODO: check what this means...
        }
        else
          Debug.WriteLine("Warning: No meta provided for type: " + this.owner.GetType().Name);  // TODO: check what this means...
        return type;
      }

      PdfArray CreateArray(Type type, PdfArray oldArray)
      {
        ConstructorInfo ctorInfo;
        PdfArray array;
        if (oldArray == null)
        {
          // Use contstructor with signature 'Ctor(PdfDocument owner)'.
          ctorInfo = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
            null, new Type[] { typeof(PdfDocument) }, null);
          Debug.Assert(ctorInfo != null, "No appropriate constructor found for type: " + type.Name);
          array = ctorInfo.Invoke(new object[] { this.owner.Owner }) as PdfArray;
        }
        else
        {
          // Use contstructor with signature 'Ctor(PdfDictionary dict)'.
          ctorInfo = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
            null, new Type[] { typeof(PdfArray) }, null);
          Debug.Assert(ctorInfo != null, "No appropriate constructor found for type: " + type.Name);
          array = ctorInfo.Invoke(new object[] { oldArray }) as PdfArray;
        }
        return array;
      }

      PdfDictionary CreateDictionary(Type type, PdfDictionary oldDictionary)
      {
        ConstructorInfo ctorInfo;
        PdfDictionary dict;
        if (oldDictionary == null)
        {
          // Use contstructor with signature 'Ctor(PdfDocument owner)'.
          ctorInfo = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
            null, new Type[] { typeof(PdfDocument) }, null);
          Debug.Assert(ctorInfo != null, "No appropriate constructor found for type: " + type.Name);
          dict = ctorInfo.Invoke(new object[] { this.owner.Owner }) as PdfDictionary;
        }
        else
        {
          // Use contstructor with signature 'Ctor(PdfDictionary dict)'.
          ctorInfo = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
            null, new Type[] { typeof(PdfDictionary) }, null);
          Debug.Assert(ctorInfo != null, "No appropriate constructor found for type: " + type.Name);
          dict = ctorInfo.Invoke(new object[] { oldDictionary }) as PdfDictionary;
        }
        return dict;
      }

      PdfItem CreateValue(Type type, PdfDictionary oldValue)
      {
        ConstructorInfo ctorInfo = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
          null, new Type[] { typeof(PdfDocument) }, null);
        PdfObject obj = ctorInfo.Invoke(new object[] { this.owner.Owner }) as PdfObject;
        if (oldValue != null)
        {
          obj.Reference = oldValue.Reference;
          obj.Reference.Value = obj;
          if (obj is PdfDictionary)
          {
            PdfDictionary dict = (PdfDictionary)obj;
            dict.elements = ((PdfDictionary)oldValue).elements;
          }
        }
        return obj;
      }

      /// <summary>
      /// Sets the entry with the specified value. DON'T USE THIS FUNCTION - IT MAY BE REMOVED.
      /// </summary>
      public void SetValue(string key, PdfItem value)
      {
        Debug.Assert((value is PdfObject && ((PdfObject)value).Reference == null) | !(value is PdfObject),
          "You try to set an indirect object directly into a dictionary.");

        // HACK?
        this.elements[key] = value;
      }

      /// <summary>
      /// Returns the indirect object if the value of the specified key is a PdfReference.
      /// </summary>
      [Obsolete("Use GetObject, GetDictionary, GetArray, or GetReference")]
      public PdfObject GetIndirectObject(string key)
      {
        PdfItem item = this[key];
        if (item is PdfReference)
          return ((PdfReference)item).Value;
        return null;
      }

      /// <summary>
      /// Gets the PdfObject with the specified key, or null, if no such object exists. If the key refers to
      /// a reference, the referenced PdfObject is returned.
      /// </summary>
      public PdfObject GetObject(string key)
      {
        PdfItem item = this[key];
        if (item is PdfReference)
          return ((PdfReference)item).Value;
        return item as PdfObject;
      }

      /// <summary>
      /// Gets the PdfArray with the specified key, or null, if no such object exists. If the key refers to
      /// a reference, the referenced PdfDictionary is returned.
      /// </summary>
      public PdfDictionary GetDictionary(string key)
      {
        return GetObject(key) as PdfDictionary;
      }

      /// <summary>
      /// Gets the PdfArray with the specified key, or null, if no such object exists. If the key refers to
      /// a reference, the referenced PdfArray is returned.
      /// </summary>
      public PdfArray GetArray(string key)
      {
        return GetObject(key) as PdfArray;
      }

      /// <summary>
      /// Gets the PdfReference with the specified key, or null, if no such object exists.
      /// </summary>
      public PdfReference GetReference(string key)
      {
        PdfItem item = this[key];
        return item as PdfReference;
      }

      /// <summary>
      /// Sets the entry to the specified object. The object must not be an indirect object,
      /// otherwise an exception is raised.
      /// </summary>
      public void SetObject(string key, PdfObject obj)
      {
        if (obj.Reference != null)
          throw new ArgumentException("PdfObject must not be an indirect object.", "obj");
        this[key] = obj;
      }

      /// <summary>
      /// Sets the entry as a reference to the specified object. The object must be an indirect object,
      /// otherwise an exception is raised.
      /// </summary>
      public void SetReference(string key, PdfObject obj)
      {
        if (obj.Reference == null)
          throw new ArgumentException("PdfObject must be an indirect object.", "obj");
        this[key] = obj.Reference;
      }

      #region IDictionary Members

      /// <summary>
      /// Gets a value indicating whether the <see cref="T:System.Collections.IDictionary"></see> object is read-only.
      /// </summary>
      public bool IsReadOnly
      {
        get { return false; }
      }

      /// <summary>
      /// Returns an <see cref="T:System.Collections.IDictionaryEnumerator"></see> object for the <see cref="T:System.Collections.IDictionary"></see> object.
      /// </summary>
      public IDictionaryEnumerator GetEnumerator()
      {
        return this.elements.GetEnumerator();
      }

      object IDictionary.this[object key]
      {
        get { return this.elements[key]; }
        set
        {
          if (key == null)
            throw new ArgumentNullException("key");
          if (!(key is string))
            throw new ArgumentException("Key must be of type System.String.");
          if (((string)key) == "")
            throw new ArgumentException(PSSR.NameMustStartWithSlash, "key");
          if (((string)key)[0] != '/')
            throw new ArgumentException(PSSR.NameMustStartWithSlash, "key");

          this.elements[key] = value;
        }
      }

      /// <summary>
      /// Gets or sets an entry in the dictionary. The specified key must be a valid PDF name
      /// starting with a slash '/'. This property provides full access to the elements of the
      /// PDF dictionary. Wrong use can lead to errors or corrupt PDF files.
      /// </summary>
      public PdfItem this[string key]
      {
        get { return (PdfItem)((IDictionary)this)[key]; }
        set
        {
          if (value == null)
            throw new ArgumentNullException("value");
#if DEBUG
          if (key == "/MediaBox")
            key.GetType();

          //if (value is PdfObject)
          //{
          //  PdfObject obj = (PdfObject)value;
          //  if (obj.Reference != null)
          //    throw new ArgumentException("An object with an indirect reference cannot be a direct value. Try to set an indirect refernece.");
          //}
          if (value is PdfDictionary)
          {
            PdfDictionary dict = (PdfDictionary)value;
            if (dict.stream != null)
              throw new ArgumentException("A dictionary with stream cannot be a direct value.");
          }
#endif
          PdfObject obj = value as PdfObject;
          if (obj != null && obj.IsIndirect)
          {
            ((IDictionary)this)[key] = obj.Reference;
            return;
          }
          ((IDictionary)this)[key] = value;
        }
      }

      /// <summary>
      /// Gets or sets an entry in the dictionary identified by a PdfName object.
      /// </summary>
      public PdfItem this[PdfName key]
      {
        get { return (PdfItem)((IDictionary)this)[key.Value]; }
        set 
        {
          if (value == null)
            throw new ArgumentNullException("value");

#if DEBUG
          if (value is PdfDictionary)
          {
            PdfDictionary dict = (PdfDictionary)value;
            if (dict.stream != null)
              throw new ArgumentException("A dictionary with stream cannot be a direct value.");
          }
#endif

          PdfObject obj = value as PdfObject;
          if (obj != null && obj.IsIndirect)
          {
            ((IDictionary)this)[key] = obj.Reference;
            return;
          }
          ((IDictionary)this)[key.Value] = value; 
        }
      }

      void IDictionary.Remove(object key)
      {
        this.elements.Remove(key);
      }

      /// <summary>
      /// Removes the value with the specified key.
      /// </summary>
      public void Remove(string key)
      {
        this.elements.Remove(key);
      }

      bool IDictionary.Contains(object key)
      {
        return this.elements.Contains(key);
      }

      /// <summary>
      /// Determines whether the dictionary contains the specified name.
      /// </summary>
      public bool Contains(string key)
      {
        return this.elements.Contains(key);
      }

      /// <summary>
      /// Removes all elements from the dictionary.
      /// </summary>
      public void Clear()
      {
        this.elements.Clear();
      }

      void IDictionary.Add(object key, object value)
      {
        if (key == null)
          throw new ArgumentNullException("key");
        if (key is PdfName)
          key = (key as PdfName).Value;
        if (!(key is string))
          throw new ArgumentException("key must be of type System.String.");
        if (((string)key) == "")
          throw new ArgumentException("key");
        if (((string)key)[0] != '/')
          throw new ArgumentException("The key must start with a slash '/'.");

        // If object is indirect automatically convert value to reference.
        PdfObject obj = value as PdfObject;
        if (obj != null && obj.IsIndirect)
          value = obj.Reference;

        this.elements.Add(key, value);
      }

      /// <summary>
      /// Adds the specified value to the dictionary.
      /// </summary>
      public void Add(object key, PdfItem value)
      {
        ((IDictionary)this).Add(key, value);
      }

      ICollection IDictionary.Keys
      {
        get { return this.elements.Keys; }
      }

      /// <summary>
      /// Gets all keys currently in use in this dictionary as an array of PdfName objects.
      /// </summary>
      public PdfName[] KeyNames
      {
        get
        {
          ICollection values = this.elements.Keys;
          int count = values.Count;
          string[] strings = new string[count];
          values.CopyTo(strings, 0);
          PdfName[] names = new PdfName[count];
          for (int idx = 0; idx < count; idx++)
            names[idx] = new PdfName(strings[idx]);
          return names;
        }
      }

      /// <summary>
      /// Get all keys currently in use in this dictionary as an array of string objects.
      /// </summary>
      public string[] Keys
      {
        get
        {
          ICollection values = this.elements.Keys;
          int count = values.Count;
          string[] keys = new string[count];
          values.CopyTo(keys, 0);
          return keys;
        }
      }

      ICollection IDictionary.Values
      {
        get { return this.elements.Values; }
      }

      /// <summary>
      /// Gets all values currently in use in this dictionary as an array of PdfItem objects.
      /// </summary>
      public PdfItem[] Values
      {
        get
        {
          ICollection values = this.elements.Values;
          PdfItem[] items = new PdfItem[values.Count];
          values.CopyTo(items, 0);
          return items;
        }
      }

      /// <summary>
      /// Return false.
      /// </summary>
      public bool IsFixedSize
      {
        get { return false; }
      }

      #endregion

      #region ICollection Members

      /// <summary>
      /// Return false.
      /// </summary>
      public bool IsSynchronized
      {
        get { return false; }
      }

      /// <summary>
      /// Gets the number of elements contained in the dictionary.
      /// </summary>
      public int Count
      {
        get { return this.elements.Count; }
      }

      /// <summary>
      /// Copies the elements of the elementes of the dictionary to an array, starting at a particular index.
      /// </summary>
      /// <param name="array">The one-dimensional array that is the destination of the elements copied from.</param>
      /// <param name="index">The zero-based index in array at which copying begins.</param>
      public void CopyTo(Array array, int index)
      {
        this.elements.CopyTo(array, index);
      }

      /// <summary>
      /// The current implementation returns null.
      /// </summary>
      public object SyncRoot
      {
        get { return null; }
      }

      #endregion

      #region IEnumerable Members

      IEnumerator System.Collections.IEnumerable.GetEnumerator()
      {
        return ((ICollection)this.elements).GetEnumerator();
      }

      #endregion
    }

    /// <summary>
    /// The PDF stream objects.
    /// </summary>
    public sealed class PdfStream
    {
      /// <summary>
      /// The dictionary the stream belongs to.
      /// </summary>
      PdfDictionary owner;

      internal PdfStream(PdfDictionary owner)
      {
        if (owner == null)
          throw new ArgumentNullException("owner");
        this.owner = owner;
      }

      /// <summary>
      /// A .NET string can contain char(0) as a valid character.
      /// </summary>
      internal PdfStream(byte[] value, PdfDictionary owner)
        : this(owner)
      {
        this.value = value;
      }

      /// <summary>
      /// Clones this stream by creating a deep copy.
      /// </summary>
      public PdfStream Clone()
      {
        PdfStream stream = (PdfStream)MemberwiseClone();
        stream.owner = null;
        if (stream.value != null)
        {
          stream.value = new byte[stream.value.Length];
          this.value.CopyTo(stream.value, 0);
        }
        return stream;
      }

      /// <summary>
      /// Moves this instance to another dictionary during object type transformation.
      /// </summary>
      internal void SetOwner(PdfDictionary dict)
      {
        this.owner = dict;
        owner.stream = this;
      }

      /// <summary>
      /// Gets the length of the stream, i.e. the actual number of bytes in the stream.
      /// </summary>
      public int Length
      {
        get { return this.value != null ? value.Length : 0; }
      }

      //    /// <summary>
      //    /// Gets the native length of the stream, i.e. the number of bytes when they are neither
      //    /// compressed nor encrypted.
      //    /// </summary>
      //    public int Length
      //    {
      //      get {return this.length;}
      //    }

      /// <summary>
      /// Get or sets the bytes of the stream as they are, i.e. if one or more filters exists the bytes are
      /// not unfiltered.
      /// </summary>
      public byte[] Value
      {
        get { return this.value; }
        set
        {
          if (value == null)
            throw new ArgumentNullException("value");
          this.value = value;
          this.owner.Elements.SetInteger(Keys.Length, value.Length);
        }
      }
      byte[] value;

      /// <summary>
      /// Gets the value of the stream unfiltered. The stream content is not modified by this operation.
      /// </summary>
      public byte[] UnfilteredValue
      {
        get
        {
          byte[] bytes = null;
          if (this.value != null)
          {
            PdfItem filter = this.owner.Elements["/Filter"];
            if (filter != null)
            {
              bytes = Filtering.Decode(this.value, filter);
              if (bytes == null)
              {
                string message = String.Format("Cannot decode filter '{0}'", filter.ToString());
                bytes = PdfEncoders.RawEncoding.GetBytes(message);
              }
            }
            else
            {
              bytes = new byte[this.value.Length];
              this.value.CopyTo(bytes, 0);
            }
          }
          return bytes == null ? new byte[0] : bytes;
        }
      }

      /// <summary>
      /// Tries the unfilter the bytes of the stream. If the stream is filtered and PDFsharp knows the filter
      /// algorithm, the stream content is replaced by its unfiltered value and the function return true.
      /// Otherwise the content keeps untouched and the function returns flase.
      /// The funktion is useful for analysing existing PDF files.
      /// </summary>
      public bool TryUnfilter()
      {
        byte[] bytes = null;
        if (this.value != null)
        {
          PdfItem filter = this.owner.Elements["/Filter"];
          if (filter != null)
          {
            // PDFsharp can only uncompress streams that are compressed with
            // the ZIP or LHZ algorithm.
            bytes = Filtering.Decode(this.value, filter);
            if (bytes != null)
            {
              this.owner.Elements.Remove(Keys.Filter);
              Value = bytes;
            }
            else
              return false;
          }
        }
        return true;
      }

      /// <summary>
      /// Compresses the stream with the FlateDecode filter.
      /// If a filter is already defined, the function has no effect.
      /// </summary>
      public void Zip()
      {
        if (this.value == null)
          return;

        if (!this.owner.Elements.Contains("/Filter"))
        {
          this.value = Filtering.FlateDecode.Encode(this.value);
          this.owner.Elements["/Filter"] = new PdfName("/FlateDecode");
          this.owner.Elements["/Length"] = new PdfInteger(this.value.Length);
        }
      }

      /// <summary>
      /// Returns the strem content a raw string.
      /// </summary>
      public override string ToString()
      {
        if (this.value == null)
          return "null";

        string stream = "";
        PdfItem filter = this.owner.Elements["/Filter"];
        if (filter != null)
        {
#if true
          byte[] bytes = Filtering.Decode(this.value, filter);
          if (bytes != null)
            stream = PdfEncoders.RawEncoding.GetString(bytes);
#else

          if (this.owner.Elements.GetString("/Filter") == "/FlateDecode")
          {
            stream = Filtering.FlateDecode.DecodeToString(this.value);
          }
#endif
          else
            throw new NotImplementedException("Unknown filter");
        }
        else
          stream = PdfEncoders.RawEncoding.GetString(this.value);

        return stream;
      }

      //internal void WriteObject_(Stream stream)
      //{
      //  if (this.value != null)
      //    stream.Write(this.value, 0, this.value.Length);
      //}

      /// <summary>
      /// Common keys for all streams.
      /// </summary>
      public class Keys : KeysBase
      {
        /// <summary>
        /// (Required) The number of bytes from the beginning of the line following the keyword
        /// stream to the last byte just before the keywordendstream. (There may be an additional
        /// EOL marker, preceding endstream, that is not included in the count and is not logically
        /// part of the stream data.)
        /// </summary>
        [KeyInfo(KeyType.Integer | KeyType.Required)]
        public const string Length = "/Length";

        /// <summary>
        /// (Optional) The name of a filter to be applied in processing the stream data found between
        /// the keywords stream and endstream, or an array of such names. Multiple filters should be
        /// specified in the order in which they are to be applied.
        /// </summary>
        [KeyInfo(KeyType.NameOrArray | KeyType.Optional)]
        public const string Filter = "/Filter";

        /// <summary>
        /// (Optional) A parameter dictionary or an array of such dictionaries, used by the filters
        /// specified by Filter. If there is only one filter and that filter has parameters, DecodeParms
        /// must be set to the filters parameter dictionary unless all the filters parameters have
        /// their default values, in which case the DecodeParms entry may be omitted. If there are 
        /// multiple filters and any of the filters has parameters set to nondefault values, DecodeParms
        /// must be an array with one entry for each filter: either the parameter dictionary for that
        /// filter, or the null object if that filter has no parameters (or if all of its parameters have
        /// their default values). If none of the filters have parameters, or if all their parameters
        /// have default values, the DecodeParms entry may be omitted.
        /// </summary>
        [KeyInfo(KeyType.ArrayOrDictionary | KeyType.Optional)]
        public const string DecodeParms = "/DecodeParms";

        /// <summary>
        /// (Optional; PDF 1.2) The file containing the stream data. If this entry is present, the bytes
        /// between stream and endstream are ignored, the filters are specified by FFilter rather than
        /// Filter, and the filter parameters are specified by FDecodeParms rather than DecodeParms.
        /// However, the Length entry should still specify the number of those bytes. (Usually, there are
        /// no bytes and Length is 0.)
        /// </summary>
        [KeyInfo("1.2", KeyType.String | KeyType.Optional)]
        public const string F = "/F";

        /// <summary>
        /// (Optional; PDF 1.2) The name of a filter to be applied in processing the data found in the
        /// streams external file, or an array of such names. The same rules apply as for Filter.
        /// </summary>
        [KeyInfo("1.2", KeyType.NameOrArray | KeyType.Optional)]
        public const string FFilter = "/FFilter";

        /// <summary>
        /// (Optional; PDF 1.2) A parameter dictionary, or an array of such dictionaries, used by the
        /// filters specified by FFilter. The same rules apply as for DecodeParms.
        /// </summary>
        [KeyInfo("1.2", KeyType.ArrayOrDictionary | KeyType.Optional)]
        public const string FDecodeParms = "/FDecodeParms";

        /// <summary>
        /// Optional; PDF 1.5) A non-negative integer representing the number of bytes in the decoded
        /// (defiltered) stream. It can be used to determine, for example, whether enough disk space is
        /// available to write a stream to a file.
        /// This value should be considered a hint only; for some stream filters, it may not be possible
        /// to determine this value precisely.
        /// </summary>
        [KeyInfo("1.5", KeyType.Integer | KeyType.Optional)]
        public const string DL = "/DL";
      }
    }
  }
}