lobasic-macros

LibreOffice macros I use often

commit ec2b843e116b1f28600d2f3f75c2c55447b75ed5
parent eb97a0eb5088fe1192a737243be809285ed40546
Author: Bharatvaj Hemanth <bharatvaj@yahoo.com>
Date: Sat, 24 Aug 2024 23:33:36 +0530

Completed pending report generation

Completed SBC list generation

Automation 100%
4 files changed, 742 insertions(+), 412 deletions(-)
D
Standard/Bharatgas.xba
|
411
-------------------------------------------------------------------------------
A
Standard/CylinderAutomation.xba
|
267
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
Standard/HandyTools.xba
|
473
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
M
Standard/script.xlb
|
3
++-
diff --git a/Standard/Bharatgas.xba b/Standard/Bharatgas.xba
@@ -1,410 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE script:module PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "module.dtd">
-<script:module xmlns:script="http://openoffice.org/2000/script" script:name="Bharatgas" script:language="StarBasic">REM  *****  BASIC  *****
-&apos;Option VBASupport 1
-Option Explicit
-
-Global Const exportFolder = &quot;C:\Users\bhara\tSync\Pending-01-07-2024&quot;
-Global Const rowsToSkip = 1
-Global Const insertNewRowFor = False
-Global Const printOnlySummary = False
-Global Const roughHeaderMatch = True
-Global Const highlightBasedOn = &quot;Payment Option&quot;
-Global Const highlightSearchString = &quot;Online Payment&quot;
-Global Const highlightRemoveColumn = True
-&apos; TODO change suffix based on the report type
-Global Const sheetNameSuffix = &quot;- PENDING&quot;
-Global Const shouldExportPDF = True
-Global Const isPendingReport = True
-
-Function UsedRangeCursor(Optional ByRef oSheet as Object) as Object
-	&apos;FIXME Check if the split has already happen
-	Dim cursor as Object
-	if IsNull(oSheet) Or IsMissing(oSheet) Then
-			oSheet = ThisComponent.getCurrentController().getActiveSheet()
-	End If
-	cursor = oSheet.createCursor()
-	cursor.gotoStartOfUsedArea(False)
-	cursor.gotoEndOfUsedArea(True)
-	UsedRangeCursor = cursor
-End Function
-
-Function UserFriendlyName(str as String) as String
-	Dim l as Long
-	Dim c as String
-	Dim i as Long
-	Dim prevChar as String
-	str = Trim(str)
-	&apos; Check if Len(str) is really called multiple times
-	l = Len(str)
-	For i = 0 To l
-		c = Mid(str, i + 1, 1)
-		If i &gt; 1 And (c &gt; &quot;A&quot; And c &lt; &quot;Z&quot;) Then
-				&apos; Only prepend space if &apos;c&apos; is not the first character...
-				&apos; or previous char doesn&apos;t have space
-				UserFriendlyName = UserFriendlyName &amp;  IIf(prevChar = &quot; &quot;, &quot;&quot;, &quot; &quot;)  &amp; c
-		Else
-			UserFriendlyName = UserFriendlyName &amp; c
-		End If
-		prevChar = c
-	Next
-End Function
-
-Sub TestFn()
-	MsgBox UserFriendlyName(&quot;Area Name&quot;)
-		MsgBox UserFriendlyName(&quot;AreaName&quot;)
-				MsgBox UserFriendlyName(&quot;   AreaName&quot;)
-End Sub
-
-Sub SortAreaName(Optional oSheet As Variant, Optional cursor as Variant)
-	Dim oRange as Object
-	Dim oSortFields(1) as new com.sun.star.util.SortField
-	Dim oSortDesc(0) as new com.sun.star.beans.PropertyValue
-	Dim endRow as Integer
-	Dim endColumn as Integer
-	
-	If IsMissing(oSheet) Then
-		oSheet = ThisComponent.Sheets(0)
-		cursor = UsedRangeCursor(oSheet)
-	End If
-
-	endRow = cursor.RangeAddress.EndRow
-	endColumn = cursor.RangeAddress.EndColumn
-	
-	oRange = oSheet.getCellRangeByPosition(0, 1, endColumn, endRow)
-	ThisComponent.getCurrentController.select(oRange)
-	
-	&apos; Area
-	oSortFields(0).Field = 0
-	oSortFields(0).SortAscending = True
-	
-	&apos; FIXME This is not working
-	&apos; Date
-	oSortFields(1).Field = 4
-	oSortFields(1).SortAscending = True
-	
-	oSortDesc(0).Name = &quot;SortFields&quot;
-    oSortDesc(0).Value = oSortFields
-	
-	oRange.Sort(oSortDesc)
-End Sub
-
-Sub NewSheet(sheetName as String)
-			Dim sheets as Object
-			sheets = ThisComponent.Sheets()
-			If Not sheets.hasByName(sheetName) Then
-				sheets.insertNewByName(sheetName, sheets.getCount())
-			End If
-			
-End Sub
-
-Sub CleanColumnHeaders(oSheet as Object, endColumn as Integer)
-	Dim cell as Object
-	Dim neatName as String
-	Dim i as Integer
-	For i = 0 To endColumn
-		cell = oSheet.getCellByPosition(i, 0)
-		neatName = cell.getString()
-		neatName = UserFriendlyName(neatName)
-		cell.setString(neatName)
-	Next
-End Sub
-
-Sub RemoveExtraMergeCells(oSheet as Object, oRangeAddress as Variant)
-	Dim oRange as Object
-	Dim oSortFields(0) as new com.sun.star.util.SortField
-	Dim oSortDesc(0) as new com.sun.star.beans.PropertyValue
-	Dim endRow as Integer
-	
-	endRow = oRangeAddress.EndRow
-	lastColumn = oRangeAddress.EndColumn
-		
-	rem set the range on which to sort&apos;
-
-	&apos;oRange = oSheet.getCellRangeByName(&quot;A2:F20&quot;)
-	oRange = oSheet.getCellRangeByPosition(0, 0, lastColumn, 0)
-	ThisComponent.getCurrentController.select(oRange)
-	initialColumnCount = oRange.Columns.getCount() - 1
-	deletedColumns = 0
-	For j = 0 To (initialColumnCount - deletedColumns)
-		oCell = oRange.getCellByPosition(j, 0)
-		con = oCell.String
-		If con = &quot;&quot; Then
-			oRange.Columns.removeByIndex(j, 1)
-			deletedColumns = deletedColumns - 1
-		Else
-			Print con
-		End If
-	Next
-End Sub
-
-
-Sub UnFreezeSelection
-	Dim document   as Object
-	Dim dispatcher as Object
-	document   = ThisComponent.CurrentController.Frame
-	dispatcher = createUnoService(&quot;com.sun.star.frame.DispatchHelper&quot;)
-	dispatcher.executeDispatch(document, &quot;.uno:FreezePanes&quot;, &quot;&quot;, 0, Array())
-End Sub
-
-Function GetHeaderPosition(ByRef oSheet as Object, searchString as String, endColumn as Long) as Integer
-	Dim iColumn as Integer
-	Dim oCell as Object
-	Dim cellString as String
-	GetHeaderPosition = -1
-	If roughHeaderMatch Then
-		searchString = UserFriendlyName(searchString)
-	End If
-	For iColumn = 0 To endColumn - 1
-        oCell = oSheet.getCellByPosition(iColumn, 0)
-        cellString = oCell.String
-        If roughHeaderMatch Then
-			cellString = UserFriendlyName(cellString)
-		End If
-        If  cellString = searchString Then
-            GetHeaderPosition = iColumn
-            Exit For
-        End If
-    Next iColumn
-End Function
-
-Sub HighlightOnline(ByRef oSheet as Object, endColumn as Long, endRow as Long)
-	&apos; Fix false positives when having ONLINE and the content has &quot;Online Payment&quot;
-	Dim currentStr as String
-	Dim oCell as Object
-	Dim oCellRange as Object
-	Dim str as String
-	Dim iSearchColumn as Long
-	Dim i as Integer
-
-	&apos; Search for column position that has highlightBasedOn
-	iSearchColumn = GetHeaderPosition(oSheet, highlightBasedOn, endRow)
-	If iSearchColumn = -1 Then Exit Sub
-	For i = 0 To  endRow - 1
-		oCell = oSheet.getCellByPosition(iSearchColumn, i)
-		If InStr(oCell.getString(), highlightSearchString) &gt; 0 Then
-			oCellRange = oSheet.getCellRangeByPosition(0, i, endColumn, i)
-			oCellRange.CellBackColor = RGB(255, 255, 0) &apos; Yellow
-		End If
-	Next i
-	
-	If highlightRemoveColumn Then
-		oSheet.Columns.removeByIndex(iSearchColumn, 1)
-	End If
-End Sub
-
-
-Sub MainNew
-	Dim oRange as Object
-	Dim oSheet as Object
-	Dim oCellStyle as Object
-	Dim pageStyle as Object
-	Dim oStyle as Object
-	oSheet = ThisComponent.Sheets.getByIndex(0)
-	&apos;oRange = oSheet.getCellRangeByPosition(1, 1, 4, 1)
-	&apos;oCellStyle = ThisComponent.StyleFamilies.getByName(&quot;CellStyles&quot;).getByName(&quot;InterHeader&quot;)
-	&apos;oRange.CellStyle = &quot;InterHeader&quot;
-	pageStyle = ThisComponent.StyleFamilies.getByName(&quot;PageStyles&quot;)
-	oStyle = pageStyle.getByName(&quot;Default&quot;)
-	oStyle.CenterVertically = True
-	pageStyle.insertByName(&quot;NewStyle&quot;)
-	
-	if NOT IsNull(pageStyle) then
-		Print pageStyle.dbg_methods
-	end if
-
-End Sub
-
-Sub Main
-	Dim s as Object
-	Dim cursor as Object
-	Dim c as Integer
-	Dim destSheet as Variant
-	Dim sheetName as String
-	Dim iArea as Long
-	Dim d as String
-	Dim a as String
-	Dim startRow as Long
-	Dim areaNames as New Collection
-	Dim dColumns as Object
-	Dim endRow as Long
-	Dim endColumn as Long
-	Dim areaColumn as Object
-	Dim areaRange, idRange, bookRange
-	Dim headerRange as Object
-	Dim cellRangeToCopy as Object
-	Dim pageStyle as Object
-
-	s = ThisComponent.Sheets(0)
-	cursor = UsedRangeCursor(s)
-	
-	&apos; Skip Header
-	startRow =  rowsToSkip
-	endRow = cursor.RangeAddress.EndRow
-	endColumn = cursor.RangeAddress.EndColumn
-	
-	&apos; TODO pass the column name/index as an argument
-	&apos; Transformations to be applied to the Main sheet before splitting the sheet by Area Name
-	&apos;Call UnFreezeSelection
-	cursor = UsedRangeCursor(s)
-	If isPendingReport Then
-		Call HighlightOnline(s, endColumn, endRow)
-	End If
-	Call SortAreaName(s, cursor)
-	Call CleanColumnHeaders(s, endColumn)
-	
-	&apos; Justify Leftmost cells to left and Rightmost cells to right
-	&apos; TODO Maybe use more descriptive code? Like (&quot;AreaName&quot;)?
-	idRange =  s.getCellRangeByPosition(1,startRow, 1, endRow)
-    idRange.setPropertyValue(&quot;HoriJustify&quot;, com.sun.star.table.CellHoriJustify.LEFT)
-    
-    bookRange = s.getCellRangeByPosition(endColumn, startRow, endColumn, endRow)
-    bookRange.setPropertyValue(&quot;HoriJustify&quot;, com.sun.star.table.CellHoriJustify.RIGHT)
-
-	areaColumn = s.getCellRangeByPosition(0,startRow, 0, endRow)
-	headerRange = s.getCellRangeByPosition(0, 0, endColumn, 0)
-	
-	headerRange.CellStyle = &quot;Heading 1&quot;
-   
-	&apos;On Error Goto ErrorHandler
-	For iArea = rowsToSkip To endRow - 1
-		d = areaColumn.getCellByPosition(0, iArea).String
-		If  d = &quot;&quot; Then
-			Exit For
-		End If
-
-		If a &lt;&gt; d Or  iArea = (endRow - 1) Then
-			&apos; FIXME Get the columns automatically from the sheet
-			&apos; FIXME Covert the end column from endColumn above
-			
-			If insertNewRowFor Then
-				&apos; FIXME This is a stub, not correct
-				s.Rows.insertByIndex(startRow, 1)
-				startRow = startRow + 1
-				endRow = endRow + 1
-			End If
-			
-			areaRange = s.getCellRangeByPosition(0, startRow, endColumn, iArea)
-			If printOnlySummary Then
-				&apos; TODO maybe there is an elegant way than
-				&apos; writing this two times
-				If shouldExportPDF Then
-					ExportPDF(destSheet)
-				End If
-				GoTo Continue
-			End If
-			
-			&apos; TODO subtotals might solve this
-			&apos;s.group(areaRange.RangeAddress, 1)
-
-			sheetName = a &amp; sheetNameSuffix
-
-			&apos; Copy the Headers from the Main Document
-			&apos; FIXME This DOES NOT work when using with filtered data
-			&apos; TODO Get the number of columns to copy dynamically from the Sheet.
-			cellRangeToCopy = areaRange.RangeAddress
-
-			If (cellRangeToCopy.EndRow - cellRangeToCopy.StartRow) = 0 Then
-				Goto Continue
-			End If
-			
-			&apos; Prepare Destination sheet
-			NewSheet(sheetName)
-
-			destSheet = ThisComponent.Sheets().getByName(sheetName)
-
-			pageStyle = ThisComponent.StyleFamilies.getByName(&quot;PageStyles&quot;).getByName(&quot;Default&quot;)
-			pageStyle.setPropertyValue(&quot;PrintGrid&quot;, True)
-			pageStyle.setPropertyValue(&quot;CenterHorizontally&quot;, True)
-			&apos; Make the margins 0.2&quot; thick	
-			pageStyle.setPropertyValue(&quot;LeftMargin&quot;, 0.2 * 2540)
-			pageStyle.setPropertyValue(&quot;RightMargin&quot;, 0.2 * 2540)
-			
-			&apos; Customize the Destination Sheet&apos;s Columns
-			&apos; TODO Make snuggly calculation. There should be a minimum width and AutoFit
-			With destSheet.getColumns()
-				If isPendingReport Then
-					.getByName(&quot;B&quot;).Width = 2500
-					.getByName(&quot;C&quot;).Width = 8000
-					.getByName(&quot;D&quot;).Width = 2500
-				Else
-					.getByName(&quot;B&quot;).Width = 1700
-					.getByName(&quot;C&quot;).Width = 5000
-					.getByName(&quot;D&quot;).Width = 11000
-					.getByName(&quot;E&quot;).Width = 2200
-				End If
-			End With
-
-
-			&apos; Copy Header
-			&apos; TODO Check if it&apos;s possible to use UsedRange instead of endColumn
-			s.copyRange(destSheet.getCellRangeByName(&quot;A1&quot;).CellAddress, _
-				headerRange.RangeAddress)
-
-			&apos; Copy all the contents uptil 
-			s.copyRange(destSheet.getCellRangeByName(&quot;A2&quot;).CellAddress, _
-				cellRangeToCopy)
-
-			&apos;TODO Remove Columns that should be deleted in leaveColumns
-			&apos; Better leave it when copying above
-			destSheet.getColumns().removeByIndex(0, 1)
-			destSheet.getColumns().removeByIndex(4, 1)
-			
-
-			&apos;TODO Maybe use Dispatcher?
-			If shouldExportPDF Then
-				ExportPDF(destSheet)
-			End If
-			Continue:
-			startRow = (iArea + 1)
-			a = d
-		End If
-	Next iArea
-
-	ErrorHandler:
-		MsgBox &quot;Error#: &quot; &amp; Erl &amp; Error
-		&apos;MsgBox &quot;arrCount: &quot; &amp; areaNames.Count
-		MsgBox &quot;a: &quot; &amp; a &amp; &quot;, d: &quot; &amp; d
-	Reset
-End Sub
-
-Sub ValidateFileName(fileName as String)
-    Dim invalidChars as String : invalidChars = &quot;\/:*?&quot;&quot;&lt;&gt;|&quot;
-    
-    For i = 1 To Len(fileName)	
-   		c = Mid(fileName, i, 1)
-    	If InStr(invalidChars, c) &gt; 0 Then
-    		Print &quot;invalid&quot; &amp; c
-    	End If
-    Next
-End Sub
-
-Sub ExportPDF(Optional ByVal oSheet as Object)
-	&apos;Exit Sub
-	Dim cursor as Object
-	Dim args(2) as New com.sun.star.beans.PropertyValue
-	Dim fd(2) as New com.sun.star.beans.PropertyValue
-
-	Dim fileName as String
-	Dim fileUrl As String
-
-	cursor = UsedRangeCursor(oSheet)
-	
-	&apos;ThisComponent.CurrentController.select(cursor)
-	fileName = exportFolder &amp; &quot;\&quot; &amp; oSheet.Name &amp; &quot;.pdf&quot;
-	fileUrl = ConvertToUrl(fileName)
-	
-	With args(0) : .Name = &quot;FilterName&quot; : .Value = &quot;calc_pdf_Export&quot; : End With
-	WIth fd(0) : .Name = &quot;Selection&quot; : .Value = cursor : End With
-	
-	&apos; conflicts with the Selection
-	With fd(1) : .Name = &quot;SinglePageSheets&quot; : .Value = False : End With
-	With fd(2) : .Name = &quot;IsSkipEmptyPages&quot; : .Value = True : End With
-		
-	With args(1) : .Name = &quot;FilterData&quot; : .Value = fd : End With
-	With args(2) : .Name = &quot;Overwrite&quot; : .Value = True : End With
-
-	ThisComponent.storeToURL(fileUrl,  args)
-End Sub
-
-</script:module>-
\ No newline at end of file
diff --git a/Standard/CylinderAutomation.xba b/Standard/CylinderAutomation.xba
@@ -0,0 +1,266 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE script:module PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "module.dtd">
+<script:module xmlns:script="http://openoffice.org/2000/script" script:name="CylinderAutomation" script:language="StarBasic">REM  *****  BASIC  *****
+&apos;Option VBASupport 1
+Option Explicit
+
+Global Const rowsToSkip = 1
+Global Const roughHeaderMatch = True
+Global Const highlightBasedOn = &quot;Payment Option&quot; &apos; A yellow background is drawn that matches this column and &apos;highlightSearchString&apos;
+Global Const highlightSearchString = &quot;Online&quot; &apos; Does partial match
+Global Const highlightRemoveColumn = True &apos; True if you want remove the column once the highlight is done
+Global Const reportType = &quot;PENDING&quot; &apos; Allowed values are &quot;PENDING&quot;, &quot;SAFETY&quot; and &quot;SBC&quot;. The number of columns that are copied and the column size will be affected
+Global Const shouldExportPDF = True &apos; If True this will export all the created sheets with Area specific data individually TODO change name to easily understand
+Global Const badDayThreshold = 4 &apos; The days below &apos;the maximum date in report&apos; to show as bad
+Global Const shouldSegregateAreaWise = True&apos; True if you want to create sheets with Area specific data
+Global Const exportFolderPrefix = &quot;C:\Users\bhara\Sync\&quot;
+
+&apos; Values are loaded from the document
+Private tillDate as Date
+Private tillDateStr as String
+Private sheetNameSuffix as String
+Private exportFolder as String
+
+&apos;Private shouldExportSummaryPDF as Boolean&apos; If True the first page will be exported as PDF
+
+Sub Main
+	Dim oDoc : oDoc = ThisComponent
+	Dim s as Object
+	Dim cursor as Object
+	Dim c as Integer
+	Dim destSheet as Variant
+	Dim sheetName$, d$, a$
+	Dim areaColumn%, areaNames as New Collection
+	Dim startColumn%, startRow%, endColumn%, endRow%
+	Dim rowsToRemove%, iArea%
+	Dim areaRange, idRange, bookRange
+	Dim dColumns as Object
+	Dim headerRange as Object
+	Dim cellRangeToCopy as Object
+	Dim pageStyle as Object
+	Dim oCellStyles
+	Dim oConFormat
+	Dim oCondition(2) as new com.sun.star.beans.PropertyValue
+	Dim T4Style
+
+	&apos; Initialize Globals
+	&apos; Casting to Long removes the time component
+	tillDate = CLng(Now())
+
+	s = oDoc.Sheets(0)
+	cursor = UsedRangeCursor(s)
+
+	&apos; NaiveLastTable gives us the last non-blank table&apos;s
+	rowsToRemove = NaiveLastTable(s)
+	If rowsToRemove &lt;&gt; 0 Then
+		s.getRows().removeByIndex(0, rowsToRemove + 1)
+	End If
+
+	startRow = 1
+	startColumn = 0
+	endRow = cursor.RangeAddress.EndRow
+	endColumn = cursor.RangeAddress.EndColumn
+
+	&apos; TODO move this to knowledge base
+	oCellStyles = ThisComponent.StyleFamilies(&quot;CellStyles&quot;)
+	If Not oCellStyles.hasByName(&quot;T4&quot;) Then
+		T4Style = oDoc.createInstance(&quot;com.sun.star.style.CellStyle&quot;)
+		oCellStyles.insertByName(&quot;T4&quot;, T4Style)
+		oCellStyles.getByName(&quot;T4&quot;).CellBackColor = RGB(255, 0, 0)
+	End If
+
+	If ThisComponent.CurrentController.hasFrozenPanes() Then
+		Call UnFreezeSelection
+	End If
+	
+	&apos; TODO pass the column name/index as an argument
+	&apos; Transformations to be applied to the Main sheet before splitting the sheet by Area Name
+	cursor = UsedRangeCursor(s)
+	
+	Call CleanColumnHeaders(s, endColumn)
+	
+	areaColumn = GetHeaderPosition(s, endColumn, &quot;Area&quot;)
+	Call ShortenDirections(s, areaColumn)
+
+	If Left(reportType, 1) = &quot;P&quot; Then
+		Call ApplyTheme &quot;millennium.ots&quot;
+		endColumn = RemoveColumnsExcept(s, endColumn, Array( _
+			&quot;Area&quot;, _
+			&quot;ID&quot;, _
+			&quot;Name&quot;, _
+			&quot;Mobile&quot;, _
+			&quot;Book&quot;, _
+			&quot;Payment Option&quot; _
+		))
+		
+		endColumn = HighlightRowWithColumn(s, endColumn, endRow, _
+			&quot;Payment Option&quot;, &quot;Online Payment&quot;, True, _
+			RGB(255, 255, 0))
+
+		&apos; Justify Leftmost cells to left and Rightmost cells to right
+
+		bookRange = GetColumnRange(s, endColumn, endRow, &quot;Book&quot;)
+		bookRange.setPropertyValue(&quot;HoriJustify&quot;, com.sun.star.table.CellHoriJustify.RIGHT)
+
+		FormatRangeAsNumber(s, new com.sun.star.lang.Locale, oDoc.getNumberFormats(), bookRange, &quot;DD/MM&quot;)
+		
+		tillDate = FindHighestDateAsString(s, &quot;Book&quot;, endColumn, endRow)
+
+		With oCondition(0) : .Name = &quot;Operator&quot; : .Value = com.sun.star.sheet.ConditionOperator.LESS_EQUAL : End With
+		oCondition(1).Name = &quot;Formula1&quot; : oCondition(1).Value = CLng(tillDate - badDayThreshold)
+	    With oCondition(2) : .Name = &quot;StyleName&quot; : .Value = &quot;T4&quot; : End With
+		oConFormat = bookRange.ConditionalFormat
+		oConFormat.clear() : oConFormat.addNew(oCondition)
+
+		SetWidths(s, endColumn, Array( _
+				Array(&quot;Area&quot;, 0), _
+				Array(&quot;ID&quot;, 0), _
+				Array(&quot;Name&quot;, 7000), _
+				Array(&quot;Mobile&quot;, 0), _
+				Array(&quot;Book&quot;, 0) _
+		))
+	ElseIf reportType = &quot;SBC&quot; Then
+		Call ApplyTheme &quot;pumpkin.ots&quot;
+		endColumn = RemoveColumnsExcept(s, endColumn, Array( _
+			&quot;ID&quot;, _
+			&quot;Name&quot;, _
+			&quot;Address&quot;, _
+			&quot;Mobile&quot;, _
+			&quot;Area&quot;, _
+			&quot;No Of Cylinder&quot; _
+			))
+			SetWidths(s, endColumn, Array( _
+				Array(&quot;ID&quot;, 0), _
+				Array(&quot;Name&quot;, 3000), _
+				Array(&quot;Address&quot;, 8000), _
+				Array(&quot;Mobile&quot;, 0), _
+				Array(&quot;Area&quot;, 0) _
+			))
+	End If
+		
+	idRange = GetColumnRange(s	, endColumn, endRow, &quot;ID&quot;) _
+		.setPropertyValue(&quot;HoriJustify&quot;, com.sun.star.table.CellHoriJustify.LEFT)
+
+	Call SortColumn(s, endColumn, endRow, &quot;Area&quot;)
+
+	&apos; Setup exportFolder and sheetName
+	tillDateStr = Replace(CDate(tillDate), &quot;/&quot;, &quot;-&quot;)
+	tillDateStr = Trim(tillDateStr)
+	sheetNameSuffix = &quot; - &quot; &amp; reportType &amp; &quot; &quot; &amp; tillDateStr
+	exportFolder = exportFolderPrefix &amp; &quot;/&quot; &amp; reportType &amp; &quot; &quot; &amp; tillDateStr
+
+	headerRange = s.getCellRangeByPosition(0, 0, endColumn, 0)
+	headerRange.CellStyle = &quot;Heading 1&quot;
+		
+	pageStyle = ThisComponent.StyleFamilies.getByName(&quot;PageStyles&quot;).getByName(&quot;Default&quot;)
+	pageStyle.setPropertyValue(&quot;PrintGrid&quot;, True)
+	pageStyle.setPropertyValue(&quot;CenterHorizontally&quot;, True)
+	&apos; Make the margins 0.2&quot; thick	
+	
+	pageStyle.setPropertyValue(&quot;LeftMargin&quot;, 0.2 * 2540)
+	pageStyle.setPropertyValue(&quot;RightMargin&quot;, 0.2 * 2540)
+
+	&apos; Change original sheet name
+	s.Name = &quot;SUMMARY&quot; &amp; sheetNameSuffix
+
+	&apos; Export summary
+	If shouldExportPDF Then
+		ExportPDF(s, exportFolder)
+	End If
+
+	If NOT shouldSegregateAreaWise Then Exit Sub
+	
+	Dim statusBar
+	statusBar = oDoc.CurrentController.StatusIndicator
+	statusBar.start(&quot;Creating Area Wise&quot;, 10)
+
+	areaRange = s.getCellRangeByPosition(areaColumn, startRow, areaColumn, endRow)
+	a = areaRange.getCellByPosition(0, startRow).getString()
+	&apos;On Error Goto ErrorHandler
+	For iArea = startRow To endRow - 1
+		d = areaRange.getCellByPosition(0, iArea).getString()
+		If  d = &quot;&quot; Then
+			Exit For
+		End If
+
+		If a &lt;&gt; d Or  iArea = (endRow - 1) Then
+			 statusBar.setValue((iArea / endRow) * 100)
+			&apos; FIXME Get the columns automatically from the sheet
+			&apos; FIXME Covert the end column from endColumn above
+
+			Goto Con:
+			Dim cName
+			s.Rows.insertByIndex(startRow, 1)
+			cName = s.getCellByPosition(startColumn, startRow)
+			cName.setString(d)
+			startRow = startRow + 1
+			endRow = endRow + 1
+			
+			Con:
+			&apos; TODO maybe there is an elegant way than
+			&apos; writing this two times
+			sheetName = a &amp; sheetNameSuffix
+
+			&apos; TODO subtotals might solve this
+			&apos;s.group(areaRange.RangeAddress, 1)
+
+			&apos; Prepare Destination sheet
+			destSheet = NewSheet(sheetName)
+			
+			
+			&apos; Copy the Headers from the Main Document
+			If areaColumn &gt; 0 And areaColumn &lt; endColumn Then
+				MsgBox &quot;Can&apos;t have Area column in the middle&quot;, 16
+				Exit Sub
+			End If
+
+			&apos; start                     end
+			&apos;1 :                           endColumn          0 4
+			&apos;areaColumn + 1 : endColumn           2  4
+			&apos;0 :                           endColumn - 1     4  4
+							
+			&apos; Copy Header
+			s.copyRange(destSheet.getCellRangeByName(&quot;A1&quot;).CellAddress, _
+				s.getCellRangeByPosition( _
+				(endColumn - areaColumn)/ endColumn, _
+				0, endcolumn - CInt(areaColumn/endColumn), 0).RangeAddress)
+
+			&apos; Copy Contents
+			s.copyRange(destSheet.getCellRangeByName(&quot;A2&quot;).CellAddress, _
+				s.getCellRangeByPosition(_
+				(endColumn - areaColumn)/ endColumn, _
+				startRow, _
+				endcolumn - CInt(areaColumn/endColumn), _
+				iArea).RangeAddress)
+			
+		    &apos; Customize the Destination Sheet&apos;s Columns
+			If  Left(reportType, 1) = &quot;P&quot; Then
+					SetWidths(destSheet, endColumn, Array( _
+						Array(&quot;ID&quot;, 0), _
+						Array(&quot;Name&quot;, 7000), _
+						Array(&quot;Mobile&quot;, 0), _
+						Array(&quot;Book&quot;, 0) _
+					))
+			ElseIf reportType = &quot;SBC&quot; Then
+						SetWidths(destSheet, endColumn, Array( _
+							Array(&quot;ID&quot;, 0), _
+							Array(&quot;Name&quot;, 4000), _
+							Array(&quot;Address&quot;, 11000), _
+							Array(&quot;Mobile&quot;, 0) _
+						))
+			End If
+	
+			If shouldExportPDF Then
+				ExportPDF(destSheet, exportFolder)
+			End If
+			Continue:
+			startRow = (iArea + 1)
+			a = d
+		End If
+	Next iArea
+
+	ErrorHandler:
+		statusBar.end()
+	Reset
+End Sub
+</script:module>+
\ No newline at end of file
diff --git a/Standard/HandyTools.xba b/Standard/HandyTools.xba
@@ -0,0 +1,472 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE script:module PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "module.dtd">
+<script:module xmlns:script="http://openoffice.org/2000/script" script:name="HandyTools" script:language="StarBasic" script:moduleType="normal">REM  *****  BASIC  *****
+Option Explicit
+
+Function GetFriendlyWords() as Variant
+	GetFriendlyWords = Array( _
+	    &quot;AreaCodeDesc&quot;, &quot;Area&quot;, _
+	     &quot;AreaDescription&quot;,  &quot;Area&quot;, _
+	    &quot;ConsumerNumber&quot;, &quot;ID&quot;, _
+	    &quot;ConsumerName&quot;, &quot;Name&quot;, _
+	    &quot;MobileNumber&quot;, &quot;Mobile&quot;, _
+	    &quot;BookDate&quot;, &quot;Book&quot;_
+	    )
+	    If (UBound(GetFriendlyWords) Mod 2) &lt;&gt; 1 Then
+			Print &quot;Mismatch in friendlyWords array&quot;
+			Exit Function
+		End If
+End Function
+
+Sub FormatRangeAsNumber(oSheet, oLocale, ByRef oFormats, oRange, formatStr as String)
+	&apos; BASIC equivalent of &apos;Text to Columns&apos;
+	Dim formatNum as Long, i as Integer
+	Dim oReplace
+	oReplace = oRange.createReplaceDescriptor()
+	
+	If oRange.getCellByPosition(0,0).getValue() = 0 Then
+		formatNum = oFormats.queryKey(formatStr, oLocale, False)
+		&apos; add formatStr if it doesn&apos;t exist
+		If formatNum = -1 Then
+			formatNum = oFormats.addNew(formatStr, oLocale)
+			If formatNum = -1 Then
+				MsgBox &quot;Cannot add &quot; &amp; formatStr &amp; &quot; as NumberFormat&quot;, 0, &quot;Fatal&quot;
+				Exit Sub
+			End If
+		End If
+		
+		With oReplace
+			.searchString = &quot;.+&quot;
+			.replaceString = &quot;&amp;&quot;
+			.SearchRegularExpression = True
+		End With
+
+		oRange.NumberFormat = formatNum
+		oRange.replaceAll(oReplace)
+	End If
+End Sub
+
+Function GetColumnRange(oSheet, endColumn as Long, endRow as Long, searchString as String) as Object
+	Dim iHeaderPos
+	&apos;GetColumn = -1
+	iHeaderPos = GetHeaderPosition(oSheet, endColumn, searchString)
+	If iHeaderPos = -1 Then
+		Exit Function
+	End If
+	GetColumnRange = oSheet.getCellRangeByPosition(iHeaderPos, 1, iHeaderPos, endRow)
+End Function
+
+Function RemoveColumns(oSheet, endColumn as Integer, columnNames) as Long
+	Dim columnName as String
+	Dim iHeader as Integer
+	RemoveColumns = endColumn
+	For Each columnName in columnNames
+		iHeader = GetHeaderPosition(oSheet, endColumn, columnName)
+		If iHeader &lt;&gt; -1 Then
+			oSheet.getColumns().removeByIndex(iHeader, 1)
+			endColumn = endColumn - 1
+		End If
+	Next
+	RemoveColumns = endColumn
+End Function
+
+Function RemoveColumnsExcept(oSheet as Object, endColumn as Integer, columnNames as Variant) as Long
+	Dim headerStr as String, columnName as String
+	Dim i% : i = 0
+	Dim rI% : rI = 0
+	Dim columnsToRemove(endColumn) as String
+	Dim headerRange, headerData
+	Dim found as Boolean : found = False
+	RemoveColumnsExcept = endColumn
+
+	headerRange = oSheet.getCellRangeByPosition(0, 0, endColumn, 0)
+	headerData = headerRange.getFormulaArray()
+
+	&apos; TODO Handle condition when columnNames does not have valid header
+	&apos; This causes an extra string element in the array
+	For i = 0 To UBound(headerData(0))
+		headerStr = headerData(0)(i)
+		found = False
+		For Each columnName in columnNames
+			If headerStr = columnName Then
+				found = True
+				Exit For
+			End If
+		Next columnName
+		If Not found Then
+			columnsToRemove(rI) = headerStr
+			rI = rI + 1
+		End If
+	Next i
+	
+	If i = 0 Then
+		Exit Function
+	End If
+
+	ReDim Preserve columnsToRemove(rI - 1)
+	
+	RemoveColumnsExcept = RemoveColumns(oSheet, endColumn, columnsToRemove)
+End Function
+
+Sub ApplyTheme(sFileName As String)
+	Dim oFamilies
+	Dim aOptions(0) as New com.sun.star.beans.PropertyValue
+	Dim stylesDir()
+	Dim StylePath as String
+	GlobalScope.BasicLibraries.loadLibrary(&quot;Tools&quot;)
+	oFamilies = ThisComponent.StyleFamilies
+	StylesDir = GetOfficeSubPath(&quot;Template&quot;, &quot;wizard/styles/&quot;)
+	StylePath = StylesDir &amp; sFileName
+	aOptions(0).Name = &quot;OverwriteStyles&quot;
+	aOptions(0).Value = true
+	oFamilies.loadStylesFromURL(StylePath, aOptions())
+End Sub
+
+Function NaiveLastTable(oSheet) as Long
+	&apos; Return the row position last table that is separated by an empty row
+	Dim oRows
+	Dim iRow as Long
+	Dim usedRange
+	NaiveLastTable = 0
+
+	oRows = UsedRangeCursor(oSheet).Rows
+
+	For iRow = 0 To oRows.getCount() - 1
+		&apos;Print rows(i).dbg_methods()
+		If oRows.getByIndex(iRow).computeFunction(com.sun.star.sheet.GeneralFunction.COUNT) = 0 Then
+			NaiveLastTable = iRow
+		End If
+	Next
+End Function
+
+Function IsValidFileName(fileName as String) as Boolean
+    Dim invalidChars as String : invalidChars = &quot;\/:*?&quot;&quot;&lt;&gt;|&quot;
+    Dim c as String
+    Dim i as Integer
+    IsValidFileName = False
+    For i = 1 To Len(fileName)	
+   		c = Mid(fileName, i, 1)
+    	If InStr(invalidChars, c) &gt; 0 Then
+    		Print &quot;Invalid character &apos;&quot; &amp; c &amp; &quot;&apos; found in &apos;&quot; &amp; fileName &amp; &quot;&apos;&quot;
+    		Exit Function
+    	End If
+    Next
+    IsValidFileName = True
+End Function
+
+Sub ExportPDF(oSheet, exportFolder as String)
+	Dim cursor as Object
+	Dim args(2) as New com.sun.star.beans.PropertyValue
+	Dim fd(2) as New com.sun.star.beans.PropertyValue
+	Dim fileName as String
+
+	If Not IsValidFileName(oSheet.Name) Then
+		Exit Sub
+	End If
+
+	fileName = exportFolder &amp; &quot;/&quot; &amp; oSheet.Name &amp; &quot;.pdf&quot;
+	
+	WIth fd(0) : .Name = &quot;Selection&quot; : .Value = UsedRangeCursor(oSheet) : End With
+	&apos; Disabled, because it conflicts with selection
+	With fd(1) : .Name = &quot;SinglePageSheets&quot; : .Value = False : End With
+	With fd(2) : .Name = &quot;IsSkipEmptyPages&quot; : .Value = True : End With
+
+	With args(0) : .Name = &quot;FilterName&quot; : .Value = &quot;calc_pdf_Export&quot; : End With
+	With args(1) : .Name = &quot;FilterData&quot; : .Value = fd : End With
+	With args(2) : .Name = &quot;Overwrite&quot; : .Value = True : End With
+
+	ThisComponent.storeToURL(ConvertToUrl(fileName),  args)
+End Sub
+
+
+Sub ListOfActiveCustomers()
+	Dim oSheet, destSheet, cursor, oRange
+	Dim startRow as Long
+	Dim endRow as Long
+	Dim startColumn as Long
+	Dim endColumn as Long
+	Dim requiredFields()
+	Dim requiredFieldIndices() as Integer
+	Dim iColumn as Integer, iRequiredField as Integer
+	Dim headerCellStr as String
+
+	oSheet = ThisComponent.Sheets(0)
+	cursor = UsedRangeCursor(oSheet)
+
+	&apos; Skip Header
+	startRow =  NaiveLastTable(ThisComponent, cursor)
+	endRow = cursor.RangeAddress.EndRow
+	endColumn = cursor.RangeAddress.EndColumn
+	&apos; TODO Use data from PhoneNumber if some MobileNumber is missing
+	requiredFields = Array(&quot;ConsumerNumber&quot;, &quot;ConsumerName&quot;, &quot;MobileNumber&quot;, &quot;AreaCodeDesc&quot;, &quot;LastDelivDate&quot;)
+	&apos;oSheet.Rows.removeByIndex(0, 3)
+
+	For iColumn = startColumn To endColumn
+		headerCellStr = oSheet.getCellByPosition(iColumn, startRow, iColumn, startRow).getString()
+		For iRequiredField = 0 To UBound(requiredFields)
+			If headerCellStr = requiredFields(iRequiredField) Then
+				&apos; TODO Allocate memory before hand and use a tracker index?
+				ReDim Preserve requiredFieldIndices(UBound(requiredFieldIndices) + 1)
+				requiredFieldIndices(UBound(requiredFieldIndices)) = iColumn
+			End If
+		Next iRequiredField
+	Next iColumn
+	destSheet = NewSheet(&quot;SBC Connection&quot;)
+	
+	Print Join(requiredFieldIndices)
+	
+	&apos; TODO Ignore the Header Table if it exists
+	&apos; i.e. If two tables exists, assume the first one to be header table
+	&apos; and ignore it while copying to new sheet
+	
+	&apos; Copy the cells in &apos;requiredFieldIndices&apos; to &apos;destSheet&apos;
+End Sub
+
+
+Function UsedRangeCursor(Optional ByRef oSheet as Object) as Object
+	Dim cursor as Object
+	if IsNull(oSheet) Or IsMissing(oSheet) Then
+			oSheet = ThisComponent.getCurrentController().getActiveSheet()
+	End If
+	cursor = oSheet.createCursor()
+	cursor.gotoStartOfUsedArea(False)
+	cursor.gotoEndOfUsedArea(True)
+	UsedRangeCursor = cursor
+End Function
+
+Function UserFriendlyName(str as String) as String
+	&apos; Checks headerName and headerNameMatches for the string &apos;str&apos;,
+	&apos; If it&apos;s not available there, a naïve implementation adds spacing to &apos;str&apos;
+	Dim l as Long
+	Dim c as String
+	Dim i as Long
+	Dim prevChar as String
+	Dim friendlyWords : friendlyWords = GetFriendlyWords()
+	str = Trim(str)
+	&apos; Check if Len(str) is really called multiple times
+	l = Len(str)
+	&apos; Check with header &quot;database&quot;
+
+	For i = 0 To UBound(friendlyWords) Step 2
+		If friendlyWords(i) = str Then
+			UserFriendlyName = friendlyWords(i + 1)
+			Exit Function
+		End If
+	Next i
+
+	For i = 0 To l
+		c = Mid(str, i + 1, 1)
+		If i &gt; 1 And (c &gt; &quot;A&quot; And c &lt; &quot;Z&quot;) Then
+				&apos; Only prepend space if &apos;c&apos; is not the first character...
+				&apos; or previous char doesn&apos;t have space
+				UserFriendlyName = UserFriendlyName &amp;  IIf(prevChar = &quot; &quot;, &quot;&quot;, &quot; &quot;)  &amp; c
+		Else
+			UserFriendlyName = UserFriendlyName &amp; c
+		End If
+		prevChar = c
+	Next
+End Function
+
+Sub SortColumn(oSheet As Variant, endColumn as Integer, endRow as Integer, columnName as String)
+	Dim oRange as Object
+	Dim iHeaderPos as Integer
+	Dim oSortFields(1) as new com.sun.star.util.SortField
+	Dim oSortDesc(0) as new com.sun.star.beans.PropertyValue
+	
+	oRange = oSheet.getCellRangeByPosition(0, 1, endColumn, endRow)
+	ThisComponent.getCurrentController.select(oRange)
+	
+	iHeaderPos = GetHeaderPosition(oSheet, endColumn, columnName)
+	If iHeaderPos = -1 Then
+		Exit Sub
+	End If
+	&apos; Area
+	oSortFields(0).Field = iHeaderPos
+	oSortFields(0).SortAscending = True
+	
+	&apos; TODO Enable this later
+	&apos; Date
+	&apos;oSortFields(1).Field = 4
+	&apos;oSortFields(1).SortAscending = True
+	
+	oSortDesc(0).Name = &quot;SortFields&quot;
+    oSortDesc(0).Value = oSortFields
+	
+	oRange.Sort(oSortDesc)
+End Sub
+
+Function NewSheet(sheetName as String) as Object
+			Dim sheets as Object
+			sheets = ThisComponent.Sheets()
+			If Not sheets.hasByName(sheetName) Then
+				sheets.insertNewByName(sheetName, sheets.getCount())
+			End If
+			NewSheet = sheets.getByName(sheetName)
+End Function
+
+Sub CleanColumnHeaders(oSheet as Object, endColumn as Integer)
+	Dim cell as Object
+	Dim neatName as String
+	Dim i as Integer
+	For i = 0 To endColumn
+		cell = oSheet.getCellByPosition(i, 0)
+		neatName = cell.getString()
+		neatName = UserFriendlyName(neatName)
+		cell.setString(neatName)
+	Next
+End Sub
+
+Sub RemoveExtraMergeCells(oSheet as Object, oRangeAddress as Variant)
+	Dim oRange as Object
+	Dim oSortFields(0) as new com.sun.star.util.SortField
+	Dim oSortDesc(0) as new com.sun.star.beans.PropertyValue
+	Dim endRow as Integer
+	
+	endRow = oRangeAddress.EndRow
+	lastColumn = oRangeAddress.EndColumn
+		
+	rem set the range on which to sort&apos;
+
+	&apos;oRange = oSheet.getCellRangeByName(&quot;A2:F20&quot;)
+	oRange = oSheet.getCellRangeByPosition(0, 0, lastColumn, 0)
+	ThisComponent.getCurrentController.select(oRange)
+	initialColumnCount = oRange.Columns.getCount() - 1
+	deletedColumns = 0
+	For j = 0 To (initialColumnCount - deletedColumns)
+		oCell = oRange.getCellByPosition(j, 0)
+		con = oCell.String
+		If con = &quot;&quot; Then
+			oRange.Columns.removeByIndex(j, 1)
+			deletedColumns = deletedColumns - 1
+		Else
+			Print con
+		End If
+	Next
+End Sub
+
+
+Sub UnFreezeSelection
+	Dim document   as Object
+	Dim dispatcher as Object
+	document   = ThisComponent.CurrentController.Frame
+	dispatcher = createUnoService(&quot;com.sun.star.frame.DispatchHelper&quot;)
+	dispatcher.executeDispatch(document, &quot;.uno:FreezePanes&quot;, &quot;&quot;, 0, Array())
+End Sub
+
+Function GetHeaderPosition(ByRef oSheet as Object, endColumn as Integer, searchString as String) as Integer
+	Dim iColumn as Integer
+	Dim oCell as Object
+	Dim cellString as String
+	GetHeaderPosition = -1
+	If searchString = &quot;&quot; Then
+		Exit Function
+	End If
+	If roughHeaderMatch Then
+		searchString = UserFriendlyName(searchString)
+	End If
+	For iColumn = 0 To endColumn
+        oCell = oSheet.getCellByPosition(iColumn, 0)
+        cellString = oCell.String
+        If cellString = &quot;&quot; Then
+        	&apos;MsgBox &quot;Cannot have empty column, endColumn is &quot; &amp; CStr(endColumn), 16, &quot;Bad argument&quot;
+        	Exit Function
+        End If
+        If roughHeaderMatch Then
+			cellString = UserFriendlyName(cellString)
+		End If
+        If  cellString = searchString OR Instr(cellString, searchString) = 1 Then
+            GetHeaderPosition = iColumn
+            Exit For
+        End If
+    Next iColumn
+End Function
+
+Function HighlightRowWithColumn(ByRef oSheet as Object, endColumn as Long, endRow as Long, highlightColumnName, highlightValue, removeColumn as Boolean, Optional highlightColor as Long) as Long
+	Dim currentStr as String
+	Dim oCell as Object
+	Dim oCellRange as Object
+	Dim str as String
+	Dim iSearchColumn as Long
+	Dim i as Integer
+	HighlightRowWithColumn = endColumn
+	
+
+	&apos; Search for column position that has highlightBasedOn
+	iSearchColumn = GetHeaderPosition(oSheet, endColumn, highlightColumnName)
+	If iSearchColumn = -1 Then Exit Function
+	For i = 0 To  endRow - 1
+		oCell = oSheet.getCellByPosition(iSearchColumn, i)
+		If InStr(oCell.getString(), highlightValue) &gt; 0 Then
+			oCellRange = oSheet.getCellRangeByPosition(0, i, endColumn, i)
+			oCellRange.CellBackColor = highlightColor
+		End If
+	Next i
+
+	If removeColumn Then
+		oSheet.Columns.removeByIndex(iSearchColumn, 1)
+		endColumn = endColumn - 1
+	End If
+End Function
+
+Function FindHighestDateAsString(oSheet as Object, columnName as String, endColumn as Integer, endRow as Integer) as Date
+	Dim dateHeaderPos
+	Dim dateColumn
+	Dim dateVal
+	dateHeaderPos = GetHeaderPosition(oSheet, endColumn, columnName)
+	If dateHeaderPos = -1 Then
+		MsgBox &quot;Cannot find a column header that starts with &apos;&quot; &amp; columnName &amp; &quot;&apos;&quot;, 0, &quot;Bad column name&quot;
+		Exit Function
+	End If
+	&apos; TODO Check if the column we are searching, actually has date values.
+	dateColumn = oSheet.getCellRangeByPosition(dateHeaderPos, 1, dateHeaderPos, endRow)
+	dateVal =  dateColumn.computeFunction(com.sun.star.sheet.GeneralFunction.MAX)
+	If dateVal = 0 Then MsgBox &quot;Invalid date found in column &apos;&quot; &amp; columnName &amp; &quot;&apos; - &quot; &amp; dateHeaderPos &amp; &quot;&quot;, 16, &quot;Bad date found&quot;
+	FindHighestDateAsString = dateVal
+End Function
+
+&apos; TODO Use a general function that takes arrays
+&apos; and instead of the function ShortenDirections, use
+&apos; ReplaceArrays to make it more usable across other projects.
+Sub ShortenDirections(oSheet as Object, columnIdx as Integer)
+	&apos;Shorten East as E. etc
+	Dim toReplace() as String
+	Dim toReplaceWith() as String
+	Dim i as Long
+	Dim oDescriptor
+	Dim oColumn
+	Dim columnToReplace
+	oColumn = oSheet.getColumns().getByIndex(columnIdx)
+	&apos; TODO arrange it with convention
+	toReplace() = Array(&quot;East&quot;, &quot;West&quot;, &quot;South&quot;, &quot;North&quot;)
+	toReplaceWith() = Array(&quot;E.&quot;, &quot;W.&quot;, &quot;S.&quot;, &quot;N.&quot;)
+	oDescriptor = oColumn.createReplaceDescriptor()
+	For i = LBound(toReplace) To UBound(toReplace)
+		With oDescriptor
+			.SearchString = toReplace(i)
+			.ReplaceString = toReplaceWIth(i)
+		End With
+		oColumn.replaceAll(oDescriptor)
+	Next i
+End Sub
+				
+Sub SetWidths(oSheet, endColumn as Integer, columnWidthArray() as Variant)
+	&apos; columnWidthArray has values like Array(Array(&quot;Area&quot;, 0), Array(&quot;ID&quot;, 2000))
+	&apos; 0 means autofill
+	Dim i as Integer
+	Dim columnWidth
+	Dim iHeaderColumn
+	For Each columnWidth in columnWidthArray
+		iHeaderColumn = GetHeaderPosition(oSheet, endColumn, columnWidth(0))
+		If iHeaderColumn = -1 Then
+			Print &quot;Cannot find: &quot; &amp; columnWidth(0) &amp; &quot; in headers&quot;
+			Exit Sub
+		End If
+		if columnWidth(1) = 0 Then
+			oSheet.getColumns().getByIndex(iHeaderColumn).OptimalWidth = True
+		Else
+			oSheet.getColumns().getByIndex(iHeaderColumn).Width = columnWidth(1)
+		End If
+	Next
+End Sub
+
+</script:module>+
\ No newline at end of file
diff --git a/Standard/script.xlb b/Standard/script.xlb
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE library:library PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "library.dtd">
 <library:library xmlns:library="http://openoffice.org/2000/library" library:name="Standard" library:readonly="false" library:passwordprotected="false">
- <library:element library:name="Bharatgas"/>
+ <library:element library:name="HandyTools"/>
+ <library:element library:name="CylinderAutomation"/>
 </library:library> 
\ No newline at end of file