Example #1
0
// parseStyle parse an SSA/ASS Subtitle Dialog.
func parseDialog(key, value string) *Dialog {
	// TODO ?: use sprintf
	d := strings.SplitN(value, ",", 10)
	return &Dialog{
		Layer:     utils.Str2int(d[0]),
		StartTime: d[1],
		EndTime:   d[2],
		StyleName: d[3],
		Actor:     d[4],
		Effect:    d[8],
		Text:      strings.TrimSpace(d[9]),
		Comment:   key == "comment",
	}
}
Example #2
0
// parseStyle parse an SSA/ASS Subtitle Style.
func parseStyle(value string) *Style {
	// TODO ?: use sprintf
	sty := strings.SplitN(value, ",", 23)

	c1, a1 := color.SSALtoHEXAlpha(sty[3])
	c2, a2 := color.SSALtoHEXAlpha(sty[4])
	c3, a3 := color.SSALtoHEXAlpha(sty[5])
	c4, a4 := color.SSALtoHEXAlpha(sty[6])

	return &Style{
		Name:     sty[0],
		FontName: sty[1],
		FontSize: utils.Str2int(sty[2]),
		Color: [4]string{
			c1,  // Primary
			c2,  // Secondary
			c3,  // Bord
			c4}, // Shadow
		Alpha: [4]uint8{
			uint8(a1),  // Primary
			uint8(a2),  // Secondary
			uint8(a3),  // Bord
			uint8(a4)}, // Shadow
		Bold:      utils.Str2bool(sty[7]),
		Italic:    utils.Str2bool(sty[8]),
		Underline: utils.Str2bool(sty[9]),
		StrikeOut: utils.Str2bool(sty[10]),
		Scale: [2]float64{
			utils.Str2float(sty[11]), // X
			utils.Str2float(sty[12]), // Y
		},
		Spacing:   utils.Str2float(sty[13]),
		Angle:     utils.Str2int(sty[14]),
		OpaqueBox: utils.Obox2bool(sty[15]),
		Bord:      utils.Str2float(sty[16]),
		Shadow:    utils.Str2float(sty[17]),
		Alignment: utils.Str2int(sty[18]),
		Margin: [3]int{
			utils.Str2int(sty[19]), // L
			utils.Str2int(sty[20]), // R
			utils.Str2int(sty[21]), // V
		},
	}
}
Example #3
0
func (d *Line) Syls() (syls []*Syl) {

	lineStart := d.StartTime
	lineEnd := d.EndTime
	end := 0
	fontFace := d.fontFace

	spaceWidth, _ := utils.MeasureString(fontFace, " ")
	spaceWidth *= d.Style.Scale[0] / 100.0

	curX := d.Left
	maxWidth := 0.0
	sumHeight := 0.0
	resx, resy := float64(d.resolution[0]), float64(d.resolution[1])

	for i, dlg := range d.syls {
		duration, inline, text := dlg[1], dlg[2], dlg[3]
		dur := utils.Str2int(duration) * 10 // cs to ms

		// Absolute times
		start := lineStart
		lineStart += dur
		if i == d.SylN-1 {
			// Ensure that the end time and the width of the last syl
			// is the same that the end time and width of the line
			end = lineEnd
		} else {
			end = lineStart
		}

		strippedText, preSpace, postSpace := utils.TrimSpaceCount(text)

		width, height := utils.MeasureString(fontFace, strippedText)
		width *= d.Style.Scale[0] / 100.0
		height *= d.Height

		middleheight := float64(height) / 2.0
		middlewidth := float64(width) / 2.0
		align := d.Style.Alignment

		curX += float64(preSpace) * spaceWidth
		sleft := float64(curX)
		scenter := sleft + middlewidth
		sright := sleft + width
		x := 0.0
		y := 0.0
		stop := 0.0
		smid := 0.0
		sbot := 0.0

		maxWidth = math.Max(maxWidth, width)
		sumHeight += height

		// line x
		if align > 6 || align < 4 {
			switch align {
			case 1, 7: // left
				x = sleft
			case 2, 8: // center
				x = scenter
			case 3, 9: // right
				x = sright
			}
			curX += width + float64(postSpace)*spaceWidth

		} else { // vertical alignment
			xFix := (maxWidth - width) / 2.0
			switch align {
			case 4: // left
				sleft = d.Left + xFix
				scenter = sleft + middlewidth
				sright = sleft + width
				x = sleft
			case 5: // center
				sleft = resx/2.0 - middlewidth
				scenter = sleft + middlewidth
				sright = sleft + width
				x = scenter
			case 6: // right
				sleft = d.Right - width - xFix
				scenter = sleft + middlewidth
				sright = sleft + width
				x = sright
			}
		}

		curY := resy/2.0 - sumHeight/2.0 + float64(d.Style.Spacing)

		// line y
		if align > 6 || align < 4 {
			stop = d.Top
			smid = d.Middle
			sbot = d.Bottom
			y = d.Y
		} else { // vertical alignment
			stop = curY
			smid = stop + middleheight
			sbot = stop + height
			y = smid
			curY += height
		}

		if text != "" {
			s := &Syl{
				Layer:     d.Layer,
				Style:     d.Style,
				StyleName: d.StyleName,
				Actor:     d.Actor,
				Effect:    d.Effect,
				Tags:      d.Tags,
				Comment:   d.Comment,
				// Syl
				StartTime: start,
				EndTime:   end,
				Duration:  dur,
				MidTime:   end - start,
				Text:      strippedText,
				Inline:    inline,
				Width:     float64(width),
				Height:    float64(height),
				Size:      [2]float64{float64(width), float64(height)},
				X:         float64(x),
				Y:         float64(y),
				Top:       float64(stop),
				Middle:    float64(smid),
				Bottom:    float64(sbot),
				Left:      float64(sleft),
				Center:    float64(scenter),
				Right:     float64(sright),
			}

			syls = append(syls, s)
		}
	}
	return syls
}
Example #4
0
func SSAtoSplit(t string) (h, m, s, cs int) {
	//H:MM:SS.CC (H=Hour, M=Minute, S=Second, C=centisecond)
	tm := ReSSAfmt.FindStringSubmatch(t)
	return utils.Str2int(tm[1]), utils.Str2int(tm[2]),
		utils.Str2int(tm[3]), utils.Str2int(tm[4])
}
Example #5
0
// Read parse and read an SSA/ASS Subtitle Script.
func Read(fn string) *Script {

	s := &Script{}
	f, err := os.Open(fn)
	if err != nil {
		panic(fmt.Errorf("reader: failed opening subtitle file: %s", err))
	}
	defer f.Close()

	s.Style = make(map[string]*Style)
	s.StyleUsed = make(map[string]*Style)
	var playresx, playresy int
	var videozoom, videoar float64

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if line == "" || strings.HasPrefix(line, ";") ||
			strings.HasPrefix(line, "!:") ||
			strings.HasPrefix(line, "Format:") {
			continue
		}

		keyvalue := strings.SplitN(line, ":", 2)
		if len(keyvalue) != 2 {
			continue
		}
		key, value := keyvalue[0], keyvalue[1]
		key = strings.TrimSpace(key)
		key = strings.ToLower(key)
		key = strings.Replace(key, " ", "_", -1)
		value = strings.TrimSpace(value)

		switch key {
		case "dialogue", "comment":
			s.Dialog = append(s.Dialog, parseDialog(key, value))
		case "style":
			style := parseStyle(value)
			s.Style[style.Name] = style
		case "playresx":
			playresx = utils.Str2int(value)
		case "playresy":
			playresy = utils.Str2int(value)
		case "audio_uri", "audio_file":
			s.Audio = value
		case "video_file":
			s.VideoPath = value
		case "video_zoom_percent":
			videozoom = utils.Str2float(value)
		case "video_zoom":
			// Use "video_zoom_percent" key if present
			// else use "video_zoom" key
			if videozoom == 0 {
				zoom := strings.Replace(value, "%", "", -1)
				videozoom = utils.Str2float(zoom) / 100.0
			}
		case "video_aspect_ratio", "video_ar_value",
			"aegisub_video_aspect_ratio":
			ar := strings.Replace(value, "c", "", -1)
			numden := strings.SplitN(ar, ":", 2)
			if len(numden) == 2 {
				num, den := utils.Str2float(numden[0]),
					utils.Str2float(numden[1])
				videoar = num / den
			} else {
				videoar = utils.Str2float(ar)
			}
			s.VideoAR = videoar
		case "video_position":
			s.VideoPosition = utils.Str2int(value)
		case "title":
			s.MetaTitle = value
		case "original_script":
			s.MetaOriginalScript = value
		case "translation":
			s.MetaTranslation = value
		case "timing":
			s.MetaTiming = value
		default:
			continue
		}
	}

	s.Resolution = [2]int{playresx, playresy}
	s.VideoZoom = videozoom
	s.MetaFilename = fn

	// Get only the styles used in dialogs
	for _, d := range s.Dialog {
		sty, ok := s.Style[d.StyleName]
		if !ok {
			sty = s.Style["Default"]
		}
		d.Style = sty
		if !d.Comment {
			s.StyleUsed[d.StyleName] = sty
		}
	}

	return s
}