• 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 27. 2017

LDC #57: More CSS Property Editing With Legato

Last week, we explored editing SGML properties with Legato. By using some of the Legato SDK functions, we can read the properties of an SGML tag and change those properties to new values. This is pretty simple and straightforward. However, what if we want to do something based on a particular setting? For example, what if we want to employ an “Indent Body of Paragraph” function, where the first line of the paragraph doesn’t move but the subsequent lines are indented? We can’t simply read the indent value and add to it because we cannot guarantee that the same unit of measurement is being used. We need to read the unit, normalize it into a standard unit, add to it, convert it back to the original measurement style, and write it back out. It’s a bit more involved than simply setting properties, but it lets us perform some pretty interesting operations.


When we read our value from the SGML parser, it is going to come in as a PVALUE, type of a dword, which is a bitwise arrangement of a parameter type and parameter data. This allows us to store the the data type, as well as the data value, as a single dword instead of multiple variables. Legato also has the ability to translate these PVALUE variables into twips, which are a standardized unit of measurement. A twip is 1/20 of a point (pt) or 1/1440th of an inch (in). So in our case, we can get the measurement of an attribute as a PVALUE, convert it to a twip, increment it, convert it back, and set it into our document.


Let’s take a look at our example script, which indents the body of a paragraph using this method:



//
//      GoFiler Legato Script - Indent Second Line Text
//      --------------------------------------------------
//
//      Rev     10/16/2017
//
//      (c) 2017 Novaworks, LLC -- All rights reserved.
//
//      Will examine active HTML window to indent the second line of it 20pt (400 twips)
//
//      Notes:
//
//         -    Requires GoFiler 4.20a or later.


#define                 INDENT_TWIPS                    400

    void                run_indent                      (int f_id, string mode);
    void                run_outdent                     (int f_id, string mode);
    void                indent_para                     (int f_id, string mode, int weight);
    string              do_indent                       (handle sgml, int weight);

                                                                        /****************************************/
int setup() {                                                           /* Called from Application Startup      */
                                                                        /****************************************/
    string              fnScript;                                       /* Us                                   */
    string              item[10];                                       /* Menu Item                            */
    int                 rc;                                             /* Return Code                          */
                                                                        /* ** Add Menu Item                     */
                                                                        /*  * Define Function                   */
    item["Code"] = "EXTENSION_INDENT_BODY";                             /* Function Code                        */
    item["MenuText"] = "&Indent Paragraph Body";                        /* Menu Text                            */
    item["Description"] = "<B>Indent Paragraph Body</B>\r\rIndent the"; /* Description (long)                   */
    item["Description"]+= " paragraph body.";                           /*  * description                       */
    item["Class"] = "DocumentExtension";                                /* add to document toolbar              */
    fnScript = GetScriptFilename();                                     /* Get the script filename              */
    MenuAddFunction(item);                                              /* add menu function                    */
    MenuSetHook(item["Code"], fnScript, "run_indent");                  /* Set the Test Hook                    */
                                                                        /* * Outdent                            */
    item["Code"] = "EXTENSION_OUTDENT_BODY";                            /* Function Code                        */
    item["MenuText"] = "&Outdent Paragraph Body";                       /* Menu Text                            */
    item["Description"] = "<B>Outdent Paragraph Body</B>\r\rOutdent";   /* Description (long)                   */
    item["Description"]+= " the paragraph body.";                       /*  * description                       */
    item["Class"] = "DocumentExtension";                                /* add to document toolbar              */
    fnScript = GetScriptFilename();                                     /* Get the script filename              */
    MenuAddFunction(item);                                              /* add menu function                    */
    MenuSetHook(item["Code"], fnScript, "run_outdent");                 /* Set the Test Hook                    */
    QuickKeyRegister("Page View","3_KEY_CONTROL",                       /* set hotkey to ctrl 2                 */
        "EXTENSION_INDENT_BODY");                                       /* set hotkey to ctrl 2                 */
    QuickKeyRegister("Page View","2_KEY_CONTROL",                       /* set hotkey to ctrl 2                 */
        "EXTENSION_OUTDENT_BODY");                                      /* set hotkey to ctrl 2                 */
    return ERROR_NONE;                                                  /* Return value (does not matter)       */
    }                                                                   /* 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_outdent(int f_id, string mode) {                               /* Call from Hook Processor             */
                                                                        /****************************************/
    if (mode!="preprocess"){
      return;
      }
    indent_para(f_id, mode, (-1));                                      /* outdent the paragraph                */
    }                                                                   /*                                      */
                                                                        /****************************************/
