mirror of
https://github.com/tfussell/xlnt.git
synced 2024-03-22 13:11:17 +08:00
use streams to access zip files and their contents instead of std::stringstream to reduce memory usage, replaces miniz with zlib, closes #66
This commit is contained in:
parent
6d749aaa91
commit
5b4de6a150
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -22,3 +22,6 @@
|
|||
path = third-party/botan
|
||||
url = https://github.com/randombit/botan
|
||||
branch = master
|
||||
[submodule "third-party/zlib"]
|
||||
path = third-party/zlib
|
||||
url = https://github.com/madler/zlib.git
|
||||
|
|
|
@ -1,149 +0,0 @@
|
|||
// Copyright (c) 2014-2016 Thomas Fussell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE
|
||||
//
|
||||
// @license: http://www.opensource.org/licenses/mit-license.php
|
||||
// @author: see AUTHORS file
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <xlnt/xlnt_config.hpp>
|
||||
#include <xlnt/utils/path.hpp>
|
||||
|
||||
// Note: this comes from https://github.com/tfussell/miniz-cpp
|
||||
|
||||
struct mz_zip_archive_tag;
|
||||
|
||||
namespace xlnt {
|
||||
|
||||
/// <summary>
|
||||
/// Information about a specific file in zip_file.
|
||||
/// </summary>
|
||||
struct XLNT_API zip_info
|
||||
{
|
||||
/// <summary>
|
||||
/// A struct representing a particular date and time.
|
||||
/// </summary>
|
||||
struct date_time_t
|
||||
{
|
||||
int year;
|
||||
int month;
|
||||
int day;
|
||||
int hours;
|
||||
int minutes;
|
||||
int seconds;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor for zip_info.
|
||||
/// </summary>
|
||||
zip_info();
|
||||
|
||||
date_time_t date_time;
|
||||
path filename;
|
||||
std::string comment;
|
||||
std::string extra;
|
||||
uint16_t create_system;
|
||||
uint16_t create_version;
|
||||
uint16_t extract_version;
|
||||
uint16_t flag_bits;
|
||||
std::size_t volume;
|
||||
uint32_t internal_attr;
|
||||
uint32_t external_attr;
|
||||
std::size_t header_offset;
|
||||
uint32_t crc;
|
||||
std::size_t compress_size;
|
||||
std::size_t file_size;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// A compressed archive file that exists in memory which can read
|
||||
/// or write to and from the filesystem, std::iostreams, and byte vectors.
|
||||
/// </summary>
|
||||
class XLNT_API zip_file
|
||||
{
|
||||
public:
|
||||
zip_file();
|
||||
zip_file(const path &filename);
|
||||
zip_file(const std::vector<uint8_t> &bytes);
|
||||
zip_file(std::istream &stream);
|
||||
~zip_file();
|
||||
|
||||
// to/from file
|
||||
void load(const path &filename);
|
||||
void save(const path &filename);
|
||||
|
||||
// to/from byte vector
|
||||
void load(const std::vector<std::uint8_t> &bytes);
|
||||
void save(std::vector<std::uint8_t> &bytes);
|
||||
|
||||
// to/from iostream
|
||||
void load(std::istream &stream);
|
||||
void save(std::ostream &stream);
|
||||
|
||||
void reset();
|
||||
|
||||
bool has_file(const path &name);
|
||||
bool has_file(const zip_info &name);
|
||||
|
||||
zip_info getinfo(const path &name);
|
||||
|
||||
std::vector<zip_info> infolist();
|
||||
std::vector<path> namelist();
|
||||
|
||||
std::ostream &open(const path &name);
|
||||
std::ostream &open(const zip_info &name);
|
||||
|
||||
std::string read(const path &name);
|
||||
std::string read(const zip_info &name);
|
||||
|
||||
bool check_crc();
|
||||
|
||||
void write_file(const path &source_file);
|
||||
void write_file(const path &source_file, const path &archive_path);
|
||||
|
||||
void write_string(const std::string &string, const path &archive_path);
|
||||
void write_string(const std::string &string, const zip_info &archive_path);
|
||||
|
||||
path get_filename() const;
|
||||
|
||||
std::string comment;
|
||||
|
||||
private:
|
||||
void start_read();
|
||||
void start_write();
|
||||
|
||||
void append_comment();
|
||||
void remove_comment();
|
||||
|
||||
zip_info getinfo(int index);
|
||||
|
||||
std::unique_ptr<mz_zip_archive_tag> archive_;
|
||||
std::vector<char> buffer_;
|
||||
std::stringstream open_stream_;
|
||||
path filename_;
|
||||
};
|
||||
|
||||
} // namespace xlnt
|
|
@ -37,7 +37,6 @@
|
|||
// packaging
|
||||
#include <xlnt/packaging/manifest.hpp>
|
||||
#include <xlnt/packaging/relationship.hpp>
|
||||
#include <xlnt/packaging/zip_file.hpp>
|
||||
|
||||
// styles
|
||||
#include <xlnt/styles/alignment.hpp>
|
||||
|
|
|
@ -67,7 +67,9 @@ include_directories(${XLNT_INCLUDE_DIR}
|
|||
${THIRD_PARTY_DIR}/libstudxml
|
||||
${THIRD_PARTY_DIR}/utfcpp/source
|
||||
${THIRD_PARTY_DIR}/pole
|
||||
${THIRD_PARTY_DIR}/botan)
|
||||
${THIRD_PARTY_DIR}/botan
|
||||
${THIRD_PARTY_DIR}/partio
|
||||
${THIRD_PARTY_DIR}/zlib)
|
||||
|
||||
file(GLOB ROOT_HEADERS ${XLNT_INCLUDE_DIR}/xlnt/*.hpp)
|
||||
file(GLOB CELL_HEADERS ${XLNT_INCLUDE_DIR}/xlnt/cell/*.hpp)
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
//
|
||||
// @license: http://www.opensource.org/licenses/mit-license.php
|
||||
// @author: see AUTHORS file
|
||||
|
||||
#include <cctype>
|
||||
#include <numeric> // for std::accumulate
|
||||
|
||||
|
@ -27,10 +28,10 @@
|
|||
#include <detail/constants.hpp>
|
||||
#include <detail/custom_value_traits.hpp>
|
||||
#include <detail/workbook_impl.hpp>
|
||||
#include <detail/zip.hpp>
|
||||
#include <xlnt/cell/cell.hpp>
|
||||
#include <xlnt/utils/path.hpp>
|
||||
#include <xlnt/packaging/manifest.hpp>
|
||||
#include <xlnt/packaging/zip_file.hpp>
|
||||
#include <xlnt/workbook/const_worksheet_iterator.hpp>
|
||||
#include <xlnt/workbook/workbook.hpp>
|
||||
#include <xlnt/worksheet/worksheet.hpp>
|
||||
|
@ -98,13 +99,13 @@ xlnt::color read_color(xml::parser &parser)
|
|||
return result;
|
||||
}
|
||||
|
||||
std::vector<xlnt::relationship> read_relationships(const xlnt::path &part, xlnt::zip_file &archive)
|
||||
std::vector<xlnt::relationship> read_relationships(const xlnt::path &part, Partio::ZipFileReader &archive)
|
||||
{
|
||||
std::vector<xlnt::relationship> relationships;
|
||||
if (!archive.has_file(part)) return relationships;
|
||||
if (!archive.Has_File(part.string())) return relationships;
|
||||
|
||||
std::istringstream rels_stream(archive.read(part));
|
||||
xml::parser parser(rels_stream, part.string());
|
||||
std::unique_ptr<std::istream> rels_stream(archive.Get_File(part.string(), true));
|
||||
xml::parser parser(*rels_stream, part.string());
|
||||
|
||||
xlnt::uri source(part.string());
|
||||
|
||||
|
@ -149,21 +150,9 @@ xlsx_consumer::xlsx_consumer(workbook &target)
|
|||
{
|
||||
}
|
||||
|
||||
void xlsx_consumer::read(const path &source)
|
||||
{
|
||||
source_.load(source);
|
||||
populate_workbook();
|
||||
}
|
||||
|
||||
void xlsx_consumer::read(std::istream &source)
|
||||
{
|
||||
source_.load(source);
|
||||
populate_workbook();
|
||||
}
|
||||
|
||||
void xlsx_consumer::read(const std::vector<std::uint8_t> &source)
|
||||
{
|
||||
source_.load(source);
|
||||
archive_.reset(new Partio::ZipFileReader(source));
|
||||
populate_workbook();
|
||||
}
|
||||
|
||||
|
@ -181,8 +170,8 @@ void xlsx_consumer::populate_workbook()
|
|||
|
||||
for (const auto &rel : manifest.get_relationships(path("/")))
|
||||
{
|
||||
std::istringstream parser_stream(source_.read(rel.get_target().get_path()));
|
||||
xml::parser parser(parser_stream, rel.get_target().get_path().string());
|
||||
std::unique_ptr<std::istream> parser_stream(archive_->Get_File(rel.get_target().get_path().string(), true));
|
||||
xml::parser parser(*parser_stream, rel.get_target().get_path().string());
|
||||
parser_ = &parser;
|
||||
|
||||
switch (rel.get_type())
|
||||
|
@ -246,11 +235,11 @@ void xlsx_consumer::populate_workbook()
|
|||
for (const auto &rel : manifest.get_relationships(workbook_rel.get_target().get_path()))
|
||||
{
|
||||
path part_path(rel.get_source().get_path().parent().append(rel.get_target().get_path()));
|
||||
std::istringstream parser_stream(source_.read(part_path));
|
||||
std::unique_ptr<std::istream> parser_stream(archive_->Get_File(part_path.string(), true));
|
||||
auto using_namespaces = rel.get_type() == relationship::type::styles;
|
||||
auto receive = xml::parser::receive_default
|
||||
| (using_namespaces ? xml::parser::receive_namespace_decls : 0);
|
||||
xml::parser parser(parser_stream, rel.get_target().get_path().string(), receive);
|
||||
xml::parser parser(*parser_stream, part_path.string(), receive);
|
||||
parser_ = &parser;
|
||||
|
||||
switch (rel.get_type())
|
||||
|
@ -279,9 +268,9 @@ void xlsx_consumer::populate_workbook()
|
|||
for (const auto &rel : manifest.get_relationships(workbook_rel.get_target().get_path()))
|
||||
{
|
||||
path part_path(rel.get_source().get_path().parent().append(rel.get_target().get_path()));
|
||||
std::istringstream parser_stream(source_.read(part_path));
|
||||
std::unique_ptr<std::istream> parser_stream(archive_->Get_File(part_path.string(), true));
|
||||
auto receive = xml::parser::receive_default | xml::parser::receive_namespace_decls;
|
||||
xml::parser parser(parser_stream, rel.get_target().get_path().string(), receive);
|
||||
xml::parser parser(*parser_stream, rel.get_target().get_path().string(), receive);
|
||||
parser_ = &parser;
|
||||
|
||||
switch (rel.get_type())
|
||||
|
@ -317,15 +306,16 @@ void xlsx_consumer::read_manifest()
|
|||
{
|
||||
path package_rels_path("_rels/.rels");
|
||||
|
||||
if (!source_.has_file(package_rels_path))
|
||||
if (!archive_->Has_File(package_rels_path.string()))
|
||||
{
|
||||
throw invalid_file("missing package rels");
|
||||
}
|
||||
|
||||
auto package_rels = read_relationships(package_rels_path, source_);
|
||||
auto package_rels = read_relationships(package_rels_path, *archive_);
|
||||
|
||||
std::istringstream parser_stream(source_.read(path("[Content_Types].xml")));
|
||||
xml::parser parser(parser_stream, "[Content_Types].xml");
|
||||
std::unique_ptr<std::istream> parser_stream(archive_->Get_File("[Content_Types].xml", true));
|
||||
//std::string stream_string((std::istreambuf_iterator<char>(*parser_stream)), std::istreambuf_iterator<char>());
|
||||
xml::parser parser(*parser_stream, "[Content_Types].xml");
|
||||
|
||||
auto &manifest = target_.get_manifest();
|
||||
|
||||
|
@ -365,18 +355,23 @@ void xlsx_consumer::read_manifest()
|
|||
package_rel.get_id());
|
||||
}
|
||||
|
||||
for (const auto &relationship_source : source_.infolist())
|
||||
{
|
||||
if (relationship_source.filename == path("_rels/.rels")
|
||||
|| relationship_source.filename.extension() != "rels") continue;
|
||||
std::vector<std::string> file_list;
|
||||
archive_->Get_File_List(file_list);
|
||||
|
||||
path part(relationship_source.filename.parent().parent());
|
||||
part = part.append(relationship_source.filename.split_extension().first);
|
||||
for (const auto &relationship_source_string : file_list)
|
||||
{
|
||||
auto relationship_source = path(relationship_source_string);
|
||||
|
||||
if (relationship_source == path("_rels/.rels")
|
||||
|| relationship_source.extension() != "rels") continue;
|
||||
|
||||
path part(relationship_source.parent().parent());
|
||||
part = part.append(relationship_source.split_extension().first);
|
||||
uri source(part.string());
|
||||
|
||||
path source_directory = part.parent();
|
||||
|
||||
auto part_rels = read_relationships(relationship_source.filename, source_);
|
||||
auto part_rels = read_relationships(relationship_source, *archive_);
|
||||
|
||||
for (const auto part_rel : part_rels)
|
||||
{
|
||||
|
@ -2017,9 +2012,10 @@ void xlsx_consumer::read_worksheet(const std::string &rel_id)
|
|||
}
|
||||
part_path = std::accumulate(split_part_path.begin(), split_part_path.end(), path(""),
|
||||
[](const path &a, const std::string &b) { return a.append(b); });
|
||||
std::istringstream parser_stream(source_.read(part_path));
|
||||
|
||||
std::unique_ptr<std::istream> parser_stream(archive_->Get_File(part_path.string(), true));
|
||||
auto receive = xml::parser::receive_default;
|
||||
xml::parser parser(parser_stream, rel.get_target().get_path().string(), receive);
|
||||
xml::parser parser(*parser_stream, rel.get_target().get_path().string(), receive);
|
||||
parser_ = &parser;
|
||||
|
||||
switch (rel.get_type())
|
||||
|
|
|
@ -25,12 +25,15 @@
|
|||
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <detail/include_libstudxml.hpp>
|
||||
#include <xlnt/xlnt_config.hpp>
|
||||
#include <xlnt/packaging/zip_file.hpp>
|
||||
|
||||
namespace Partio {
|
||||
class ZipFileReader;
|
||||
}
|
||||
|
||||
namespace xlnt {
|
||||
|
||||
|
@ -44,23 +47,15 @@ namespace detail {
|
|||
/// <summary>
|
||||
/// Handles writing a workbook into an XLSX file.
|
||||
/// </summary>
|
||||
class XLNT_API xlsx_consumer
|
||||
class xlsx_consumer
|
||||
{
|
||||
public:
|
||||
xlsx_consumer(workbook &destination);
|
||||
|
||||
void read(const path &source);
|
||||
|
||||
void read(std::istream &source);
|
||||
|
||||
void read(const std::vector<std::uint8_t> &source);
|
||||
|
||||
void read(const path &source, const std::string &password);
|
||||
|
||||
void read(std::istream &source, const std::string &password);
|
||||
|
||||
void read(const std::vector<std::uint8_t> &source, const std::string &password);
|
||||
|
||||
private:
|
||||
/// <summary>
|
||||
/// Ignore all remaining elements at the same depth in the current XML parser.
|
||||
|
@ -223,7 +218,7 @@ private:
|
|||
/// <summary>
|
||||
/// The ZIP file containing the files that make up the OOXML package.
|
||||
/// </summary>
|
||||
zip_file source_;
|
||||
std::unique_ptr<Partio::ZipFileReader> archive_;
|
||||
|
||||
/// <summary>
|
||||
/// Map of sheet titles to relationship IDs.
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <botan_all.h>
|
||||
#include <include_libstudxml.hpp>
|
||||
|
||||
#include <detail/vector_streambuf.hpp>
|
||||
#include <detail/xlsx_consumer.hpp>
|
||||
#include <xlnt/utils/exceptions.hpp>
|
||||
#include <xlnt/workbook/workbook.hpp>
|
||||
|
@ -271,9 +272,14 @@ std::vector<std::uint8_t> decrypt_xlsx_standard(const std::vector<std::uint8_t>
|
|||
|
||||
//todo: verify here
|
||||
|
||||
return aes(key_derived, {}, std::vector<std::uint8_t>(
|
||||
std::size_t package_offset = 0;
|
||||
auto decrypted_size = read_int<std::uint64_t>(package_offset, encrypted_package);
|
||||
auto decrypted = aes(key_derived, {}, std::vector<std::uint8_t>(
|
||||
encrypted_package.begin() + 8, encrypted_package.end()),
|
||||
cipher_chaining::ecb, cipher_direction::decryption);
|
||||
decrypted.resize(decrypted_size);
|
||||
|
||||
return decrypted;
|
||||
}
|
||||
|
||||
|
||||
|
@ -567,23 +573,14 @@ std::vector<std::uint8_t> decrypt_xlsx(const std::vector<std::uint8_t> &bytes, c
|
|||
return decrypt_xlsx_standard(encryption_info, password, encrypted_package);
|
||||
}
|
||||
|
||||
void xlsx_consumer::read(const std::vector<std::uint8_t> &source, const std::string &password)
|
||||
{
|
||||
source_.load(decrypt_xlsx(source, password));
|
||||
populate_workbook();
|
||||
}
|
||||
|
||||
void xlsx_consumer::read(std::istream &source, const std::string &password)
|
||||
{
|
||||
std::vector<std::uint8_t> data((std::istreambuf_iterator<char>(source)),
|
||||
std::istreambuf_iterator<char>());
|
||||
return read(data, password);
|
||||
}
|
||||
|
||||
void xlsx_consumer::read(const path &source, const std::string &password)
|
||||
{
|
||||
std::ifstream file_stream(source.string(), std::iostream::binary);
|
||||
return read(file_stream, password);
|
||||
std::vector<std::uint8_t> data((std::istreambuf_iterator<char>(source)),
|
||||
(std::istreambuf_iterator<char>()));
|
||||
const auto decrypted = decrypt_xlsx(data, password);
|
||||
vector_istreambuf decrypted_buffer(decrypted);
|
||||
std::istream decrypted_stream(&decrypted_buffer);
|
||||
read(decrypted_stream);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
#include <xlnt/cell/cell.hpp>
|
||||
#include <xlnt/utils/path.hpp>
|
||||
#include <xlnt/packaging/manifest.hpp>
|
||||
#include <xlnt/packaging/zip_file.hpp>
|
||||
#include <xlnt/workbook/const_worksheet_iterator.hpp>
|
||||
#include <xlnt/workbook/workbook.hpp>
|
||||
#include <xlnt/workbook/workbook_view.hpp>
|
||||
|
@ -83,22 +82,11 @@ xlsx_producer::xlsx_producer(const workbook &target) : source_(target)
|
|||
{
|
||||
}
|
||||
|
||||
void xlsx_producer::write(const path &destination)
|
||||
{
|
||||
populate_archive();
|
||||
destination_.save(destination);
|
||||
}
|
||||
|
||||
void xlsx_producer::write(std::ostream &destination)
|
||||
{
|
||||
Partio::ZipFileWriter archive(destination);
|
||||
archive_ = &archive;
|
||||
populate_archive();
|
||||
destination_.save(destination);
|
||||
}
|
||||
|
||||
void xlsx_producer::write(std::vector<std::uint8_t> &destination)
|
||||
{
|
||||
populate_archive();
|
||||
destination_.save(destination);
|
||||
}
|
||||
|
||||
// Part Writing Methods
|
||||
|
@ -112,11 +100,7 @@ void xlsx_producer::populate_archive()
|
|||
|
||||
for (auto &rel : root_rels)
|
||||
{
|
||||
std::ostringstream serializer_stream;
|
||||
xml::serializer serializer(serializer_stream, rel.get_target().get_path().string());
|
||||
serializer_ = &serializer;
|
||||
|
||||
bool write_document = true;
|
||||
begin_part(rel.get_target().get_path());
|
||||
|
||||
switch (rel.get_type())
|
||||
{
|
||||
|
@ -138,57 +122,73 @@ void xlsx_producer::populate_archive()
|
|||
|
||||
case relationship::type::thumbnail:
|
||||
write_thumbnail(rel);
|
||||
write_document = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (write_document)
|
||||
{
|
||||
destination_.write_string(serializer_stream.str(), rel.get_target().get_path());
|
||||
}
|
||||
}
|
||||
|
||||
// Unknown Parts
|
||||
|
||||
void write_unknown_parts();
|
||||
void write_unknown_relationships();
|
||||
|
||||
end_part();
|
||||
}
|
||||
|
||||
void xlsx_producer::end_part()
|
||||
{
|
||||
if (current_part_serializer_)
|
||||
{
|
||||
current_part_serializer_.reset();
|
||||
}
|
||||
|
||||
if (current_part_stream_)
|
||||
{
|
||||
current_part_stream_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void xlsx_producer::begin_part(const path &part)
|
||||
{
|
||||
end_part();
|
||||
|
||||
current_part_stream_.reset(archive_->Add_File(part.string(), true));
|
||||
current_part_serializer_.reset(new xml::serializer(*current_part_stream_, part.string()));
|
||||
}
|
||||
|
||||
// Package Parts
|
||||
|
||||
void xlsx_producer::write_content_types()
|
||||
{
|
||||
std::ostringstream content_types_stream;
|
||||
xml::serializer content_types_serializer(content_types_stream, "[Content_Types].xml");
|
||||
const auto content_types_path = path("[Content_Types].xml");
|
||||
begin_part(content_types_path);
|
||||
|
||||
const auto xmlns = "http://schemas.openxmlformats.org/package/2006/content-types"s;
|
||||
|
||||
content_types_serializer.start_element(xmlns, "Types");
|
||||
content_types_serializer.namespace_decl(xmlns, "");
|
||||
serializer().start_element(xmlns, "Types");
|
||||
serializer().namespace_decl(xmlns, "");
|
||||
|
||||
for (const auto &extension : source_.get_manifest().get_extensions_with_default_types())
|
||||
{
|
||||
content_types_serializer.start_element(xmlns, "Default");
|
||||
content_types_serializer.attribute("Extension", extension);
|
||||
content_types_serializer.attribute("ContentType",
|
||||
serializer().start_element(xmlns, "Default");
|
||||
serializer().attribute("Extension", extension);
|
||||
serializer().attribute("ContentType",
|
||||
source_.get_manifest().get_default_type(extension));
|
||||
content_types_serializer.end_element(xmlns, "Default");
|
||||
serializer().end_element(xmlns, "Default");
|
||||
}
|
||||
|
||||
for (const auto &part : source_.get_manifest().get_parts_with_overriden_types())
|
||||
{
|
||||
content_types_serializer.start_element(xmlns, "Override");
|
||||
content_types_serializer.attribute("PartName", part.resolve(path("/")).string());
|
||||
content_types_serializer.attribute("ContentType",
|
||||
serializer().start_element(xmlns, "Override");
|
||||
serializer().attribute("PartName", part.resolve(path("/")).string());
|
||||
serializer().attribute("ContentType",
|
||||
source_.get_manifest().get_override_type(part));
|
||||
content_types_serializer.end_element(xmlns, "Override");
|
||||
serializer().end_element(xmlns, "Override");
|
||||
}
|
||||
|
||||
content_types_serializer.end_element(xmlns, "Types");
|
||||
destination_.write_string(content_types_stream.str(), path("[Content_Types].xml"));
|
||||
serializer().end_element(xmlns, "Types");
|
||||
}
|
||||
|
||||
void xlsx_producer::write_extended_properties(const relationship &rel)
|
||||
|
@ -509,9 +509,8 @@ void xlsx_producer::write_workbook(const relationship &rel)
|
|||
|
||||
for (const auto &child_rel : workbook_rels)
|
||||
{
|
||||
std::ostringstream child_stream;
|
||||
xml::serializer child_serializer(child_stream, child_rel.get_target().get_path().string());
|
||||
serializer_ = &child_serializer;
|
||||
path archive_path(child_rel.get_source().get_path().parent().append(child_rel.get_target().get_path()));
|
||||
begin_part(archive_path);
|
||||
|
||||
switch (child_rel.get_type())
|
||||
{
|
||||
|
@ -574,9 +573,6 @@ void xlsx_producer::write_workbook(const relationship &rel)
|
|||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
path archive_path(child_rel.get_source().get_path().parent().append(child_rel.get_target().get_path()));
|
||||
destination_.write_string(child_stream.str(), archive_path);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2223,24 +2219,6 @@ void xlsx_producer::write_worksheet(const relationship &rel)
|
|||
|
||||
for (const auto &child_rel : worksheet_rels)
|
||||
{
|
||||
std::ostringstream child_stream;
|
||||
xml::serializer child_serializer(child_stream, child_rel.get_target().get_path().string());
|
||||
serializer_ = &child_serializer;
|
||||
|
||||
switch (child_rel.get_type())
|
||||
{
|
||||
case relationship::type::comments:
|
||||
write_comments(child_rel, ws, cells_with_comments);
|
||||
break;
|
||||
|
||||
case relationship::type::vml_drawing:
|
||||
write_vml_drawings(child_rel, ws, cells_with_comments);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
path archive_path(worksheet_part.parent().append(child_rel.get_target().get_path()));
|
||||
auto split_part_path = archive_path.split();
|
||||
auto part_path_iter = split_part_path.begin();
|
||||
|
@ -2256,7 +2234,22 @@ void xlsx_producer::write_worksheet(const relationship &rel)
|
|||
}
|
||||
archive_path = std::accumulate(split_part_path.begin(), split_part_path.end(), path(""),
|
||||
[](const path &a, const std::string &b) { return a.append(b); });
|
||||
destination_.write_string(child_stream.str(), archive_path);
|
||||
|
||||
begin_part(archive_path);
|
||||
|
||||
switch (child_rel.get_type())
|
||||
{
|
||||
case relationship::type::comments:
|
||||
write_comments(child_rel, ws, cells_with_comments);
|
||||
break;
|
||||
|
||||
case relationship::type::vml_drawing:
|
||||
write_vml_drawings(child_rel, ws, cells_with_comments);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2506,13 +2499,15 @@ void xlsx_producer::write_unknown_relationships()
|
|||
void xlsx_producer::write_thumbnail(const relationship &rel)
|
||||
{
|
||||
const auto &thumbnail = source_.get_thumbnail();
|
||||
std::string thumbnail_string(thumbnail.begin(), thumbnail.end());
|
||||
destination_.write_string(thumbnail_string, rel.get_target().get_path());
|
||||
std::unique_ptr<std::ostream> thumbnail_stream(
|
||||
archive_->Add_File(rel.get_target().get_path().string(), true));
|
||||
std::for_each(thumbnail.begin(), thumbnail.end(),
|
||||
[&thumbnail_stream](std::uint8_t b) { *thumbnail_stream << b; });
|
||||
}
|
||||
|
||||
xml::serializer &xlsx_producer::serializer()
|
||||
{
|
||||
return *serializer_;
|
||||
return *current_part_serializer_;
|
||||
}
|
||||
|
||||
std::string xlsx_producer::write_bool(bool boolean) const
|
||||
|
@ -2535,33 +2530,31 @@ void xlsx_producer::write_relationships(const std::vector<xlnt::relationship> &r
|
|||
parent = path(parent.string().substr(1));
|
||||
}
|
||||
|
||||
std::ostringstream rels_stream;
|
||||
path rels_path(parent.append("_rels").append(part.filename() + ".rels").string());
|
||||
xml::serializer rels_serializer(rels_stream, rels_path.string());
|
||||
path rels_path(parent.append("_rels").append(part.filename() + ".rels").string());
|
||||
begin_part(rels_path);
|
||||
|
||||
const auto xmlns = xlnt::constants::get_namespace("relationships");
|
||||
|
||||
rels_serializer.start_element(xmlns, "Relationships");
|
||||
rels_serializer.namespace_decl(xmlns, "");
|
||||
serializer().start_element(xmlns, "Relationships");
|
||||
serializer().namespace_decl(xmlns, "");
|
||||
|
||||
for (const auto &relationship : relationships)
|
||||
{
|
||||
rels_serializer.start_element(xmlns, "Relationship");
|
||||
serializer().start_element(xmlns, "Relationship");
|
||||
|
||||
rels_serializer.attribute("Id", relationship.get_id());
|
||||
rels_serializer.attribute("Type", relationship.get_type());
|
||||
rels_serializer.attribute("Target", relationship.get_target().get_path().string());
|
||||
serializer().attribute("Id", relationship.get_id());
|
||||
serializer().attribute("Type", relationship.get_type());
|
||||
serializer().attribute("Target", relationship.get_target().get_path().string());
|
||||
|
||||
if (relationship.get_target_mode() == xlnt::target_mode::external)
|
||||
{
|
||||
rels_serializer.attribute("TargetMode", "External");
|
||||
serializer().attribute("TargetMode", "External");
|
||||
}
|
||||
|
||||
rels_serializer.end_element(xmlns, "Relationship");
|
||||
serializer().end_element(xmlns, "Relationship");
|
||||
}
|
||||
|
||||
rels_serializer.end_element(xmlns, "Relationships");
|
||||
destination_.write_string(rels_stream.str(), rels_path);
|
||||
serializer().end_element(xmlns, "Relationships");
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -24,11 +24,15 @@
|
|||
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <detail/include_libstudxml.hpp>
|
||||
#include <xlnt/xlnt_config.hpp>
|
||||
#include <xlnt/packaging/zip_file.hpp>
|
||||
#include <detail/zip.hpp>
|
||||
|
||||
namespace Partio {
|
||||
class ZipFileWriter;
|
||||
}
|
||||
|
||||
namespace xml {
|
||||
class serializer;
|
||||
|
@ -48,17 +52,13 @@ namespace detail {
|
|||
/// <summary>
|
||||
/// Handles writing a workbook into an XLSX file.
|
||||
/// </summary>
|
||||
class XLNT_API xlsx_producer
|
||||
class xlsx_producer
|
||||
{
|
||||
public:
|
||||
xlsx_producer(const workbook &target);
|
||||
|
||||
void write(const path &destination);
|
||||
|
||||
void write(std::ostream &destination);
|
||||
|
||||
void write(std::vector<std::uint8_t> &destination);
|
||||
|
||||
private:
|
||||
/// <summary>
|
||||
/// Write all files needed to create a valid XLSX file which represents all
|
||||
|
@ -66,6 +66,9 @@ private:
|
|||
/// </summary>
|
||||
void populate_archive();
|
||||
|
||||
void begin_part(const path &part);
|
||||
void end_part();
|
||||
|
||||
// Package Parts
|
||||
|
||||
void write_content_types();
|
||||
|
@ -134,18 +137,10 @@ private:
|
|||
/// A reference to the workbook which is the object of read/write operations.
|
||||
/// </summary>
|
||||
const workbook &source_;
|
||||
|
||||
/// <summary>
|
||||
/// A reference to the archive into which files representing the workbook
|
||||
/// will be written.
|
||||
/// </summary>
|
||||
zip_file destination_;
|
||||
|
||||
/// <summary>
|
||||
/// Instead of passing the current serializer into part serialization methods,
|
||||
/// store pointer in this field and access it in methods with xlsx_producer::serializer().
|
||||
/// </summary>
|
||||
xml::serializer *serializer_;
|
||||
Partio::ZipFileWriter *archive_;
|
||||
std::unique_ptr<std::ostream> current_part_stream_;
|
||||
std::unique_ptr<xml::serializer> current_part_serializer_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
|
502
source/detail/zip.cpp
Normal file
502
source/detail/zip.cpp
Normal file
|
@ -0,0 +1,502 @@
|
|||
/*
|
||||
PARTIO SOFTWARE
|
||||
Copyright 2010 Disney Enterprises, Inc. All rights reserved
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
|
||||
Studios" or the names of its contributors may NOT be used to
|
||||
endorse or promote products derived from this software without
|
||||
specific prior written permission from Walt Disney Pictures.
|
||||
|
||||
Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
|
||||
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
|
||||
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
*/
|
||||
|
||||
extern "C"{
|
||||
#include <zlib.h>
|
||||
}
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include <detail/zip.hpp>
|
||||
|
||||
namespace Partio{
|
||||
|
||||
template<class T>
|
||||
inline void Swap_Endianity(T& x)
|
||||
{
|
||||
assert(sizeof(T)<=8);
|
||||
if(sizeof(T)>1) {
|
||||
T old=x;
|
||||
for(unsigned int k=1;k<=sizeof(T);k++) ((char*)&x)[k-1]=((char*)&old)[sizeof(T)-k];
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline void Read_Primitive(std::istream& stream,T& x)
|
||||
{
|
||||
stream.read(&(char&)x,sizeof(T));
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline void Write_Primitive(std::ostream& stream,const T& x)
|
||||
{
|
||||
stream.write(&(char&)x,sizeof(T));
|
||||
}
|
||||
|
||||
//#####################################################################
|
||||
// class ZipFileHeader
|
||||
//#####################################################################
|
||||
struct ZipFileHeader
|
||||
{
|
||||
unsigned short version;
|
||||
unsigned short flags;
|
||||
unsigned short compression_type;
|
||||
unsigned short stamp_date,stamp_time;
|
||||
unsigned int crc;
|
||||
unsigned int compressed_size,uncompressed_size;
|
||||
std::string filename;
|
||||
unsigned int header_offset; // local header offset
|
||||
|
||||
ZipFileHeader()
|
||||
{}
|
||||
|
||||
ZipFileHeader(const std::string& filename_input)
|
||||
:version(20),flags(0),compression_type(8),stamp_date(0),stamp_time(0),crc(0),
|
||||
compressed_size(0),uncompressed_size(0),filename(filename_input),header_offset(0)
|
||||
{}
|
||||
|
||||
bool Read(std::istream& istream,const bool global)
|
||||
{unsigned int sig;
|
||||
unsigned short version,flags;
|
||||
// read and check for local/global magic
|
||||
if(global){
|
||||
Read_Primitive(istream,sig);
|
||||
if(sig!=0x02014b50){std::cerr<<"Did not find global header signature"<<std::endl;return false;}
|
||||
Read_Primitive(istream,version);}
|
||||
else{
|
||||
Read_Primitive(istream,sig);
|
||||
if(sig!=0x04034b50){std::cerr<<"Did not find local header signature"<<std::endl;return false;}}
|
||||
// Read rest of header
|
||||
Read_Primitive(istream,version);
|
||||
Read_Primitive(istream,flags);
|
||||
Read_Primitive(istream,compression_type);
|
||||
Read_Primitive(istream,stamp_date);
|
||||
Read_Primitive(istream,stamp_time);
|
||||
Read_Primitive(istream,crc);
|
||||
Read_Primitive(istream,compressed_size);
|
||||
Read_Primitive(istream,uncompressed_size);
|
||||
unsigned short filename_length,extra_length;
|
||||
Read_Primitive(istream,filename_length);
|
||||
Read_Primitive(istream,extra_length);
|
||||
unsigned short comment_length=0;
|
||||
if(global){
|
||||
Read_Primitive(istream,comment_length); // filecomment
|
||||
unsigned short disk_number_start,int_file_attrib;
|
||||
unsigned int ext_file_attrib;
|
||||
Read_Primitive(istream,disk_number_start); // disk# start
|
||||
Read_Primitive(istream,int_file_attrib); // internal file
|
||||
Read_Primitive(istream,ext_file_attrib); // ext final
|
||||
Read_Primitive(istream,header_offset);} // rel offset
|
||||
char* buf=new char[std::max(comment_length,std::max(filename_length,extra_length))+1];
|
||||
istream.read(buf,filename_length);
|
||||
buf[filename_length]=0;
|
||||
filename=std::string(buf, buf + filename_length);
|
||||
istream.read(buf,extra_length);
|
||||
if(global) istream.read(buf,comment_length);
|
||||
delete [] buf;
|
||||
return true;}
|
||||
|
||||
void Write(std::ostream& ostream,const bool global) const
|
||||
{if(global){
|
||||
Write_Primitive(ostream,(unsigned int)0x02014b50); // header sig
|
||||
Write_Primitive(ostream,(unsigned short)00);} // version made by
|
||||
else Write_Primitive(ostream,(unsigned int)0x04034b50);
|
||||
Write_Primitive(ostream,version);
|
||||
Write_Primitive(ostream,flags);
|
||||
Write_Primitive(ostream,compression_type);
|
||||
Write_Primitive(ostream,stamp_date);
|
||||
Write_Primitive(ostream,stamp_time);
|
||||
Write_Primitive(ostream,crc);
|
||||
Write_Primitive(ostream,compressed_size);
|
||||
Write_Primitive(ostream,uncompressed_size);
|
||||
Write_Primitive(ostream,(unsigned short)filename.length());
|
||||
Write_Primitive(ostream,(unsigned short)0); // extra lengthx
|
||||
if(global){
|
||||
Write_Primitive(ostream,(unsigned short)0); // filecomment
|
||||
Write_Primitive(ostream,(unsigned short)0); // disk# start
|
||||
Write_Primitive(ostream,(unsigned short)0); // internal file
|
||||
Write_Primitive(ostream,(unsigned int)0); // ext final
|
||||
Write_Primitive(ostream,(unsigned int)header_offset);} // rel offset
|
||||
for(unsigned int i=0;i<filename.length();i++) Write_Primitive(ostream,filename.c_str()[i]);}
|
||||
//#####################################################################
|
||||
};
|
||||
|
||||
//#####################################################################
|
||||
// class ZipStreambufDecompress
|
||||
//#####################################################################
|
||||
class ZipStreambufDecompress:public std::streambuf
|
||||
{
|
||||
static const unsigned int buffer_size=512;
|
||||
std::istream& istream;
|
||||
|
||||
z_stream strm;
|
||||
unsigned char in[buffer_size],out[buffer_size];
|
||||
ZipFileHeader header;
|
||||
int total_read,total_uncompressed;
|
||||
bool own_istream;
|
||||
bool valid;
|
||||
bool compressed_data;
|
||||
|
||||
static const unsigned short DEFLATE=8;
|
||||
static const unsigned short UNCOMPRESSED=0;
|
||||
public:
|
||||
ZipStreambufDecompress(std::istream& stream,ZipFileHeader central_header)
|
||||
:istream(stream),total_read(0),total_uncompressed(0),valid(true),header(central_header)
|
||||
{
|
||||
strm.zalloc=Z_NULL;strm.zfree=Z_NULL;strm.opaque=Z_NULL;strm.avail_in=0;strm.next_in=Z_NULL;
|
||||
setg((char*)in,(char*)in,(char*)in);
|
||||
setp(0,0);
|
||||
// skip the header
|
||||
valid=header.Read(istream,false);
|
||||
if(header.compression_type==DEFLATE) compressed_data=true;
|
||||
else if(header.compression_type==UNCOMPRESSED) compressed_data=false;
|
||||
else{
|
||||
compressed_data=false;std::cerr<<"ZIP: got unrecognized compressed data (Supported deflate/uncompressed)"<<std::endl;
|
||||
valid=false;}
|
||||
// initialize the inflate
|
||||
if(compressed_data && valid){
|
||||
int result=inflateInit2(&strm,-MAX_WBITS);
|
||||
if(result!=Z_OK){std::cerr<<"gzip: inflateInit2 did not return Z_OK"<<std::endl;valid=false;}}
|
||||
header = central_header;
|
||||
}
|
||||
|
||||
virtual ~ZipStreambufDecompress()
|
||||
{if(compressed_data && valid) inflateEnd(&strm);}
|
||||
|
||||
int process()
|
||||
{if(!valid) return -1;
|
||||
if(compressed_data){
|
||||
strm.avail_out=buffer_size-4;
|
||||
strm.next_out=(Bytef*)(out+4);
|
||||
while(strm.avail_out!=0){
|
||||
if(strm.avail_in==0){ // buffer empty, read some more from file
|
||||
istream.read((char*)in,std::min((unsigned int)buffer_size,header.compressed_size-total_read));
|
||||
strm.avail_in=istream.gcount();
|
||||
total_read+=strm.avail_in;
|
||||
strm.next_in=(Bytef*)in;}
|
||||
int ret=inflate(&strm,Z_NO_FLUSH); // decompress
|
||||
switch(ret){
|
||||
case Z_STREAM_ERROR:
|
||||
std::cerr<<"libz error Z_STREAM_ERROR"<<std::endl;
|
||||
valid=false;return -1;
|
||||
case Z_NEED_DICT:
|
||||
case Z_DATA_ERROR:
|
||||
case Z_MEM_ERROR:
|
||||
std::cerr<<"gzip error "<<strm.msg<<std::endl;
|
||||
valid=false;return -1;}
|
||||
if(ret==Z_STREAM_END) break;}
|
||||
int unzip_count=buffer_size-strm.avail_out-4;
|
||||
total_uncompressed+=unzip_count;
|
||||
return unzip_count;}
|
||||
else{ // uncompressed, so just read
|
||||
istream.read((char*)(out+4),std::min(buffer_size-4,header.uncompressed_size-total_read));
|
||||
int count=istream.gcount();
|
||||
total_read+=count;
|
||||
return count;}
|
||||
return 1;}
|
||||
|
||||
virtual int underflow()
|
||||
{if(gptr() && (gptr()<egptr())) return traits_type::to_int_type(*gptr()); // if we already have data just use it
|
||||
int put_back_count=gptr()-eback();
|
||||
if(put_back_count>4) put_back_count=4;
|
||||
std::memmove(out+(4-put_back_count),gptr()-put_back_count,put_back_count);
|
||||
int num=process();
|
||||
setg((char*)(out+4-put_back_count),(char*)(out+4),(char*)(out+4+num));
|
||||
if(num<=0) return EOF;
|
||||
return traits_type::to_int_type(*gptr());}
|
||||
|
||||
virtual int overflow(int c=EOF)
|
||||
{assert(false);return EOF;}
|
||||
|
||||
//#####################################################################
|
||||
};
|
||||
|
||||
//#####################################################################
|
||||
// class ZipStreambufCompress
|
||||
//#####################################################################
|
||||
class ZipStreambufCompress:public std::streambuf
|
||||
{
|
||||
static const int buffer_size=512;
|
||||
std::ostream& ostream; // owned when header==0 (when not part of zip file)
|
||||
|
||||
z_stream strm;
|
||||
unsigned char in[buffer_size],out[buffer_size];
|
||||
|
||||
ZipFileHeader* header;
|
||||
unsigned int header_offset;
|
||||
unsigned int uncompressed_size;
|
||||
unsigned int crc;
|
||||
|
||||
bool valid;
|
||||
|
||||
public:
|
||||
ZipStreambufCompress(ZipFileHeader* header,std::ostream& stream)
|
||||
:ostream(stream),header(header),valid(true)
|
||||
{
|
||||
strm.zalloc=Z_NULL;strm.zfree=Z_NULL;strm.opaque=Z_NULL;
|
||||
int ret=deflateInit2(&strm,Z_DEFAULT_COMPRESSION,Z_DEFLATED,-MAX_WBITS,8,Z_DEFAULT_STRATEGY);
|
||||
if(ret != Z_OK){std::cerr<<"libz: failed to deflateInit"<<std::endl;valid=false;return;}
|
||||
setg(0,0,0);
|
||||
setp((char*)in,(char*)(in+buffer_size-4)); // we want to be 4 aligned
|
||||
// Write appropriate header
|
||||
if(header){header->header_offset=stream.tellp();header->Write(ostream,false);}
|
||||
uncompressed_size=crc=0;
|
||||
}
|
||||
|
||||
virtual ~ZipStreambufCompress()
|
||||
{if(valid){
|
||||
process(true);
|
||||
deflateEnd(&strm);
|
||||
if(header){
|
||||
std::ios::streampos final_position=ostream.tellp();
|
||||
header->uncompressed_size=uncompressed_size;
|
||||
header->crc=crc;
|
||||
ostream.seekp(header->header_offset);
|
||||
header->Write(ostream,false);
|
||||
ostream.seekp(final_position);}
|
||||
else{Write_Primitive(ostream,crc);Write_Primitive(ostream,uncompressed_size);}}
|
||||
if(!header) delete &ostream;}
|
||||
|
||||
protected:
|
||||
int process(bool flush)
|
||||
{if(!valid) return -1;
|
||||
strm.next_in=(Bytef*)pbase();
|
||||
strm.avail_in=pptr()-pbase();
|
||||
while(strm.avail_in!=0 || flush){
|
||||
strm.avail_out=buffer_size;
|
||||
strm.next_out=(Bytef*)out;
|
||||
int ret=deflate(&strm,flush?Z_FINISH:Z_NO_FLUSH);
|
||||
if(!(ret!=Z_BUF_ERROR && ret!=Z_STREAM_ERROR)){
|
||||
valid=false;
|
||||
std::cerr<<"gzip: gzip error "<<strm.msg<<std::endl;;
|
||||
return -1;}
|
||||
int generated_output=strm.next_out-(Bytef*)out;
|
||||
ostream.write((char*)out,generated_output);
|
||||
if(header) header->compressed_size+=generated_output;
|
||||
if(ret==Z_STREAM_END) break;}
|
||||
// update counts, crc's and buffers
|
||||
int consumed_input=pptr()-pbase();
|
||||
uncompressed_size+=consumed_input;
|
||||
crc=crc32(crc,(Bytef*)in,consumed_input);
|
||||
setp(pbase(),pbase()+buffer_size-4);return 1;}
|
||||
|
||||
virtual int sync()
|
||||
{if(pptr() && pptr()>pbase()) return process(false);return 0;}
|
||||
|
||||
virtual int underflow()
|
||||
{std::runtime_error("Attempt to read write only ostream");return 0;}
|
||||
|
||||
virtual int overflow(int c=EOF)
|
||||
{if(c!=EOF){*pptr()=c;pbump(1);}
|
||||
if(process(false)==EOF) return EOF;
|
||||
return c;}
|
||||
|
||||
//#####################################################################
|
||||
};
|
||||
//#####################################################################
|
||||
// Class ZIP_FILE_ISTREAM
|
||||
//#####################################################################
|
||||
// Class needed because istream cannot own its streambuf
|
||||
class ZIP_FILE_ISTREAM:public std::istream
|
||||
{
|
||||
ZipStreambufDecompress buf;
|
||||
public:
|
||||
ZIP_FILE_ISTREAM(std::istream& istream,ZipFileHeader header)
|
||||
:std::istream(&buf),buf(istream,header)
|
||||
{}
|
||||
|
||||
virtual ~ZIP_FILE_ISTREAM()
|
||||
{}
|
||||
|
||||
//#####################################################################
|
||||
};
|
||||
//#####################################################################
|
||||
// Class ZIP_FILE_OSTREAM
|
||||
//#####################################################################
|
||||
// Class needed because ostream cannot own its streambuf
|
||||
class ZIP_FILE_OSTREAM:public std::ostream
|
||||
{
|
||||
ZipStreambufCompress buf;
|
||||
public:
|
||||
ZIP_FILE_OSTREAM(ZipFileHeader* header,std::ostream& ostream)
|
||||
:std::ostream(&buf),buf(header,ostream)
|
||||
{}
|
||||
|
||||
virtual ~ZIP_FILE_OSTREAM()
|
||||
{}
|
||||
|
||||
//#####################################################################
|
||||
};
|
||||
//#####################################################################
|
||||
// Function ZipFileWriter
|
||||
//#####################################################################
|
||||
ZipFileWriter::
|
||||
ZipFileWriter(std::ostream& stream) : ostream(stream)
|
||||
{
|
||||
if(!ostream) throw std::runtime_error("ZIP: Invalid file handle");
|
||||
}
|
||||
//#####################################################################
|
||||
// Function ZipFileWriter
|
||||
//#####################################################################
|
||||
ZipFileWriter::
|
||||
~ZipFileWriter()
|
||||
{
|
||||
// Write all file headers
|
||||
std::ios::streampos final_position=ostream.tellp();
|
||||
for(unsigned int i=0;i<files.size();i++){files[i]->Write(ostream,true);delete files[i];}
|
||||
std::ios::streampos central_end=ostream.tellp();
|
||||
// Write end of central
|
||||
Write_Primitive(ostream,(unsigned int)0x06054b50); // end of central
|
||||
Write_Primitive(ostream,(unsigned short)0); // this disk number
|
||||
Write_Primitive(ostream,(unsigned short)0); // this disk number
|
||||
Write_Primitive(ostream,(unsigned short)files.size()); // one entry in center in this disk
|
||||
Write_Primitive(ostream,(unsigned short)files.size()); // one entry in center
|
||||
Write_Primitive(ostream,(unsigned int)(central_end-final_position)); // size of header
|
||||
Write_Primitive(ostream,(unsigned int)final_position); // offset to header
|
||||
Write_Primitive(ostream,(unsigned short)0); // zip comment
|
||||
}
|
||||
//#####################################################################
|
||||
// Function ZipFileWriter
|
||||
//#####################################################################
|
||||
std::ostream* ZipFileWriter::
|
||||
Add_File(const std::string& filename,const bool binary)
|
||||
{
|
||||
files.push_back(new ZipFileHeader(filename));
|
||||
return new ZIP_FILE_OSTREAM(files.back(),ostream);
|
||||
}
|
||||
//#####################################################################
|
||||
// Function ZipFileReader
|
||||
//#####################################################################
|
||||
ZipFileReader::
|
||||
ZipFileReader(std::istream &stream) : istream(stream)
|
||||
{
|
||||
if(!istream) throw std::runtime_error("ZIP: Invalid file handle");
|
||||
Find_And_Read_Central_Header();
|
||||
}
|
||||
//#####################################################################
|
||||
// Function ZipFileReader
|
||||
//#####################################################################
|
||||
ZipFileReader::
|
||||
~ZipFileReader()
|
||||
{
|
||||
std::map<std::string,ZipFileHeader*>::iterator i=filename_to_header.begin();
|
||||
for(;i!=filename_to_header.end();++i)
|
||||
delete i->second;
|
||||
}
|
||||
//#####################################################################
|
||||
// Function Find_And_Read_Central_Header
|
||||
//#####################################################################
|
||||
bool ZipFileReader::
|
||||
Find_And_Read_Central_Header()
|
||||
{
|
||||
// Find the header
|
||||
// NOTE: this assumes the zip file header is the last thing written to file...
|
||||
istream.seekg(0,std::ios_base::end);
|
||||
std::ios::streampos end_position=istream.tellg();
|
||||
unsigned int max_comment_size=0xffff; // max size of header
|
||||
unsigned int read_size_before_comment=22;
|
||||
std::ios::streamoff read_start=max_comment_size+read_size_before_comment;
|
||||
if(read_start>end_position) read_start=end_position;
|
||||
istream.seekg(end_position-read_start);
|
||||
char *buf=new char[read_start];
|
||||
if(read_start<=0){std::cerr<<"ZIP: Invalid read buffer size"<<std::endl;return false;}
|
||||
istream.read(buf,read_start);
|
||||
int found=-1;
|
||||
for(unsigned int i=0;i<read_start-3;i++){
|
||||
if(buf[i]==0x50 && buf[i+1]==0x4b && buf[i+2]==0x05 && buf[i+3]==0x06){found=i;break;}}
|
||||
delete [] buf;
|
||||
if(found==-1){std::cerr<<"ZIP: Failed to find zip header"<<std::endl;return false;}
|
||||
// seek to end of central header and read
|
||||
istream.seekg(end_position-(read_start-found));
|
||||
unsigned int word;
|
||||
unsigned short disk_number1,disk_number2,num_files,num_files_this_disk;
|
||||
Read_Primitive(istream,word); // end of central
|
||||
Read_Primitive(istream,disk_number1); // this disk number
|
||||
Read_Primitive(istream,disk_number2); // this disk number
|
||||
if(disk_number1!=disk_number2 || disk_number1!=0){
|
||||
std::cerr<<"ZIP: multiple disk zip files are not supported"<<std::endl;return false;}
|
||||
Read_Primitive(istream,num_files); // one entry in center in this disk
|
||||
Read_Primitive(istream,num_files_this_disk); // one entry in center
|
||||
if(num_files != num_files_this_disk){
|
||||
std::cerr<<"ZIP: multi disk zip files are not supported"<<std::endl;return false;}
|
||||
unsigned int size_of_header,header_offset;
|
||||
Read_Primitive(istream,size_of_header); // size of header
|
||||
Read_Primitive(istream,header_offset); // offset to header
|
||||
// go to header and read all file headers
|
||||
istream.seekg(header_offset);
|
||||
for(int i=0;i<num_files;i++){
|
||||
ZipFileHeader* header=new ZipFileHeader;
|
||||
bool valid=header->Read(istream,true);
|
||||
if(valid) filename_to_header[header->filename]=header;}
|
||||
return true;
|
||||
}
|
||||
//#####################################################################
|
||||
// Function Get_File
|
||||
//#####################################################################
|
||||
std::istream* ZipFileReader::Get_File(const std::string& filename,const bool binary)
|
||||
{
|
||||
std::map<std::string,ZipFileHeader*>::iterator i=filename_to_header.find(filename);
|
||||
if(i!=filename_to_header.end()){
|
||||
ZipFileHeader* header=i->second;
|
||||
istream.seekg((*header).header_offset);return new ZIP_FILE_ISTREAM(istream,*header);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
//#####################################################################
|
||||
// Function Get_File_List
|
||||
//#####################################################################
|
||||
void ZipFileReader::Get_File_List(std::vector<std::string>& filenames) const
|
||||
{
|
||||
filenames.clear();
|
||||
std::map<std::string,ZipFileHeader*>::const_iterator i=filename_to_header.begin();
|
||||
for(;i!=filename_to_header.end();++i)
|
||||
filenames.push_back(i->first);
|
||||
}
|
||||
//#####################################################################
|
||||
// Function Has_File
|
||||
//#####################################################################
|
||||
bool ZipFileReader::Has_File(const std::string &filename) const
|
||||
{
|
||||
return filename_to_header.find(filename) != filename_to_header.end();
|
||||
}
|
||||
|
||||
} // namespace Partio
|
88
source/detail/zip.hpp
Normal file
88
source/detail/zip.hpp
Normal file
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
PARTIO SOFTWARE
|
||||
Copyright 2010 Disney Enterprises, Inc. All rights reserved
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
|
||||
Studios" or the names of its contributors may NOT be used to
|
||||
endorse or promote products derived from this software without
|
||||
specific prior written permission from Walt Disney Pictures.
|
||||
|
||||
Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
|
||||
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
|
||||
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
*/
|
||||
|
||||
#ifndef __ZIP__
|
||||
#define __ZIP__
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
namespace Partio{
|
||||
struct ZipFileHeader;
|
||||
//#####################################################################
|
||||
// Functions Gzip_Out/Gzip_In - Create streams that read/write .gz
|
||||
//#####################################################################
|
||||
std::istream* Gzip_In(const std::string& filename,std::ios::openmode mode);
|
||||
std::ostream* Gzip_Out(const std::string& filename,std::ios::openmode mode);
|
||||
//#####################################################################
|
||||
// Class ZipFileWriter
|
||||
//#####################################################################
|
||||
class ZipFileWriter
|
||||
{
|
||||
std::ostream &ostream;
|
||||
std::vector<ZipFileHeader*> files;
|
||||
public:
|
||||
|
||||
//#####################################################################
|
||||
ZipFileWriter(std::ostream &filename);
|
||||
virtual ~ZipFileWriter();
|
||||
std::ostream* Add_File(const std::string& filename,const bool binary=true);
|
||||
//#####################################################################
|
||||
};
|
||||
|
||||
//#####################################################################
|
||||
// Class ZipFileReader
|
||||
//#####################################################################
|
||||
class ZipFileReader
|
||||
{
|
||||
std::istream &istream;
|
||||
public:
|
||||
std::map<std::string,ZipFileHeader*> filename_to_header;
|
||||
|
||||
//#####################################################################
|
||||
ZipFileReader(std::istream &stream);
|
||||
virtual ~ZipFileReader();
|
||||
std::istream* Get_File(const std::string& filename,const bool binary=true);
|
||||
void Get_File_List(std::vector<std::string>& filenames) const;
|
||||
bool Has_File(const std::string &filename) const;
|
||||
private:
|
||||
bool Find_And_Read_Central_Header();
|
||||
//#####################################################################
|
||||
};
|
||||
}
|
||||
#endif
|
|
@ -1,226 +0,0 @@
|
|||
#include <cassert>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <cxxtest/TestSuite.h>
|
||||
|
||||
#include <xlnt/xlnt.hpp>
|
||||
#include "helpers/path_helper.hpp"
|
||||
#include "helpers/temporary_file.hpp"
|
||||
|
||||
class test_zip_file : public CxxTest::TestSuite
|
||||
{
|
||||
public:
|
||||
test_zip_file()
|
||||
{
|
||||
existing_file = path_helper::get_data_directory("4_not-package.xlsx");
|
||||
expected_string = "not-empty";
|
||||
}
|
||||
|
||||
bool files_equal(const xlnt::path &left, const xlnt::path &right)
|
||||
{
|
||||
if(left.string() == right.string())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
std::ifstream stream_left(left.string(), std::ios::binary);
|
||||
std::ifstream stream_right(right.string(), std::ios::binary);
|
||||
|
||||
while(stream_left && stream_right)
|
||||
{
|
||||
if(stream_left.get() != stream_right.get())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void test_load_file()
|
||||
{
|
||||
temporary_file temp_file;
|
||||
xlnt::zip_file f(existing_file);
|
||||
f.save(temp_file.get_path());
|
||||
TS_ASSERT(files_equal(existing_file, temp_file.get_path()));
|
||||
}
|
||||
|
||||
void test_load_stream()
|
||||
{
|
||||
temporary_file temp;
|
||||
|
||||
std::ifstream in_stream(existing_file.string(), std::ios::binary);
|
||||
xlnt::zip_file f(in_stream);
|
||||
std::ofstream out_stream(temp.get_path().string(), std::ios::binary);
|
||||
f.save(out_stream);
|
||||
out_stream.close();
|
||||
|
||||
TS_ASSERT(files_equal(existing_file, temp.get_path()));
|
||||
}
|
||||
|
||||
void test_load_bytes()
|
||||
{
|
||||
temporary_file temp_file;
|
||||
|
||||
std::vector<std::uint8_t> source_bytes;
|
||||
std::ifstream in_stream(existing_file.string(), std::ios::binary);
|
||||
|
||||
while(in_stream)
|
||||
{
|
||||
source_bytes.push_back(static_cast<std::uint8_t>(in_stream.get()));
|
||||
}
|
||||
|
||||
xlnt::zip_file f(source_bytes);
|
||||
f.save(temp_file.get_path());
|
||||
|
||||
xlnt::zip_file f2;
|
||||
f2.load(temp_file.get_path());
|
||||
std::vector<std::uint8_t> result_bytes;
|
||||
f2.save(result_bytes);
|
||||
|
||||
TS_ASSERT(source_bytes == result_bytes);
|
||||
}
|
||||
|
||||
void test_reset()
|
||||
{
|
||||
xlnt::zip_file f(existing_file);
|
||||
|
||||
TS_ASSERT(!f.namelist().empty());
|
||||
|
||||
try
|
||||
{
|
||||
f.read(xlnt::path("text.txt"));
|
||||
}
|
||||
catch(std::exception e)
|
||||
{
|
||||
TS_ASSERT(false);
|
||||
}
|
||||
|
||||
f.reset();
|
||||
|
||||
TS_ASSERT(f.namelist().empty());
|
||||
|
||||
try
|
||||
{
|
||||
f.read(xlnt::path("doesnt-exist.txt"));
|
||||
TS_ASSERT(false);
|
||||
}
|
||||
catch(std::exception e)
|
||||
{
|
||||
}
|
||||
|
||||
f.write_string("b", xlnt::path("a"));
|
||||
f.reset();
|
||||
|
||||
TS_ASSERT(f.namelist().empty());
|
||||
|
||||
f.write_string("b", xlnt::path("a"));
|
||||
|
||||
TS_ASSERT_DIFFERS(f.getinfo(xlnt::path("a")).file_size, 0);
|
||||
}
|
||||
|
||||
void test_getinfo()
|
||||
{
|
||||
xlnt::zip_file f(existing_file);
|
||||
auto info = f.getinfo(xlnt::path("text.txt"));
|
||||
TS_ASSERT(info.filename.string() == "text.txt");
|
||||
}
|
||||
|
||||
void test_infolist()
|
||||
{
|
||||
xlnt::zip_file f(existing_file);
|
||||
TS_ASSERT_EQUALS(f.infolist().size(), 1);
|
||||
}
|
||||
|
||||
void test_namelist()
|
||||
{
|
||||
xlnt::zip_file f(existing_file);
|
||||
TS_ASSERT_EQUALS(f.namelist().size(), 1);
|
||||
}
|
||||
|
||||
void test_open_by_name()
|
||||
{
|
||||
xlnt::zip_file f(existing_file);
|
||||
std::stringstream ss;
|
||||
ss << f.open(xlnt::path("text.txt")).rdbuf();
|
||||
std::string result = ss.str();
|
||||
TS_ASSERT(result == expected_string);
|
||||
}
|
||||
|
||||
void test_open_by_info()
|
||||
{
|
||||
xlnt::zip_file f(existing_file);
|
||||
std::stringstream ss;
|
||||
ss << f.open(xlnt::path("text.txt")).rdbuf();
|
||||
std::string result = ss.str();
|
||||
TS_ASSERT(result == expected_string);
|
||||
}
|
||||
|
||||
|
||||
void test_read()
|
||||
{
|
||||
xlnt::zip_file f(existing_file);
|
||||
TS_ASSERT(f.read(xlnt::path("text.txt")) == expected_string);
|
||||
TS_ASSERT(f.read(f.getinfo(xlnt::path("text.txt"))) == expected_string);
|
||||
}
|
||||
|
||||
void test_testzip()
|
||||
{
|
||||
xlnt::zip_file f(existing_file);
|
||||
TS_ASSERT(!f.check_crc());
|
||||
}
|
||||
|
||||
void test_write_file()
|
||||
{
|
||||
temporary_file temp_file;
|
||||
|
||||
xlnt::zip_file f;
|
||||
auto text_file = path_helper::get_data_directory("2_text.xlsx");
|
||||
f.write_file(text_file);
|
||||
f.write_file(text_file, xlnt::path("a.txt"));
|
||||
f.save(temp_file.get_path());
|
||||
|
||||
xlnt::zip_file f2(temp_file.get_path());
|
||||
|
||||
for(auto &info : f2.infolist())
|
||||
{
|
||||
TS_ASSERT(f2.read(info) == expected_string);
|
||||
}
|
||||
}
|
||||
|
||||
void test_write_string()
|
||||
{
|
||||
xlnt::zip_file f;
|
||||
f.write_string("a\na", xlnt::path("a.txt"));
|
||||
xlnt::zip_info info;
|
||||
info.filename = xlnt::path("b.txt");
|
||||
info.date_time.year = 2014;
|
||||
f.write_string("b\nb", info);
|
||||
|
||||
temporary_file temp_file;
|
||||
f.save(temp_file.get_path());
|
||||
|
||||
xlnt::zip_file f2(temp_file.get_path());
|
||||
TS_ASSERT(f2.read(xlnt::path("a.txt")) == "a\na");
|
||||
TS_ASSERT(f2.read(f2.getinfo(xlnt::path("b.txt"))) == "b\nb");
|
||||
}
|
||||
|
||||
void test_comment()
|
||||
{
|
||||
xlnt::zip_file f;
|
||||
f.comment = "comment";
|
||||
temporary_file temp_file;
|
||||
f.save(temp_file.get_path());
|
||||
|
||||
xlnt::zip_file f2(temp_file.get_path());
|
||||
TS_ASSERT(f2.comment == "comment");
|
||||
|
||||
xlnt::zip_file f3;
|
||||
std::vector<std::uint8_t> bytes { 1, 2, 3 };
|
||||
TS_ASSERT_THROWS(f3.load(bytes), xlnt::invalid_file);
|
||||
}
|
||||
|
||||
private:
|
||||
xlnt::path existing_file;
|
||||
std::string expected_string;
|
||||
};
|
|
@ -1,627 +0,0 @@
|
|||
// Copyright (c) 2014-2016 Thomas Fussell
|
||||
// Copyright (c) 2010-2015 openpyxl
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE
|
||||
//
|
||||
// @license: http://www.opensource.org/licenses/mit-license.php
|
||||
// @author: see AUTHORS file
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <miniz.h>
|
||||
|
||||
#include <xlnt/packaging/zip_file.hpp>
|
||||
#include <xlnt/utils/path.hpp>
|
||||
#include <xlnt/utils/exceptions.hpp>
|
||||
|
||||
namespace {
|
||||
|
||||
uint32_t crc32buf(const char *buf, std::size_t len)
|
||||
{
|
||||
uint32_t oldcrc32 = 0xFFFFFFFF;
|
||||
|
||||
uint32_t crc_32_tab[] = {
|
||||
/* CRC polynomial 0xedb88320 */
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832,
|
||||
0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
|
||||
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a,
|
||||
0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
|
||||
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
|
||||
0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
|
||||
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab,
|
||||
0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
||||
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4,
|
||||
0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
|
||||
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074,
|
||||
0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
|
||||
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525,
|
||||
0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
|
||||
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
|
||||
0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
||||
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76,
|
||||
0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
|
||||
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6,
|
||||
0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
||||
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7,
|
||||
0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
|
||||
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7,
|
||||
0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
||||
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
|
||||
0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
|
||||
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330,
|
||||
0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
|
||||
};
|
||||
|
||||
#define UPDC32(octet, crc) (crc_32_tab[((crc) ^ static_cast<uint8_t>(octet)) & 0xff] ^ ((crc) >> 8))
|
||||
|
||||
for (; len; --len, ++buf)
|
||||
{
|
||||
oldcrc32 = UPDC32(*buf, oldcrc32);
|
||||
}
|
||||
|
||||
return ~oldcrc32;
|
||||
}
|
||||
|
||||
tm safe_localtime(const time_t &t)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
tm time;
|
||||
localtime_s(&time, &t);
|
||||
return time;
|
||||
#else
|
||||
tm *time = localtime(&t);
|
||||
assert(time != nullptr);
|
||||
return *time;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::size_t write_callback(void *opaque, mz_uint64 file_ofs, const void *pBuf, std::size_t n)
|
||||
{
|
||||
auto buffer = static_cast<std::vector<char> *>(opaque);
|
||||
|
||||
if (file_ofs + n > buffer->size())
|
||||
{
|
||||
auto new_size = static_cast<std::vector<char>::size_type>(file_ofs + n);
|
||||
buffer->resize(new_size);
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < n; i++)
|
||||
{
|
||||
(*buffer)[static_cast<std::size_t>(file_ofs + i)] = (static_cast<const char *>(pBuf))[i];
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace xlnt {
|
||||
|
||||
zip_info::zip_info()
|
||||
: create_system(0),
|
||||
create_version(0),
|
||||
extract_version(0),
|
||||
flag_bits(0),
|
||||
volume(0),
|
||||
internal_attr(0),
|
||||
external_attr(0),
|
||||
header_offset(0),
|
||||
crc(0),
|
||||
compress_size(0),
|
||||
file_size(0)
|
||||
{
|
||||
date_time.year = 1980;
|
||||
date_time.month = 0;
|
||||
date_time.day = 0;
|
||||
date_time.hours = 0;
|
||||
date_time.minutes = 0;
|
||||
date_time.seconds = 0;
|
||||
}
|
||||
|
||||
zip_file::zip_file() : archive_(new mz_zip_archive())
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
zip_file::zip_file(const path &filename) : zip_file()
|
||||
{
|
||||
load(filename);
|
||||
}
|
||||
|
||||
zip_file::zip_file(std::istream &stream) : zip_file()
|
||||
{
|
||||
load(stream);
|
||||
}
|
||||
|
||||
zip_file::zip_file(const std::vector<std::uint8_t> &bytes) : zip_file()
|
||||
{
|
||||
load(bytes);
|
||||
}
|
||||
|
||||
zip_file::~zip_file()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void zip_file::load(std::istream &stream)
|
||||
{
|
||||
if (!stream.good())
|
||||
{
|
||||
throw invalid_file("((std::istream))");
|
||||
}
|
||||
|
||||
reset();
|
||||
buffer_.assign(std::istreambuf_iterator<char>(stream), std::istreambuf_iterator<char>());
|
||||
|
||||
if (buffer_.empty())
|
||||
{
|
||||
throw invalid_file("((stream)) - empty file");
|
||||
}
|
||||
|
||||
remove_comment();
|
||||
start_read();
|
||||
}
|
||||
|
||||
void zip_file::load(const path &filename)
|
||||
{
|
||||
filename_ = filename;
|
||||
std::ifstream stream(filename.string(), std::ios::binary);
|
||||
|
||||
if (!stream.good())
|
||||
{
|
||||
throw invalid_file(filename.string());
|
||||
}
|
||||
|
||||
reset();
|
||||
buffer_.assign(std::istreambuf_iterator<char>(stream), std::istreambuf_iterator<char>());
|
||||
|
||||
if (buffer_.empty())
|
||||
{
|
||||
throw invalid_file(filename.string() + " - empty file");
|
||||
}
|
||||
|
||||
remove_comment();
|
||||
start_read();
|
||||
}
|
||||
|
||||
void zip_file::load(const std::vector<std::uint8_t> &bytes)
|
||||
{
|
||||
if (bytes.empty())
|
||||
{
|
||||
throw invalid_file("((bytes))");
|
||||
}
|
||||
|
||||
reset();
|
||||
buffer_.assign(bytes.begin(), bytes.end());
|
||||
remove_comment();
|
||||
start_read();
|
||||
}
|
||||
|
||||
void zip_file::save(const path &filename)
|
||||
{
|
||||
filename_ = filename;
|
||||
std::ofstream stream(filename.string(), std::ios::binary);
|
||||
save(stream);
|
||||
}
|
||||
|
||||
void zip_file::save(std::ostream &stream)
|
||||
{
|
||||
if (archive_->m_zip_mode == MZ_ZIP_MODE_WRITING)
|
||||
{
|
||||
mz_zip_writer_finalize_archive(archive_.get());
|
||||
}
|
||||
|
||||
if (archive_->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)
|
||||
{
|
||||
mz_zip_writer_end(archive_.get());
|
||||
}
|
||||
|
||||
if (archive_->m_zip_mode == MZ_ZIP_MODE_INVALID)
|
||||
{
|
||||
start_read();
|
||||
}
|
||||
|
||||
append_comment();
|
||||
stream.write(buffer_.data(), static_cast<long>(buffer_.size()));
|
||||
}
|
||||
|
||||
void zip_file::save(std::vector<std::uint8_t> &bytes)
|
||||
{
|
||||
if (archive_->m_zip_mode == MZ_ZIP_MODE_WRITING)
|
||||
{
|
||||
mz_zip_writer_finalize_archive(archive_.get());
|
||||
}
|
||||
|
||||
if (archive_->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)
|
||||
{
|
||||
mz_zip_writer_end(archive_.get());
|
||||
}
|
||||
|
||||
if (archive_->m_zip_mode == MZ_ZIP_MODE_INVALID)
|
||||
{
|
||||
start_read();
|
||||
}
|
||||
|
||||
append_comment();
|
||||
bytes.assign(buffer_.begin(), buffer_.end());
|
||||
}
|
||||
|
||||
void zip_file::append_comment()
|
||||
{
|
||||
if (!comment.empty())
|
||||
{
|
||||
auto comment_length = std::min(static_cast<uint16_t>(comment.length()), std::numeric_limits<uint16_t>::max());
|
||||
buffer_[buffer_.size() - 2] = static_cast<char>(comment_length);
|
||||
buffer_[buffer_.size() - 1] = static_cast<char>(comment_length >> 8);
|
||||
std::copy(comment.begin(), comment.end(), std::back_inserter(buffer_));
|
||||
}
|
||||
}
|
||||
|
||||
void zip_file::remove_comment()
|
||||
{
|
||||
if (buffer_.empty()) return;
|
||||
|
||||
std::size_t position = buffer_.size() - 1;
|
||||
|
||||
for (; position >= 3; position--)
|
||||
{
|
||||
if (buffer_[position - 3] == 'P' && buffer_[position - 2] == 'K' && buffer_[position - 1] == '\x05' &&
|
||||
buffer_[position] == '\x06')
|
||||
{
|
||||
position = position + 17;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (position <= 3)
|
||||
{
|
||||
throw invalid_file("not a zip file");
|
||||
}
|
||||
|
||||
uint16_t length = static_cast<uint16_t>(buffer_[position + 1]);
|
||||
length = static_cast<uint16_t>(length << 8) + static_cast<uint16_t>(buffer_[position]);
|
||||
position += 2;
|
||||
|
||||
if (length != 0)
|
||||
{
|
||||
comment = std::string(buffer_.data() + position, buffer_.data() + position + length);
|
||||
buffer_.resize(buffer_.size() - length);
|
||||
buffer_[buffer_.size() - 1] = 0;
|
||||
buffer_[buffer_.size() - 2] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void zip_file::reset()
|
||||
{
|
||||
switch (archive_->m_zip_mode)
|
||||
{
|
||||
case MZ_ZIP_MODE_READING:
|
||||
mz_zip_reader_end(archive_.get());
|
||||
break;
|
||||
case MZ_ZIP_MODE_WRITING:
|
||||
mz_zip_writer_finalize_archive(archive_.get());
|
||||
mz_zip_writer_end(archive_.get());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
buffer_.clear();
|
||||
comment.clear();
|
||||
|
||||
start_write();
|
||||
mz_zip_writer_finalize_archive(archive_.get());
|
||||
mz_zip_writer_end(archive_.get());
|
||||
}
|
||||
|
||||
zip_info zip_file::getinfo(const path &name)
|
||||
{
|
||||
if (archive_->m_zip_mode != MZ_ZIP_MODE_READING)
|
||||
{
|
||||
start_read();
|
||||
}
|
||||
|
||||
int index = mz_zip_reader_locate_file(archive_.get(), name.string().c_str(), nullptr, 0);
|
||||
|
||||
if (index == -1)
|
||||
{
|
||||
throw std::runtime_error("not found");
|
||||
}
|
||||
|
||||
return getinfo(index);
|
||||
}
|
||||
|
||||
zip_info zip_file::getinfo(int index)
|
||||
{
|
||||
if (archive_->m_zip_mode != MZ_ZIP_MODE_READING)
|
||||
{
|
||||
start_read();
|
||||
}
|
||||
|
||||
mz_zip_archive_file_stat stat;
|
||||
mz_zip_reader_file_stat(archive_.get(), static_cast<mz_uint>(index), &stat);
|
||||
|
||||
zip_info result;
|
||||
|
||||
result.filename = path(std::string(stat.m_filename, stat.m_filename + std::strlen(stat.m_filename)));
|
||||
result.comment = std::string(stat.m_comment, stat.m_comment + stat.m_comment_size);
|
||||
result.compress_size = static_cast<std::size_t>(stat.m_comp_size);
|
||||
result.file_size = static_cast<std::size_t>(stat.m_uncomp_size);
|
||||
result.header_offset = static_cast<std::size_t>(stat.m_local_header_ofs);
|
||||
result.crc = stat.m_crc32;
|
||||
auto time = safe_localtime(stat.m_time);
|
||||
result.date_time.year = 1900 + time.tm_year;
|
||||
result.date_time.month = 1 + time.tm_mon;
|
||||
result.date_time.day = time.tm_mday;
|
||||
result.date_time.hours = time.tm_hour;
|
||||
result.date_time.minutes = time.tm_min;
|
||||
result.date_time.seconds = time.tm_sec;
|
||||
result.flag_bits = stat.m_bit_flag;
|
||||
result.internal_attr = stat.m_internal_attr;
|
||||
result.external_attr = stat.m_external_attr;
|
||||
result.extract_version = stat.m_version_needed;
|
||||
result.create_version = stat.m_version_made_by;
|
||||
result.volume = stat.m_file_index;
|
||||
result.create_system = stat.m_method;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void zip_file::start_read()
|
||||
{
|
||||
if (archive_->m_zip_mode == MZ_ZIP_MODE_READING) return;
|
||||
|
||||
if (archive_->m_zip_mode == MZ_ZIP_MODE_WRITING)
|
||||
{
|
||||
mz_zip_writer_finalize_archive(archive_.get());
|
||||
}
|
||||
|
||||
if (archive_->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)
|
||||
{
|
||||
mz_zip_writer_end(archive_.get());
|
||||
}
|
||||
|
||||
if (!mz_zip_reader_init_mem(archive_.get(), buffer_.data(), buffer_.size(), 0))
|
||||
{
|
||||
throw std::runtime_error("bad zip");
|
||||
}
|
||||
}
|
||||
|
||||
void zip_file::start_write()
|
||||
{
|
||||
if (archive_->m_zip_mode == MZ_ZIP_MODE_WRITING) return;
|
||||
|
||||
switch (archive_->m_zip_mode)
|
||||
{
|
||||
case MZ_ZIP_MODE_READING:
|
||||
{
|
||||
mz_zip_archive archive_copy;
|
||||
std::memset(&archive_copy, 0, sizeof(mz_zip_archive));
|
||||
std::vector<char> buffer_copy(buffer_.begin(), buffer_.end());
|
||||
|
||||
if (!mz_zip_reader_init_mem(&archive_copy, buffer_copy.data(), buffer_copy.size(), 0))
|
||||
{
|
||||
throw std::runtime_error("bad zip");
|
||||
}
|
||||
|
||||
mz_zip_reader_end(archive_.get());
|
||||
|
||||
archive_->m_pWrite = &write_callback;
|
||||
archive_->m_pIO_opaque = &buffer_;
|
||||
buffer_ = std::vector<char>();
|
||||
|
||||
if (!mz_zip_writer_init(archive_.get(), 0))
|
||||
{
|
||||
throw std::runtime_error("bad zip");
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < static_cast<unsigned int>(archive_copy.m_total_files); i++)
|
||||
{
|
||||
if (!mz_zip_writer_add_from_zip_reader(archive_.get(), &archive_copy, i))
|
||||
{
|
||||
throw std::runtime_error("fail");
|
||||
}
|
||||
}
|
||||
|
||||
mz_zip_reader_end(&archive_copy);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
archive_->m_pWrite = &write_callback;
|
||||
archive_->m_pIO_opaque = &buffer_;
|
||||
|
||||
mz_zip_writer_init(archive_.get(), 0);
|
||||
}
|
||||
|
||||
void zip_file::write_file(const path &filename)
|
||||
{
|
||||
path arcname(filename);
|
||||
|
||||
if (filename.is_absolute())
|
||||
{
|
||||
auto split = filename.split();
|
||||
|
||||
auto iter = split.begin() + 1;
|
||||
arcname = path();
|
||||
|
||||
while (iter != split.end())
|
||||
{
|
||||
arcname.append(*iter++);
|
||||
}
|
||||
}
|
||||
|
||||
write_file(filename, arcname);
|
||||
}
|
||||
|
||||
void zip_file::write_file(const path &filename, const path &arcname)
|
||||
{
|
||||
std::fstream file(filename.string(), std::ios::binary | std::ios::in);
|
||||
|
||||
std::stringstream ss;
|
||||
ss << file.rdbuf();
|
||||
|
||||
write_string(ss.str(), arcname);
|
||||
}
|
||||
|
||||
void zip_file::write_string(const std::string &bytes, const path &arcname)
|
||||
{
|
||||
if (archive_->m_zip_mode != MZ_ZIP_MODE_WRITING)
|
||||
{
|
||||
start_write();
|
||||
}
|
||||
|
||||
mz_zip_writer_add_mem(archive_.get(), arcname.string().c_str(),
|
||||
bytes.data(), bytes.size(), MZ_BEST_COMPRESSION);
|
||||
}
|
||||
|
||||
void zip_file::write_string(const std::string &bytes, const zip_info &info)
|
||||
{
|
||||
if (info.filename.string().empty() || info.date_time.year < 1980)
|
||||
{
|
||||
throw std::runtime_error("must specify a filename and valid date (year >= 1980");
|
||||
}
|
||||
|
||||
if (archive_->m_zip_mode != MZ_ZIP_MODE_WRITING)
|
||||
{
|
||||
start_write();
|
||||
}
|
||||
|
||||
auto crc = crc32buf(bytes.c_str(), bytes.size());
|
||||
|
||||
mz_zip_writer_add_mem_ex(archive_.get(), info.filename.string().c_str(), bytes.data(), bytes.size(),
|
||||
info.comment.c_str(), static_cast<mz_uint16>(info.comment.size()),
|
||||
MZ_BEST_COMPRESSION, 0, crc);
|
||||
}
|
||||
|
||||
std::string zip_file::read(const zip_info &info)
|
||||
{
|
||||
std::size_t size;
|
||||
void *data_raw = mz_zip_reader_extract_file_to_heap(archive_.get(),
|
||||
info.filename.string().c_str(), &size, 0);
|
||||
|
||||
if (data_raw == nullptr)
|
||||
{
|
||||
throw std::runtime_error("file couldn't be read");
|
||||
}
|
||||
|
||||
auto data = static_cast<char *>(data_raw);
|
||||
std::string extracted(data, data + size);
|
||||
mz_free(data);
|
||||
|
||||
return extracted;
|
||||
}
|
||||
|
||||
std::string zip_file::read(const path &name)
|
||||
{
|
||||
return read(getinfo(name));
|
||||
}
|
||||
|
||||
bool zip_file::has_file(const path &name)
|
||||
{
|
||||
if (archive_->m_zip_mode != MZ_ZIP_MODE_READING)
|
||||
{
|
||||
start_read();
|
||||
}
|
||||
|
||||
int index = mz_zip_reader_locate_file(archive_.get(), name.string().c_str(), nullptr, 0);
|
||||
|
||||
return index != -1;
|
||||
}
|
||||
|
||||
bool zip_file::has_file(const zip_info &name)
|
||||
{
|
||||
return has_file(name.filename);
|
||||
}
|
||||
|
||||
std::vector<zip_info> zip_file::infolist()
|
||||
{
|
||||
if (archive_->m_zip_mode != MZ_ZIP_MODE_READING)
|
||||
{
|
||||
start_read();
|
||||
}
|
||||
|
||||
std::vector<zip_info> info;
|
||||
|
||||
for (std::size_t i = 0; i < mz_zip_reader_get_num_files(archive_.get()); i++)
|
||||
{
|
||||
info.push_back(getinfo(static_cast<int>(i)));
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
std::vector<path> zip_file::namelist()
|
||||
{
|
||||
std::vector<path> names;
|
||||
|
||||
for (auto &info : infolist())
|
||||
{
|
||||
names.push_back(info.filename);
|
||||
}
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
std::ostream &zip_file::open(const path &name)
|
||||
{
|
||||
return open(getinfo(name));
|
||||
}
|
||||
|
||||
std::ostream &zip_file::open(const zip_info &name)
|
||||
{
|
||||
auto data = read(name);
|
||||
std::string data_string(data.begin(), data.end());
|
||||
open_stream_ << data_string;
|
||||
return open_stream_;
|
||||
}
|
||||
|
||||
bool zip_file::check_crc()
|
||||
{
|
||||
if (archive_->m_zip_mode == MZ_ZIP_MODE_INVALID)
|
||||
{
|
||||
throw std::runtime_error("not open");
|
||||
}
|
||||
|
||||
for (auto &file : infolist())
|
||||
{
|
||||
auto content = read(file);
|
||||
auto crc = crc32buf(content.c_str(), content.size());
|
||||
|
||||
if (crc != file.crc)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
path zip_file::get_filename() const
|
||||
{
|
||||
return filename_;
|
||||
}
|
||||
|
||||
} // namespace xlnt
|
|
@ -7,47 +7,61 @@
|
|||
#include <helpers/path_helper.hpp>
|
||||
#include <xlnt/workbook/workbook.hpp>
|
||||
|
||||
#define TEST_CRYPTO true
|
||||
|
||||
#ifndef TEST_CRYPTO
|
||||
#define TEST_CRYPTO false
|
||||
#endif
|
||||
|
||||
class test_consume_xlsx : public CxxTest::TestSuite
|
||||
{
|
||||
public:
|
||||
void test_decrypt_agile()
|
||||
{
|
||||
xlnt::workbook wb;
|
||||
wb.load(path_helper::get_data_directory("14_encrypted_excel_2016.xlsx"), "secret");
|
||||
}
|
||||
void test_decrypt_agile()
|
||||
{
|
||||
xlnt::workbook wb;
|
||||
#if TEST_CRYPTO
|
||||
wb.load(path_helper::get_data_directory("14_encrypted_excel_2016.xlsx"), "secret");
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_decrypt_libre_office()
|
||||
{
|
||||
xlnt::workbook wb;
|
||||
wb.load(path_helper::get_data_directory("15_encrypted_libre_office.xlsx"), "secret");
|
||||
}
|
||||
void test_decrypt_libre_office()
|
||||
{
|
||||
xlnt::workbook wb;
|
||||
#if TEST_CRYPTO
|
||||
wb.load(path_helper::get_data_directory("15_encrypted_libre_office.xlsx"), "secret");
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_decrypt_standard()
|
||||
{
|
||||
xlnt::workbook wb;
|
||||
wb.load(path_helper::get_data_directory("16_encrypted_excel_2007.xlsx"), "password");
|
||||
}
|
||||
void test_decrypt_standard()
|
||||
{
|
||||
xlnt::workbook wb;
|
||||
#if TEST_CRYPTO
|
||||
wb.load(path_helper::get_data_directory("16_encrypted_excel_2007.xlsx"), "password");
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_decrypt_numbers()
|
||||
{
|
||||
{
|
||||
TS_SKIP("");
|
||||
xlnt::workbook wb;
|
||||
wb.load(path_helper::get_data_directory("17_encrypted_numbers.xlsx"), "secret");
|
||||
}
|
||||
xlnt::workbook wb;
|
||||
#if TEST_CRYPTO
|
||||
wb.load(path_helper::get_data_directory("17_encrypted_numbers.xlsx"), "secret");
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_comments()
|
||||
{
|
||||
xlnt::workbook wb;
|
||||
wb.load("data/18_basic_comments.xlsx");
|
||||
xlnt::workbook wb;
|
||||
wb.load("data/18_basic_comments.xlsx");
|
||||
|
||||
auto sheet1 = wb[0];
|
||||
TS_ASSERT_EQUALS(sheet1.get_cell("A1").get_value<std::string>(), "Sheet1!A1");
|
||||
TS_ASSERT_EQUALS(sheet1.get_cell("A1").comment().plain_text(), "Sheet1 comment");
|
||||
TS_ASSERT_EQUALS(sheet1.get_cell("A1").comment().author(), "Microsoft Office User");
|
||||
auto sheet1 = wb[0];
|
||||
TS_ASSERT_EQUALS(sheet1.get_cell("A1").get_value<std::string>(), "Sheet1!A1");
|
||||
TS_ASSERT_EQUALS(sheet1.get_cell("A1").comment().plain_text(), "Sheet1 comment");
|
||||
TS_ASSERT_EQUALS(sheet1.get_cell("A1").comment().author(), "Microsoft Office User");
|
||||
|
||||
auto sheet2 = wb[1];
|
||||
TS_ASSERT_EQUALS(sheet2.get_cell("A1").get_value<std::string>(), "Sheet2!A1");
|
||||
TS_ASSERT_EQUALS(sheet2.get_cell("A1").comment().plain_text(), "Sheet2 comment");
|
||||
TS_ASSERT_EQUALS(sheet2.get_cell("A1").comment().author(), "Microsoft Office User");
|
||||
auto sheet2 = wb[1];
|
||||
TS_ASSERT_EQUALS(sheet2.get_cell("A1").get_value<std::string>(), "Sheet2!A1");
|
||||
TS_ASSERT_EQUALS(sheet2.get_cell("A1").comment().plain_text(), "Sheet2 comment");
|
||||
TS_ASSERT_EQUALS(sheet2.get_cell("A1").comment().author(), "Microsoft Office User");
|
||||
}
|
||||
};
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
#include <iostream>
|
||||
#include <cxxtest/TestSuite.h>
|
||||
|
||||
#include <detail/vector_streambuf.hpp>
|
||||
#include <helpers/temporary_file.hpp>
|
||||
#include <helpers/path_helper.hpp>
|
||||
#include <helpers/xml_helper.hpp>
|
||||
#include <xlnt/packaging/zip_file.hpp>
|
||||
#include <xlnt/workbook/workbook.hpp>
|
||||
|
||||
class test_produce_xlsx : public CxxTest::TestSuite
|
||||
|
@ -14,13 +14,19 @@ class test_produce_xlsx : public CxxTest::TestSuite
|
|||
public:
|
||||
bool workbook_matches_file(xlnt::workbook &wb, const xlnt::path &file)
|
||||
{
|
||||
std::vector<std::uint8_t> buffer;
|
||||
wb.save(buffer);
|
||||
std::vector<std::uint8_t> wb_data;
|
||||
wb.save(wb_data);
|
||||
|
||||
xlnt::zip_file wb_archive(buffer);
|
||||
xlnt::zip_file file_archive(file);
|
||||
std::ifstream file_stream(file.string(), std::ios::binary);
|
||||
std::vector<std::uint8_t> file_data;
|
||||
|
||||
return xml_helper::xlsx_archives_match(wb_archive, file_archive);
|
||||
{
|
||||
xlnt::detail::vector_ostreambuf file_data_buffer(file_data);
|
||||
std::ostream file_data_stream(&file_data_buffer);
|
||||
file_data_stream << file_stream.rdbuf();
|
||||
}
|
||||
|
||||
return xml_helper::xlsx_archives_match(wb_data, file_data);
|
||||
}
|
||||
|
||||
void test_produce_minimal()
|
||||
|
@ -35,8 +41,9 @@ public:
|
|||
TS_ASSERT(workbook_matches_file(wb, path_helper::get_data_directory("9_default-excel.xlsx")));
|
||||
}
|
||||
|
||||
void _test_produce_default_libre_office()
|
||||
void test_produce_default_libre_office()
|
||||
{
|
||||
TS_SKIP("");
|
||||
xlnt::workbook wb = xlnt::workbook::empty_libre_office();
|
||||
TS_ASSERT(workbook_matches_file(wb, path_helper::get_data_directory("10_default-libre-office.xlsx")));
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
#include <iostream>
|
||||
#include <cxxtest/TestSuite.h>
|
||||
|
||||
#include <detail/vector_streambuf.hpp>
|
||||
#include <helpers/path_helper.hpp>
|
||||
#include <helpers/xml_helper.hpp>
|
||||
#include <xlnt/packaging/zip_file.hpp>
|
||||
#include <xlnt/workbook/workbook.hpp>
|
||||
|
||||
class test_round_trip : public CxxTest::TestSuite
|
||||
|
@ -18,22 +18,16 @@ public:
|
|||
/// </summary>
|
||||
bool round_trip_matches_wrw(const xlnt::workbook &original)
|
||||
{
|
||||
std::vector<std::uint8_t> buffer;
|
||||
original.save(buffer);
|
||||
|
||||
xlnt::zip_file original_archive;
|
||||
original_archive.load(buffer);
|
||||
std::vector<std::uint8_t> original_buffer;
|
||||
original.save(original_buffer);
|
||||
|
||||
xlnt::workbook resulting_workbook;
|
||||
resulting_workbook.load(buffer);
|
||||
resulting_workbook.load(original_buffer);
|
||||
|
||||
buffer.clear();
|
||||
resulting_workbook.save(buffer);
|
||||
|
||||
xlnt::zip_file resulting_archive;
|
||||
resulting_archive.load(buffer);
|
||||
std::vector<std::uint8_t> resulting_buffer;
|
||||
resulting_workbook.save(resulting_buffer);
|
||||
|
||||
return xml_helper::xlsx_archives_match(original_archive, resulting_archive);
|
||||
return xml_helper::xlsx_archives_match(original_buffer, resulting_buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -42,8 +36,14 @@ public:
|
|||
/// </summary>
|
||||
bool round_trip_matches_rw(const xlnt::path &original)
|
||||
{
|
||||
xlnt::zip_file original_archive;
|
||||
original_archive.load(original);
|
||||
std::ifstream file_stream(original.string(), std::ios::binary);
|
||||
std::vector<std::uint8_t> original_data;
|
||||
|
||||
{
|
||||
xlnt::detail::vector_ostreambuf file_data_buffer(original_data);
|
||||
std::ostream file_data_stream(&file_data_buffer);
|
||||
file_data_stream << file_stream.rdbuf();
|
||||
}
|
||||
|
||||
xlnt::workbook original_workbook;
|
||||
original_workbook.load(original);
|
||||
|
@ -51,10 +51,7 @@ public:
|
|||
std::vector<std::uint8_t> buffer;
|
||||
original_workbook.save(buffer);
|
||||
|
||||
xlnt::zip_file resulting_archive;
|
||||
resulting_archive.load(buffer);
|
||||
|
||||
return xml_helper::xlsx_archives_match(original_archive, resulting_archive);
|
||||
return xml_helper::xlsx_archives_match(original_data, buffer);
|
||||
}
|
||||
|
||||
void test_round_trip_minimal_wrw()
|
||||
|
@ -69,14 +66,14 @@ public:
|
|||
TS_ASSERT(round_trip_matches_wrw(wb));
|
||||
}
|
||||
|
||||
void _test_round_trip_empty_libre_office_wrw()
|
||||
void test_round_trip_empty_libre_office_wrw()
|
||||
{
|
||||
TS_SKIP("");
|
||||
xlnt::workbook wb = xlnt::workbook::empty_libre_office();
|
||||
TS_ASSERT(round_trip_matches_wrw(wb));
|
||||
}
|
||||
|
||||
void _test_round_trip_empty_pages_wrw()
|
||||
void test_round_trip_empty_pages_wrw()
|
||||
{
|
||||
TS_SKIP("");
|
||||
xlnt::workbook wb = xlnt::workbook::empty_numbers();
|
||||
|
|
|
@ -26,19 +26,18 @@
|
|||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
|
||||
#include <detail/cell_impl.hpp>
|
||||
#include <detail/constants.hpp>
|
||||
#include <detail/excel_thumbnail.hpp>
|
||||
#include <detail/xlsx_consumer.hpp>
|
||||
#include <detail/xlsx_producer.hpp>
|
||||
#include <detail/vector_streambuf.hpp>
|
||||
#include <detail/workbook_impl.hpp>
|
||||
#include <detail/worksheet_impl.hpp>
|
||||
#include <detail/xlsx_consumer.hpp>
|
||||
#include <detail/xlsx_producer.hpp>
|
||||
#include <xlnt/cell/cell.hpp>
|
||||
#include <xlnt/packaging/manifest.hpp>
|
||||
#include <xlnt/packaging/relationship.hpp>
|
||||
#include <xlnt/packaging/zip_file.hpp>
|
||||
#include <xlnt/styles/alignment.hpp>
|
||||
#include <xlnt/styles/border.hpp>
|
||||
#include <xlnt/styles/format.hpp>
|
||||
|
@ -689,7 +688,9 @@ void workbook::load(const std::vector<std::uint8_t> &data)
|
|||
{
|
||||
clear();
|
||||
detail::xlsx_consumer consumer(*this);
|
||||
consumer.read(data);
|
||||
xlnt::detail::vector_istreambuf data_buffer(data);
|
||||
std::istream data_stream(&data_buffer);
|
||||
consumer.read(data_stream);
|
||||
}
|
||||
|
||||
void workbook::load(const std::string &filename)
|
||||
|
@ -701,7 +702,8 @@ void workbook::load(const path &filename)
|
|||
{
|
||||
clear();
|
||||
detail::xlsx_consumer consumer(*this);
|
||||
consumer.read(filename);
|
||||
std::ifstream file_stream(filename.string(), std::ios::binary);
|
||||
consumer.read(file_stream);
|
||||
}
|
||||
|
||||
void workbook::load(const std::string &filename, const std::string &password)
|
||||
|
@ -719,7 +721,9 @@ void workbook::load(const std::vector<std::uint8_t> &data, const std::string &pa
|
|||
{
|
||||
clear();
|
||||
detail::xlsx_consumer consumer(*this);
|
||||
consumer.read(data, password);
|
||||
xlnt::detail::vector_istreambuf data_buffer(data);
|
||||
std::istream data_stream(&data_buffer);
|
||||
consumer.read(data_stream, password);
|
||||
}
|
||||
|
||||
void workbook::load(std::istream &stream, const std::string &password)
|
||||
|
@ -731,8 +735,9 @@ void workbook::load(std::istream &stream, const std::string &password)
|
|||
|
||||
void workbook::save(std::vector<std::uint8_t> &data) const
|
||||
{
|
||||
detail::xlsx_producer producer(*this);
|
||||
producer.write(data);
|
||||
xlnt::detail::vector_ostreambuf data_buffer(data);
|
||||
std::ostream data_stream(&data_buffer);
|
||||
save(data_stream);
|
||||
}
|
||||
|
||||
void workbook::save(const std::string &filename) const
|
||||
|
@ -743,7 +748,8 @@ void workbook::save(const std::string &filename) const
|
|||
void workbook::save(const path &filename) const
|
||||
{
|
||||
detail::xlsx_producer producer(*this);
|
||||
producer.write(filename);
|
||||
std::ofstream file_stream(filename.string(), std::ios::binary);
|
||||
save(file_stream);
|
||||
}
|
||||
|
||||
void workbook::save(std::ostream &stream) const
|
||||
|
|
|
@ -4,6 +4,7 @@ project(${LIBRARY_NAME}.test VERSION ${LIBRARY_VERSION} LANGUAGES CXX C)
|
|||
|
||||
if(NOT COMBINED_PROJECT)
|
||||
add_subdirectory(${LIBRARY_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/source)
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../third-party ${CMAKE_CURRENT_BINARY_DIR}/third-party)
|
||||
endif()
|
||||
|
||||
include_directories(${LIBRARY_INCLUDE_DIR})
|
||||
|
@ -12,6 +13,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
|||
include_directories(${THIRD_PARTY_DIR}/cxxtest)
|
||||
include_directories(${THIRD_PARTY_DIR}/utfcpp/source)
|
||||
include_directories(${THIRD_PARTY_DIR}/pugixml/src)
|
||||
include_directories(${THIRD_PARTY_DIR}/zlib)
|
||||
|
||||
file(GLOB CELL_TESTS ${LIBRARY_SOURCE_DIR}/cell/tests/test_*.hpp)
|
||||
file(GLOB CHARTS_TESTS ${LIBRARY_SOURCE_DIR}/charts/tests/test_*.hpp)
|
||||
|
@ -24,6 +26,8 @@ file(GLOB UTILS_TESTS ${LIBRARY_SOURCE_DIR}/utils/tests/test_*.hpp)
|
|||
file(GLOB WORKBOOK_TESTS ${LIBRARY_SOURCE_DIR}/workbook/tests/test_*.hpp)
|
||||
file(GLOB WORKSHEET_TESTS ${LIBRARY_SOURCE_DIR}/worksheet/tests/test_*.hpp)
|
||||
|
||||
set(ZIP ${LIBRARY_SOURCE_DIR}/detail/zip.cpp)
|
||||
|
||||
set(TESTS ${CELL_TESTS} ${CHARTS_TESTS} ${CHARTSHEET_TESTS} ${DRAWING_TESTS}
|
||||
${FORMULA_TESTS} ${PACKAGING_TESTS} ${STYLES_TESTS} ${UTILS_TESTS}
|
||||
${WORKBOOK_TESTS} ${WORKSHEET_TESTS})
|
||||
|
@ -38,7 +42,7 @@ file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/tests")
|
|||
set(RUNNER "${CMAKE_CURRENT_BINARY_DIR}/runner-autogen.cpp")
|
||||
set_source_files_properties(${RUNNER} PROPERTIES GENERATED TRUE)
|
||||
|
||||
add_executable(${PROJECT_NAME} ${TEST_HELPERS} ${TESTS} ${RUNNER} ${PUGIXML})
|
||||
add_executable(${PROJECT_NAME} ${TEST_HELPERS} ${TESTS} ${RUNNER} ${PUGIXML} $<TARGET_OBJECTS:xlnt.third-party> ${ZIP})
|
||||
|
||||
source_group(helpers FILES ${TEST_HELPERS})
|
||||
source_group(tests\\cell FILES ${CELL_TESTS})
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <pugixml.hpp>
|
||||
#include <sstream>
|
||||
#include <pugixml.hpp>
|
||||
|
||||
#include <detail/zip.hpp>
|
||||
#include <helpers/path_helper.hpp>
|
||||
#include <xlnt/packaging/manifest.hpp>
|
||||
|
||||
|
@ -20,7 +21,7 @@ public:
|
|||
child_order_differs,
|
||||
equivalent,
|
||||
};
|
||||
|
||||
|
||||
struct comparison_result
|
||||
{
|
||||
difference_type difference;
|
||||
|
@ -274,8 +275,8 @@ public:
|
|||
{
|
||||
std::vector<std::uint8_t> bytes;
|
||||
wb.save(bytes);
|
||||
xlnt::zip_file archive;
|
||||
archive.load(bytes);
|
||||
std::istringstream file_stream(std::string(bytes.begin(), bytes.end()));
|
||||
Partio::ZipFileReader archive(file_stream);
|
||||
|
||||
return string_matches_archive_member(expected, archive, part, content_type);
|
||||
}
|
||||
|
@ -285,27 +286,34 @@ public:
|
|||
{
|
||||
std::vector<std::uint8_t> bytes;
|
||||
wb.save(bytes);
|
||||
xlnt::zip_file archive;
|
||||
archive.load(bytes);
|
||||
std::istringstream file_stream(std::string(bytes.begin(), bytes.end()));
|
||||
Partio::ZipFileReader archive(file_stream);
|
||||
|
||||
return file_matches_archive_member(expected, archive, part, content_type);
|
||||
}
|
||||
|
||||
static bool string_matches_archive_member(const std::string &expected,
|
||||
xlnt::zip_file &archive,
|
||||
Partio::ZipFileReader &archive,
|
||||
const xlnt::path &member,
|
||||
const std::string &content_type)
|
||||
{
|
||||
return compare_files(expected, archive.read(member), content_type);
|
||||
auto stream = archive.Get_File(member.string(), true);
|
||||
std::string contents((std::istreambuf_iterator<char>(*stream)), (std::istreambuf_iterator<char>()));
|
||||
delete stream;
|
||||
return compare_files(expected, contents, content_type);
|
||||
}
|
||||
|
||||
static bool file_matches_archive_member(const xlnt::path &file,
|
||||
xlnt::zip_file &archive,
|
||||
Partio::ZipFileReader &archive,
|
||||
const xlnt::path &member,
|
||||
const std::string &content_type)
|
||||
{
|
||||
if (!archive.has_file(member)) return false;
|
||||
return compare_files(file.read_contents(), archive.read(member), content_type);
|
||||
if (!archive.Has_File(member.string())) return false;
|
||||
std::ostringstream member_stream;
|
||||
auto stream = archive.Get_File(member.string(), true);
|
||||
std::string contents((std::istreambuf_iterator<char>(*stream)), (std::istreambuf_iterator<char>()));
|
||||
delete stream;
|
||||
auto result = compare_files(file.read_contents(), contents, content_type);
|
||||
}
|
||||
|
||||
static bool file_matches_document(const xlnt::path &expected,
|
||||
|
@ -323,10 +331,19 @@ public:
|
|||
return compare_files(string, ss.str(), content_type);
|
||||
}
|
||||
|
||||
static bool xlsx_archives_match(xlnt::zip_file &left, xlnt::zip_file &right)
|
||||
static bool xlsx_archives_match(const std::vector<std::uint8_t> &left, const std::vector<std::uint8_t> &right)
|
||||
{
|
||||
const auto left_info = left.infolist();
|
||||
const auto right_info = right.infolist();
|
||||
xlnt::detail::vector_istreambuf left_buffer(left);
|
||||
std::istream left_stream(&left_buffer);
|
||||
Partio::ZipFileReader left_archive(left_stream);
|
||||
|
||||
const auto left_info = left_archive.filename_to_header;
|
||||
|
||||
xlnt::detail::vector_istreambuf right_buffer(right);
|
||||
std::istream right_stream(&right_buffer);
|
||||
Partio::ZipFileReader right_archive(right_stream);
|
||||
|
||||
const auto right_info = right_archive.filename_to_header;
|
||||
|
||||
if (left_info.size() != right_info.size())
|
||||
{
|
||||
|
@ -335,52 +352,58 @@ public:
|
|||
std::cout << "left has: ";
|
||||
for (auto &info : left_info)
|
||||
{
|
||||
std::cout << info.filename.string() << ", ";
|
||||
std::cout << info.first << ", ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
std::cout << "right has: ";
|
||||
for (auto &info : right_info)
|
||||
{
|
||||
std::cout << info.filename.string() << ", ";
|
||||
std::cout << info.first << ", ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
bool match = true;
|
||||
std::vector<std::uint8_t> buffer;
|
||||
|
||||
left.save(buffer);
|
||||
xlnt::workbook left_workbook;
|
||||
left_workbook.load(buffer);
|
||||
|
||||
buffer.clear();
|
||||
|
||||
right.save(buffer);
|
||||
|
||||
xlnt::workbook right_workbook;
|
||||
right_workbook.load(buffer);
|
||||
right_workbook.load(right);
|
||||
|
||||
xlnt::workbook left_workbook;
|
||||
left_workbook.load(left);
|
||||
|
||||
auto &left_manifest = left_workbook.get_manifest();
|
||||
auto &right_manifest = right_workbook.get_manifest();
|
||||
|
||||
for (auto left_member : left_info)
|
||||
{
|
||||
if (!right.has_file(left_member))
|
||||
if (!right_archive.Has_File(left_member.first))
|
||||
{
|
||||
match = false;
|
||||
std::cout << "right is missing file: " << left_member.filename.string() << std::endl;
|
||||
std::cout << "right is missing file: " << left_member.first << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto left_member_contents = left.read(left_member);
|
||||
auto right_member_contents = right.read(left_member.filename);
|
||||
|
||||
std::unique_ptr<std::istream> left_member_stream(left_archive.Get_File(left_member.first));
|
||||
std::vector<std::uint8_t> left_contents_raw;
|
||||
xlnt::detail::vector_ostreambuf left_contents_buffer(left_contents_raw);
|
||||
std::ostream left_contents_stream(&left_contents_buffer);
|
||||
left_contents_stream << left_member_stream->rdbuf();
|
||||
std::string left_member_contents(left_contents_raw.begin(), left_contents_raw.end());
|
||||
|
||||
std::unique_ptr<std::istream> right_member_stream(left_archive.Get_File(left_member.first));
|
||||
std::vector<std::uint8_t> right_contents_raw;
|
||||
xlnt::detail::vector_ostreambuf right_contents_buffer(right_contents_raw);
|
||||
std::ostream right_contents_stream(&right_contents_buffer);
|
||||
right_contents_stream << right_member_stream->rdbuf();
|
||||
std::string right_member_contents(right_contents_raw.begin(), right_contents_raw.end());
|
||||
|
||||
std::string left_content_type, right_content_type;
|
||||
|
||||
if (left_member.filename.string() != "[Content_Types].xml")
|
||||
if (left_member.first != "[Content_Types].xml")
|
||||
{
|
||||
left_content_type = left_manifest.get_content_type(xlnt::path(left_member.filename.string()));
|
||||
right_content_type = right_manifest.get_content_type(xlnt::path(left_member.filename.string()));
|
||||
left_content_type = left_manifest.get_content_type(xlnt::path(left_member.first));
|
||||
right_content_type = right_manifest.get_content_type(xlnt::path(left_member.first));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -390,7 +413,7 @@ public:
|
|||
if (left_content_type != right_content_type)
|
||||
{
|
||||
std::cout << "content types differ: "
|
||||
<< left_member.filename.string()
|
||||
<< left_member.first
|
||||
<< " "
|
||||
<< left_content_type
|
||||
<< " "
|
||||
|
@ -400,7 +423,7 @@ public:
|
|||
}
|
||||
else if (!compare_files(left_member_contents, right_member_contents, left_content_type))
|
||||
{
|
||||
std::cout << left_member.filename.string() << std::endl;
|
||||
std::cout << left_member.first << std::endl;
|
||||
match = false;
|
||||
}
|
||||
}
|
||||
|
|
29
third-party/CMakeLists.txt
vendored
29
third-party/CMakeLists.txt
vendored
|
@ -2,16 +2,12 @@ include(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/common.cmake)
|
|||
project(${LIBRARY_NAME}.third-party VERSION ${LIBRARY_VERSION} LANGUAGES CXX C)
|
||||
|
||||
# Includes
|
||||
include_directories(miniz
|
||||
libstudxml
|
||||
include_directories(libstudxml
|
||||
utfcpp/source
|
||||
pole
|
||||
botan
|
||||
${LIBRARY_SOURCE_DIR}/detail)
|
||||
zlib)
|
||||
|
||||
set(MINIZ
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/miniz/miniz.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/miniz/miniz.h)
|
||||
set(LIBSTUDXML
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/parser.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/qname.cxx
|
||||
|
@ -26,6 +22,21 @@ set(LIBSTUDXML
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/details/genx/genx.c)
|
||||
set(POLE pole/pole.cpp)
|
||||
set(BOTAN ${CMAKE_CURRENT_SOURCE_DIR}/botan/botan_all.cpp)
|
||||
set(ZLIB ${CMAKE_CURRENT_SOURCE_DIR}/zlib/adler32.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/zlib/compress.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/zlib/crc32.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/zlib/deflate.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/zlib/gzclose.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/zlib/gzlib.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/zlib/gzread.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/zlib/gzwrite.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/zlib/infback.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/zlib/inffast.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/zlib/inflate.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/zlib/inftrees.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/zlib/trees.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/zlib/uncompr.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/zlib/zutil.c)
|
||||
|
||||
if(MSVC)
|
||||
set_source_files_properties(${BOTAN} PROPERTIES COMPILE_FLAGS "/wd\"4244\"")
|
||||
|
@ -42,7 +53,7 @@ add_custom_command(OUTPUT ${BOTAN}
|
|||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/botan
|
||||
COMMENT "Generating botan amalgamation ${BOTAN}")
|
||||
|
||||
add_library(xlnt.third-party OBJECT ${MINIZ} ${LIBSTUDXML} ${POLE} ${BOTAN})
|
||||
add_library(xlnt.third-party OBJECT ${LIBSTUDXML} ${POLE} ${BOTAN} ${ZLIB})
|
||||
target_compile_definitions(xlnt.third-party PRIVATE LIBSTUDXML_STATIC_LIB=1)
|
||||
|
||||
if(NOT STATIC)
|
||||
|
@ -55,5 +66,7 @@ if(MSVC)
|
|||
set_target_properties(xlnt.third-party PROPERTIES COMPILE_FLAGS "/MP")
|
||||
endif()
|
||||
|
||||
source_group(miniz FILES ${MINIZ})
|
||||
source_group(botan FILES ${BOTAN})
|
||||
source_group(libstudxml FILES ${LIBSTUDXML})
|
||||
source_group(pole FILES ${LIBSTUDXML})
|
||||
source_group(zlib FILES ${ZLIB})
|
||||
|
|
4924
third-party/miniz/miniz.c
vendored
4924
third-party/miniz/miniz.c
vendored
File diff suppressed because it is too large
Load Diff
7
third-party/miniz/miniz.h
vendored
7
third-party/miniz/miniz.h
vendored
|
@ -1,7 +0,0 @@
|
|||
#define MINIZ_HEADER_FILE_ONLY
|
||||
|
||||
#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1
|
||||
#define MINIZ_LITTLE_ENDIAN 1
|
||||
#define MINIZ_HAS_64BIT_REGISTERS 1
|
||||
|
||||
#include "miniz.c"
|
1
third-party/zlib
vendored
Submodule
1
third-party/zlib
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 50893291621658f355bc5b4d450a8d06a563053d
|
Loading…
Reference in New Issue
Block a user