add support for arbitrary titles/subtitles

Amolith created

Change summary

main.go | 166 +++++++++++++++++++++++++++++++++++-----------------------
1 file changed, 100 insertions(+), 66 deletions(-)

Detailed changes

main.go 🔗

@@ -33,9 +33,11 @@ var (
 	flagHelp          *bool   = flag.BoolP("help", "h", false, "Show the help message")
 	flagInput         *string = flag.StringP("input", "i", "", "Path to input Markdown")
 	flagOutput        *string = flag.StringP("output", "o", "", "Path to output PNG")
-	flagMetaSize      *int    = flag.IntP("metasize", "m", 40, "Size of font for meta information")
-	flagPostTitleSize *int    = flag.IntP("posttitlesize", "p", 60, "Size of font for post title")
-	flagSiteTitleSize *int    = flag.IntP("sitetitlesize", "s", 50, "Size of font for site title")
+	flagMetaSize      *int    = flag.IntP("metasize", "M", 40, "Size of font for meta information")
+	flagPostTitleSize *int    = flag.IntP("posttitlesize", "P", 60, "Size of font for post title")
+	flagSiteTitleSize *int    = flag.IntP("sitetitlesize", "S", 50, "Size of font for site title")
+	flagTitle         *string = flag.StringP("posttitle", "t", "", "Title displayed in the generated image")
+	flagSubtitle      *string = flag.StringP("subtitle", "s", "", "Subtitle displayed in the generated image")
 )
 
 //go:embed fonts
@@ -49,12 +51,17 @@ func main() {
 		os.Exit(0)
 	}
 
-	validateFlags()
+	manual := validateFlags()
+
+	var postTitle, postDate, postContent, postReadTime, dateEdited string
+
+	if !manual {
+		postTitle, postDate, postContent = getPostInfo(*flagInput)
+		postReadTime = getReadTime(postContent)
+		dateEdited = getEditDate(*flagInput)
+	}
 
-	postTitle, postDate, postContent := getPostInfo(*flagInput)
-	postReadTime := getReadTime(postContent)
 	siteTitle := getSiteTitle()
-	dateEdited := getEditDate(*flagInput)
 
 	collection := fontCollection()
 