void run_indent(int f_id, string mode) {                                /* Call from Hook Processor             */
                                                                        /****************************************/
    if (mode!="preprocess"){
      return;
      }
    indent_para(f_id, mode, 1);                                         /* indent the paragraph                 */
    }                                                                   /*                                      */
                                                                        /****************************************/
void indent_para(int f_id, string mode, int weight){                    /* indent the paragraph                 */
                                                                        /****************************************/
    int                 ex,ey,sx,sy;                                    /* positional variables                 */
    string              token;                                          /* token of current element             */
    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            */
      token = SGMLGetElementString(sgml);                               /* get token of current element         */
      if (token=="P"){                                                  /* check if the tag is Paragraph        */
        sx = SGMLGetItemPosSX(sgml);                                    /* get sx                               */
        sy = SGMLGetItemPosSY(sgml);                                    /* get sy                               */
        ex = SGMLGetItemPosEX(sgml);                                    /* get ex                               */
        ey = SGMLGetItemPosEY(sgml);                                    /* get ey                               */
        WriteSegment(edit_object,do_indent(sgml,weight),sx,sy,ex,ey);   /* write new text                       */
        break;                                                          /* end the loop                         */
        }                                                               /*                                      */
      element = SGMLPreviousElement(sgml);                              /* get the next sgml element            */
      }                                                                 /*                                      */
    }                                                                   /* end setup                            */
                                                                        /****************************************/
string do_indent(handle sgml, int weight){                              /* removes all spacing from the item    */
                                                                        /****************************************/
    dword               first_line;                                     /* first line indent                    */
    dword               margin_left;                                    /* left padding                         */
    int                 first_line_t;                                   /* first line indent in twips           */
    int                 margin_left_t;                                  /* second line indent in twips          */
    string              properties[];                                   /* properties to put into string        */
                                                                        /*                                      */
    first_line = SGMLGetParameterValue(sgml,"text-indent");             /* get first line property              */
    if (IsError(GetLastError()) || first_line==0){                      /* if we have an error                  */
      first_line = PT_PT;                                               /* set to zero point                    */
      }                                                                 /*                                      */
    margin_left = SGMLGetParameterValue(sgml, "margin-left");           /* get margin left                      */
    if (IsError(GetLastError()) || margin_left==0){                     /* if we have an error                  */
      margin_left = PT_PT;                                              /* set to zero point                    */
      }                                                                 /*                                      */
    first_line_t = SGMLValueToTWIPS(first_line);                        /* get first line as twips              */
    first_line_t = first_line_t - (weight * INDENT_TWIPS);              /* indent by 20 pt                      */
    margin_left_t = SGMLValueToTWIPS(margin_left);                      /*                                      */
    margin_left_t = margin_left_t + (weight * INDENT_TWIPS);            /* indent by 20 pt                      */
                                                                        /*                                      */
    properties = SGMLGetParameters(sgml);                               /* get props of current item            */
    margin_left = SGMLTWIPSToValue(margin_left_t,margin_left,true);     /* set margin left                      */
    if (IsError(GetLastError())){                                       /* if we have an error                  */
      margin_left = PT_PT;                                              /* set to zero point                    */
      }                                                                 /*                                      */
    first_line = SGMLTWIPSToValue(first_line_t,first_line,true);        /* set first line indent                */
    if (IsError(GetLastError())){                                       /* if we have an error                  */
      first_line = PT_PT;                                               /* set to zero point                    */
      }                                                                 /*                                      */
    properties["margin-left"] = SGMLValueToString(margin_left,sgml);    /* set value in props array             */
    properties["text-indent"] = SGMLValueToString(first_line,sgml);     /* set value in props array             */
                                                                        /*                                      */
    CSSSetProperties(sgml,properties);                                  /* set properties in SGML parser        */
    return SGMLToString(sgml);                                          /* return modified element tag          */
    }                                                                   /*                                      */


Our setup function is pretty basic. We’re adding two menu functions here: “Indent Paragraph Body” and “Outdent Paragraph Body”. The indent routine increments the body of the paragraph by 400 twips (or 20 pt). The outdent function moves the paragraph back by the same amount. These functions are also added to the Quick Keys in GoFiler by using the QuickKeyRegister function as hotkeys CTRL+2 and CTRL+3, respectively.



                                                                        /****************************************/
