root/branches/perian-1.1/Subtitles/SubParsing.m.rl

Revision 887, 13.6 kB (checked in by astrange, 8 months ago)

Merge trunk to 1.1 branch.

Line 
1 /*
2  *  SubParsing.c
3  *  SSARender2
4  *
5  *  Created by Alexander Strange on 7/25/07.
6  *  Copyright 2007 __MyCompanyName__. All rights reserved.
7  *
8  */
9
10 #import "SubParsing.h"
11 #import "SubUtilities.h"
12 #import "SubContext.h"
13 #import "Codecprintf.h"
14
15 %%machine SSAfile;
16 %%write data;
17
18 @implementation SubRenderSpan
19 +(SubRenderSpan*)startingSpanForDiv:(SubRenderDiv*)div delegate:(SubRenderer*)delegate
20 {
21         SubRenderSpan *span = [[SubRenderSpan alloc] init];
22         span->offset = 0;
23         span->ex = [delegate spanExtraFromRenderDiv:div];
24         span->delegate = delegate;
25         return [span autorelease];
26 }
27
28 -(SubRenderSpan*)cloneWithDelegate:(SubRenderer*)delegate_
29 {
30         SubRenderSpan *span = [[SubRenderSpan alloc] init];
31         span->offset = offset;
32         span->ex = [delegate_ cloneSpanExtra:self];
33         span->delegate = delegate_;
34         return [span autorelease];
35 }
36
37 -(void)dealloc
38 {
39         [delegate releaseSpanExtra:ex];
40         [super dealloc];
41 }
42
43 -(void)finalize {[delegate releaseSpanExtra:ex]; [super finalize];}
44 @end
45
46 @implementation SubRenderDiv
47 -(NSString*)description
48 {
49         int i, sc = [spans count];
50         NSMutableString *tmp = [NSMutableString stringWithFormat:@"div \"%@\" with %d spans:", text, sc];
51         for (i = 0; i < sc; i++) {[tmp appendFormat:@" %d",((SubRenderSpan*)[spans objectAtIndex:i])->offset];}
52         [tmp appendFormat:@" %d", [text length]];
53         return tmp;
54 }
55
56 -(SubRenderDiv*)init
57 {
58         if (self = [super init]) {
59                 text = nil;
60                 styleLine = nil;
61                 marginL = marginR = marginV = layer = 0;
62                 spans = nil;
63                
64                 posX = posY = 0;
65                 alignH = kSubAlignmentMiddle; alignV = kSubAlignmentBottom;
66                
67                 is_shape = positioned = NO;
68                 render_complexity = 0;
69         }
70        
71         return self;
72 }
73
74 -(SubRenderDiv*)nextDivWithDelegate:(SubRenderer*)delegate
75 {
76         SubRenderDiv *div = [[[SubRenderDiv alloc] init] autorelease];
77        
78         div->text    = [[NSMutableString string] retain];
79   div->styleLine = [styleLine retain];
80         div->marginL = marginL;
81         div->marginR = marginR;
82         div->marginV = marginV;
83         div->layer   = layer;
84        
85         div->spans   = [[NSMutableArray arrayWithObject:[[spans objectAtIndex:[spans count]-1] cloneWithDelegate:delegate]] retain];
86        
87         div->posX    = posX;
88         div->posY    = posY;
89         div->alignH  = alignH;
90         div->alignV  = alignV;
91   div->wrapStyle = wrapStyle;
92  
93     div->is_shape = NO;
94         div->positioned = positioned;
95         div->render_complexity = render_complexity;
96        
97         return div;
98 }
99
100 -(void)dealloc
101 {
102         [text release];
103         [styleLine release];
104         [spans release];
105         [super dealloc];
106 }
107 @end
108
109 extern BOOL IsScriptASS(NSDictionary *headers);
110
111 static NSArray *SplitByFormat(NSString *format, NSArray *lines)
112 {
113         NSArray *formarray = STSplitStringIgnoringWhitespace(format,@",");
114         int i, numlines = [lines count], numfields = [formarray count];
115         NSMutableArray *ar = [NSMutableArray arrayWithCapacity:numlines];
116        
117         for (i = 0; i < numlines; i++) {
118                 NSString *s = [lines objectAtIndex:i];
119                 NSArray *splitline = STSplitStringWithCount(s, @",", numfields);
120                
121                 if ([splitline count] != numfields) continue;
122                 [ar addObject:[NSDictionary dictionaryWithObjects:splitline
123                                                                                                   forKeys:formarray]];
124         }
125        
126         return ar;
127 }
128
129 void SubParseSSAFile(const unichar *ssa, size_t len, NSDictionary **headers, NSArray **styles, NSArray **subs)
130 {
131         const unichar *p = ssa, *pe = ssa + len, *strbegin = p;
132         int cs=0;
133        
134         NSMutableDictionary *hd = [NSMutableDictionary dictionary];
135         NSMutableArray *stylearr = [NSMutableArray array], *eventarr = [NSMutableArray array], *cur_array=NULL;
136         NSCharacterSet *wcs = [NSCharacterSet whitespaceCharacterSet];
137         NSString *str=NULL, *styleformat=NULL, *eventformat=NULL;
138        
139 #define send() [NSString stringWithCharacters:strbegin length:p-strbegin]
140        
141         %%{
142                 alphtype unsigned short;
143                
144                 action sstart {strbegin = p;}
145                 action setheaderval {[hd setObject:send() forKey:str];}
146                 action savestr {str = send();}
147                 action csvlineend {[cur_array addObject:[send() stringByTrimmingCharactersInSet:wcs]];}
148                 action setupevents {
149                         cur_array=eventarr;
150                         eventformat = IsScriptASS(hd) ?
151                                 @"Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text":
152                                 @"Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text";
153                 }
154                                
155                 nl = ("\n" | "\r" | "\r\n");
156                 str = any*;
157                 ws = space | 0xa0;
158                 bom = 0xfeff;
159                
160                 hline = ((";" str) | (([^;] str) >sstart %savestr :> (":" ws* str >sstart %setheaderval)?))? :> nl;
161                
162                 header = "[Script Info]" nl hline*;
163                                
164                 format = "Format:" ws* %sstart str %savestr :> nl;
165                                
166                 action assformat {styleformat = @"Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding";}
167                 action ssaformat {styleformat = @"Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding";}
168
169                 stylename = ("[" [Vv] "4 Styles]") %ssaformat | ("[" [Vv] "4+ Styles]") %assformat;
170                
171                 sline = (("Style:" ws* %sstart str %csvlineend) | str) :> nl;
172                
173                 styles = stylename % {cur_array=stylearr;} nl :> (format %{styleformat=str;})? <: (sline*);
174                
175                 event_txt = (("Dialogue:" ws* %sstart str %csvlineend) | str);
176                 event = event_txt :> nl;
177                        
178                 lines = "[Events]" %setupevents nl :> (format %{eventformat=str;})? <: (event*);
179                
180                 main := bom? header styles lines?;
181         }%%
182                
183         %%write init;
184         %%write exec;
185         %%write eof;
186
187         *headers = hd;
188         if (styles) *styles = SplitByFormat(styleformat, stylearr);
189         if (subs) *subs = SplitByFormat(eventformat, eventarr);
190 }
191
192 %%machine SSAtag;
193 %%write data;
194
195 static NSMutableString *FilterSlashEscapes(NSMutableString *s)
196 {
197         [s replaceOccurrencesOfString:@"\\n" withString:@"\n" options:NSCaseInsensitiveSearch range:NSMakeRange(0, [s length])];
198         unichar nbsp = 0xA0;
199        
200         [s replaceOccurrencesOfString:@"\\h" withString:[NSString stringWithCharacters:&nbsp length:1] options:0 range: NSMakeRange(0,[s length])];
201         return s;
202 }
203
204 static int compare_layer(const void *a, const void *b)
205 {
206         const SubRenderDiv *divA = a, *divB = b;
207        
208         if (divA->layer < divB->layer) return -1;
209         else if (divA->layer > divB->layer) return 1;
210         return 0;
211 }
212
213 NSArray *SubParsePacket(NSString *packet, SubContext *context, SubRenderer *delegate, unichar *linebuf)
214 {
215         packet = STStandardizeStringNewlines(packet);
216         NSArray *lines = (context->scriptType == kSubTypeSRT) ? [NSArray arrayWithObject:[packet substringToIndex:[packet length]-1]] : [packet componentsSeparatedByString:@"\n"];
217         size_t line_count = [lines count];
218         NSMutableArray *divs = [NSMutableArray arrayWithCapacity:line_count];
219         int i;
220        
221         for (i = 0; i < line_count; i++) {
222                 NSString *inputText = [lines objectAtIndex:(context->collisions == kSubCollisionsReverse) ? (line_count - i - 1) : i];
223                 SubRenderDiv *div = [[[SubRenderDiv alloc] init] autorelease];
224                
225                 div->text = [[NSMutableString string] retain];
226                 div->spans = [[NSMutableArray array] retain];
227                
228                 if (context->scriptType == kSubTypeSRT) {
229                         div->styleLine = [context->defaultStyle retain];
230                         div->marginL = div->styleLine->marginL;
231                         div->marginR = div->styleLine->marginR;
232                         div->marginV = div->styleLine->marginV;
233                         div->layer = 0;
234                         div->wrapStyle = kSubLineWrapTopWider;
235                 } else {
236                         NSArray *fields = STSplitStringWithCount(inputText, @",", 9);
237                         if ([fields count] < 9) continue;
238                         div->layer = [[fields objectAtIndex:1] intValue];
239                         div->styleLine = [[context styleForName:[fields objectAtIndex:2]] retain];
240                         div->marginL = [[fields objectAtIndex:4] intValue];
241                         div->marginR = [[fields objectAtIndex:5] intValue];
242                         div->marginV = [[fields objectAtIndex:6] intValue];
243                         inputText = [fields objectAtIndex:8];
244                         if ([inputText length] == 0) continue;
245                        
246                         if (div->marginL == 0) div->marginL = div->styleLine->marginL;
247                         if (div->marginR == 0) div->marginR = div->styleLine->marginR;
248                         if (div->marginV == 0) div->marginV = div->styleLine->marginV;
249                        
250                         div->wrapStyle = context->wrapStyle;
251                 }
252                
253                 div->alignH = div->styleLine->alignH;
254                 div->alignV = div->styleLine->alignV;
255                
256                 size_t linelen = [inputText length];
257                 [inputText getCharacters:linebuf];
258                 linebuf[linelen] = 0;
259                
260 #undef send
261 #define send() [NSString stringWithCharacters:outputbegin length:p-outputbegin]
262 #define psend() [NSString stringWithCharacters:parambegin length:p-parambegin]
263 #define tag(tagt, p) [delegate spanChangedTag:tag_##tagt span:current_span div:div param:&(p)]
264                                
265                 {
266                         unichar *p = linebuf, *pe = linebuf + linelen, *outputbegin = p, *parambegin=p, *last_cmd_start=p;
267                         const unichar *pb = p;
268                         int cs = 0;
269                         SubRenderSpan *current_span = [SubRenderSpan startingSpanForDiv:div delegate:delegate];
270                         unsigned chars_deleted = 0; int intnum = 0; float floatnum = 0;
271                         NSString *strval=NULL;
272                         unsigned curX, curY;
273                         BOOL reached_end = NO, startNewLayout = NO;
274                        
275                         %%{
276                                 action bold {tag(b, intnum);}
277                                 action italic {tag(i, intnum);}
278                                 action underline {tag(u, intnum);}
279                                 action strikeout {tag(s, intnum);}
280                                 action outlinesize {tag(bord, floatnum);}
281                                 action shadowdist {tag(shad, floatnum);}
282                                 action bluredge {tag(be, intnum);}
283                                 action fontname {tag(fn, strval);}
284                                 action fontsize {tag(fs, floatnum);}
285                                 action scalex {tag(fscx, floatnum);}
286                                 action scaley {tag(fscy, floatnum);}
287                                 action tracking {tag(fsp, floatnum);}
288                                 action frz {tag(frz, floatnum);}
289                                 action frx {tag(frx, floatnum);}
290                                 action fry {tag(fry, floatnum);}
291                                 action primaryc {tag(1c, intnum);}
292                                 action secondaryc {tag(2c, intnum);}
293                                 action outlinec {tag(3c, intnum);}
294                                 action shadowc {tag(4c, intnum);}
295                                 action alpha {tag(alpha, intnum);}
296                                 action primarya {tag(1a, intnum);}
297                                 action secondarya {tag(2a, intnum);}
298                                 action outlinea {tag(3a, intnum);}
299                                 action shadowa {tag(4a, intnum);}
300                                 action stylerevert {tag(r, strval);}
301
302                                 action paramset {parambegin=p;}
303                                 action setintnum {intnum = [psend() intValue];}
304                                 action sethexnum {intnum = strtoul([psend() UTF8String], NULL, 16);}
305                                 action setfloatnum {floatnum = [psend() floatValue];}
306                                 action setstringval {strval = psend();}
307                                 action setxypos {curX=curY=-1; sscanf([psend() UTF8String], "(%d,%d)", &curX, &curY);}
308                                
309                                 action ssaalign {
310                                         if (outputbegin == pb) ParseASSAlignment(SSA2ASSAlignment(intnum), &div->alignH, &div->alignV);
311                                 }
312                                
313                                 action align {
314                                         if (outputbegin == pb) ParseASSAlignment(intnum, &div->alignH, &div->alignV);
315                                 }
316                                
317                                 action wrapstyle {
318                                         if (!startNewLayout) {
319                                                 startNewLayout = YES;
320                                                
321                                                 if ([div->text length] > 0) {[divs addObject:div]; div = [div nextDivWithDelegate:delegate];}
322                                         }
323                                        
324                                         div->wrapStyle = intnum;
325                                 }
326                                
327                                 action position {
328                                         if (!startNewLayout) {
329                                                 startNewLayout = YES;
330                                                
331                                                 if ([div->text length] > 0) {[divs addObject:div]; div = [div nextDivWithDelegate:delegate];}
332                                         }
333                                        
334                                         div->posX = curX;
335                                         div->posY = curY;
336                                         div->positioned = YES;
337                                 }
338
339                                 intnum = ("-"? [0-9]+) >paramset %setintnum;
340                                 flag = [01] >paramset %setintnum;
341                                 floatnum = ([0-9]+ ("." [0-9]*)?) >paramset %setfloatnum;
342                                 string = ([^\\}]*) >paramset %setstringval;
343                                 color = ("H"|"&"){,2} (xdigit+) >paramset %sethexnum "&"?;
344                                 parens = "(" [^)]* ")";
345                                 xypos = ("(" "-"? [0-9]+ "," "-"? [0-9]+ ")") >paramset %setxypos;
346                                
347                                 cmd = "\\" (
348                                                         "b" intnum %bold
349                                                         |"i" flag %italic
350                                                         |"u" flag %underline
351                                                         |"s" flag %strikeout
352                                                         |"bord" floatnum %outlinesize
353                                                         |"shad" floatnum %shadowdist
354                                                         |"be" flag %bluredge
355                                                         |"fn" string %fontname
356                                                         |"fs" floatnum %fontsize
357                                                         |"fscx" floatnum %scalex
358                                                         |"fscy" floatnum %scaley
359                                                         |"fsp" floatnum %tracking
360                                                         |("fr" "z"? floatnum %frz)
361                                                         |"frx" floatnum %frx
362                                                         |"fry" floatnum %fry
363                                                         |"fe" intnum
364                                                         |("1"? "c" color %primaryc)
365                                                         |"2c" color %secondaryc
366                                                         |"3c" color %outlinec
367                                                         |"4c" color %shadowc
368                                                         |"alpha" color %alpha
369                                                         |"1a" color %primarya
370                                                         |"2a" color %secondarya
371                                                         |"3a" color %outlinea
372                                                         |"4a" color %shadowa
373                                                         |"a" intnum %ssaalign
374                                                         |"an" intnum %align
375                                                         |([kK] [fo]? intnum)
376                                                         |"q" intnum %wrapstyle
377                                                         |"r" string %stylerevert
378                                                         |"pos" xypos %position
379                                                         |"t" parens
380                                                         |"org" parens
381                                                         |("fad" "e"? parens)
382                                                         |"clip" parens
383                                                         |"p" floatnum
384                                                         |"pbo" floatnum
385                                            );
386                                
387                                 cmd_list = "{" (cmd* | any*) :> "}";
388
389                                 action backslash_handler {
390                                         [div->text appendString:send()];                                       
391                                         unichar c = *(p+1), o=c;
392                                        
393                                         if (c) {
394                                                 switch (c) {
395                                                         case 'N': case 'n':
396                                                                 o = '\n';
397                                                                 break;
398                                                         case 'h':
399                                                                 o = 0xA0; //non-breaking space
400                                                                 break;
401                                                         default:
402                                                                 o = c;
403                                                 }
404                                         }
405                                        
406                                         [div->text appendFormat:@"%C",o];
407                                        
408                                         chars_deleted++;
409                                        
410                                         outputbegin = p+2;
411                                 }
412                                
413                                 action enter_tag {                                     
414                                         if (p > outputbegin) [div->text appendString:send()];
415                                        
416                                         if (p != pb) {
417                                                 [div->spans addObject:current_span];
418                                                 current_span = [current_span cloneWithDelegate:delegate];
419                                         }
420                                        
421                                         last_cmd_start = p;
422                                         if (p == pe) reached_end = YES;
423                                 }
424                                
425                                 action exit_tag {                       
426                                         p++;
427                                         chars_deleted += (p - last_cmd_start);
428                                        
429                                         current_span->offset = (p - pb) - chars_deleted;
430                                         outputbegin = p;
431                                        
432                                         if (startNewLayout) {
433                                                 startNewLayout = NO;
434                                                 chars_deleted = outputbegin - pb;
435                                         }
436                                        
437                                         p--;
438                                 }
439                                                                
440                                 special = ("\\" any) >backslash_handler | cmd_list >enter_tag @exit_tag;
441                                 sub_text_char = [^\\{];
442                                 sub_text = sub_text_char*;
443                                
444                                 main := ((sub_text | special)* "\\"?) %/enter_tag;
445                         }%%
446                                
447                         %%write init;
448                         %%write exec;
449                         %%write eof;
450
451                         if (!reached_end) Codecprintf(NULL, "parse error: %s\n", [inputText UTF8String]);
452                         if (linebuf[linelen-1] == '\\') [div->text appendString:@"\\"];
453                         [divs addObject:div];
454                 }
455                
456         }
457        
458         STSortMutableArrayStably(divs, compare_layer);
459         return divs;
460 }
Note: See TracBrowser for help on using the browser.