source: trunk/SSATagParsing.m.rl @ 358

Revision 358, 17.5 KB checked in by astrange, 8 years ago (diff)

Justify text more correctly for positioned subtitles. Refs #108

Line 
1/*
2 *  SSARenderCodec.m
3 *  Copyright (c) 2007 Perian Project
4 *
5 *  This program is free software; you can redistribute it and/or
6 *  modify it under the terms of the GNU Lesser General Public
7 *  License as published by the Free Software Foundation;
8 *  version 2.1 of the License.
9 *
10 *  This program is distributed in the hope that it will be useful,
11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 *  Lesser General Public License for more details.
14 *
15 *  You should have received a copy of the GNU Lesser General Public
16 *  License along with this program; if not, write to the Free Software
17 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18 *
19 */
20
21#import "SSATagParsing.h"
22#import "Categories.h"
23
24@implementation SSAStyleSpan
25-(void)dealloc
26{
27        ATSUDisposeStyle(astyle);
28        [super dealloc];
29}
30@end
31
32@implementation SSARenderEntity
33-(void)dealloc
34{
35        int i;
36        for (i=0; i < style_count; i++) [styles[i] release];
37        if (disposelayout) ATSUDisposeTextLayout(layout);
38        free(text);
39        free(styles);
40        [nstext release];
41        [super dealloc];
42}
43
44-(void)increasestyles
45{
46        style_count++;
47        styles = realloc(styles, sizeof(SSAStyleSpan*[style_count]));
48}
49@end
50
51%% machine SSAtag;
52%% write data;
53
54void SetATSUStyleFlag(ATSUStyle style, ATSUAttributeTag t, Boolean v)
55{
56        ATSUAttributeTag tags[] = {t};
57        ByteCount                sizes[] = {sizeof(v)};
58        ATSUAttributeValuePtr vals[] = {&v};
59       
60        ATSUSetAttributes(style,1,tags,sizes,vals);
61}
62
63void SetATSUStyleOther(ATSUStyle style, ATSUAttributeTag t, ByteCount s, const ATSUAttributeValuePtr v)
64{
65        ATSUAttributeTag tags[] = {t};
66        ByteCount                sizes[] = {s};
67        ATSUAttributeValuePtr vals[] = {v};
68       
69        ATSUSetAttributes(style,1,tags,sizes,vals);
70}
71
72void SetATSULayoutOther(ATSUTextLayout l, ATSUAttributeTag t, ByteCount s, const ATSUAttributeValuePtr v)
73{
74        ATSUAttributeTag tags[] = {t};
75        ByteCount                sizes[] = {s};
76        ATSUAttributeValuePtr vals[] = {v};
77       
78        ATSUSetLayoutControls(l,1,tags,sizes,vals);
79}
80
81static ATSURGBAlphaColor ParseColorTag(unsigned long c, float a)
82{
83        unsigned char r,g,b;
84        r = c & 0xff;
85        g = (c >> 8) & 0xff;
86        b = (c >> 16) & 0xff;
87       
88        return (ATSURGBAlphaColor){r/255.,g/255.,b/255.,a};
89}
90
91static void PruneEmptyStyleSpans(SSARenderEntity *re)
92{
93        SSAStyleSpan *styles_new[re->style_count];
94        size_t style_count_new = 0;
95        int i;
96       
97        if (re->style_count <= 1) return;
98       
99        for (i = 0; i < re->style_count; i++) {
100                if (re->styles[i]->range.length == 0) {
101                        [re->styles[i] release];
102                } else {
103                        styles_new[style_count_new++] = re->styles[i];
104                }
105        }
106       
107        if (style_count_new != re->style_count) {
108                re->styles = realloc(re->styles, sizeof(SSAStyleSpan*) * style_count_new);
109                memcpy(re->styles, styles_new, sizeof(SSAStyleSpan*) * style_count_new);
110               
111                re->style_count = style_count_new;
112        }
113}
114
115static void PruneIdenticalStyleSpans(SSARenderEntity *re)
116{
117        SSAStyleSpan *styles_new[re->style_count];
118       
119        if (re->multipleruns || re->style_count <= 1) return;
120
121        size_t style_count_new = 1, remaining = re->style_count-1;
122        int i=1;
123       
124       
125        styles_new[0] = re->styles[0];
126       
127        while (remaining) {
128                ATSUStyleComparison asc;
129                ATSUCompareStyles(styles_new[style_count_new-1]->astyle,re->styles[i]->astyle,&asc);
130               
131                if (asc == kATSUStyleEquals) {
132                        styles_new[style_count_new-1]->range.length += re->styles[i]->range.length;
133                        [re->styles[i] release];
134                } else {
135                        styles_new[style_count_new++] = re->styles[i];
136                }
137               
138                i++;
139                remaining--;
140        }
141       
142        if (style_count_new != re->style_count) {
143                re->styles = realloc(re->styles, sizeof(SSAStyleSpan*) * style_count_new);
144                memcpy(re->styles, styles_new, sizeof(SSAStyleSpan*) * style_count_new);
145               
146                re->style_count = style_count_new;
147        }
148}
149
150static void UpdateAlignment(int inum, int cur_posx, int *valign, int *halign, ATSUTextLayout cur_layout)
151{
152        int cur_halign, cur_valign;
153       
154        switch (inum)
155        {case 1: case 4: case 7: cur_halign = S_LeftAlign; break;
156        case 2: case 5: case 8: default: cur_halign = S_CenterAlign; break;
157        case 3: case 6: case 9: cur_halign = S_RightAlign;}
158       
159        switch (inum)
160        {case 1: case 2: case 3: default: cur_valign = S_BottomAlign; break;
161        case 4: case 5: case 6: cur_valign = S_MiddleAlign; break;
162        case 7: case 8: case 9: cur_valign = S_TopAlign;}
163       
164        *halign = cur_halign;
165        *valign = cur_valign;
166        Fract alignment;
167       
168        if (cur_posx != -1) {
169                switch(cur_halign) {
170                        case S_LeftAlign:
171                                alignment = FloatToFract(0.);
172                                break;
173                        case S_CenterAlign: default:
174                                alignment = kATSUCenterAlignment; 
175                                break;
176                        case S_RightAlign:
177                                alignment = FloatToFract(1.);
178                }
179                SetATSULayoutOther(cur_layout, kATSULineFlushFactorTag, sizeof(Fract), &alignment);
180        }
181}
182
183NSArray *ParseSubPacket(NSString *str, SSADocument *ssa, Boolean plaintext)
184{
185        NSArray *linea;
186        int i, j, pcount; unsigned len;
187        NSMutableArray *rentities = [NSMutableArray array];
188       
189        if (plaintext) {
190                linea = [NSArray arrayWithObject:str];
191                pcount = 1;
192        } else {
193                linea =  [str componentsSeparatedByString:@"\n"];
194                pcount = [linea count];
195        }
196       
197        for (i = 0; i < pcount; i++) {
198                SSARenderEntity *re = [[[SSARenderEntity alloc] init] autorelease];
199                BOOL ldirty = NO;
200               
201                if (plaintext) {
202                        re->style = ssa->defaultstyle;
203                        re->layout = re->style->layout;
204                        re->layer = 0;
205                        re->marginl = re->style->marginl;
206                        re->marginr = re->style->marginl;
207                        re->marginv = re->style->marginl;
208                        re->usablewidth = re->style->usablewidth;
209                        re->halign = 1;
210                        re->valign = 0;
211                        re->nstext = [linea objectAtIndex:i];
212                        re->styles = NULL;
213                        re->disposelayout = NO;
214                } else {
215                        NSArray *ar = [[linea objectAtIndex:i] componentsSeparatedByString:@"," count:9];
216
217                        if ([ar count] < 9) continue;
218                       
219                        re->layer = [[ar objectAtIndex:1] intValue];
220                        NSString *sn = [ar objectAtIndex:2];
221                       
222                        re->style = ssa->defaultstyle;
223                       
224                        for (j=0; j < ssa->stylecount; j++) {
225                                if ([sn isEqualToString:ssa->styles[j].name])  {
226                                        re->style = &ssa->styles[j];
227                                        break;
228                                }
229                        }
230
231                        re->marginl = [[ar objectAtIndex:5] intValue];
232                        re->marginr = [[ar objectAtIndex:6] intValue];
233                        re->marginv = [[ar objectAtIndex:7] intValue];
234                       
235                        if (re->marginl == 0) re->marginl = re->style->marginl; else ldirty = TRUE;
236                        if (re->marginr == 0) re->marginr = re->style->marginr; else ldirty = TRUE;
237                        if (re->marginv == 0) re->marginv = re->style->marginv; else ldirty = TRUE;
238                       
239                        if (ldirty) {
240                                ATSUTextMeasurement width;
241                                ATSUAttributeTag tag[] = {kATSULineWidthTag};
242                                ByteCount                size[] = {sizeof(ATSUTextMeasurement)};
243                                ATSUAttributeValuePtr val[] = {&width};
244                               
245                                re->usablewidth = ssa->resX - re->marginl - re->marginr;
246                                ATSUCreateAndCopyTextLayout(re->style->layout,&re->layout);
247                                width = IntToFixed(re->usablewidth);
248                               
249                                ATSUSetLayoutControls(re->layout,1,tag,size,val);
250                                re->disposelayout = YES;
251                        } else {
252                                re->usablewidth = re->style->usablewidth;
253                                re->layout = re->style->layout;
254                                re->disposelayout = NO;
255                        }
256                       
257                        re->nstext = [ar objectAtIndex:8];
258                }
259                len = [re->nstext length];
260                re->text = malloc(sizeof(unichar[len]));
261               
262                [re->nstext getCharacters:re->text];
263                re->style_count = 0;
264                re->posx = re->posy = -1;
265                re->halign = re->style->halign;
266                re->valign = re->style->valign;
267                re->multipleruns=NO;
268                re->is_shape=NO;
269               
270#define end_re \
271                cur_range = (NSRange){strbegin - pb, p - strbegin};\
272                cur_range.location -= outputoffset;\
273                cur_range.length -= lengthreduce;\
274                [re increasestyles];\
275                re->styles[re->style_count-1] = [[SSAStyleSpan alloc] init];\
276                re->styles[re->style_count-1]->outline = cur_outline;\
277                re->styles[re->style_count-1]->shadow = cur_shadow;\
278                re->styles[re->style_count-1]->astyle = cur_style;\
279                re->styles[re->style_count-1]->range = cur_range;\
280                re->styles[re->style_count-1]->outlineblur = cur_be;\
281                re->styles[re->style_count-1]->color = cur_color;\
282                parsetmp = [NSString stringWithCharacters:skipbegin length:p-skipbegin];\
283                [output appendString:parsetmp]; \
284                re->nstext = dtmp = output;\
285                re->text = malloc(sizeof(unichar[[re->nstext length]]));\
286                [re->nstext getCharacters:re->text];\
287                [re->nstext retain];
288               
289                {
290                        NSRange cur_range = {0,0};
291                        ATSUStyle cur_style;
292                        ATSUTextLayout cur_layout;
293                        NSMutableString *output = [NSMutableString string], *dtmp;
294                        unichar *p = re->text, *pe = &re->text[len], *numbegin = p, *strbegin = p, *skipbegin = p, *intbegin = p, *pb = p, *posbegin=p, *strparambegin=p;
295                        float num, cur_outline = re->style->outline, cur_shadow = re->style->shadow, cur_scalex = re->style->scalex, cur_scaley = re->style->scaley;
296                        NSString *parsetmp;
297                        int cs, cur_valign=re->valign, cur_halign=re->halign, cur_posx=-1, cur_posy=-1;
298                        ssacolors cur_color = re->style->color;
299                        CGAffineTransform matrix;
300                       
301                        unsigned long inum=0;
302                        unsigned outputoffset=0, lengthreduce=0;
303                        BOOL flag=0, newLayout=FALSE, cur_be = FALSE;
304                        Fixed fixv;
305                       
306                        ATSUCreateAndCopyStyle(re->style->atsustyle,&cur_style);
307                       
308                        %%{
309                                alphtype unsigned short;
310                               
311                                action bold {SetATSUStyleFlag(cur_style, kATSUQDBoldfaceTag, flag);}
312                               
313                                action italic {SetATSUStyleFlag(cur_style, kATSUQDItalicTag, flag);}
314                               
315                                action uline {SetATSUStyleFlag(cur_style, kATSUQDUnderlineTag, flag);}
316                               
317                                action strike {SetATSUStyleFlag(cur_style, kATSUStyleStrikeThroughTag, flag);}
318                               
319                                action bordersize {cur_outline = num; re->multipleruns = YES;}
320                               
321                                action shadowsize {cur_shadow = num; re->multipleruns = YES;}
322                               
323                                action bluredge {cur_be = flag;}
324                               
325                                action frot {
326                                        if (!newLayout) {
327                                                newLayout = TRUE;
328                                                ATSUCreateAndCopyTextLayout(re->layout,&cur_layout);
329                                        }
330                                        fixv = FloatToFixed(num);
331                                       
332                                        SetATSULayoutOther(cur_layout, kATSULineRotationTag, sizeof(Fixed), &fixv);
333                                }
334                               
335                                action fsize {
336                                        num *= (72./96.); // scale from Windows 96dpi
337                                        fixv = FloatToFixed(num);
338                                        SetATSUStyleOther(cur_style, kATSUSizeTag, sizeof(fixv), &fixv);
339                                }
340                               
341                                action ftrack {
342                                        fixv = FloatToFixed(num);
343                                        SetATSUStyleOther(cur_style, kATSUTrackingTag, sizeof(fixv), &fixv);
344                                }
345                               
346                                action fscalex {
347                                        cur_scalex = num;
348                                        matrix = CGAffineTransformMakeScale(cur_scalex/100.,cur_scaley/100.);           
349                                        SetATSUStyleOther(cur_style, kATSUFontMatrixTag, sizeof(matrix), &matrix);
350                                }
351                               
352                                action fscaley {
353                                        cur_scaley = num;
354                                        matrix = CGAffineTransformMakeScale(cur_scalex/100.,cur_scaley/100.);   
355                                        SetATSUStyleOther(cur_style, kATSUFontMatrixTag, sizeof(matrix), &matrix);
356                                }
357                               
358                                action strp_begin {
359                                        strparambegin = p;
360                                }
361                               
362                                action fontname {
363                                        ATSUFontID      font;
364                                        font = FMGetFontFromATSFontRef(ATSFontFindFromName((CFStringRef)[NSString stringWithCharacters:strparambegin length:p-strparambegin],kATSOptionFlagsDefault));
365                                        SetATSUStyleOther(cur_style, kATSUFontTag, sizeof(ATSUFontID), &font);
366                                }
367                               
368                                action alignment {
369                                        if (!newLayout) {
370                                                newLayout = TRUE;
371                                                ATSUCreateAndCopyTextLayout(re->layout,&cur_layout);
372                                        }
373                                       
374                                        UpdateAlignment(inum, cur_posx, &cur_valign, &cur_halign, cur_layout);
375                                }
376
377                                action ssa_alignment {
378                                        if (!newLayout) {
379                                                newLayout = TRUE;
380                                                ATSUCreateAndCopyTextLayout(re->layout,&cur_layout);
381                                        }
382                                       
383                                        UpdateAlignment(SSA2ASSAlignment(inum), cur_posx, &cur_valign, &cur_halign, cur_layout);
384                                }
385                               
386                                action pos_begin {
387                                        posbegin=p;
388                                }
389                               
390                                action pos_end {
391                                        if (!newLayout) {
392                                                newLayout = TRUE;
393                                                ATSUCreateAndCopyTextLayout(re->layout,&cur_layout);
394                                        }
395                                        NSArray *coo = [[NSString stringWithCharacters:posbegin length:p-posbegin] componentsSeparatedByString:@","];
396                                        cur_posx = [[coo objectAtIndex:0] intValue];
397                                        cur_posy = [[coo objectAtIndex:1] intValue];
398                                }
399                               
400                                action primarycolor {
401                                        cur_color.primary = ParseColorTag(inum,cur_color.primary.alpha);
402                                        re->multipleruns = YES;
403                                }
404                               
405                                action secondarycolor {
406                                        cur_color.secondary = ParseColorTag(inum,cur_color.secondary.alpha);
407                                        re->multipleruns = YES;
408                                }
409                               
410                                action outlinecolor {
411                                        cur_color.outline = ParseColorTag(inum,cur_color.outline.alpha);
412                                        re->multipleruns = YES;
413                                }
414                               
415                                action shadowcolor {
416                                        cur_color.shadow = ParseColorTag(inum,cur_color.shadow.alpha);
417                                        re->multipleruns = YES;
418                                }
419                               
420                                action primaryalpha {
421                                        cur_color.primary.alpha = (255 - inum) / 255.f;
422                                        re->multipleruns = YES;
423                                }
424                               
425                                action secondaryalpha {
426                                        cur_color.secondary.alpha = (255 - inum) / 255.f;
427                                        re->multipleruns = YES;
428                                }
429                               
430                                action outlinealpha {
431                                        cur_color.outline.alpha = (255 - inum) / 255.f;
432                                        re->multipleruns = YES;
433                                }
434                               
435                                action shadowalpha {
436                                        cur_color.shadow.alpha = (255 - inum) / 255.f;
437                                        re->multipleruns = YES;
438                                }
439                               
440                                action nl_handler {
441                                        parsetmp = [NSString stringWithCharacters:skipbegin length:(p-2)-skipbegin];
442                                        [output appendString:parsetmp];
443                                       
444                                        skipbegin = p;
445
446                                        [output appendString:@"\n"];
447                                       
448                                        lengthreduce++;
449                                }
450                               
451                                action enter_tag {
452                                        parsetmp = [NSString stringWithCharacters:skipbegin length:p-skipbegin];
453                                        [output appendString:parsetmp];
454                                       
455                                        outputoffset += lengthreduce;
456                                        lengthreduce = 0;
457                                       
458                                        skipbegin = p;
459                                       
460                                        cur_range = (NSRange){strbegin - pb, p - strbegin};
461                                        cur_range.location -= outputoffset;
462                                        cur_range.length -= lengthreduce;
463                                       
464                                        [re increasestyles];
465                                        re->styles[re->style_count-1] = [[SSAStyleSpan alloc] init];
466                                        re->styles[re->style_count-1]->outline = cur_outline;
467                                        re->styles[re->style_count-1]->shadow = cur_shadow;
468                                        re->styles[re->style_count-1]->astyle = cur_style;
469                                        re->styles[re->style_count-1]->range = cur_range;
470                                        re->styles[re->style_count-1]->outlineblur = cur_be;
471                                        re->styles[re->style_count-1]->color = cur_color;
472                                        ATSUCreateAndCopyStyle(cur_style,&cur_style);
473                                }
474                               
475                                action exit_tag {
476                                        outputoffset += p - skipbegin;
477                                        skipbegin = p;
478                                        strbegin = p;
479
480                                        if (newLayout) {
481                                                newLayout = FALSE;
482                                                end_re;
483                                               
484                                                if ([re->nstext length] != 0) [rentities addObject:re];
485                                               
486                                                SSARenderEntity *nre = [[SSARenderEntity alloc] init];
487                                                nre->layer = re->layer;
488                                                nre->style = re->style;
489                                                nre->marginl = re->marginl; nre->marginr = re->marginr; nre->marginv = re->marginv; nre->usablewidth = re->usablewidth;
490                                                nre->posx = cur_posx; nre->posy = cur_posy; nre->halign = cur_halign; nre->valign = cur_valign;
491                                                nre->nstext = nil;
492                                                nre->text = nil;
493                                                nre->layout = cur_layout;
494                                                nre->styles = malloc(0);
495                                                nre->style_count = 0;
496                                                nre->disposelayout = YES;
497                                                nre->multipleruns = NO;
498                                                nre->is_shape = re->is_shape;
499                                                re = nre;
500                                        }
501                                }
502                               
503                                action color_end {
504                                        NSString *hexn = [NSString stringWithCharacters:intbegin length:p-intbegin];
505                                        inum = strtoul([hexn UTF8String], NULL, 16);
506                                }
507                               
508                                action styleset {
509                                        if (!newLayout) {
510                                                newLayout = TRUE;
511                                                ATSUCreateAndCopyTextLayout(re->layout,&cur_layout);
512                                        }
513                                        re->multipleruns = YES;
514                                        ssastyleline *the_style = re->style;
515                                        NSString *searchsn = [NSString stringWithCharacters:strparambegin length:p-strparambegin];
516                                       
517                                        if ([searchsn length] > 0) {
518                                                for (j=0; j < ssa->stylecount; j++) {
519                                                        if ([searchsn isEqualToString:ssa->styles[j].name])  {
520                                                                the_style = &ssa->styles[j];
521                                                                break;
522                                                        }
523                                                }
524                                        }
525                                       
526                                        ATSUCopyAttributes(the_style->atsustyle,cur_style);
527                                        cur_color = the_style->color;
528                                        cur_outline = the_style->outline;
529                                        cur_shadow = the_style->shadow;
530                                        cur_posx = cur_posy = -1;
531                                }
532                               
533                                action skip_t_tag {
534                                        while (p != pe && *p != ')' && *p != '}') p++;
535                                        p++;
536                                }
537                               
538                                action draw_mode {
539                                        re->is_shape = inum != 0;
540                                }
541                               
542                                flag = ([01] % {unichar fl = *(p-1); if (fl == '0' || fl == '1') flag = fl - '0';})? > {flag = 0;};
543                                num_ = "-"? digit+ ('.' digit*)?;
544                                num = num_ > {numbegin = p;} % {num = [[NSString stringWithCharacters:numbegin length:p-numbegin] doubleValue];};
545                               
546                                intnum = "-"? (digit+) > {intbegin = p;} % {inum = [[NSString stringWithCharacters:intbegin length:p-intbegin] intValue];};
547                               
548                                color = ("H"|"&"){,2} (xdigit+) > {intbegin = p;} % color_end "&";
549
550                                cmd_specific = (("pos(" [^)]+ > pos_begin ")" % pos_end)
551                                                                |"bord" num %bordersize
552                                                                |"b" flag %bold
553                                                                |"be" flag %bluredge
554                                                                |"i" flag %italic
555                                                                |"u" flag %uline
556                                                                |"s" flag %strike
557                                                                |"fs" num %fsize
558                                                                |"fsp" num %ftrack
559                                                                |"fscx" num %fscalex
560                                                                |"fscy" num %fscaley
561                                                                |("fr" "z"? num %frot)
562                                                                |("fn" [^\\}]* > strp_begin %fontname)
563                                                                |"shad" num %shadowsize
564                                                                |"a" intnum %ssa_alignment
565                                                                |"an" intnum %alignment
566                                                                |"c" color %primarycolor
567                                                                |"1c" color %primarycolor
568                                                                |"2c" color %secondarycolor
569                                                                |"3c" color %outlinecolor
570                                                                |"4c" color %shadowcolor
571                                                                |"1a" color %primaryalpha
572                                                                |"2a" color %secondaryalpha
573                                                                |"3a" color %outlinealpha
574                                                                |"4a" color %shadowalpha
575                                                                |("r" [^\\}]* > strp_begin %styleset)
576                                                                |("fe"|"k"|"kf"|"K"|"ko"|"q"|"fr"|"fad"|"move"|"clip"|"o") [^\\}]*
577                                                                |"p" num %draw_mode
578                                                                #|"t" [^)}]* # enabling this crashes ragel
579                                                                |"t(" % skip_t_tag
580                                                                );
581                               
582                                cmd = "\\"  cmd_specific;
583                               
584                                tag = "{" ((cmd*) | ([^\\}]*)) "}";
585                               
586                                nl = "\\" [Nn];
587                               
588                                special = nl % nl_handler |
589                                                 (tag > enter_tag % exit_tag);
590                                                               
591                                text = any*;
592                                main := (text :> special?)*;
593                        }%%
594                               
595                        %%write init;
596                        %%write exec;
597                        %%write eof;
598                       
599                        if (pb[len-1] == '}') skipbegin = p; // make up for how exit_tag isn't called if the } is the last char in the line
600
601                        end_re;
602
603                        PruneEmptyStyleSpans(re);
604                        PruneIdenticalStyleSpans(re);
605
606                        free(pb);
607                        if ([re->nstext length] != 0) [rentities addObject:re];
608                }
609               
610        }
611       
612        return rentities;
613}
Note: See TracBrowser for help on using the repository browser.