Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds support for styled paragraph inside table cells #123

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ public class DocxDocumentHandler

private Stack<ContainerProperties> paragraphsStack;

private Stack<Boolean> tableStack;

private Stack<SpanProperties> spansStack;

private HyperlinkRegistry hyperlinkRegistry;
Expand Down Expand Up @@ -109,6 +111,7 @@ public void startDocument()
this.paragraphsStack = new Stack<ContainerProperties>();
this.spansStack = new Stack<SpanProperties>();
this.addLineBreak = 0;
this.tableStack = new Stack<Boolean>();
}

public void endDocument()
Expand Down Expand Up @@ -320,10 +323,8 @@ private void processRunProperties( boolean isInsidePPr, boolean bold, boolean it
private void startParagraphIfNeeded()
throws IOException
{

if ( paragraphWasInserted && paragraphsStack.isEmpty() )
{
internalStartParagraph( null );
if (((paragraphWasInserted || !tableStack.isEmpty()) && paragraphsStack.isEmpty())) {
internalStartParagraph(null);
}
}

Expand Down Expand Up @@ -735,6 +736,8 @@ protected void doStartTable( TableProperties properties )
// w:tblGrid
// that's here temp writer is used.
pushTempWriter();
//Control if inside table
tableStack.push(true);
}

public void doEndTable( TableProperties properties )
Expand All @@ -751,7 +754,8 @@ public void doEndTable( TableProperties properties )
popTempWriter( startTable.toString() );
super.write( "</w:tbl>" );
// if table is inside a table cell, a paragraph is required.
super.write( "<w:p/>" );
super.write("<w:p/>");
tableStack.pop();
}

public void doStartTableRow( TableRowProperties properties )
Expand All @@ -773,14 +777,16 @@ public void doStartTableCell( TableCellProperties properties )
/*
* super.write( "<w:tcPr>" ); super.write( "<w:tcW w:w=\"0\" w:type=\"auto\" />" ); super.write( "</w:tcPr>" );
*/
internalStartParagraph( null );
if (properties != null) {
internalStartParagraph(properties);
}

}

public void doEndTableCell()
throws IOException
throws IOException
{
endParagraph();
endParagraphIfNeeded();
super.write( "</w:tc>" );
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,32 @@ public void testTable()
handler.getTextEnd() );
}

@Test
public void testStyledTable()
throws Exception {
IContext context = new MockContext();
BufferedElement parent = null;

ITextStylingTransformer formatter = HTMLTextStylingTransformer.INSTANCE;
IDocumentHandler handler = new DocxDocumentHandler(parent, context, "word/document.xml");
formatter.transform("<table><tbody>"
+ "<tr><td style='name:Standard'>A</td><td>B</td></tr>"
+ "<tr><td style='name:Standard'>C</td><td>D</td></tr>"
+ "<tr><td style='name:Standard'>E</td><td>F</td></tr>"
+ "</tbody></table>",
handler);

Assert.assertEquals("", handler.getTextBefore());
Assert.assertEquals("", handler.getTextBody());
Assert.assertEquals("<w:tbl>"
+ "<w:tblGrid><w:gridCol w:w=\"2994\" /><w:gridCol w:w=\"2994\" /></w:tblGrid>"
+ "<w:tr><w:tc><w:p><w:pPr><w:pStyle w:val=\"Standard\" /></w:pPr><w:r><w:t xml:space=\"preserve\" >A</w:t></w:r></w:p></w:tc><w:tc><w:p><w:r><w:t xml:space=\"preserve\" >B</w:t></w:r></w:p></w:tc></w:tr>"
+ "<w:tr><w:tc><w:p><w:pPr><w:pStyle w:val=\"Standard\" /></w:pPr><w:r><w:t xml:space=\"preserve\" >C</w:t></w:r></w:p></w:tc><w:tc><w:p><w:r><w:t xml:space=\"preserve\" >D</w:t></w:r></w:p></w:tc></w:tr>"
+ "<w:tr><w:tc><w:p><w:pPr><w:pStyle w:val=\"Standard\" /></w:pPr><w:r><w:t xml:space=\"preserve\" >E</w:t></w:r></w:p></w:tc><w:tc><w:p><w:r><w:t xml:space=\"preserve\" >F</w:t></w:r></w:p></w:tc></w:tr>"
+ "</w:tbl><w:p/>",
handler.getTextEnd());
}

