• 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, July 19. 2019

LDC #144: Using Legato to Create Hidden Text in a Document

I was discussing inline XBRL with a client the other day when a thought occurred to me. GoFiler uses XDX to mark information in a document to convert it to inline XBRL at a later time, which is generally great, but it means if you have a line item on a table that doesn’t actually have a label (like a total line, for example), then GoFiler is just going to pick the standard label for that element. That’s not a bad thing, in and of itself, but if you want to customize the label, there needs to be some text on the line item for XDX to pick up. Adding hidden text to the document is a great way to add “hints” for XDX, without actually changing the look of the exported document.


When making text hidden, we need to take special care that we don’t actually change what the output of our document looks like. To that effect, we cannot just make a single table cell or paragraph hidden, because that would alter the spacing of our document. Making a table cell hidden, for example, will just cause all the other cells to shift over to the left to make up for the gap of the hidden cell. Making a paragraph hidden will alter the spacing between other HTML blocks in our file. We can get around this by wrapping the contents of our HTML block with font tags, that just have the display CSS property set to none. This will allow our block to still take up the space it needs to take up, without showing the text inside of it. This means you could add a label to a table cell that otherwise has no label, and then use the script to hide the text. The resulting inline XBRL would look exactly like the original HTML document but the label used for the element would be your custom label rather than the standard label.


Our script this week is going to use the SGML parser in GoFiler to automatically add these tags to the document for us to make it a little easier.


 



#define         HIDE_TAG                "<FONT STYLE=\"display:none\">"
#define         CLOSE_HIDE              "</FONT>"



First, let’s take a look at our defined values. I’m using HIDE_TAG and CLOSE_HIDE as the names of my opening and closing tags for the selection of the document that will be hidden. These are FONT tags, with the display property set to none. FONT is a generally inoffensive and inert tag, that can be stacked, so having extra tags in the document shouldn’t cause any issues. FONT tags are not allowed by the EDGAR system within an inline XBRL document, but GoFiler compensates for that when converting HTML to inline XBRL, so there’s no issue there.


This script has the standard setup and main functions that most other scripts have, so we’re going to skip over them, and jump right into our run function, where the work actually happens.




int run(int f_id, string  mode){

    ... variable declarations omitted ...

    if(mode!="preprocess"){
      return ERROR_NONE;
      }

    window = GetActiveEditWindow();
    wType = GetEditWindowType(window) & EDX_TYPE_ID_MASK;
    if (wType != EDX_TYPE_PSG_PAGE_VIEW){
      MessageBox('x',"This function must be used in page view, current view is %0x",wType);
      return ERROR_NONE;
      }
    edit_obj = GetEditObject(window);


Our run function begins like most other run functions.... we need to first check the run mode to ensure we’re not running in anything other than preprocess mode so our script only runs once. After we’ve checked that, we can get the active edit window with GetActiveEditWindow, and get the type of window with GetEditWindowType. We do this because we need to know the script is running in Page View. If it’s not, the function will return an error message and then return. Once we know we’re in a Page View window, we can get the edit object for the view, and continue.



    sgml = SGMLCreate(edit_obj);
    sx = GetCaretXPosition(edit_obj);
    sy = GetCaretYPosition(edit_obj);
    rc = SGMLSetPosition(sgml,sx,sy);
    if (IsError(rc)){
      MessageBox('x',"Cannot set SMGL parser to caret position.");
      return ERROR_EXIT;
      }


Next, we need to get a handle to our SGML parser object with SGMLCreate. There are other ways to parse through a file, but I find the SGML parser is probably the easiest way to do it. Once we have our parser, we can use the GetCaretXPosition and GetCaretYPosition functions to get our caret’s position in the document, and set our SGML parser to that position. It’s important to check that this function worked correctly, because, if for some reason our caret position is out of bounds (this shouldn’t happen), we want to know if our SGML parser failed to set its location correctly. If the SGML parser position is wrong, it could lead to the wrong text being highlighted, so erring on the side of caution here and checking the return value is a good idea.



    tag = SGMLPreviousElement(sgml);
    element = SGMLGetElementString(sgml);
    while ((HTMLIsBlockElement(element)==false) && (tag!="")){
      tag = SGMLPreviousElement(sgml);
      element = SGMLGetElementString(sgml);
      }


So far so good, we now have our document, all the relevant handles, and our SGML parser is created and in the right place. We can then use the SGMLPreviousElement function to grab the last element before our caret, so we can test to see if it’s a block element or not. The SGMLPreviousElement function just returns the full element opening tag, so we can then use SGMLGetElementString to get the element type as a string. This will return “P” for a paragraph tag, “TD” for a table cell, etc. After we have our element as a string, we can enter a while loop to iterate backwards until we hit a block element. This is an important step, because if we don’t do this, our we might end up breaking on an inline tag like a bold tag or something, and only hiding part of our text.


    
    open = tag;
    sx = SGMLGetItemPosSX(sgml);
    sy = SGMLGetItemPosSY(sgml);
    tag = SGMLFindClosingElement(sgml,SP_FCE_CODE_AS_IS);
    ex = SGMLGetItemPosEX(sgml);
    ey = SGMLGetItemPosEY(sgml);
    close = FormatString("</%s>",element);
    tag = open+HIDE_TAG+tag+CLOSE_HIDE+close;
    WriteSegment(edit_obj,tag,sx,sy,ex,ey);
    CloseHandle(sgml);
    CloseHandle(window);
    CloseHandle(edit_obj);
    return ERROR_NONE;
    }


