mirror of
https://github.com/ONLYOFFICE/core.git
synced 2026-07-11 05:24:10 +08:00
Co-authored-by: Alexander Trofimov <alexander.trofimov@onlyoffice.com> Co-committed-by: Alexander Trofimov <alexander.trofimov@onlyoffice.com>
504 lines
16 KiB
C++
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)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|