@Test
public void testAll()
throws Exception
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,21 @@ public class ODTDocumentHandler

private boolean insideHeader = false;

private Stack<Boolean> tableStack;

private Stack<Integer> spanStack;

/**
* Stores each nested tag added to the document.
* Required to verify if a new paragraph can be added.
* Paragraphs cannot be direct children of another paragraph, but they
* can, sometimes, be nested if there are other tags between them, like
* a list and a list-item.
*
* Relevant document: http://docs.oasis-open.org/office/v1.2/os/OpenDocument-v1.2-os-part1.html#__RefHeading__1415138_253892949
*/
private Stack<String> nestedTagStack;

private int listDepth = 0;

private List<Boolean> lastItemAlreadyClosed = new ArrayList<Boolean>();
Expand All @@ -75,6 +88,8 @@ public void startDocument()
{
this.paragraphsStack = new Stack<Boolean>();
this.spanStack = new Stack<Integer>();
this.tableStack = new Stack<Boolean>();
this.nestedTagStack = new Stack<String>();
}

public void endDocument()
Expand Down Expand Up @@ -237,6 +252,8 @@ private void internalStartSpan( String styleName, boolean push )
{
spanStack.push( 1 );
}

pushNestedTag("span");
}

private void internalEndSpan()
Expand All @@ -249,27 +266,30 @@ private void internalEndSpan()
while ( depth > 0 )
{
super.write( "</text:span>" );
popNestedTag("span");
depth--;
}
}

private void startParagraphIfNeeded()
throws IOException
{

if ( ( paragraphWasInserted && paragraphsStack.isEmpty() ) || closeHeader )
{
if (((paragraphWasInserted || !tableStack.isEmpty()) && paragraphsStack.isEmpty()) || closeHeader) {
internalStartParagraph( false, (String) null );
}
}

public void startParagraph( ParagraphProperties properties )
throws IOException
{
if ( paragraphsStack.isEmpty() || !paragraphsStack.peek() )
if ( !paragraphsStack.isEmpty() && !paragraphsStack.peek() )
{
super.setTextLocation( TextLocation.End );
internalStartParagraph( false, properties );
internalEndParagraph();
}

if(nestedTagStack.isEmpty() || !"p".equals(nestedTagStack.peek())) {
super.setTextLocation(TextLocation.End);
internalStartParagraph(false, properties);
}
}

Expand Down Expand Up @@ -304,15 +324,6 @@ else if ( properties.isPageBreakBefore() )
}
}
internalStartParagraph( containerIsList, styleName );

// if ( properties != null )
// {
// // Remove "span" added by internalStartParagraph
// // spanStack.pop();
//
// // Process properties
// // startSpan( properties );
// }
}

private void internalStartParagraph( boolean containerIsList, String styleName )
Expand All @@ -335,6 +346,7 @@ private void internalStartParagraph( boolean containerIsList, String styleName )

// Put a 0 in the stack, endSpan is called when a paragraph is ended
spanStack.push( 0 );
pushNestedTag("p");
}

private void internalEndParagraph()
Expand All @@ -345,8 +357,10 @@ private void internalEndParagraph()
// Close any spans from paragraph style
internalEndSpan();


super.write( "</text:p>" );
paragraphsStack.pop();
popNestedTag("p");
}
}

Expand All @@ -359,6 +373,7 @@ public void startHeading( int level, HeaderProperties properties )
+ level + "\">" );
insideHeader = true;
closeHeader = false;
pushNestedTag("h");
}

public void endHeading( int level )
Expand All @@ -367,6 +382,7 @@ public void endHeading( int level )
super.write( "</text:h>" );
insideHeader = false;
closeHeader = true;
popNestedTag("h");
// startParagraph();
}

