Executing Command Line from With XSL (Using Java)

Here should go questions about transforming XML with XSLT and FOP.
SixForty
Posts: 7
Joined: Sun May 09, 2021 7:34 am

Executing Command Line from With XSL (Using Java)

Post by SixForty » Wed May 12, 2021 12:10 pm

As one step of a larger workflow, I have a portion of text from an XML document that I would like to be converting to an audio file. I can do this in multiple steps, having the XSL write the text to a text file, and then use the command line on my Mac to run the 'say' command and convert the text file to an audio file using the built in text to speech functionality. Since I'll be doing this a lot, I'm hoping to streamline the process by having the command line instruction run from directly within XSL, if possible.

After finding this information, I was able to get the access of the command line to work, but can't seem to get it to save files. I have stored the text I want converted to audio into an XSL variable, $text. By declaring a namespace in my root stylesheet element

Code: Select all

 xmlns:jv="java:java.lang.Runtime" 
and using this

Code: Select all

<xsl:value-of select="jv:exec(jv:getRuntime(), concat('say --voice=Ava --rate=250 ', $text))"/>
my computer will actually speak the text aloud, thus proving that access to the command line works. But when I try to add options to the 'say' command to have the audio saved to a file instead of spoken aloud, like this:

Code: Select all

<xsl:value-of select="jv:exec(jv:getRuntime(), concat('say --voice=Ava --rate=250 --output-file=audio.m4a --file-format=m4af data-format=aac ', $text))"/>
absolutely nothing happens. No file is created (I've searched the Mac and can't find it anywhere) I know the command is correct, since a straight copy and paste to the command line in Terminal will get the desired resulting file (when I substitute a string for the variable). So executing that exact command from the command line works, but from within the Java code inside XSL in Oxygen, it doesn't work.

My only thought is that from the command line, it knows where to put a file, in the "current working directory". I'm guessing that when executed within the Java environment as I am doing it within Oxygen, there is no definable "current working directory". Or at least not in a way that can be usable. Thus, it can't actually create the file anywhere. Can anyone comment on that?

Any insight into what might be going on here, how Oxygen, Java, and XSL are working together here, would be greatly appreciated!

adrian
Posts: 2736
Joined: Tue May 17, 2005 4:01 pm

Re: Executing Command Line from With XSL (Using Java)

Post by adrian » Wed May 12, 2021 2:26 pm

Hello,
My only thought is that from the command line, it knows where to put a file, in the "current working directory". I'm guessing that when executed within the Java environment as I am doing it within Oxygen, there is no definable "current working directory". Or at least not in a way that can be usable. Thus, it can't actually create the file anywhere. Can anyone comment on that?
You're using Java's Runtime.exec(String command) which is short for Runtime.exec(command, null, null). The last null argument (dir) is the working directory.
Java 8 API - java.lang.Runtime
dir - the working directory of the subprocess, or null if the subprocess should inherit the working directory of the current process.
So, the command inherits the working directory from Oxygen. Oxygen's working directory is usually its installation folder (can be different in some cases). By default that's in /Applications and the process can't write files to that folder.

It would probably be much simpler to just provide an absolute path for the output file:

Code: Select all

--output-file=/path/to/my/audio/files/audio.m4a
Regards,
Adrian
Adrian Buza
<oXygen/> XML Editor, Schema Editor and XSLT Editor/Debugger
http://www.oxygenxml.com

SixForty
Posts: 7
Joined: Sun May 09, 2021 7:34 am

Re: Executing Command Line from With XSL (Using Java)

Post by SixForty » Wed May 12, 2021 10:59 pm

Adrian, you are awesomeness personified! Thanks so much!

I was pretty confident that it was something like this, in relation to the working directory. I had previously tried using an absolute path for the output file, but originally that didn't work either. However, given your answer here, I went back and tried that again, and through various testing found that the reason an absolute path wasn't working originally was a different reason: issues with escaping special characters in filenames (one folder in the path has a space in the folder name).

So your advice has definitely led me to a solution that will work. Now I just have a choice to make: adjust the directory structure of my project by changing some folder names (not sure the longer term implications of this), or spend some time trying to figure out the issues I'm having with escaping a space character in a folder name.

Either way, I know I have a solution that will work. So thank you very much! I really appreciate it!

adrian
Posts: 2736
Joined: Tue May 17, 2005 4:01 pm

Re: Executing Command Line from With XSL (Using Java)

Post by adrian » Thu May 13, 2021 10:36 am

or spend some time trying to figure out the issues I'm having with escaping a space character in a folder name.
If you use single or double quotes around the paths (or the entire argument), the paths don't need escaping:
e.g.

Code: Select all

"--output-file=/path with spaces/to/my/audio/files/audio.m4a"
Regards,
Adrian
Adrian Buza
<oXygen/> XML Editor, Schema Editor and XSLT Editor/Debugger
http://www.oxygenxml.com

