org.articleEditor.insertContent.DocumentUpdater1.java Source code

Java tutorial

Introduction

Here is the source code for org.articleEditor.insertContent.DocumentUpdater1.java

Source

/*
 * Copyright 2013 Japplis.
 *
 * Licensed 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.articleEditor.insertContent;

import static javax.swing.text.StyleConstants.*;

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Enumeration;
import java.util.List;
import java.util.StringTokenizer;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.*;
import javax.swing.text.Document;

import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlCursor;
import org.articleEditor.articleKit.DocxEditorKit;
import org.openide.util.Exceptions;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTText;

/**
 * Updates the XWPFDocument according to the changes of the DocxDocument.
 *
 * @author Anthony Goubard - Japplis
 */
public class DocumentUpdater1 implements DocumentListener {

    private XWPFDocument document;
    private int currentOffset;

    public DocumentUpdater1(XWPFDocument document) {
        this.document = document;
    }

    public void insertString(String text, int offset) {
        try {
            currentOffset = 0;
            StringTokenizer stText = new StringTokenizer(text, "\n", true);
            while (stText.hasMoreTokens()) {
                String textPart = stText.nextToken();
                DocumentPosition position = searchPart(document.getBodyElements(), offset);
                if (position != null) {
                    String oldText = position.text.getStringValue();
                    String newText = oldText.substring(0, position.offsetInText) + textPart
                            + (position.offsetInText == oldText.length() ? ""
                                    : oldText.substring(position.offsetInText));
                    position.text.setStringValue(newText);
                    offset += textPart.length();
                } else {
                    XWPFParagraph paragraph = (XWPFParagraph) document.getBodyElements()
                            .get(document.getBodyElements().size() - 1);
                    paragraph.createRun().setText(textPart);
                }
            }
        } catch (BadLocationException ex) {
            Exceptions.printStackTrace(ex);
        }
    }

    public void insertImage(ImageIcon image, int offset) {
        try {
            currentOffset = 0;
            DocumentPosition position = searchPart(document.getBodyElements(), offset);
            if (position != null) {
                int width = image.getIconWidth();
                int height = image.getIconHeight();
                ByteArrayOutputStream output = new ByteArrayOutputStream();
                BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
                bi.getGraphics().drawImage(image.getImage(), 0, 0, null);
                ImageIO.write(bi, "png", output);
                byte[] imageData = output.toByteArray();
                InputStream imageDataStream = new ByteArrayInputStream(imageData);
                position.run.addPicture(imageDataStream, XWPFDocument.PICTURE_TYPE_PNG, image.getDescription(),
                        width, height);
            }
        } catch (BadLocationException | IOException | InvalidFormatException ex) {
            Exceptions.printStackTrace(ex);
        }
    }

    public void remove(int offset, int length) {
        try {
            currentOffset = 0;
            DocumentPosition position = searchPart(document.getBodyElements(), offset);
            currentOffset = 0;
            DocumentPosition endPosition = searchPart(document.getBodyElements(), offset + length);
            if (position != null) {
                boolean deleted = deleteFromSameText(position, endPosition);
                if (!deleted) {
                    deleteFromSameText(position, null);
                    deleteInBetween(position, endPosition);
                    String endText = endPosition.text.getStringValue();
                    String newEndText = endText.substring(endPosition.offsetInText);
                    endPosition.text.setStringValue(newEndText);
                }
            }

        } catch (BadLocationException ex) {
            Exceptions.printStackTrace(ex);
        }
    }

    private boolean deleteFromSameText(DocumentPosition position, DocumentPosition endPosition) {
        boolean sameText = endPosition == null || position.text == endPosition.text;
        if (sameText) {
            String oldValue = position.text.getStringValue();
            String newValue = oldValue.substring(0, position.offsetInText)
                    + (endPosition == null ? "" : oldValue.substring(endPosition.offsetInText));
            position.text.setStringValue(newValue);
            if (newValue.isEmpty()) {
                position.run.getCTR().removeT(position.positionInRun);
                if (position.run.getCTR().getTList().isEmpty()) {
                    position.run.getParagraph().removeRun(position.positionInParagraph);
                }
            }
        }
        return sameText;
    }