Expand Down Expand Up @@ -427,6 +443,7 @@ protected void internalStartList( String style )
super.write( "<text:list>" );
}
listDepth++;
pushNestedTag("list");
}

protected void internalEndList()
Expand All @@ -438,11 +455,14 @@ protected void internalEndList()
{
// startParagraph();
}

popNestedTag("list");
}

public void startListItem( ListItemProperties properties )
throws IOException
{
pushNestedTag("list-item");
if ( itemStyle != null )
{
super.write( "<text:list-item text:style-name=\"" + itemStyle + "\">" );
Expand All @@ -453,6 +473,8 @@ public void startListItem( ListItemProperties properties )
super.write( "<text:list-item>" );
internalStartParagraph( true, (String) null );
}


}

public void endListItem()
Expand All @@ -468,6 +490,7 @@ public void endListItem()
}
endParagraphIfNeeded();
super.write( "</text:list-item>" );
popNestedTag("list-item");
}

public void startSpan( SpanProperties properties )
Expand Down Expand Up @@ -515,6 +538,8 @@ protected void doStartTable( TableProperties properties )
// table:table-column
// that's here temp writer is used.
pushTempWriter();
//Control if inside table
tableStack.push(true);
}

public void doEndTable( TableProperties properties )
Expand All @@ -529,32 +554,52 @@ public void doEndTable( TableProperties properties )
startTable.append( "\" >" );
startTable.append( "</table:table-column>" );
popTempWriter( startTable.toString() );
super.write( "</table:table>" );
super.write("</table:table>");
tableStack.pop();
}

protected void doStartTableRow( TableRowProperties properties )
throws IOException
{
super.write( "<table:table-row>" );
pushNestedTag("table-row");
}

protected void doEndTableRow()
throws IOException
{
super.write( "</table:table-row>" );
popNestedTag("table-row");
}

protected void doStartTableCell( TableCellProperties properties )
throws IOException
{
super.write( "<table:table-cell>" );
internalStartParagraph( false, (String) null );
super.write("<table:table-cell>");
pushNestedTag("table-cell");
if (properties != null) {
internalStartParagraph(false, styleGen.getTextStyleName(properties));
}
}

public void doEndTableCell()
throws IOException
throws IOException
{
endParagraph();
super.write( "</table:table-cell>" );
endParagraphIfNeeded();
super.write("</table:table-cell>");
popNestedTag("table-cell");
}

private void popNestedTag(String tag) {
String stackTag = nestedTagStack.peek();
if(stackTag.equals(tag)) {
nestedTagStack.pop();
} else {
throw new RuntimeException("Invalid nested tag. Should be <" + tag + "> found <" + stackTag + ">.");
}
}

