Adding an attribute to XmlWriter causes all namespaces to become aliased - xmlwriter

Here is a line of XML found in an Excel book I created with a PivotTable/Cache in it:
<pivotCacheDefinition xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" r:id="rId1" refreshOnLoad="1" refreshedBy="m" refreshedDate="44873.446783912033" createdVersion="4" refreshedVersion="4" minRefreshableVersion="3" recordCount="4">
I am using XmlWriter (and System.IO.Packaging) to modify this XML to cause it to not use cached values and instead recalculate from the original data every time it's opened (you can do this in Excel, they always forget to). All this needs is an additional attribute in this header, savedata="0".
We use similar code to rewrite the worksheets holding the data, so I simply copypasta it and changed the element names to produce this:
Dim WR As XmlWriter
Dim WRSettings As XmlWriterSettings
WRSettings = New XmlWriterSettings() With {.CloseOutput = False}
pPivotPart.Data = New MemoryStream()
pPivotPart.Data.Position = 0
WR = XmlWriter.Create(pPivotPart.Data, WRSettings)
WR.WriteStartDocument(True)
WR.WriteStartElement("pivotCacheDefinition", cXl07WorksheetSchema)
WR.WriteAttributeString("xmlns", "r", Nothing, cXl07RelationshipSchema)
WR.WriteAttributeString("r", "Id", Nothing, "rId" & RelId)
WR.WriteAttributeString("saveData", "0")
'the rest of the lengthy code creates the other attributes and then copies over the original XML line by line
The r:id attribute is causing a problem. When it is present, XmlWriter adds an alias for the main namespace, and I get this:
<x:pivotCacheDefinition xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" saveData="0" refreshOnLoad="1" refreshedBy="m" refreshedDate="44816.473130671293" createdVersion="4" refreshedVersion="4" minRefreshableVersion="3" recordCount="4" r:Id="rId1" xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
This makes regression testing... difficult. If I comment out the single line that inserts that attribute, all the aliasing goes away - even if I leave in the line that manually inserts that namespace for it:
<pivotCacheDefinition xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" saveData="0" refreshOnLoad="1" refreshedBy="m" refreshedDate="44816.473130671293" createdVersion="4" refreshedVersion="4" minRefreshableVersion="3" recordCount="4" xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
I thought perhaps that manually adding the r NS was the issue, and tried adding just the attribute and letting XmlWriter add it for me, but that did not do what I expected either, it simply ignored the "r" namespace entirely:
<x:pivotCacheDefinition saveData="0" refreshOnLoad="1" refreshedBy="m" refreshedDate="44816.473130671293" createdVersion="4" refreshedVersion="4" minRefreshableVersion="3" recordCount="4" Id="rId1" xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
It is difficult to find this topic because of PHP's similar-name library, but a few threads I found present seemingly similar code.
Can someone who better understands XmlWriter's NS logic explain what's happening here, and how to avoid the renaming?
UPDATE:
I'm still playing with it using the dox on MS and various posts, and in doing so found this problem occurs if these are the only lines:
WR.WriteStartElement("pivotCacheDefinition", cXl07WorksheetSchema)
WR.WriteAttributeString("xmlns", "r", Nothing, cXl07RelationshipSchema)
WR.WriteAttributeString("r", "Id", cXl07RelationshipSchema, "rId" & RelId)
WR.WriteEndElement()
WR.WriteEndDocument()
As soon as it encounters the second NS the first is renamed. I have tried adding the first NS explicitly, I have tried using ns parameters instead of the xmlns tags, every variation seems to have the same effect: as soon as you insert an element on the r namespace the first one gets renamed x.

Related

How to automatically resize all tables to optimal column width in a docx with LibreOffice Writer?