    private void deleteInBetween(DocumentPosition startPosition, DocumentPosition endPosition) {
        XWPFDocument doc = startPosition.run.getParagraph().getDocument();
        boolean deletePart = false;
        for (int elemIndex = 0; elemIndex < doc.getBodyElements().size(); elemIndex++) {
            IBodyElement elem = doc.getBodyElements().get(elemIndex);
            if (!deletePart && startPosition.run.getParagraph() != elem) {
                // Skip
            } else if (!deletePart) {
                deletePart = true;
                deleteRuns(startPosition, true);
            } else if (elem != endPosition.run.getParagraph()) {
                doc.removeBodyElement(elemIndex);
                elemIndex--;
            } else {
                deleteRuns(endPosition, false);
                return;
            }
        }
    }

    private void deleteRuns(DocumentPosition position, boolean fromPosition) {
        boolean deleteRun = false;
        List<CTText> texts = position.run.getCTR().getTList();
        for (int textIndex = 0; textIndex < texts.size(); textIndex++) {
            CTText text = texts.get(textIndex);
            if (!fromPosition && text != position.text) {
                position.run.getCTR().removeT(0);
                textIndex--;
            } else if (!fromPosition) {
                return;
            }
            if (deleteRun) {
                position.run.getCTR().removeT(position.positionInRun + 1);
                textIndex--;
            } else if (fromPosition && text == position.text) {
                deleteRun = true;
            }
        }
        if (position.run.getCTR().getTList().isEmpty() && !position.run.getParagraph().getRuns().isEmpty()) {
            position.run.getParagraph().removeRun(position.positionInParagraph);
        }
    }

    @Override
    public void insertUpdate(DocumentEvent de) {
        int offset = de.getOffset();
        int length = de.getLength();
        try {
            Element selectedCharElement = ((StyledDocument) de.getDocument()).getCharacterElement(offset);
            ImageIcon image = (ImageIcon) StyleConstants.getIcon(selectedCharElement.getAttributes());
            if (image != null) {
                insertImage(image, offset);
            }
            String addedText = de.getDocument().getText(offset, length);
            insertString(addedText, offset);
        } catch (BadLocationException ex) {
            Exceptions.printStackTrace(ex);
        }
    }

    @Override
    public void removeUpdate(DocumentEvent de) {
        int offset = de.getOffset();
        int length = de.getLength();
        remove(offset, length);
    }

    @Override
    public void changedUpdate(DocumentEvent de) {
        Element root = de.getDocument().getDefaultRootElement();
        processDocumentEventChange(de, root);
    }

    private void processDocumentEventChange(DocumentEvent de, Element root) {
        int start = de.getOffset();
        int length = de.getLength();
        DocumentEvent.ElementChange change = de.getChange(root);
        if (change != null) {
            applyChangeToXWPF(change, root);
        }

        for (int i = 0; i < root.getElementCount(); i++) {
            Element child = root.getElement(i);
            if (child.getStartOffset() <= start && child.getEndOffset() >= start + length) {
                processDocumentEventChange(de, child);
            }
        }
    }

    private void applyChangeToXWPF(DocumentEvent.ElementChange change, Element root) {
        Document doc = root.getDocument();

        Element[] removedElements = change.getChildrenRemoved();
        Element[] addedElements = change.getChildrenAdded();
        System.out.println(root + " removed=" + removedElements.length + " added=" + addedElements.length);

        for (int i = removedElements.length - 1; i >= 0; i--) {
            int start = removedElements[i].getStartOffset();
            int length = removedElements[i].getEndOffset() - removedElements[i].getStartOffset();
            remove(start, length);
        }
        for (Element addedElem : addedElements) {
            try {
                currentOffset = 0;
                DocumentPosition pos = searchPart(document.getBodyElements(), addedElem.getStartOffset());
                if (pos != null) {
                    String text = doc.getText(addedElem.getStartOffset(),
                            addedElem.getEndOffset() - addedElem.getStartOffset());
                    XWPFRun run = pos.run.getParagraph().createRun();
                    run.setText(text);
                    applyAttributes(run, addedElem.getAttributes());
                }
            } catch (BadLocationException ex) {
                Exceptions.printStackTrace(ex);
            }
        }

    }

