2 Copyright (C) 2015-2019 Carl Hetherington <cth@carlh.net>
4 This file is part of libdcp.
6 libdcp is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 libdcp is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with libdcp. If not, see <http://www.gnu.org/licenses/>.
19 In addition, as a special exception, the copyright holders give
20 permission to link the code of portions of this program with the
21 OpenSSL library under certain conditions as described in each
22 individual source file, and distribute linked combinations
25 You must obey the GNU General Public License in all respects
26 for all of the code used other than OpenSSL. If you modify
27 file(s) with this exception, you may extend this exception to your
28 version of the file(s), but you are not obligated to do so. If you
29 do not wish to do so, delete this exception statement from your
30 version. If you delete this exception statement from all source
31 files in the program, then also delete it here.
34 #include "interop_subtitle_asset.h"
35 #include "smpte_subtitle_asset.h"
36 #include "subtitle_string.h"
37 #include "subtitle_image.h"
38 #include "subtitle_asset_internal.h"
39 #include "reel_subtitle_asset.h"
45 #include <boost/test/unit_test.hpp>
49 using boost::shared_ptr;
51 /** Test dcp::order::Font::take_intersection */
52 BOOST_AUTO_TEST_CASE (take_intersection_test)
55 A._values["foo"] = "bar";
56 A._values["fred"] = "jim";
59 B._values["foo"] = "bar";
60 B._values["sheila"] = "baz";
62 A.take_intersection (B);
63 BOOST_REQUIRE_EQUAL (A._values.size(), 1);
64 BOOST_CHECK_EQUAL (A._values["foo"], "bar");
69 A._values["foo"] = "bar";
70 A._values["fred"] = "jim";
72 B._values["foo"] = "hello";
73 B._values["sheila"] = "baz";
75 A.take_intersection (B);
76 BOOST_CHECK_EQUAL (A._values.size(), 0);
79 /** Test dcp::order::Font::take_difference */
80 BOOST_AUTO_TEST_CASE (take_difference_test)
83 A._values["foo"] = "bar";
84 A._values["fred"] = "jim";
87 B._values["foo"] = "bar";
88 B._values["sheila"] = "baz";
90 A.take_difference (B);
91 BOOST_REQUIRE_EQUAL (A._values.size(), 1);
92 BOOST_CHECK_EQUAL (A._values["fred"], "jim");
95 /** Test dcp::order::Subtitle::pull_fonts */
96 BOOST_AUTO_TEST_CASE (pull_fonts_test1)
98 shared_ptr<dcp::order::Part> root (new dcp::order::Part (shared_ptr<dcp::order::Part> ()));
99 shared_ptr<dcp::order::Subtitle> sub1 (new dcp::order::Subtitle (root, dcp::Time(), dcp::Time(), dcp::Time(), dcp::Time()));
100 root->children.push_back (sub1);
101 shared_ptr<dcp::order::Text> text1 (new dcp::order::Text (sub1, dcp::HALIGN_CENTER, 0, dcp::VALIGN_TOP, 0, dcp::DIRECTION_LTR));
102 sub1->children.push_back (text1);
103 text1->font._values["font"] = "Inconsolata";
104 text1->font._values["size"] = "42";
106 dcp::SubtitleAsset::pull_fonts (root);
108 BOOST_REQUIRE_EQUAL (sub1->font._values.size(), 2);
109 BOOST_CHECK_EQUAL (sub1->font._values["font"], "Inconsolata");
110 BOOST_CHECK_EQUAL (sub1->font._values["size"], "42");
111 BOOST_CHECK_EQUAL (text1->font._values.size(), 0);
114 /** Test dcp::order::Subtitle::pull_fonts */
115 BOOST_AUTO_TEST_CASE (pull_fonts_test2)
117 shared_ptr<dcp::order::Part> root (new dcp::order::Part (shared_ptr<dcp::order::Part> ()));
118 shared_ptr<dcp::order::Subtitle> sub1 (new dcp::order::Subtitle (root, dcp::Time(), dcp::Time(), dcp::Time(), dcp::Time()));
119 root->children.push_back (sub1);
120 shared_ptr<dcp::order::Text> text1 (new dcp::order::Text (sub1, dcp::HALIGN_CENTER, 0, dcp::VALIGN_TOP, 0, dcp::DIRECTION_LTR));
121 sub1->children.push_back (text1);
122 text1->font._values["font"] = "Inconsolata";
123 text1->font._values["size"] = "42";
124 shared_ptr<dcp::order::Text> text2 (new dcp::order::Text (sub1, dcp::HALIGN_CENTER, 0, dcp::VALIGN_TOP, 0, dcp::DIRECTION_LTR));
125 sub1->children.push_back (text2);
126 text2->font._values["font"] = "Inconsolata";
127 text2->font._values["size"] = "48";
129 dcp::SubtitleAsset::pull_fonts (root);
131 BOOST_REQUIRE_EQUAL (sub1->font._values.size(), 1);
132 BOOST_CHECK_EQUAL (sub1->font._values["font"], "Inconsolata");
133 BOOST_REQUIRE_EQUAL (text1->font._values.size(), 1);
134 BOOST_CHECK_EQUAL (text1->font._values["size"], "42");
135 BOOST_REQUIRE_EQUAL (text2->font._values.size(), 1);
136 BOOST_CHECK_EQUAL (text2->font._values["size"], "48");
139 /** Test dcp::order::Subtitle::pull_fonts */
140 BOOST_AUTO_TEST_CASE (pull_fonts_test3)
142 shared_ptr<dcp::order::Part> root (new dcp::order::Part (shared_ptr<dcp::order::Part> ()));
143 shared_ptr<dcp::order::Subtitle> sub1 (new dcp::order::Subtitle (root, dcp::Time(), dcp::Time(), dcp::Time(), dcp::Time()));
144 root->children.push_back (sub1);
145 shared_ptr<dcp::order::Text> text1 (new dcp::order::Text (sub1, dcp::HALIGN_CENTER, 0, dcp::VALIGN_TOP, 0, dcp::DIRECTION_LTR));
146 sub1->children.push_back (text1);
147 dcp::order::Font font;
148 font._values["font"] = "Inconsolata";
149 font._values["size"] = "42";
150 shared_ptr<dcp::order::String> string1 (new dcp::order::String (text1, font, "Hello world"));
151 text1->children.push_back (string1);
153 dcp::SubtitleAsset::pull_fonts (root);
155 BOOST_REQUIRE_EQUAL (sub1->font._values.size(), 2);
156 BOOST_CHECK_EQUAL (sub1->font._values["font"], "Inconsolata");
157 BOOST_CHECK_EQUAL (sub1->font._values["size"], "42");
160 /** Write some subtitle content as Interop XML and check that it is right */
161 BOOST_AUTO_TEST_CASE (write_interop_subtitle_test)
163 dcp::InteropSubtitleAsset c;
164 c.set_reel_number ("1");
165 c.set_language ("EN");
166 c.set_movie_title ("Test");
169 shared_ptr<dcp::Subtitle> (
170 new dcp::SubtitleString (
175 dcp::Colour (255, 255, 255),
178 dcp::Time (0, 4, 9, 22, 24),
179 dcp::Time (0, 4, 11, 22, 24),
187 dcp::Colour (0, 0, 0),
188 dcp::Time (0, 0, 0, 0, 24),
189 dcp::Time (0, 0, 0, 0, 24)
195 shared_ptr<dcp::Subtitle> (
196 new dcp::SubtitleString (
197 boost::optional<string> (),
201 dcp::Colour (128, 0, 64),
204 dcp::Time (5, 41, 0, 21, 24),
205 dcp::Time (6, 12, 15, 21, 24),
213 dcp::Colour (1, 2, 3),
214 dcp::Time (1, 2, 3, 4, 24),
215 dcp::Time (5, 6, 7, 8, 24)
220 c._id = "a6c58cff-3e1e-4b38-acec-a42224475ef6";
223 "<DCSubtitle Version=\"1.0\">"
224 "<SubtitleID>a6c58cff-3e1e-4b38-acec-a42224475ef6</SubtitleID>"
225 "<MovieTitle>Test</MovieTitle>"
226 "<ReelNumber>1</ReelNumber>"
227 "<Language>EN</Language>"
228 "<Font AspectAdjust=\"1.0\" Color=\"FFFFFFFF\" Effect=\"none\" EffectColor=\"FF000000\" Id=\"Frutiger\" Italic=\"no\" Script=\"normal\" Size=\"48\" Underlined=\"no\" Weight=\"normal\">"
229 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:04:09:229\" TimeOut=\"00:04:11:229\" FadeUpTime=\"0\" FadeDownTime=\"0\">"
230 "<Text VAlign=\"top\" VPosition=\"80\">Hello world</Text>"
233 "<Font AspectAdjust=\"1.0\" Color=\"FF800040\" Effect=\"border\" EffectColor=\"FF010203\" Italic=\"yes\" Script=\"normal\" Size=\"91\" Underlined=\"yes\" Weight=\"bold\">"
234 "<Subtitle SpotNumber=\"2\" TimeIn=\"05:41:00:219\" TimeOut=\"06:12:15:219\" FadeUpTime=\"930792\" FadeDownTime=\"4591834\">"
235 "<Text VAlign=\"bottom\" VPosition=\"40\">What's going on</Text>"
244 /** Write some subtitle content as Interop XML and check that it is right.
245 * This test includes some horizontal alignment.
247 BOOST_AUTO_TEST_CASE (write_interop_subtitle_test2)
249 dcp::InteropSubtitleAsset c;
250 c.set_reel_number ("1");
251 c.set_language ("EN");
252 c.set_movie_title ("Test");
255 shared_ptr<dcp::Subtitle> (
256 new dcp::SubtitleString (
261 dcp::Colour (255, 255, 255),
264 dcp::Time (0, 4, 9, 22, 24),
265 dcp::Time (0, 4, 11, 22, 24),
273 dcp::Colour (0, 0, 0),
274 dcp::Time (0, 0, 0, 0, 24),
275 dcp::Time (0, 0, 0, 0, 24)
281 shared_ptr<dcp::Subtitle> (
282 new dcp::SubtitleString (
283 boost::optional<string> (),
287 dcp::Colour (128, 0, 64),
290 dcp::Time (5, 41, 0, 21, 24),
291 dcp::Time (6, 12, 15, 21, 24),
299 dcp::Colour (1, 2, 3),
300 dcp::Time (1, 2, 3, 4, 24),
301 dcp::Time (5, 6, 7, 8, 24)
306 c._id = "a6c58cff-3e1e-4b38-acec-a42224475ef6";
309 "<DCSubtitle Version=\"1.0\">"
310 "<SubtitleID>a6c58cff-3e1e-4b38-acec-a42224475ef6</SubtitleID>"
311 "<MovieTitle>Test</MovieTitle>"
312 "<ReelNumber>1</ReelNumber>"
313 "<Language>EN</Language>"
314 "<Font AspectAdjust=\"1.0\" Color=\"FFFFFFFF\" Effect=\"none\" EffectColor=\"FF000000\" Id=\"Frutiger\" Italic=\"no\" Script=\"normal\" Size=\"48\" Underlined=\"no\" Weight=\"normal\">"
315 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:04:09:229\" TimeOut=\"00:04:11:229\" FadeUpTime=\"0\" FadeDownTime=\"0\">"
316 "<Text HPosition=\"-20\" VAlign=\"top\" VPosition=\"80\">Hello world</Text>"
319 "<Font AspectAdjust=\"1.0\" Color=\"FF800040\" Effect=\"border\" EffectColor=\"FF010203\" Italic=\"yes\" Script=\"normal\" Size=\"91\" Underlined=\"yes\" Weight=\"bold\">"
320 "<Subtitle SpotNumber=\"2\" TimeIn=\"05:41:00:219\" TimeOut=\"06:12:15:219\" FadeUpTime=\"930792\" FadeDownTime=\"4591834\">"
321 "<Text HPosition=\"-20\" VAlign=\"bottom\" VPosition=\"40\">What's going on</Text>"
330 /* Write some subtitle content as Interop XML using bitmaps and check that it is right */
331 BOOST_AUTO_TEST_CASE (write_interop_subtitle_test3)
333 shared_ptr<dcp::InteropSubtitleAsset> c (new dcp::InteropSubtitleAsset());
334 c->set_reel_number ("1");
335 c->set_language ("EN");
336 c->set_movie_title ("Test");
339 shared_ptr<dcp::Subtitle> (
340 new dcp::SubtitleImage (
341 dcp::Data ("test/data/sub.png"),
342 dcp::Time (0, 4, 9, 22, 24),
343 dcp::Time (0, 4, 11, 22, 24),
348 dcp::Time (0, 0, 0, 0, 24),
349 dcp::Time (0, 0, 0, 0, 24)
354 c->_id = "a6c58cff-3e1e-4b38-acec-a42224475ef6";
355 boost::filesystem::create_directories ("build/test/write_interop_subtitle_test3");
356 c->write ("build/test/write_interop_subtitle_test3/subs.xml");
358 shared_ptr<dcp::Reel> reel (new dcp::Reel());
359 reel->add(shared_ptr<dcp::ReelSubtitleAsset>(new dcp::ReelSubtitleAsset(c, dcp::Fraction(24, 1), 6046, 0)));
361 dcp::XMLMetadata xml_meta;
362 xml_meta.issue_date = "2018-09-02T04:45:18+00:00";
363 xml_meta.issuer = "libdcp";
364 xml_meta.creator = "libdcp";
365 xml_meta.annotation_text = "Created by libdcp";
367 shared_ptr<dcp::CPL> cpl (new dcp::CPL ("My film", dcp::FEATURE));
369 cpl->set_metadata (xml_meta);
370 cpl->set_content_version_label_text ("foo");
372 dcp::DCP dcp ("build/test/write_interop_subtitle_test3");
374 dcp.write_xml (dcp::INTEROP, xml_meta);
377 dcp::file_to_string("test/ref/write_interop_subtitle_test3/subs.xml"),
378 dcp::file_to_string("build/test/write_interop_subtitle_test3/subs.xml"),
381 check_file ("build/test/write_interop_subtitle_test3/ef5c6baa-be2d-4f86-9f15-b1acc792ee8b.png", "test/data/sub.png");
384 dcp::file_to_string("test/ref/write_interop_subtitle_test3/ASSETMAP"),
385 dcp::file_to_string("build/test/write_interop_subtitle_test3/ASSETMAP"),
390 dcp::file_to_string("test/ref/write_interop_subtitle_test3/pkl.xml"),
391 dcp::file_to_string("build/test/write_interop_subtitle_test3/pkl_f5aab304-8145-44e3-a265-aa8d8812d8a2.xml"),
396 /* Write some subtitle content as SMPTE XML and check that it is right */
397 BOOST_AUTO_TEST_CASE (write_smpte_subtitle_test)
399 dcp::SMPTESubtitleAsset c;
400 c.set_reel_number (1);
401 c.set_language ("EN");
402 c.set_content_title_text ("Test");
403 c.set_issue_date (dcp::LocalTime ("2016-04-01T03:52:00+00:00"));
406 shared_ptr<dcp::Subtitle> (
407 new dcp::SubtitleString (
412 dcp::Colour (255, 255, 255),
415 dcp::Time (0, 4, 9, 22, 24),
416 dcp::Time (0, 4, 11, 22, 24),
424 dcp::Colour (0, 0, 0),
425 dcp::Time (0, 0, 0, 0, 24),
426 dcp::Time (0, 0, 0, 0, 24)
432 shared_ptr<dcp::Subtitle> (
433 new dcp::SubtitleString (
434 boost::optional<string> (),
438 dcp::Colour (128, 0, 64),
441 dcp::Time (5, 41, 0, 21, 24),
442 dcp::Time (6, 12, 15, 21, 24),
450 dcp::Colour (1, 2, 3),
451 dcp::Time (1, 2, 3, 4, 24),
452 dcp::Time (5, 6, 7, 8, 24)
457 c._xml_id = "a6c58cff-3e1e-4b38-acec-a42224475ef6";
460 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
461 "<dcst:SubtitleReel xmlns:dcst=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
462 "<dcst:Id>urn:uuid:a6c58cff-3e1e-4b38-acec-a42224475ef6</dcst:Id>"
463 "<dcst:ContentTitleText>Test</dcst:ContentTitleText>"
464 "<dcst:IssueDate>2016-04-01T03:52:00.000+00:00</dcst:IssueDate>"
465 "<dcst:ReelNumber>1</dcst:ReelNumber>"
466 "<dcst:Language>EN</dcst:Language>"
467 "<dcst:EditRate>24 1</dcst:EditRate>"
468 "<dcst:TimeCodeRate>24</dcst:TimeCodeRate>"
469 "<dcst:SubtitleList>"
470 "<dcst:Font AspectAdjust=\"1.0\" Color=\"FFFFFFFF\" Effect=\"none\" EffectColor=\"FF000000\" ID=\"Frutiger\" Italic=\"no\" Script=\"normal\" Size=\"48\" Underline=\"no\" Weight=\"normal\">"
471 "<dcst:Subtitle SpotNumber=\"1\" TimeIn=\"00:04:09:22\" TimeOut=\"00:04:11:22\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
472 "<dcst:Text Valign=\"top\" Vposition=\"80\">Hello world</dcst:Text>"
475 "<dcst:Font AspectAdjust=\"1.0\" Color=\"FF800040\" Effect=\"border\" EffectColor=\"FF010203\" Italic=\"yes\" Script=\"normal\" Size=\"91\" Underline=\"yes\" Weight=\"bold\">"
476 "<dcst:Subtitle SpotNumber=\"2\" TimeIn=\"05:41:00:21\" TimeOut=\"06:12:15:21\" FadeUpTime=\"01:02:03:04\" FadeDownTime=\"05:06:07:08\">"
477 "<dcst:Text Valign=\"bottom\" Vposition=\"40\" Direction=\"rtl\">What's going on</dcst:Text>"
480 "</dcst:SubtitleList>"
481 "</dcst:SubtitleReel>",
487 /* Write some subtitle content as SMPTE XML and check that it is right.
488 This includes in-line font changes.
490 BOOST_AUTO_TEST_CASE (write_smpte_subtitle_test2)
492 dcp::SMPTESubtitleAsset c;
493 c.set_reel_number (1);
494 c.set_language ("EN");
495 c.set_content_title_text ("Test");
496 c.set_issue_date (dcp::LocalTime ("2016-04-01T03:52:00+00:00"));
499 shared_ptr<dcp::Subtitle> (
500 new dcp::SubtitleString (
505 dcp::Colour (255, 255, 255),
508 dcp::Time (0, 0, 1, 0, 24),
509 dcp::Time (0, 0, 9, 0, 24),
517 dcp::Colour (0, 0, 0),
518 dcp::Time (0, 0, 0, 0, 24),
519 dcp::Time (0, 0, 0, 0, 24)
525 shared_ptr<dcp::Subtitle> (
526 new dcp::SubtitleString (
531 dcp::Colour (255, 255, 255),
534 dcp::Time (0, 0, 1, 0, 24),
535 dcp::Time (0, 0, 9, 0, 24),
543 dcp::Colour (0, 0, 0),
544 dcp::Time (0, 0, 0, 0, 24),
545 dcp::Time (0, 0, 0, 0, 24)
551 shared_ptr<dcp::Subtitle> (
552 new dcp::SubtitleString (
557 dcp::Colour (255, 255, 255),
560 dcp::Time (0, 0, 1, 0, 24),
561 dcp::Time (0, 0, 9, 0, 24),
569 dcp::Colour (0, 0, 0),
570 dcp::Time (0, 0, 0, 0, 24),
571 dcp::Time (0, 0, 0, 0, 24)
577 shared_ptr<dcp::Subtitle> (
578 new dcp::SubtitleString (
583 dcp::Colour (255, 255, 255),
586 dcp::Time (0, 0, 1, 0, 24),
587 dcp::Time (0, 0, 9, 0, 24),
595 dcp::Colour (0, 0, 0),
596 dcp::Time (0, 0, 0, 0, 24),
597 dcp::Time (0, 0, 0, 0, 24)
603 shared_ptr<dcp::Subtitle> (
604 new dcp::SubtitleString (
609 dcp::Colour (255, 255, 255),
612 dcp::Time (0, 0, 1, 0, 24),
613 dcp::Time (0, 0, 9, 0, 24),
621 dcp::Colour (0, 0, 0),
622 dcp::Time (0, 0, 0, 0, 24),
623 dcp::Time (0, 0, 0, 0, 24)
629 shared_ptr<dcp::Subtitle> (
630 new dcp::SubtitleString (
635 dcp::Colour (255, 255, 255),
638 dcp::Time (0, 0, 1, 0, 24),
639 dcp::Time (0, 0, 9, 0, 24),
647 dcp::Colour (0, 0, 0),
648 dcp::Time (0, 0, 0, 0, 24),
649 dcp::Time (0, 0, 0, 0, 24)
654 c._xml_id = "a6c58cff-3e1e-4b38-acec-a42224475ef6";
658 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
659 "<dcst:SubtitleReel xmlns:dcst=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
660 "<dcst:Id>urn:uuid:a6c58cff-3e1e-4b38-acec-a42224475ef6</dcst:Id>"
661 "<dcst:ContentTitleText>Test</dcst:ContentTitleText>"
662 "<dcst:IssueDate>2016-04-01T03:52:00.000+00:00</dcst:IssueDate>"
663 "<dcst:ReelNumber>1</dcst:ReelNumber>"
664 "<dcst:Language>EN</dcst:Language>"
665 "<dcst:EditRate>24 1</dcst:EditRate>"
666 "<dcst:TimeCodeRate>24</dcst:TimeCodeRate>"
667 "<dcst:SubtitleList>"
668 "<dcst:Font AspectAdjust=\"1.0\" Color=\"FFFFFFFF\" Effect=\"none\" EffectColor=\"FF000000\" ID=\"Arial\" Script=\"normal\" Size=\"48\" Underline=\"no\" Weight=\"normal\">"
669 "<dcst:Subtitle SpotNumber=\"1\" TimeIn=\"00:00:01:00\" TimeOut=\"00:00:09:00\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
670 "<dcst:Text Valign=\"top\" Vposition=\"80\">"
671 "<dcst:Font Italic=\"no\">Testing is </dcst:Font>"
672 "<dcst:Font Italic=\"yes\">really</dcst:Font>"
673 "<dcst:Font Italic=\"no\"> fun</dcst:Font>"
675 "<dcst:Text Valign=\"top\" Vposition=\"90\">"
676 "<dcst:Font Italic=\"no\">This is the </dcst:Font>"
677 "<dcst:Font Italic=\"yes\">second</dcst:Font>"
678 "<dcst:Font Italic=\"no\"> line</dcst:Font>"
682 "</dcst:SubtitleList>"
683 "</dcst:SubtitleReel>",
688 /* Write some subtitle content as SMPTE using bitmaps and check that it is right */
689 BOOST_AUTO_TEST_CASE (write_smpte_subtitle_test3)
691 dcp::SMPTESubtitleAsset c;
692 c.set_reel_number (1);
693 c.set_language ("EN");
694 c.set_content_title_text ("Test");
697 shared_ptr<dcp::Subtitle> (
698 new dcp::SubtitleImage (
699 dcp::Data ("test/data/sub.png"),
700 dcp::Time (0, 4, 9, 22, 24),
701 dcp::Time (0, 4, 11, 22, 24),
706 dcp::Time (0, 0, 0, 0, 24),
707 dcp::Time (0, 0, 0, 0, 24)
712 c._id = "a6c58cff-3e1e-4b38-acec-a42224475ef6";
714 boost::filesystem::create_directories ("build/test/write_smpte_subtitle_test3");
715 c.write ("build/test/write_smpte_subtitle_test3/subs.mxf");
717 /* XXX: check this result when we can read them back in again */