Problem
I have 100+ *.docx files created with Microsoft Word on a Windows machine that I would like to use with LibreOffice Writer.
Unfortunately, somehow all the tables have been squished in Writer as shown:
I've tried to fix this by:
Selecting the entire table (ctrl-a, ctrl-a)
Right-click the table
Go to size
Click optimal column width
This indeed gives me the desired result:
What I've tried so far
failed approach 1
I've created the following macro that does the formatting for a single table:
REM ***** BASIC *****
sub setTableColumns
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")
dispatcher.executeDispatch(document, ".uno:SelectAll", "", 0, Array())
dispatcher.executeDispatch(document, ".uno:SelectAll", "", 0, Array())
dispatcher.executeDispatch(document, ".uno:SetOptimalColumnWidth", "", 0, Array())
end sub
I've assigned a keyboard shortcut to this macro such that I can reformat a single table by:
Clicking on the table of interest.
Running the shortcut.
This is an improvement, but it still requires a lot of manual work.
failed approach 2
I've also tinkered with the examples given in this different SO question.
I've managed to set the relative width of all tables to 100% by changing this property of all my tables:
https://www.openoffice.org/api/docs/common/ref/com/sun/star/text/TextTable.html#RelativeWidth
I did this by using the following macro (snippet):
tables = ThisComponent.TextTables
for tid = 0 to tables.count - 1
table = tables(tid)
table.RelativeWidth = 100
next
This does widen all the tables, however the format is not desirable.
Question
Is there a way to apply the optimal column width setting to all tables in a file?
It would be awesome if I could apply it to all tables in multiple docx files at once.
However, it would already make me very happy if I could automatically format all tables in a single docx file.
The code needs to loop through all tables in the document, select each one and then optimize the selected table.
The following Basic code from Andrew Pitonyak's macro document section 7.2.1 shows how to select a table.
ThisComponent.getCurrentController().select(oTable)
Here is some python code from a project of mine.
cellsRange = table.getCellRangeByPosition(
0, 0, endCol, numRows - 1)
controller.select(cellsRange)
dispatcher.executeDispatch(
frame, ".uno:SetOptimalColumnWidth", "", 0, ())
There are also ways to loop through a set of documents, so the code can open, optimize, save and close each document. The most difficult part is closing properly without a crash, as closing events can sometimes interrupt each other.
If this is a problem, you may find it easier to open, save and close each by hand, running the macro to optimize all tables in the currently open document. It's also not too hard to loop through all open documents, if you find it more convenient to open multiple files at the same time.

Save an Excel sheet as PDF programatically through powerbuilder

There is a requirement to save an excel sheet as a pdf file programmatically through powerbuilder (Powerbuilder 12.5.1).
I run the code below; however, I am not getting the right results. Please let me know if I should do something different.
OLEObject ole_excel;
ole_excel = create OLEObject;
IF ( ole_excel.ConnectToObject(ls_DocPath) = 0 ) THEN
ole_excel.application.activeworkbook.SaveAs(ls_DocPath,17);
ole_excel.application.activeworkbook.ExportAsFixedFormat(0,ls_DocPath);
END IF;
....... (Parsing values from excel)
DESTROY ole_excel;
I have searched through this community and others for a solution but no luck so far. I tried using two different commands that I found during this search. Both of them return a null object reference error. It would be great if someone can point me in the right direction.
It looks to me like you need to have a reference to the 'activeworkbook'. This would be of type OLEobject so the declaration would be similar to: OLEobject lole_workbook.
Then you need to set this to the active work book. Look for the VBA code on Excel (should be in the Excel help) for something like a 'getactiveworkbook' method. You would then (in PB) need to do something like
lole_workbook = ole_excel.application.activeworkbook
This gets the reference for PB to the activeworkbook. Then do you saveas and etc. like this lole_workbook.SaveAs(ls_DocPath,17)
workBook.saveAs() documentation says that saveAs() has the following parameters:
SaveAs(Filename, FileFormat, Password, WriteResPassword, ReadOnlyRecommended, CreateBackup, AccessMode, ConflictResolution, AddToMru, TextCodepage, TextVisualLayout, Local)
we need the two first params:
FileName - full path with filename and extension, for instance: c:\myfolder\file.pdf
FileFormat - predefined constant, that represents the target file format.
According to google (MS does not list pdf format constant for XLFileFormat), FileFormat for pdf is equal to 57
so, try to use the following call:
ole_excel.application.activeworkbook.SaveAs(ls_DocPath, 57);

How to write some amounts to a File?

I'm doing this program for a class; I have a listbox with 4 choices that are counted each time they're selected,and i'm supposed to write out the results to an out.File so that they can then be retrieved and displayed when asked to.
Here's an image,so you can see what i mean.
Here's the code i got for the file part so far:
'declare a streamwriter variable
Dim outFile As IO.StreamWriter
'open file for output
outFile = IO.File.CreateText("Commercials.txt")
'write to file
For intIndex As Integer = 0 To lstCommercial.Items.Count - 1
outFile.WriteLine(lstCommercial.Items(intIndex))
Next intIndex
'close th efile
outFile.Close()
So my problem is this,i can get everything to work except for it to write the totals to the file,with the result of not displaying. How do i go about doing this? What am i doing wrong in any case?
It depends on what lstCommercial.Items is.
If it is a Collection object, then you can get the index of an item using the following:
outFile.WriteLine(lstCommercial.Items.Item(intIndex))
If it is an array, then instead of using the Count property to find the length, you should rather use GetUpperBound(0) to find the last index:
For intIndex As Integer = 0 To lstCommercial.Items.GetUpperBound(0)
I have very little experience in Visual Basic so I am probably wrong, but I have just gathered this from reading Arrays in Visual Basic and Collection Object (Visual Basic).
Also, looking at the docs here, you may need to 'Flush' the buffer to the file before closing it:
outFile.Flush()
'close the file
outFile.Close()