After we’ve successfully moved our parser to the start of the block we want to hide, we can make a copy of that tag, get our start and end coordinates, build our new tag value, and write it out with WriteSegment. Finally, we can close our handles, and return.


This is a fairly simple bit of code, but it automates a task that would take 6-7 separate actions in GoFiler’s UI to do. The script took maybe an hour to write, so if it saves a minute each time it’s used, it will easily pay for itself in time savings. It’s always important to keep that in mind: how much time will a script save vs how much time will it take to write. There’s very little point in automating a task that’s done rarely, if the automation takes days (or longer) to write.


Here's the full script without commentary.



#define         HIDE_TAG                "<FONT STYLE=\"display:none\">"
#define         CLOSE_HIDE              "</FONT>"


void            setup();
int             run(int f_id, string mode, string file);


void main(){

    setup();
    }

void setup(){

    string              menu[];
    string              fn;

    menu["Code"] = "HIDE_BLOCK_CONTENTS";
    menu["MenuText"] = "Hide Block Contents";
    menu["Description"] = "Hides the contents of any block tag.";
    MenuAddFunction(menu);
    fn = GetScriptFilename();
    MenuSetHook(menu["Code"],fn,"run");
    }

int run(int f_id, string  mode){

    int                 sx,sy,ex,ey;
    int                 rc;
    handle              window;
    handle              edit_obj;
    handle              sgml;
    dword               wType;
    string              tag;
    string              element;
    string              open;
    string              close;

    if(mode!="preprocess"){
      return ERROR_NONE;
      }

    window = GetActiveEditWindow();
    wType = GetEditWindowType(window) & EDX_TYPE_ID_MASK;
    if (wType != EDX_TYPE_PSG_PAGE_VIEW){
      MessageBox('x',"This function must be used in page view, current view is %0x",wType);
      return ERROR_NONE;
      }
    edit_obj = GetEditObject(window);

    sgml = SGMLCreate(edit_obj);
    sx = GetCaretXPosition(edit_obj);
    sy = GetCaretYPosition(edit_obj);
    rc = SGMLSetPosition(sgml,sx,sy);
    if (IsError(rc)){
      MessageBox('x',"Cannot set SMGL parser to caret position.");
      return ERROR_EXIT;
      }

    tag = SGMLPreviousElement(sgml);
    element = SGMLGetElementString(sgml);
    while ((HTMLIsBlockElement(element)==false) && (tag!="")){
      tag = SGMLPreviousElement(sgml);
      element = SGMLGetElementString(sgml);
      }
    open = tag;
    sx = SGMLGetItemPosSX(sgml);
    sy = SGMLGetItemPosSY(sgml);
    tag = SGMLFindClosingElement(sgml,SP_FCE_CODE_AS_IS);
    ex = SGMLGetItemPosEX(sgml);
    ey = SGMLGetItemPosEY(sgml);
    close = FormatString("</%s>",element);
    tag = open+HIDE_TAG+tag+CLOSE_HIDE+close;
    WriteSegment(edit_obj,tag,sx,sy,ex,ey);
    CloseHandle(sgml);
    CloseHandle(window);
    CloseHandle(edit_obj);
    return ERROR_NONE;
    }


 


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:35
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
Friday, May 23. 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