private void pushNestedTag(String tag) {
nestedTagStack.push(tag);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -775,7 +775,7 @@ public void testHeaderAndTextAndParagraph()

Assert.assertEquals( "", handler.getTextBefore() );
Assert.assertEquals( "", handler.getTextBody() );
Assert.assertEquals( "<text:h text:style-name=\"Heading_20_1\" text:outline-level=\"1\">Title1</text:h><text:p><text:span text:style-name=\"XDocReport_EmptyText\" >text</text:span><text:p><text:span text:style-name=\"XDocReport_EmptyText\" >paragraph</text:span></text:p></text:p>",
Assert.assertEquals( "<text:h text:style-name=\"Heading_20_1\" text:outline-level=\"1\">Title1</text:h><text:p><text:span text:style-name=\"XDocReport_EmptyText\" >text</text:span></text:p><text:p><text:span text:style-name=\"XDocReport_EmptyText\" >paragraph</text:span></text:p>",
handler.getTextEnd() );
}

Expand Down Expand Up @@ -1057,7 +1057,7 @@ public void testSpansInP()

@Test
public void testTable()
throws Exception
throws Exception
{
IContext context = new MockContext();
BufferedElement parent = null;
Expand All @@ -1075,10 +1075,36 @@ public void testTable()
Assert.assertEquals( "", handler.getTextBody() );
Assert.assertEquals( "<table:table>"
+ "<table:table-column table:number-columns-repeated=\"2\" ></table:table-column>"
+ "<table:table-row><table:table-cell><text:p><text:span text:style-name=\"XDocReport_EmptyText\" >A</text:span></text:p></table:table-cell><table:table-cell><text:p><text:span text:style-name=\"XDocReport_EmptyText\" >B</text:span></text:p></table:table-cell></table:table-row>"
+ "<table:table-row><table:table-cell><text:p><text:span text:style-name=\"XDocReport_EmptyText\" >C</text:span></text:p></table:table-cell><table:table-cell><text:p><text:span text:style-name=\"XDocReport_EmptyText\" >D</text:span></text:p></table:table-cell></table:table-row>"
+ "<table:table-row><table:table-cell><text:p><text:span text:style-name=\"XDocReport_EmptyText\" >E</text:span></text:p></table:table-cell><table:table-cell><text:p><text:span text:style-name=\"XDocReport_EmptyText\" >F</text:span></text:p></table:table-cell></table:table-row>"
+ "</table:table>",
+ "<table:table-row><table:table-cell><text:p><text:span text:style-name=\"XDocReport_EmptyText\" >A</text:span></text:p></table:table-cell><table:table-cell><text:p><text:span text:style-name=\"XDocReport_EmptyText\" >B</text:span></text:p></table:table-cell></table:table-row>"
+ "<table:table-row><table:table-cell><text:p><text:span text:style-name=\"XDocReport_EmptyText\" >C</text:span></text:p></table:table-cell><table:table-cell><text:p><text:span text:style-name=\"XDocReport_EmptyText\" >D</text:span></text:p></table:table-cell></table:table-row>"
+ "<table:table-row><table:table-cell><text:p><text:span text:style-name=\"XDocReport_EmptyText\" >E</text:span></text:p></table:table-cell><table:table-cell><text:p><text:span text:style-name=\"XDocReport_EmptyText\" >F</text:span></text:p></table:table-cell></table:table-row>"
+ "</table:table>",
handler.getTextEnd() );
}

@Test
public void testStyledTable()
throws Exception {
IContext context = new MockContext();
BufferedElement parent = null;

ITextStylingTransformer formatter = HTMLTextStylingTransformer.INSTANCE;
IDocumentHandler handler = new ODTDocumentHandler(parent, context, "content.xml");
formatter.transform("<table><tbody>"
+ "<tr><td style='name:Standard'>A</td><td>B</td></tr>"
+ "<tr><td style='name:Standard'>C</td><td>D</td></tr>"
+ "<tr><td style='name:Standard'>E</td><td>F</td></tr>"
+ "</tbody></table>",
handler);

Assert.assertEquals("", handler.getTextBefore());
Assert.assertEquals("", handler.getTextBody());
Assert.assertEquals("<table:table>"
+ "<table:table-column table:number-columns-repeated=\"2\" ></table:table-column>"
+ "<table:table-row><table:table-cell><text:p text:style-name=\"Standard\"><text:span text:style-name=\"XDocReport_EmptyText\" >A</text:span></text:p></table:table-cell><table:table-cell><text:p><text:span text:style-name=\"XDocReport_EmptyText\" >B</text:span></text:p></table:table-cell></table:table-row>"
+ "<table:table-row><table:table-cell><text:p text:style-name=\"Standard\"><text:span text:style-name=\"XDocReport_EmptyText\" >C</text:span></text:p></table:table-cell><table:table-cell><text:p><text:span text:style-name=\"XDocReport_EmptyText\" >D</text:span></text:p></table:table-cell></table:table-row>"
+ "<table:table-row><table:table-cell><text:p text:style-name=\"Standard\"><text:span text:style-name=\"XDocReport_EmptyText\" >E</text:span></text:p></table:table-cell><table:table-cell><text:p><text:span text:style-name=\"XDocReport_EmptyText\" >F</text:span></text:p></table:table-cell></table:table-row>"
+ "</table:table>",
handler.getTextEnd());
}
}
Loading