Libreoffice Writer Macro Change Font Size - basic

This question was posted to help solve this Ask Ubuntu 350 point bounty that ends today. I would rather someone in Stack Overflow post an answer and get the bounty than see it go unrewarded and the OP not getting a working solution.
I have this macro pieced together from three sources (sorry kind of ugly at this stage).
The total project is to change everything not 18 pt to 12 pt. Then change 18 pt to 22 pt. Then set Heading 1 to 28 pt. I've spent hours trying to get this simple thing done by recording macros which just leaves one dissappointed.
Here is the recorded macro so far:
to change 10 point to 12 point. It runs without error but doesn't change a thing:
Sub AllFonts
rem - change all font names to Ubuntu.
rem - If heading 1 set font size to 28
rem - else if font size is 18 set to 22
rem - else set font size to 12
rem The macro will save document and exit Libreoffice Writer.
Dim CharHeight As Long, oSel as Object, oTC as Object
Dim CharStyleName As String
Dim oParEnum as Object, oPar as Object, oSecEnum as Object, oSec as Object
Dim oVC as Object, oText As Object
Dim oParSection 'Current Section
oText = ThisComponent.Text
oSel = ThisComponent.CurrentSelection.getByIndex(0) 'get the current selection
oTC = oText.createTextCursorByRange(oSel) ' and span it with a cursor
rem Scan the cursor range for chunks of given text size.
rem (Doesn't work - affects the whole document)
oParEnum = oTC.Text.createEnumeration()
Do While oParEnum.hasMoreElements()
oPar = oParEnum.nextElement()
If oPar.supportsService("com.sun.star.text.Paragraph") Then
oSecEnum = oPar.createEnumeration()
oParSection = oSecEnum.nextElement()
Do While oSecEnum.hasMoreElements()
oSec = oSecEnum.nextElement()
If oSec.TextPortionType = "Text" Then
CharStyleName = oParSection.CharStyleName
CharHeight = oSec.CharHeight
if CharStyleName = "Heading 1" Then
oSec.CharHeight = 28
elseif CharHeight = 18 Then
oSec.CharHeight = 22
else
oSec.CharHeight = 12
End If
End If
Loop
End If
Loop
FileSave
stardesktop.terminate()
End Sub
Sub UbuntuFontName
rem ----------------------------------------------------------------------
rem define variables
dim document as object
dim dispatcher as object
rem ----------------------------------------------------------------------
rem get access to the document
document = ThisComponent.CurrentController.Frame
dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")
rem ----------- Select all text ------------------------------------------
dispatcher.executeDispatch(document, ".uno:SelectAll", "", 0, Array())
rem ----------- Change all fonts to Ubuntu -------------------------------
dim args5(4) as new com.sun.star.beans.PropertyValue
args5(0).Name = "CharFontName.StyleName"
args5(0).Value = ""
args5(1).Name = "CharFontName.Pitch"
args5(1).Value = 2
args5(2).Name = "CharFontName.CharSet"
args5(2).Value = -1
args5(3).Name = "CharFontName.Family"
args5(3).Value = 0
args5(4).Name = "CharFontName.FamilyName"
args5(4).Value = "Ubuntu"
dispatcher.executeDispatch(document, ".uno:CharFontName", "", 0, args5())
end sub
sub FileSave
rem ----------------------------------------------------------------------
rem define variables
dim document as object
dim dispatcher as object
rem ----------------------------------------------------------------------
rem get access to the document
document = ThisComponent.CurrentController.Frame
dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")
rem ----------------------------------------------------------------------
dispatcher.executeDispatch(document, ".uno:Save", "", 0, Array())
end sub
It crashes at end with this message:

Here is the corrected code. However, the details about Heading 1 are not clear. The code below assumes that the headings have been used properly, with the paragraph style applied to text with no direct formatting.
Sub ChangeAllFonts
rem - Change all font names to Ubuntu.
rem - If heading 1 set font size to 28
rem - else if font size is 18 set to 22
rem - else set font size to 12
rem - The macro will save document and exit LibreOffice Writer.
Dim oDoc As Object
Dim oParEnum As Object, oPar As Object, oSecEnum As Object, oSec As Object
Dim oFamilies As Object, oParaStyles As Object, oStyle As Object
oDoc = ThisComponent
oParEnum = oDoc.Text.createEnumeration()
Do While oParEnum.hasMoreElements()
oPar = oParEnum.nextElement()
If oPar.supportsService("com.sun.star.text.Paragraph") Then
oSecEnum = oPar.createEnumeration()
Do While oSecEnum.hasMoreElements()
oSec = oSecEnum.nextElement()
If oSec.TextPortionType = "Text" Then
If oSec.ParaStyleName = "Heading 1" Then
rem ignore for now
ElseIf oSec.CharHeight = 18 Then
oSec.CharHeight = 22.0
Else
oSec.CharHeight = 12.0
End If
End If
Loop
End If
Loop
oFamilies = oDoc.getStyleFamilies()
oParaStyles = oFamilies.getByName("ParagraphStyles")
oStyle = oParaStyles.getByName("Heading 1")
oStyle.setPropertyValue("CharHeight", 28.0)
FileSave
StarDesktop.terminate()
End Sub
Exiting LibreOffice from a macro without a crash is notoriously tricky. For batch processing, it's better to close the document and leave the LO application open. Then when it's all finished, one approach is to force kill the process from a shell script.
There is plenty of information online about other ways to exit LO gracefully.

Related

SAP GUI script from Excel VBA Macro optimizing

I am trying to optimize my Excel VBA to SAP connection and don't want to click "OK" on two message boxes that appear when starting the following code:
1 Sub SAP_1()
2
3 Dim obj_Shell As Object
4 Dim obj_SAPGUI As Object
5 Dim obj_Application As Object
6 Dim obj_Connection As Object
7 Dim obj_session As Object
8
9 Application.DisplayAlerts = False
10 Shell "C:\Program Files (x86)\SAP\FrontEnd\SAPgui\saplogon.exe", 4
11 Set obj_Shell = CreateObject("WScript.Shell")
12 Do Until obj_Shell.AppActivate("SAP Logon")
13 application.Wait Now + TimeValue("0:00:01")
14 Loop
15 Set obj_Shell = Nothing
16 Set obj_SAPGUI = GetObject("SAPGUI")
17 Set obj_Application = obj_SAPGUI.GetScriptingEngine
18 Set obj_Connection = obj_Application.OpenConnection(str_ConName, True)
19 Set obj_session = obj_Connection.Children(0)
20 ' rest of the code
21 Application.DisplayAlerts = True
22 End Sub
How can I avoid the following SAP message boxes or click them via VBA:
Line 17: "A script tries to access SAP"
Line 18: "A script opens a connection to the following system: ..."
And what's the differents to the code below? Why is the SAP GUI Scripting asking not to define them as Objects? Is this a better alternative?
1 If Not IsObject(obj_SAPGUI) Then
2 Set obj_SAPGUI = GetObject("SAPGUI")
3 Set obj_Application = obj_SAPGUI.GetScriptingEngine
4 End If
5 If Not IsObject(obj_Connection) Then
6 Set obj_Connection = obj_Application.Children(0)
7 End If
8 If Not IsObject(obj_session) Then
9 Set obj_session = obj_Connection.Children(0)
10 End If
11 If IsObject(obj_WScript) Then
12 obj_WScript.ConnectObject obj_session, "on"
13 obj_WScript.ConnectObject obj_Application, "on"
14 End If
Are there other things in the code that can be optimized?
Thank you for your help.
In order to avoid the messages that a script tires to access resp. connect to the SAPGUI you have to change settings either in the registry or via SAPGUI.
In the SAPGUI press Alt-F12 and then select Options, goto Scripting, and uncheck all check boxes below Enable scripting.
These settings are stored in the registy and one could also use VBA code to set them. The key is HKEY_CURRENT_USER\Software\SAP\SAPGUI Front\SAP Frontend Server\Security\
Thank you very much, that's my final code right now:
Function RegKeyExists(i_RegKey As String) As Boolean
Dim myWS As Object
Set myWS = CreateObject("WScript.Shell")
On Error GoTo ErrorHandler
myWS.RegRead i_RegKey
RegKeyExists = True
Exit Function
ErrorHandler:
RegKeyExists = False
End Function
'
'-----------------------------------------------------------------------
Sub RegKeyReset()
Dim obj_WS As Object
Dim RegKey1 As String
Dim RegKey2 As String
Dim RegKey3 As String
Set obj_WS = CreateObject("WScript.Shell")
RegKey1 = "HKEY_CURRENT_USER\Software\SAP\SAPGUI Front\SAP Frontend Server\Security\UserScripting"
RegKey2 = "HKEY_CURRENT_USER\Software\SAP\SAPGUI Front\SAP Frontend Server\Security\WarnOnAttach"
RegKey3 = "HKEY_CURRENT_USER\Software\SAP\SAPGUI Front\SAP Frontend Server\Security\WarnOnConnection"
' RegKey1
If RegKeyExists(RegKey1) = False Then
Exit Sub
Else
obj_WS.RegWrite RegKey1, 1, "REG_DWORD" ' Value = 1, Type = Boolean
End If
' RegKey2
If RegKeyExists(RegKey2) = False Then
Exit Sub
Else
obj_WS.RegWrite RegKey2, 0, "REG_DWORD" ' Value = 0, Type = Boolean
End If
' RegKey3
If RegKeyExists(RegKey3) = False Then
Exit Sub
Else
obj_WS.RegWrite RegKey3, 0, "REG_DWORD" ' Value = 0, Type = Boolean
End If
End Sub
'
'-----------------------------------------------------------------------
Sub SAPTransaction()
Dim ...
Set ...
Call RegKeyReset ' <--------------------------- Problem solved here...
' rest of the code
End Sub
'
I did it this way, because I wont be the only person/user to use the macro, so I don't have to tell everybody to change their settings in SAP.
Also thanks to: https://www.slipstick.com/developer/read-and-change-a-registry-key-using-vba/

Indexing of repeating content control items

My question is based on this question and this solution:
I have a similar problem, but I need to insert items in order, but I could not index the inserted repeating content controls correctly. I do not know how many items I should insert in advance, so inserting could be fully dynamic.
Could anybody help me?
Here is a simple code:
Dim wordApp As Variant
Dim wDoc As Variant
Set wordApp = CreateObject("word.application")
wordApp.DisplayAlerts = False
Set wDoc = wordApp.Documents.Open(ThisWorkbook.Path & "/example.docm")
wordApp.Visible = True
Dim i As Integer
Dim counter As Integer
counter = 1
Dim cc As Variant
Dim repCC As Variant
Set cc = wDoc.SelectContentControlsByTag("container").Item(1)
For i = 1 To 4
If counter <> 1 Then
Set repCC = cc.RepeatingSectionItems.Item(cc.RepeatingSectionItems.Count)
repCC.InsertItemAfter
End If
wDoc.SelectContentControlsByTag("number").Item(counter).Range.Text = counter
counter = counter + 1
Next i
A picture of my word doc:
The tag name of the repeating content control is "container". The tag name of the rich text content control is "number".
A picture of the wrong result:
And what I would like to get :)
Thank you for your help in advance!
Finally I could resolve my problem:
TASK: This is a simply example of inserting Repeating Section Content Controls (RSCC) dynamically from vba and fill out their inner Content Controls in order.
PROBLEM: When inserting a new RSCC like here, their Content Controls will get the same tags (titles), and indexes are assigned randomly.
SOLUTION: Content controls must be filled out on the fly, when a new RSCC has been just inserted.
Dim cc As Variant
Dim repCC As Variant
Dim i As Integer
Set cc = wDoc.SelectContentControlsByTag("container").Item(1)
For i = 1 To 4 'it could be any number
If i = 1 Then 'because already has a RSCC in the doc file, so I need only 3 more.
Set repCC = cc.RepeatingSectionItems.Item(1)
Else
repCC.InsertItemAfter
Set repCC = cc.RepeatingSectionItems.Item(i) '(or .Item(cc.RepeatingSectionItems.Count))
End If
For Each cc_current In repCC.Range.ContentControls
Select Case cc_current.Tag
Case Is = "number"
cc_current.Range.Text = i
'Case Is = .....
End Select
Next cc_current
Next i