    public void changedUpdateOriginal(DocumentEvent de) {
        DefaultStyledDocument doc = (DefaultStyledDocument) de.getDocument();
        ElementIterator iter = new ElementIterator(de.getDocument());
        for (Element elem = iter.first(); elem != null; elem = iter.next()) {
            DocumentEvent.ElementChange change = de.getChange(elem);
            if (change != null) {
                Element[] removedElems = change.getChildrenRemoved();
                for (int i = removedElems.length - 1; i >= 0; i--) {
                    remove(removedElems[i].getStartOffset(), removedElems[i].getEndOffset());
                }
                for (Element addedElem : change.getChildrenAdded()) {
                    try {
                        currentOffset = 0;
                        DocumentPosition pos = searchPart(document.getBodyElements(), addedElem.getStartOffset());
                        if (pos != null) {
                            String text = doc.getText(addedElem.getStartOffset(),
                                    addedElem.getEndOffset() - addedElem.getStartOffset());
                            XWPFRun run = pos.run.getParagraph().createRun();
                            run.setText(text);
                            applyAttributes(run, addedElem.getAttributes());
                        }
                    } catch (BadLocationException ex) {
                        Exceptions.printStackTrace(ex);
                    }
                }
            }
        }
    }

    DocumentPosition searchPart(List<IBodyElement> content, int offset) throws BadLocationException {
        for (IBodyElement elem : content) {
            if (elem instanceof XWPFParagraph) {
                DocumentPosition position = searchParagraph((XWPFParagraph) elem, offset);
                if (position != null) {
                    return position;
                }
            } else if (elem instanceof XWPFTable) {
                searchTable((XWPFTable) elem, offset);
            }
        }
        return null;
    }

    DocumentPosition searchParagraph(XWPFParagraph paragraph, int offset) throws BadLocationException {
        int runIndex = 0;
        for (XWPFRun run : paragraph.getRuns()) {
            DocumentPosition position = searchRun(run, offset);
            if (position != null) {
                position.positionInParagraph = runIndex;
                return position;
            }
            runIndex++;
        }
        return null;
    }

    DocumentPosition createRun(XWPFParagraph paragraph) {
        XWPFRun run = paragraph.createRun();
        DocumentPosition position = new DocumentPosition();
        position.run = run;
        position.text = run.getCTR().addNewT();
        return position;
    }

    DocumentPosition searchRun(XWPFRun run, int offset) throws BadLocationException {
        for (XWPFPicture picture : run.getEmbeddedPictures()) {
            // currentOffset++;
        }
        List<CTText> texts = run.getCTR().getTList();
        int textIndex = 0;
        for (CTText text : texts) {
            String textValue = text.getStringValue();
            int textLength = textValue.length();
            if (currentOffset + textLength >= offset) {// || (currentOffset + textLength == offset && !textValue.endsWith("\n"))) {
                DocumentPosition position = new DocumentPosition();
                position.run = run;
                position.text = text;
                position.offsetInText = offset - currentOffset;
                position.positionInRun = textIndex;
                return position;
            } else {
                currentOffset += textLength;
            }
            textIndex++;
        }
        return null;
    }

    DocumentPosition searchTable(XWPFTable table, int offset) throws BadLocationException {
        for (XWPFTableRow row : table.getRows()) {
            for (XWPFTableCell cell : row.getTableCells()) {
                DocumentPosition position = searchPart(cell.getBodyElements(), offset);
                if (position != null) {
                    return position;
                }
            }
        }
        return null;
    }

