blob: 5ce603f09b5196c7e567aee34ff55e9780fefded [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#include "DFPlatform.h"
#include "WordTblPr.h"
#include "WordStyles.h"
#include "WordSection.h"
#include "CSS.h"
#include "DFHTML.h"
#include "DFString.h"
#include "DFCommon.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
static Tag WordTblPr_children[] = {
WORD_TBLSTYLE,
WORD_TBLPPR,
WORD_TBLOVERLAP,
WORD_BIDIVISUAL,
WORD_TBLSTYLEROWBANDSIZE,
WORD_TBLSTYLECOLBANDSIZE,
WORD_TBLW,
WORD_JC,
WORD_TBLCELLSPACING,
WORD_TBLIND,
WORD_TBLBORDERS,
WORD_SHD,
WORD_TBLLAYOUT,
WORD_TBLCELLMAR,
WORD_TBLLOOK,
WORD_TBLCAPTION,
WORD_TBLDESCRIPTION,
WORD_TBLPRCHANGE,
0,
};
static Tag WordTblBorders_children[] = {
WORD_TOP,
WORD_START,
WORD_LEFT,
WORD_BOTTOM,
WORD_END,
WORD_RIGHT,
WORD_INSIDEH,
WORD_INSIDEV,
0,
};
static Tag WordTblCellMar_children[] = {
WORD_TOP,
WORD_START,
WORD_LEFT,
WORD_BOTTOM,
WORD_END,
WORD_RIGHT,
0,
};
static Tag Word_TcPr_children[] = {
WORD_CNFSTYLE,
WORD_TCW,
WORD_GRIDSPAN,
WORD_HMERGE,
WORD_VMERGE,
WORD_TCBORDERS,
WORD_SHD,
WORD_NOWRAP,
WORD_TCMAR,
WORD_TEXTDIRECTION,
WORD_TCFITTEXT,
WORD_VALIGN,
WORD_HIDEMARK,
WORD_HEADERS,
WORD_CELLINS,
WORD_CELLDEL,
WORD_CELLMERGE,
WORD_TCPRCHANGE,
0,
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// Table properties (tblPr and related elements) //
// //
////////////////////////////////////////////////////////////////////////////////////////////////////
static char *cssPctWidth(const char *type, const char *width, WordSection *section)
{
if ((type == NULL) || (width == NULL))
return NULL;
if (!strcmp(type,"pct")) {
// Units: 1/50ths of a percent
double pct = atoi(width)/50.0;
char buf[100];
return xstrdup(DFFormatDoublePct(buf,100,pct));
}
else if (!strcmp(type,"dxa") && (WordSectionContentWidth(section) > 0)) {
// Units: 1/20ths of a point
double pct = 100.0*atoi(width)/(double)WordSectionContentWidth(section);
char buf[100];
return xstrdup(DFFormatDoublePct(buf,100,pct));
}
return NULL;
}
static void WordGetTblBorders(DFNode *concrete, CSSProperties *table, CSSProperties *cell)
{
int haveInsideH = 0;
int haveInsideV = 0;
for (DFNode *child = concrete->first; child != NULL; child = child->next) {
switch (child->tag) {
case WORD_TOP:
WordGetBorder(child,"top",table);
break;
case WORD_BOTTOM:
WordGetBorder(child,"bottom",table);
break;
case WORD_LEFT:
WordGetBorder(child,"left",table);
break;
case WORD_RIGHT:
WordGetBorder(child,"right",table);
break;
case WORD_INSIDEH:
WordGetBorder(child,"top",cell);
WordGetBorder(child,"bottom",cell);
haveInsideH = 1;
break;
case WORD_INSIDEV:
WordGetBorder(child,"left",cell);
WordGetBorder(child,"right",cell);
haveInsideV = 1;
break;
}
}
if (haveInsideH) {
if (CSSGet(table,"border-left-style") == NULL) {
CSSPut(table,"border-left-style","hidden");
CSSPut(table,"border-left-width",NULL);
CSSPut(table,"border-left-color",NULL);
}
if (CSSGet(table,"border-right-style") == NULL) {
CSSPut(table,"border-right-style","hidden");
CSSPut(table,"border-right-width",NULL);
CSSPut(table,"border-right-color",NULL);
}
}
if (haveInsideV) {
if (CSSGet(table,"border-top-style") == NULL) {
CSSPut(table,"border-top-style","hidden");
CSSPut(table,"border-top-width",NULL);
CSSPut(table,"border-top-color",NULL);
}
if (CSSGet(table,"border-bottom-style") == NULL) {
CSSPut(table,"border-bottom-style","hidden");
CSSPut(table,"border-bottom-width",NULL);
CSSPut(table,"border-bottom-color",NULL);
}
}
}
static void WordPutTblBorders(DFNode *concrete,
CSSProperties *oldTable,
CSSProperties *oldCell,
CSSProperties *newTable,
CSSProperties *newCell)
{
DFNode *children[PREDEFINED_TAG_COUNT];
childrenToArray(concrete,children);
DFDocument *doc = concrete->doc;
WordPutBorder(doc,oldTable,newTable,&children[WORD_TOP],WORD_TOP,"top");
WordPutBorder(doc,oldTable,newTable,&children[WORD_BOTTOM],WORD_BOTTOM,"bottom");
WordPutBorder(doc,oldTable,newTable,&children[WORD_LEFT],WORD_LEFT,"left");
WordPutBorder(doc,oldTable,newTable,&children[WORD_RIGHT],WORD_RIGHT,"right");
// Avoid updating inside borders for HTML tables with direct formatting
if (newCell != NULL) {
WordPutBorder(doc,oldCell,newCell,&children[WORD_INSIDEH],WORD_INSIDEH,"bottom");
WordPutBorder(doc,oldCell,newCell,&children[WORD_INSIDEV],WORD_INSIDEV,"right");
}
replaceChildrenFromArray(concrete,children,WordTblBorders_children);
}
static void WordGetTblCellMarSide(DFNode *concrete, CSSProperties *properties, const char *side)
{
const char *type = DFGetAttribute(concrete,WORD_TYPE);
const char *w = DFGetAttribute(concrete,WORD_W);
if ((type != NULL) && (w != NULL) && !strcmp(type,"dxa")) {
double pts = atoi(w)/20.0;
char *name = DFFormatString("padding-%s",side);
char buf[100];
char *value = xstrdup(DFFormatDoublePt(buf,100,pts));
CSSPut(properties,name,value);
free(name);
free(value);
}
}
static void WordPutTblCellMarSide(DFDocument *doc, CSSProperties *oldp, CSSProperties *newp,
DFNode **childp, Tag tag, const char *side)
{
// Avoid updating cell margins for HTML tables with direct formatting
if (newp == NULL)
return;
char *name = DFFormatString("padding-%s",side);
const char *oldValue = CSSGet(oldp,name);
const char *newValue = CSSGet(newp,name);
if (!DFStringEquals(oldValue,newValue)) {
*childp = NULL;
if (newValue != NULL) {
CSSLength length = CSSLengthFromString(newValue);
if (CSSLengthIsValid(length) && (length.units == UnitsPt)) {
*childp = DFCreateElement(doc,tag);
int twips = (int)round(length.value*20.0);
DFFormatAttribute(*childp,WORD_W,"%d",twips);
DFSetAttribute(*childp,WORD_TYPE,"dxa");
}
}
}
free(name);
}
static void WordPutTblCellMar(DFNode *concrete, CSSProperties *oldp, CSSProperties *newp)
{
DFNode *children[PREDEFINED_TAG_COUNT];
childrenToArray(concrete,children);
DFDocument *doc = concrete->doc;
WordPutTblCellMarSide(doc,oldp,newp,&children[WORD_LEFT],WORD_LEFT,"left");
WordPutTblCellMarSide(doc,oldp,newp,&children[WORD_RIGHT],WORD_RIGHT,"right");
WordPutTblCellMarSide(doc,oldp,newp,&children[WORD_TOP],WORD_TOP,"top");
WordPutTblCellMarSide(doc,oldp,newp,&children[WORD_BOTTOM],WORD_BOTTOM,"bottom");
replaceChildrenFromArray(concrete,children,WordTblCellMar_children);
}
static void WordGetTblCellMar(DFNode *concrete, CSSProperties *properties)
{
for (DFNode *child = concrete->first; child != NULL; child = child->next) {
switch (child->tag) {
case WORD_LEFT:
WordGetTblCellMarSide(child,properties,"left");
break;
case WORD_RIGHT:
WordGetTblCellMarSide(child,properties,"right");
break;
case WORD_TOP:
WordGetTblCellMarSide(child,properties,"top");
break;
case WORD_BOTTOM:
WordGetTblCellMarSide(child,properties,"bottom");
break;
}
}
}
void WordGetTblPr(DFNode *concrete, CSSProperties *table, CSSProperties *cell, WordSection *section, const char **styleId)
{
if (styleId != NULL)
*styleId = NULL;
for (DFNode *child = concrete->first; child != NULL; child = child->next) {
switch (child->tag) {
case WORD_TBLW: {
const char *type = DFGetAttribute(child,WORD_TYPE);
const char *width = DFGetAttribute(child,WORD_W);
if (type == NULL)
type = "dxa";
if (width == NULL)
break;
char *widthValue = cssPctWidth(type,width,section);
CSSPut(table,"width",widthValue);
free(widthValue);
break;
}
case WORD_TBLSTYLE:
if (styleId != NULL)
*styleId = DFGetAttribute(child,WORD_VAL);
break;
case WORD_TBLBORDERS:
if (cell != NULL)
WordGetTblBorders(child,table,cell);
break;
case WORD_TBLCELLMAR:
WordGetTblCellMar(child,cell);
break;
case WORD_JC: {
const char *value = DFGetAttribute(child,WORD_VAL);
if ((value != NULL) && !strcmp(value,"center")) {
CSSPut(table,"margin-left","auto");
CSSPut(table,"margin-right","auto");
}
else if ((value != NULL) && (!strcmp(value,"right") || !strcmp(value,"end"))) {
CSSPut(table,"margin-left","auto");
CSSPut(table,"margin-right","0");
}
else if ((value != NULL) && (!strcmp(value,"left") || !strcmp(value,"start"))) {
CSSPut(table,"margin-left","0");
CSSPut(table,"margin-right","auto");
}
break;
}
}
}
// We have to process tblInd *after* jc, because a jc value of anything other than "left" or
// "start" causes the tblInd to be ignored
for (DFNode *child = concrete->first; child != NULL; child = child->next) {
switch (child->tag) {
case WORD_TBLIND:
if ((CSSGet(table,"margin-left") == NULL) || !strcmp(CSSGet(table,"margin-left"),"0")) {
const char *type = DFGetAttribute(child,WORD_TYPE);
const char *w = DFGetAttribute(child,WORD_W);
char *newMarginLeft = cssPctWidth(type,w,section);
if ((newMarginLeft != NULL) && !DFStringEquals(newMarginLeft,"0%"))
CSSPut(table,"margin-left",newMarginLeft);
free(newMarginLeft);
}
break;
}
}
}
void WordPutTblPr(DFNode *concrete, CSSProperties *newTable, CSSProperties *newCell,
WordSection *section, const char *styleId)
{
DFNode *children[PREDEFINED_TAG_COUNT];
childrenToArray(concrete,children);
CSSProperties *oldTable = CSSPropertiesNew();
CSSProperties *oldCell = CSSPropertiesNew();
const char *oldStyleId = NULL;
WordGetTblPr(concrete,oldTable,oldCell,section,&oldStyleId);
if (!DFStringEquals(oldStyleId,styleId)) {
if (styleId != NULL) {
children[WORD_TBLSTYLE] = DFCreateElement(concrete->doc,WORD_TBLSTYLE);
DFSetAttribute(children[WORD_TBLSTYLE],WORD_VAL,styleId);
}
else {
children[WORD_TBLSTYLE] = NULL;
}
}
if (!DFStringEquals(CSSGet(oldTable,"width"),CSSGet(newTable,"width"))) {
CSSLength length = CSSLengthNull;
if (CSSGet(newTable,"width") != NULL) {
length = CSSLengthFromString(CSSGet(newTable,"width"));
if (!CSSLengthIsValid(length) || (length.units != UnitsPct))
length = CSSLengthNull;
}
if (CSSLengthIsValid(length) && (length.units == UnitsPct)) {
int pctVal = (int)(round(length.value*50));
children[WORD_TBLW] = DFCreateElement(concrete->doc,WORD_TBLW);
DFFormatAttribute(children[WORD_TBLW],WORD_W,"%d",pctVal);
DFSetAttribute(children[WORD_TBLW],WORD_TYPE,"pct");
}
else {
children[WORD_TBLW] = NULL;
}
}
if (!DFStringEquals(CSSGet(oldTable,"margin-left"),CSSGet(newTable,"margin-left")) ||
!DFStringEquals(CSSGet(oldTable,"margin-right"),CSSGet(newTable,"margin-right"))) {
const char *jc = NULL;
const char *newMarginLeft = CSSGet(newTable,"margin-left");
const char *newMarginRight = CSSGet(newTable,"margin-right");
if (DFStringEquals(newMarginLeft,"0") && DFStringEquals(newMarginRight,"auto")) {
jc = "left";
}
else if (DFStringEquals(newMarginLeft,"auto") && DFStringEquals(newMarginRight,"0")) {
jc = "right";
}
else if (DFStringEquals(newMarginLeft,"auto") && DFStringEquals(newMarginRight,"auto")) {
jc = "center";
}
if (jc != NULL) {
children[WORD_JC] = DFCreateElement(concrete->doc,WORD_JC);
DFSetAttribute(children[WORD_JC],WORD_VAL,jc);
}
else {
children[WORD_JC] = NULL;
}
// Only set tblInd if table is left justified (or unspecified)
if ((jc == NULL) || DFStringEquals(jc,"left")) {
if (!DFStringEquals(CSSGet(oldTable,"margin-left"),CSSGet(newTable,"margin-left"))) {
int haveTwips = 0;
int twips = 0;
CSSLength length = CSSLengthFromString(CSSGet(newTable,"margin-left"));
if (CSSLengthIsValid(length) && (length.units == UnitsPct)) {
twips = (int)round((length.value/100.0)*WordSectionContentWidth(section));
haveTwips = 1;
}
else if (CSSLengthIsValid(length) && (length.units == UnitsPt)) {
twips = (int)round(length.value*20);
haveTwips = 1;
}
if (haveTwips) {
children[WORD_TBLIND] = DFCreateElement(concrete->doc,WORD_TBLIND);
DFSetAttribute(children[WORD_TBLIND],WORD_TYPE,"dxa");
DFFormatAttribute(children[WORD_TBLIND],WORD_W,"%d",twips);
}
else {
children[WORD_TBLIND] = NULL;
}
}
}
}
if (children[WORD_TBLBORDERS] == NULL)
children[WORD_TBLBORDERS] = DFCreateElement(concrete->doc,WORD_TBLBORDERS);
WordPutTblBorders(children[WORD_TBLBORDERS],oldTable,oldCell,newTable,newCell);
if (children[WORD_TBLBORDERS]->first == NULL)
children[WORD_TBLBORDERS] = NULL;
if (children[WORD_TBLCELLMAR] == NULL)
children[WORD_TBLCELLMAR] = DFCreateElement(concrete->doc,WORD_TBLCELLMAR);
WordPutTblCellMar(children[WORD_TBLCELLMAR],oldCell,newCell);
if (children[WORD_TBLCELLMAR]->first == NULL)
children[WORD_TBLCELLMAR] = NULL;
replaceChildrenFromArray(concrete,children,WordTblPr_children);
CSSPropertiesRelease(oldTable);
CSSPropertiesRelease(oldCell);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// Table row properties (trPr element) //
// //
////////////////////////////////////////////////////////////////////////////////////////////////////
void WordPutTrPr(DFNode *concrete, const char *oldJc, const char *newJc)
{
if (!DFStringEquals(oldJc,newJc)) {
DFNode *jcElem = DFChildWithTag(concrete,WORD_JC);
if (newJc != NULL) {
if (jcElem == NULL) {
jcElem = DFCreateElement(concrete->doc,WORD_JC);
DFInsertBefore(concrete,jcElem,concrete->first);
}
DFSetAttribute(jcElem,WORD_VAL,newJc);
}
else {
if (jcElem != NULL)
DFRemoveNode(jcElem);
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// Table cell properties (tcPr element) //
// //
////////////////////////////////////////////////////////////////////////////////////////////////////
void WordGetTcPr(DFNode *concrete, CSSProperties *properties)
{
for (DFNode *child = concrete->first; child != NULL; child = child->next) {
switch (child->tag) {
case WORD_SHD:
WordGetShd(child,properties);
break;
}
}
}
void WordPutTcPr1(DFNode *concrete, CSSProperties *newp)
{
DFNode *children[PREDEFINED_TAG_COUNT];
childrenToArray(concrete,children);
CSSProperties *oldp = CSSPropertiesNew();
WordGetTcPr(concrete,oldp);
if (!DFStringEquals(CSSGet(oldp,"width"),CSSGet(newp,"width"))) {
if (CSSGet(newp,"width") != NULL) {
CSSLength length = CSSLengthFromString(CSSGet(newp,"width"));
if (length.units == UnitsPct) {
int pct50 = (int)round(length.value*50);
children[WORD_TCW] = DFCreateElement(concrete->doc,WORD_TCW);
DFSetAttribute(children[WORD_TCW],WORD_TYPE,"pct");
DFFormatAttribute(children[WORD_TCW],WORD_W,"%d",pct50);
}
else {
children[WORD_TCW] = NULL;
}
}
else {
children[WORD_TCW] = NULL;
}
}
char *oldBackgroundColor = CSSHexColor(CSSGet(oldp,"background-color"),0);
char *newBackgroundColor = CSSHexColor(CSSGet(newp,"background-color"),0);
if (!DFStringEquals(oldBackgroundColor,newBackgroundColor))
WordPutShd(concrete->doc,&children[WORD_SHD],newBackgroundColor);
free(oldBackgroundColor);
free(newBackgroundColor);
replaceChildrenFromArray(concrete,children,Word_TcPr_children);
CSSPropertiesRelease(oldp);
}
void WordPutTcPr2(DFNode *concrete, unsigned int gridSpan, const char *vMerge)
{
DFNode *children[PREDEFINED_TAG_COUNT];
childrenToArray(concrete,children);
if (gridSpan > 1) {
children[WORD_GRIDSPAN] = DFCreateElement(concrete->doc,WORD_GRIDSPAN);
DFFormatAttribute(children[WORD_GRIDSPAN],WORD_VAL,"%u",gridSpan);
}
else {
children[WORD_GRIDSPAN] = NULL;
}
if (vMerge != NULL) {
children[WORD_VMERGE] = DFCreateElement(concrete->doc,WORD_VMERGE);
if ((vMerge != NULL) && !strcmp(vMerge,"restart"))
DFSetAttribute(children[WORD_VMERGE],WORD_VAL,vMerge);
}
else {
children[WORD_VMERGE] = NULL;
}
replaceChildrenFromArray(concrete,children,Word_TcPr_children);
}