Printing multiple document types with different page settings

I have an automation script running whose output is various document types and printing those documents with various printer settings. I am printing to the same printer for all documents, so that's nice. I have three excel worksheets that need to be printed. These I am printing using the built-in "PrintOutEx" method. One onto a letter size paper and two to 11x17. Next, I have the script print a word document, which I am printing using the built-in "PrintOut" method. This is to be printed on an 11x17 paper but scaled so that two pages print side by side. Lastly, I have to print a multi-page tiff document which I am printing using a print handler. I am having a number of issues with this and I apologize if these are silly question and appreciate any help I can gather.
I am trying to keep the default printer the same as when I start the subroutines and that doesn't seem to be working using the PrinterSettings methods.
About the default printer: the default printer changes to the printer I want (doesn't change back at the end). The default printer is set to 11x17. If I change the default settings of the printer to 8.5x11 everything prints to 8.5x11.
The script seems to be ignoring the settings/pagesettings/setups that I am providing it with.
The word document print is not scaled to fit the paper and I have tried setting the paper size to 11x17 as well as 8.5x11 and scaling the print but that just looks really bad.
The first excel worksheet does not print to a letter size paper.
The second and third worksheets work okay (as long as the default printer settings are as desired; see point 2).
The multipage tiff document prints okay as long as the default printer's default settings are correct.
I am not a professional programmer so I apologize if the question has a simple answer or the code looks horrifying. I researched tons on this question and couldn't find a solution that worked. :( I stepped through the code line by line and there are no errors. I am attaching the code I have thus far.
Thanks!!!
Public Sub PrintSPIDPackage(SPID As String)
On Error GoTo errHandler
Dim printerSettings As New Printing.PrinterSettings
Dim curPrinter As String
Dim engPrinter As String
Dim purPrinter As String
engPrinter = "engBLAH"
purPrinter = "purBLAH"
curPrinter = printerSettings.PrinterName
Dim spidBBPath As String
spidBBPath = ****REDACTED
spidDrawingPackagePath = ***REDACTED***
Dim eAPP As New Excel.Application
Dim wB As Excel.Workbook
Dim wS As Excel.Worksheet
Dim rng As Excel.Range
Dim lRow As Integer
Dim wAPP As New Word.Application
Dim doc As Word.Document
wB = eAPP.Workbooks.Open(spidBBPath)
wS = wB.Worksheets(selectedSheet & " OPs")
wS.PageSetup.PaperSize = Excel.XlPaperSize.xlPaperLetter 'I would think this causes the printer to printout to a letter size paper
wS.PrintOutEx(ActivePrinter:=engPrinter)
wS = wB.Worksheets(selectedSheet)
lRow = wS.Range("A" & wS.Rows.Count).End(Excel.XlDirection.xlUp).Row + 3
rng = wS.Range("A1:I" & lRow)
rng.PrintOutEx(ActivePrinter:=engPrinter) 'PRINT SHOULD BE AN 11x17
wS = wB.Worksheets(selectedSheet & " MLB")
wS.PrintOutEx(ActivePrinter:=engPrinter) 'PRINT SHOULD BE AN 11x17
wB.Close(False)
eAPP.Quit()
Runtime.InteropServices.Marshal.ReleaseComObject(wS)
Runtime.InteropServices.Marshal.ReleaseComObject(wB)
Runtime.InteropServices.Marshal.ReleaseComObject(eAPP)
If InStr(xPath, ".doc") <> 0 Then
doc = wAPP.Documents.Open(xPath)
wAPP.ActivePrinter = engPrinter
doc.PageSetup.PaperSize = Word.WdPaperSize.wdPaperLetter ''''''print out should be 11x17 with two pages side by side
wAPP.PrintOut(PrintZoomColumn:=2, PrintZoomRow:=1) ', PrintZoomPaperHeight:=2 * (11 * 1440), PrintZoomPaperWidth:=2 * (8.5 * 1440)) '''''AS you see I tried scaling...
doc.Close(False)
doc = Nothing
wAPP.Quit()
wAPP = Nothing
End If
Dim pDoc As New Printing.PrintDocument
currPage = 0
AddHandler pDoc.PrintPage, AddressOf pDocPage
PrintDialog1.Document = pDoc
pDoc.DefaultPageSettings.Landscape = True
pDoc.DocumentName = SPID & "-Drawing Package"
pDoc.PrinterSettings.PrinterName = engPrinter
pDoc.DefaultPageSettings.Landscape = True
pDoc.DefaultPageSettings.Margins.Left = 50
pDoc.DefaultPageSettings.Margins.Right = 50
pDoc.DefaultPageSettings.Margins.Top = 50
pDoc.DefaultPageSettings.Margins.Bottom = 50
Dim ps As New Printing.PaperSize("Tabloid", 1700, 1100)
ps.PaperName = Printing.PaperKind.Tabloid
pDoc.DefaultPageSettings.PaperSize = ps
pDoc.Print()
printerSettings.PrinterName = curPrinter 'this should reset the user's default printer to the original setting to before when this routine started??
Exit Sub
errHandler:
MsgBox(Err.Description)
End Sub
Sub pDocPage(ByVal sender As Object, ByVal e As Printing.PrintPageEventArgs)
Dim img As Image = Image.FromFile(spidDrawingPackagePath)
Dim pCount = img.GetFrameCount(FrameDimension.Page)
img.SelectActiveFrame(FrameDimension.Page, currPage)
Using stReader As IO.MemoryStream = New IO.MemoryStream
img.Save(stReader, ImageFormat.Bmp)
Dim bmp As Bitmap = CType(Image.FromStream(stReader), Bitmap)
e.Graphics.DrawImage(bmp, 0, 0)
bmp.Dispose()
End Using
currPage += 1
If currPage < pCount Then
e.HasMorePages = True
End If
End Sub

Run-time error '7': Out of memory

I'm trying to edit embedded charts in Word documents. My source code is below. It has worked a long time but not for the last two days. I get this error:
Run-time error '7': Out of memory
I have searched a lot , but I don't understand the problem. When I shutdown computer and after open it, then it works correctly, but after I get error again.
It gives error in this part:
'create range with Cell
Set oChart = oInShapes.Chart
oChart.ChartData.Activate ' ***Note: It gives error here***
'Set oWorkbook = oChart.ChartData.Workbook
Set oWorksheet = oChart.ChartData.Workbook.Worksheets("Tabelle1")
Set oRange = oWorksheet.Range(Cell)
Public Sub updatechart(Doc As word.Application, ChartName As String, ChartTitle As String, Cell As String, data As String)`
Dim oInShapes As word.InlineShape
Dim oChart As word.Chart
Dim oWorksheet As Excel.Worksheet
'Dim oWorkbook As Excel.Workbook
Dim columnArray() As String
Dim rowArray() As String
Dim oRange As Range
Dim i As Integer
Dim j As Integer
For Each oInShapes In Doc.ActiveDocument.InlineShapes
' Check Shape type and Chart Title
If oInShapes.HasChart Then
'create range with Cell
Set oChart = oInShapes.Chart
oChart.ChartData.Activate ' ***Note: It gives error here***
'Set oWorkbook = oChart.ChartData.Workbook
Set oWorksheet = oChart.ChartData.Workbook.Worksheets("Tabelle1")
Set oRange = oWorksheet.Range(Cell)
' Commet for debug
'oWorksheet.Range("B33") = (ChartTitle & 33)
' Split text
columnArray = Split(data, SeperateChar)
For i = LBound(columnArray) To UBound(columnArray)
rowArray = Split(Trim(columnArray(i)), " ")
' Set Title. For example; ChartTitle = "XY" ----- Table Titles ----> | XY1 | XY2 | XY2 | ....
' After Set Value | 0,33| 0,1 | 0,46| ....
oRange.Cells(1, i + 1) = ChartTitle & (i + 1)
For j = LBound(rowArray) To UBound(rowArray)
' Set Values
oRange.Cells(j + 2, i + 1) = CDbl(rowArray(j))
Next j
Next i
'oWorkbook.Close
oChart.Refresh
End If
Next
Set oInShapes = Nothing
Set oChart = Nothing
Set oWorksheet = Nothing
'Set oWorkbook = Nothing
Erase rowArray, columnArray
End Sub
This has happened to me before. I had the same solution, exit excel, free up some memory and try again - and it worked. You may have to shut down other programs while using this. Its literally what it says it is, lack of available memory.
Keep in mind that if you've run other macros that copy information to the clipboard, you will have less RAM freed up to run the macro.
Also, are you using 32 or 64 bit Excel - 64 will allow you to use more RAM.
I notice that you not set oRange to nothing when cleaning up your sub, could it be that this object is using a lot of memory which isn't being released when the sub ends?
I had a similar error and finally traced it down to the "For Each" statement. I think it has to do with the memory allocation for the Collection, Doc.ActiveDocument.InlineShapes in your example.
My bad code (PowerPoint to Excel):
For Each sh In InputBook.Sheets("Exec Sum").Shapes
sh.Visible = False
Next
Set sh = Nothing
My fixed code:
For i = 1 To InputBook.Sheets("Exec Sum").Shapes.Count
InputBook.Sheets("Exec Sum").Shapes(i).Visible = False
Next
Avoiding a reference to a collection solved my issue.
The frequent access to the worksheet can create problems with resource usage. The way to go about this is to fetch data in a single access point, like
Dim V as Variant
V = InputRange
' Now V becomes a m x n array of the cell values in InputRange
' you may manipulate and work with this data and fill all your results in
' OutputV(m,n) variant array
Dim OutputV() as Variant
ReDim OutputV(m,n)
oRange = OutputV
Usually speeds up the code by several hundred times depending on the size of the range and also uses far less resources.

Formula or Macro for Open Office calc to retrieve Comments (annotation) from a Cell

I need to harvest and colate data from an oOcalc workbook.
Part of the information is presented as comments on the cell.
I cant figure out a formula to do it and Im not familiar with oOcalc DOM's to manipulate the item.
Hope someone can help me out.
Thanks.
Just had to figure this one out myself, so here is a macro that will copy the comments of cells in one sheet to actual cells in another sheet.
It could be better, but it gets the job done, so its not worth putting anymore (of my) time into!
REM ****** BASIC *********
Sub ExtractCommentAnnotationThings
Dim myDoc as Object
Dim originalSheet as Object
Dim newSheet as Object
Dim originalCell as Object
Dim newCell as Object
Dim commentString As String
REM DEFINE VAR FOR OUR LOOP
Dim iTargetRow, iTargetColumn As Long
Const kEndRow = 950
Const kEndColumn = 20
REM SET DOC
myDoc = ThisComponent
REM GET SHEET
originalSheet = myDoc.Sheets(0)
newSheet = myDoc.Sheets(1)
REM START LOOP
For iTargetRow = 0 To kEndRow: DoEvents
For iTargetColumn = 0 To kEndColumn: DoEvents
originalCell = originalSheet.getCellByPosition(iTargetColumn,iTargetRow)
REM commentString = Trim(originalCell.Comment.Text)
If originalCell.Annotation.isVisible = True Then
commentString = originalCell.getAnnotation().String
newCell = newSheet.getCellByPosition(iTargetColumn,iTargetRow)
newCell.String = commentString
End If
Next
Next
REM CONTINUE LOOP
End Sub
Set the kEndRow and kEndColumn to include only the range of cells you want copied.
Set the originalSheet and newSheetappropriately as well (might need to create a new sheet first), so they're copied where you want them to be.
Hope it helps!

Resources