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)
335 shared_ptr<dcp::InteropSubtitleAsset> c (new dcp::InteropSubtitleAsset());
336 c->set_reel_number ("1");
337 c->set_language ("EN");
338 c->set_movie_title ("Test");
341 shared_ptr<dcp::Subtitle> (
342 new dcp::SubtitleImage (
343 dcp::Data ("test/data/sub.png"),
344 dcp::Time (0, 4, 9, 22, 24),
345 dcp::Time (0, 4, 11, 22, 24),
350 dcp::Time (0, 0, 0, 0, 24),
351 dcp::Time (0, 0, 0, 0, 24)
356 c->_id = "a6c58cff-3e1e-4b38-acec-a42224475ef6";
357 boost::filesystem::create_directories ("build/test/write_interop_subtitle_test3");
358 c->write ("build/test/write_interop_subtitle_test3/subs.xml");
360 shared_ptr<dcp::Reel> reel (new dcp::Reel());
361 reel->add(shared_ptr<dcp::ReelSubtitleAsset>(new dcp::ReelSubtitleAsset(c, dcp::Fraction(24, 1), 6046, 0)));
363 dcp::XMLMetadata xml_meta;
364 xml_meta.issue_date = "2018-09-02T04:45:18+00:00";
365 xml_meta.issuer = "libdcp";
366 xml_meta.creator = "libdcp";
367 xml_meta.annotation_text = "Created by libdcp";
369 shared_ptr<dcp::CPL> cpl (new dcp::CPL ("My film", dcp::FEATURE));
371 cpl->set_metadata (xml_meta);
372 cpl->set_content_version_label_text ("foo");
374 dcp::DCP dcp ("build/test/write_interop_subtitle_test3");
376 dcp.write_xml (dcp::INTEROP, xml_meta);
379 dcp::file_to_string("test/ref/write_interop_subtitle_test3/subs.xml"),
380 dcp::file_to_string("build/test/write_interop_subtitle_test3/subs.xml"),
383 check_file ("build/test/write_interop_subtitle_test3/ef5c6baa-be2d-4f86-9f15-b1acc792ee8b.png", "test/data/sub.png");
386 dcp::file_to_string("test/ref/write_interop_subtitle_test3/ASSETMAP"),
387 dcp::file_to_string("build/test/write_interop_subtitle_test3/ASSETMAP"),
392 dcp::file_to_string("test/ref/write_interop_subtitle_test3/pkl.xml"),
393 dcp::file_to_string("build/test/write_interop_subtitle_test3/pkl_f5aab304-8145-44e3-a265-aa8d8812d8a2.xml"),
398 /* Write some subtitle content as SMPTE XML and check that it is right */
399 BOOST_AUTO_TEST_CASE (write_smpte_subtitle_test)
401 dcp::SMPTESubtitleAsset c;
402 c.set_reel_number (1);
403 c.set_language ("EN");
404 c.set_content_title_text ("Test");
405 c.set_issue_date (dcp::LocalTime ("2016-04-01T03:52:00+00:00"));
408 shared_ptr<dcp::Subtitle> (
409 new dcp::SubtitleString (
414 dcp::Colour (255, 255, 255),
417 dcp::Time (0, 4, 9, 22, 24),
418 dcp::Time (0, 4, 11, 22, 24),
426 dcp::Colour (0, 0, 0),
427 dcp::Time (0, 0, 0, 0, 24),
428 dcp::Time (0, 0, 0, 0, 24)
434 shared_ptr<dcp::Subtitle> (
435 new dcp::SubtitleString (
436 boost::optional<string> (),
440 dcp::Colour (128, 0, 64),
443 dcp::Time (5, 41, 0, 21, 24),
444 dcp::Time (6, 12, 15, 21, 24),
452 dcp::Colour (1, 2, 3),
453 dcp::Time (1, 2, 3, 4, 24),
454 dcp::Time (5, 6, 7, 8, 24)
459 c._xml_id = "a6c58cff-3e1e-4b38-acec-a42224475ef6";
462 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
463 "<dcst:SubtitleReel xmlns:dcst=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
464 "<dcst:Id>urn:uuid:a6c58cff-3e1e-4b38-acec-a42224475ef6</dcst:Id>"
465 "<dcst:ContentTitleText>Test</dcst:ContentTitleText>"
466 "<dcst:IssueDate>2016-04-01T03:52:00.000+00:00</dcst:IssueDate>"
467 "<dcst:ReelNumber>1</dcst:ReelNumber>"
468 "<dcst:Language>EN</dcst:Language>"
469 "<dcst:EditRate>24 1</dcst:EditRate>"
470 "<dcst:TimeCodeRate>24</dcst:TimeCodeRate>"
471 "<dcst:SubtitleList>"
472 "<dcst:Font AspectAdjust=\"1.0\" Color=\"FFFFFFFF\" Effect=\"none\" EffectColor=\"FF000000\" ID=\"Frutiger\" Italic=\"no\" Script=\"normal\" Size=\"48\" Underline=\"no\" Weight=\"normal\">"
473 "<dcst:Subtitle SpotNumber=\"1\" TimeIn=\"00:04:09:22\" TimeOut=\"00:04:11:22\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
474 "<dcst:Text Valign=\"top\" Vposition=\"80\">Hello world</dcst:Text>"
477 "<dcst:Font AspectAdjust=\"1.0\" Color=\"FF800040\" Effect=\"border\" EffectColor=\"FF010203\" Italic=\"yes\" Script=\"normal\" Size=\"91\" Underline=\"yes\" Weight=\"bold\">"
478 "<dcst:Subtitle SpotNumber=\"2\" TimeIn=\"05:41:00:21\" TimeOut=\"06:12:15:21\" FadeUpTime=\"01:02:03:04\" FadeDownTime=\"05:06:07:08\">"
479 "<dcst:Text Valign=\"bottom\" Vposition=\"40\" Direction=\"rtl\">What's going on</dcst:Text>"
482 "</dcst:SubtitleList>"
483 "</dcst:SubtitleReel>",
489 /* Write some subtitle content as SMPTE XML and check that it is right.
490 This includes in-line font changes.
492 BOOST_AUTO_TEST_CASE (write_smpte_subtitle_test2)
494 dcp::SMPTESubtitleAsset c;
495 c.set_reel_number (1);
496 c.set_language ("EN");
497 c.set_content_title_text ("Test");
498 c.set_issue_date (dcp::LocalTime ("2016-04-01T03:52:00+00:00"));
501 shared_ptr<dcp::Subtitle> (
502 new dcp::SubtitleString (
507 dcp::Colour (255, 255, 255),
510 dcp::Time (0, 0, 1, 0, 24),
511 dcp::Time (0, 0, 9, 0, 24),
519 dcp::Colour (0, 0, 0),
520 dcp::Time (0, 0, 0, 0, 24),
521 dcp::Time (0, 0, 0, 0, 24)
527 shared_ptr<dcp::Subtitle> (
528 new dcp::SubtitleString (
533 dcp::Colour (255, 255, 255),
536 dcp::Time (0, 0, 1, 0, 24),
537 dcp::Time (0, 0, 9, 0, 24),
545 dcp::Colour (0, 0, 0),
546 dcp::Time (0, 0, 0, 0, 24),
547 dcp::Time (0, 0, 0, 0, 24)
553 shared_ptr<dcp::Subtitle> (
554 new dcp::SubtitleString (
559 dcp::Colour (255, 255, 255),
562 dcp::Time (0, 0, 1, 0, 24),
563 dcp::Time (0, 0, 9, 0, 24),
571 dcp::Colour (0, 0, 0),
572 dcp::Time (0, 0, 0, 0, 24),
573 dcp::Time (0, 0, 0, 0, 24)
579 shared_ptr<dcp::Subtitle> (
580 new dcp::SubtitleString (
585 dcp::Colour (255, 255, 255),
588 dcp::Time (0, 0, 1, 0, 24),
589 dcp::Time (0, 0, 9, 0, 24),
597 dcp::Colour (0, 0, 0),
598 dcp::Time (0, 0, 0, 0, 24),
599 dcp::Time (0, 0, 0, 0, 24)
605 shared_ptr<dcp::Subtitle> (
606 new dcp::SubtitleString (
611 dcp::Colour (255, 255, 255),
614 dcp::Time (0, 0, 1, 0, 24),
615 dcp::Time (0, 0, 9, 0, 24),
623 dcp::Colour (0, 0, 0),
624 dcp::Time (0, 0, 0, 0, 24),
625 dcp::Time (0, 0, 0, 0, 24)
631 shared_ptr<dcp::Subtitle> (
632 new dcp::SubtitleString (
637 dcp::Colour (255, 255, 255),
640 dcp::Time (0, 0, 1, 0, 24),
641 dcp::Time (0, 0, 9, 0, 24),
649 dcp::Colour (0, 0, 0),
650 dcp::Time (0, 0, 0, 0, 24),
651 dcp::Time (0, 0, 0, 0, 24)
656 c._xml_id = "a6c58cff-3e1e-4b38-acec-a42224475ef6";
660 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
661 "<dcst:SubtitleReel xmlns:dcst=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
662 "<dcst:Id>urn:uuid:a6c58cff-3e1e-4b38-acec-a42224475ef6</dcst:Id>"
663 "<dcst:ContentTitleText>Test</dcst:ContentTitleText>"
664 "<dcst:IssueDate>2016-04-01T03:52:00.000+00:00</dcst:IssueDate>"
665 "<dcst:ReelNumber>1</dcst:ReelNumber>"
666 "<dcst:Language>EN</dcst:Language>"
667 "<dcst:EditRate>24 1</dcst:EditRate>"
668 "<dcst:TimeCodeRate>24</dcst:TimeCodeRate>"
669 "<dcst:SubtitleList>"
670 "<dcst:Font AspectAdjust=\"1.0\" Color=\"FFFFFFFF\" Effect=\"none\" EffectColor=\"FF000000\" ID=\"Arial\" Script=\"normal\" Size=\"48\" Underline=\"no\" Weight=\"normal\">"
671 "<dcst:Subtitle SpotNumber=\"1\" TimeIn=\"00:00:01:00\" TimeOut=\"00:00:09:00\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
672 "<dcst:Text Valign=\"top\" Vposition=\"80\">"
673 "<dcst:Font Italic=\"no\">Testing is </dcst:Font>"
674 "<dcst:Font Italic=\"yes\">really</dcst:Font>"
675 "<dcst:Font Italic=\"no\"> fun</dcst:Font>"
677 "<dcst:Text Valign=\"top\" Vposition=\"90\">"
678 "<dcst:Font Italic=\"no\">This is the </dcst:Font>"
679 "<dcst:Font Italic=\"yes\">second</dcst:Font>"
680 "<dcst:Font Italic=\"no\"> line</dcst:Font>"
684 "</dcst:SubtitleList>"
685 "</dcst:SubtitleReel>",
690 /* Write some subtitle content as SMPTE using bitmaps and check that it is right */
691 BOOST_AUTO_TEST_CASE (write_smpte_subtitle_test3)
693 dcp::SMPTESubtitleAsset c;
694 c.set_reel_number (1);
695 c.set_language ("EN");
696 c.set_content_title_text ("Test");
699 shared_ptr<dcp::Subtitle> (
700 new dcp::SubtitleImage (
701 dcp::Data ("test/data/sub.png"),
702 dcp::Time (0, 4, 9, 22, 24),
703 dcp::Time (0, 4, 11, 22, 24),
708 dcp::Time (0, 0, 0, 0, 24),
709 dcp::Time (0, 0, 0, 0, 24)
714 c._id = "a6c58cff-3e1e-4b38-acec-a42224475ef6";
716 boost::filesystem::create_directories ("build/test/write_smpte_subtitle_test3");
717 c.write ("build/test/write_smpte_subtitle_test3/subs.mxf");
719 /* XXX: check this result when we can read them back in again */