• 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 14. 2016

Legato Developers Corner #5: Converting Wingding Checkboxes to Unicode

This week we examine a script that replaces Wingdings checkboxes with Unicode checkboxes. This is a very useful thing to do for cross-browser compatibility, as some web browsers do not display Wingdings font sets correctly. Our script iterates over the document and examines every HTML tag to determine if it’s a font tag. If it is, the script checks if the tag has Wingdings set as its font. If so, the script then checks if the font tag is bold or not and stores this information. If the font tag has bold tags inside of it, the script notes that before removing them. The script also removes any space characters and checks the contents of the font tag. If all that remains is an ‘x’ character, it replaces the ‘x’ with a Unicode checkbox. If all that’s left is a ¨ char or an ‘o’ character, it replaces it with a Unicode open box. The script then adds tags as appropriate to embolden the checkbox.


Like previous weeks, this script is a .ms file so it runs at startup. It has the same three user functions as well: setup, main, and run.  It also hooks our conversion script into a menu function. This time we’ll cover the following topics:

  1. Defines
  2. Object handles
  3. SGML parsing
  4. The WriteSegment SDK function

Note as well the different comment style used in this script. This style is more verbose, and more work, but lets you narrarate everything done in the script for clarity.


Our Sample Script


//	GoFiler Legato Script - Replace Wingdings Checkboxes
//	------------------------------------------
//
//	Rev	10/13/2016
//		
//
//	(c) 2016 Novaworks, LLC -- All rights reserved.
//
//	Will examine active HTML window to replace any wingdings checkboxes with Unicode checkboxes
//
//	Notes:
//
//	   -	Requires GoFiler 4.15a or later.
//


#define CHECKEDBOX 		"☒"		/* character to use for checked checkboxes		*/
#define UNCHECKEDBOX 		"☐"		/* character to use for unchecked checkboxes		*/
             						/********************************************************/
							/* Global Items						*/
                                                        /* ------------						*/
							/********************************************************/
							
    string		entries[];			/* wingdings character entries				*/
    int 		run			(int f_id, string mode);/* Call from Hook Processor		*/

							/********************************************************/
							/* Menu Setup and Patch					*/
                                                        /* --------------------					*/
							/********************************************************/

									/****************************************/
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_REMOVE_WINGDINGS";			/* Function Code			*/
    item["MenuText"] = "&Replace Wingdings Checkboxes";			/* Menu Text				*/
    item["Description"] = "<B>Replace Wingdings Checkboxes</B> ";	/* Description (long)			*/
    item["Description"]+= "\r\rReplace wingdings with unicode chars.";	/*  * description			*/
									/*  * Check for Existing		*/
    rc = MenuFindFunctionID(item["Code"]);				/* Look for existing			*/
    if (IsNotError(rc)) {						/* Was already be added			*/
      return ERROR_NONE;						/* Exit					*/
      }									/* end error				*/
									/*  * Registration			*/
    rc = MenuAddFunction(item);						/* Add the item				*/
    if (IsError(rc)) {							/* Was already be added			*/
      return ERROR_NONE;						/* Exit					*/
      }									/* end error				*/
    fnScript = GetScriptFilename();					/* Get the script filename		*/
    MenuSetHook(item["Code"], fnScript, "run");				/* Set the Test Hook			*/
    return ERROR_NONE;							/* Return value (does not matter)	*/
    }									/* end setup				*/

								/************************************************/
								/* First Run from Menu Hook			*/
								/* ------------------------			*/
								/************************************************/

									/****************************************/
int main() {								/* Initialize from Hook Processor	*/
									/****************************************/
    string		s1;						/* General				*/
    									/*					*/
    s1 = GetScriptParent();						/* Get the parent			*/
    if (s1 == "LegatoIDE") {						/* Is run from the IDE (debug)		*/
      setup();								/* Add to the menu			*/
      run(0,"preprocess");
      }									/* end IDE run				*/
    return ERROR_NONE;							/* Return value (does not matter)	*/
    }									/* end setup				*/

									/****************************************/
