com.github.codeurjc.slidesconverter.PowerPointToHTML.java Source code

Java tutorial

Introduction

Here is the source code for com.github.codeurjc.slidesconverter.PowerPointToHTML.java

Source

/*
 *  ====================================================================
 *    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 com.github.codeurjc.slidesconverter;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.poi.hslf.usermodel.HSLFSlide;
import org.apache.poi.hslf.usermodel.HSLFSlideShow;
import org.apache.poi.sl.usermodel.TextParagraph.TextAlign;
import org.apache.poi.xslf.usermodel.XMLSlideShow;
import org.apache.poi.xslf.usermodel.XSLFPictureShape;
import org.apache.poi.xslf.usermodel.XSLFShape;
import org.apache.poi.xslf.usermodel.XSLFSlide;
import org.apache.poi.xslf.usermodel.XSLFTextParagraph;
import org.apache.poi.xslf.usermodel.XSLFTextRun;
import org.apache.poi.xslf.usermodel.XSLFTextShape;

public class PowerPointToHTML {

    private static class Section {

        public String name;

        public int level;
        public int sectionNum;
        public int subsectionNum;
        public int startSlide;
        public Section parentSection;

        public Section(int level, int numInLevel, String name) {
            super();
            this.level = level;
            this.sectionNum = numInLevel;
            this.name = name;
        }
    }

    public static void main(String[] args) throws Exception {

        PowerPointToHTML converter = new PowerPointToHTML(Paths.get("../../1-Introduccin.pptx"),
                Paths.get("../../1-Introduccin.ppt"), Paths.get("../../out/Introduction.html"));

        converter.setMainTitle(1, 1, "Tema 1", "Introduccin", "Desarrollo de aplicaciones web");

        converter.setTitleSlides(2, 14, 24, 62, 73, 85, 101, 112);

        converter.setImageSlide(7, 8, 9, 64, 82, 83, 84, 111, 29, 30, 32, 33, 36, 40, 44, 50, 57, 58, 59, 60);

        converter.setNonSubtitleSlides(1, 26, 27, 34, 38, 42, 45, 54, 59, 77, 113);

        converter.convert();

    }

    // Input parameters

    private int mainTitleFromSlide;
    private int mainTitleToSlide;
    private String mainTitleNumber;
    private String mainTitle;
    private String slidesContext;

    private int[] titleSlides = {};
    private int[] imageSlides = {};
    private int[] nonSubtitleSlides = {};

    // Conversion attributes

    private Section[] sectionsPerPage;

    private Path htmlFile;
    private Path pptxFile;
    private Path pptFile;

    private PrintWriter out;
    private double width;
    private double height;

    private void setMainTitle(int fromSlide, int toSlide, String number, String mainTitle, String context) {

        this.mainTitleFromSlide = fromSlide;
        this.mainTitleToSlide = toSlide;
        this.mainTitleNumber = number;
        this.mainTitle = mainTitle;
        this.slidesContext = context;
    }

    private void setNonSubtitleSlides(int... nonSubtitleSlides) {
        this.nonSubtitleSlides = nonSubtitleSlides;
    }

    private void setImageSlide(int... imageSlides) {
        this.imageSlides = imageSlides;
    }

    public void setTitleSlides(int... titleSlides) {
        this.titleSlides = titleSlides;
    }

    public PowerPointToHTML(Path pptxFile, Path pptFile, Path htmlFile) {

        this.pptxFile = pptxFile;
        this.pptFile = pptFile;
        this.htmlFile = htmlFile;
    }

    public void convert() throws IOException {

        InputStream fis = Files.newInputStream(pptxFile);
        XMLSlideShow pptx = new XMLSlideShow(fis);
        fis.close();

        InputStream is = Files.newInputStream(pptFile);
        HSLFSlideShow ppt = new HSLFSlideShow(is);
        is.close();

        width = pptx.getPageSize().getWidth();
        height = pptx.getPageSize().getHeight();

        out = new PrintWriter(Files.newOutputStream(htmlFile));

        out.println("<!DOCTYPE html>");
        out.println("<html><body>");

        out.println("<h1>" + this.mainTitleNumber + " " + mainTitle + "</h1>");
        out.println("<h2>" + this.slidesContext + "</h2>");

        List<Section> sections = calculateSections(pptx, ppt);

        generateTOC(sections);

        generateSlidesContent(pptx, ppt);

        pptx.close();
        ppt.close();
        out.close();

    }

    private void generateSlidesContent(XMLSlideShow pptx, HSLFSlideShow ppt) throws IOException {

        out.println("<h1>Slides</h1>");

        for (int numSlide = 0; numSlide < pptx.getSlides().size(); numSlide++) {

            System.out.println("Processing slide " + numSlide);

            XSLFSlide slideX = pptx.getSlides().get(numSlide);
            HSLFSlide slide = ppt.getSlides().get(numSlide);

            Section section = getSection(numSlide);

            if (section != null && section.startSlide == numSlide) {

                if (section.level == 0) {

                    out.println("<h2>" + escape(section.sectionNum + ". " + section.name) + "</h2>");

                } else {

                    out.println(
                            "<h3>" + escape(section.sectionNum + "." + section.subsectionNum + ". " + section.name)
                                    + "</h3>");
                }
            }

            generateSlideImage(slide);

            out.println("<h4>Slide (" + (numSlide + 1) + ")</h4>");

            out.println("<img src=" + getImageFileName(slideX.getSlideNumber()) + "><br>");

            for (XSLFShape shape : slideX.getShapes()) {

                if (shape instanceof XSLFTextShape) {

                    XSLFTextShape tsh = (XSLFTextShape) shape;

                    boolean first = true;
                    boolean code = false;

                    boolean subtitle = false;

                    for (XSLFTextParagraph p : tsh) {

                        String indent = "";

                        int indentLevel = p.getIndentLevel();
                        if (subtitle) {
                            indentLevel++;
                        }

                        if (indentLevel > 0) {
                            for (int j = 0; j < indentLevel; j++) {
                                indent += "> ";
                            }
                        }

                        StringBuilder sb = new StringBuilder();
                        for (XSLFTextRun r : p) {
                            sb.append(r.getRawText());
                        }

                        String text = sb.toString();

                        // Avoid section or subsection name to appear in slide
                        // contents
                        if ((section != null && (text.equals(section.name)
                                || (section.parentSection != null && section.parentSection.name.equals(text))))
                                || text.trim().isEmpty()) {
                            continue;
                        }

                        if (first) {

                            code = p.getDefaultFontFamily().startsWith("Courier");

                            if (code) {
                                out.println("<pre style='border:1px;border-style: solid;'>");
                            }
                        }

                        if (!code) {
                            out.println("<p>");
                        }

                        out(indent + text);

                        if (!code) {
                            out.println("</p>");
                        }

                        first = false;
                        if (p.getTextAlign() == TextAlign.CENTER) {
                            subtitle = true;
                        }
                    }

                    if (code) {
                        out.println("</pre>");
                    }

                } else if (shape instanceof XSLFPictureShape) {

                    XSLFPictureShape pShape = (XSLFPictureShape) shape;

                    out.println("<p>Imagen: " + pShape.getShapeName() + "</p>");
                }
            }

            out.println("<hr>");
        }

        out.println("</body></html>");
    }

    private Section getSection(int slideNum) {

        return sectionsPerPage[slideNum];
    }

    private void generateTOC(List<Section> sections) {

        out.println("<h1>Contenido</h1>");
        out.println("<ul>");

        boolean lastInSubsection = false;

        for (Section section : sections) {
            if (section.level == 0) {

                if (lastInSubsection) {
                    out.println("</ul></li>");
                    lastInSubsection = false;
                }
                out.println("<li><strong>"
                        + escape(section.sectionNum + ". " + section.name + " (" + (section.startSlide + 1) + ")")
                        + "</strong>");
            } else {

                if (!lastInSubsection) {
                    out.println("<ul>");
                }

                out.println("<li>" + escape(section.sectionNum + "." + section.subsectionNum + ". " + section.name)
                        + " (" + (section.startSlide + 1) + ")" + "</li>");

                lastInSubsection = true;
            }
        }

        if (lastInSubsection) {
            out.println("</ul></li>");
            lastInSubsection = false;
        }

        out.println("</ul>");
    }

    private List<Section> calculateSections(XMLSlideShow pptx, HSLFSlideShow ppt) {

        sectionsPerPage = new Section[pptx.getSlides().size()];

        List<Section> sectionsList = new ArrayList<>();

        String lastSection = null;
        String lastSubsection = null;

        Section lastSectionObj = null;
        Section lastSubsectionObj = null;

        int numSections = 0;
        int numSubsectionsInSection = 0;

        for (int slideNum = 0; slideNum < pptx.getSlides().size(); slideNum++) {

            XSLFSlide slideX = pptx.getSlides().get(slideNum);
            HSLFSlide slide = ppt.getSlides().get(slideNum);

            String title = calculateTitle(slideX, slide);
            String subtitle = calculateSubtitle(slideX, slide);

            if (title != null && !title.trim().isEmpty()) {

                if (lastSection == null || !lastSection.equals(title)) {
                    lastSection = title;
                    numSections++;
                    Section section = new Section(0, numSections, title);
                    section.startSlide = slideNum;
                    sectionsList.add(section);

                    lastSubsection = null;
                    lastSubsectionObj = null;

                    numSubsectionsInSection = 0;

                    this.sectionsPerPage[slideNum] = section;

                    lastSectionObj = section;

                    System.out.println("Section " + section.sectionNum + ". " + section.name);
                }

            }

            if (subtitle != null && !subtitle.trim().isEmpty()) {

                if (lastSubsection == null || !lastSubsection.equals(subtitle)) {
                    lastSubsection = subtitle;
                    numSubsectionsInSection++;

                    Section section = new Section(1, numSections, subtitle);
                    section.startSlide = slideNum;
                    section.subsectionNum = numSubsectionsInSection;
                    section.parentSection = lastSectionObj;
                    sectionsList.add(section);

                    this.sectionsPerPage[slideNum] = section;

                    lastSubsectionObj = section;

                    System.out.println("  Subsection " + section.sectionNum + ". " + section.name);
                }

            }

            if (sectionsPerPage[slideNum] == null) {

                if (lastSubsectionObj != null) {
                    this.sectionsPerPage[slideNum] = lastSubsectionObj;
                } else {
                    this.sectionsPerPage[slideNum] = lastSectionObj;
                }
            }
        }

        return sectionsList;
    }

    private String calculateSubtitle(XSLFSlide slideX, HSLFSlide slide) {

        if (isContentSlide(slide.getSlideNumber())) {
            return null;
        }

        for (XSLFShape shape : slideX.getShapes()) {

            if (shape instanceof XSLFTextShape) {

                XSLFTextShape tsh = (XSLFTextShape) shape;

                Rectangle2D figure = getRelativeFigure(tsh);

                if (figure.getY() < 0.1) {
                    continue;
                }

                for (XSLFTextParagraph p : tsh) {
                    for (XSLFTextRun r : p) {
                        return r.getRawText();
                    }
                }

                return null;
            }
        }

        return null;
    }

    private boolean isContentSlide(int slideNum) {
        return (slideNum >= mainTitleFromSlide && slideNum <= mainTitleToSlide)
                || ArrayUtils.contains(nonSubtitleSlides, slideNum) || ArrayUtils.contains(titleSlides, slideNum)
                || ArrayUtils.contains(imageSlides, slideNum);
    }

    private String escape(String text) {
        return StringEscapeUtils.escapeHtml4(text);
    }

    private String calculateTitle(XSLFSlide slideX, HSLFSlide slide) {

        String title = slide.getTitle();

        if (title != null) {
            return title;
        }

        title = slideX.getTitle();

        if (title != null) {
            return title;
        }

        boolean titleSlide = ArrayUtils.contains(titleSlides, slideX.getSlideNumber());

        for (XSLFShape shape : slideX.getShapes()) {

            if (shape instanceof XSLFTextShape) {

                XSLFTextShape tsh = (XSLFTextShape) shape;

                Rectangle2D figure = getRelativeFigure(tsh);

                if (titleSlide || figure.getY() < 0.1) {

                    StringBuilder titleSB = new StringBuilder();

                    for (XSLFTextParagraph p : tsh) {
                        for (XSLFTextRun r : p) {
                            titleSB.append(r.getRawText());
                        }
                    }

                    title = titleSB.toString();

                    if (!title.trim().isEmpty()) {
                        return title;
                    }
                }
            }
        }

        return null;
    }

    private Rectangle2D getRelativeFigure(XSLFTextShape tsh) {

        Rectangle2D anchor = tsh.getAnchor();

        return new Rectangle2D.Double(anchor.getX() * 100 / width, anchor.getY() * 100 / height,
                anchor.getWidth() * 100 / width, anchor.getHeight() * 100 / height);
    }

    private void out(String text) {
        out.print(StringEscapeUtils.escapeHtml4(text) + "\n");
    }

    public void generateSlideImage(HSLFSlide slide) throws IOException {

        float scale = 0.5f;

        int sWidth = (int) (width * scale);
        int sHeight = (int) (height * scale);

        String title = slide.getTitle();
        System.out.println("Rendering slide " + slide.getSlideNumber() + (title == null ? "" : ": " + title));

        BufferedImage img = new BufferedImage(sWidth, sHeight, BufferedImage.TYPE_INT_RGB);
        Graphics2D graphics = img.createGraphics();
        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);

        graphics.setPaint(Color.white);
        graphics.fill(new Rectangle2D.Float(0, 0, sWidth, sHeight));

        graphics.scale((double) sWidth / width, (double) sHeight / height);

        try {

            slide.draw(graphics);

            Path imagePath = htmlFile.getParent().resolve(Paths.get(getImageFileName(slide.getSlideNumber())));

            OutputStream out = Files.newOutputStream(imagePath);
            ImageIO.write(img, "png", out);
            out.close();

        } catch (Exception e) {
            System.err.println("Exception rendering slide " + slide.getSlideNumber() + ": " + title);
        }
    }

    private String getImageFileName(int slideNumber) {
        return htmlFile.getFileName() + "-Slide" + slideNumber + ".png";
    }
}