How to get programatically the file path from a directory parameter in abap?

The transaction AL11 returns a mapping of "directory parameters" to file paths on the application server AFAIK.
The trouble with transaction AL11 is that its program only calls c modules, there's almost no trace of select statements or function calls to analize there.
I want the ability to do this dynamically, in my code, like for instance a function module that took "DATA_DIR" as input and "E:\usr\sap\IDS\DVEBMGS00\data" as output.
This thread is about a similar topic, but it doesn't help.
Some other guy has the same problem, and he explains it quite well here.
I strongly suspect that the only way to get these values is through the kernel directly. some of them can vary depending on the application server, so you probably won't be able to find them in the database. You could try this:
TYPE-POOLS abap.
TYPES: BEGIN OF t_directory,
log_name TYPE dirprofilenames,
phys_path TYPE dirname_al11,
END OF t_directory.
DATA: lt_int_list TYPE TABLE OF abaplist,
lt_string_list TYPE list_string_table,
lt_directories TYPE TABLE OF t_directory,
ls_directory TYPE t_directory.
FIELD-SYMBOLS: <l_line> TYPE string.
START-OF-SELECTION-OR-FORM-OR-METHOD-OR-WHATEVER.
* get the output of the program as string table
SUBMIT rswatch0 EXPORTING LIST TO MEMORY AND RETURN.
CALL FUNCTION 'LIST_FROM_MEMORY'
TABLES
listobject = lt_int_list.
CALL FUNCTION 'LIST_TO_ASCI'
EXPORTING
with_line_break = abap_true
IMPORTING
list_string_ascii = lt_string_list
TABLES
listobject = lt_int_list.
* remove the separators and the two header lines
DELETE lt_string_list WHERE table_line CO '-'.
DELETE lt_string_list INDEX 1.
DELETE lt_string_list INDEX 1.
* parse the individual lines
LOOP AT lt_string_list ASSIGNING <l_line>.
* If you're on a newer system, you can do this in a more elegant way using regular expressions
CONDENSE <l_line>.
SHIFT <l_line> LEFT DELETING LEADING '|'.
SHIFT <l_line> RIGHT DELETING TRAILING '|'.
SPLIT <l_line>+1 AT '|' INTO ls_directory-log_name ls_directory-phys_path.
APPEND ls_directory TO lt_directories.
ENDLOOP.
Try the following
data : dirname type DIRNAME_AL11.
CALL 'C_SAPGPARAM' ID 'NAME' FIELD 'DIR_DATA'
ID 'VALUE' FIELD dirname.
Alternatively if you wanted to use your own parameters(AL11->configure) then read these out of table user_dir.

ControlCommand in AutoIt v3

I am trying to use ControlCommands with a .NET application (hence, these should all be standard Microsoft controls), but most of the ones that are of interest don't seem to do anything.
I am currently looking at a combobox (the drop down box). I used the "showdropdown" command to have it drop down, and it worked successfully.
I then tried to use "SelectString", but it didn't go to the string that I specified. How does the "SelectString" ControlCommand option work?
I have also tried "SetCurrentSelection".
This is the statement I used:
ControlCommand($windowName, "", "[Name:myComboBox]", "SelectString", "a")
I have also tried searching for it first with:
ControlCommand($windowName, "", "[Name:myComboBox]", "FindString", "a")
but it didn't find it either. Strange, the single character "a" is there.
If it helps, this is the control class: WindowsForms10.COMBOBOX
_GUICtrlComboBox_xxx functions also work on external controls. For example, _GUICtrlComboBox_FindString, _GUICtrlComboBox_SelectString, _GUICtrlComboBox_SetCurSel. Try those instead.
Call ControlGetHandle first, then use this handle to call the functions above.
Remember to include the GuiComboBox library, otherwise you will get an error message "Error: Unknown function name":
#include <GuiComboBox.au3>
You can send key presses to this control, like this:
ControlSend("Window title", "", "[NAME:comboBoxName]", "ComboBox value")
It works because ComboBox interprets input as a search string and selects the first item starting with this string.
Note: Because it searches the matching item as you type, there's no need to send a complete value, only the shortest substring.

Resources