mirror of
https://github.com/tfussell/xlnt.git
synced 2024-03-22 13:11:17 +08:00
Merge branch 'master' of github.com:tfussell/xlnt
This commit is contained in:
commit
05fb40438c
|
@ -17,4 +17,10 @@ foreach(SAMPLE_SOURCE IN ITEMS ${SAMPLE_SOURCES})
|
|||
set(SAMPLE_EXECUTABLE benchmark-${SAMPLE_NAME})
|
||||
add_executable(${SAMPLE_EXECUTABLE} ${SAMPLE_SOURCE})
|
||||
target_link_libraries(${SAMPLE_EXECUTABLE} PRIVATE xlnt)
|
||||
if(NOT STATIC)
|
||||
add_custom_command(TARGET ${SAMPLE_EXECUTABLE} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
$<TARGET_FILE:xlnt>
|
||||
$<TARGET_FILE_DIR:${SAMPLE_EXECUTABLE}>)
|
||||
endif()
|
||||
endforeach()
|
||||
|
|
165
include/xlnt/styles/conditional_format.hpp
Normal file
165
include/xlnt/styles/conditional_format.hpp
Normal file
|
@ -0,0 +1,165 @@
|
|||
// Copyright (c) 2014-2017 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include <xlnt/xlnt_config.hpp>
|
||||
#include <xlnt/utils/optional.hpp>
|
||||
|
||||
namespace xlnt {
|
||||
|
||||
class border;
|
||||
class fill;
|
||||
class font;
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct conditional_format_impl;
|
||||
struct stylesheet;
|
||||
class xlsx_consumer;
|
||||
class xlsx_producer;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
class XLNT_API condition
|
||||
{
|
||||
public:
|
||||
static condition text_starts_with(const std::string &start);
|
||||
static condition text_ends_with(const std::string &end);
|
||||
static condition text_contains(const std::string &start);
|
||||
static condition text_does_not_contain(const std::string &start);
|
||||
|
||||
private:
|
||||
friend class detail::xlsx_producer;
|
||||
|
||||
enum class type
|
||||
{
|
||||
contains_text
|
||||
} type_;
|
||||
|
||||
enum class condition_operator
|
||||
{
|
||||
starts_with,
|
||||
ends_with,
|
||||
contains,
|
||||
does_not_contain
|
||||
} operator_;
|
||||
|
||||
std::string text_comparand_;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Describes a conditional format that will be applied to all cells in the
|
||||
/// associated range that satisfy the condition. This can only be constructed
|
||||
/// using methods on worksheet or range.
|
||||
/// </summary>
|
||||
class XLNT_API conditional_format
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Delete zero-argument constructor
|
||||
/// </summary>
|
||||
conditional_format() = delete;
|
||||
|
||||
/// <summary>
|
||||
/// Default copy constructor. Constructs a format using the same PIMPL as other.
|
||||
/// </summary>
|
||||
conditional_format(const conditional_format &other) = default;
|
||||
|
||||
// Formatting (xf) components
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
bool has_border() const;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
class border border() const;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
conditional_format border(const xlnt::border &new_border);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
bool has_fill() const;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
class fill fill() const;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
conditional_format fill(const xlnt::fill &new_fill);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
bool has_font() const;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
class font font() const;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
conditional_format font(const xlnt::font &new_font);
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this format is equivalent to other.
|
||||
/// </summary>
|
||||
bool operator==(const conditional_format &other) const;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this format is not equivalent to other.
|
||||
/// </summary>
|
||||
bool operator!=(const conditional_format &other) const;
|
||||
|
||||
private:
|
||||
friend struct detail::stylesheet;
|
||||
friend class detail::xlsx_consumer;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
conditional_format(detail::conditional_format_impl *d);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
detail::conditional_format_impl *d_;
|
||||
};
|
||||
|
||||
} // namespace xlnt
|
|
@ -30,6 +30,13 @@
|
|||
#include <vector>
|
||||
|
||||
#include <xlnt/xlnt_config.hpp>
|
||||
#include <xlnt/styles/alignment.hpp>
|
||||
#include <xlnt/styles/border.hpp>
|
||||
#include <xlnt/styles/conditional_format.hpp>
|
||||
#include <xlnt/styles/fill.hpp>
|
||||
#include <xlnt/styles/font.hpp>
|
||||
#include <xlnt/styles/number_format.hpp>
|
||||
#include <xlnt/styles/protection.hpp>
|
||||
#include <xlnt/worksheet/cell_vector.hpp>
|
||||
#include <xlnt/worksheet/major_order.hpp>
|
||||
#include <xlnt/worksheet/range_iterator.hpp>
|
||||
|
@ -163,6 +170,11 @@ public:
|
|||
/// </summary>
|
||||
range style(const std::string &style_name);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
xlnt::conditional_format conditional_format(const condition &when);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the first row or column in this range.
|
||||
/// </summary>
|
||||
|
|
|
@ -44,6 +44,8 @@ class cell_reference;
|
|||
class cell_vector;
|
||||
class column_properties;
|
||||
class comment;
|
||||
class condition;
|
||||
class conditional_format;
|
||||
class const_range_iterator;
|
||||
class footer;
|
||||
class header;
|
||||
|
@ -656,6 +658,11 @@ public:
|
|||
/// </summary>
|
||||
void page_break_at_column(column_t column);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a conditional format for the given range with the given condition.
|
||||
/// </summary>
|
||||
xlnt::conditional_format conditional_format(const range_reference &ref, const condition &when);
|
||||
|
||||
private:
|
||||
friend class cell;
|
||||
friend class const_range_iterator;
|
||||
|
|
|
@ -15,4 +15,10 @@ foreach(SAMPLE_SOURCE IN ITEMS ${SAMPLE_SOURCES})
|
|||
set(SAMPLE_EXECUTABLE sample-${SAMPLE_NAME})
|
||||
add_executable(${SAMPLE_EXECUTABLE} ${SAMPLE_SOURCE})
|
||||
target_link_libraries(${SAMPLE_EXECUTABLE} PRIVATE xlnt)
|
||||
if(NOT STATIC)
|
||||
add_custom_command(TARGET ${SAMPLE_EXECUTABLE} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
$<TARGET_FILE:xlnt>
|
||||
$<TARGET_FILE_DIR:${SAMPLE_EXECUTABLE}>)
|
||||
endif()
|
||||
endforeach()
|
||||
|
|
213
samples/img2xlsx.cpp
Normal file
213
samples/img2xlsx.cpp
Normal file
|
@ -0,0 +1,213 @@
|
|||
#include <array>
|
||||
#include <iostream>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include <xlnt/xlnt.hpp>
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
|
||||
// This sample demonstrates the use of some complex formatting methods to create
|
||||
// a workbook in which each cell has a fill based on the pixels of an image
|
||||
// thereby appearing as a mosaic of the given image. Two methods for achieving
|
||||
// this effect are demonstrated: cell-level format and conditional formatting.
|
||||
|
||||
// Clean up the code with some alias declarations
|
||||
using byte = std::uint8_t;
|
||||
using pixel = std::array<byte, 3>;
|
||||
using pixmap = std::vector<std::vector<pixel>>;
|
||||
|
||||
// Specify whether the CF or cell format method should be used
|
||||
static const bool use_conditional_formatting = false;
|
||||
|
||||
// Returns a 2D vector of pixels from a given filename. Accepts all file types
|
||||
// supported by stb_image (JPEG, PNG, TGA, BMP, PSD, GIF, HDR, PIC, PNM).
|
||||
pixmap load_image(const std::string &filename)
|
||||
{
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
int bpp = 0;
|
||||
|
||||
// Must be freed after use with stbi_image_free
|
||||
auto image_data = stbi_load(filename.c_str(), &width, &height, &bpp, 0);
|
||||
|
||||
if (image_data == nullptr)
|
||||
{
|
||||
throw std::runtime_error("bad image or file not found: " + filename);
|
||||
}
|
||||
|
||||
pixmap result;
|
||||
|
||||
for (auto row = 0; row < height; ++row)
|
||||
{
|
||||
std::vector<pixel> row_pixels;
|
||||
|
||||
for (auto column = 0; column < width; ++column)
|
||||
{
|
||||
auto r = image_data[row * width * bpp + column * bpp];
|
||||
auto g = image_data[row * width * bpp + column * bpp + 1];
|
||||
auto b = image_data[row * width * bpp + column * bpp + 2];
|
||||
auto current_pixel = pixel({ r, g, b });
|
||||
row_pixels.push_back(current_pixel);
|
||||
}
|
||||
|
||||
result.push_back(row_pixels);
|
||||
}
|
||||
|
||||
stbi_image_free(image_data);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Builds and returns a workbook in which each cell has a value
|
||||
// equal to the color of the pixel in the given image. A conditional format
|
||||
// is created for every color to set the background fill of each cell to the
|
||||
// color of its value. This is very slow for large images but is intended
|
||||
// to illustrate a realistic use of conditional formatting.
|
||||
xlnt::workbook build_workbook_cf(const pixmap &image)
|
||||
{
|
||||
// Create a default workbook with a single worksheet
|
||||
xlnt::workbook wb;
|
||||
// Get the active sheet (the only sheet in this case)
|
||||
auto ws = wb.active_sheet();
|
||||
// The reference to the cell which is being operated upon
|
||||
auto current_cell = xlnt::cell_reference("A1");
|
||||
// The range of cells which will be modified. This is required for conditional formats
|
||||
auto range = ws.range(xlnt::range_reference(1, 1, image[0].size(), image.size()));
|
||||
|
||||
// Track the previously created conditonal formats so they are only created once
|
||||
std::unordered_set<std::string> defined_colors;
|
||||
|
||||
// Iterate over each row in the source image
|
||||
for (const auto &image_row : image)
|
||||
{
|
||||
// Iterate over each pixel in the image row
|
||||
for (const auto &image_pixel : image_row)
|
||||
{
|
||||
// Build an xlnt compatible RGB color from the pixel byte array
|
||||
const auto color = xlnt::rgb_color(image_pixel[0], image_pixel[1], image_pixel[2]);
|
||||
|
||||
// Only create the conditional format if it doesn't yet exist
|
||||
if (defined_colors.count(color.hex_string()) == 0)
|
||||
{
|
||||
// The condition under which the conditional format applies to a cell
|
||||
// In this case, the condition is satisfied when the text of the cell
|
||||
// contains the hex string representing the pixel color.
|
||||
const auto condition = xlnt::condition::text_contains(color.hex_string());
|
||||
// Create a new conditional format with the above condition on the image pixel range
|
||||
auto format = range.conditional_format(condition);
|
||||
// Define the fill for the conditional format
|
||||
format.fill(xlnt::pattern_fill().background(color));
|
||||
|
||||
// Record the created of this CF
|
||||
defined_colors.insert(color.hex_string());
|
||||
}
|
||||
|
||||
// Dereference the cell at the current position and set its value to the pixel color
|
||||
ws.cell(current_cell).value(color.hex_string());
|
||||
// Increment the column
|
||||
current_cell.column_index(current_cell.column_index() + 1);
|
||||
}
|
||||
|
||||
// Reached the end of the row--move to the first column of the next row
|
||||
current_cell.row(current_cell.row() + 1);
|
||||
current_cell.column_index("A");
|
||||
|
||||
// Show some progress, it can take a while...
|
||||
std::cout << current_cell.row() << " " << defined_colors.size() << std::endl;
|
||||
}
|
||||
|
||||
// Return the resulting workbook
|
||||
return wb;
|
||||
}
|
||||
|
||||
// Builds and returns a workbook in which each cell has a value
|
||||
// equal to the color of the pixel in the given image. To accomplish this,
|
||||
// a named style is created for every color in the image and is applied
|
||||
// to each cell corresponding to pixels of that color.
|
||||
xlnt::workbook build_workbook_normal(const pixmap &image)
|
||||
{
|
||||
// Create a default workbook with a single worksheet
|
||||
xlnt::workbook wb;
|
||||
// Get the active sheet (the only sheet in this case)
|
||||
auto ws = wb.active_sheet();
|
||||
// The reference to the cell which is being operated upon
|
||||
auto current_cell = xlnt::cell_reference("A1");
|
||||
|
||||
// Iterate over each row in the source image
|
||||
for (const auto &image_row : image)
|
||||
{
|
||||
// Iterate over each pixel in the image row
|
||||
for (const auto &image_pixel : image_row)
|
||||
{
|
||||
// Build an xlnt compatible RGB color from the pixel byte array
|
||||
const auto color = xlnt::rgb_color(image_pixel[0], image_pixel[1], image_pixel[2]);
|
||||
|
||||
// Only create the style if it doesn't yet exist in the workbook
|
||||
if (!wb.has_style(color.hex_string()))
|
||||
{
|
||||
// A style is constructed on a workbook by providing a unique name
|
||||
auto color_style = wb.create_style(color.hex_string());
|
||||
// Set the fill to a solid fill of the pixel color
|
||||
color_style.fill(xlnt::fill::solid(color));
|
||||
}
|
||||
|
||||
// Dereference the cell at the current position and use the previously created color style
|
||||
ws.cell(current_cell).style(color.hex_string());
|
||||
// Increment the column
|
||||
current_cell.column_index(current_cell.column_index() + 1);
|
||||
}
|
||||
|
||||
// Reached the end of the row--move to the first column of the next row
|
||||
current_cell.row(current_cell.row() + 1);
|
||||
current_cell.column_index("A");
|
||||
|
||||
// Show some progress, it can take a while...
|
||||
std::cout << current_cell.row() << std::endl;
|
||||
}
|
||||
|
||||
// Return the resulting workbook
|
||||
return wb;
|
||||
}
|
||||
|
||||
// Builds and returns a workbook in which each cell has a value
|
||||
// equal to the color of the pixel in the given image using the
|
||||
// conditional formatting or cell-level formatting depending on
|
||||
// the value of use_conditional_formatting defined at the top of
|
||||
// this file.
|
||||
xlnt::workbook build_workbook(const pixmap &image)
|
||||
{
|
||||
if (use_conditional_formatting)
|
||||
{
|
||||
return build_workbook_cf(image);
|
||||
}
|
||||
else
|
||||
{
|
||||
return build_workbook_normal(image);
|
||||
}
|
||||
}
|
||||
|
||||
// Entry point
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Ensure that there is a correct number of arguments
|
||||
if (argc < 3)
|
||||
{
|
||||
std::cout << "usage: img2xlsx <input> <output>" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The first argument is the name of the input image
|
||||
const auto input = std::string(argv[1]);
|
||||
// Load the input image. An exception will be thrown if it doesn't exist.
|
||||
const auto image = load_image(input);
|
||||
// Build the workbook from the image
|
||||
auto workbook = build_workbook(image);
|
||||
// The second argument is the name of the file to save the workbook as.
|
||||
const auto output = std::string(argv[2]);
|
||||
// Save the workbook
|
||||
workbook.save(output);
|
||||
|
||||
return 0;
|
||||
}
|
7177
samples/stb_image.h
Normal file
7177
samples/stb_image.h
Normal file
File diff suppressed because it is too large
Load Diff
37
source/detail/conditional_format_impl.hpp
Normal file
37
source/detail/conditional_format_impl.hpp
Normal file
|
@ -0,0 +1,37 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include <xlnt/styles/conditional_format.hpp>
|
||||
#include <xlnt/utils/optional.hpp>
|
||||
#include <xlnt/worksheet/range_reference.hpp>
|
||||
|
||||
namespace xlnt {
|
||||
|
||||
class border;
|
||||
class fill;
|
||||
class font;
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct stylesheet;
|
||||
struct worksheet_impl;
|
||||
|
||||
struct conditional_format_impl
|
||||
{
|
||||
stylesheet *parent;
|
||||
range_reference target_range;
|
||||
worksheet_impl *target_sheet;
|
||||
|
||||
std::size_t priority;
|
||||
std::size_t differential_format_id;
|
||||
|
||||
condition when;
|
||||
|
||||
optional<std::size_t> border_id;
|
||||
optional<std::size_t> fill_id;
|
||||
optional<std::size_t> font_id;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace xlnt
|
|
@ -27,15 +27,17 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <detail/conditional_format_impl.hpp>
|
||||
#include <detail/format_impl.hpp>
|
||||
#include <detail/style_impl.hpp>
|
||||
#include <xlnt/cell/cell.hpp>
|
||||
#include <xlnt/styles/conditional_format.hpp>
|
||||
#include <xlnt/styles/format.hpp>
|
||||
#include <xlnt/styles/style.hpp>
|
||||
#include <xlnt/utils/exceptions.hpp>
|
||||
#include <xlnt/worksheet/worksheet.hpp>
|
||||
#include <xlnt/workbook/workbook.hpp>
|
||||
#include <xlnt/workbook/worksheet_iterator.hpp>
|
||||
#include <xlnt/worksheet/worksheet.hpp>
|
||||
|
||||
namespace xlnt {
|
||||
namespace detail {
|
||||
|
@ -64,6 +66,7 @@ struct stylesheet
|
|||
{
|
||||
auto iter = format_impls.begin();
|
||||
std::advance(iter, static_cast<std::list<format_impl>::difference_type>(index));
|
||||
|
||||
return xlnt::format(&*iter);
|
||||
}
|
||||
|
||||
|
@ -495,6 +498,7 @@ struct stylesheet
|
|||
|
||||
void clear()
|
||||
{
|
||||
conditional_format_impls.clear();
|
||||
format_impls.clear();
|
||||
|
||||
style_impls.clear();
|
||||
|
@ -510,10 +514,25 @@ struct stylesheet
|
|||
colors.clear();
|
||||
}
|
||||
|
||||
conditional_format add_conditional_format_rule(worksheet_impl *ws, const range_reference &ref, const condition &when)
|
||||
{
|
||||
conditional_format_impls.push_back(conditional_format_impl());
|
||||
|
||||
auto &impl = conditional_format_impls.back();
|
||||
impl.when = when;
|
||||
impl.parent = this;
|
||||
impl.target_sheet = ws;
|
||||
impl.target_range = ref;
|
||||
impl.differential_format_id = conditional_format_impls.size() - 1;
|
||||
|
||||
return xlnt::conditional_format(&impl);
|
||||
}
|
||||
|
||||
workbook *parent;
|
||||
|
||||
bool garbage_collection_enabled = true;
|
||||
bool garbage_collection_enabled = false;
|
||||
|
||||
std::list<conditional_format_impl> conditional_format_impls;
|
||||
std::list<format_impl> format_impls;
|
||||
std::unordered_map<std::string, style_impl> style_impls;
|
||||
std::vector<std::string> style_names;
|
||||
|
|
|
@ -909,9 +909,201 @@ void xlsx_producer::write_shared_workbook_user_data(const relationship & /*rel*/
|
|||
write_end_element(constants::ns("spreadsheetml"), "users");
|
||||
}
|
||||
|
||||
void xlsx_producer::write_font(const font &f)
|
||||
{
|
||||
static const auto &xmlns = constants::ns("spreadsheetml");
|
||||
|
||||
write_start_element(xmlns, "font");
|
||||
|
||||
if (f.bold())
|
||||
{
|
||||
write_start_element(xmlns, "b");
|
||||
write_attribute("val", write_bool(true));
|
||||
write_end_element(xmlns, "b");
|
||||
}
|
||||
|
||||
if (f.italic())
|
||||
{
|
||||
write_start_element(xmlns, "i");
|
||||
write_attribute("val", write_bool(true));
|
||||
write_end_element(xmlns, "i");
|
||||
}
|
||||
|
||||
if (f.underlined())
|
||||
{
|
||||
write_start_element(xmlns, "u");
|
||||
write_attribute("val", f.underline());
|
||||
write_end_element(xmlns, "u");
|
||||
}
|
||||
|
||||
if (f.strikethrough())
|
||||
{
|
||||
write_start_element(xmlns, "strike");
|
||||
write_attribute("val", write_bool(true));
|
||||
write_end_element(xmlns, "strike");
|
||||
}
|
||||
|
||||
write_start_element(xmlns, "sz");
|
||||
write_attribute("val", f.size());
|
||||
write_end_element(xmlns, "sz");
|
||||
|
||||
if (f.has_color())
|
||||
{
|
||||
write_start_element(xmlns, "color");
|
||||
write_color(f.color());
|
||||
write_end_element(xmlns, "color");
|
||||
}
|
||||
|
||||
write_start_element(xmlns, "name");
|
||||
write_attribute("val", f.name());
|
||||
write_end_element(xmlns, "name");
|
||||
|
||||
if (f.has_family())
|
||||
{
|
||||
write_start_element(xmlns, "family");
|
||||
write_attribute("val", f.family());
|
||||
write_end_element(xmlns, "family");
|
||||
}
|
||||
|
||||
if (f.has_scheme())
|
||||
{
|
||||
write_start_element(xmlns, "scheme");
|
||||
write_attribute("val", f.scheme());
|
||||
write_end_element(xmlns, "scheme");
|
||||
}
|
||||
|
||||
write_end_element(xmlns, "font");
|
||||
}
|
||||
|
||||
void xlsx_producer::write_fill(const fill &f)
|
||||
{
|
||||
static const auto &xmlns = constants::ns("spreadsheetml");
|
||||
|
||||
write_start_element(xmlns, "fill");
|
||||
|
||||
if (f.type() == xlnt::fill_type::pattern)
|
||||
{
|
||||
const auto &pattern = f.pattern_fill();
|
||||
|
||||
write_start_element(xmlns, "patternFill");
|
||||
|
||||
if (pattern.type() != pattern_fill_type::none)
|
||||
{
|
||||
write_attribute("patternType", pattern.type());
|
||||
}
|
||||
|
||||
if (pattern.foreground().is_set())
|
||||
{
|
||||
write_start_element(xmlns, "fgColor");
|
||||
write_color(pattern.foreground().get());
|
||||
write_end_element(xmlns, "fgColor");
|
||||
}
|
||||
|
||||
if (pattern.background().is_set())
|
||||
{
|
||||
write_start_element(xmlns, "bgColor");
|
||||
write_color(pattern.background().get());
|
||||
write_end_element(xmlns, "bgColor");
|
||||
}
|
||||
|
||||
write_end_element(xmlns, "patternFill");
|
||||
}
|
||||
else if (f.type() == xlnt::fill_type::gradient)
|
||||
{
|
||||
const auto &gradient = f.gradient_fill();
|
||||
|
||||
write_start_element(xmlns, "gradientFill");
|
||||
write_attribute("gradientType", gradient.type());
|
||||
|
||||
if (gradient.degree() != 0.)
|
||||
{
|
||||
write_attribute("degree", gradient.degree());
|
||||
}
|
||||
|
||||
if (gradient.left() != 0.)
|
||||
{
|
||||
write_attribute("left", gradient.left());
|
||||
}
|
||||
|
||||
if (gradient.right() != 0.)
|
||||
{
|
||||
write_attribute("right", gradient.right());
|
||||
}
|
||||
|
||||
if (gradient.top() != 0.)
|
||||
{
|
||||
write_attribute("top", gradient.top());
|
||||
}
|
||||
|
||||
if (gradient.bottom() != 0.)
|
||||
{
|
||||
write_attribute("bottom", gradient.bottom());
|
||||
}
|
||||
|
||||
for (const auto &stop : gradient.stops())
|
||||
{
|
||||
write_start_element(xmlns, "stop");
|
||||
write_attribute("position", stop.first);
|
||||
write_start_element(xmlns, "color");
|
||||
write_color(stop.second);
|
||||
write_end_element(xmlns, "color");
|
||||
write_end_element(xmlns, "stop");
|
||||
}
|
||||
|
||||
write_end_element(xmlns, "gradientFill");
|
||||
}
|
||||
|
||||
write_end_element(xmlns, "fill");
|
||||
}
|
||||
|
||||
void xlsx_producer::write_border(const border ¤t_border)
|
||||
{
|
||||
static const auto &xmlns = constants::ns("spreadsheetml");
|
||||
|
||||
write_start_element(xmlns, "border");
|
||||
|
||||
if (current_border.diagonal().is_set())
|
||||
{
|
||||
auto up = current_border.diagonal().get() == diagonal_direction::both
|
||||
|| current_border.diagonal().get() == diagonal_direction::up;
|
||||
write_attribute("diagonalUp", write_bool(up));
|
||||
|
||||
auto down = current_border.diagonal().get() == diagonal_direction::both
|
||||
|| current_border.diagonal().get() == diagonal_direction::down;
|
||||
write_attribute("diagonalDown", write_bool(down));
|
||||
}
|
||||
|
||||
for (const auto &side : xlnt::border::all_sides())
|
||||
{
|
||||
if (current_border.side(side).is_set())
|
||||
{
|
||||
const auto current_side = current_border.side(side).get();
|
||||
|
||||
auto side_name = to_string(side);
|
||||
write_start_element(xmlns, side_name);
|
||||
|
||||
if (current_side.style().is_set())
|
||||
{
|
||||
write_attribute("style", current_side.style().get());
|
||||
}
|
||||
|
||||
if (current_side.color().is_set())
|
||||
{
|
||||
write_start_element(xmlns, "color");
|
||||
write_color(current_side.color().get());
|
||||
write_end_element(xmlns, "color");
|
||||
}
|
||||
|
||||
write_end_element(xmlns, side_name);
|
||||
}
|
||||
}
|
||||
|
||||
write_end_element(xmlns, "border");
|
||||
}
|
||||
|
||||
void xlsx_producer::write_styles(const relationship & /*rel*/)
|
||||
{
|
||||
static const auto &xmlns = constants::ns("spreadsheetml");
|
||||
static const auto &xmlns = constants::ns("spreadsheetml");
|
||||
|
||||
write_start_element(xmlns, "styleSheet");
|
||||
write_namespace(xmlns, "");
|
||||
|
@ -954,69 +1146,10 @@ void xlsx_producer::write_styles(const relationship & /*rel*/)
|
|||
write_start_element(xmlns, "fonts");
|
||||
write_attribute("count", fonts.size());
|
||||
|
||||
for (const auto ¤t_font : fonts)
|
||||
{
|
||||
write_start_element(xmlns, "font");
|
||||
|
||||
if (current_font.bold())
|
||||
{
|
||||
write_start_element(xmlns, "b");
|
||||
write_attribute("val", write_bool(true));
|
||||
write_end_element(xmlns, "b");
|
||||
}
|
||||
|
||||
if (current_font.italic())
|
||||
{
|
||||
write_start_element(xmlns, "i");
|
||||
write_attribute("val", write_bool(true));
|
||||
write_end_element(xmlns, "i");
|
||||
}
|
||||
|
||||
if (current_font.underlined())
|
||||
{
|
||||
write_start_element(xmlns, "u");
|
||||
write_attribute("val", current_font.underline());
|
||||
write_end_element(xmlns, "u");
|
||||
}
|
||||
|
||||
if (current_font.strikethrough())
|
||||
{
|
||||
write_start_element(xmlns, "strike");
|
||||
write_attribute("val", write_bool(true));
|
||||
write_end_element(xmlns, "strike");
|
||||
}
|
||||
|
||||
write_start_element(xmlns, "sz");
|
||||
write_attribute("val", current_font.size());
|
||||
write_end_element(xmlns, "sz");
|
||||
|
||||
if (current_font.has_color())
|
||||
{
|
||||
write_start_element(xmlns, "color");
|
||||
write_color(current_font.color());
|
||||
write_end_element(xmlns, "color");
|
||||
}
|
||||
|
||||
write_start_element(xmlns, "name");
|
||||
write_attribute("val", current_font.name());
|
||||
write_end_element(xmlns, "name");
|
||||
|
||||
if (current_font.has_family())
|
||||
{
|
||||
write_start_element(xmlns, "family");
|
||||
write_attribute("val", current_font.family());
|
||||
write_end_element(xmlns, "family");
|
||||
}
|
||||
|
||||
if (current_font.has_scheme())
|
||||
{
|
||||
write_start_element(xmlns, "scheme");
|
||||
write_attribute("val", current_font.scheme());
|
||||
write_end_element(xmlns, "scheme");
|
||||
}
|
||||
|
||||
write_end_element(xmlns, "font");
|
||||
}
|
||||
for (const auto ¤t_font : fonts)
|
||||
{
|
||||
write_font(current_font);
|
||||
}
|
||||
|
||||
write_end_element(xmlns, "fonts");
|
||||
}
|
||||
|
@ -1030,79 +1163,9 @@ void xlsx_producer::write_styles(const relationship & /*rel*/)
|
|||
write_start_element(xmlns, "fills");
|
||||
write_attribute("count", fills.size());
|
||||
|
||||
for (auto &fill_ : fills)
|
||||
for (auto ¤t_fill : fills)
|
||||
{
|
||||
write_start_element(xmlns, "fill");
|
||||
|
||||
if (fill_.type() == xlnt::fill_type::pattern)
|
||||
{
|
||||
const auto &pattern = fill_.pattern_fill();
|
||||
|
||||
write_start_element(xmlns, "patternFill");
|
||||
write_attribute("patternType", pattern.type());
|
||||
|
||||
if (pattern.foreground().is_set())
|
||||
{
|
||||
write_start_element(xmlns, "fgColor");
|
||||
write_color(pattern.foreground().get());
|
||||
write_end_element(xmlns, "fgColor");
|
||||
}
|
||||
|
||||
if (pattern.background().is_set())
|
||||
{
|
||||
write_start_element(xmlns, "bgColor");
|
||||
write_color(pattern.background().get());
|
||||
write_end_element(xmlns, "bgColor");
|
||||
}
|
||||
|
||||
write_end_element(xmlns, "patternFill");
|
||||
}
|
||||
else if (fill_.type() == xlnt::fill_type::gradient)
|
||||
{
|
||||
const auto &gradient = fill_.gradient_fill();
|
||||
|
||||
write_start_element(xmlns, "gradientFill");
|
||||
write_attribute("gradientType", gradient.type());
|
||||
|
||||
if (gradient.degree() != 0.)
|
||||
{
|
||||
write_attribute("degree", gradient.degree());
|
||||
}
|
||||
|
||||
if (gradient.left() != 0.)
|
||||
{
|
||||
write_attribute("left", gradient.left());
|
||||
}
|
||||
|
||||
if (gradient.right() != 0.)
|
||||
{
|
||||
write_attribute("right", gradient.right());
|
||||
}
|
||||
|
||||
if (gradient.top() != 0.)
|
||||
{
|
||||
write_attribute("top", gradient.top());
|
||||
}
|
||||
|
||||
if (gradient.bottom() != 0.)
|
||||
{
|
||||
write_attribute("bottom", gradient.bottom());
|
||||
}
|
||||
|
||||
for (const auto &stop : gradient.stops())
|
||||
{
|
||||
write_start_element(xmlns, "stop");
|
||||
write_attribute("position", stop.first);
|
||||
write_start_element(xmlns, "color");
|
||||
write_color(stop.second);
|
||||
write_end_element(xmlns, "color");
|
||||
write_end_element(xmlns, "stop");
|
||||
}
|
||||
|
||||
write_end_element(xmlns, "gradientFill");
|
||||
}
|
||||
|
||||
write_end_element(xmlns, "fill");
|
||||
write_fill(current_fill);
|
||||
}
|
||||
|
||||
write_end_element(xmlns, "fills");
|
||||
|
@ -1119,45 +1182,7 @@ void xlsx_producer::write_styles(const relationship & /*rel*/)
|
|||
|
||||
for (const auto ¤t_border : borders)
|
||||
{
|
||||
write_start_element(xmlns, "border");
|
||||
|
||||
if (current_border.diagonal().is_set())
|
||||
{
|
||||
auto up = current_border.diagonal().get() == diagonal_direction::both
|
||||
|| current_border.diagonal().get() == diagonal_direction::up;
|
||||
write_attribute("diagonalUp", write_bool(up));
|
||||
|
||||
auto down = current_border.diagonal().get() == diagonal_direction::both
|
||||
|| current_border.diagonal().get() == diagonal_direction::down;
|
||||
write_attribute("diagonalDown", write_bool(down));
|
||||
}
|
||||
|
||||
for (const auto &side : xlnt::border::all_sides())
|
||||
{
|
||||
if (current_border.side(side).is_set())
|
||||
{
|
||||
const auto current_side = current_border.side(side).get();
|
||||
|
||||
auto side_name = to_string(side);
|
||||
write_start_element(xmlns, side_name);
|
||||
|
||||
if (current_side.style().is_set())
|
||||
{
|
||||
write_attribute("style", current_side.style().get());
|
||||
}
|
||||
|
||||
if (current_side.color().is_set())
|
||||
{
|
||||
write_start_element(xmlns, "color");
|
||||
write_color(current_side.color().get());
|
||||
write_end_element(xmlns, "color");
|
||||
}
|
||||
|
||||
write_end_element(xmlns, side_name);
|
||||
}
|
||||
}
|
||||
|
||||
write_end_element(xmlns, "border");
|
||||
write_border(current_border);
|
||||
}
|
||||
|
||||
write_end_element(xmlns, "borders");
|
||||
|
@ -1433,8 +1458,35 @@ void xlsx_producer::write_styles(const relationship & /*rel*/)
|
|||
write_end_element(xmlns, "cellStyles");
|
||||
}
|
||||
|
||||
// Conditional Formats
|
||||
write_start_element(xmlns, "dxfs");
|
||||
write_attribute("count", "0");
|
||||
write_attribute("count", stylesheet.conditional_format_impls.size());
|
||||
|
||||
for (auto &rule : stylesheet.conditional_format_impls)
|
||||
{
|
||||
write_start_element(xmlns, "dxf");
|
||||
|
||||
if (rule.border_id.is_set())
|
||||
{
|
||||
const auto ¤t_border = stylesheet.borders.at(rule.border_id.get());
|
||||
write_border(current_border);
|
||||
}
|
||||
|
||||
if (rule.fill_id.is_set())
|
||||
{
|
||||
const auto ¤t_fill = stylesheet.fills.at(rule.fill_id.get());
|
||||
write_fill(current_fill);
|
||||
}
|
||||
|
||||
if (rule.font_id.is_set())
|
||||
{
|
||||
const auto ¤t_font = stylesheet.fonts.at(rule.font_id.get());
|
||||
write_font(current_font);
|
||||
}
|
||||
|
||||
write_end_element(xmlns, "dxf");
|
||||
}
|
||||
|
||||
write_end_element(xmlns, "dxfs");
|
||||
|
||||
write_start_element(xmlns, "tableStyles");
|
||||
|
@ -2312,6 +2364,49 @@ void xlsx_producer::write_worksheet(const relationship &rel)
|
|||
write_end_element(xmlns, "mergeCells");
|
||||
}
|
||||
|
||||
if (source_.impl().stylesheet_.is_set())
|
||||
{
|
||||
const auto &stylesheet = source_.impl().stylesheet_.get();
|
||||
const auto &cf_impls = stylesheet.conditional_format_impls;
|
||||
|
||||
std::unordered_map<std::string, std::vector<const conditional_format_impl *>> range_map;
|
||||
|
||||
for (auto &cf : cf_impls)
|
||||
{
|
||||
if (cf.target_sheet != ws.d_) continue;
|
||||
|
||||
if (range_map.find(cf.target_range.to_string()) == range_map.end())
|
||||
{
|
||||
range_map[cf.target_range.to_string()] = {};
|
||||
}
|
||||
|
||||
range_map[cf.target_range.to_string()].push_back(&cf);
|
||||
}
|
||||
|
||||
for (const auto &range_rules_pair : range_map)
|
||||
{
|
||||
write_start_element(xmlns, "conditionalFormatting");
|
||||
write_attribute("sqref", range_rules_pair.first);
|
||||
|
||||
std::size_t i = 1;
|
||||
|
||||
for (auto rule : range_rules_pair.second)
|
||||
{
|
||||
write_start_element(xmlns, "cfRule");
|
||||
write_attribute("type", "containsText");
|
||||
write_attribute("operator", "containsText");
|
||||
write_attribute("dxfId", rule->differential_format_id);
|
||||
write_attribute("priority", i++);
|
||||
write_attribute("text", rule->when.text_comparand_);
|
||||
//TODO: what does this formula mean and why is it necessary?
|
||||
write_element(xmlns, "formula", "NOT(ISERROR(SEARCH(\"" + rule->when.text_comparand_ + "\",A1)))");
|
||||
write_end_element(xmlns, "cfRule");
|
||||
}
|
||||
|
||||
write_end_element(xmlns, "conditionalFormatting");
|
||||
}
|
||||
}
|
||||
|
||||
if (!hyperlink_rels.empty())
|
||||
{
|
||||
write_start_element(xmlns, "hyperlinks");
|
||||
|
|
|
@ -122,7 +122,9 @@ private:
|
|||
|
||||
void write_relationships(const std::vector<xlnt::relationship> &relationships, const path &part);
|
||||
void write_color(const xlnt::color &color);
|
||||
void write_dxfs();
|
||||
void write_border(const xlnt::border &b);
|
||||
void write_fill(const xlnt::fill &f);
|
||||
void write_font(const xlnt::font &f);
|
||||
void write_table_styles();
|
||||
void write_colors(const std::vector<xlnt::color> &colors);
|
||||
|
||||
|
|
117
source/styles/conditional_format.cpp
Normal file
117
source/styles/conditional_format.cpp
Normal file
|
@ -0,0 +1,117 @@
|
|||
// Copyright (c) 2014-2017 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 <xlnt/styles/border.hpp>
|
||||
#include <xlnt/styles/fill.hpp>
|
||||
#include <xlnt/styles/font.hpp>
|
||||
#include <xlnt/styles/conditional_format.hpp>
|
||||
#include <detail/conditional_format_impl.hpp>
|
||||
#include <detail/stylesheet.hpp>
|
||||
|
||||
namespace xlnt {
|
||||
|
||||
condition condition::text_starts_with(const std::string &text)
|
||||
{
|
||||
condition c;
|
||||
c.type_ = type::contains_text;
|
||||
c.operator_ = condition_operator::starts_with;
|
||||
c.text_comparand_ = text;
|
||||
return c;
|
||||
}
|
||||
|
||||
condition condition::text_ends_with(const std::string &text)
|
||||
{
|
||||
condition c;
|
||||
c.type_ = type::contains_text;
|
||||
c.operator_ = condition_operator::ends_with;
|
||||
c.text_comparand_ = text;
|
||||
return c;
|
||||
}
|
||||
|
||||
condition condition::text_contains(const std::string &text)
|
||||
{
|
||||
condition c;
|
||||
c.type_ = type::contains_text;
|
||||
c.operator_ = condition_operator::contains;
|
||||
c.text_comparand_ = text;
|
||||
return c;
|
||||
}
|
||||
|
||||
condition condition::text_does_not_contain(const std::string &text)
|
||||
{
|
||||
condition c;
|
||||
c.type_ = type::contains_text;
|
||||
c.operator_ = condition_operator::does_not_contain;
|
||||
c.text_comparand_ = text;
|
||||
return c;
|
||||
}
|
||||
|
||||
conditional_format::conditional_format(detail::conditional_format_impl *d) : d_(d)
|
||||
{
|
||||
}
|
||||
|
||||
bool conditional_format::operator==(const conditional_format &other) const
|
||||
{
|
||||
return d_ == other.d_;
|
||||
}
|
||||
|
||||
bool conditional_format::operator!=(const conditional_format &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
xlnt::border conditional_format::border() const
|
||||
{
|
||||
return d_->parent->borders.at(d_->border_id.get());
|
||||
}
|
||||
|
||||
conditional_format conditional_format::border(const xlnt::border &new_border)
|
||||
{
|
||||
d_->border_id = d_->parent->find_or_add(d_->parent->borders, new_border);
|
||||
return *this;
|
||||
}
|
||||
|
||||
xlnt::fill conditional_format::fill() const
|
||||
{
|
||||
return d_->parent->fills.at(d_->fill_id.get());
|
||||
}
|
||||
|
||||
conditional_format conditional_format::fill(const xlnt::fill &new_fill)
|
||||
{
|
||||
d_->fill_id = d_->parent->find_or_add(d_->parent->fills, new_fill);
|
||||
return *this;
|
||||
}
|
||||
|
||||
xlnt::font conditional_format::font() const
|
||||
{
|
||||
return d_->parent->fonts.at(d_->font_id.get());
|
||||
}
|
||||
|
||||
conditional_format conditional_format::font(const xlnt::font &new_font)
|
||||
{
|
||||
d_->font_id = d_->parent->find_or_add(d_->parent->fonts, new_font);
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace xlnt
|
|
@ -252,8 +252,10 @@ bool gradient_fill::operator!=(const gradient_fill &other) const
|
|||
|
||||
fill fill::solid(const color &fill_color)
|
||||
{
|
||||
return fill(
|
||||
xlnt::pattern_fill().type(xlnt::pattern_fill_type::solid).foreground(fill_color).background(indexed_color(64)));
|
||||
return fill(xlnt::pattern_fill()
|
||||
.type(xlnt::pattern_fill_type::solid)
|
||||
.foreground(fill_color)
|
||||
.background(indexed_color(64)));
|
||||
}
|
||||
|
||||
fill::fill()
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <xlnt/styles/number_format.hpp>
|
||||
#include <xlnt/styles/protection.hpp>
|
||||
#include <xlnt/styles/style.hpp>
|
||||
#include <detail/style_impl.hpp>
|
||||
#include <detail/stylesheet.hpp>
|
||||
|
||||
namespace {
|
||||
|
|
|
@ -141,6 +141,11 @@ range range::style(const std::string &style_name)
|
|||
return style(ws_.workbook().style(style_name));
|
||||
}
|
||||
|
||||
conditional_format range::conditional_format(const condition &when)
|
||||
{
|
||||
return ws_.conditional_format(ref_, when);
|
||||
}
|
||||
|
||||
void range::apply(std::function<void(class cell)> f)
|
||||
{
|
||||
for (auto row : *this)
|
||||
|
|
|
@ -976,4 +976,9 @@ void worksheet::parent(xlnt::workbook &wb)
|
|||
d_->parent_ = &wb;
|
||||
}
|
||||
|
||||
conditional_format worksheet::conditional_format(const range_reference &ref, const condition &when)
|
||||
{
|
||||
return workbook().d_->stylesheet_.get().add_conditional_format_rule(d_, ref, when);
|
||||
}
|
||||
|
||||
} // namespace xlnt
|
||||
|
|
Loading…
Reference in New Issue
Block a user