Correct paths
parent
7bf1944b6d
commit
3509ac11c0
@ -1,20 +0,0 @@
|
||||
INT. HOUSE - DAY
|
||||
|
||||
MARY
|
||||
I can't believe how easy it is to write in Fountain.
|
||||
|
||||
TOM
|
||||
(typing)
|
||||
Look! I just made a parenthetical!
|
||||
|
||||
SOMETHING HAPPENS!
|
||||
|
||||
(what? I don't know...)
|
||||
|
||||
EXT. GARDEN
|
||||
|
||||
TOM
|
||||
What am I doing here now?
|
||||
To be honest, I have absolutely no idea!
|
||||
|
||||
And that means really no idea!
|
@ -1,202 +0,0 @@
|
||||
// Fountain is a Markdown-like language for screenplays and the main inspiration for this tool.
|
||||
// Read more at fountain.io
|
||||
package fountain
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"github.com/lapingvino/lexington/lex"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Scene contains all the prefixes the scene detection looks for.
|
||||
// This can be changed with the toml configuration in the rules package.
|
||||
var Scene = []string{"INT", "EXT", "EST", "INT./EXT", "INT/EXT", "EXT/INT", "EXT./INT", "I/E"}
|
||||
|
||||
func last(out *lex.Screenplay, i int) *lex.Line {
|
||||
if len(*out) >= i {
|
||||
return &(*out)[len(*out)-i]
|
||||
} else {
|
||||
line := lex.Line{Type: "empty"}
|
||||
return &line
|
||||
}
|
||||
}
|
||||
|
||||
func CheckScene(row string) (bool, string, string) {
|
||||
var scene bool
|
||||
row = strings.ToUpper(row)
|
||||
for _, prefix := range Scene {
|
||||
if strings.HasPrefix(row, prefix+" ") ||
|
||||
strings.HasPrefix(row, prefix+".") {
|
||||
scene = true
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(row, ".") && !strings.HasPrefix(row, "..") {
|
||||
row = row[1:]
|
||||
scene = true
|
||||
}
|
||||
return scene, "scene", row
|
||||
}
|
||||
|
||||
func CheckCrow(row string) (bool, string, string) {
|
||||
var crow bool
|
||||
var el string
|
||||
row = strings.ToUpper(row)
|
||||
if strings.HasPrefix(row, ">") || strings.HasSuffix(row, " TO:") {
|
||||
crow = true
|
||||
el = "trans"
|
||||
}
|
||||
if strings.HasPrefix(row, ">") && strings.HasSuffix(row, "<") {
|
||||
el = "center"
|
||||
}
|
||||
return crow, el, strings.Trim(row, ">< ")
|
||||
}
|
||||
|
||||
func CheckEqual(row string) (bool, string, string) {
|
||||
var equal bool
|
||||
var el string
|
||||
if strings.HasPrefix(row, "=") {
|
||||
equal = true
|
||||
el = "synopse"
|
||||
}
|
||||
if len(row) >= 3 && strings.Trim(row, "=") == "" {
|
||||
el = "newpage"
|
||||
}
|
||||
return equal, el, strings.TrimLeft(row, "= ")
|
||||
}
|
||||
|
||||
func CheckSection(row string) (bool, string, string) {
|
||||
var section bool
|
||||
if strings.HasPrefix(row, "#") {
|
||||
section = true
|
||||
}
|
||||
return section, "section", row
|
||||
}
|
||||
|
||||
func CheckForce(row string) (bool, string, string) {
|
||||
var force = true
|
||||
var ftype string
|
||||
if len(row) < 1 {
|
||||
return false, "", ""
|
||||
}
|
||||
switch row[0] {
|
||||
case '@':
|
||||
ftype = "speaker"
|
||||
case '~':
|
||||
ftype = "lyrics"
|
||||
case '!':
|
||||
ftype = "action"
|
||||
default:
|
||||
force = false
|
||||
}
|
||||
if force {
|
||||
row = row[1:]
|
||||
}
|
||||
return force, ftype, row
|
||||
}
|
||||
|
||||
// This is a Fountain parser, trying to be as close as possible to the description
|
||||
// found at https://fountain.io/syntax but it can be incomplete.
|
||||
func Parse(scenes []string, file io.Reader) (out lex.Screenplay) {
|
||||
Scene = scenes
|
||||
var err error
|
||||
var titlepage, dialog bool = true, false
|
||||
var s, titletag string
|
||||
var toParse []string
|
||||
f := bufio.NewReader(file)
|
||||
for err == nil {
|
||||
s, err = f.ReadString('\n')
|
||||
toParse = append(toParse, s)
|
||||
}
|
||||
toParse = append(toParse, "") // Trigger the backtracking also for the last line
|
||||
for _, row := range toParse {
|
||||
if titlepage && !strings.Contains(toParse[0], ":") {
|
||||
out = append(out, lex.Line{Type: "titlepage"})
|
||||
out = append(out, lex.Line{Type: "Title", Contents: "Untitled"})
|
||||
out = append(out, lex.Line{Type: "newpage"})
|
||||
titlepage = false
|
||||
}
|
||||
row = strings.TrimRight(row, "\n\r")
|
||||
action := "action"
|
||||
if row == "" {
|
||||
action = "empty"
|
||||
if titlepage {
|
||||
titlepage = false
|
||||
out = append(out, lex.Line{Type: "newpage"})
|
||||
continue
|
||||
}
|
||||
}
|
||||
if last(&out, 1).Type != "action" {
|
||||
last(&out, 1).Contents = strings.TrimSpace(last(&out, 1).Contents)
|
||||
}
|
||||
|
||||
// Backtracking to check for dialog sequence
|
||||
if dialog {
|
||||
if row == "" {
|
||||
dialog = false
|
||||
action = "empty"
|
||||
if last(&out, 1).Type == "speaker" {
|
||||
last(&out, 1).Type = "action"
|
||||
}
|
||||
} else {
|
||||
if row[0] == '(' && row[len(row)-1] == ')' {
|
||||
action = "paren"
|
||||
} else {
|
||||
action = "dialog"
|
||||
}
|
||||
}
|
||||
}
|
||||
charcheck := strings.Split(row, "(")
|
||||
if charcheck[0] == strings.ToUpper(charcheck[0]) && action == "action" {
|
||||
action = "speaker"
|
||||
dialog = true
|
||||
}
|
||||
|
||||
checkfuncs := []func(string) (bool, string, string){
|
||||
CheckScene, // should actually check for empty lines, but doing that creates more problems than it solves
|
||||
CheckCrow,
|
||||
CheckEqual,
|
||||
CheckSection,
|
||||
CheckForce,
|
||||
}
|
||||
for _, checkfunc := range checkfuncs {
|
||||
check, element, contents := checkfunc(row)
|
||||
if check {
|
||||
action = element
|
||||
row = contents
|
||||
if action == "speaker" {
|
||||
dialog = true
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if titlepage {
|
||||
if titletag == "" {
|
||||
out = append(out, lex.Line{Type: "titlepage"})
|
||||
}
|
||||
split := strings.SplitN(row, ":", 2)
|
||||
if len(split) == 2 && !strings.HasPrefix(row, " ") {
|
||||
action = split[0]
|
||||
switch strings.ToLower(action) {
|
||||
case "title", "credit", "author", "authors":
|
||||
titletag = "title"
|
||||
default:
|
||||
if titletag == "title" {
|
||||
out = append(out, lex.Line{Type: "metasection"})
|
||||
}
|
||||
titletag = action
|
||||
}
|
||||
row = strings.TrimSpace(split[1])
|
||||
if row == "" {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
action = titletag
|
||||
row = strings.TrimSpace(row)
|
||||
}
|
||||
}
|
||||
out = append(out, lex.Line{action, row})
|
||||
}
|
||||
return out
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
package fountain
|
||||
|
||||
import (
|
||||
"github.com/lapingvino/lexington/lex"
|
||||
"io"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func Write(f io.Writer, scene []string, screenplay lex.Screenplay) {
|
||||
var titlepage = "start"
|
||||
for _, line := range screenplay {
|
||||
element := line.Type
|
||||
if titlepage == "start" && line.Type != "titlepage" {
|
||||
titlepage = ""
|
||||
}
|
||||
if titlepage != "" {
|
||||
element = titlepage
|
||||
}
|
||||
switch element {
|
||||
case "start":
|
||||
titlepage = "titlepage"
|
||||
case "titlepage":
|
||||
if line.Type == "metasection" {
|
||||
continue
|
||||
}
|
||||
if line.Type == "newpage" {
|
||||
fmt.Fprintln(f, "")
|
||||
titlepage = ""
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(f, "%s: %s\n", line.Type, line.Contents)
|
||||
case "newpage":
|
||||
fmt.Fprintln(f, "===")
|
||||
case "empty":
|
||||
fmt.Fprintln(f, "")
|
||||
case "speaker":
|
||||
if line.Contents != strings.ToUpper(line.Contents) {
|
||||
fmt.Fprint(f, "@")
|
||||
}
|
||||
fmt.Fprintln(f, line.Contents)
|
||||
case "scene":
|
||||
var supported bool
|
||||
for _, prefix := range scene {
|
||||
if strings.HasPrefix(line.Contents, prefix+" ") ||
|
||||
strings.HasPrefix(line.Contents, prefix+".") {
|
||||
supported = true
|
||||
}
|
||||
}
|
||||
if !supported {
|
||||
fmt.Fprint(f, ".")
|
||||
}
|
||||
fmt.Fprintln(f, line.Contents)
|
||||
case "lyrics":
|
||||
fmt.Fprintf(f, "~%s\n", line.Contents)
|
||||
case "action":
|
||||
if line.Contents == strings.ToUpper(line.Contents) {
|
||||
fmt.Fprint(f, "!")
|
||||
}
|
||||
fmt.Fprintln(f, line.Contents)
|
||||
default:
|
||||
fmt.Fprintln(f, line.Contents)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,16 +1,4 @@
|
||||
Lexington commandline tool for screenwriters
|
||||
============================================
|
||||
Print Draft Fast
|
||||
================
|
||||
|
||||
Lexington helps you convert between Final Draft, Fountain and its own lex file formats, and output to PDF, HTML and ebook formats.
|
||||
|
||||
At the moment the Fountain parser should be pretty decent, although still lacking features like simultaneous dialog, and inline markup is not yet supported and might never be. Also the PDF output doesn't do anything to keep pieces from your screenplay together. Feel free to contribute and help me out in knowing how best to handle this!
|
||||
|
||||
To run the tool, make sure to have Go installed first and then run
|
||||
|
||||
`go get github.com/lapingvino/lexington`
|
||||
|
||||
to install Lexington to your go/bin directory. If this directory is in your execution path, you can then run it like
|
||||
|
||||
`lexington -i inputfile.fountain -o outputfile.pdf -to pdf`
|
||||
|
||||
For a more complete overview of the command line options, use `lexington -help`.
|
||||
Based on my work on [lexington](https://github.com/lapingvino/lexington) and a question on Reddit about having a fast Markdown to PDF conversion, here is my attempt to create a quick and dirty PDF creation tool for Markdown source files.
|
||||
|
Loading…
Reference in New Issue