int run(int f_id, string mode) {					/* Call from Hook Processor		*/
									/****************************************/
    int			counter;					/* increment counter			*/
    int			ex,ey,sx,sy;					/* positional variables			*/
    boolean		bold;						/* check if the checkbox is bold	*/
    string		opentag;					/* opening tag to write out		*/
    string		closetag;					/* closing tag to write out		*/
    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	*/
    
    if (mode!="preprocess"){						/* if mode is not preprocess		*/
      return ERROR_NONE;						/* return no error			*/
      }									/*					*/
    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 ERROR_EXIT;						/* return				*/
      }									/*					*/
    edit_object = GetEditObject(edit_window);				/* create edit object			*/
    sgml = SGMLCreate(edit_object);					/* create sgml object			*/
    element = SGMLNextElement(sgml);					/* get the first sgml element		*/
    while(element != ""){						/* while element isn't empty		*/
      if (IsError(element)){						/* if it couldn't read the element	*/
        MessageBox('x',"Could not read HTML element, aborting.");	/* print error				*/
        return ERROR_EXIT;						/* return error				*/	
        }								/* check if not last tag		*/
      if (FindInString(element,"<font",0,false)>(-1)){			/* Is it a font tag?			*/
	if (FindInString(element,"bold",0,false)>(-1)){			/* Check if the font tag specifies bold	*/
	  bold = true;							/* remember it was bold			*/
	  }								/*					*/
	else{								/* if it wasn't bold			*/
	  bold = false;							/* remember it wasn't bold		*/
	  }								/*					*/
	if (FindInString(element,"wingdings")>(-1)){			/* Does it have wingdings?		*/
	  sx = SGMLGetItemPosSX(sgml);					/* get start x pos			*/
	  sy = SGMLGetItemPosSY(sgml);					/* get start y pos			*/
	  text = SGMLFindClosingElement(sgml);				/* get the text in the tag		*/
	  if (text!=""){						/* if we can find the closing element	*/
	    if(FindInString(text,"<b>")>(-1)){				/* if the text inside is bold		*/
	      bold = true;						/* remember we have bold for later	*/
	      text = ReplaceInString(text,"<b>","",true);		/* remove open b tags			*/
	      text = ReplaceInString(text,"</b>","",true);		/* remove closing b tags		*/
	      }								/*					*/
	    ex = SGMLGetItemPosEX(sgml);				/* get end x pos			*/
	    ey = SGMLGetItemPosEY(sgml);				/* get end y pos			*/
	    text = ReplaceInString(text," ","",true);			/* remove extra space characters	*/
	    if (bold==true){						/* if the text was bold			*/
	      opentag = "<b>";						/* opening tag				*/
	      closetag = "</b>";					/* closing tag				*/
	      }								/*					*/
	    else{							/* if it wasn't bold, write nothing	*/
	      opentag = "";						/* opening tag				*/
	      closetag = "";						/* closing tag				*/
	      }								/*					*/
	    if (TrimString(text)=="x"){					/* if it's a checked box		*/
	      WriteSegment(edit_object,opentag+CHECKEDBOX+closetag,	/* write out checked box char		*/
	      		   sx,sy,ex,ey);				/* write out checked box char		*/
	      }								/*					*/
	    else{							/* if it's not a checked box		*/
	      WriteSegment(edit_object,opentag+UNCHECKEDBOX+closetag,	/* write out unchecked box char		*/
	      		   sx,sy,ex,ey);				/* write out unchecked box char		*/
	      }								/*					*/
	    SGMLSetPosition(sgml,ex,ey);				/* set position of writer to end	*/
	    counter++;							/* increment counter			*/
	    }								/* check if it's last tag		*/
	  }
	}								/*					*/
      element = SGMLNextElement(sgml);					/* get the next sgml element		*/
      }									/*					*/
    CloseHandle(edit_object);						/* close edit object			*/
    MessageBox('i',"Found and removed %d checkboxes.",counter);		/* messagebox				*/
    return ERROR_NONE;							/* Exit Done				*/
    }									/* end setup				*/

 


Script Walkthrough


 


The first thing to note is the use of the DEFINE keyword. The DEFINE keyword instructs the script interpreter to replace the identifier that immediately follows the keyword with the value specified after that. So,
for example, we define two identifiers, CHECKEDBOX and UNCHECKEDBOX, to be our two Unicode characters we’d like to insert. Using the DEFINE keyword allows you both to make your code more understandable by using semantically meaningful terms in place of less meaningful values and to facilitate easy changing of certain values (like constants, such as our characters in this script).


 


#define CHECKEDBOX 		"&#9746;"		/* character to use for checked checkboxes		*/
#define UNCHECKEDBOX 		"&#9744;"		/* character to use for unchecked checkboxes		*/

The contents of the setup and main functions won’t be covered in detail. For more information on hooking a user function into a menu function, see previous blog posts. For now, let’s examine the run function where we parse the SGML file and replace the Wingdings characters with Unicode characters. As in previous scripts, we check the mode of the calling function and exit if it is not in a pre-process state. Now we use some SDK functions to get handles to the edit window and its contents.


    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 ERROR_EXIT;						/* return				*/
      }									/*					*/
    edit_object = GetEditObject(edit_window);				/* create edit object			*/
    sgml = SGMLCreate(edit_object);					/* create sgml object			*/

The GetActiveEditWindow SDK function returns a handle to the edit window. Whenever you’re retrieving a handle, it’s good practice to check to ensure the handle is valid before using it, and we do that with the IsError SDK function and exiting if the handle is invalid. After that, we use the GetEditObject SDK function to retrieve a handle to the contents of the edit window. Using that handle, we create an SGML object using the SGMLCreate SDK function. At this point, we now have a handle to an SGML object we can parse, which we do with a while loop. It should be noted that Legato handles are managed by Legato rather than the operating system.


