1
2 package org.freehep.graphicsio.pdf;
3
4 import java.awt.Color;
5 import java.awt.Shape;
6 import java.awt.geom.AffineTransform;
7 import java.awt.image.RenderedImage;
8 import java.io.IOException;
9 import java.io.OutputStream;
10
11 import org.freehep.graphicsio.ImageConstants;
12 import org.freehep.util.io.ASCII85OutputStream;
13 import org.freehep.util.io.ASCIIHexOutputStream;
14 import org.freehep.util.io.CountedByteOutputStream;
15 import org.freehep.util.io.FinishableOutputStream;
16 import org.freehep.util.io.FlateOutputStream;
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 public class PDFStream extends PDFDictionary implements PDFConstants {
32
33 private String name;
34
35 private PDFObject object;
36
37 private boolean dictionaryOpen;
38
39 private OutputStream[] stream;
40
41 private CountedByteOutputStream byteCountStream;
42
43 private String[] encode;
44
45 PDFStream(PDF pdf, PDFByteWriter writer, String name, PDFObject parent,
46 String[] encode) throws IOException {
47 super(pdf, writer);
48 this.name = name;
49 object = parent;
50 if (object == null)
51 System.err
52 .println("PDFWriter: 'PDFStream' cannot have a null parent");
53
54 dictionaryOpen = true;
55 this.encode = encode;
56 }
57
58
59
60
61
62 private void startStream() throws IOException {
63 startStream(encode);
64 }
65
66
67
68
69
70 private void startStream(String[] encode) throws IOException {
71 if (dictionaryOpen) {
72 PDFName[] filters = decodeFilters(encode);
73 if (filters != null)
74 entry("Filter", filters);
75
76 super.close();
77 dictionaryOpen = false;
78 out.printPlain("stream\n");
79
80 byteCountStream = new CountedByteOutputStream(out);
81 stream = openFilters(byteCountStream, encode);
82 }
83 }
84
85 private void write(int b) throws IOException {
86 startStream();
87 stream[0].write(b);
88 }
89
90 private void write(byte[] b) throws IOException {
91 for (int i = 0; i < b.length; i++) {
92 write((int) b[i]);
93 }
94 }
95
96 private static PDFName[] decodeFilters(String[] encode) {
97 PDFName[] filters = null;
98 if ((encode != null) && (encode.length != 0)) {
99 filters = new PDFName[encode.length];
100 for (int i = 0; i < filters.length; i++) {
101 filters[i] = new PDFName(encode[encode.length - i - 1]
102 + "Decode");
103 }
104 }
105 return filters;
106 }
107
108
109
110 private static OutputStream[] openFilters(OutputStream s, String[] filters) {
111 OutputStream[] os;
112 if ((filters != null) && (filters.length != 0)) {
113 os = new OutputStream[filters.length + 1];
114 os[os.length - 1] = s;
115 for (int i = os.length - 2; i >= 0; i--) {
116 if (filters[i].equals("ASCIIHex")) {
117 os[i] = new ASCIIHexOutputStream(os[i + 1]);
118 } else if (filters[i].equals("ASCII85")) {
119 os[i] = new ASCII85OutputStream(os[i + 1]);
120 } else if (filters[i].equals("Flate")) {
121 os[i] = new FlateOutputStream(os[i + 1]);
122 } else if (filters[i].equals("DCT")) {
123 os[i] = os[i + 1];
124 } else {
125 System.err.println("PDFWriter: unknown stream format: "
126 + filters[i]);
127 }
128 }
129 } else {
130 os = new OutputStream[1];
131 os[0] = s;
132 }
133 return os;
134 }
135
136
137 private static void closeFilters(OutputStream[] s) throws IOException {
138 for (int i = 0; i < s.length - 1; i++) {
139 s[i].flush();
140 if (s[i] instanceof FinishableOutputStream) {
141 ((FinishableOutputStream) s[i]).finish();
142 }
143 }
144 s[s.length - 1].flush();
145 }
146
147 private void write(String s) throws IOException {
148 byte[] b = s.getBytes("ISO-8859-1");
149 for (int i = 0; i < b.length; i++) {
150 write(b[i]);
151 }
152 }
153
154 void close() throws IOException {
155 closeFilters(stream);
156 stream = null;
157 out.printPlain("\nendstream");
158 out.println();
159 object.close();
160
161 if (gStates > 0) {
162 System.err.println("PDFStream: unbalanced saves()/restores(), too many saves: "+gStates);
163 }
164 }
165
166 String getName() {
167 return name;
168 }
169
170 public int getLength() {
171 return byteCountStream.getCount();
172 }
173
174 public void print(String s) throws IOException {
175 write(s);
176 }
177
178 public void println(String s) throws IOException {
179 write(s);
180 write(EOL);
181 }
182
183 public void comment(String comment) throws IOException {
184 println("% " + comment);
185 }
186
187
188
189
190
191
192
193
194 private int gStates = 0;
195
196 public void save() throws IOException {
197 println("q");
198 gStates++;
199 }
200
201 public void restore() throws IOException {
202 if (gStates <= 0) {
203 System.err.println("PDFStream: unbalanced saves()/restores(), too many restores");
204 }
205 gStates--;
206 println("Q");
207 }
208
209 public void matrix(AffineTransform xform) throws IOException {
210 matrix(xform.getScaleX(), xform.getShearY(), xform.getShearX(), xform
211 .getScaleY(), xform.getTranslateX(), xform.getTranslateY());
212 }
213
214 public void matrix(double m00, double m10, double m01, double m11,
215 double m02, double m12) throws IOException {
216 println(PDFUtil.fixedPrecision(m00) + " " + PDFUtil.fixedPrecision(m10)
217 + " " + PDFUtil.fixedPrecision(m01) + " "
218 + PDFUtil.fixedPrecision(m11) + " "
219 + PDFUtil.fixedPrecision(m02) + " "
220 + PDFUtil.fixedPrecision(m12) + " cm");
221 }
222
223 public void width(double width) throws IOException {
224 println(PDFUtil.fixedPrecision(width) + " w");
225 }
226
227 public void cap(int capStyle) throws IOException {
228 println(capStyle + " J");
229 }
230
231 public void join(int joinStyle) throws IOException {
232 println(joinStyle + " j");
233 }
234
235 public void mitterLimit(double limit) throws IOException {
236 println(PDFUtil.fixedPrecision(limit) + " M");
237 }
238
239 public void dash(int[] dash, double phase) throws IOException {
240 print("[");
241 for (int i = 0; i < dash.length; i++) {
242 print(" " + PDFUtil.fixedPrecision(dash[i]));
243 }
244 println("] " + PDFUtil.fixedPrecision(phase) + " d");
245 }
246
247 public void dash(float[] dash, double phase) throws IOException {
248 print("[");
249 for (int i = 0; i < dash.length; i++) {
250 print(" " + PDFUtil.fixedPrecision(dash[i]));
251 }
252 println("] " + PDFUtil.fixedPrecision(phase) + " d");
253 }
254
255 public void flatness(double flatness) throws IOException {
256 println(PDFUtil.fixedPrecision(flatness) + " i");
257 }
258
259 public void state(PDFName stateDictionary) throws IOException {
260 println(stateDictionary + " gs");
261 }
262
263
264
265
266 public void cubic(double x1, double y1, double x2, double y2, double x3,
267 double y3) throws IOException {
268 println(PDFUtil.fixedPrecision(x1) + " " + PDFUtil.fixedPrecision(y1)
269 + " " + PDFUtil.fixedPrecision(x2) + " "
270 + PDFUtil.fixedPrecision(y2) + " " + PDFUtil.fixedPrecision(x3)
271 + " " + PDFUtil.fixedPrecision(y3) + " c");
272 }
273
274 public void cubicV(double x2, double y2, double x3, double y3)
275 throws IOException {
276 println(PDFUtil.fixedPrecision(x2) + " " + PDFUtil.fixedPrecision(y2)
277 + " " + PDFUtil.fixedPrecision(x3) + " "
278 + PDFUtil.fixedPrecision(y3) + " v");
279 }
280
281 public void cubicY(double x1, double y1, double x3, double y3)
282 throws IOException {
283 println(PDFUtil.fixedPrecision(x1) + " " + PDFUtil.fixedPrecision(y1)
284 + " " + PDFUtil.fixedPrecision(x3) + " "
285 + PDFUtil.fixedPrecision(y3) + " y");
286 }
287
288 public void move(double x, double y) throws IOException {
289 println(PDFUtil.fixedPrecision(x) + " " + PDFUtil.fixedPrecision(y)
290 + " m");
291 }
292
293 public void line(double x, double y) throws IOException {
294 println(PDFUtil.fixedPrecision(x) + " " + PDFUtil.fixedPrecision(y)
295 + " l");
296 }
297
298 public void closePath() throws IOException {
299 println("h");
300 }
301
302 public void rectangle(double x, double y, double width, double height)
303 throws IOException {
304 println(PDFUtil.fixedPrecision(x) + " " + PDFUtil.fixedPrecision(y)
305 + " " + PDFUtil.fixedPrecision(width) + " "
306 + PDFUtil.fixedPrecision(height) + " re");
307 }
308
309
310
311
312 public void stroke() throws IOException {
313 println("S");
314 }
315
316 public void closeAndStroke() throws IOException {
317 println("s");
318 }
319
320 public void fill() throws IOException {
321 println("f");
322 }
323
324 public void fillEvenOdd() throws IOException {
325 println("f*");
326 }
327
328 public void fillAndStroke() throws IOException {
329 println("B");
330 }
331
332 public void fillEvenOddAndStroke() throws IOException {
333 println("B*");
334 }
335
336 public void closeFillAndStroke() throws IOException {
337 println("b");
338 }
339
340 public void closeFillEvenOddAndStroke() throws IOException {
341 println("b*");
342 }
343
344 public void endPath() throws IOException {
345 println("n");
346 }
347
348
349
350
351 public void clip() throws IOException {
352 println("W");
353 }
354
355 public void clipEvenOdd() throws IOException {
356 println("W*");
357 }
358
359
360
361
362 private boolean textOpen = false;
363
364 public void beginText() throws IOException {
365 if (textOpen)
366 System.err.println("PDFStream: nested beginText() not allowed.");
367 println("BT");
368 textOpen = true;
369 }
370
371 public void endText() throws IOException {
372 if (!textOpen)
373 System.err
374 .println("PDFStream: unbalanced use of beginText()/endText().");
375 println("ET");
376 textOpen = false;
377 }
378
379
380
381
382 public void charSpace(double charSpace) throws IOException {
383 println(PDFUtil.fixedPrecision(charSpace) + " Tc");
384 }
385
386 public void wordSpace(double wordSpace) throws IOException {
387 println(PDFUtil.fixedPrecision(wordSpace) + " Tw");
388 }
389
390 public void scale(double scale) throws IOException {
391 println(PDFUtil.fixedPrecision(scale) + " Tz");
392 }
393
394 public void leading(double leading) throws IOException {
395 println(PDFUtil.fixedPrecision(leading) + " TL");
396 }
397
398 private boolean fontWasSet = false;
399
400 public void font(PDFName fontName, double size) throws IOException {
401 println(fontName + " " + PDFUtil.fixedPrecision(size) + " Tf");
402 fontWasSet = true;
403 }
404
405 public void rendering(int mode) throws IOException {
406 println(mode + " Tr");
407 }
408
409 public void rise(double rise) throws IOException {
410 println(PDFUtil.fixedPrecision(rise) + " Ts");
411 }
412
413
414
415
416 public void text(double x, double y) throws IOException {
417 println(PDFUtil.fixedPrecision(x) + " " + PDFUtil.fixedPrecision(y)
418 + " Td");
419 }
420
421 public void textLeading(double x, double y) throws IOException {
422 println(PDFUtil.fixedPrecision(x) + " " + PDFUtil.fixedPrecision(y)
423 + " TD");
424 }
425
426 public void textMatrix(double a, double b, double c, double d, double e,
427 double f) throws IOException {
428 println(PDFUtil.fixedPrecision(a) + " " + PDFUtil.fixedPrecision(b)
429 + " " + PDFUtil.fixedPrecision(c) + " "
430 + PDFUtil.fixedPrecision(d) + " " + PDFUtil.fixedPrecision(e)
431 + " " + PDFUtil.fixedPrecision(f) + " Tm");
432 }
433
434 public void textLine() throws IOException {
435 println("T*");
436 }
437
438
439
440
441 public void show(String text) throws IOException {
442 if (!fontWasSet)
443 System.err
444 .println("PDFStream: cannot use Text Showing operator before font is set.");
445 if (!textOpen)
446 System.err
447 .println("PDFStream: Text Showing operator only allowed inside Text section.");
448 println("(" + PDFUtil.escape(text) + ") Tj");
449 }
450
451 public void showLine(String text) throws IOException {
452 if (!fontWasSet)
453 System.err
454 .println("PDFStream: cannot use Text Showing operator before font is set.");
455 if (!textOpen)
456 System.err
457 .println("PDFStream: Text Showing operator only allowed inside Text section.");
458 println("(" + PDFUtil.escape(text) + ") '");
459 }
460
461 public void showLine(double wordSpace, double charSpace, String text)
462 throws IOException {
463 if (!fontWasSet)
464 System.err
465 .println("PDFStream: cannot use Text Showing operator before font is set.");
466 if (!textOpen)
467 System.err
468 .println("PDFStream: Text Showing operator only allowed inside Text section.");
469 println(PDFUtil.fixedPrecision(wordSpace) + " "
470 + PDFUtil.fixedPrecision(charSpace) + " ("
471 + PDFUtil.escape(text) + ") \"");
472 }
473
474 public void show(Object[] array) throws IOException {
475 print("[");
476 for (int i = 0; i < array.length; i++) {
477 Object object = array[i];
478 if (object instanceof String) {
479 print(" (" + PDFUtil.escape(object.toString()) + ")");
480 } else if (object instanceof Integer) {
481 print(" " + ((Integer) object).intValue());
482 } else if (object instanceof Double) {
483 print(" " + ((Double) object).doubleValue());
484 } else {
485 System.err
486 .println("PDFStream: input array of operator TJ may only contain objects of type 'String', 'Integer' or 'Double'");
487 }
488 }
489 println("] TJ");
490 }
491
492
493
494
495 public void glyph(double wx, double wy) throws IOException {
496 println(PDFUtil.fixedPrecision(wx) + " " + PDFUtil.fixedPrecision(wy)
497 + " d0");
498 }
499
500 public void glyph(double wx, double wy, double llx, double lly, double urx,
501 double ury) throws IOException {
502 println(PDFUtil.fixedPrecision(wx) + " " + PDFUtil.fixedPrecision(wy)
503 + " " + PDFUtil.fixedPrecision(llx) + " "
504 + PDFUtil.fixedPrecision(lly) + " "
505 + PDFUtil.fixedPrecision(urx) + " "
506 + PDFUtil.fixedPrecision(ury) + " d1");
507 }
508
509
510
511
512 public void colorSpace(PDFName colorSpace) throws IOException {
513 println(colorSpace + " cs");
514 }
515
516 public void colorSpaceStroke(PDFName colorSpace) throws IOException {
517 println(colorSpace + " CS");
518 }
519
520 public void colorSpace(double[] color) throws IOException {
521 for (int i = 0; i < color.length; i++) {
522 print(" " + color[i]);
523 }
524 println(" scn");
525 }
526
527 public void colorSpaceStroke(double[] color) throws IOException {
528 for (int i = 0; i < color.length; i++) {
529 print(" " + color[i]);
530 }
531 println(" SCN");
532 }
533
534 public void colorSpace(double[] color, PDFName name) throws IOException {
535 if (color != null) {
536 for (int i = 0; i < color.length; i++) {
537 print(PDFUtil.fixedPrecision(color[i]) + " ");
538 }
539 }
540 println(name + " scn");
541 }
542
543 public void colorSpaceStroke(double[] color, PDFName name)
544 throws IOException {
545 if (color != null) {
546 for (int i = 0; i < color.length; i++) {
547 print(PDFUtil.fixedPrecision(color[i]) + " ");
548 }
549 }
550 println(name + " SCN");
551 }
552
553 public void colorSpace(double g) throws IOException {
554 println(PDFUtil.fixedPrecision(g) + " g");
555 }
556
557 public void colorSpaceStroke(double g) throws IOException {
558 println(PDFUtil.fixedPrecision(g) + " G");
559 }
560
561 public void colorSpace(double r, double g, double b) throws IOException {
562 println(PDFUtil.fixedPrecision(r) + " " + PDFUtil.fixedPrecision(g)
563 + " " + PDFUtil.fixedPrecision(b) + " rg");
564 }
565
566 public void colorSpaceStroke(double r, double g, double b)
567 throws IOException {
568 println(PDFUtil.fixedPrecision(r) + " " + PDFUtil.fixedPrecision(g)
569 + " " + PDFUtil.fixedPrecision(b) + " RG");
570 }
571
572 public void colorSpace(double c, double m, double y, double k)
573 throws IOException {
574 println(PDFUtil.fixedPrecision(c) + " " + PDFUtil.fixedPrecision(m)
575 + " " + PDFUtil.fixedPrecision(y) + " "
576 + PDFUtil.fixedPrecision(k) + " k");
577 }
578
579 public void colorSpaceStroke(double c, double m, double y, double k)
580 throws IOException {
581 println(PDFUtil.fixedPrecision(c) + " " + PDFUtil.fixedPrecision(m)
582 + " " + PDFUtil.fixedPrecision(y) + " "
583 + PDFUtil.fixedPrecision(k) + " K");
584 }
585
586
587
588
589 public void shade(PDFName name) throws IOException {
590 println(name + " sh");
591 }
592
593
594
595
596
597
598
599 private PDFName[] getFilterName(String encode) {
600 if (ImageConstants.ZLIB.equals(encode)) {
601 return decodeFilters(new String[] {
602 ImageConstants.ENCODING_FLATE,
603 ImageConstants.ENCODING_ASCII85});
604 }
605
606 if (ImageConstants.JPG.equals(encode)) {
607 return decodeFilters(new String[] {
608 ImageConstants.ENCODING_DCT,
609 ImageConstants.ENCODING_ASCII85});
610 }
611
612 throw new IllegalArgumentException("unknown image encoding " + encode + " for PDFStream");
613 }
614
615
616
617
618
619
620
621
622
623
624 public void image(RenderedImage image, Color bkg, String encode)
625 throws IOException {
626
627 ImageBytes bytes = new ImageBytes(image, bkg, encode, ImageConstants.COLOR_MODEL_RGB);
628
629 entry("Width", image.getWidth());
630 entry("Height", image.getHeight());
631 entry("ColorSpace", pdf.name("DeviceRGB"));
632 entry("BitsPerComponent", 8);
633 entry("Filter", getFilterName(bytes.getFormat()));
634 write(bytes.getBytes());
635 }
636
637 public void imageMask(RenderedImage image, String encode)
638 throws IOException {
639
640 ImageBytes bytes = new ImageBytes(image, null, encode, ImageConstants.COLOR_MODEL_A);
641
642 entry("Width", image.getWidth());
643 entry("Height", image.getHeight());
644 entry("BitsPerComponent", 8);
645 entry("ColorSpace", pdf.name("DeviceGray"));
646 entry("Filter", getFilterName(bytes.getFormat()));
647 write(bytes.getBytes());
648 }
649
650
651
652
653
654
655
656
657
658
659
660
661 public void inlineImage(RenderedImage image, Color bkg, String encode)
662 throws IOException {
663
664 ImageBytes bytes = new ImageBytes(image, bkg, ImageConstants.JPG, ImageConstants.COLOR_MODEL_RGB);
665
666 println("BI");
667 imageInfo("Width", image.getWidth());
668 imageInfo("Height", image.getHeight());
669 imageInfo("ColorSpace", pdf.name("DeviceRGB"));
670 imageInfo("BitsPerComponent", 8);
671
672 imageInfo("Filter", getFilterName(bytes.getFormat()));
673 print("ID\n");
674
675 write(bytes.getBytes());
676
677 println("\nEI");
678 }
679
680
681
682
683 private void imageInfo(String key, int number) throws IOException {
684 println("/" + key + " " + number);
685 }
686
687 private void imageInfo(String key, PDFName name) throws IOException {
688 println("/" + key + " " + name);
689 }
690
691 private void imageInfo(String key, Object[] array) throws IOException {
692 print("/" + key + " [");
693 for (int i = 0; i < array.length; i++) {
694 print(" " + array[i]);
695 }
696 println("]");
697 }
698
699
700
701
702
703
704
705
706 public boolean drawPath(Shape s) throws IOException {
707 PDFPathConstructor path = new PDFPathConstructor(this);
708 return path.addPath(s);
709 }
710
711
712
713
714 public void xObject(PDFName name) throws IOException {
715 println(name + " Do");
716 }
717
718
719
720
721
722
723
724
725
726 private boolean compatibilityOpen = false;
727
728 public void beginCompatibility() throws IOException {
729 if (compatibilityOpen)
730 System.err
731 .println("PDFStream: nested use of Compatibility sections not allowed.");
732 println("BX");
733 compatibilityOpen = true;
734 }
735
736 public void endCompatibility() throws IOException {
737 if (!compatibilityOpen)
738 System.err
739 .println("PDFStream: unbalanced use of begin/endCompatibilty().");
740 println("EX");
741 compatibilityOpen = false;
742 }
743
744 }