Page 1 of 1

customizations.xml

PostPosted: Sat Jan 02, 2021 5:58 pm
by Jack2
Hi, thanks for useful tools.

I would like to add keyboard shortcuts to a generated docx file, which can be done by adding a part at /word/customizations.xml with a type of application/vnd.ms-word.keyMapCustomizations+xml and with elements under the http://schemas.microsoft.com/office/word/2006/wordml namespace. (I've done this successfully before in C# with this library https://github.com/OfficeDev/Open-XML-SDK)

Is this possible out of the box with docx4j? If not, is it possible for me to extend docx4j? (I had a look at the jaxb context creation and this doesn't seem extensible). And if not, is it something that could be added to the project? Perhaps I can make a pull-request?

Thanks for your advice.
Jack

Re: customizations.xml

PostPosted: Sun Jan 03, 2021 7:29 am
by jason
HI Jack

In the absence of a specific class which handles a keyMapCustomizations part, docx4j would probably represent it as a DefaultXmlPart

To help with adding specifc support, please attach a short docx containing a keyMapCustomizations part

It is probably easier for me to add the support. Need a copy of the relevant XSD, if you can find in online somewhere.

Re: customizations.xml

PostPosted: Mon Jan 04, 2021 10:04 am
by jason
Following https://wordribbon.tips.net/T010305_Cha ... _Keys.html I now have a docx containing the relevant part.

For example:

Syntax: [ Download ] [ Hide ]
Using xml Syntax Highlighting
<wne:tcg xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml">
  <wne:keymaps>
    <wne:keymap wne:kcmPrimary="0434">
      <wne:wch wne:val="000020AC"/>
    </wne:keymap>
  </wne:keymaps>
</wne:tcg>
 
Parsed in 0.001 seconds, using GeSHi 1.0.8.4


It does use xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" which you can find in [MS-OFFMACRO] at https://docs.microsoft.com/en-us/opensp ... 2a8c243241 (and docx4j already has a representation of this at org.docx4j.com.microsoft.schemas.office.word.x2006.wordml )

However, that XSD does not define the tcg or keymaps elements!

Instead, these can be found in [MS-OI29500]

I'm adding JAXB representations for these now...

Re: customizations.xml

PostPosted: Tue Jan 05, 2021 4:22 pm
by jason
See now announces/docx4j-8-2-8-released-t2965.html

There is getKeyMapCustomizationsPart() at
https://github.com/plutext/docx4j/blob/ ... .java#L857

There are a couple of things which I didn't add:
1. a specialised part for the binary toolbar; see further https://docs.microsoft.com/en-us/opensp ... 47299f9e39 and https://www.alphr.com/realworld/136470/ ... he-ribbon/
2. generated JAXB classes for https://github.com/plutext/docx4j/blob/ ... stomui.xsd since JAXB contexts take a while to load, and this seems like a fair few classes for which there will be little need

Re: customizations.xml

PostPosted: Mon Jan 18, 2021 9:28 am
by Jack2
Oh, great. Thanks for that. I saw your first email and went hunting for documentation, and found MS-OFFMACRO also, but unlike you, I didn't find MS-OI29500. Then I went on holidays and missed your follow-ups. I'll give the new build a spin, but I'm sure that it will work for my needs. Thanks again.

Re: customizations.xml

PostPosted: Mon Jan 18, 2021 5:22 pm
by Jack2
hi again,

Should the following...

Code: Select all
public final class KeyMapCustomizationsPart extends JaxbXmlPart<JAXBElement<CTTcg>>


perhaps be...

Code: Select all
public final class KeyMapCustomizationsPart extends JaxbXmlPart<CTTcg>


This kotlin code (which compiles ok)...
Code: Select all
    val part = File("src/test/resources/customization.xml").inputStream().use {
      KeyMapCustomizationsPart().apply { unmarshal(it) }
    }
   
    part.contents.value.keymaps.keymap.forEach {
      println(it.acd.acdName)
    }


is throwing an exception:

class org.docx4j.com.microsoft.schemas.office.word.x2006.wordml.CTTcg cannot be cast to class javax.xml.bind.JAXBElement


Is it because unwrap is being called already in unmarshal??

Thanks.

Re: customizations.xml

PostPosted: Wed Jan 20, 2021 6:43 pm
by jason
As a quick sanity test, the following works ok:

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
                KeyMapCustomizationsPart kPart = wordMLPackage.getMainDocumentPart().getKeyMapCustomizationsPart();
                Object o = kPart.getContents();
                System.out.println(XmlUtils.marshaltoString(o));
 
Parsed in 0.015 seconds, using GeSHi 1.0.8.4


Sketch of Java code to achieve what you want:

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
                org.docx4j.com.microsoft.schemas.office.word.x2006.wordml.ObjectFactory factory = new org.docx4j.com.microsoft.schemas.office.word.x2006.wordml.ObjectFactory();
                JAXBElement<CTTcg> wrapped = factory.createTcg( your_unmarshal(it) );
                kPart.setContents(wrapped);            
 
Parsed in 0.013 seconds, using GeSHi 1.0.8.4

Re: customizations.xml

PostPosted: Thu Jan 21, 2021 12:26 am
by Jack2
As a quick sanity test, the following works ok


yep, that does work ok

Sketch of Java code to achieve what you want


I guess "your_unmarshal" is the missing piece. Shouldn't I use JaxbXmlPart::unmarshal() ??

I can actually get it to work in Java, but not in Kotlin (weirdly)...

Here is some working Java code:

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
    var part = new KeyMapCustomizationsPart();
    part.unmarshal(new FileInputStream("src/test/resources/customization.xml"));
    // var object = part.getContents(); <<< this fails at runtime with "CTTcg cannot be cast to class javax.xml.bind.JAXBElement"
    Object object = part.getContents(); // <<< but this works ok
    var tcg = (CTTcg) object; // <<< intellij gives me this warning "Casting 'object' to 'CTTcg' will produce 'ClassCastException' for any non-null value", yet it compiles and runs??
    tcg.getAcds().getAcd().forEach((a) -> System.out.println(a.getAcdName())); // this prints the expected values
 
Parsed in 0.014 seconds, using GeSHi 1.0.8.4


And if I don't use JaxbXmlPart's unmarshal, and hence don't call unwrap, then as your second sketch shows, I can set the contents correctly.

Kotlin:
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
    val part = File("src/test/resources/customization.xml").inputStream().use {
      KeyMapCustomizationsPart().apply { contents = jaxbContext.createUnmarshaller().unmarshal(it) as JAXBElement<CTTcg> }
    }
 
Parsed in 0.014 seconds, using GeSHi 1.0.8.4


I had a quick look at e.g. HeaderPart which uses the plain class as the generic type, rather than JAXBElement, and thought maybe KeyMapCustomizationsPart should be the same.

Anyway, I now have working code using the work-around shown above, so I'm grateful for your help.

Cheers.