SixForty
Posts: 7
Joined: Sun May 09, 2021 7:34 am

Re: Executing Command Line from With XSL (Using Java)

Post by SixForty » Sat May 15, 2021 5:28 pm

Thanks for the help, Adrian. A part of my difficulty is that, since I have to use an absolute path (due to the solution to the original question), I have to dynamically generate that path based upon the document that is currently being processed. I pass in the path of the current document's directory as a parameter to the XSL stylesheet, using the ${cfdu} Editor Variable. Then I end up generating the final path that I'll need and storing that in a variable. Here's the basics of the code I'm using:

Code: Select all


    <xsl:param name="DirectoryPath"/>
    <!-- Passed in from the Configure Transformation Scenario dialog, 
            using ${cfdu} Editor Variable -->

    <xsl:template match="Element">

        <xsl:variable name="FileName">
            <xsl:apply-templates mode="FileName" select="."/>   
            <!-- Dynamically generates the name for the audio file, 
                    based upon content in the XML file currently being processed -->
        </xsl:variable>

        <xsl:variable name="TextToSpeak">
            <xsl:apply-templates mode="TextToSpeak" select="."/>    
            <!-- Dynamically generates content for the audio file, 
                    based upon content in the XML file currently being processed -->
        </xsl:variable>
        
        <xsl:variable name="FilePath">
            <xsl:value-of select="concat(substring-after($DirectoryPath, 'file:'), 'Audio/', $FileName, '.m4a')"/>    
            <!-- Creates the absolute path to store the audio file, 
                    which is in a sub-folder of the file currently being processed -->
        </xsl:variable>

        <xsl:variable name="Command" as="xs:string">
            <xsl:value-of select="concat('say --voice=Ava --rate=250 --output-file=', $FilePath, ' --file-format=m4af --data-format=aac ', $TextToSpeak)"/>
            <!-- Creates the full command to pass through the Java function for execution -->
        </xsl:variable>

        <xsl:value-of select="jv:exec(jv:getRuntime(), $Command)"/>

    </xsl:template>

With an absolute path with no spaces, this works fine.

If I try to use a path that has spaces, I end up having to escape something somewhere: either the spaces in the pathname, or if I try using quotes on the path or entire argument, as you've suggested, then I have to escape the quotes within the concat() function. But that ends up failing somehow also, as it seems the system gets a command it doesn't know how to process. Instead of saving the file, it just speaks it, along with the speaking a broken portion of the path. I've experimented with numerous options, but can't seem to get it to work.

Again, I'm sure I'm missing some aspect of properly escaping things, since it's an execution of a command line style command, through a java function call, within an XSL stylesheet. Somewhere the logic is breaking down: either in the combination of those environments, or in my understanding of the combination of those environments!

Either way, I'll keep working on this for a little while longer. If I can't get this to work, I'll give up and go the route of rearranging the project directory structure to eliminate folders with spaces in their names!

Thanks again!

adrian
Posts: 2736
Joined: Tue May 17, 2005 4:01 pm

Re: Executing Command Line from With XSL (Using Java)

Post by adrian » Thu May 20, 2021 2:25 pm

Hi,

In my experience with scripting, using quotes around paths is usually much simpler than trying to escape spaces and other special characters within paths (without using quotes).

I see you are using ${cfdu} as a "DirectoryPath". Note the description of ${cfdu}
${cfdu} - Current file folder as URL, that is the path of the currently edited document up to the name of the parent folder, represented as a URL.
So, ${cfdu} is the URL representation of the current file directory path. In practice this means spaces are escaped as for a URL (%20). AFAIK these escaped spaces cannot be used in a directory/file path, so these could cause trouble. Why not use ${cfd} instead which is a plain path with plain spaces and surround that with double qoutes? I know ${cfd} is not proposed in the list of editor variables, but it works nonetheless.

When trying to debug this, log the Command with an XSL message:

Code: Select all

<xsl:message select="$Command"/>
And try that Command in a terminal.

Try

Code: Select all

            <xsl:value-of select="concat('say --voice=Ava --rate=250 --output-file=&quot;', $FilePath, '&quot; --file-format=m4af --data-format=aac ', $TextToSpeak)"/>
Or move the first &quot; at the beginning of the --output-file= argument itself.

Regards,
Adrian
Adrian Buza
<oXygen/> XML Editor, Schema Editor and XSLT Editor/Debugger
http://www.oxygenxml.com

SixForty
Posts: 7
Joined: Sun May 09, 2021 7:34 am

Re: Executing Command Line from With XSL (Using Java)

Post by SixForty » Fri May 21, 2021 2:46 am

Thanks for the suggestions, Adrian. I shall give them a try!

Post Reply