How to retrieve the value of an Input field and use it to modify a placeholder in a LibreOffice Basic macro? - macros

I've spent two days on this and I'm still not able to figure it out 8-)
I have a LibreOffice Writer document with some Placeholders (Insert -> Fields -> More Fields -> Functions -> Placeholder -> Image) and Input fields (Insert -> Fields -> More Fieds -> Functions -> Input field) and I need to retrieve the value of an Input field and use it to replace a specified Placeholder in the same document.
To be more precise. I have an Input field where I enter for example 123
and somewhere in the document is a button, which triggers a macro, and this macro should:
retrieve the current value of the specified (named?) Input field ("123"),
"replace" a specified (named?) Placeholder with an image loaded from http://domain.tld/image/123.png
Is this somehow possible? Would be great, because I'm trying to to insert externally generated barcodes into my document...

These are both "Text fields", and some information and macro examples are in Andrew Pitonyak's book OpenOffice Macros Explained (available as a free pdf download from http://www.pitonyak.org/oo.php). The wiki page also has some good background.
Form controls (from the toolbar "Form controls") are named, so they have an advantage when working with macros. Text fields, however - the kind you have in your document - are not named, so you have to cycle through all the fields in a document, or highlight a particular run of text and cycle through the field within the highlighted area to find the one you are after. The Pitonyak document has examples of both methods.
Assuming the document has only one input field, this StarBasic code will print its current value:
Sub DisplayFields
Dim oEnum As Object
Dim oField As Object
oEnum = ThisComponent.getTextFields().createEnumeration()
Do While oEnum.hasMoreElements()
oField = oEnum.nextElement()
If oField.getPresentation(True) = "Input field" Then
Print "Input field contents: " & oField.getPresentation(False)
Exit Do
End If
Loop
End Sub
As far as I can tell, there is no API to replace a placeholder with its designated content. There might be a way with the dispatcher - the list of dispatch commands tantalizingly includes "FieldDialog" - but I wasn't able to find any documentation or examples.
I think what you'd have to do is find the field, put your cursor there, insert the image, then delete the placeholder field. Some more StarBasic code (again, assuming there's only a single placeholder field in the document):
Sub InsertImage
Dim oEnum As Object
Dim oField As Object
Dim oAnchor As Object
Dim oText As Object
Dim oCursor As Object
Dim FileName As String
Dim FileURL As String
Dim objTextGraphicObject As Object
oEnum = ThisComponent.getTextFields().createEnumeration()
Do While oEnum.hasMoreElements()
oField = oEnum.nextElement()
If oField.getPresentation(True) = "Placeholder" Then
oAnchor = oField.Anchor
oText = oAnchor.getText()
oCursor = oText.createTextCursorByRange(oAnchor.getEnd)
FileName = "C:\after zoo.JPG"
FileURL = convertToURL(FileName)
objTextGraphicObject = ThisComponent.createInstance("com.sun.star.text.TextGraphicObject")
REM Optional to set the size
' Dim objSize as New com.sun.star.awt.Size
' objSize.Width = 3530
' objSize.Height = 1550
' objTextGraphicObject.setSize(objSize)
objTextGraphicObject.GraphicURL = FileURL
oText.insertTextContent(oCursor.Start, objTextGraphicObject, false)
oField.dispose()
Exit Do
End If
Loop
End Sub

Related

XLSX file via OpenXml SDK Both Valid and Invalid

