using Invercargill; using Gee; namespace Ppub { public class Builder { private Series assets = new Series(); private uint64 position = 0; public void add_metadata(Metadata metadata) { var data = (uint8[])metadata.to_string().to_utf8(); var mis = new MemoryInputStream.from_data(data); var compress = new CompressionInfo(mis); mis.seek(0, SeekType.SET); add_asset("metadata", "application/x-ppub-metadata", mis, empty(), compress); } public void add_asset(string name, string mimetype, InputStream stream, Enumerable flags, CompressionInfo compression) { lock(assets) { var asset = new BuilderAsset(); asset.name = name; asset.start_location = position; asset.end_location = position + compression.size_in_ppub; asset.mimetype = mimetype; asset.stream = stream; // Exclude any gzip flags, as we will add that later if we are compressing the content asset.flags = flags.where(f => f != "gzip").to_vector(); if(compression.compress) { asset.flags.add("gzip"); } position += compression.size_in_ppub; assets.add(asset); } } public void write(OutputStream stream) throws IOError { var index_entries = assets.to_string(a => a.to_string()); print(index_entries); var index_data = (uint8[])index_entries.to_utf8(); var header_data = (uint8[])(@"$(Publication.MAGIC)\n$(index_data.length)\n".to_utf8()); stream.write(header_data); stream.write(index_data); foreach (var asset in assets) { if(asset.flags.any(f => f == "gzip")) { var compressor = new ZlibCompressor(ZlibCompressorFormat.GZIP, 9); var conv_stream = new ConverterInputStream(asset.stream, compressor); stream.splice(conv_stream, OutputStreamSpliceFlags.CLOSE_SOURCE); } else { stream.splice(asset.stream, OutputStreamSpliceFlags.CLOSE_SOURCE); } } } } public class BuilderAsset : Asset { public InputStream stream {get; set;} } public class CompressionInfo { public bool compress {get; private set;} public uint64 uncompressed_size {get; private set;} public uint64 compressed_size {get; private set;} public CompressionInfo.uncompressed(uint64 size) { compress = false; uncompressed_size = size; } public CompressionInfo(InputStream stream, CompressionPolicy policy = CompressionPolicy.AUTO) throws IOError { var uncompressed_counter = new InputStreamMonitor(stream); var compressor = new ZlibCompressor(ZlibCompressorFormat.GZIP, 9); var conv_stream = new ConverterInputStream(uncompressed_counter, compressor); var compressed_counter = new InputStreamMonitor(conv_stream); compressed_counter.count_to_end(); uncompressed_size = uncompressed_counter.bytes_read; compressed_size = compressed_counter.bytes_read; switch (policy) { case CompressionPolicy.AUTO: compress = uncompressed_size > compressed_size; break; case CompressionPolicy.ALWAYS_COMPRESS: compress = true; break; case CompressionPolicy.NEVER_COMPRESS: compress = false; break; } } public uint64 size_in_ppub { get { return compress ? compressed_size : uncompressed_size; } } } public enum CompressionPolicy { AUTO, ALWAYS_COMPRESS, NEVER_COMPRESS } public static string guess_mimetype(string name, uint8[] sample) { if(name == "metadata") { return "application/x-ppub-metadata"; } if(name.has_suffix(".ppvm")) { return "application/x-ppvm"; } bool result_uncertain; var content_type = ContentType.guess(name, sample, out result_uncertain); var mime = ContentType.get_mime_type(content_type); return mime ?? "application/octet-stream"; } }