source: trunk/Subtitles/SubParsing.m.rl @ 1195

Revision 1195, 14.8 KB checked in by astrange, 4 years ago (diff)

SSA: Add missing VSFilter 2.39 tags to the parser.

Fixes some wrong font sizes in [BSS]_Darker_Than_Black_-_Gemini_of_the_Meteor_-_04_[8767FC3C].mkv.
These were caused by the parser skipping command blocks with unknown tags.
Maybe it just shouldn't do that?

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