API/Samples/Groovy/Writer/CleanUpForHTML

    From The Document Foundation Wiki
    < API‎ | Samples‎ | Groovy

    About the CleanUpForHTML Example

    This is an example shows how to access properties in a Groovy macro or extension using a category (aka "mixin") UNO API helper. It is the CleanUpForHTML macro used in IFCX Wings to compensate for issues with the HTML generated by OpenOffice for Writer documents.


    The Code

    /**
     * IFCX Wings - CleanUpForHTML.groovy macro.
     * Copyright 2007-2008 by James P. White.  All rights reserved.
     * mailto:jim@pagesmiths.com
     * http://www.ifcx.org/
     *
     * This software may be redistributed under the term of the GNU GENERAL PUBLIC LICENSE Version 2.
     * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
     *
     * For expert professional services and/or distribution under other terms, contact the author:
     *
     * James P. White
     * mailto:jim@pagesmiths.com
     * 26272 Summerhill Lane
     * Laguna Hills, CA
     * USA 92653
     *
     */
    
    package org.ifcx.wings.openoffice
    
    /*
        Import standard LibreOffice API classes. For more information on
        these classes and the LibreOffice API, see the LibreOffice
        Developers Guide at:
    
        http://api.openoffice.org/
    */
    
    import com.sun.star.uno.UnoRuntime
    import com.sun.star.uno.XInterface
    
    /*
        Import XScriptContext class. An instance of this class is available
        to all BeanShell scripts in the global variable "XSCRIPTCONTEXT". This
        variable can be used to access the document for which this script
        was invoked.
    
        Methods available are:
    
            XSCRIPTCONTEXT.getDocument() returns XModel
            XSCRIPTCONTEXT.getDesktop() returns XDesktop
            XSCRIPTCONTEXT.getComponentContext() returns XComponentContext
    
        For more information on using this class see the scripting
        developer guides at:
    
            http://framework.openoffice.org/scripting/index.html
    */
    
    import com.sun.star.beans.XPropertySet
    
    import com.sun.star.text.XTextRange
    import com.sun.star.text.XTextViewCursorSupplier
    import com.sun.star.view.XViewCursor
    import com.sun.star.text.XParagraphCursor
    
    class UnoCategory {
        public static Object uno(Object unoObj, Class clazz) {UnoRuntime.queryInterface(clazz, unoObj)}
    
        public static Object getAt(XPropertySet pset, String pname) {pset.getPropertyValue(pname)}
    
        public static void putAt(XPropertySet pset, String pname, Object newValue) {pset.setPropertyValue(pname, newValue)}
    }
    
    /**
     * In order for the paragraph style to apply to an entire code block (a run of code
     * paragraphs that Wings evaluates as a single block), we find all multiple line
     * code blocks and make sure that they are a single paragraph.  It is normal for
     * code blocks to consist of multiple paragraphs because pressing 'return' starts
     * a new paragraph.
     */
    def doMergeCode(document) {
        def viewCursor = document.currentController.uno(XTextViewCursorSupplier).viewCursor
        // We get the XText from the cursor so that if it is from a frame or table it'll work.
        def docText = viewCursor.text
        XParagraphCursor textCursor = docText.createTextCursorByRange(viewCursor).uno(XParagraphCursor)
    
        // It's a "paragraph" cursor, but may be positioned inside one.
        // We assume that since "GroovyCode" is supposed to be a "paragragh" style
        // that we'll still be in "GroovyCode" when we go to the beginning.
    
        textCursor.gotoStartOfParagraph(false)
    
        while (true) {
            // Code blocks are runs of paragraphs whose style name ends with "Code".
            while (!(textCursor.uno(XPropertySet)['ParaStyleName'] =~ /.*Code$/)) {
    //            println "Skipping ahead."
                if (!textCursor.gotoNextParagraph(false)) {
                    // Can't find another paragraph, so we're done.
                    return;
                }
            }
    
            XParagraphCursor codeRange = docText.createTextCursorByRange(textCursor).uno(XParagraphCursor)
    
            def codeStyle = textCursor.uno(XPropertySet)['ParaStyleName']
    
            // Move forward paragraphs until the end of the document,
            // or we find a paragraph that isn't "GroovyCode" then move back one.
    
            while (textCursor.gotoNextParagraph(false)) {
                if (textCursor.uno(XPropertySet)['ParaStyleName'] != codeStyle) {
                    if (!textCursor.gotoPreviousParagraph(false)) doError("Couldn't move back (end)!");
                    break;
                }
            }
    
            // The cursor is at the start of the paragraph, move it to the end.
            textCursor.gotoEndOfParagraph(false);
    
            // Then select all that text as our code.
            codeRange.gotoRange(textCursor.end, true)
    
            def codeString = codeRange.string
    
    //        println codeString
    
            // the TextRange.getString() will have replaced paragraph end characters with '\n',
            // which is the same as line breaks within a paragraph.  So we don't really know
            // whether we have multiple paragraphs or not, but we don't really care.
    
            if (codeString.contains('\n')) {
    //            println "Replacing some"
                // There are multiple lines, so we replace the paragraph(s) with a single
                // paragraph of multiple lines.
                codeRange.text.insertString(codeRange, codeString, true)
                // The paragraph style is unchanged so we don't need to do anything about that.
            }
    
            if (!textCursor.gotoNextParagraph(false))
                return
        }
    }  // def doMergeCode...
    
    /**
     * When saving as HTML, OpenOffice doesn't take into consideration that all whitespace runs
     * will be collapsed into a single space (or line break).  That is disaster for most program
     * text (code and output) which typically depend on leading whitespace on lines to be preserved.
     * So what this method does is replace all leading whitespace on each line of a style that
     * looks like it is used for Wings (those whose nanes end with "Code", "Result", "Exception",
     * "Output", "Error", or "Input") and replace it with non-breaking spaces.
     */
    def doFixFormatting(document) {
        def viewCursor = document.currentController.uno(XTextViewCursorSupplier).viewCursor
        // We get the XText from the cursor so that if it is from a frame or table it'll work.
        def docText = viewCursor.text
        XParagraphCursor textCursor = docText.createTextCursorByRange(viewCursor).uno(XParagraphCursor)
    
        // It's a "paragraph" cursor, but may be positioned inside one.
        // We assume that since "GroovyCode" is supposed to be a "paragragh" style
        // that we'll still be in "GroovyCode" when we go to the beginning.
    
        textCursor.gotoStartOfParagraph(false)
    
        while (true) {
            String codeStyle = textCursor.uno(XPropertySet)['ParaStyleName']
    
            // Match style names of interest, if no match then it just falls thru.
            switch (codeStyle) {
                case ~/.*Code$/:
                case ~/.*Result$/:
                case ~/.*Exception$/:
                case ~/.*Output$/:
                case ~/.*Errors$/:
                case ~/.*Input$/:
    
                    XParagraphCursor codeRange = docText.createTextCursorByRange(textCursor).uno(XParagraphCursor)
    
                    // The cursor is at the start of the paragraph, move it to the end.
                    textCursor.gotoEndOfParagraph(false);
    
                    // Then select all that text as our code.
                    codeRange.gotoRange(textCursor.end, true)
    
                    def codeString = codeRange.string
    
    //                println codeString
    
                    if (codeString.contains(' ') || codeString.contains('\t')) {
    //                    println "Replacing some"
    
                        String[] lines = codeString.split('\n')
    
                        lines.length.times {
                            String line = lines[it]
    
                            // Treat tabs as eight spaces.
                            line = line.replace('\t', '        ')
    
                            if (line.startsWith(' ')) {
                                char[] chars = line.toCharArray()
    
                                // Replace the leading spaces with non-breaking spaces.
                                for (int i = 0; i < chars.length; ++i) {
                                    if (' ' != chars[i]) break
                                    chars[i] = '\u00A0'
                                }
    
                                lines[it] = new String(chars)
                            }
                        }
    
                        codeRange.text.insertString(codeRange, lines.join('\n'), true)
                    }
            }
    
    //        println "Skipping ahead."
            if (!textCursor.gotoNextParagraph(false)) {
                return;
            }
    
        }
    }  // def doFixFormatting...
    
    ////////////// BEGIN //////////////
    // This is the XModel for the currently active document.
    def document = XSCRIPTCONTEXT.document
    
    use(UnoCategory) {
        doMergeCode(document)
        doFixFormatting(document)
    }
    
    0

    Apache Feather Logo.svg

    This work is licensed under the Apache License version 2.0