• Solutions
    • FERC XBRL Reporting
    • FDTA Financial Reporting
    • SEC Compliance
    • Windows Clipboard Management
    • Legato Scripting
  • Products
    • GoFiler Suite
    • XBRLworks
    • SEC Exhibit Explorer
    • SEC Extractor
    • Clipboard Scout
    • Legato
  • Education
    • Training
    • SEC and EDGAR Compliance
    • Legato Developers
  • Blog
  • Support
  • Skip to blog entries
  • Skip to archive page
  • Skip to right sidebar

Friday, October 20. 2017

LDC #56: Editing CSS Properties With Legato

In past blog posts, we’ve used Legato to remove HTML tags or to add new ones. One function we haven’t really explored is looking at a tag and changing its properties. Editing CSS properties with Legato is extremely powerful because it lets us change the document’s appearance in clean, interesting, and intelligent ways. This week’s script is a very basic but very powerful example of this concept.



We’ll begin by looking for the nearest block tag around our current cursor position and removing the padding and margins from it, thereby ensuring the text isn’t indented. This is a snap using the functions CSSGetProperties and CSSSetProperties. These functions retrieve an array of properties, and with that we can change what we want and set them back into the element without having to worry about parsing the CSS attributes ourselves. This is a huge time saver, as writing a CSS parser would be an extremely involved affair and take a very long time. 


This week’s script example:



//
//      GoFiler Legato Script - Remove Margins And Padding
//      --------------------------------------------------
//
//      Rev     10/16/2017
//
//      (c) 2017 Novaworks, LLC -- All rights reserved.
//
//      Will examine active HTML window to remove margins and padding
//
//      Notes:
//
//         -    Requires GoFiler 4.20a or later.


    void                run                     (int f_id, string mode);/* Call from Hook Processor             */
    boolean             is_block                (string item);          /* true if item is a block tag          */
    string              remove_spacing          (handle sgml);          /* modify the current element           */

                                                                        /****************************************/
void setup() {                                                          /* Called from Application Startup      */
                                                                        /****************************************/
    string              fnScript;                                       /* Us                                   */
    string              item[10];                                       /* Menu Item                            */
                                                                        /* ** Add Menu Item                     */
                                                                        /*  * Define Function                   */
    item["Code"] = "EXTENSION_REMOVE_MARGINS";                          /* Function Code                        */
    item["MenuText"] = "&Remove All Margins/Padding";                   /* Menu Text                            */
    item["Description"] = "<B>Remove Margins And Padding</B>\r\rRemove";/* Description (long)                   */
    item["Description"]+= " all margins and padding on block items.";   /*  * description                       */
    item["Class"] = "DocumentExtension";                                /* add to document toolbar              */
    fnScript = GetScriptFilename();                                     /* Get the script filename              */
    MenuAddFunction(item);                                              /* add function to menu                 */
    MenuSetHook(item["Code"], fnScript, "run");                         /* Set the Test Hook                    */
    QuickKeyRegister("Page View","1_KEY_CONTROL",                       /* set hotkey to ctrl 1                 */
        "EXTENSION_REMOVE_MARGINS");                                    /* set hotkey to ctrl 1                 */
    }                                                                   /* end setup                            */
                                                                        /****************************************/
void main() {                                                           /* Initialize from Hook Processor       */
                                                                        /****************************************/
    if (GetScriptParent()=="LegatoIDE"){                                /* if not running in IDE                */
      setup();                                                          /* Add to the menu                      */
      }                                                                 /*                                      */
    }                                                                   /* end setup                            */
                                                                        /****************************************/
