blob: 3cb6055d3fdb466702e5d6847bcc9e112d8d2378 [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2019-2020 CEA LIST.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Yupanqui Munoz (CEA LIST) yupanqui.munozjulho@cea.fr - Initial API and implementation
* Vincent Lorenzo (CEA LIST) vincent.lorenzo@cea.fr - Initial API and implementation
* Vincent Lorenzo (CEA LIST) vincent.lorenzo@cea.fr - bug 559826
* Pauline DEVILLE (CEA LIST) pauline.deville@cea.fr - Bug 569249
*
*****************************************************************************/
package org.eclipse.papyrus.model2doc.odt.internal.util;
import java.io.File;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.osgi.util.NLS;
import org.eclipse.papyrus.model2doc.core.builtintypes.AbstractTable;
import org.eclipse.papyrus.model2doc.core.builtintypes.Cell;
import org.eclipse.papyrus.model2doc.core.builtintypes.TextCell;
import org.eclipse.papyrus.model2doc.core.service.FileIOService;
import org.eclipse.papyrus.model2doc.core.service.FileIOServiceImpl;
import org.eclipse.papyrus.model2doc.core.transcription.ImageDescription;
import org.eclipse.papyrus.model2doc.odt.Activator;
import org.eclipse.papyrus.model2doc.odt.internal.editor.ODTEditor;
import org.eclipse.papyrus.model2doc.odt.service.ODTFileIOService;
import org.eclipse.papyrus.model2doc.odt.service.ODTFileIOServiceImpl;
import com.sun.star.beans.PropertyValue;
import com.sun.star.beans.XPropertySet;
import com.sun.star.container.XNameContainer;
import com.sun.star.document.XDocumentInsertable;
import com.sun.star.io.IOException;
import com.sun.star.lang.IllegalArgumentException;
import com.sun.star.lang.XMultiServiceFactory;
import com.sun.star.text.ControlCharacter;
import com.sun.star.text.SizeType;
import com.sun.star.text.TextContentAnchorType;
import com.sun.star.text.WrapTextMode;
import com.sun.star.text.XParagraphCursor;
import com.sun.star.text.XText;
import com.sun.star.text.XTextContent;
import com.sun.star.text.XTextCursor;
import com.sun.star.text.XTextFrame;
import com.sun.star.text.XTextTable;
import com.sun.star.uno.UnoRuntime;
/**
* Utility class for writing odt documents.
*
*/
public class WriteUtil {
/**
* RegEx for selecting all HTML/RichText tags.
*/
private static final String HTML_TAG_REGEX = "<[^<>]+>"; //$NON-NLS-1$
private static final String HTML_EXTENSION = "html"; //$NON-NLS-1$
private static final FileIOService fileIOService = new FileIOServiceImpl();
private static final ODTFileIOService odtFileIOService = new ODTFileIOServiceImpl();
/**
* Constructor.
*
*/
private WriteUtil() {
// to avoid instantiation
}
/**
* Add Paragraph.
*
* @param xTextCursor
* the {@link XTextCursor} to use to edit the document
* @param text
* the text to write
* @param processRichText
* if <code>true</code> the text represents RichText contents
*/
public static void addParagraph(XTextCursor xTextCursor, String text, boolean processRichText) {
addParagraph(xTextCursor, text, processRichText, true);
}
/**
* Add Paragraph.
*
* @param xTextCursor
* the {@link XTextCursor} to use to edit the document
* @param text
* the text to write
* @param processRichText
* if <code>true</code> the text represents RichText contents
* @param endParagraph
* if <code>true</code>, we will add a empty line at the end of the paragraph
*/
public static void addParagraph(final XTextCursor xTextCursor, final String text, final boolean processRichText, final boolean endParagraph) {
if (processRichText) {
addParagraphWithRichText(xTextCursor, text);
} else {
addText(xTextCursor, text);
if (endParagraph) {
endParagraph(xTextCursor);
}
}
}
/**
* Add control character to end of document.
*
* @param xTextCursor
* @param controlCharacter
*/
public static void addControlCharacter(XTextCursor xTextCursor, short controlCharacter) {
XText xText = xTextCursor.getText();
xText.insertControlCharacter(xTextCursor, controlCharacter, false);
xTextCursor.gotoEnd(false);
}
/**
* Add Text.
*
* @param xTextCursor
* @param text
*/
public static void addText(XTextCursor xTextCursor, String text) {
xTextCursor.setString(text);
xTextCursor.gotoEnd(false);
}
/**
* Add TextContent.
*
* @param xTextCursor
* @param text
*/
public static void addTextContent(XTextCursor xTextCursor, XTextContent xTextContent) {
XText xText = xTextCursor.getText();
xText.insertTextContent(xTextCursor, xTextContent, false);
xTextCursor.gotoEnd(false);
}
public static void addImageLink(String imageFilePath, String caption, XTextCursor xTextCursor, ODTEditor odtEditor) {
ParagraphImageWriter writer = new ParagraphImageWriter(odtEditor);
writer.writeImage(xTextCursor, imageFilePath, caption);
}
public static void addImageLink(ImageDescription image, String caption, XTextCursor xTextCursor, ODTEditor odtEditor) {
String imageFilePath = odtFileIOService.getODTFilePrefix() + image.getPath();
try {
XMultiServiceFactory xMultiServiceFactory = odtEditor.getXMultiServiceFactory();
Object tmp = xMultiServiceFactory.createInstance("com.sun.star.text.TextFrame"); //$NON-NLS-1$
final XTextFrame textFrame = UnoRuntime.queryInterface(XTextFrame.class, tmp);
XPropertySet framePropertySet = createXPropertySet(textFrame);
framePropertySet.setPropertyValue("SizeType", SizeType.VARIABLE); //$NON-NLS-1$
framePropertySet.setPropertyValue("AnchorType", TextContentAnchorType.AS_CHARACTER); //$NON-NLS-1$
framePropertySet.setPropertyValue("ZOrder", 1);// not really sure //$NON-NLS-1$
framePropertySet.setPropertyValue("TextWrap", WrapTextMode.THROUGH); //$NON-NLS-1$
// remove the margin around the image
framePropertySet.setPropertyValue(BaseFramePropertiesConstants.LEFT_BORDER_DISTANCE, 0);
framePropertySet.setPropertyValue(BaseFramePropertiesConstants.RIGHT_BORDER_DISTANCE, 0);
framePropertySet.setPropertyValue(BaseFramePropertiesConstants.TOP_BORDER_DISTANCE, 0);
framePropertySet.setPropertyValue(BaseFramePropertiesConstants.BOTTOM_BORDER_DISTANCE, 0);
// Creating the service GraphicObject
Object graphicObject = xMultiServiceFactory.createInstance("com.sun.star.text.TextGraphicObject"); //$NON-NLS-1$
// Creating TextContent for GraphicObject
XTextContent graphicContent = UnoRuntime.queryInterface(XTextContent.class, graphicObject);
// Creating bitmap container service
XNameContainer bitmapContainer = odtEditor.getBitmapTable(imageFilePath);
// Inserting image to the container
PropertySetUtil.setProperty(graphicContent, "AnchorType", TextContentAnchorType.AT_CHARACTER); //$NON-NLS-1$
if (bitmapContainer != null) {
PropertySetUtil.setProperty(graphicContent, "GraphicURL", bitmapContainer.getByName(imageFilePath)); //$NON-NLS-1$
}
graphicContent = ImageUtil.resizeImage(graphicContent, imageFilePath, odtEditor.getXTextDocument(), odtEditor.getXMultiComponentFactory(), odtEditor.getXComponentContext());
XPropertySet graphicPropSet = createXPropertySet(graphicContent);
Object heightValue = graphicPropSet.getPropertyValue("Height"); //$NON-NLS-1$
Object widthValue = graphicPropSet.getPropertyValue("Width"); //$NON-NLS-1$
XPropertySet textFrameSet = createXPropertySet(textFrame);
textFrameSet.setPropertyValue("Height", heightValue);// TODO don't work, and should be on the next level... //$NON-NLS-1$
textFrameSet.setPropertyValue("Width", widthValue); //$NON-NLS-1$
xTextCursor.getText().insertTextContent(xTextCursor, textFrame, false);
XTextCursor localCursor = textFrame.getText().createTextCursor();
XParagraphCursor paragraphCursor = UnoRuntime.queryInterface(XParagraphCursor.class, localCursor);
XPropertySet paraSet = createXPropertySet(paragraphCursor);
paraSet.setPropertyValue("ParaStyleName", "Illustration");// it works!!! in fact we can't push style which have not been declared and which don't exist by default //$NON-NLS-1$ //$NON-NLS-2$
localCursor.getText().insertTextContent(localCursor, graphicContent, false);
localCursor.gotoEnd(true);
localCursor.getText().insertString(localCursor, "Illustration", false); //$NON-NLS-1$
// code to add the number of the illustration: doesn't work yet
// it is probably not a GetReference I should use...
// Object getReferenceObject = xMultiServiceFactory.createInstance("com.sun.star.text.textfield.GetReference");
//
//
// XTextContent xRef = UnoRuntime.queryInterface(XTextContent.class, getReferenceObject);
// XPropertySet xRefPropertySet = UnoRuntime.queryInterface(XPropertySet.class, getReferenceObject);
//
//
// xRefPropertySet.setPropertyValue("SourceName", "Illustration");// I get <text:sequence-ref
// xRefPropertySet.setPropertyValue("ReferenceFieldPart", ReferenceFieldPart.UP_DOWN);
// xRefPropertySet.setPropertyValue("ReferenceFieldSource", ReferenceFieldSource.SEQUENCE_FIELD);
// xRefPropertySet.setPropertyValue("SequenceNumber", (short) 2);
// localCursor.getText().insertControlCharacter(localCursor, ControlCharacter.LINE_BREAK, false);// add style around it
// localCursor.getText().insertTextContent(localCursor, xRef, false);
if (null != caption && false == caption.isEmpty()) {
StringBuilder builder = new StringBuilder();
builder.append(": "); //$NON-NLS-1$
builder.append(caption);
localCursor.getText().insertString(localCursor, builder.toString(), false);
}
endParagraph(xTextCursor);
fileIOService.removeFile(new File(image.getPath()));
} catch (Exception e) {
Activator.log.error(e);
}
}
private static final XPropertySet createXPropertySet(final Object object) {
return UnoRuntime.queryInterface(XPropertySet.class, object);
}
/**
* End line.
*
* @param xTextCursor
*/
public static void endLine(XTextCursor xTextCursor) {
addControlCharacter(xTextCursor, ControlCharacter.LINE_BREAK);
}
/**
* End paragraph.
*
* @param xTextCursor
*/
public static void endParagraph(XTextCursor xTextCursor) {
addControlCharacter(xTextCursor, ControlCharacter.PARAGRAPH_BREAK);
}
/**
* Insert content from text file.
*
* @param xTextCursor
* @param fileURL
*/
public static void insertTextFile(XTextCursor xTextCursor, String fileURL) {
xTextCursor.gotoEnd(false);
XDocumentInsertable inserter = UnoRuntime.queryInterface(XDocumentInsertable.class, xTextCursor);
if (inserter != null) {
try {
inserter.insertDocumentFromURL(fileURL, new PropertyValue[0]);
} catch (IllegalArgumentException | IOException e) {
Activator.log.error(e);
}
}
}
/**
* Remove HTML/RichText tags from string.
*
* @param text
* @return
*/
public static String removeHTMLTags(String text) {
return text.replaceAll(HTML_TAG_REGEX, ""); //$NON-NLS-1$
}
/**
* Add Paragraph with Rich Text format.
*
* @param xTextCursor
* @param text
*/
public static void addParagraphWithRichText(XTextCursor xTextCursor, String text) {
// Split the text for retrieving the first part without rich text
String[] noRichText = text.split(HTML_TAG_REGEX, 2);
String richText = text;
// Verify if there is the first part of text without rich text
if (noRichText.length != 0) {
// Add the first part of text without rich text
addText(xTextCursor, noRichText[0]);
// Retrieve the part of text with rich text
richText = text.replaceFirst(noRichText[0], ""); //$NON-NLS-1$
}
richText = richText.replaceAll("&nbsp;", " "); //$NON-NLS-1$ //$NON-NLS-2$
// Create file with the part of text with rich text
String location = ResourcesPlugin.getWorkspace().getRoot().getLocation().toString();
StringBuilder pathName = new StringBuilder();
pathName.append(location);
pathName.append(File.separator);
pathName.append("tmp_ParagraphWithRichText_"); //$NON-NLS-1$
pathName.append(new Date().getTime());
pathName.append("."); //$NON-NLS-1$
pathName.append(HTML_EXTENSION);
File file = fileIOService.createFile(pathName.toString(), richText);
String fileURL = odtFileIOService.getFileURL(file);
// Insert rich text content from the file
insertTextFile(xTextCursor, fileURL);
// Remove file with the part of text with rich text
fileIOService.removeFile(file);
endParagraph(xTextCursor);
}
/**
* Write Table.
*
* @param xTextCursor
* @param table
* the table contents
* @param odtEditor
*
*/
public static void writeTable(XTextCursor xTextCursor, AbstractTable table, ODTEditor odtEditor) {
// get number of rows and columns
int rowsNumber = table.getRowsNumber();
int colNumbers = table.getColumnsNumber();
if (rowsNumber <= 0 || colNumbers <= 0) {
return;
}
final List<Cell> allCells = table.getAllCells();
final int totalCells = allCells.size();
if (rowsNumber * colNumbers != totalCells) {
Activator.log.warn(NLS.bind("The number of cells in the table is not as excepted. We won't manage the table {0}.", table.getCaption())); //$NON-NLS-1$
return;
}
Activator.log.info(NLS.bind("Start the creation of the table {0} in LibreOffice", table.getCaption())); //$NON-NLS-1$
Activator.log.info(NLS.bind("--This table have {0} columns and {1} rows", colNumbers, rowsNumber)); //$NON-NLS-1$
try {
XMultiServiceFactory xMultiServiceFactory = odtEditor.getXMultiServiceFactory();
// create a text table
Object obj = xMultiServiceFactory.createInstance("com.sun.star.text.TextTable"); //$NON-NLS-1$
XTextTable textTable = UnoRuntime.queryInterface(XTextTable.class, obj);
// Initialize and add table
Activator.log.info("--Intializing LibreOffice Table."); //$NON-NLS-1$
textTable.initialize(rowsNumber, colNumbers);
Activator.log.info("--Add the new empty table to the the document"); //$NON-NLS-1$
addTextContent(xTextCursor, textTable);
// Set cell's contents
Activator.log.info("--Request Libreoffice to get the cells."); //$NON-NLS-1$
final List<String> cellNames = Arrays.asList(textTable.getCellNames());
if (cellNames.size() != allCells.size()) {
Activator.log.warn(NLS.bind("--The number of LibreOffice cells is {0} instead of {1}, as required", cellNames.size(), allCells.size())); //$NON-NLS-1$
}
// TODO : we should add something like a TableStyleProvider in the metamodel builtInTypes.
// A such element will be referenced by the DocumentGeneratorConfiguration or something else
// and each table element would have a method to be able to read it and returns the style to apply for it when we write the final document
Object headerBackGroundColor = 0xD4D4D4; // grey
final Iterator<String> libreOfficeCellNamesIter = cellNames.iterator();
final Iterator<Cell> tableCellIterator = allCells.iterator();
int i = 0;
while (libreOfficeCellNamesIter.hasNext() && tableCellIterator.hasNext()) {
final XText cellText = UnoRuntime.queryInterface(XText.class, textTable.getCellByName(libreOfficeCellNamesIter.next()));
final Cell cell = tableCellIterator.next();
if (cell instanceof TextCell) {
cellText.setString(((TextCell) cell).getText());
} else {
Activator.log.warn(NLS.bind("--The cell eClass {0} is not managed by the transcription", cell.eClass().getName())); //$NON-NLS-1$
}
switch (cell.getLocation()) {
case CORNER:
case COLUMN_HEADER:
case ROW_HEADER:
PropertySetUtil.setProperty(cellText, "BackColor", headerBackGroundColor); //$NON-NLS-1$
break;
case BODY:
default:
// do nothing
}
if (i % 100 == 0) {
Activator.log.info(NLS.bind("----Fill {0}/{1} cells-----", i, totalCells)); //$NON-NLS-1$
}
i++;
}
} catch (com.sun.star.uno.Exception e) {
Activator.log.error(e);
}
Activator.log.info(NLS.bind("Finish the creation of the table {0}", table.getCaption())); //$NON-NLS-1$
}
}