Jean,
My reply took a bit more of time - sorry.
As said I haven’t cared about ASobjC for years and have to dive into objC.
Satimage had been a safe haven for nearly two decades for my often complicated XML stuff.
I spent the last two days with “try and error”. The result is at least promising though I still miss things (or better can’t get some stuff to work)
Below a script (partial script) of what I figured out. Maybe it can be helpful for others fighting the same problems.
A lot of things are put into handlers just for me as a lazy typer and cause they are called dozens of times in a final script.
The XMLs nesting is a bit more complicated compared to your example.
Some things like the “formatter” are used to get a better “prettyPrint” XML at the end.
The XML present a kind of “subtitles for video”
Parts of script are lost when I post/paste.
Here a link: https://www.dropbox.com/s/g2h52nuj5ln3kv9/create%20xml%20from%20scratch.scpt.zip?dl=0
How can I make it appear here?
> use AppleScript version "2.4" -- Yosemite (10.10) or later
> use framework "Foundation"
> use scripting additions
>
> property cA : a reference to current application
>
> property NSArray : a reference to cA's NSArray
> property NSMutableArray : a reference to cA's NSMutableArray
>
> property NSString : a reference to cA's NSString
>
> property xmlElemt : a reference to cA's NSXMLElement
> property xmlNode : a reference to cA's NSXMLNode
> property xmlDoc : a reference to cA's NSXMLDocument
>
> --
> property theRoot : null
> property theXML : null
>
> --
> property theFPS : 30
> property frameDur : 1 / theFPS
> property frameDurNominator : 1000
> property frameDurDeNominator : 1000 * theFPS
> property theNTSC : false
> property dropMode : "NDF"
> --
> -- this is a part extracted and cleaned from a given XML
> set aTitleTempl to "<title>
> <param name=\"Anchor Point\" key=\"9999/16130/1999783474/1/100/107\" value=\"768 -432\"/>
> <param name=\"Layout Method\" key=\"9999/16130/1999783474/2/314\" value=\"1 (Paragraph)\"/>
> <param name=\"Fade In\" key=\"9999/16130/1999783474/2/319/322\" value=\"0\"/>
> <param name=\"Left Margin\" key=\"9999/16130/1999783474/2/323\" value=\"0.0880737\"/>
> <param name=\"Right Margin\" key=\"9999/16130/1999783474/2/324\" value=\"1536.3\"/>
> <param name=\"Bottom Margin\" key=\"9999/16130/1999783474/2/326\" value=\"-864.234\"/>
> <param name=\"Alignment\" key=\"9999/16130/1999783474/2/354/1999786174/401\" value=\"1 (Center)\"/>
> <param name=\"Alignment\" key=\"9999/16130/1999783474/2/373\" value=\"0 (Left) 2 (Bottom)\"/>
> <param name=\"Color\" key=\"9999/16130/1999783474/5/3008338027/30/32\" value=\"0 0 0\"/>
> <param name=\"Wrap Mode\" key=\"9999/16130/1999783474/5/3008338027/30/34/5\" value=\"1 (Repeat)\"/>
> <param name=\"Width\" key=\"9999/16130/1999783474/5/3008338027/30/36\" value=\"2\"/>
> <param name=\"Blur\" key=\"9999/16130/1999783474/5/3008338027/30/76\" value=\"1 1\"/>
> </title>"
>
> -- prepare other values
> set intFPS to (round theFPS) as integer
> if theFPS mod 1 > 0 then
> set frameDurNominator to "1001"
> if intFPS = 24 then
> set dropMode to "NDF"
> else
> set dropMode to "DF"
> end if
> else
> set dropMode to "NDF"
> set frameDurNominator to "1000"
> end if
> set frameDurDeNominator to (intFPS * 1000) as string
>
> set frameDurTx to my framesToTxTime(1)
>
> -- these resources must be indexed later for reference
> set rsrcValueList to {{tagValPair:{"format", ""}, attrValList:{{"name", "Undefined"}, {"frameDuration", frameDurTx}, {"width", "1920"}, {"height", "1080"}}}, {tagValPair:{"effect", ""}, attrValList:{{"name", "TextUp"}, {"uid", "somePosixPath.moti"}}}}
>
> set seqFmtAttrList to {{"duration", "300000/30000s"}, {"tcStart", "0s"}, {"tcFormat", dropMode}, {"audioLayout", "stereo"}, {"audioRate", "48k"}}
>
> -- these text parts are simplified, items are either different color or style
> -- the numbers represent frames for "offset" and "duration"
> -- at a later state this could be handled with "dictionaries"
> set aTextList to {{0, 100, {"first title"}}, {150, 80, {"second title", linefeed, "second line"}}, {240, 120, {"third title"}}, {400, 180, {"4th title", linefeed, "second line"}}}
> set titleRole to "titles.English"
>
> -----------------------
> set theRoot to xmlNode's elementWithName:"fcpxml"
> my setXMLAttribute(theRoot, {"version", "1.3"})
>
> set theXML to xmlNode's documentWithRootElement:theRoot
> theXML's setCharacterEncoding:"UTF-8"
> theXML's setStandalone:true
>
> --??? how to set a doc type: <!DOCTYPE fcpxml>
>
> set projNode to my addXMLChild(theRoot, {"project", ""})
> my addFormatter(projNode)
> my setXMLAttribute(projNode, {"name", "Untitled Project"})
> my setXMLAttribute(projNode, {"uid", my genUID()})
>
> -- add the res node at the project
> set resNode to my addXMLChild(projNode, {"resources", ""})
> my addFormatter(resNode)
>
> -- add all the children needed
> -- the first "format" is the relevant one for the later sequence
> set seqFmtSet to false
> set fxFmtSet to false
> set r to 0
> repeat with theItem in rsrcValueList
> set r to r + 1
> set idVal to ("r" & r)
> set {tagName, tagValue} to theItem's tagValPair
> if not seqFmtSet then
> if tagName as string = "format" then
> set seqFmtId to idVal as string
> set beginning of seqFmtAttrList to {"format", seqFmtId}
> set seqFmtSet to true
> end if
> end if
> if not fxFmtSet then
> if tagName as string = "effect" then
> set fxFmtId to idVal as string
> set fxFmtSet to true
> end if
> end if
>
> set someNode to my addXMLChild(resNode, {tagName, tagValue})
> my setXMLAttribute(someNode, {"id", idVal})
> set attrValist to theItem's attrValList
> setXMLAttributeList(someNode, attrValist)
> end repeat
>
> -- now add a sequence to the project
> set seqNode to my addXMLChild(projNode, {"sequence", ""})
> -- set seq attributes - the format from above
> setXMLAttributeList(seqNode, seqFmtAttrList)
> my addFormatter(seqNode)
>
> --set comNode to my addXMLChild(seqNode, {"!--", " test "})
>
> -- add the basic spine where everything is attached
> set spineNode to my addXMLChild(seqNode, {"spine", ""})
> my addFormatter(spineNode)
>
> -- now add the titles to the spine
> -- times (offset, duration) are dummy values
> -- role has to be set upfront
> set t to 0 -- base index to set up text styles
> repeat with aText in aTextList
> set t to t + 1
> set aName to "" -- create a "name" from all text entries
>
> set {tFramesIn, tFramesDur, tTextItems} to aText
> set tOff to my framesToTxTime(tFramesIn)
> set tDur to my framesToTxTime(tFramesDur)
>
> set aComment to (xmlNode's commentWithStringValue:("Title No. " & t & " +++++++++++++++++++++"))
> (spineNode's addChild:aComment)
>
> set styleIdBase to ("ts" & t) as string
> set titleNode to my addXMLChildFromString(spineNode, aTitleTempl)
>
> -- dependent on the effect type above a repeat loop is needed
> repeat 1 times -- our effect has only one text entry
> set txNode to my addXMLChild(titleNode, {"text", ""})
> my addXMLChild(txNode, {"remove", ""})
>
> -- now create a style node for each style part
> set s to 0
> repeat with txPart in tTextItems
> set aName to (aName & txPart) as string
> set s to s + 1
> set styleID to (styleIdBase & "-" & s)
> set txStyledEntryNode to my addXMLChild(txNode, {"text-style", txPart})
> setXMLAttribute(txStyledEntryNode, {"ref", styleID})
>
> -- add a style definition
> set txStyleDefNode to my addXMLChild(titleNode, {"text-style-def", ""})
> setXMLAttribute(txStyleDefNode, {"id", styleID})
> my addFormatter(txStyleDefNode)
>
> set txStyleDescrNode to my addXMLChild(txStyleDefNode, {"text-style", ""})
> end repeat -- with txPart in aText
> end repeat -- repeat 1 times
>
> -- now add attribures to the title node
> set attrValist to {{"name", aName}, {"ref", fxFmtId}, {"offset", tOff}, {"duration", tDur}, {"start", "3600s"}, {"role", titleRole}}
> setXMLAttributeList(titleNode, attrValist)
>
> -- add a reference
> my addXMLChild(titleNode, {"note", my genUID()})
> end repeat -- with aText in aTextList
>
> -- once the last title is set the sequence dummy duration must be corrected
> -- it is based on the last title's out frame
> set lastTitleOut to tFramesIn + tFramesDur
> set seqDur to framesToTxTime(lastTitleOut)
> setXMLAttribute(seqNode, {"duration", seqDur})
> ---------------------------------------------------------
> -- set all fx and styles
> set outLineAttrList to {{"strokeColor", "0 0 0 1"}, {"strokeWidth", "2"}}
> set fontAttrList to {{"font", "Arial"}, {"fontSize", "52"}}
>
> -- set font and size
> set allTxStyles to my XPathNodes(theRoot, "//text-style-def/text-style")
> repeat with txStyleNode in allTxStyles
> my setXMLAttributeList(txStyleNode, fontAttrList)
> my setXMLAttributeList(txStyleNode, outLineAttrList)
> end repeat
> ---------------------------------------------------------
> -- clean up
> set theXMLString to theXML's XMLStringWithOptions:(cA's NSXMLNodePrettyPrint)
> set theXMLString to my replaceString(theXMLString, "<remove></remove>", "")
> set the clipboard to theXMLString
> theXMLString
>
>
> (* === xml handlers === *)
> on XPathNodes(startNode, theArg)
> set theArg to theArg as text
> set {theNode, theError} to startNode's nodesForXPath:(theArg) |error|:(reference)
> if theNode = missing value then
> set theNode to {}
> end if
> return theNode
> end XPathNodes
>
> on getXMLAttribute(theNode, attrName)
> set attrName to attrName as string
> set attrVal to (theNode's attributeForName:attrName)
> set attrVal to (attrVal's stringValue) as string
> return attrVal
> end getXMLAttribute
>
> on setXMLAttribute(theNode, ValPair)
> set {theName, theValue} to ValPair
> set {theName, theValue} to {theName as string, theValue as string}
> set theAttr to xmlNode's attributeWithName:theName stringValue:theValue
> theNode's addAttribute:theAttr
> end setXMLAttribute
>
> on setXMLAttributeList(theNode, valPairList)
> repeat with ValPair in valPairList
> try
> set {theName, theValue} to ValPair
> set {theName, theValue} to {theName as string, theValue as string}
> set theAttr to (xmlNode's attributeWithName:theName stringValue:theValue)
> (theNode's addAttribute:theAttr)
> on error errMsg
> log errMsg
> end try
> end repeat
> end setXMLAttributeList
>
> on addXMLChild(theNode, ValPair)
> set {theName, theValue} to ValPair
> set {theName, theValue} to {theName as string, theValue as string}
> set childNode to xmlNode's elementWithName:theName stringValue:theValue
> theNode's addChild:childNode
> return childNode
> end addXMLChild
>
> on addXMLChildFromString(theNode, theString)
> set {nElement, theError} to (xmlElemt's alloc()'s initWithXMLString:theString |error|:(reference))
> theNode's addChild:nElement
> return nElement
> end addXMLChildFromString
>
> on addFormatter(parentNode)
> my addXMLChild(parentNode, {"remove", ""})
> end addFormatter
>
> (* === time etc === *)
> on framesToTxTime(theFrames)
> set theFrames to (frameDurNominator * theFrames) as string
> set theTime to theFrames & "/" & frameDurDeNominator & "s" as string
> return theTime
> end framesToTxTime
>
> (* === text === *)
> on cleanWhiteSpace(theString)
> -- create Cocoa string from passed AppleScript string
> set the theString to current application's NSString's stringWithString:theString
> set the cleanString to theString's stringByTrimmingCharactersInSet:(current application's NSCharacterSet's whitespaceAndNewlineCharacterSet)
> return (cleanString as string)
> end cleanWhiteSpace
>
> on replaceString(sourceText, searchString, replacementString)
> set the sourceString to current application's NSString's stringWithString:sourceText
> return (the sourceString's stringByReplacingOccurrencesOfString:searchString withString:replacementString) as string
> end replaceString
>
> (* === other === *)
> on genUID()
> return (do shell script "uuidgen") as string
> end genUID
The result of the script:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<fcpxml version="1.3">
<project name="Untitled Project" uid="C8CA7C4F-DB99-4E40-8675-E00CAC27F7C4">
<resources>
<format id="r1" name="Undefined" frameDuration="1000/30000s" width="1920" height="1080"></format>
<effect id="r2" name="TextUp" uid="somePosixPath.moti"></effect>
</resources>
<sequence f="o" r="1" duration="300000/30000s" tcStart="0s" tcFormat="NDF" audioLayout="stereo" audioRate="48k">
<spine>
<!--Title No. 1 +++++++++++++++++++++-->
<title name="first title" ref="r2" offset="0/30000s" duration="100000/30000s" start="3600s" role="titles.English">
<param name="Anchor Point" key="9999/16130/1999783474/1/100/107" value="768 -432"></param>
<param name="Layout Method" key="9999/16130/1999783474/2/314" value="1 (Paragraph)"></param>
<param name="Fade In" key="9999/16130/1999783474/2/319/322" value="0"></param>
<param name="Left Margin" key="9999/16130/1999783474/2/323" value="0.0880737"></param>
<param name="Right Margin" key="9999/16130/1999783474/2/324" value="1536.3"></param>
<param name="Bottom Margin" key="9999/16130/1999783474/2/326" value="-864.234"></param>
<param name="Alignment" key="9999/16130/1999783474/2/354/1999786174/401" value="1 (Center)"></param>
<param name="Alignment" key="9999/16130/1999783474/2/373" value="0 (Left) 2 (Bottom)"></param>
<param name="Color" key="9999/16130/1999783474/5/3008338027/30/32" value="0 0 0"></param>
<param name="Wrap Mode" key="9999/16130/1999783474/5/3008338027/30/34/5" value="1 (Repeat)"></param>
<param name="Width" key="9999/16130/1999783474/5/3008338027/30/36" value="2"></param>
<param name="Blur" key="9999/16130/1999783474/5/3008338027/30/76" value="1 1"></param>
<text>
<text-style ref="ts1-1">first title</text-style>
</text>
<text-style-def id="ts1-1">
<text-style font="Arial" fontSize="52" strokeColor="0 0 0 1" strokeWidth="2"></text-style>
</text-style-def>
<note>3B649C00-4F8D-4270-89CC-E3DDF24E5F44</note>
</title>
<!--Title No. 2 +++++++++++++++++++++-->
<title name="second title
second line" ref="r2" offset="150000/30000s" duration="80000/30000s" start="3600s" role="titles.English">
<param name="Anchor Point" key="9999/16130/1999783474/1/100/107" value="768 -432"></param>
<param name="Layout Method" key="9999/16130/1999783474/2/314" value="1 (Paragraph)"></param>
<param name="Fade In" key="9999/16130/1999783474/2/319/322" value="0"></param>
<param name="Left Margin" key="9999/16130/1999783474/2/323" value="0.0880737"></param>
<param name="Right Margin" key="9999/16130/1999783474/2/324" value="1536.3"></param>
<param name="Bottom Margin" key="9999/16130/1999783474/2/326" value="-864.234"></param>
<param name="Alignment" key="9999/16130/1999783474/2/354/1999786174/401" value="1 (Center)"></param>
<param name="Alignment" key="9999/16130/1999783474/2/373" value="0 (Left) 2 (Bottom)"></param>
<param name="Color" key="9999/16130/1999783474/5/3008338027/30/32" value="0 0 0"></param>
<param name="Wrap Mode" key="9999/16130/1999783474/5/3008338027/30/34/5" value="1 (Repeat)"></param>
<param name="Width" key="9999/16130/1999783474/5/3008338027/30/36" value="2"></param>
<param name="Blur" key="9999/16130/1999783474/5/3008338027/30/76" value="1 1"></param>
<text>
<text-style ref="ts2-1">second title</text-style>
<text-style ref="ts2-2">
</text-style>
<text-style ref="ts2-3">second line</text-style>
</text>
<text-style-def id="ts2-1">
<text-style font="Arial" fontSize="52" strokeColor="0 0 0 1" strokeWidth="2"></text-style>
</text-style-def>
<text-style-def id="ts2-2">
<text-style font="Arial" fontSize="52" strokeColor="0 0 0 1" strokeWidth="2"></text-style>
</text-style-def>
<text-style-def id="ts2-3">
<text-style font="Arial" fontSize="52" strokeColor="0 0 0 1" strokeWidth="2"></text-style>
</text-style-def>
<note>0BD069A4-176A-4250-A29F-97638BA9C710</note>
</title>
<!--Title No. 3 +++++++++++++++++++++-->
<title name="third title" ref="r2" offset="240000/30000s" duration="120000/30000s" start="3600s" role="titles.English">
<param name="Anchor Point" key="9999/16130/1999783474/1/100/107" value="768 -432"></param>
<param name="Layout Method" key="9999/16130/1999783474/2/314" value="1 (Paragraph)"></param>
<param name="Fade In" key="9999/16130/1999783474/2/319/322" value="0"></param>
<param name="Left Margin" key="9999/16130/1999783474/2/323" value="0.0880737"></param>
<param name="Right Margin" key="9999/16130/1999783474/2/324" value="1536.3"></param>
<param name="Bottom Margin" key="9999/16130/1999783474/2/326" value="-864.234"></param>
<param name="Alignment" key="9999/16130/1999783474/2/354/1999786174/401" value="1 (Center)"></param>
<param name="Alignment" key="9999/16130/1999783474/2/373" value="0 (Left) 2 (Bottom)"></param>
<param name="Color" key="9999/16130/1999783474/5/3008338027/30/32" value="0 0 0"></param>
<param name="Wrap Mode" key="9999/16130/1999783474/5/3008338027/30/34/5" value="1 (Repeat)"></param>
<param name="Width" key="9999/16130/1999783474/5/3008338027/30/36" value="2"></param>
<param name="Blur" key="9999/16130/1999783474/5/3008338027/30/76" value="1 1"></param>
<text>
<text-style ref="ts3-1">third title</text-style>
</text>
<text-style-def id="ts3-1">
<text-style font="Arial" fontSize="52" strokeColor="0 0 0 1" strokeWidth="2"></text-style>
</text-style-def>
<note>79B2F4ED-9697-4D3E-9D9C-91C5F8DC3B66</note>
</title>
<!--Title No. 4 +++++++++++++++++++++-->
<title name="4th title
second line" ref="r2" offset="400000/30000s" duration="180000/30000s" start="3600s" role="titles.English">
<param name="Anchor Point" key="9999/16130/1999783474/1/100/107" value="768 -432"></param>
<param name="Layout Method" key="9999/16130/1999783474/2/314" value="1 (Paragraph)"></param>
<param name="Fade In" key="9999/16130/1999783474/2/319/322" value="0"></param>
<param name="Left Margin" key="9999/16130/1999783474/2/323" value="0.0880737"></param>
<param name="Right Margin" key="9999/16130/1999783474/2/324" value="1536.3"></param>
<param name="Bottom Margin" key="9999/16130/1999783474/2/326" value="-864.234"></param>
<param name="Alignment" key="9999/16130/1999783474/2/354/1999786174/401" value="1 (Center)"></param>
<param name="Alignment" key="9999/16130/1999783474/2/373" value="0 (Left) 2 (Bottom)"></param>
<param name="Color" key="9999/16130/1999783474/5/3008338027/30/32" value="0 0 0"></param>
<param name="Wrap Mode" key="9999/16130/1999783474/5/3008338027/30/34/5" value="1 (Repeat)"></param>
<param name="Width" key="9999/16130/1999783474/5/3008338027/30/36" value="2"></param>
<param name="Blur" key="9999/16130/1999783474/5/3008338027/30/76" value="1 1"></param>
<text>
<text-style ref="ts4-1">4th title</text-style>
<text-style ref="ts4-2">
</text-style>
<text-style ref="ts4-3">second line</text-style>
</text>
<text-style-def id="ts4-1">
<text-style font="Arial" fontSize="52" strokeColor="0 0 0 1" strokeWidth="2"></text-style>
</text-style-def>
<text-style-def id="ts4-2">
<text-style font="Arial" fontSize="52" strokeColor="0 0 0 1" strokeWidth="2"></text-style>
</text-style-def>
<text-style-def id="ts4-3">
<text-style font="Arial" fontSize="52" strokeColor="0 0 0 1" strokeWidth="2"></text-style>
</text-style-def>
<note>EE75DAE4-EA08-4C30-BC60-AD6E70D3B3E8</note>
</title>
</spine>
</sequence>
</project>
</fcpxml>
My problem still is to set a
<!DOCTYPE fcpxml>
before the root.
Additionally I don’t succeed to get a “parent” of a node - looks simple, but I don’t get it.