I have a program which exports a System.Data.DataTable to an XLSX / OpenXml Spreadsheet. Finally have it mostly working. However when opening the Spreadsheet in Excel, Excel complains about the file being invalid, and needing repair, giving this message...
We found a problem with some content in . Do you want us to
try to recover as much as we can? If you trust the source of the
workbook, clik Yes.
If I click Yes, it comes back with this message...
Clicking the log file and opening that, just shows this...
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<recoveryLog xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
<logFileName>error268360_01.xml</logFileName>
<summary>Errors were detected in file 'C:\Users\aabdi\AppData\Local\Temp\data.20190814.152538.xlsx'</summary>
<repairedRecords>
<repairedRecord>Repaired Records: Cell information from /xl/worksheets/sheet1.xml part</repairedRecord>
</repairedRecords>
</recoveryLog>
Obviously, we don't want to deploy this into a production environment like this. So I've been trying to figure out how to fix this. I threw together a quick little sample to validate the XML and show the errors, based on this link from MSDN. But when I run the program and load the exact same XLSX document that Excel complains about, the Validator comes back saying that the file is perfectly Valid. So I'm not sure where else to go from there.
Any better tools for trying to validate my XLSX XML? Following is the complete code I'm using to generate the XLSX file. (Yes, it's in VB.NET, it's a legacy app.)
If I comment out the line in the For Each dr As DataRow loop, then the XLSX file opens fine in Excel, (just without any data). So it's something with the individual cells, but I'm not really DOING much with them. Setting a value and data type, and that's it.
I also tried replacing the For Each loop in ConstructDataRow with the following, but it still outputs the same "bad" XML...
rv.Append(
(From dc In dr.Table.Columns
Select ConstructCell(
NVL(dr(dc.Ordinal), String.Empty),
MapSystemTypeToCellType(dc.DataType)
)
).ToArray()
)
Also tried replacing the call to Append with AppendChild for each cell too, but that didn't help either.
The zipped up XLSX file (erroring, with dummy data) is available here:
https://drive.google.com/open?id=1KVVWEqH7VHMxwbRA-Pn807SXHZ32oJWR
Full DataTable to Excel XLSX Code
#Region " ToExcel "
<Extension>
Public Function ToExcel(ByVal target As DataTable) As Attachment
Dim filename = Path.GetTempFileName()
Using doc As SpreadsheetDocument = SpreadsheetDocument.Create(filename, DocumentFormat.OpenXml.SpreadsheetDocumentType.Workbook)
Dim data = New SheetData()
Dim wbp = doc.AddWorkbookPart()
wbp.Workbook = New Workbook()
Dim wsp = wbp.AddNewPart(Of WorksheetPart)()
wsp.Worksheet = New Worksheet(data)
Dim sheets = wbp.Workbook.AppendChild(New Sheets())
Dim sheet = New Sheet() With {.Id = wbp.GetIdOfPart(wsp), .SheetId = 1, .Name = "Data"}
sheets.Append(sheet)
data.AppendChild(ConstructHeaderRow(target))
For Each dr As DataRow In target.Rows
data.AppendChild(ConstructDataRow(dr)) '// THIS LINE YIELDS THE BAD PARTS
Next
wbp.Workbook.Save()
End Using
Dim attachmentname As String = Path.Combine(Path.GetDirectoryName(filename), $"data.{Now.ToString("yyyyMMdd.HHmmss")}.xlsx")
File.Move(filename, attachmentname)
Return New Attachment(attachmentname, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
End Function
Private Function ConstructHeaderRow(dt As DataTable) As Row
Dim rv = New Row()
For Each dc As DataColumn In dt.Columns
rv.Append(ConstructCell(dc.ColumnName, CellValues.String))
Next
Return rv
End Function
Private Function ConstructDataRow(dr As DataRow) As Row
Dim rv = New Row()
For Each dc As DataColumn In dr.Table.Columns
rv.Append(ConstructCell(NVL(dr(dc.Ordinal), String.Empty), MapSystemTypeToCellType(dc.DataType)))
Next
Return rv
End Function
Private Function ConstructCell(value As String, datatype As CellValues) As Cell
Return New Cell() With {
.CellValue = New CellValue(value),
.DataType = datatype
}
End Function
Private Function MapSystemTypeToCellType(t As System.Type) As CellValues
Dim rv As CellValues
Select Case True
Case t Is GetType(String)
rv = CellValues.String
Case t Is GetType(Date)
rv = CellValues.Date
Case t Is GetType(Boolean)
rv = CellValues.Boolean
Case IsNumericType(t)
rv = CellValues.Number
Case Else
rv = CellValues.String
End Select
Return rv
End Function
#End Region
For anyone else coming in and finding this, I finally tracked this down to the Cell.DataType
Setting a value of CellValues.Date will cause Excel to want to "fix" the document.
(apparently for dates, the DataType should be NULL, and Date was only used in Office 2010).
Also, if you specify a DataType of CellValues.Boolean, then the CellValue needs to be either 0 or 1. "true" / "false" will also cause Excel to want to "fix" your spreadsheet.
Also, Microsoft has a better validator tool already built for download here:
https://www.microsoft.com/en-us/download/details.aspx?id=30425

How to cut a string from the end in UIPATH

I have this string: "C:\Procesos\rrhh\CorteDocumentos\Cortados\10001662-1_20060301_29_1_20190301.pdf" and im trying to get this part : "20190301". The problem is the lenght is not always the same. It would be:
"9001662-1_20060301_4_1_20190301".
I've tried this: item.ToString.Substring(66,8), but it doesn't work sometimes.
What can I do?.
This is a code example of what I said in my comment.
Sub Main()
Dim strFileName As String = ""
Dim di As New DirectoryInfo("C:\Users\Maniac\Desktop\test")
Dim aryFi As FileInfo() = di.GetFiles("*.pdf")
Dim fi As FileInfo
For Each fi In aryFi
Dim arrname() As String
arrname = Split(Path.GetFileNameWithoutExtension(fi.Name), "_")
strFileName = arrname(arrname.Count - 1)
Console.WriteLine(strFileName)
Next
End Sub
You could achieve this using a simple regular expressions, which has the added benefit of including pattern validation.
If you need to get exactly eight numbers from the end of file name (and after an underscore), you can use this pattern:
_(\d{8})\.pdf
And then this VB.NET line:
Regex.Match(fileName, "_(\d{8})\.pdf").Groups(1).Value
It's important to mention that Regex is by default case sensitive, so to prevent from being in a situations where "pdf" is matched and "PDF" is not, the patter can be adjusted like this:
(?i)_(\d{8})\.pdf
You can than use it directly in any expression window:
PS: You should also ensure that System.Text.RegularExpressions reference is in the Imports:
You can achieve it by this way as well :)
Path.GetFileNameWithoutExtension(Str1).Split("_"c).Last
Path.GetFileNameWithoutExtension
Returns the file name of the specified path string without the extension.
so with your String it will return to you - 10001662-1_20060301_29_1_20190301
then Split above String i.e. 10001662-1_20060301_29_1_20190301 based on _ and will return an array of string.
Last
It will return you the last element of an array returned by Split..
Regards..!!
AKsh

