Builder.vala 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. using Invercargill;
  2. using Gee;
  3. namespace Ppub {
  4. public class Builder {
  5. private Series<BuilderAsset> assets = new Series<BuilderAsset>();
  6. private uint64 position = 0;
  7. public void add_metadata(Metadata metadata) {
  8. var data = (uint8[])metadata.to_string().to_utf8();
  9. var mis = new MemoryInputStream.from_data(data);
  10. var compress = new CompressionInfo(mis);
  11. mis.seek(0, SeekType.SET);
  12. add_asset("metadata", "application/x-ppub-metadata", mis, empty<string>(), compress);
  13. }
  14. public void add_asset(string name, string mimetype, InputStream stream, Enumerable<string> flags, CompressionInfo compression) {
  15. lock(assets) {
  16. var asset = new BuilderAsset();
  17. asset.name = name;
  18. asset.start_location = position;
  19. asset.end_location = position + compression.size_in_ppub;
  20. asset.mimetype = mimetype;
  21. asset.stream = stream;
  22. // Exclude any gzip flags, as we will add that later if we are compressing the content
  23. asset.flags = flags.where(f => f != "gzip").to_vector();
  24. if(compression.compress) {
  25. asset.flags.add("gzip");
  26. }
  27. position += compression.size_in_ppub;
  28. assets.add(asset);
  29. }
  30. }
  31. public void write(OutputStream stream) throws IOError {
  32. var index_entries = assets.to_string(a => a.to_string());
  33. print(index_entries);
  34. var index_data = (uint8[])index_entries.to_utf8();
  35. var header_data = (uint8[])(@"$(Publication.MAGIC)\n$(index_data.length)\n".to_utf8());
  36. stream.write(header_data);
  37. stream.write(index_data);
  38. foreach (var asset in assets) {
  39. if(asset.flags.any(f => f == "gzip")) {
  40. var compressor = new ZlibCompressor(ZlibCompressorFormat.GZIP, 9);
  41. var conv_stream = new ConverterInputStream(asset.stream, compressor);
  42. stream.splice(conv_stream, OutputStreamSpliceFlags.CLOSE_SOURCE);
  43. }
  44. else {
  45. stream.splice(asset.stream, OutputStreamSpliceFlags.CLOSE_SOURCE);
  46. }
  47. }
  48. }
  49. }
  50. public class BuilderAsset : Asset {
  51. public InputStream stream {get; set;}
  52. }
  53. public class CompressionInfo {
  54. public bool compress {get; private set;}
  55. public uint64 uncompressed_size {get; private set;}
  56. public uint64 compressed_size {get; private set;}
  57. public CompressionInfo.uncompressed(uint64 size) {
  58. compress = false;
  59. uncompressed_size = size;
  60. }
  61. public CompressionInfo(InputStream stream, CompressionPolicy policy = CompressionPolicy.AUTO) throws IOError {
  62. var uncompressed_counter = new InputStreamMonitor(stream);
  63. var compressor = new ZlibCompressor(ZlibCompressorFormat.GZIP, 9);
  64. var conv_stream = new ConverterInputStream(uncompressed_counter, compressor);
  65. var compressed_counter = new InputStreamMonitor(conv_stream);
  66. compressed_counter.count_to_end();
  67. uncompressed_size = uncompressed_counter.bytes_read;
  68. compressed_size = compressed_counter.bytes_read;
  69. switch (policy) {
  70. case CompressionPolicy.AUTO:
  71. compress = uncompressed_size > compressed_size;
  72. break;
  73. case CompressionPolicy.ALWAYS_COMPRESS:
  74. compress = true;
  75. break;
  76. case CompressionPolicy.NEVER_COMPRESS:
  77. compress = false;
  78. break;
  79. }
  80. }
  81. public uint64 size_in_ppub {
  82. get {
  83. return compress ? compressed_size : uncompressed_size;
  84. }
  85. }
  86. }
  87. public enum CompressionPolicy {
  88. AUTO,
  89. ALWAYS_COMPRESS,
  90. NEVER_COMPRESS
  91. }
  92. public static string guess_mimetype(string name, uint8[] sample) {
  93. if(name == "metadata") {
  94. return "application/x-ppub-metadata";
  95. }
  96. if(name.has_suffix(".ppvm")) {
  97. return "application/x-ppvm";
  98. }
  99. bool result_uncertain;
  100. var content_type = ContentType.guess(name, sample, out result_uncertain);
  101. var mime = ContentType.get_mime_type(content_type);
  102. return mime ?? "application/octet-stream";
  103. }
  104. }