int setup() {                                                           /* Called from Application Startup      */
                                                                        /****************************************/
    string              fnScript;                                       /* Us                                   */
    string              item[10];                                       /* Menu Item                            */
    int                 rc;                                             /* Return Code                          */
                                                                        /* ** Add Menu Item                     */
                                                                        /*  * Define Function                   */
    item["Code"] = "EXTENSION_INDENT_BODY";                             /* Function Code                        */
    item["MenuText"] = "&Indent Paragraph Body";                        /* Menu Text                            */
    item["Description"] = "<B>Indent Paragraph Body</B>\r\rIndent the"; /* Description (long)                   */
    item["Description"]+= " paragraph body.";                           /*  * description                       */
    item["Class"] = "DocumentExtension";                                /* add to document toolbar              */
    fnScript = GetScriptFilename();                                     /* Get the script filename              */
    MenuAddFunction(item);                                              /* add menu function                    */
    MenuSetHook(item["Code"], fnScript, "run_indent");                  /* Set the Test Hook                    */
                                                                        /* * Outdent                            */
    item["Code"] = "EXTENSION_OUTDENT_BODY";                            /* Function Code                        */
    item["MenuText"] = "&Outdent Paragraph Body";                       /* Menu Text                            */
    item["Description"] = "<B>Outdent Paragraph Body</B>\r\rOutdent";   /* Description (long)                   */
    item["Description"]+= " the paragraph body.";                       /*  * description                       */
    item["Class"] = "DocumentExtension";                                /* add to document toolbar              */
    fnScript = GetScriptFilename();                                     /* Get the script filename              */
    MenuAddFunction(item);                                              /* add menu function                    */
    MenuSetHook(item["Code"], fnScript, "run_outdent");                 /* Set the Test Hook                    */
    QuickKeyRegister("Page View","3_KEY_CONTROL",                       /* set hotkey to ctrl 2                 */
        "EXTENSION_INDENT_BODY");                                       /* set hotkey to ctrl 2                 */
    QuickKeyRegister("Page View","2_KEY_CONTROL",                       /* set hotkey to ctrl 2                 */
        "EXTENSION_OUTDENT_BODY");                                      /* set hotkey to ctrl 2                 */
    return ERROR_NONE;                                                  /* Return value (does not matter)       */
    }                                                                   /* end setup                            */


The run_outdent and run_indent functions are hooked into the menu by the setup function. They both trigger the indent_para function, setting the weight parameter to 1 or -1. This specifies the direction the indent is going. If it’s a -1, the indent_para function outdents it. If it’s 1, the function indents instead.



                                                                        /****************************************/
void run_outdent(int f_id, string mode) {                               /* Call from Hook Processor             */
                                                                        /****************************************/
    if (mode!="preprocess"){
      return;
      }
    indent_para(f_id, mode, (-1));                                      /* outdent the paragraph                */
    }                                                                   /*                                      */
                                                                        /****************************************/
void run_indent(int f_id, string mode) {                                /* Call from Hook Processor             */
                                                                        /****************************************/
    if (mode!="preprocess"){
      return;
      }
    indent_para(f_id, mode, 1);                                         /* indent the paragraph                 */
    }                                                                   /*                                      */


The indent_para function is where we actually parse the document. This function is very similar to the run function from last week. It gets the active edit window and checks to make sure it’s actually an edit window. Then it gets the Edit Object, creates an SGML Object, and acquires the X and Y position of the cursor. Using the SGMLGetPreviousElement function, it obtains the element before the cursor, and then while we have a previous element we examine the tag to see if we need to do anything. Unlike last week, this time we’ll use the SGMLGetEelementString function to get a string value to represent the type of tag we’re looking at. If it returns a “P”, then that means we have a paragraph and we can proceed to get our start and end coordinates. After we use the WriteSegment function to write out our new tag, we break out of the loop.



                                                                        /****************************************/
void indent_para(int f_id, string mode, int weight){                    /* indent the paragraph                 */
                                                                        /****************************************/
    int                 ex,ey,sx,sy;                                    /* positional variables                 */
    string              token;                                          /* token of current element             */
    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            */
      token = SGMLGetElementString(sgml);                               /* get token of current element         */
      if (token=="P"){                                                  /* check if the tag is Paragraph        */
        sx = SGMLGetItemPosSX(sgml);                                    /* get sx                               */
        sy = SGMLGetItemPosSY(sgml);                                    /* get sy                               */
        ex = SGMLGetItemPosEX(sgml);                                    /* get ex                               */
        ey = SGMLGetItemPosEY(sgml);                                    /* get ey                               */
        WriteSegment(edit_object,do_indent(sgml,weight),sx,sy,ex,ey);   /* write new text                       */
        break;                                                          /* end the loop                         */
        }                                                               /*                                      */
      element = SGMLPreviousElement(sgml);                              /* get the next sgml element            */
      }                                                                 /*                                      */
    }                                                                   /* end setup                            */