Redemption in MS Access / Outlook (trying to include a separate message file in email)

This code is in MS Access (2010) VBA, using the Redemption library with Microsoft Outlook 2010.
I had this process working before, but we recently had a Citrix upgrade that I guess reset something in my Outlook and now the process no longer works.
I have a folder of .msg files which are basically pre-made email templates with all the proper formatting, images, text, etc.
This is what I was doing before:
Dim outlookApp As Object, namespace As Object
Dim oItem, MyItem
Set outlookApp = CreateObject("Outlook.Application")
Set namespace = outlookApp.GetNamespace("MAPI")
namespace.Logon
Set MyItem = outlookApp.CreateItemFromTemplate(path_to_dot_msg_file)
'And then there are many calls like this:
MyItem.HTMLBody = Replace(MyItem.HTMLBody, "Dear Person,", "Dear " & name)
'...
Set safeItem = CreateObject("Redemption.SafeMailItem")
Set oItem = MyItem
safeItem.Item = oItem
'this next line displays the email, and as of this line, it looks correct
'safeItem.Display
'but as of this line, the issue occurs
safeItem.HTMLBody = "<p>This is an extra message that shows up before the .msg file</p>" & safeItem.HTMLBody
safeItem.Recipients.ResolveAll
safeItem.Send
Now when the email is sent, the .msg contents aren't present at all -- the only thing that shows up is the "extra message" that I prepended to the HTMLBody.
What do I need to change or update? Is this something I need to change in the code, or in my Outlook settings, etc?
Extra: body insertion:
Function insertStringBodyTag(htmlBody As String, stringToInsert As String)
Dim s As String
Dim i As Integer
s = htmlBody
i = InStr(1, s, "<body")
i = InStr(i, s, ">")
s = Left(s, i) & stringToInsert & Right(s, Len(s) - i)
insertStringBodyTag = s
End Function
'Called with safeItem.htmlBody = insertStringBodyTag(safeItem.htmlBody, prefix_string)
You cannot concatenate 2 HTML strings and expect a valid HTML string back - the two must be merged - find the position of the "<body"substring in the original HTML body, then find the positon of the following ">" (this way you take care of the body element with attributes), then insert your HTML string following that ">".

Access VBA visible control form from another form

