Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

initialsBB

macrumors 6502a
Original poster
Oct 18, 2010
688
2
Hi,
Don't know if programming is the right place for this thread, but it seems to me the best place to look.

I would like to know if anyone can help me figure out how to manipulate an XML file exported by Final Cut Pro. FCP allows batch changing properties like scale, position, rotation, of a text generator inside it's timeline, but it does not allow batch changing of text-specific properties such as font size, font color, etc, but all these properties can be seen inside an XML export of the sequence.

I would like to change the font color from white to yellow, can't realistically be done manually as there are hundreds of text generators (it's a subtitle track). I tried opening the XML in TextEdit and doing a find & replace but it won't find all the occurrences of this block below:

Code:
<parameter>
								<parameterid>textcolor</parameterid>
								<name>Text Color</name>
								<value>
									<alpha>255</alpha>
									<red>235</red>
									<green>235</green>
									<blue>235</blue>
								</value>
							</parameter>

I tried a program called "editix" but it won't let me change this parameter for all the instances... any help for this problem ? Is there a terminal app that can help me ? Why won't TextEdit Let me do this ?

Thanks for any help !
 
Last edited:

jiminaus

macrumors 65816
Dec 16, 2010
1,449
1
Sydney
Based around just the XML you posted, I came up with the following awk program that make the changes.

subtitlecolor.awk
Code:
BEGIN {
    FS = ""
}

/<parameter>/ {
    in_parameter = 1
    parameterid_is_textcolor = 0
}

in_parameter && /<parameterid>.*<\/parameterid>/ {
    split($0, a, /<\/?parameterid>/)
    parameterid_is_textcolor = (a[2] == "textcolor")
}

parameterid_is_textcolor && /<value>/ {
    in_value = 1
}

in_value && /<red>235<\/red>/ {
    split($0, a, /<red>235<\/red>/)
    print a[1] "<red>255</red>" a[2]
    next
}

in_value && /<green>235<\/green>/ {
    split($0, a, /<green>235<\/green>/)
    print a[1] "<green>255</green>" a[2]
    next
}

in_value && /<blue>235<\/blue>/ {
    split($0, a, /<blue>235<\/blue>/)
    print a[1] "<blue>255</blue>" a[2]
    next
}

/<\/value>/ {
    in_value = 0
}

/<\/parameter>/ {
    in_parameter = 0
}

{ print $0 }

You can run the program like so from Terminal.
Code:
awk -f subtitlecolor.awk < in.xml > out.xml
Assuming you saved the awk program in subtitlecolor.awk, the FCP xml is in in.xml, both the awk program and FCP xml are in the same directory, and you've changed to that directory in Terminal.

Afterwards you should have a new file called out.xml which has yellow textcolor parameters changed to white textcolor parameters.

Note that it will change all such occurrences. You have given enough XML context to limit it just to the subtitle track(s).
 

larkost

macrumors 6502a
Oct 13, 2007
534
1
When you get up to this complexity, then it is time to start learning a scripting language like Python, Ruby, or Perl. My recommendation would be for Python as the xml.dom.minidom class is not too bad (but a little awkward). Perl is a bit worse for xml handling, and someone else is going to have to chime in on Ruby, as I have not used it for xml handling.

A second thought: you might be able to get away with an XSLT transformation, assuming you can express the rule in XSLT.
 

initialsBB

macrumors 6502a
Original poster
Oct 18, 2010
688
2
@jiminaus
Amazing, thank you so much for this piece of knowledge !

I created to AWK and ran it in the terminal. Unfortunately the text is still white, but looking at the code I was wondering if maybe the values you entered are not quite matched. If you wouldn't mind explaining this following bit I can try and adapt it better on my side :

Code:
in_value && /<red>235<\/red>/ {
    split($0, a, /<red>235<\/red>/)
    print a[1] "<red>255</red>" a[2]
    next

is the print line that is actually writing the color component value into the XML ?
Code:
print a[1] "<red>255</red>" a[2]

If I can read your code, then first block after the BEGIN header looks for the instance where the parameterid is textcolor, the next block loads the parameter and then there are the three print commands for R, G and B...?

@larkost
Much appreciation for the thoughts. As you may know Final Cut Pro X is due out this month and I am not alone in praying these issues have been sorted out. Nevertheless XML sequence exporting is one of the more confidential but incredibly powerful features of the app. I will definitely look into Python when I have some time on my hands.
 
Last edited:

jiminaus

macrumors 65816
Dec 16, 2010
1,449
1
Sydney
Code:
in_value && /<red>235<\/red>/ {
    split($0, a, /<red>235<\/red>/)
    print a[1] "<red>255</red>" a[2]
    next
}

Let me paraphrase the code.

1) If we're inside a value element (associated with a textcolor parameter), and the line contains <red>235</red>, then do a-c below. If not, skip over a-c.
a) Put everything on the line before <red>235</red> into a[1] and everything after into a[2]
b) Print out a[1], followed by <red>255</red>, followed by a[2]
c) Don't process any more instructions for this line, move on to the next line

a) and b) is just a way of outputting a line, but first replacing <red>235</red> with <red>255</red>.

BTW Awk is not my thing. I do have the re-learn it every time I have to go use it, which isn't very often. So I may not have created the best awk program possible. Having said that though, I did test the code against the sample XML you gave and it did work. When you look at the outputted xml (I called it out.xml in my instructions above), do you see the changed values insides the red, green, and blue tags?
 

initialsBB

macrumors 6502a
Original poster
Oct 18, 2010
688
2
Thank you for the explanation ! I may not have clearly stated my problem but thanks to you here is the code that works :)
(I wanted to go from RGB values 255,255,255 to 255,235,0)

Code:
BEGIN {
    FS = ""
}

/<parameter>/ {
    in_parameter = 1
    parameterid_is_textcolor = 0
}

in_parameter && /<parameterid>.*<\/parameterid>/ {
    split($0, a, /<\/?parameterid>/)
    parameterid_is_textcolor = (a[2] == "textcolor")
}

parameterid_is_textcolor && /<value>/ {
    in_value = 1
}

in_value && /<red>255<\/red>/ {
    split($0, a, /<red>255<\/red>/)
    print a[1] "<red>255</red>" a[2]
    next
}

in_value && /<green>255<\/green>/ {
    split($0, a, /<green>255<\/green>/)
    print a[1] "<green>235</green>" a[2]
    next
}

in_value && /<blue>255<\/blue>/ {
    split($0, a, /<blue>255<\/blue>/)
    print a[1] "<blue>0</blue>" a[2]
    next
}

/<\/value>/ {
    in_value = 0
}

/<\/parameter>/ {
    in_parameter = 0
}

{ print $0 }

This piece of code can now help me batch change the text controls !! :)
If I wanted to change now say the font, I would simply replace the textcolor ID with the relevant font one, cut out the green and blue channel changes and be done... I'll definitely be having oodles of fun with this, and make colleagues green with envy :p

Thank you again !
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.