Files
core/OdfFile/Reader/Converter/pptx_table_context.cpp
Alexander Trofimov 76ee07f61c [copyright] Update copyright header
Co-authored-by: Alexander Trofimov <alexander.trofimov@onlyoffice.com>
Co-committed-by: Alexander Trofimov <alexander.trofimov@onlyoffice.com>
2026-05-14 08:23:56 +00:00

504 lines
16 KiB
C++

/*
* Copyright (C) Ascensio System SIA, 2009-2026
*
* This program is a free software product. You can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License (AGPL)
* version 3 as published by the Free Software Foundation, together with the
* additional terms provided in the LICENSE file.
*
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
* details, see the GNU AGPL at: https://www.gnu.org/licenses/agpl-3.0.html
*
* You can contact Ascensio System SIA by email at info@onlyoffice.com
* or by postal mail at 20A-6 Ernesta Birznieka-Upisha Street, Riga,
* LV-1050, Latvia, European Union.
*
* The interactive user interfaces in modified versions of the Program
* are required to display Appropriate Legal Notices in accordance with
* Section 5 of the GNU AGPL version 3.
*
* No trademark rights are granted under this License.
*
* All non-code elements of the Product, including illustrations,
* icon sets, and technical writing content, are licensed under the
* Creative Commons Attribution-ShareAlike 4.0 International License:
* https://creativecommons.org/licenses/by-sa/4.0/legalcode
*
* This license applies only to such non-code elements and does not
* modify or replace the licensing terms applicable to the Program's
* source code, which remains licensed under the GNU Affero General
* Public License v3.
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
#include <iostream>
#include <xml/simple_xml_writer.h>
#include "../Format/odf_document.h"
#include "../Format/odfcontext.h"
#include "../Format/draw_common.h"
#include "../Format/calcs_styles.h"
#include "../../DataTypes/borderstyle.h"
#include "../../Common/logging.h"
#include "pptx_conversion_context.h"
namespace cpdoccore {
namespace oox {
/**/
pptx_table_state::pptx_table_state(pptx_conversion_context & Context,
const std::wstring & StyleName) : context_(Context),
table_style_(StyleName),
current_table_column_(-1),
columns_spanned_num_(0),
rows_(0),
current_row_(0),
total_columns_(0)
{
}
void pptx_table_state::start_column(unsigned int repeated, const std::wstring & defaultCellStyleName)
{
columns_.push_back(repeated);
for (unsigned int i = 0; i < repeated; ++i)
columnsDefaultCellStyleName_.push_back(defaultCellStyleName);
}
std::wstring pptx_table_state::get_default_cell_style_col(unsigned int column)
{
return columnsDefaultCellStyleName_.at(column);
}
std::wstring pptx_table_state::get_default_cell_style_row()
{
return default_row_cell_style_name_;
}
void pptx_table_state::set_default_cell_style_row(const std::wstring& style_name)
{
default_row_cell_style_name_ = style_name;
}
void pptx_table_state::set_default_cell_style_col(unsigned int column, const std::wstring style_name)
{
if (column >= columnsDefaultCellStyleName_.size())
return;
columnsDefaultCellStyleName_[column] = style_name;
}
void pptx_table_state::start_row(const std::wstring & StyleName, const std::wstring & defaultCellStyleName)
{
current_table_column_ = -1;
columns_spanned_style_ = L"";
table_row_style_stack_.push_back(StyleName);
default_row_cell_style_name_ = defaultCellStyleName;
current_row_++;
}
void pptx_table_state::end_row()
{
table_row_style_stack_.pop_back();
default_row_cell_style_name_ = L"";
}
std::wstring pptx_table_state::current_row_style() const
{
if (table_row_style_stack_.size())
return table_row_style_stack_.back();
else
return L"";
}
void pptx_table_state::start_cell()
{
current_table_column_++;
if (current_table_column_ >= (int)(rows_spanned_.size()))
rows_spanned_.push_back(table_row_spanned());
}
void pptx_table_state::end_cell()
{
}
bool pptx_table_state::start_covered_cell(pptx_conversion_context & Context)
{
std::wostream & _Wostream = context_.get_table_context().tableData();
current_table_column_++;
// update the vector that stores row merge information
// add a new column to it
if (current_table_column_ >= (int)(rows_spanned_.size()))
rows_spanned_.push_back(table_row_spanned());
_Wostream << L"<a:tc";
if (columns_spanned_num_ == 0 && rows_spanned_[current_table_column_].num() > 0)
{
_Wostream << L" vMerge=\"1\"";
if (rows_spanned_[current_table_column_].column_spanned() > 0)
_Wostream << L" gridSpan=\"" << rows_spanned_[current_table_column_].column_spanned() + 1 << "\"";
_Wostream << L">";
}
else
{
_Wostream << L" hMerge=\"1\">";
}
odf_reader::style_instance* inst =
context_.root()->odf_context().styleContainer().style_by_name(
rows_spanned_[current_table_column_].style(), odf_types::style_family::TableCell, false);
// used current cell, decrement counters of remaining merged cells
// for columns and rows
if (columns_spanned_num_ > 0)
columns_spanned_num_--;
if (rows_spanned_[current_table_column_].num() > 0)
rows_spanned_[current_table_column_].decrease();
return true;
}
void pptx_table_state::end_covered_cell()
{
std::wostream & _Wostream = context_.get_table_context().tableData();
std::vector<const odf_reader::style_instance*> style_instances;
std::wstring style_name;
odf_reader::style_instance* style_inst = context_.root()->odf_context().styleContainer().style_default_by_type(odf_types::style_family::TableCell);
if (style_inst) style_instances.push_back(style_inst);
if (!default_cell_style_name_.empty())//template
{
style_inst = context_.root()->odf_context().styleContainer().style_by_name(default_cell_style_name_, odf_types::style_family::TableCell, false);
if (style_inst) style_instances.push_back(style_inst);
}
if (!default_row_cell_style_name_.empty())
{
style_inst = context_.root()->odf_context().styleContainer().style_by_name(default_row_cell_style_name_, odf_types::style_family::TableCell, false);
if (style_inst) style_instances.push_back(style_inst);
}
oox::oox_serialize_tcPr(_Wostream, style_instances, context_);
// close the opened cell
_Wostream << L"</a:tc>";
}
int pptx_table_state::current_column() const
{
return current_table_column_;
}
void pptx_table_state::set_columns_spanned(unsigned int Val)
{
if ( current_columns_spaned() > 0 )
{
}
columns_spanned_num_ = Val;
}
unsigned int pptx_table_state::current_columns_spaned() const
{
return columns_spanned_num_;
}
void pptx_table_state::set_rows_spanned(unsigned int Column, unsigned int Val, unsigned int ColumnsSpanned, const std::wstring & Style)
{
if (rows_spanned_.size() <= Column)
{
_CP_LOG << L"[warning] set_rows_spanned error\n";
}
else
{
rows_spanned_[Column].num(Val);
rows_spanned_[Column].set_style(Style);
rows_spanned_[Column].column_spanned(ColumnsSpanned);
}
}
unsigned int pptx_table_state::current_rows_spanned(unsigned int Column) const
{
if (rows_spanned_.size() <= Column)
{
_CP_LOG << L"[warning] current_rows_spanned error\n";
return 0;
}
else
{
return rows_spanned_[Column].num();
}
}
void pptx_table_state::set_rows(int rows)
{
rows_ = rows;
}
int pptx_table_state::get_rows() const
{
return rows_;
}
int pptx_table_state::get_current_row() const
{
return current_row_;
}
void pptx_table_state::set_columns(int cols)
{
total_columns_ = cols;
}
int pptx_table_state::get_columns() const
{
return total_columns_;
}
void pptx_table_state::set_template_row_style_name(const std::wstring style_name)
{
template_row_style_name_ = style_name;
}
std::wstring pptx_table_state::get_template_row_style_name() const
{
return template_row_style_name_;
}
struct pptx_border_edge
{
bool present = false;
bool none = false;
std::wstring color;
int width = 0;
std::wstring cmpd;
std::wstring prstDash;
};
void convert_border_style(const odf_types::border_style& borderStyle, pptx_border_edge & border)
{
border.cmpd = L"sng";
border.prstDash = L"solid";
if (borderStyle.initialized())
{
border.present = true;
if (borderStyle.is_none()) border.none = true;
switch(borderStyle.get_style())
{
case odf_types::border_style::none: border.none = true; break;
case odf_types::border_style::double_: border.cmpd = L"dbl"; break;
case odf_types::border_style::dotted: border.prstDash = L"dot"; break;
case odf_types::border_style::dash: border.prstDash = L"dash"; break;
case odf_types::border_style::long_dash: border.prstDash = L"lgDash"; break;
case odf_types::border_style::dot_dash: border.prstDash = L"dashDot"; break;
case odf_types::border_style::dot_dot_dash: border.prstDash = L"lgDashDotDot"; break;
}
}
}
//dbl (Double Lines) Double lines of equal width
//sng (Single Line) Single line: one normal width
//thickThin (Thick Thin Double Lines) Double lines: one thick, one thin
//thinThick (Thin Thick Double Lines) Double lines: one thin, one thick
//tri (Thin Thick Thin Triple Lines) Three lines: thin, thick, thin
void process_border(pptx_border_edge & borderEdge, _CP_OPT(odf_types::border_style) & borderStyle)
{
borderEdge.present = true;
if (borderStyle)
{
borderEdge.color = borderStyle->get_color().get_hex_value();
borderEdge.width = boost::lexical_cast<int>(borderStyle->get_length().get_value_unit(odf_types::length::emu));
convert_border_style(*borderStyle, borderEdge);
}
else
{
borderEdge.none = true;
}
}
void oox_serialize_border(std::wostream & strm, std::wstring Node, pptx_border_edge & content)
{
if (content.present == false) return;
CP_XML_WRITER(strm)
{
CP_XML_NODE(Node)
{
if (content.none)
{
CP_XML_NODE(L"a:noFill") {}
}
else
{
CP_XML_ATTR(L"w", content.width);
//CP_XML_ATTR(L"cap", L"flat");
CP_XML_ATTR(L"cmpd", content.cmpd);
//CP_XML_ATTR(L"algn", L"ctr");
CP_XML_NODE(L"a:solidFill")
{
_CP_OPT(double) opacity;
oox_serialize_srgb(CP_XML_STREAM(), content.color, opacity);
}
CP_XML_NODE(L"a:prstDash")
{
CP_XML_ATTR(L"val", content.prstDash);
}
}
}
}
}
void oox_serialize_tcPr(std::wostream & strm, std::vector<const odf_reader::style_instance *> & instances, oox::pptx_conversion_context & Context)
{
CP_XML_WRITER(strm)
{
CP_XML_NODE(L"a:tcPr")
{
if (instances.size() > 0)
{
odf_reader::style_table_cell_properties_attlist style_cell_attlist = odf_reader::calc_table_cell_properties(instances);
odf_reader::graphic_format_properties_ptr graphic_props = odf_reader::calc_graphic_properties_content(instances);
if (style_cell_attlist.style_vertical_align_ || (graphic_props && graphic_props->draw_textarea_vertical_align_))
{
odf_types::vertical_align::type algn = style_cell_attlist.style_vertical_align_ ? style_cell_attlist.style_vertical_align_->get_type() :
graphic_props->draw_textarea_vertical_align_->get_type();
std::wstring vAlign;
switch(algn)
{
case odf_types::vertical_align::Baseline:
case odf_types::vertical_align::Top: vAlign = L"t"; break;
case odf_types::vertical_align::Middle: vAlign = L"ctr"; break;
case odf_types::vertical_align::Bottom: vAlign = L"b"; break;
case odf_types::vertical_align::Auto:
break;
}
if (false == vAlign.empty())
CP_XML_ATTR(L"anchor", vAlign );
}
double padding_common = -1;
if (style_cell_attlist.common_padding_attlist_.fo_padding_)
{
padding_common = style_cell_attlist.common_padding_attlist_.fo_padding_->get_value_unit(odf_types::length::emu);
}
else if (graphic_props && graphic_props->common_padding_attlist_.fo_padding_)
{
padding_common = graphic_props->common_padding_attlist_.fo_padding_->get_value_unit(odf_types::length::emu);
}
if (style_cell_attlist.common_padding_attlist_.fo_padding_top_)
{
double padding = style_cell_attlist.common_padding_attlist_.fo_padding_top_->get_value_unit(odf_types::length::emu);
CP_XML_ATTR(L"marT", (long)padding);
}
else if (graphic_props && graphic_props->common_padding_attlist_.fo_padding_top_)
{
double padding = graphic_props->common_padding_attlist_.fo_padding_top_->get_value_unit(odf_types::length::emu);
CP_XML_ATTR(L"marT", (long)padding);
}
else if (padding_common > 0)
CP_XML_ATTR(L"marT", (long)padding_common);
if (style_cell_attlist.common_padding_attlist_.fo_padding_bottom_)
{
double padding = style_cell_attlist.common_padding_attlist_.fo_padding_bottom_->get_value_unit(odf_types::length::emu);
CP_XML_ATTR(L"marB", (long)padding);
}
else if (graphic_props && graphic_props->common_padding_attlist_.fo_padding_bottom_)
{
double padding = graphic_props->common_padding_attlist_.fo_padding_bottom_->get_value_unit(odf_types::length::emu);
CP_XML_ATTR(L"marB", (long)padding);
}
else if (padding_common > 0)
CP_XML_ATTR(L"marB", (long)padding_common);
if (style_cell_attlist.common_padding_attlist_.fo_padding_left_)
{
double padding = style_cell_attlist.common_padding_attlist_.fo_padding_left_->get_value_unit(odf_types::length::emu);
CP_XML_ATTR(L"marL", (long)padding);
}
else if (graphic_props && graphic_props->common_padding_attlist_.fo_padding_left_)
{
double padding = graphic_props->common_padding_attlist_.fo_padding_left_->get_value_unit(odf_types::length::emu);
CP_XML_ATTR(L"marL", (long)padding);
}
else if (padding_common > 0)
CP_XML_ATTR(L"marL", (long)padding_common);
if (style_cell_attlist.common_padding_attlist_.fo_padding_right_)
{
double padding = style_cell_attlist.common_padding_attlist_.fo_padding_right_->get_value_unit(odf_types::length::emu);
CP_XML_ATTR(L"marR", (long)padding);
}
else if (graphic_props && graphic_props->common_padding_attlist_.fo_padding_right_)
{
double padding = graphic_props->common_padding_attlist_.fo_padding_right_->get_value_unit(odf_types::length::emu);
CP_XML_ATTR(L"marR", (long)padding);
}
else if (padding_common > 0)
CP_XML_ATTR(L"marR", (long)padding_common);
//vert //
//style_cell_attlist.pptx_serialize(Context, CP_XML_STREAM()); //nodes
odf_reader::paragraph_format_properties style_paragraph = odf_reader::calc_paragraph_properties_content(instances);//instances);
pptx_border_edge left, top, bottom, right;
process_border(left, style_paragraph.fo_border_left_ ? style_paragraph.fo_border_left_ : style_paragraph.fo_border_);
process_border(top, style_paragraph.fo_border_top_ ? style_paragraph.fo_border_top_ : style_paragraph.fo_border_);
process_border(right, style_paragraph.fo_border_right_ ? style_paragraph.fo_border_right_ : style_paragraph.fo_border_);
process_border(bottom, style_paragraph.fo_border_bottom_ ? style_paragraph.fo_border_bottom_ : style_paragraph.fo_border_);
oox_serialize_border(CP_XML_STREAM(), L"a:lnL", left);
oox_serialize_border(CP_XML_STREAM(), L"a:lnR", right);
oox_serialize_border(CP_XML_STREAM(), L"a:lnT", top);
oox_serialize_border(CP_XML_STREAM(), L"a:lnB", bottom);
//no diagonals in OO.
////////////////////////////////////////////////////////////////////////////////////////////////
oox::_oox_fill fill;
if (graphic_props)
{
odf_reader::Compute_GraphicFill(graphic_props->common_draw_fill_attlist_,
graphic_props->style_background_image_, Context.root(), fill);
}
if (fill.bitmap)
{
bool isMediaInternal = true;
std::wstring ref;
fill.bitmap->rId = Context.get_slide_context().get_mediaitems()->add_or_find(fill.bitmap->xlink_href_, oox::typeImage, isMediaInternal, ref, oox::document_place);
Context.get_slide_context().add_rels(isMediaInternal, fill.bitmap->rId, ref, oox::typeImage);
}
oox::oox_serialize_fill(CP_XML_STREAM(), fill);
/////////////////////////////////////////////////////////////////////////////////
//headers (Header Cells Associated With Table Cell) §21.1.3.4
//lnBlToTr (Bottom-Left to Top-Right Border Line Properties) §21.1.3.6
//lnTlToBr (Top-Left to Bottom-Right Border Line Properties)
}
}
}
}
}
}