# Pretty-print Meal-Master recipe to .RTF format # Usage: awk -f mmrtf.awk recipe.rtf # Only works with single-column Meal-Master format # # RTF hints: # # * https://metacpan.org/dist/RTF-Writer/view/lib/RTF/Cookbook.pod # * https://en.wikipedia.org/wiki/Rich_Text_Format # # RTF uses the ANSI character set aka Windows-1252. # To convert a recipe from UTF-8 on DOS or Linux: # # DOS: # C:\> copy recipe.mmf recipe.bak # C:\> utf8tocp.exe 1252 recipe.mmf # # Linux: # $ cp recipe.mmf recipe.bak # $ iconv -f UTF-8 -t WINDOWS-1252 recipe.mmf function amount_format(amt) { retval = trim(amt) if (retval ~ /^[0-9]+$/) { retval = retval " " } return retval } function array_size(a) { retval = 0 for (i in a) { retval++ } return retval } function heading_fontsize(size) { retval = fontsize + (7 - size) return retval } function inches(value) { # 1440 twips per inch retval = value * 1440 return retval } function ingredient_parse(line) { new_amount = substr(line, 1, 7) new_unit = substr(line, 9, 2) new_ingredient = substr(line, 12) if (new_amount == " " && new_unit == " " && match(new_ingredient, /^[ \t]*- */)) { ingredient = ingredient " " substr(new_ingredient, RLENGTH+1) is_continuation = 1 } else { amount = new_amount unit = new_unit ingredient = new_ingredient is_continuation = 0 } return } function rtf() { fontsize = 24 heading_bottom = inches(1/4) par_bottom = inches(1/8) printf "{\\rtf1\\ansi\\deff0\\deflang1033\\windowctrl\n" printf "{\\*\\comment generator: mmrtf.awk}\n" printf "{\\fonttbl\n" printf "{\\f0 \\froman Times New Roman;}\n" printf "{\\f1 \\fmodern Line Printer;}\n" printf "{\\f2 \\froman Symbol;}\n" printf "{\\f3 \\fswiss Ariel;}\n" printf "}\n" heading_top = 0 rtf_heading(2, title) heading_top = inches(1/4) table_gap = 0 table_left = 0 cell1 = inches(1.6) cell2 = inches(7) # beginning of table printf "{\\pard\n" printf "\\trowd\\trgaph%d\\trleft%d\\cellx%d\\cellx%d\n", table_gap, table_left, cell1, cell2 if (length(categories) > 0) { printf "\\pard\\intbl{\\b{Categories:}}\\cell\n" printf "\\pard\\intbl{%s}\\cell\n", rtf_encode(categories) printf "\\row\n" } if (length(yield) > 0) { printf "\\pard\\intbl{\\b{Yield:}}\\cell\n" printf "\\pard\\intbl{%s}\\cell\n", rtf_encode(yield) printf "\\row\n" } # end of table printf "}\n" if (array_size(ingredients) > 0) { rtf_heading(3, "Ingredients") # beginning of table cell1 = inches(1.25) cell2 = inches(2.5) cell3 = inches(7) printf "{\\pard\n" printf "\\trowd\\trgaph%d\\trleft%d\\cellx%d\\cellx%d\\cellx%d\n", table_gap, table_left, cell1, cell2, cell3 printf "\\pard\\intbl{\\b{Amt}}\\cell\n" printf "\\pard\\intbl{\\b{Unit}}\\cell\n" printf "\\pard\\intbl{\\b{Ingredient}}\\cell\n" printf "\\row\n" for (gsection in gsections) { printf "\\trowd\\trgaph%d\\trleft%d\n", table_gap, table_left printf "\\clmgf\\cellx%d\n", cell1 printf "\\clmrg\\cellx%d\n", cell2 printf "\\clmrg\\cellx%d\n", cell3 printf "\\pard\\intbl{\\i{%s}}\\cell\n", rtf_encode(gsection) printf "\\pard\\intbl{}\\cell\n" printf "\\pard\\intbl{}\\cell\n" printf "\\row\n" ingredient_count = gsections[gsection] for (i = 1; i <= ingredient_count; i++) { amount = amounts[gsection, i] unit = units[gsection, i] ingredient = ingredients[gsection, i] printf "\\trowd\\trgaph%d\\trleft%d\\cellx%d\\cellx%d\\cellx%d\n", table_gap, table_left, cell1, cell2, cell3 printf "\\pard\\intbl{%7s}\\cell\n", rtf_encode(amount_format(amount)) printf "\\pard\\intbl{%s}\\cell\n", unit_name(unit) printf "\\pard\\intbl{%s}\\cell\n", rtf_encode(ingredient) printf "\\row\n" } } # end of table printf "}\n" } if (array_size(instructions) > 0) { rtf_heading(3, "Instructions") list_open = 0 par_open = 0 for (tsection in tsections) { rtf_close() if (length(tsection) > 0) { rtf_heading(4, tsection) } instruction_count = tsections[tsection] for (i = 1; i <= instruction_count; i++) { line = instructions[tsection,i] if (line ~ /^[ \t]*$/) { if (list_open || par_open) { rtf_close() } } else if (match(line, /^[ \t]*\* /)) { if (par_open == 1) { rtf_close() } if (list_open == 0) { list_open = 1 printf "{\n" } text = substr(line, RLENGTH+1) printf "{\\pard\\sa%d\\bullet %s\\par}\n", par_bottom, rtf_encode(text) } else { if (list_open) { rtf_close() } if (par_open == 0) { printf "{\\pard\\sa%d\n", par_bottom par_open = 1 } printf "{%s} \n", rtf_encode(trim(line)) } } } rtf_close() } printf "\n}\n" return } function rtf_close() { if (list_open) { printf "}\n" list_open = 0 } else if (par_open) { printf "\\par}\n" par_open = 0 } return } function rtf_encode(str) { gsub(/\\/, "\\\\", str) gsub(/{/, "\\{", str) gsub(/}/, "\\}", str) return str } # rtf_heading(2, txt) is like HTML: printf "