The do_indent function is what handles reading the tag and changing the indent based on the given weight variable. First, we need to get the PVALUEs for our first_line and margin_left variables. If the SGMLGetParameterValue function returns an error or it retrieves a zero, we can set it to PT_PT, which is a PVALUE that equals 0 pt. Then the SGMLValueToTWIPS function converts the PVALUE to a twip value. The math is pretty simple for this: the first line needs to have the weight * INDENT_TWIPS (which is 400, or 20 pt) subtracted from it, while the margin left needs to have the weight * INDENT_TWIPS added to it. This makes it look like the first line isn’t changing, while the body of the paragraph is indenting or outdenting depending on the weight given. After we’ve done our math to figure out the twip value for our parameters, we need to convert it back to a PVALUE with the SGMLTWIPSToValue function. If that returns an error, we can again simply set it to PT_PT, or 0 pt. Then we can convert those values back to string values with the SGMLValueToString function, set them into our array of properties, and use the CSSSetProperties function to put it back into our element. Finally, we use the SGMLToString function to return our new element as a string so the WriteSegment function in the indent_para function can write it back out to our document.



                                                                        /****************************************/
string do_indent(handle sgml, int weight){                              /* removes all spacing from the item    */
                                                                        /****************************************/
    dword               first_line;                                     /* first line indent                    */
    dword               margin_left;                                    /* left padding                         */
    int                 first_line_t;                                   /* first line indent in twips           */
    int                 margin_left_t;                                  /* second line indent in twips          */
    string              properties[];                                   /* properties to put into string        */
                                                                        /*                                      */
    first_line = SGMLGetParameterValue(sgml,"text-indent");             /* get first line property              */
    if (IsError(GetLastError()) || first_line==0){                      /* if we have an error                  */
      first_line = PT_PT;                                               /* set to zero point                    */
      }                                                                 /*                                      */
    margin_left = SGMLGetParameterValue(sgml, "margin-left");           /* get margin left                      */
    if (IsError(GetLastError()) || margin_left==0){                     /* if we have an error                  */
      margin_left = PT_PT;                                              /* set to zero point                    */
      }                                                                 /*                                      */
    first_line_t = SGMLValueToTWIPS(first_line);                        /* get first line as twips              */
    first_line_t = first_line_t - (weight * INDENT_TWIPS);              /* indent by 20 pt                      */
    margin_left_t = SGMLValueToTWIPS(margin_left);                      /*                                      */
    margin_left_t = margin_left_t + (weight * INDENT_TWIPS);            /* indent by 20 pt                      */
                                                                        /*                                      */
    properties = SGMLGetParameters(sgml);                               /* get props of current item            */
    margin_left = SGMLTWIPSToValue(margin_left_t,margin_left,true);     /* set margin left                      */
    if (IsError(GetLastError())){                                       /* if we have an error                  */
      margin_left = PT_PT;                                              /* set to zero point                    */
      }                                                                 /*                                      */
    first_line = SGMLTWIPSToValue(first_line_t,first_line,true);        /* set first line indent                */
    if (IsError(GetLastError())){                                       /* if we have an error                  */
      first_line = PT_PT;                                               /* set to zero point                    */
      }                                                                 /*                                      */
    properties["margin-left"] = SGMLValueToString(margin_left,sgml);    /* set value in props array             */
    properties["text-indent"] = SGMLValueToString(first_line,sgml);     /* set value in props array             */
                                                                        /*                                      */
    CSSSetProperties(sgml,properties);                                  /* set properties in SGML parser        */
    return SGMLToString(sgml);                                          /* return modified element tag          */
    }                                                                   /*                                      */


This is just another example of how Legato can be used to modify HTML tags. This one is a bit more complicated but still fairly simple overall. Using Legato to modify CSS properties, you can automate a lot of annoying manual work inside a document that could otherwise take a lot of time. Since Legato can directly interpret the CSS it is easy to deal with the multitude of units and coding standards CSS allows.


 


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 17:23
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
Thursday, May 15. 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