1 | /*****************************************************************************/ |
2 | // Copyright 2006-2008 Adobe Systems Incorporated |
3 | // All Rights Reserved. |
4 | // |
5 | // NOTICE: Adobe permits you to use, modify, and distribute this file in |
6 | // accordance with the terms of the Adobe license agreement accompanying it. |
7 | /*****************************************************************************/ |
8 | |
9 | /* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_iptc.cpp#1 $ */ |
10 | /* $DateTime: 2012/05/30 13:28:51 $ */ |
11 | /* $Change: 832332 $ */ |
12 | /* $Author: tknoll $ */ |
13 | |
14 | /*****************************************************************************/ |
15 | |
16 | #include "dng_iptc.h" |
17 | |
18 | #include "dng_assertions.h" |
19 | #include "dng_auto_ptr.h" |
20 | #include "dng_memory_stream.h" |
21 | #include "dng_stream.h" |
22 | #include "dng_utils.h" |
23 | |
24 | /*****************************************************************************/ |
25 | |
26 | dng_iptc::dng_iptc () |
27 | |
28 | : fTitle () |
29 | |
30 | , fUrgency (-1) |
31 | |
32 | , fCategory () |
33 | |
34 | , fSupplementalCategories () |
35 | |
36 | , fKeywords () |
37 | |
38 | , fInstructions () |
39 | |
40 | , fDateTimeCreated () |
41 | |
42 | , fDigitalCreationDateTime () |
43 | |
44 | , fAuthors () |
45 | , fAuthorsPosition () |
46 | |
47 | , fCity () |
48 | , fState () |
49 | , fCountry () |
50 | , fCountryCode () |
51 | |
52 | , fLocation () |
53 | |
54 | , fTransmissionReference () |
55 | |
56 | , fHeadline () |
57 | |
58 | , fCredit () |
59 | |
60 | , fSource () |
61 | |
62 | , fCopyrightNotice () |
63 | |
64 | , fDescription () |
65 | , fDescriptionWriter () |
66 | |
67 | { |
68 | |
69 | } |
70 | |
71 | /*****************************************************************************/ |
72 | |
73 | dng_iptc::~dng_iptc () |
74 | { |
75 | |
76 | } |
77 | |
78 | /*****************************************************************************/ |
79 | |
80 | bool dng_iptc::IsEmpty () const |
81 | { |
82 | |
83 | if (fTitle.NotEmpty ()) |
84 | { |
85 | return false; |
86 | } |
87 | |
88 | if (fUrgency >= 0) |
89 | { |
90 | return false; |
91 | } |
92 | |
93 | if (fCategory.NotEmpty ()) |
94 | { |
95 | return false; |
96 | } |
97 | |
98 | if (fSupplementalCategories.Count () > 0) |
99 | { |
100 | return false; |
101 | } |
102 | |
103 | if (fKeywords.Count () > 0) |
104 | { |
105 | return false; |
106 | } |
107 | |
108 | if (fInstructions.NotEmpty ()) |
109 | { |
110 | return false; |
111 | } |
112 | |
113 | if (fDateTimeCreated.IsValid ()) |
114 | { |
115 | return false; |
116 | } |
117 | |
118 | if (fDigitalCreationDateTime.IsValid ()) |
119 | { |
120 | return false; |
121 | } |
122 | |
123 | if (fAuthors.Count () != 0 || |
124 | fAuthorsPosition.NotEmpty ()) |
125 | { |
126 | return false; |
127 | } |
128 | |
129 | if (fCity .NotEmpty () || |
130 | fState .NotEmpty () || |
131 | fCountry.NotEmpty ()) |
132 | { |
133 | return false; |
134 | } |
135 | |
136 | if (fCountryCode.NotEmpty ()) |
137 | { |
138 | return false; |
139 | } |
140 | |
141 | if (fLocation.NotEmpty ()) |
142 | { |
143 | return false; |
144 | } |
145 | |
146 | if (fTransmissionReference.NotEmpty ()) |
147 | { |
148 | return false; |
149 | } |
150 | |
151 | if (fHeadline.NotEmpty ()) |
152 | { |
153 | return false; |
154 | } |
155 | |
156 | if (fCredit.NotEmpty ()) |
157 | { |
158 | return false; |
159 | } |
160 | |
161 | if (fSource.NotEmpty ()) |
162 | { |
163 | return false; |
164 | } |
165 | |
166 | if (fCopyrightNotice.NotEmpty ()) |
167 | { |
168 | return false; |
169 | } |
170 | |
171 | if (fDescription .NotEmpty () || |
172 | fDescriptionWriter.NotEmpty ()) |
173 | { |
174 | return false; |
175 | } |
176 | |
177 | return true; |
178 | |
179 | } |
180 | |
181 | /*****************************************************************************/ |
182 | |
183 | void dng_iptc::ParseString (dng_stream &stream, |
184 | dng_string &s, |
185 | CharSet charSet) |
186 | { |
187 | |
188 | uint32 length = stream.Get_uint16 (); |
189 | |
190 | dng_memory_data buffer (length + 1); |
191 | |
192 | char *c = buffer.Buffer_char (); |
193 | |
194 | stream.Get (c, length); |
195 | |
196 | c [length] = 0; |
197 | |
198 | switch (charSet) |
199 | { |
200 | |
201 | case kCharSetUTF8: |
202 | { |
203 | s.Set_UTF8 (c); |
204 | break; |
205 | } |
206 | |
207 | default: |
208 | { |
209 | s.Set_SystemEncoding (c); |
210 | } |
211 | |
212 | } |
213 | |
214 | s.SetLineEndingsToNewLines (); |
215 | |
216 | s.StripLowASCII (); |
217 | |
218 | s.TrimTrailingBlanks (); |
219 | |
220 | } |
221 | |
222 | /*****************************************************************************/ |
223 | |
224 | void dng_iptc::Parse (const void *blockData, |
225 | uint32 blockSize, |
226 | uint64 offsetInOriginalFile) |
227 | { |
228 | |
229 | dng_stream stream (blockData, |
230 | blockSize, |
231 | offsetInOriginalFile); |
232 | |
233 | stream.SetBigEndian (); |
234 | |
235 | // Make a first pass though the data, trying to figure out the |
236 | // character set. |
237 | |
238 | CharSet charSet = kCharSetUnknown; |
239 | |
240 | bool isValidUTF8 = true; |
241 | |
242 | bool hasEncodingMarker = false; |
243 | |
244 | uint64 firstOffset = stream.Position (); |
245 | |
246 | uint64 nextOffset = firstOffset; |
247 | |
248 | while (nextOffset + 5 < stream.Length ()) |
249 | { |
250 | |
251 | stream.SetReadPosition (nextOffset); |
252 | |
253 | uint8 firstByte = stream.Get_uint8 (); |
254 | |
255 | if (firstByte != 0x1C) break; |
256 | |
257 | uint8 record = stream.Get_uint8 (); |
258 | uint8 dataSet = stream.Get_uint8 (); |
259 | uint32 dataSize = stream.Get_uint16 (); |
260 | |
261 | nextOffset = stream.Position () + dataSize; |
262 | |
263 | if (record == 1) |
264 | { |
265 | |
266 | switch (dataSet) |
267 | { |
268 | |
269 | case 90: |
270 | { |
271 | |
272 | hasEncodingMarker = true; |
273 | |
274 | if (dataSize == 3) |
275 | { |
276 | |
277 | uint32 byte1 = stream.Get_uint8 (); |
278 | uint32 byte2 = stream.Get_uint8 (); |
279 | uint32 byte3 = stream.Get_uint8 (); |
280 | |
281 | if (byte1 == 27 /* Escape */ && |
282 | byte2 == 0x25 && |
283 | byte3 == 0x47) |
284 | { |
285 | |
286 | charSet = kCharSetUTF8; |
287 | |
288 | } |
289 | |
290 | } |
291 | |
292 | break; |
293 | |
294 | } |
295 | |
296 | default: |
297 | break; |
298 | |
299 | } |
300 | |
301 | } |
302 | |
303 | else if (record == 2) |
304 | { |
305 | |
306 | dng_memory_data buffer (dataSize + 1); |
307 | |
308 | char *s = buffer.Buffer_char (); |
309 | |
310 | stream.Get (s, dataSize); |
311 | |
312 | s [dataSize] = 0; |
313 | |
314 | isValidUTF8 = isValidUTF8 && dng_string::IsUTF8 (s); |
315 | |
316 | } |
317 | |
318 | } |
319 | |
320 | // If we don't have an encoding marker, and the data is valid |
321 | // UTF-8, then assume that it is UTF-8 (rather than system encoding). |
322 | |
323 | if (!hasEncodingMarker && isValidUTF8) |
324 | { |
325 | |
326 | charSet = kCharSetUTF8; |
327 | |
328 | } |
329 | |
330 | // Make a second pass though the data, actually reading the data. |
331 | |
332 | nextOffset = firstOffset; |
333 | |
334 | while (nextOffset + 5 < stream.Length ()) |
335 | { |
336 | |
337 | stream.SetReadPosition (nextOffset); |
338 | |
339 | uint8 firstByte = stream.Get_uint8 (); |
340 | |
341 | if (firstByte != 0x1C) break; |
342 | |
343 | uint8 record = stream.Get_uint8 (); |
344 | uint8 dataSet = stream.Get_uint8 (); |
345 | uint32 dataSize = stream.Get_uint16 (); |
346 | |
347 | nextOffset = stream.Position () + dataSize; |
348 | |
349 | if (record == 2) |
350 | { |
351 | |
352 | stream.SetReadPosition (stream.Position () - 2); |
353 | |
354 | switch ((DataSet) dataSet) |
355 | { |
356 | |
357 | case kObjectNameSet: |
358 | { |
359 | ParseString (stream, fTitle, charSet); |
360 | break; |
361 | } |
362 | |
363 | case kUrgencySet: |
364 | { |
365 | |
366 | int32 size = stream.Get_uint16 (); |
367 | |
368 | if (size == 1) |
369 | { |
370 | |
371 | char c = stream.Get_int8 (); |
372 | |
373 | if (c >= '0' && c <= '9') |
374 | { |
375 | fUrgency = c - '0'; |
376 | } |
377 | |
378 | } |
379 | |
380 | break; |
381 | |
382 | } |
383 | |
384 | case kCategorySet: |
385 | { |
386 | ParseString (stream, fCategory, charSet); |
387 | break; |
388 | } |
389 | |
390 | case kSupplementalCategoriesSet: |
391 | { |
392 | |
393 | dng_string category; |
394 | |
395 | ParseString (stream, category, charSet); |
396 | |
397 | if (category.NotEmpty ()) |
398 | { |
399 | fSupplementalCategories.Append (category); |
400 | } |
401 | |
402 | break; |
403 | |
404 | } |
405 | |
406 | case kKeywordsSet: |
407 | { |
408 | |
409 | dng_string keyword; |
410 | |
411 | ParseString (stream, keyword, charSet); |
412 | |
413 | if (keyword.NotEmpty ()) |
414 | { |
415 | fKeywords.Append (keyword); |
416 | } |
417 | |
418 | break; |
419 | |
420 | } |
421 | |
422 | case kSpecialInstructionsSet: |
423 | { |
424 | ParseString (stream, fInstructions, charSet); |
425 | break; |
426 | } |
427 | |
428 | case kDateCreatedSet: |
429 | { |
430 | |
431 | uint32 length = stream.Get_uint16 (); |
432 | |
433 | if (length == 8) |
434 | { |
435 | |
436 | char date [9]; |
437 | |
438 | stream.Get (date, 8); |
439 | |
440 | date [8] = 0; |
441 | |
442 | fDateTimeCreated.Decode_IPTC_Date (date); |
443 | |
444 | } |
445 | |
446 | break; |
447 | |
448 | } |
449 | |
450 | case kTimeCreatedSet: |
451 | { |
452 | |
453 | uint32 length = stream.Get_uint16 (); |
454 | |
455 | if (length >= 4 && length <= 11) |
456 | { |
457 | |
458 | char time [12]; |
459 | |
460 | stream.Get (time, length); |
461 | |
462 | time [length] = 0; |
463 | |
464 | fDateTimeCreated.Decode_IPTC_Time (time); |
465 | |
466 | } |
467 | |
468 | break; |
469 | |
470 | } |
471 | |
472 | case kDigitalCreationDateSet: |
473 | { |
474 | |
475 | uint32 length = stream.Get_uint16 (); |
476 | |
477 | if (length == 8) |
478 | { |
479 | |
480 | char date [9]; |
481 | |
482 | stream.Get (date, 8); |
483 | |
484 | date [8] = 0; |
485 | |
486 | fDigitalCreationDateTime.Decode_IPTC_Date (date); |
487 | |
488 | } |
489 | |
490 | break; |
491 | |
492 | } |
493 | |
494 | case kDigitalCreationTimeSet: |
495 | { |
496 | |
497 | uint32 length = stream.Get_uint16 (); |
498 | |
499 | if (length >= 4 && length <= 11) |
500 | { |
501 | |
502 | char time [12]; |
503 | |
504 | stream.Get (time, length); |
505 | |
506 | time [length] = 0; |
507 | |
508 | fDigitalCreationDateTime.Decode_IPTC_Time (time); |
509 | |
510 | } |
511 | |
512 | break; |
513 | |
514 | } |
515 | |
516 | case kBylineSet: |
517 | { |
518 | |
519 | dng_string author; |
520 | |
521 | ParseString (stream, author, charSet); |
522 | |
523 | if (author.NotEmpty ()) |
524 | { |
525 | fAuthors.Append (author); |
526 | } |
527 | |
528 | break; |
529 | |
530 | } |
531 | |
532 | case kBylineTitleSet: |
533 | { |
534 | ParseString (stream, fAuthorsPosition, charSet); |
535 | break; |
536 | } |
537 | |
538 | case kCitySet: |
539 | { |
540 | ParseString (stream, fCity, charSet); |
541 | break; |
542 | } |
543 | |
544 | case kProvinceStateSet: |
545 | { |
546 | ParseString (stream, fState, charSet); |
547 | break; |
548 | } |
549 | |
550 | case kCountryNameSet: |
551 | { |
552 | ParseString (stream, fCountry, charSet); |
553 | break; |
554 | } |
555 | |
556 | case kCountryCodeSet: |
557 | { |
558 | ParseString (stream, fCountryCode, charSet); |
559 | break; |
560 | } |
561 | |
562 | case kSublocationSet: |
563 | { |
564 | ParseString (stream, fLocation, charSet); |
565 | break; |
566 | } |
567 | |
568 | case kOriginalTransmissionReferenceSet: |
569 | { |
570 | ParseString (stream, fTransmissionReference, charSet); |
571 | break; |
572 | } |
573 | |
574 | case kHeadlineSet: |
575 | { |
576 | ParseString (stream, fHeadline, charSet); |
577 | break; |
578 | } |
579 | |
580 | case kCreditSet: |
581 | { |
582 | ParseString (stream, fCredit, charSet); |
583 | break; |
584 | } |
585 | |
586 | case kSourceSet: |
587 | { |
588 | ParseString (stream, fSource, charSet); |
589 | break; |
590 | } |
591 | |
592 | case kCopyrightNoticeSet: |
593 | { |
594 | ParseString (stream, fCopyrightNotice, charSet); |
595 | break; |
596 | } |
597 | |
598 | case kCaptionSet: |
599 | { |
600 | ParseString (stream, fDescription, charSet); |
601 | break; |
602 | } |
603 | |
604 | case kCaptionWriterSet: |
605 | { |
606 | ParseString (stream, fDescriptionWriter, charSet); |
607 | break; |
608 | } |
609 | |
610 | // All other IPTC records are not part of the IPTC core |
611 | // and/or are not kept in sync with XMP tags, so we ignore |
612 | // them. |
613 | |
614 | default: |
615 | break; |
616 | |
617 | } |
618 | |
619 | } |
620 | |
621 | } |
622 | |
623 | } |
624 | |
625 | /*****************************************************************************/ |
626 | |
627 | void dng_iptc::SpoolString (dng_stream &stream, |
628 | const dng_string &s, |
629 | uint8 dataSet, |
630 | uint32 maxChars, |
631 | CharSet charSet) |
632 | { |
633 | |
634 | if (s.IsEmpty ()) |
635 | { |
636 | return; |
637 | } |
638 | |
639 | stream.Put_uint16 (0x1C02); |
640 | stream.Put_uint8 (dataSet); |
641 | |
642 | dng_string ss (s); |
643 | |
644 | ss.SetLineEndingsToReturns (); |
645 | |
646 | if (charSet == kCharSetUTF8) |
647 | { |
648 | |
649 | // UTF-8 encoding. |
650 | |
651 | if (ss.Length () > maxChars) |
652 | { |
653 | ss.Truncate (maxChars); |
654 | } |
655 | |
656 | uint32 len = ss.Length (); |
657 | |
658 | stream.Put_uint16 ((uint16) len); |
659 | |
660 | stream.Put (ss.Get (), len); |
661 | |
662 | } |
663 | |
664 | else |
665 | { |
666 | |
667 | // System character set encoding. |
668 | |
669 | dng_memory_data buffer; |
670 | |
671 | uint32 len = ss.Get_SystemEncoding (buffer); |
672 | |
673 | if (len > maxChars) |
674 | { |
675 | |
676 | uint32 lower = 0; |
677 | uint32 upper = ss.Length () - 1; |
678 | |
679 | while (upper > lower) |
680 | { |
681 | |
682 | uint32 middle = (upper + lower + 1) >> 1; |
683 | |
684 | dng_string sss (ss); |
685 | |
686 | sss.Truncate (middle); |
687 | |
688 | len = sss.Get_SystemEncoding (buffer); |
689 | |
690 | if (len <= maxChars) |
691 | { |
692 | |
693 | lower = middle; |
694 | |
695 | } |
696 | |
697 | else |
698 | { |
699 | |
700 | upper = middle - 1; |
701 | |
702 | } |
703 | |
704 | } |
705 | |
706 | ss.Truncate (lower); |
707 | |
708 | len = ss.Get_SystemEncoding (buffer); |
709 | |
710 | } |
711 | |
712 | stream.Put_uint16 ((uint16) len); |
713 | |
714 | stream.Put (buffer.Buffer_char (), len); |
715 | |
716 | } |
717 | |
718 | } |
719 | /*****************************************************************************/ |
720 | |
721 | dng_memory_block * dng_iptc::Spool (dng_memory_allocator &allocator, |
722 | bool padForTIFF) |
723 | { |
724 | |
725 | uint32 j; |
726 | |
727 | char s [64]; |
728 | |
729 | dng_memory_stream stream (allocator, NULL, 2048); |
730 | |
731 | stream.SetBigEndian (); |
732 | |
733 | // Medata working group - now we just always write UTF-8. |
734 | |
735 | CharSet charSet = kCharSetUTF8; |
736 | |
737 | // UTF-8 encoding marker. |
738 | |
739 | if (charSet == kCharSetUTF8) |
740 | { |
741 | |
742 | stream.Put_uint16 (0x1C01); |
743 | stream.Put_uint8 (90); |
744 | stream.Put_uint16 (3); |
745 | stream.Put_uint8 (27); |
746 | stream.Put_uint8 (0x25); |
747 | stream.Put_uint8 (0x47); |
748 | |
749 | } |
750 | |
751 | stream.Put_uint16 (0x1C02); |
752 | stream.Put_uint8 (kRecordVersionSet); |
753 | stream.Put_uint16 (2); |
754 | stream.Put_uint16 (4); |
755 | |
756 | SpoolString (stream, |
757 | fTitle, |
758 | kObjectNameSet, |
759 | 64, |
760 | charSet); |
761 | |
762 | if (fUrgency >= 0) |
763 | { |
764 | |
765 | sprintf (s, "%1u" , (unsigned) fUrgency); |
766 | |
767 | stream.Put_uint16 (0x1C02); |
768 | stream.Put_uint8 (kUrgencySet); |
769 | |
770 | stream.Put_uint16 (1); |
771 | |
772 | stream.Put (s, 1); |
773 | |
774 | } |
775 | |
776 | SpoolString (stream, |
777 | fCategory, |
778 | kCategorySet, |
779 | 3, |
780 | charSet); |
781 | |
782 | for (j = 0; j < fSupplementalCategories.Count (); j++) |
783 | { |
784 | |
785 | SpoolString (stream, |
786 | fSupplementalCategories [j], |
787 | kSupplementalCategoriesSet, |
788 | 32, |
789 | charSet); |
790 | |
791 | } |
792 | |
793 | for (j = 0; j < fKeywords.Count (); j++) |
794 | { |
795 | |
796 | SpoolString (stream, |
797 | fKeywords [j], |
798 | kKeywordsSet, |
799 | 64, |
800 | charSet); |
801 | |
802 | } |
803 | |
804 | SpoolString (stream, |
805 | fInstructions, |
806 | kSpecialInstructionsSet, |
807 | 255, |
808 | charSet); |
809 | |
810 | if (fDateTimeCreated.IsValid ()) |
811 | { |
812 | |
813 | dng_string dateString = fDateTimeCreated.Encode_IPTC_Date (); |
814 | |
815 | if (dateString.NotEmpty ()) |
816 | { |
817 | |
818 | DNG_ASSERT (dateString.Length () == 8, "Wrong length IPTC date" ); |
819 | |
820 | stream.Put_uint16 (0x1C02); |
821 | stream.Put_uint8 (kDateCreatedSet); |
822 | |
823 | stream.Put_uint16 (8); |
824 | |
825 | stream.Put (dateString.Get (), 8); |
826 | |
827 | } |
828 | |
829 | dng_string timeString = fDateTimeCreated.Encode_IPTC_Time (); |
830 | |
831 | if (timeString.NotEmpty ()) |
832 | { |
833 | |
834 | stream.Put_uint16 (0x1C02); |
835 | stream.Put_uint8 (kTimeCreatedSet); |
836 | |
837 | stream.Put_uint16 ((uint16)timeString.Length ()); |
838 | |
839 | stream.Put (timeString.Get (), timeString.Length ()); |
840 | |
841 | } |
842 | |
843 | } |
844 | |
845 | if (fDigitalCreationDateTime.IsValid ()) |
846 | { |
847 | |
848 | dng_string dateString = fDigitalCreationDateTime.Encode_IPTC_Date (); |
849 | |
850 | if (dateString.NotEmpty ()) |
851 | { |
852 | |
853 | DNG_ASSERT (dateString.Length () == 8, "Wrong length IPTC date" ); |
854 | |
855 | stream.Put_uint16 (0x1C02); |
856 | stream.Put_uint8 (kDigitalCreationDateSet); |
857 | |
858 | stream.Put_uint16 (8); |
859 | |
860 | stream.Put (dateString.Get (), 8); |
861 | |
862 | } |
863 | |
864 | dng_string timeString = fDigitalCreationDateTime.Encode_IPTC_Time (); |
865 | |
866 | if (timeString.NotEmpty ()) |
867 | { |
868 | |
869 | stream.Put_uint16 (0x1C02); |
870 | stream.Put_uint8 (kDigitalCreationTimeSet); |
871 | |
872 | stream.Put_uint16 ((uint16)timeString.Length ()); |
873 | |
874 | stream.Put (timeString.Get (), timeString.Length ()); |
875 | |
876 | } |
877 | |
878 | } |
879 | |
880 | for (j = 0; j < fAuthors.Count (); j++) |
881 | { |
882 | |
883 | SpoolString (stream, |
884 | fAuthors [j], |
885 | kBylineSet, |
886 | 32, |
887 | charSet); |
888 | |
889 | } |
890 | |
891 | SpoolString (stream, |
892 | fAuthorsPosition, |
893 | kBylineTitleSet, |
894 | 32, |
895 | charSet); |
896 | |
897 | SpoolString (stream, |
898 | fCity, |
899 | kCitySet, |
900 | 32, |
901 | charSet); |
902 | |
903 | SpoolString (stream, |
904 | fLocation, |
905 | kSublocationSet, |
906 | 32, |
907 | charSet); |
908 | |
909 | SpoolString (stream, |
910 | fState, |
911 | kProvinceStateSet, |
912 | 32, |
913 | charSet); |
914 | |
915 | SpoolString (stream, |
916 | fCountryCode, |
917 | kCountryCodeSet, |
918 | 3, |
919 | charSet); |
920 | |
921 | SpoolString (stream, |
922 | fCountry, |
923 | kCountryNameSet, |
924 | 64, |
925 | charSet); |
926 | |
927 | SpoolString (stream, |
928 | fTransmissionReference, |
929 | kOriginalTransmissionReferenceSet, |
930 | 32, |
931 | charSet); |
932 | |
933 | SpoolString (stream, |
934 | fHeadline, |
935 | kHeadlineSet, |
936 | 255, |
937 | charSet); |
938 | |
939 | SpoolString (stream, |
940 | fCredit, |
941 | kCreditSet, |
942 | 32, |
943 | charSet); |
944 | |
945 | SpoolString (stream, |
946 | fSource, |
947 | kSourceSet, |
948 | 32, |
949 | charSet); |
950 | |
951 | SpoolString (stream, |
952 | fCopyrightNotice, |
953 | kCopyrightNoticeSet, |
954 | 128, |
955 | charSet); |
956 | |
957 | SpoolString (stream, |
958 | fDescription, |
959 | kCaptionSet, |
960 | 2000, |
961 | charSet); |
962 | |
963 | SpoolString (stream, |
964 | fDescriptionWriter, |
965 | kCaptionWriterSet, |
966 | 32, |
967 | charSet); |
968 | |
969 | if (padForTIFF) |
970 | { |
971 | |
972 | while (stream.Length () & 3) |
973 | { |
974 | stream.Put_uint8 (0); |
975 | } |
976 | |
977 | } |
978 | |
979 | stream.Flush (); |
980 | |
981 | return stream.AsMemoryBlock (allocator); |
982 | |
983 | } |
984 | |
985 | /*****************************************************************************/ |
986 | |