void run(int f_id, string mode) {                                       /* Call from Hook Processor             */
                                                                        /****************************************/
    int                 ex,ey,sx,sy;                                    /* positional variables                 */
    string              element;                                        /* sgml element                         */
    handle              sgml;                                           /* sgml object                          */
    handle              edit_object;                                    /* edit object                          */
    handle              edit_window;                                    /* edit window handle                   */
                                                                        /*                                      */
    edit_window = GetActiveEditWindow();                                /* get handle to edit window            */
    if(IsError(edit_window)){                                           /* get active edit window               */
      MessageBox('x',"This is not an edit window.");                    /* display error                        */
      return;                                                           /* return                               */
      }                                                                 /*                                      */
    edit_object = GetEditObject(edit_window);                           /* create edit object                   */
    sgml = SGMLCreate(edit_object);                                     /* create sgml object                   */
    sx = GetCaretXPosition(edit_object);                                /* get cursor x pos                     */
    sy = GetCaretYPosition(edit_object);                                /* get cursor y pos                     */
    element = SGMLPreviousElement(sgml,sx,sy);                          /* get the first sgml element           */
    while(element != ""){                                               /* while element isn't empty            */
      if (is_block(element)){                                           /* check if the tag is blcok            */
        sx = SGMLGetItemPosSX(sgml);                                    /* get sx                               */
        sy = SGMLGetItemPosSY(sgml);                                    /* get sy                               */
        ex = SGMLGetItemPosEX(sgml);                                    /* get ex                               */
        ey = SGMLGetItemPosEY(sgml);                                    /* get ey                               */
        WriteSegment(edit_object,remove_spacing(sgml),sx,sy,ex,ey);     /* write new text                       */
        break;                                                          /* end the loop                         */
        }                                                               /*                                      */
      element = SGMLPreviousElement(sgml);                              /* get the next sgml element            */
      }                                                                 /*                                      */
    }                                                                   /* end setup                            */
                                                                        /****************************************/
string remove_spacing(handle sgml){                                     /* removes all spacing from the itme    */
                                                                        /****************************************/
    string              properties[];                                   /* properties to put into string        */
                                                                        /*                                      */
    properties = CSSGetProperties(sgml);                                /* get current properties of element    */
    properties["margin"] = "0";                                         /* set margin to zero                   */
    properties["padding"] = "0";                                        /* set margin to zero                   */
    properties["text-indent"] = "";                                     /* remove text indent                   */
    CSSSetProperties(sgml,properties);                                  /* set properties in SGML parser        */
    return SGMLToString(sgml);                                          /* return modified element tag          */
    }                                                                   /*                                      */
                                                                        /****************************************/
boolean is_block(string item){                                          /* true if item is a block tag          */
                                                                        /****************************************/
    if (FindInString(item,"<p")==0){                                    /* if it's a paragraph                  */
      return true;                                                      /* return true                          */
      }                                                                 /*                                      */
    if (FindInString(item,"<div")==0){                                  /* if it's a division                   */
      return true;                                                      /* return true                          */
      }                                                                 /*                                      */
    if (FindInString(item,"<td")==0){                                   /* if it's a table cell                 */
      return true;                                                      /* return true                          */
      }                                                                 /*                                      */
    if (FindInString(item,"<th")==0){                                   /* if it's a table header               */
      return true;                                                      /* return true                          */
      }                                                                 /*                                      */
    return false;                                                       /* return false                         */
    }


Let’s begin with the setup function. This is pretty standard, and we’ve covered almost everything here in depth in other blog posts. The menu item’s class is set to “DocumentExtension”, so it shows up on the document toolbar, which is only available in HTML files. The one new item is the QuickKeyRegister function call. This registers our function to the hotkey CTRL+1. Doing this overrides the existing hotkey, which is a hang table function, with our new function. You can use the QuickKeyRegister function to set custom hotkeys for any function that is defined in the specific view you’re working in. CTRL+1 is a specific hotkey for page view, so we can override it. Conversely, CTRL+O is the generic open file function, which isn’t specific to page view, so it cannot be overridden.




                                                                        /****************************************/
void setup() {                                                          /* Called from Application Startup      */
                                                                        /****************************************/
    string              fnScript;                                       /* Us                                   */
    string              item[10];                                       /* Menu Item                            */
                                                                        /* ** Add Menu Item                     */
                                                                        /*  * Define Function                   */
    item["Code"] = "EXTENSION_REMOVE_MARGINS";                          /* Function Code                        */
    item["MenuText"] = "&Remove All Margins/Padding";                   /* Menu Text                            */
    item["Description"] = "<B>Remove Margins And Padding</B>\r\rRemove";/* Description (long)                   */
    item["Description"]+= " all margins and padding on block items.";   /*  * description                       */
    item["Class"] = "DocumentExtension";                                /* add to document toolbar              */
    fnScript = GetScriptFilename();                                     /* Get the script filename              */
    MenuAddFunction(item);                                              /* add function to menu                 */
    MenuSetHook(item["Code"], fnScript, "run");                         /* Set the Test Hook                    */
    QuickKeyRegister("Page View","1_KEY_CONTROL",                       /* set hotkey to ctrl 1                 */
        "EXTENSION_REMOVE_MARGINS");                                    /* set hotkey to ctrl 1                 */
    }                                                                   /* end setup                            */