@@ -88,67 +95,79 @@ func main() {
 			layout.Rigid(func(gtx layout.Context) layout.Dimensions {
 				gtx.Constraints.Max.X = int(float64(gtx.Constraints.Max.X) * .9)
 				gtx.Constraints.Min.X = gtx.Constraints.Max.X
-				title := material.Label(th, unit.Sp(float32(*flagPostTitleSize)), postTitle)
+				var title material.LabelStyle
+				if manual {
+					title = material.Label(th, unit.Sp(float32(*flagPostTitleSize)), *flagTitle)
+				} else {
+					title = material.Label(th, unit.Sp(float32(*flagPostTitleSize)), postTitle)
+				}
 				title.Font = text.Font{Typeface: "Primary font", Variant: "", Style: text.Regular, Weight: text.Bold}
 				title.Alignment = text.Middle
 				return title.Layout(gtx)
 			}),
 
 			layout.Rigid(func(gtx layout.Context) layout.Dimensions {
-				gtx.Constraints.Max.X = int(float64(gtx.Constraints.Max.X) * .7)
-				gtx.Constraints.Min.X = gtx.Constraints.Max.X
-				return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
-					layout.Rigid(func(gtx layout.Context) layout.Dimensions {
-						return layout.Flex{Axis: layout.Horizontal, Spacing: layout.SpaceBetween}.Layout(gtx,
-							layout.Rigid(func(gtx layout.Context) layout.Dimensions {
-								rTime := material.Label(th, unit.Sp(float32(*flagMetaSize)), "Reading Time: ")
-								rTime.Font = text.Font{Typeface: "Primary font", Variant: "", Style: text.Regular, Weight: text.Bold}
-								rTime.Alignment = text.Start
-								return rTime.Layout(gtx)
-							}),
-							layout.Rigid(func(gtx layout.Context) layout.Dimensions {
-								rTime := material.Label(th, unit.Sp(float32(*flagMetaSize)), postReadTime)
-								rTime.Font = text.Font{Typeface: "Primary font", Variant: "", Style: text.Regular, Weight: text.Normal}
-								rTime.Alignment = text.End
-								return rTime.Layout(gtx)
-							}),
-						)
-					}),
-
-					layout.Rigid(func(gtx layout.Context) layout.Dimensions {
-						return layout.Flex{Axis: layout.Horizontal, Spacing: layout.SpaceBetween}.Layout(gtx,
-							layout.Rigid(func(gtx layout.Context) layout.Dimensions {
-								pDate := material.Label(th, unit.Sp(float32(*flagMetaSize)), "Published: ")
-								pDate.Font = text.Font{Typeface: "Primary font", Variant: "", Style: text.Regular, Weight: text.Bold}
-								pDate.Alignment = text.Start
-								return pDate.Layout(gtx)
-							}),
-							layout.Rigid(func(gtx layout.Context) layout.Dimensions {
-								pDate := material.Label(th, unit.Sp(float32(*flagMetaSize)), fmt.Sprint(postDate))
-								pDate.Font = text.Font{Typeface: "Primary font", Variant: "", Style: text.Regular, Weight: text.Normal}
-								pDate.Alignment = text.End
-								return pDate.Layout(gtx)
-							}),
-						)
-					}),
-
-					layout.Rigid(func(gtx layout.Context) layout.Dimensions {
-						return layout.Flex{Axis: layout.Horizontal, Spacing: layout.SpaceBetween}.Layout(gtx,
-							layout.Rigid(func(gtx layout.Context) layout.Dimensions {
-								eDate := material.Label(th, unit.Sp(float32(*flagMetaSize)), "Edited: ")
-								eDate.Font = text.Font{Typeface: "Primary font", Variant: "", Style: text.Regular, Weight: text.Bold}
-								eDate.Alignment = text.Start
-								return eDate.Layout(gtx)
-							}),
-							layout.Rigid(func(gtx layout.Context) layout.Dimensions {
-								eDate := material.Label(th, unit.Sp(float32(*flagMetaSize)), fmt.Sprint(dateEdited))
-								eDate.Font = text.Font{Typeface: "Primary font", Variant: "", Style: text.Regular, Weight: text.Normal}
-								eDate.Alignment = text.End
-								return eDate.Layout(gtx)
-							}),
-						)
-					}),
-				)
+				if manual {
+					subtitle := material.Label(th, unit.Sp(float32(*flagMetaSize)), *flagSubtitle)
+					subtitle.Font = text.Font{Typeface: "Primary font", Variant: "", Style: text.Italic, Weight: text.Normal}
+					subtitle.Alignment = text.Middle
+					return subtitle.Layout(gtx)
+				} else {
+					gtx.Constraints.Max.X = int(float64(gtx.Constraints.Max.X) * .7)
+					gtx.Constraints.Min.X = gtx.Constraints.Max.X
+					return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
+						layout.Rigid(func(gtx layout.Context) layout.Dimensions {
+							return layout.Flex{Axis: layout.Horizontal, Spacing: layout.SpaceBetween}.Layout(gtx,
+								layout.Rigid(func(gtx layout.Context) layout.Dimensions {
+									rTime := material.Label(th, unit.Sp(float32(*flagMetaSize)), "Reading Time: ")
+									rTime.Font = text.Font{Typeface: "Primary font", Variant: "", Style: text.Regular, Weight: text.Bold}
+									rTime.Alignment = text.Start
+									return rTime.Layout(gtx)
+								}),
+								layout.Rigid(func(gtx layout.Context) layout.Dimensions {
+									rTime := material.Label(th, unit.Sp(float32(*flagMetaSize)), postReadTime)
+									rTime.Font = text.Font{Typeface: "Primary font", Variant: "", Style: text.Regular, Weight: text.Normal}
+									rTime.Alignment = text.End
+									return rTime.Layout(gtx)
+								}),
+							)
+						}),
+
+						layout.Rigid(func(gtx layout.Context) layout.Dimensions {
+							return layout.Flex{Axis: layout.Horizontal, Spacing: layout.SpaceBetween}.Layout(gtx,
+								layout.Rigid(func(gtx layout.Context) layout.Dimensions {
+									pDate := material.Label(th, unit.Sp(float32(*flagMetaSize)), "Published: ")
+									pDate.Font = text.Font{Typeface: "Primary font", Variant: "", Style: text.Regular, Weight: text.Bold}
+									pDate.Alignment = text.Start
+									return pDate.Layout(gtx)
+								}),
+								layout.Rigid(func(gtx layout.Context) layout.Dimensions {
+									pDate := material.Label(th, unit.Sp(float32(*flagMetaSize)), fmt.Sprint(postDate))
+									pDate.Font = text.Font{Typeface: "Primary font", Variant: "", Style: text.Regular, Weight: text.Normal}
+									pDate.Alignment = text.End
+									return pDate.Layout(gtx)
+								}),
+							)
+						}),
+
+						layout.Rigid(func(gtx layout.Context) layout.Dimensions {
+							return layout.Flex{Axis: layout.Horizontal, Spacing: layout.SpaceBetween}.Layout(gtx,
+								layout.Rigid(func(gtx layout.Context) layout.Dimensions {
+									eDate := material.Label(th, unit.Sp(float32(*flagMetaSize)), "Edited: ")
+									eDate.Font = text.Font{Typeface: "Primary font", Variant: "", Style: text.Regular, Weight: text.Bold}
+									eDate.Alignment = text.Start
+									return eDate.Layout(gtx)
+								}),
+								layout.Rigid(func(gtx layout.Context) layout.Dimensions {
+									eDate := material.Label(th, unit.Sp(float32(*flagMetaSize)), fmt.Sprint(dateEdited))
+									eDate.Font = text.Font{Typeface: "Primary font", Variant: "", Style: text.Regular, Weight: text.Normal}
+									eDate.Alignment = text.End
+									return eDate.Layout(gtx)
+								}),
+							)
+						}),
+					)
+				}
 			}),
 
 			layout.Rigid(func(gtx layout.Context) layout.Dimensions {
@@ -314,16 +333,31 @@ It looks at...
 `)
 }
 
-// Validate flags
-func validateFlags() {
-	if *flagInput == "" {
-		fmt.Println("Error: No input file specified")
+// Validate flags and return true if the user is manually specifying the title
+// and subtitle
+func validateFlags() bool {
+	ret := false
+	if *flagInput == "" && (*flagTitle == "" || *flagSubtitle == "") {
+		fmt.Println("Error: input file not specified and title or subtitle not specified. Please specify EITHER an input file or both a title AND subtitle.")
+		os.Exit(1)
+	} else if *flagInput != "" && (*flagTitle != "" || *flagSubtitle != "") {
+		fmt.Println("Warning: input file specified and title or subtitle specified. Ignoring title and subtitle flags and continuing with input file")
+	} else if *flagTitle != "" && *flagSubtitle != "" {
+		ret = true
+	} else if *flagTitle == "" && *flagSubtitle != "" {
+		fmt.Println("Error: subtitle specified but title not specified")
+		os.Exit(1)
+	} else if *flagTitle != "" && *flagSubtitle == "" {
+		fmt.Println("Error: title specified but subtitle not specified")
 		os.Exit(1)
 	}
+
 	if *flagOutput == "" {
 		fmt.Println("Error: No output file specified")
 		os.Exit(1)
 	}
+
+	return ret
 }
 
 // Get the post's title, subtitle, and date