KeynoteContentHandlerTest.java

/*
 * 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.
 */
package org.apache.tika.parser.iwork;

import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.Test;
import org.xml.sax.helpers.AttributesImpl;

import org.apache.tika.metadata.Metadata;
import org.apache.tika.parser.ParseContext;
import org.apache.tika.sax.StrictXHTMLValidator;
import org.apache.tika.sax.ToXMLContentHandler;
import org.apache.tika.sax.XHTMLContentHandler;

public class KeynoteContentHandlerTest {

    /**
     * TIKA-4744: Keynote tables whose final row contains fewer cells than
     * numberOfColumns left {@code <tr>} open when {@code </sf:tabular-model>}
     * fired {@code </table>}. Drive the handler with a synthetic 3-column
     * table whose final row has only 2 cells and assert the XHTML stays
     * balanced through endDocument.
     */
    @Test
    public void testIncompleteFinalRowClosesTr() throws Exception {
        Metadata md = new Metadata();
        ToXMLContentHandler xml = new ToXMLContentHandler();
        XHTMLContentHandler xhtml = new XHTMLContentHandler(
                new StrictXHTMLValidator(xml), md, new ParseContext());
        KeynoteContentHandler h = new KeynoteContentHandler(xhtml, md);

        xhtml.startDocument();

        // <key:slide>
        h.startElement("", "slide", "key:slide", new AttributesImpl());
        // <sf:tabular-model sfa:ID="t1">
        AttributesImpl tabAttrs = new AttributesImpl();
        tabAttrs.addAttribute("", "sfa:ID", "sfa:ID", "CDATA", "t1");
        h.startElement("", "tabular-model", "sf:tabular-model", tabAttrs);
        // <sf:columns sf:count="3">
        AttributesImpl colAttrs = new AttributesImpl();
        colAttrs.addAttribute("", "sf:count", "sf:count", "CDATA", "3");
        h.startElement("", "columns", "sf:columns", colAttrs);
        h.endElement("", "columns", "sf:columns");
        // Two cells (less than 3) -> incomplete final row.
        AttributesImpl cell = new AttributesImpl();
        cell.addAttribute("", "sfa:s", "sfa:s", "CDATA", "A");
        h.startElement("", "ct", "sf:ct", cell);
        h.endElement("", "ct", "sf:ct");
        cell = new AttributesImpl();
        cell.addAttribute("", "sfa:s", "sfa:s", "CDATA", "B");
        h.startElement("", "ct", "sf:ct", cell);
        h.endElement("", "ct", "sf:ct");
        // </sf:tabular-model> -- without the fix this emits </table> while
        // <tr> is still topmost and StrictXHTMLValidator throws.
        h.endElement("", "tabular-model", "sf:tabular-model");
        // </key:slide>
        h.endElement("", "slide", "key:slide");

        xhtml.endDocument();

        String out = xml.toString();
        // Sanity: the cells emitted, the row closed, and the table closed.
        assertTrue(out.contains("<td>A</td>"), "expected td A; got: " + out);
        assertTrue(out.contains("<td>B</td>"), "expected td B; got: " + out);
        assertTrue(out.contains("</tr>"), "expected </tr>; got: " + out);
        assertTrue(out.contains("</table>"), "expected </table>; got: " + out);
    }
}