    public void applyAttributes(XWPFRun run, AttributeSet attributes) {
        Enumeration attributeNames = attributes.getAttributeNames();
        while (attributeNames.hasMoreElements()) {
            Object attributeName = attributeNames.nextElement();
            Object attributeValue = attributes.getAttribute(attributeName);
            if (attributeName == Bold) {
                run.setBold((Boolean) attributeValue);
            } else if (attributeName == Italic) {
                run.setItalic((Boolean) attributeValue);
            } else if (attributeName == Underline) {
                run.setUnderline((Boolean) attributeValue ? UnderlinePatterns.SINGLE : UnderlinePatterns.NONE);
            } else if (attributeName == FontFamily || attributeName == Family) {
                run.setFontFamily((String) attributeValue);
            } else if (attributeName == FontSize) {
                run.setFontSize(((Number) attributeValue).intValue());
            } else if (attributeName == Foreground) {
                Color color = (Color) attributeValue;
                String rgb = Integer.toHexString((color.getRGB() & 0xffffff) | 0x1000000).substring(1);
                run.setColor(rgb);
            } else if (attributeName == Background) {
                Color color = (Color) attributeValue;
                String rgb = Integer.toHexString((color.getRGB() & 0xffffff) | 0x1000000).substring(1);
                //run.getCTR().getRPr().setHighlight();
            } else if (attributeName == Subscript) {
                run.setSubscript((Boolean) attributeValue ? VerticalAlign.SUBSCRIPT : VerticalAlign.BASELINE);
            } else if (attributeName == Superscript) {
                run.setSubscript((Boolean) attributeValue ? VerticalAlign.SUPERSCRIPT : VerticalAlign.BASELINE);
            } else if (attributeName == StrikeThrough) {
                run.setStrike((Boolean) attributeValue);
            } else if (attributeName == Alignment) {
                ParagraphAlignment alignment = documentToPOI((Integer) attributeValue);
                run.getParagraph().setAlignment(alignment);
            }
        }
    }

    private ParagraphAlignment documentToPOI(int alignment) {
        switch (alignment) {
        case ALIGN_LEFT:
            return ParagraphAlignment.LEFT;
        case ALIGN_RIGHT:
            return ParagraphAlignment.RIGHT;
        case ALIGN_CENTER:
            return ParagraphAlignment.CENTER;
        case ALIGN_JUSTIFIED:
            return ParagraphAlignment.DISTRIBUTE;
        }
        return ParagraphAlignment.LEFT;
    }

    class DocumentPosition {

        private XWPFRun run;
        private CTText text;
        private int offsetInText;
        private int positionInRun; // index of the CTText
        private int positionInParagraph; // index of the run
    }

    public static void main(String[] args) {
        try {
            JFrame f = new JFrame();
            f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

            JTextPane pane = new JTextPane();
            DocxEditorKit kit = new DocxEditorKit();
            pane.setEditorKit(kit);
            String templateFilePath = "C:/WORK/joeffice/DocxTemplate.docx";
            String templateChangedFilePath = "C:/WORK/joeffice/DocxTemplateChanged.docx";
            kit.read(new FileInputStream(templateFilePath), pane.getDocument(), 0);
            XWPFDocument poiDocument = (XWPFDocument) pane.getDocument().getProperty("XWPFDocument");

            pane.getDocument().addDocumentListener(new DocumentUpdater1(poiDocument));
            JScrollPane scrollPane = new JScrollPane(pane);
            f.add(scrollPane);

            StyledDocument doc = (StyledDocument) pane.getDocument();
            doc.insertString(0, "aaa\nbbb\nccc", null);

            SimpleAttributeSet attrs = new SimpleAttributeSet();
            StyleConstants.setBold(attrs, true);
            doc.setCharacterAttributes(4, 3, attrs, false);

            poiDocument.write(new FileOutputStream(templateChangedFilePath));

            f.setSize(300, 300);
            f.setLocationRelativeTo(null);
            f.setVisible(true);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (BadLocationException e) {
            e.printStackTrace();
        }
    }
}