Our main function here is pretty sparse. It simply checks if the script is running in the IDE, and if so, it runs setup.



                                                                        /****************************************/
void main() {                                                           /* Initialize from Hook Processor       */
                                                                        /****************************************/
    if (GetScriptParent()=="LegatoIDE"){                                /* if not running in IDE                */
      setup();                                                          /* Add to the menu                      */
      }                                                                 /*                                      */
    }                                                                   /* end setup                            */



The run function is called from the menu hook. It uses the GetActiveEditWindow function to get the current window. If it was unable to get the window, it displays a message and exits. Otherwise, it gets the Edit Object associated with that window with the GetEditObject function, creates an SGML parser with the SGMLCreate function, retrieves the cursor positions for the current Edit Object, and uses the SGMLGetPreviousElement function to get the last HTML tag.



                                                                        /****************************************/
void run(int f_id, string mode) {                                       /* Call from Hook Processor             */
                                                                        /****************************************/
    int                 ex,ey,sx,sy;                                    /* positional variables                 */
    string              element;                                        /* sgml element                         */
    handle              sgml;                                           /* sgml object                          */
    handle              edit_object;                                    /* edit object                          */
    handle              edit_window;                                    /* edit window handle                   */
    string              text;                                           /* closing element of sgml object       */
                                                                        /*                                      */
    edit_window = GetActiveEditWindow();                                /* get handle to edit window            */
    if(IsError(edit_window)){                                           /* get active edit window               */
      MessageBox('x',"This is not an edit window.");                    /* display error                        */
      return;                                                           /* return                               */
      }                                                                 /*                                      */
    edit_object = GetEditObject(edit_window);                           /* create edit object                   */
    sgml = SGMLCreate(edit_object);                                     /* create sgml object                   */
    sx = GetCaretXPosition(edit_object);                                /* get cursor x pos                     */
    sy = GetCaretYPosition(edit_object);                                /* get cursor y pos                     */
    element = SGMLPreviousElement(sgml,sx,sy);                          /* get the first sgml element           */


While we have an element to analyze, we can check the element to see if it’s a block element with the function is_block . The only tags we’re interested are block tags, like paragraphs, divisions, table cells, and table headers. There are others, but those just listed are the common ones used in EDGAR filings. If our element is not a block tag, we can get the previous tag, and check again. If this one is a block tag, we can obtain the start and end positions of it and use the WriteSegment function to write out a copy of the tag, modified by the function remove_spacing.



    while(element != ""){                                               /* while element isn't empty            */
      if (is_block(element)){                                           /* check if the tag is blcok            */
        sx = SGMLGetItemPosSX(sgml);                                    /* get sx                               */
        sy = SGMLGetItemPosSY(sgml);                                    /* get sy                               */
        ex = SGMLGetItemPosEX(sgml);                                    /* get ex                               */
        ey = SGMLGetItemPosEY(sgml);                                    /* get ey                               */
        WriteSegment(edit_object,remove_spacing(sgml),sx,sy,ex,ey);     /* write new text                       */
        break;                                                          /* end the loop                         */
        }                                                               /*                                      */
      element = SGMLPreviousElement(sgml);                              /* get the next sgml element            */
      }                                                                 /*                                      */
    }                                                                   /* end setup                            */



The remove_spacing function performs the actual modifications of our CSS properties. We can use the SDK function CSSGetProperties to get an array of the CSS properties applying to this tag. Each item in the array is identified by a key:value pair, with the key being the CSS property name and the value being the value of the property. Our goal here is to set the margin, padding, and text-indent and them to zero, so we can set those properties into our array, and then use CSSSetProperties to change the SGML object’s element. After the element is changed, the SGMLToString function gets a string value for the current element that we just modified and returns it, so our run function can write it out with the WriteSegment function.



                                                                        /****************************************/
string remove_spacing(handle sgml){                                     /* removes all spacing from the itme    */
                                                                        /****************************************/
    string              properties[];                                   /* properties to put into string        */
                                                                        /*                                      */
    properties = CSSGetProperties(sgml);                                /* get current properties of element    */
    properties["margin"] = "0";                                         /* set margin to zero                   */
    properties["padding"] = "0";                                        /* set margin to zero                   */
    properties["text-indent"] = "";                                     /* remove text indent                   */
    CSSSetProperties(sgml,properties);                                  /* set properties in SGML parser        */
    return SGMLToString(sgml);                                          /* return modified element tag          */
    }                                                                   /*                                      */