%s

", txt function rtf_heading(lvl, str) { printf "{\\pard\\sa%d\\sb%d\\fs%d\\b{%s}\\par}\n", heading_bottom, heading_top, heading_fontsize(lvl), rtf_encode(str) return } function trim(str) { retval = str gsub(/^[ \t]+/, "", retval) gsub(/[ \t]+$/, "", retval) return retval } function unit_name(unit) { if (unit in names) { retval = names[unit] } else { retval = unit } return retval } function unit_names_init() { names["x "] = "per serving" names["sm"] = "small" names["md"] = "medium" names["lg"] = "large" names["cn"] = "can" names["pk"] = "package" names["pn"] = "pinch" names["dr"] = "drop" names["ds"] = "dash" names["ct"] = "carton" names["bn"] = "bunch" names["sl"] = "slice" names["ea"] = "each" names["t "] = "teaspoon" names["ts"] = "teaspoon" names["T "] = "tablespoon" names["tb"] = "tablespoon" names["fl"] = "fluid ounce" names["c "] = "cup" names["pt"] = "pint" names["qt"] = "quart" names["ga"] = "gallon" names["oz"] = "ounce" names["lb"] = "pound" names["ml"] = "milliliter" names["cb"] = "cubic cm" names["cl"] = "centiliter" names["dl"] = "deciliter" names["l "] = "liter" names["mg"] = "milligram" names["cg"] = "centigram" names["dg"] = "decigram" names["g "] = "gram" names["kg"] = "kilogram" return } BEGIN { after_ingredients = 0 at_end = 0 in_gsection = 0 in_heading = 0 in_ingredients = 0 in_instructions = 0 in_list = 0 unit_names_init() } { gsub(/\r/, "") if (NR == 1) { if (/^(MMMMM|-----)----- Recipe via Meal-Master/) { in_heading = 1 } else { print "Error: Not in Meal-Master format\n" exit 0 } } else if (NR == 2) { # ignore second line } else if (in_heading) { if (match($0, /^[ \t]+Title: /)) { title = substr($0, RLENGTH+1) } else if (match($0, /^[ \t]+Categories: /)) { categories = substr($0, RLENGTH+1) } else if (match($0, /^[ \t]+Yield: /)) { yield = substr($0, RLENGTH+1) } else if (/^[ \t]*$/) { in_heading = 0 in_ingredients = 1 } } else if (in_ingredients) { if (match($0, /^(MMMMM|-----)-+/)) { in_ingredients = 0 in_gsection = 1 gsection = substr($0, RLENGTH+1) gsub(/-+$/, "", gsection) } else if (/^[ \t]*$/) { in_ingredients = 0 after_ingredients = 1 } else { gsection = "" i = gsections[gsection] ingredient_parse($0) if (is_continuation == 0) { i++ gsections[gsection] = i } amounts[gsection,i] = amount units[gsection,i] = unit ingredients[gsection,i] = ingredient } } else if (after_ingredients) { if (match($0, /^(MMMMM|-----)-+/)) { after_ingredients = 0 in_gsection = 1 gsection = substr($0, RLENGTH+1) gsub(/-+$/, "", gsection) } else { after_ingredients = 0 in_instructions = 1 } } else if (in_gsection) { if (match($0, /^(MMMMM|-----)-+/)) { in_gsection = 1 gsection = substr($0, RLENGTH+1) gsub(/-+$/, "", gsection) } else if (/^[ \t]*$/) { in_gsection = 0 after_gsection = 1 } else { i = gsections[gsection] ingredient_parse($0) if (is_continuation == 0) { i++ gsections[gsection] = i } amounts[gsection,i] = amount units[gsection,i] = unit ingredients[gsection,i] = ingredient } } else if (after_gsection) { if (match($0, /^(MMMMM|-----)-+/)) { after_gsection = 0 in_gsection = 1 gsection = substr($0, RLENGTH+1) gsub(/-+$/, "", gsection) } else { after_gsection = 0 in_instructions = 1 } } if (in_instructions) { if (match($0, /^(MMMMM|-----)-+/)) { tsection = substr($0, RLENGTH+1) gsub(/-+$/, "", tsection) } else if (/^(MMMMM|-----)$/) { in_instructions = 0 at_end = 1 } else if (/^[ \t]*\* /) { in_instructions = 0 in_list = 1 } else { tsections[tsection]++ i = tsections[tsection] instructions[tsection,i] = $0 } } if (in_list) { if (/^[ \t]*$/) { in_list = 0 in_instructions = 1 tsections[tsection]++ i = tsections[tsection] instructions[tsection,i] = $0 } else if (/^[ \t]*\* /) { tsections[tsection]++ i = tsections[tsection] instructions[tsection,i] = $0 } else { i = tsections[tsection] line = trim($0) instructions[tsection,i] = instructions[tsection,i] " " line } } } END { rtf() }