From e3bb0be98ee3b90ed190ce8e64e82ea8cf3fb385 Mon Sep 17 00:00:00 2001 From: Thomas Fussell Date: Wed, 14 Oct 2015 18:05:13 -0400 Subject: [PATCH] work on writer --- include/xlnt/common/datetime.hpp | 19 +- include/xlnt/common/exceptions.hpp | 4 +- include/xlnt/common/zip_file.hpp | 12 +- include/xlnt/reader/workbook_reader.hpp | 9 +- include/xlnt/styles/alignment.hpp | 10 +- include/xlnt/styles/borders.hpp | 5 +- include/xlnt/styles/color.hpp | 14 +- include/xlnt/styles/font.hpp | 10 +- include/xlnt/styles/style.hpp | 2 +- include/xlnt/workbook/named_range.hpp | 20 +- include/xlnt/workbook/workbook.hpp | 26 +- include/xlnt/worksheet/range_reference.hpp | 2 +- include/xlnt/writer/manifest_writer.hpp | 2 +- include/xlnt/writer/workbook_writer.hpp | 7 + include/xlnt/writer/worksheet_writer.hpp | 13 + include/xlnt/writer/writer.hpp | 56 -- source/borders.cpp | 10 +- source/cell.cpp | 17 +- source/cell_reference.cpp | 30 +- source/comment.cpp | 14 +- source/datetime.cpp | 4 +- source/detail/cell_impl.cpp | 50 +- source/detail/cell_impl.hpp | 7 +- source/detail/comment_impl.cpp | 3 +- source/detail/workbook_impl.hpp | 30 +- source/detail/worksheet_impl.hpp | 2 +- source/document_properties.cpp | 2 +- source/exceptions.cpp | 2 +- source/manifest_writer.cpp | 36 +- source/named_range.cpp | 24 +- source/range.cpp | 16 +- source/range_reference.cpp | 2 +- source/reader.cpp | 317 ++++---- source/relationship_writer.cpp | 2 +- source/workbook.cpp | 176 ++--- source/workbook_reader.cpp | 13 + source/workbook_writer.cpp | 483 ++++++++++++- source/worksheet.cpp | 29 +- source/worksheet_writer.cpp | 353 +++++++++ source/writer.cpp | 798 --------------------- source/zip_file.cpp | 127 ++-- tests/helpers/helper.hpp | 8 +- tests/test_cell.hpp | 16 +- tests/test_props.hpp | 18 +- tests/test_theme.hpp | 5 +- tests/test_workbook.hpp | 8 +- tests/test_worksheet.hpp | 19 +- tests/test_write.hpp | 30 +- tests/test_write_workbook.hpp | 30 +- tests/test_zip_file.hpp | 2 +- third-party/miniz/miniz.h | 2 +- 51 files changed, 1495 insertions(+), 1401 deletions(-) create mode 100644 source/workbook_reader.cpp create mode 100644 source/worksheet_writer.cpp delete mode 100644 source/writer.cpp diff --git a/include/xlnt/common/datetime.hpp b/include/xlnt/common/datetime.hpp index 7d2d33b4..c0296f8f 100644 --- a/include/xlnt/common/datetime.hpp +++ b/include/xlnt/common/datetime.hpp @@ -54,8 +54,8 @@ struct date /// static date from_number(int days_since_base_year, calendar base_date); - date(int year, int month, int day) - : year(year), month(month), day(day) + date(int year_, int month_, int day_) + : year(year_), month(month_), day(day_) { } @@ -93,8 +93,8 @@ struct time /// static time from_number(long double number); - explicit time(int hour = 0, int minute = 0, int second = 0, int microsecond = 0) - : hour(hour), minute(minute), second(second), microsecond(microsecond) + explicit time(int hour_ = 0, int minute_ = 0, int second_ = 0, int microsecond_ = 0) + : hour(hour_), minute(minute_), second(second_), microsecond(microsecond_) { } explicit time(const std::string &time_string); @@ -128,8 +128,8 @@ struct datetime /// static datetime from_number(long double number, calendar base_date); - datetime(int year, int month, int day, int hour = 0, int minute = 0, int second = 0, int microsecond = 0) - : year(year), month(month), day(day), hour(hour), minute(minute), second(second), microsecond(microsecond) + datetime(int year_, int month_, int day_, int hour_ = 0, int minute_ = 0, int second_ = 0, int microsecond_ = 0) + : year(year_), month(month_), day(day_), hour(hour_), minute(minute_), second(second_), microsecond(microsecond_) { } @@ -152,7 +152,12 @@ struct datetime /// struct timedelta { - timedelta(int days, int hours, int minutes = 0, int seconds = 0, int microseconds = 0) : days(days), hours(hours), minutes(minutes), seconds(seconds), microseconds(microseconds) + timedelta(int days_, int hours_, int minutes_ = 0, int seconds_ = 0, int microseconds_ = 0) + : days(days_), + hours(hours_), + minutes(minutes_), + seconds(seconds_), + microseconds(microseconds_) { } diff --git a/include/xlnt/common/exceptions.hpp b/include/xlnt/common/exceptions.hpp index f8bb984b..4408c4e0 100644 --- a/include/xlnt/common/exceptions.hpp +++ b/include/xlnt/common/exceptions.hpp @@ -25,6 +25,8 @@ #include #include +#include "../common/types.hpp" + namespace xlnt { /// @@ -33,7 +35,7 @@ namespace xlnt { class cell_coordinates_exception : public std::runtime_error { public: - cell_coordinates_exception(int row, int column); + cell_coordinates_exception(row_t row, column_t column); cell_coordinates_exception(const std::string &coord_string); }; diff --git a/include/xlnt/common/zip_file.hpp b/include/xlnt/common/zip_file.hpp index b03c7679..e79dbe61 100644 --- a/include/xlnt/common/zip_file.hpp +++ b/include/xlnt/common/zip_file.hpp @@ -15,8 +15,7 @@ namespace xlnt { struct zip_info { - std::string filename; - struct + struct date_time_t { int year; int month; @@ -24,7 +23,12 @@ struct zip_info int hours; int minutes; int seconds; - } date_time; + }; + + zip_info(); + + date_time_t date_time; + std::string filename; std::string comment; std::string extra; uint16_t create_system; @@ -117,4 +121,4 @@ private: std::string filename_; }; -} // namespace xlnt \ No newline at end of file +} // namespace xlnt diff --git a/include/xlnt/reader/workbook_reader.hpp b/include/xlnt/reader/workbook_reader.hpp index 14127cc7..123a4c5d 100644 --- a/include/xlnt/reader/workbook_reader.hpp +++ b/include/xlnt/reader/workbook_reader.hpp @@ -23,11 +23,12 @@ // @author: see AUTHORS file #pragma once +#include + namespace xlnt { + +class workbook; -class workbook_reader -{ - -}; +xlnt::workbook load_workbook(const std::vector &bytes); } // namespace xlnt diff --git a/include/xlnt/styles/alignment.hpp b/include/xlnt/styles/alignment.hpp index 621c7f5a..161d0063 100644 --- a/include/xlnt/styles/alignment.hpp +++ b/include/xlnt/styles/alignment.hpp @@ -28,8 +28,9 @@ namespace xlnt { /// /// Alignment options for use in styles. /// -struct alignment +class alignment { +public: enum class horizontal_alignment { general, @@ -55,7 +56,12 @@ struct alignment bool operator==(const alignment &other) const { - return horizontal_ == other.horizontal_; + return horizontal_ == other.horizontal_ + && vertical_ == other.vertical_ + && text_rotation_ == other.text_rotation_ + && wrap_text_ == other.wrap_text_ + && shrink_to_fit_ == other.shrink_to_fit_ + && indent_ == other.indent_; } private: diff --git a/include/xlnt/styles/borders.hpp b/include/xlnt/styles/borders.hpp index 5bd42922..6bddd873 100644 --- a/include/xlnt/styles/borders.hpp +++ b/include/xlnt/styles/borders.hpp @@ -98,7 +98,10 @@ public: bool operator==(const border &other) const { - return start.value == other.start.value; + return start.value == other.start.value + && outline == other.outline + && diagonal_up == other.diagonal_up + && diagonal_down == other.diagonal_down; } }; diff --git a/include/xlnt/styles/color.hpp b/include/xlnt/styles/color.hpp index 230e8bc8..14580aca 100644 --- a/include/xlnt/styles/color.hpp +++ b/include/xlnt/styles/color.hpp @@ -25,8 +25,9 @@ namespace xlnt { -struct color +class color { +public: static const color black; static const color white; static const color red; @@ -38,12 +39,17 @@ struct color static const color yellow; static const color darkyellow; - color(int index) + color(int index) : index_(index) { - this->index = index; } - int index; + bool operator==(const color &other) const + { + return index_ == other.index_; + } + +private: + int index_; }; } // namespace xlnt diff --git a/include/xlnt/styles/font.hpp b/include/xlnt/styles/font.hpp index 80d78440..b0a49329 100644 --- a/include/xlnt/styles/font.hpp +++ b/include/xlnt/styles/font.hpp @@ -45,7 +45,15 @@ public: bool operator==(const font &other) const { - return name_ == other.name_; + return name_ == other.name_ + && size_ == other.size_ + && bold_ == other.bold_ + && italic_ == other.italic_ + && superscript_ == other.superscript_ + && subscript_ == other.subscript_ + && underline_ == other.underline_ + && strikethrough_ == other.strikethrough_ + && color_ == other.color_; } private: diff --git a/include/xlnt/styles/style.hpp b/include/xlnt/styles/style.hpp index c4691036..4d079898 100644 --- a/include/xlnt/styles/style.hpp +++ b/include/xlnt/styles/style.hpp @@ -36,7 +36,7 @@ namespace xlnt { class style { public: - style(bool static_ = false) : static_(static_) {} + style(bool is_static = false) : static_(is_static) {} style(const style &rhs); style copy() const; diff --git a/include/xlnt/workbook/named_range.hpp b/include/xlnt/workbook/named_range.hpp index 0b19bd64..c7e3c546 100644 --- a/include/xlnt/workbook/named_range.hpp +++ b/include/xlnt/workbook/named_range.hpp @@ -4,7 +4,8 @@ #include namespace xlnt { - + +class range_reference; class worksheet; std::vector> split_named_range(const std::string &named_range_string); @@ -12,7 +13,20 @@ std::vector> split_named_range(const std::st class named_range { public: - named_range(const std::string &name, const std::vector> &targets); + using target = std::pair; + + named_range(); + named_range(const named_range &other); + named_range(const std::string &name, const std::vector &targets); + + std::string get_name() const { return name_; } + const std::vector &get_targets() const { return targets_; } + + named_range &operator=(const named_range &other); + +private: + std::string name_; + std::vector targets_; }; -} \ No newline at end of file +} diff --git a/include/xlnt/workbook/workbook.hpp b/include/xlnt/workbook/workbook.hpp index 12a50a5c..08b278a0 100644 --- a/include/xlnt/workbook/workbook.hpp +++ b/include/xlnt/workbook/workbook.hpp @@ -33,20 +33,21 @@ namespace xlnt { +class alignment; +class border; +class color; class document_properties; class drawing; +class fill; +class font; +class named_range; +class pattern_fill; +class protection; class range; class range_reference; class relationship; class worksheet; -class alignment; -class border; -class fill; -class pattern_fill; -class font; -class protection; -class color; -class named_range; +class zip_file; enum class encoding; @@ -68,7 +69,6 @@ struct content_type class workbook { public: - class iterator { public: @@ -102,6 +102,8 @@ public: const workbook &wb_; std::size_t index_; }; + + static std::size_t index_from_ws_filename(const std::string &filename); //constructors workbook(); @@ -162,6 +164,7 @@ public: const document_properties &get_properties() const; //named ranges + std::vector get_named_ranges() const; void create_named_range(const std::string &name, worksheet worksheet, const range_reference &reference); bool has_named_range(const std::string &name) const; range get_named_range(const std::string &name); @@ -173,6 +176,7 @@ public: bool load(const std::vector &data); bool load(const std::string &filename); bool load(const std::istream &stream); + bool load(zip_file &archive); bool operator==(const workbook &rhs) const; @@ -204,14 +208,10 @@ public: void set_code_name(const std::string &code_name); - void add_named_range(const named_range &n); - bool has_loaded_theme(); std::string get_loaded_theme(); private: - static std::size_t index_from_ws_filename(const std::string &ws_filename); - friend class worksheet; std::shared_ptr d_; }; diff --git a/include/xlnt/worksheet/range_reference.hpp b/include/xlnt/worksheet/range_reference.hpp index b23302b6..816541e7 100644 --- a/include/xlnt/worksheet/range_reference.hpp +++ b/include/xlnt/worksheet/range_reference.hpp @@ -49,7 +49,7 @@ public: cell_reference &get_top_left() { return top_left_; } cell_reference &get_bottom_right() { return bottom_right_; } - range_reference make_offset(column_t column_offset, row_t row_offset) const; + range_reference make_offset(int column_offset, int row_offset) const; std::string to_string() const; diff --git a/include/xlnt/writer/manifest_writer.hpp b/include/xlnt/writer/manifest_writer.hpp index 183fa98a..6c7721b4 100644 --- a/include/xlnt/writer/manifest_writer.hpp +++ b/include/xlnt/writer/manifest_writer.hpp @@ -6,6 +6,6 @@ namespace xlnt { class workbook; -std::string write_content_types(const workbook &wb, bool as_template); +std::string write_content_types(const workbook &wb, bool as_template = false); }; diff --git a/include/xlnt/writer/workbook_writer.hpp b/include/xlnt/writer/workbook_writer.hpp index e73444dc..00b7cc23 100644 --- a/include/xlnt/writer/workbook_writer.hpp +++ b/include/xlnt/writer/workbook_writer.hpp @@ -50,12 +50,19 @@ public: private: workbook wb_; style_writer style_writer_; + std::vector shared_strings_; }; +std::string write_shared_strings(const std::vector &string_table); +std::string write_properties_core(const document_properties &prop); +std::string write_worksheet_rels(worksheet ws); +std::string write_theme(); std::string write_properties_app(const workbook &wb); std::string write_root_rels(const workbook &wb); std::string write_workbook(const workbook &wb); std::string write_workbook_rels(const workbook &wb); +std::string write_defined_names(const xlnt::workbook &wb); + bool save_workbook(workbook &wb, const std::string &filename, bool as_template = false); std::vector save_virtual_workbook(xlnt::workbook &wb, bool as_template = false); diff --git a/include/xlnt/writer/worksheet_writer.hpp b/include/xlnt/writer/worksheet_writer.hpp index 3f59c932..7f1717f3 100644 --- a/include/xlnt/writer/worksheet_writer.hpp +++ b/include/xlnt/writer/worksheet_writer.hpp @@ -1,2 +1,15 @@ #pragma once +#include +#include +#include + +namespace xlnt { + +class worksheet; + +std::string write_worksheet(worksheet ws, + const std::vector &string_table = {}, + const std::unordered_map &style_id_by_hash = {}); + +} // namespace xlnt diff --git a/include/xlnt/writer/writer.hpp b/include/xlnt/writer/writer.hpp index 0858a996..e69de29b 100644 --- a/include/xlnt/writer/writer.hpp +++ b/include/xlnt/writer/writer.hpp @@ -1,56 +0,0 @@ -// Copyright (c) 2015 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 -#include -#include -#include - -namespace xlnt { - -class relationship; -class workbook; -class worksheet; -class document_properties; - -class writer -{ -public: - static std::string write_content_types(const workbook &wb); - - static std::string write_properties_core(const document_properties &prop); - - static std::string write_theme(); - - static std::string write_shared_strings(const std::vector &string_table); - - static std::string write_worksheet(worksheet ws, - const std::vector &string_table = {}, - const std::unordered_map &style_table = {}); - - static std::string write_worksheet_rels(worksheet ws); -}; - -} // namespace xlnt diff --git a/source/borders.cpp b/source/borders.cpp index de3d0f40..83a06cff 100644 --- a/source/borders.cpp +++ b/source/borders.cpp @@ -3,9 +3,9 @@ namespace xlnt { - side::side(border_style style, color c) : style_(style), color_(c) - { - - } +side::side(border_style style, color c) : style_(style), color_(c) +{ -} // namespace xlnt \ No newline at end of file +} + +} // namespace xlnt diff --git a/source/cell.cpp b/source/cell.cpp index 6ee2a683..a67f87e4 100644 --- a/source/cell.cpp +++ b/source/cell.cpp @@ -194,6 +194,15 @@ void cell::set_value(char const *c) template<> void cell::set_value(cell c) { + d_->type_ = c.d_->type_; + d_->value_numeric_ = c.d_->value_numeric_; + d_->value_string_ = c.d_->value_string_; + d_->is_date_ = c.d_->is_date_; + d_->hyperlink_ = c.d_->hyperlink_; + d_->has_hyperlink_ = c.d_->has_hyperlink_; + d_->formula_ = c.d_->formula_; + set_style(c.get_style()); + set_comment(c.get_comment()); } template<> @@ -439,7 +448,7 @@ std::pair cell::get_anchor() const int left_anchor = 0; auto default_width = points_to_pixels(DefaultColumnWidth, 96.0); - for(int column_index = 1; column_index <= (int)left_columns; column_index++) + for(column_t column_index = 1; column_index <= left_columns; column_index++) { if(column_dimensions.find(column_index) != column_dimensions.end()) { @@ -462,9 +471,9 @@ std::pair cell::get_anchor() const for(int row_index = 1; row_index <= (int)top_rows; row_index++) { - if(row_dimensions.find(row_index) != row_dimensions.end()) + if(row_dimensions.find(static_cast(row_index)) != row_dimensions.end()) { - auto rdh = row_dimensions.at(row_index); + auto rdh = row_dimensions.at(static_cast(row_index)); if(rdh > 0) { @@ -639,7 +648,7 @@ datetime cell::get_value() const template<> date cell::get_value() const { - return date::from_number(d_->value_numeric_, xlnt::calendar::windows_1900); + return date::from_number(static_cast(d_->value_numeric_), xlnt::calendar::windows_1900); } template<> diff --git a/source/cell_reference.cpp b/source/cell_reference.cpp index db129e56..78e2b6ef 100644 --- a/source/cell_reference.cpp +++ b/source/cell_reference.cpp @@ -37,22 +37,22 @@ cell_reference::cell_reference(const char *reference_string) : cell_reference(st } cell_reference::cell_reference(const std::string &column, row_t row, bool absolute) -: column_(column_index_from_string(column)), -row_(row), -absolute_(absolute) + : column_(column_index_from_string(column)), + row_(row), + absolute_(absolute) { - if(row == 0 || row_ >= constants::MaxRow || column_ >= constants::MaxColumn) + if(row == 0 || row_ >= constants::MaxRow || column_ == 0 || column_ >= constants::MaxColumn) { throw cell_coordinates_exception(column_, row_); } } -cell_reference::cell_reference(column_t column_index, row_t row_index, bool absolute) -: column_(column_index), -row_(row_index), -absolute_(absolute) +cell_reference::cell_reference(column_t column_index, row_t row, bool absolute) + : column_(column_index), + row_(row), + absolute_(absolute) { - if(row_ >= constants::MaxRow || column_ >= constants::MaxColumn) + if(row_ == 0 || row_ >= constants::MaxRow || column_ == 0 || column_ >= constants::MaxColumn) { throw cell_coordinates_exception(column_, row_); } @@ -69,6 +69,7 @@ std::string cell_reference::to_string() const { return std::string("$") + column_string_from_index(column_) + "$" + std::to_string(row_); } + return column_string_from_index(column_) + std::to_string(row_); } @@ -153,7 +154,8 @@ std::pair cell_reference::split_reference(const std::string cell_reference cell_reference::make_offset(int column_offset, int row_offset) const { - return cell_reference(column_ + column_offset, row_ + row_offset); + return cell_reference(static_cast(static_cast(column_) + column_offset), + static_cast(static_cast(row_) + row_offset)); } bool cell_reference::operator==(const cell_reference &comparand) const @@ -175,12 +177,12 @@ column_t cell_reference::column_index_from_string(const std::string &column_stri for(int i = static_cast(column_string.length()) - 1; i >= 0; i--) { - if(!std::isalpha(column_string[i], std::locale::classic())) + if(!std::isalpha(column_string[static_cast(i)], std::locale::classic())) { - throw column_string_index_exception(); + throw column_string_index_exception(); } - column_index += (std::toupper(column_string[i], std::locale::classic()) - 'A' + 1) * place; + column_index += static_cast((std::toupper(column_string[static_cast(i)], std::locale::classic()) - 'A' + 1) * place); place *= 26; } @@ -201,7 +203,7 @@ std::string cell_reference::column_string_from_index(column_t column_index) throw column_string_index_exception(); } - auto temp = column_index; + int temp = static_cast(column_index); std::string column_letter = ""; while(temp > 0) diff --git a/source/comment.cpp b/source/comment.cpp index d7e253e8..2f19c968 100644 --- a/source/comment.cpp +++ b/source/comment.cpp @@ -1,5 +1,5 @@ -#include #include +#include #include "detail/comment_impl.hpp" @@ -12,6 +12,8 @@ comment::comment(detail::comment_impl *d) : d_(d) comment::comment(cell parent, const std::string &text, const std::string &author) : d_(nullptr) { d_ = parent.get_comment().d_; + d_->text_ = text; + d_->author_ = author; } comment::comment() : d_(nullptr) @@ -31,10 +33,10 @@ std::string comment::get_text() const { return d_->text_; } - - bool comment::operator==(const xlnt::comment &other) const - { - return d_ == other.d_; - } + +bool comment::operator==(const xlnt::comment &other) const +{ + return d_ == other.d_; +} } // namespace xlnt diff --git a/source/datetime.cpp b/source/datetime.cpp index 74aa2e80..6393e63f 100644 --- a/source/datetime.cpp +++ b/source/datetime.cpp @@ -123,7 +123,7 @@ time::time(const std::string &time_string) : hour(0), minute(0), second(0), micr long double time::to_number() const { - std::size_t microseconds = microsecond; + std::size_t microseconds = static_cast(microsecond); microseconds += second * 1e6; microseconds += minute * 1e6 * 60; microseconds += hour * 1e6 * 60 * 60; @@ -163,7 +163,7 @@ long double datetime::to_number(calendar base_date) const + time(hour, minute, second, microsecond).to_number(); } -std::string datetime::to_string(xlnt::calendar base_date) const +std::string datetime::to_string(xlnt::calendar /*base_date*/) const { return std::to_string(year) + "/" + std::to_string(month) + "/" + std::to_string(day) + " " +std::to_string(hour) + ":" + std::to_string(minute) + ":" + std::to_string(second) + ":" + std::to_string(microsecond); } diff --git a/source/detail/cell_impl.cpp b/source/detail/cell_impl.cpp index 107f6678..6b3194e0 100644 --- a/source/detail/cell_impl.cpp +++ b/source/detail/cell_impl.cpp @@ -6,22 +6,50 @@ namespace xlnt { namespace detail { -cell_impl::cell_impl() : parent_(nullptr), type_(cell::type::null), - column_(1), row_(1), style_(nullptr), is_merged_(false), - is_date_(false), has_hyperlink_(false), xf_index_(0), comment_(nullptr) + + cell::type type_; + + worksheet_impl *parent_; + + column_t column_; + row_t row_; + + std::string value_string_; + long double value_numeric_; + + std::string formula_; + + bool has_hyperlink_; + relationship hyperlink_; + + bool is_merged_; + bool is_date_; + + std::size_t xf_index_; + + std::unique_ptr