I have a table and form setup to control another form in my database.
I'm wanting to make a code that will take the title from my field and add it to my code as a variable to change the visibility options of my other form.
my form is set with all the of the names to all objects on the form I want to control.
LSE_FORM_ADMIN = The table with all the LSE_FORM_ALL names in it.
Table is setup with 3 columns key, names and a checkbox which I put into a form to make a continuous list.
here is my code on the form, but I keep getting and runtime 424: object required error:
Private Sub Form_Current()
Dim VARSET As Object
Dim VAR As String
VARSET = DLookup("TITLE", Table!LSE_FORM_ADMIN, "") 'keep getting error here
VAR = VARSET
If Me!CB = "-1" Then
Form_LSE_FORM_ALL!VAR.Visible = True
Else
Form_LSE_FORM_ALL!VAR.Visible = False
End If
End Sub
can someone help me fix this code so that it will grab the title field data and make it a variable to add to the rest of the code?
It's difficult to see exactly what you are trying to achieve, but your problems stem from using the variant variable type when you should be using an explicit Form or Control type. Using your last example.
RSTT.Visible = True 'getting Run-time error '424': object required
This is because you have declared RSTT as a variant. The line
RSTT = "Form_LSE_FORM_ALL" & "!" & (RST)
results in the variable RSTT containing a string, which does not have a property ".Visible"
Set DB = CurrentDb
Set RS = DB.OpenRecordset("LSE_FORM_ADMIN")
These lines are redundant as you have the values that you need available on the form fields which are already bound to the table LSE_FORM_ADMIN.
As far as I understand, you have a continuous form (ADMIN?) bound to the table LSE_FORM_Admin. As you step through the records on this form, you want code to be fired which takes the value of the TITLE field/control and use it to set a control with the same name, on a separate form, Form_LSE_FORM_ALL, to be (in)visible, dependent on the value of the checkbox control name CB on the ADMIN form?
If you want the ADMIN form to make the changes "live" to the ALL form, you should consider using an event of the CB checkbox control. Using the current event of the form means that the changes you make will not be reflected in the ALL form until you step out of the record you have just edited, then back in, to fire the form's Current event on that record.
Example using AfterUpdate event of CB checkbox
Private Sub CB_AfterUpdate()
Dim strRST As String
Dim frmTarget as Form
Dim ctlRSTT As Control
Set strRST = Me!TITLE
Set frmTarget = Forms("Form_LSE_FORM_ALL")
Set ctlRSTT = frmTarget.Controls(strRST)
ctlRSTT.Visible = Me!CB 'getting Run-time error '424': object required
End Sub
really not sure how to do the syntax when doing a recordset to a table from the form, need some help with that.
here is my code and attempt at the record set:
Private Sub Form_Current()
Dim DB As Database
Dim RS As Recordset
Dim RST As String
Set DB = CurrentDb
Set RS = DB.OpenRecordset("LSE_FORM_ADMIN")
Set RST = RS 'GETTING OBJECT REQUIRED ERROR ON "RST ="
Do Until RS.EOF
RST = Me!TITLE
RS.MoveNext
Loop
If Me!CB = "-1" Then
Form_LSE_FORM_ALL!RS.Visible = True
Else
Form_LSE_FORM_ALL!RS.Visible = False
End If
End Sub
I think I know what you are trying to do, but your descriptions / references are not matching up. Please look at the following comments and clarify:
1. You say "...make a code that will take the title from my field and ..." but your code is taking "Me.Title", "ME" is a reference to the Form - not a field.
2. Your code is in the "Form_Current" event, which means it will fire for every record you process. That will work, but I think you want to do this code only once to be more efficient.
3. You have no provision for processing more than one field. I think you need to loop through all fields in your table, setting visible to true or false.
The following is my suggestion, but I will update once you clarify the issues.
Option Compare Database
Option Explicit
Dim DB As DAO.Database
Dim RS As DAO.Recordset
'Dim RST As Variant
'Dim RSTT As Variant
Public Sub FORM_CURRENT()
Set DB = CurrentDb
Set RS = DB.OpenRecordset("LSE_FORM_ADMIN")
Do While Not RS.EOF ' Loop thru all field names for the form
If RS!HideYN = True Then ' Desire to hide the field?
Me(RS!ctlname).Visible = False ' Yes, hide the field.
Else
Me(RS!ctlname).Visible = True ' No, show the field
End If
RS.MoveNext ' Get next field name
Loop
RS.Close
Set RS = Nothing
Set DB = Nothing
'Set RST = Me!Title
'RSTT = "Form_LSE_FORM_ALL" & "!" & (RST)
'If Me!CB = "-1" Then
' RSTT.Visible = True 'getting Run-time error '424': object required
'Else
' RSTT.Visible = False
'End If
End Sub
Final code, thanks to Cheesenbranston.
Private Sub Form_AfterUpdate()
Dim strRST As String
Dim frmTarget As Form
Dim ctlRSTT As Control
strRST = Me!TITLE
Set frmTarget = Forms("LSE_FORM_ALL")
Set ctlRSTT = frmTarget.Controls(strRST)
If Me!CB = "-1" Then
ctlRSTT.Visible = True
Else
ctlRSTT.Visible = False
End If
End Sub
#Cheesenbranston: Your original code was more like a toggle of on and off so if my object was not visible then my trigger checkbox would make it visible when checked, more of a quality of life for my own needs, none the less worked. Also strRST doesn't need SET since its just a String. Thanks again =D very happy day!

