package org.jabref.logic.openoffice; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jabref.logic.layout.Layout; import org.jabref.model.database.BibDatabase; import org.jabref.model.entry.BibEntry; import com.sun.star.beans.PropertyVetoException; import com.sun.star.beans.UnknownPropertyException; import com.sun.star.beans.XPropertySet; import com.sun.star.lang.IllegalArgumentException; import com.sun.star.lang.WrappedTargetException; import com.sun.star.text.ControlCharacter; import com.sun.star.text.XParagraphCursor; import com.sun.star.text.XText; import com.sun.star.text.XTextCursor; import com.sun.star.uno.UnoRuntime; /** * Utility methods for processing OO Writer documents. */ public class OOUtil { private static final String CHAR_STRIKEOUT = "CharStrikeout"; private static final String CHAR_UNDERLINE = "CharUnderline"; private static final String PARA_STYLE_NAME = "ParaStyleName"; private static final String CHAR_CASE_MAP = "CharCaseMap"; private static final String CHAR_POSTURE = "CharPosture"; private static final String CHAR_WEIGHT = "CharWeight"; private static final String CHAR_ESCAPEMENT_HEIGHT = "CharEscapementHeight"; private static final String CHAR_ESCAPEMENT = "CharEscapement"; public enum Formatting { BOLD, ITALIC, SMALLCAPS, SUPERSCRIPT, SUBSCRIPT, UNDERLINE, STRIKEOUT, MONOSPACE } private static final Pattern HTML_TAG = Pattern.compile("</?[a-z]+>"); private static final String UNIQUEFIER_FIELD = "uniq"; private OOUtil() { // Just to hide the public constructor } /** * Insert a reference, formatted using a Layout, at the position of a given cursor. * @param text The text to insert in. * @param cursor The cursor giving the insert location. * @param layout The Layout to format the reference with. * @param parStyle The name of the paragraph style to use. * @param entry The entry to insert. * @param database The database the entry belongs to. * @param uniquefier Uniqiefier letter, if any, to append to the entry's year. */ public static void insertFullReferenceAtCurrentLocation(XText text, XTextCursor cursor, Layout layout, String parStyle, BibEntry entry, BibDatabase database, String uniquefier) throws UndefinedParagraphFormatException, UnknownPropertyException, PropertyVetoException, WrappedTargetException, IllegalArgumentException { // Backup the value of the uniq field, just in case the entry already has it: Optional<String> oldUniqVal = entry.getField(UNIQUEFIER_FIELD); // Set the uniq field with the supplied uniquefier: if (uniquefier == null) { entry.clearField(UNIQUEFIER_FIELD); } else { entry.setField(UNIQUEFIER_FIELD, uniquefier); } // Do the layout for this entry: String formattedText = layout.doLayout(entry, database); // Afterwards, reset the old value: if (oldUniqVal.isPresent()) { entry.setField(UNIQUEFIER_FIELD, oldUniqVal.get()); } else { entry.clearField(UNIQUEFIER_FIELD); } // Insert the formatted text: OOUtil.insertOOFormattedTextAtCurrentLocation(text, cursor, formattedText, parStyle); } /** * Insert a text with formatting indicated by HTML-like tags, into a text at * the position given by a cursor. * @param text The text to insert in. * @param cursor The cursor giving the insert location. * @param lText The marked-up text to insert. * @param parStyle The name of the paragraph style to use. * @throws WrappedTargetException * @throws PropertyVetoException * @throws UnknownPropertyException * @throws IllegalArgumentException */ public static void insertOOFormattedTextAtCurrentLocation(XText text, XTextCursor cursor, String lText, String parStyle) throws UndefinedParagraphFormatException, UnknownPropertyException, PropertyVetoException, WrappedTargetException, IllegalArgumentException { XParagraphCursor parCursor = UnoRuntime.queryInterface( XParagraphCursor.class, cursor); XPropertySet props = UnoRuntime.queryInterface( XPropertySet.class, parCursor); try { props.setPropertyValue(PARA_STYLE_NAME, parStyle); } catch (IllegalArgumentException ex) { throw new UndefinedParagraphFormatException(parStyle); } List<Formatting> formatting = new ArrayList<>(); // We need to extract formatting. Use a simple regexp search iteration: int piv = 0; Matcher m = OOUtil.HTML_TAG.matcher(lText); while (m.find()) { String currentSubstring = lText.substring(piv, m.start()); if (!currentSubstring.isEmpty()) { OOUtil.insertTextAtCurrentLocation(text, cursor, currentSubstring, formatting); } String tag = m.group(); // Handle tags: if ("<b>".equals(tag)) { formatting.add(Formatting.BOLD); } else if ("</b>".equals(tag)) { formatting.remove(Formatting.BOLD); } else if ("<i>".equals(tag) || "<em>".equals(tag)) { formatting.add(Formatting.ITALIC); } else if ("</i>".equals(tag) || "</em>".equals(tag)) { formatting.remove(Formatting.ITALIC); } else if ("<tt>".equals(tag)) { formatting.add(Formatting.MONOSPACE); } else if ("</tt>".equals(tag)) { formatting.remove(Formatting.MONOSPACE); } else if ("<smallcaps>".equals(tag)) { formatting.add(Formatting.SMALLCAPS); } else if ("</smallcaps>".equals(tag)) { formatting.remove(Formatting.SMALLCAPS); } else if ("<sup>".equals(tag)) { formatting.add(Formatting.SUPERSCRIPT); } else if ("</sup>".equals(tag)) { formatting.remove(Formatting.SUPERSCRIPT); } else if ("<sub>".equals(tag)) { formatting.add(Formatting.SUBSCRIPT); } else if ("</sub>".equals(tag)) { formatting.remove(Formatting.SUBSCRIPT); } else if ("<u>".equals(tag)) { formatting.add(Formatting.UNDERLINE); } else if ("</u>".equals(tag)) { formatting.remove(Formatting.UNDERLINE); } else if ("<s>".equals(tag)) { formatting.add(Formatting.STRIKEOUT); } else if ("</s>".equals(tag)) { formatting.remove(Formatting.STRIKEOUT); } piv = m.end(); } if (piv < lText.length()) { OOUtil.insertTextAtCurrentLocation(text, cursor, lText.substring(piv), formatting); } cursor.collapseToEnd(); } public static void insertParagraphBreak(XText text, XTextCursor cursor) throws IllegalArgumentException { text.insertControlCharacter(cursor, ControlCharacter.PARAGRAPH_BREAK, true); cursor.collapseToEnd(); } public static void insertTextAtCurrentLocation(XText text, XTextCursor cursor, String string, List<Formatting> formatting) throws UnknownPropertyException, PropertyVetoException, WrappedTargetException, IllegalArgumentException { text.insertString(cursor, string, true); // Access the property set of the cursor, and set the currently selected text // (which is the string we just inserted) to be bold XPropertySet xCursorProps = UnoRuntime.queryInterface( XPropertySet.class, cursor); if (formatting.contains(Formatting.BOLD)) { xCursorProps.setPropertyValue(CHAR_WEIGHT, com.sun.star.awt.FontWeight.BOLD); } else { xCursorProps.setPropertyValue(CHAR_WEIGHT, com.sun.star.awt.FontWeight.NORMAL); } if (formatting.contains(Formatting.ITALIC)) { xCursorProps.setPropertyValue(CHAR_POSTURE, com.sun.star.awt.FontSlant.ITALIC); } else { xCursorProps.setPropertyValue(CHAR_POSTURE, com.sun.star.awt.FontSlant.NONE); } if (formatting.contains(Formatting.SMALLCAPS)) { xCursorProps.setPropertyValue(CHAR_CASE_MAP, com.sun.star.style.CaseMap.SMALLCAPS); } else { xCursorProps.setPropertyValue(CHAR_CASE_MAP, com.sun.star.style.CaseMap.NONE); } // TODO: the <monospace> tag doesn't work /* if (formatting.contains(Formatting.MONOSPACE)) { xCursorProps.setPropertyValue("CharFontPitch", com.sun.star.awt.FontPitch.FIXED); } else { xCursorProps.setPropertyValue("CharFontPitch", com.sun.star.awt.FontPitch.VARIABLE); } */ if (formatting.contains(Formatting.SUBSCRIPT)) { xCursorProps.setPropertyValue(CHAR_ESCAPEMENT, (byte) -101); xCursorProps.setPropertyValue(CHAR_ESCAPEMENT_HEIGHT, (byte) 58); } else if (formatting.contains(Formatting.SUPERSCRIPT)) { xCursorProps.setPropertyValue(CHAR_ESCAPEMENT, (byte) 101); xCursorProps.setPropertyValue(CHAR_ESCAPEMENT_HEIGHT, (byte) 58); } else { xCursorProps.setPropertyValue(CHAR_ESCAPEMENT, (byte) 0); xCursorProps.setPropertyValue(CHAR_ESCAPEMENT_HEIGHT, (byte) 100); } if (formatting.contains(Formatting.UNDERLINE)) { xCursorProps.setPropertyValue(CHAR_UNDERLINE, com.sun.star.awt.FontUnderline.SINGLE); } else { xCursorProps.setPropertyValue(CHAR_UNDERLINE, com.sun.star.awt.FontUnderline.NONE); } if (formatting.contains(Formatting.STRIKEOUT)) { xCursorProps.setPropertyValue(CHAR_STRIKEOUT, com.sun.star.awt.FontStrikeout.SINGLE); } else { xCursorProps.setPropertyValue(CHAR_STRIKEOUT, com.sun.star.awt.FontStrikeout.NONE); } cursor.collapseToEnd(); } public static void insertTextAtCurrentLocation(XText text, XTextCursor cursor, String string, String parStyle) throws WrappedTargetException, PropertyVetoException, UnknownPropertyException, UndefinedParagraphFormatException { text.insertString(cursor, string, true); XParagraphCursor parCursor = UnoRuntime.queryInterface( XParagraphCursor.class, cursor); // Access the property set of the cursor, and set the currently selected text // (which is the string we just inserted) to be bold XPropertySet props = UnoRuntime.queryInterface( XPropertySet.class, parCursor); try { props.setPropertyValue(PARA_STYLE_NAME, parStyle); } catch (IllegalArgumentException ex) { throw new UndefinedParagraphFormatException(parStyle); } cursor.collapseToEnd(); } public static Object getProperty(Object o, String property) throws UnknownPropertyException, WrappedTargetException { XPropertySet props = UnoRuntime.queryInterface( XPropertySet.class, o); return props.getPropertyValue(property); } }