Our is_block function is very simple. Its purpose is to examine the SGML tag it’s given, and if the tag begins with something that looks like a block tag, the function returns true. Otherwise, it returns false. In later versions of GoFiler, this will likely be replaced with a Legato SDK function , so there will be no need for the user to define something like this.



                                                                        /****************************************/
boolean is_block(string item){                                          /* true if item is a block tag          */
                                                                        /****************************************/
    if (FindInString(item,"<p")==0){                                    /* if it's a paragraph                  */
      return true;                                                      /* return true                          */
      }                                                                 /*                                      */
    if (FindInString(item,"<div")==0){                                  /* if it's a division                   */
      return true;                                                      /* return true                          */
      }                                                                 /*                                      */
    if (FindInString(item,"<td")==0){                                   /* if it's a table cell                 */
      return true;                                                      /* return true                          */
      }                                                                 /*                                      */
    if (FindInString(item,"<th")==0){                                   /* if it's a table header               */
      return true;                                                      /* return true                          */
      }                                                                 /*                                      */
    return false;                                                       /* return false                         */
    }


This simple script is a good example of how to use Legato to edit CSS properties in HTML objects. This could easily be expanded to do a lot more than edit margins. It could be used to store paragraph or cell style presets, for example, that would increase the speed of formatting text. It could also be modified to work on multiple selection types, such as highlighting a table row and removing all the indents or padding on the entire row instead of one cell at a time. It could be used to search through a document and find any tables or paragraphs that are aligned center,and replace them with “margin auto” for inline XBRL compliance. The possibilities are really endless, and this is just scratching the surface.


 


Steven Horowitz has been working for Novaworks for over five years as a technical expert with a focus on EDGAR HTML and XBRL. Since the creation of the Legato language in 2015, Steven has been developing scripts to improve the GoFiler user experience. He is currently working toward a Bachelor of Sciences in Software Engineering at RIT and MCC.

Additional Resources

Novaworks’ Legato Resources

Legato Script Developers LinkedIn Group

Primer: An Introduction to Legato 



Posted by
Steven Horowitz
in Development at 16:28
Trackbacks
Trackback specific URI for this entry

No Trackbacks

Comments
Display comments as (Linear | Threaded)
No comments
Add Comment
Enclosing asterisks marks text as bold (*word*), underscore are made via _word_.
Standard emoticons like :-) and ;-) are converted to images.
E-Mail addresses will not be displayed and will only be used for E-Mail notifications.

To prevent automated Bots from commentspamming, please enter the string you see in the image below in the appropriate input box. Your comment will only be submitted if the strings match. Please ensure that your browser supports and accepts cookies, or your comment cannot be verified correctly.
CAPTCHA

 
   
 

Quicksearch

Categories

  • XML Accounting
  • XML AICPA News
  • XML FASB News
  • XML GASB News
  • XML IASB News
  • XML Development
  • XML Events
  • XML FERC
  • XML eForms News
  • XML FERC Filing Help
  • XML Filing Technology
  • XML Information Technology
  • XML Investor Education
  • XML MSRB
  • XML EMMA News
  • XML FDTA
  • XML MSRB Filing Help
  • XML Novaworks News
  • XML GoFiler Online Updates
  • XML GoFiler Updates
  • XML XBRLworks Updates
  • XML SEC
  • XML Corporation Finance
  • XML DERA
  • XML EDGAR News
  • XML Investment Management
  • XML SEC Filing Help
  • XML XBRL
  • XML Data Quality Committee
  • XML GRIP Taxonomy
  • XML IFRS Taxonomy
  • XML US GAAP Taxonomy

Calendar

Back May '25 Forward
Mo Tu We Th Fr Sa Su
Sunday, May 18. 2025
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  

Feeds

  • XML
Sign Up Now
Get SEC news articles and blog posts delivered monthly to your inbox!
Based on the s9y Bulletproof template framework

Compliance

  • FERC
  • EDGAR
  • EMMA

Software

  • GoFiler Suite
  • SEC Exhibit Explorer
  • SEC Extractor
  • XBRLworks
  • Legato Scripting

Company

  • About Novaworks
  • News
  • Site Map
  • Support

Follow Us:

  • LinkedIn
  • YouTube
  • RSS
  • Newsletter
  • © 2024 Novaworks, LLC
  • Privacy
  • Terms of Use
  • Trademarks and Patents
  • Contact Us