Annot doesnt have action

I'm trying to change all annotations link destination to fit page and also i need to validate the links.
This is the code i'm trying
Dim PdfReader As PdfReader
'Dim myBookmarks As New List(Of Dictionary(Of String, Object))()
Dim pg As Integer
PdfReader = New PdfReader("D:\\Annot_Testing.pdf")
Dim PageCount As Integer = PdfReader.NumberOfPages
For pg = 1 To PdfReader.NumberOfPages
Dim PageDictionary As PdfDictionary = PdfReader.GetPageN(pg)
Dim Annots As PdfArray = PageDictionary.GetAsArray(PdfName.ANNOTS)
If (Annots Is Nothing) OrElse (Annots.Length = 0) Then
Continue For
End If
''//Loop through each annotation
For Each A In Annots.ArrayList
''//I do not completely understand this but I think this turns an Indirect Reference into an actual object, but I could be wrong
''//Anyway, convert the itext-specific object as a generic PDF object
Dim AnnotationDictionary = DirectCast(PdfReader.GetPdfObject(A), PdfDictionary)
''//Make sure this annotation has a link
If Not AnnotationDictionary.Get(PdfName.SUBTYPE).Equals(PdfName.LINK) Then Continue For
''//Make sure this annotation has an ACTION
If AnnotationDictionary.Get(PdfName.ACTION) Is Nothing Then Continue For
''//Get the ACTION for the current annotation
Dim AnnotationAction = DirectCast(AnnotationDictionary.Get(PdfName.A), PdfDictionary)
''//Test if it is a named actions such as /FIRST, /LAST, etc
If AnnotationAction.Get(PdfName.S).Equals(PdfName.NAMED) Then
MsgBox("Yes named")
''//Otherwise see if its a GOTO page action
ElseIf AnnotationAction.Get(PdfName.S).Equals(PdfName.PAGE) Then
MsgBox("page")
ElseIf AnnotationAction.Get(PdfName.S).Equals(PdfName.GOTOR) Then
MsgBox("gotoR")
ElseIf AnnotationAction.Get(PdfName.S).Equals(PdfName.GOTO) Then
''//Make sure that it has a destination
If AnnotationAction.GetAsArray(PdfName.D) Is Nothing Then Continue For
Dim AnnotationReferencedPage = PdfReader.GetPdfObject(DirectCast(AnnotationAction.GetAsArray(PdfName.D).ArrayList(0), PRIndirectReference))
''//Re-loop through all of the pages in the main document comparing them to this page
For J = 1 To PageCount
If AnnotationReferencedPage.Equals(PdfReader.GetPageN(J)) Then
Trace.WriteLine(J)
Exit For
End If
Next
ElseIf AnnotationAction.Get(PdfName.S).Equals(PdfName.URI) Then
Else
MsgBox("else type")
End If
Next
Next
'bookmarkGlobalSetting(myBookmarks)
PdfReader.Close()
In the sample document attached having 3 annots, but its not showing action on it while reading via code, but its availed when seeing this in acrobat. Please suggest me on this..
please download from here... https://drive.google.com/file/d/0B72uT6TzR_cdWGczRkY3ZmNpeTg/edit?usp=sharing
Those annotations do not have any A action entry:
Your expectation to find an action with a link is wrong because it may alternatively have a destination, cf. section 12.5.6.5 Link Annotations of ISO 32000-1:
A link annotation represents either a hypertext link to a destination elsewhere in the document (see 12.3.2, “Destinations”) or an action to be performed (12.6, “Actions”). Table 173 shows the annotation dictionary entries specific to this type of annotation.
Your code only deals with the latter type (with an action to be performed) and not the former (with a hypertext link to a destination elsewhere in the document).
Thus, you have to improve your code to also deal with the former type. You'll find details in Table 173 – Additional entries specific to a link annotation of ISO 32000-1.
PS: I assume your
If AnnotationDictionary.Get(PdfName.ACTION) Is Nothing Then Continue For
should have been
If AnnotationDictionary.Get(PdfName.A) Is Nothing Then Continue For