We then iterate through the SGML tags present in the SGML object and test them for our conditions of interest, i.e., if it’s a font tag, if it’s bold, and if it contains Wingdings characters. First, we check to see if the tag is a font tag:


      if (FindInString(element,"<font",0,false)>(-1)){			/* Is it a font tag?			*/
      ...
      }

This conditional expression makes use of the FindInString SDK function to determine if the “<font” string occurs within the tag. A position greater than -1 indicates the string in present inside the element. If it is a font tag, we process further.


	if (FindInString(element,"bold",0,false)>(-1)){			/* Check if the font tag specifies bold	*/
	  bold = true;							/* remember it was bold			*/
	  }								/*					*/
	else{								/* if it wasn't bold			*/
	  bold = false;							/* remember it wasn't bold		*/
	  }								/*					*/

Again we use the FindInString SDK function to determine if the tag is bold. If so, we set a boolean flag to true. Now we look to determine if the font tag includes Wingdings, and, if so, we get the position of the tag. Note that the SGMLGetItemPosSX and SGMLGetItemPosSY SDK functions return the starting position of the last SGML item parsed within the SGML object. Thus, if this tag contains Wingdings, we retrieve it’s starting position. The SGMLFindClosingElement SDK function advances the internal position of the SGML parser to the closing tag, so we can retrieve the position of that later. It also returns any HTML or text between the opening and closing tags. We also check for the presence of bold tags (“<b>”), and flag their presence while removing them.


	if (FindInString(element,"wingdings")>(-1)){			/* Does it have wingdings?		*/
	  sx = SGMLGetItemPosSX(sgml);					/* get start x pos			*/
	  sy = SGMLGetItemPosSY(sgml);					/* get start y pos			*/
	  text = SGMLFindClosingElement(sgml);				/* get the text in the tag		*/
	  if (text!=""){						/* if we can find the closing element	*/
	    if(FindInString(text,"<b>")>(-1)){				/* if the text inside is bold		*/
	      bold = true;						/* remember we have bold for later	*/
	      text = ReplaceInString(text,"<b>","",true);		/* remove open b tags			*/
	      text = ReplaceInString(text,"</b>","",true);		/* remove closing b tags		*/
	      }								/*					*/
	    ex = SGMLGetItemPosEX(sgml);				/* get end x pos			*/
	    ey = SGMLGetItemPosEY(sgml);				/* get end y pos			*/
	    text = ReplaceInString(text," ","",true);			/* remove extra space characters	*/
	    if (bold==true){						/* if the text was bold			*/
	      opentag = "<b>";						/* opening tag				*/
	      closetag = "</b>";					/* closing tag				*/
	      }								/*					*/

We then use the SDK functions SGMLGetItemPosEX and SGMLGetItemPosEY to store the ending position of the current SGML element, which should be the closing font tag. We then remove extraneous white space. Depending on our previously set bold flag and if an ‘x’ character lies in the text of the SGML element, we then create our string with or without bold tags and with either our CHECKEDBOX or UNCHECKEDBOX identifier. To put our modified tag into the SGML object, we use the SDK function WriteSegment, which takes a handle to the edit object, the newly crafted string, and the start and end positions of the element as parameters.


	    if (TrimString(text)=="x"){					/* if it's a checked box		*/
	      WriteSegment(edit_object,opentag+CHECKEDBOX+closetag,	/* write out checked box char		*/
	      		   sx,sy,ex,ey);				/* write out checked box char		*/
	      }								/*					*/
	    else{							/* if it's not a checked box		*/
	      WriteSegment(edit_object,opentag+UNCHECKEDBOX+closetag,	/* write out unchecked box char		*/
	      		   sx,sy,ex,ey);				/* write out unchecked box char		*/
	      }								/*					*/
	    SGMLSetPosition(sgml,ex,ey);				/* set position of writer to end	*/
	    counter++;

The WriteSegment SDK function is extremely powerful in that it can write any string to a specified location inside an edit object. However, you should exercise caution in using this function as it can cause internal errors or unexpected behavior given we are parsing the SGML object (which was made from the edit object) based on a particular internal position. To counteract that, we employ the SGMLSetPosition SDK function to reset our internal pointer within the object to the end of the current element. Thus we can iterate to the next element in our while loop after incrementing our counter, which is keeping track of the number of items changed.


Finally, we close our edit object handle and alert the user of the number of Wingdings checkboxes the script changed.


    CloseHandle(edit_object);						/* close edit object			*/
    MessageBox('i',"Found and removed %d checkboxes.",counter);		/* messagebox				*/
    return ERROR_NONE;							/* Exit Done				*/

 




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 15:03
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
Monday, May 19. 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