From: Angus Leeming Date: Mon, 19 Apr 2004 18:12:23 +0000 (+0000) Subject: Re-enable preview generation using dvips, gs and pnmcrop. X-Git-Tag: 1.6.10~15294 X-Git-Url: https://git.lyx.org/gitweb/?a=commitdiff_plain;h=a2e688065a961dad4be2d1a6cb68eb4a0bc9d1c9;p=features.git Re-enable preview generation using dvips, gs and pnmcrop. git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@8675 a592a061-630c-0410-9148-cb99ea01b6c8 --- diff --git a/lib/ChangeLog b/lib/ChangeLog index cbff83dcc7..661eab06b6 100644 --- a/lib/ChangeLog +++ b/lib/ChangeLog @@ -1,3 +1,10 @@ +2004-04-19 Angus Leeming + + * scripts/legacy_lyxpreview2ppm.py: new file. Called automatically + by lyxpreview2bitmap.py if dvipng is not found. + + * scripts/lyxpreview2bitmap.py (main): changes to suit. + 2004-04-19 Angus Leeming * scripts/lyxpreview2bitmap.py (extract_metrics_info): make the diff --git a/lib/scripts/legacy_lyxpreview2ppm.py b/lib/scripts/legacy_lyxpreview2ppm.py new file mode 100644 index 0000000000..c08c9e6347 --- /dev/null +++ b/lib/scripts/legacy_lyxpreview2ppm.py @@ -0,0 +1,278 @@ +#! /usr/bin/env python + +# file legacy_lyxpreview2ppm.py +# This file is part of LyX, the document processor. +# Licence details can be found in the file COPYING. + +# author Angus Leeming + +# Full author contact details are available in file CREDITS + +# This script converts a LaTeX file to a bunch of ppm files using the +# deprecated dvi->ps->ppm conversion route. + +# If possible, please grab 'dvipng'; it's faster and more robust. +# This legacy support will be removed one day... + +import glob, os, re, string, sys +import pipes, shutil, tempfile + + +# Pre-compiled regular expressions. +latex_file_re = re.compile("\.tex$") + + +def usage(prog_name): + return "Usage: %s \n"\ + "\twhere the colors are hexadecimal strings, eg 'faf0e6'"\ + % prog_name + + +def error(message): + sys.stderr.write(message + '\n') + sys.exit(1) + + +def find_exe(candidates, path): + for prog in candidates: + for directory in path: + full_path = os.path.join(directory, prog) + if os.access(full_path, os.X_OK): + return full_path + + return None + + +def find_exe_or_terminate(candidates, path): + exe = find_exe(candidates, path) + if exe == None: + error("Unable to find executable from '%s'" % string.join(candidates)) + + return exe + + +def run_command(cmd): + handle = os.popen(cmd, 'r') + cmd_stdout = handle.read() + cmd_status = handle.close() + + return cmd_status, cmd_stdout + + +def extract_metrics_info(log_file, metrics_file): + metrics = open(metrics_file, 'w') + + log_re = re.compile("Preview: ([ST])") + data_re = re.compile("(-?[0-9]+) (-?[0-9]+) (-?[0-9]+) (-?[0-9]+)") + + tp_ascent = 0.0 + tp_descent = 0.0 + + success = 0 + for line in open(log_file, 'r').readlines(): + match = log_re.match(line) + if match == None: + continue + + snippet = (match.group(1) == 'S') + success = 1 + match = data_re.search(line) + if match == None: + error("Unexpected data in %s\n%s" % (log_file, line)) + + if snippet: + ascent = string.atof(match.group(2)) + tp_ascent + descent = string.atof(match.group(3)) - tp_descent + + frac = 0.5 + if abs(ascent + descent) > 0.1: + frac = ascent / (ascent + descent) + + metrics.write("Snippet %s %f\n" % (match.group(1), frac)) + + else: + tp_descent = string.atof(match.group(2)) + tp_ascent = string.atof(match.group(4)) + + return success + + +def extract_resolution(log_file, dpi): + fontsize_re = re.compile("Preview: Fontsize") + magnification_re = re.compile("Preview: Magnification") + extract_decimal_re = re.compile("([0-9\.]+)") + extract_integer_re = re.compile("([0-9]+)") + + found_fontsize = 0 + found_magnification = 0 + + # Default values + magnification = 1000.0 + fontsize = 0.0 + + for line in open(log_file, 'r').readlines(): + if found_fontsize and found_magnification: + break + + if not found_fontsize: + match = fontsize_re.match(line) + if match != None: + match = extract_decimal_re.search(line) + if match == None: + error("Unable to parse: %s" % line) + fontsize = string.atof(match.group(1)) + found_fontsize = 1 + continue + + if not found_magnification: + match = magnification_re.match(line) + if match != None: + match = extract_integer_re.search(line) + if match == None: + error("Unable to parse: %s" % line) + magnification = string.atof(match.group(1)) + found_magnification = 1 + continue + + return dpi * (10.0 / fontsize) * (1000.0 / magnification) + + +def get_version_info(): + version_re = re.compile("([0-9])\.([0-9])") + + match = version_re.match(sys.version) + if match == None: + error("Unable to extract version info from 'sys.version'") + + return string.atoi(match.group(1)), string.atoi(match.group(2)) + + +def mkstemp(): + major, minor = get_version_info() + + if major >= 2 and minor >= 3: + return tempfile.mkstemp() + + tmp_name = tempfile.mktemp() + return open(tmp_name, 'w'), tmp_name + + +def legacy_latex_file(latex_file, fg_color, bg_color): + use_preview_re = re.compile("(\\\\usepackage\[[^]]+)(\]{preview})") + + tmp, tmp_name = mkstemp() + + success = 0 + for line in open(latex_file, 'r').readlines(): + match = use_preview_re.match(line) + if match == None: + tmp.write(line) + continue + + success = 1 + tmp.write("%s,dvips,tightpage%s\n\n" \ + "\\AtBeginDocument{\\AtBeginDvi{%%\n" \ + "\\special{!userdict begin/bop-hook{//bop-hook exec\n" \ + "<%s%s>{255 div}forall setrgbcolor\n" \ + "clippath fill setrgbcolor}bind def end}}}\n" \ + % (match.group(1), match.group(2), fg_color, bg_color)) + + if success: + tmp.close() + shutil.copy(tmp_name, latex_file) + os.remove(tmp_name) + + return success + + +def crop_files(pnmcrop, basename): + t = pipes.Template() + t.append("%s -left $IN" % pnmcrop, 'f-') + t.append("%s -right > $OUT" % pnmcrop, '-f') + + tmp, tmp_name = mkstemp() + tmp.close() + os.remove(tmp_name) + + for file in glob.glob("%s*.ppm" % basename): + if t.copy(file, tmp_name): + shutil.copy(tmp_name, file) + os.remove(tmp_name) + + +def legacy_conversion(argv): + # Parse and manipulate the command line arguments. + if len(argv) != 6: + error(usage(argv[0])) + + # Ignore argv[1] + + dir, latex_file = os.path.split(argv[2]) + if len(dir) != 0: + os.chdir(dir) + + dpi = string.atoi(argv[3]) + fg_color = argv[4] + bg_color = argv[5] + + # External programs used by the script. + path = string.split(os.getenv("PATH"), os.pathsep) + latex = find_exe_or_terminate(["pplatex", "latex2e", "latex"], path) + dvips = find_exe_or_terminate(["dvips"], path) + gs = find_exe_or_terminate(["gs"], path) + pnmcrop = find_exe(["pnmcrop"], path) + + # Move color information into the latex file. + if not legacy_latex_file(latex_file, fg_color, bg_color): + error("Unable to move color info into the latex file") + + # Compile the latex file. + latex_call = "%s %s" % (latex, latex_file) + + latex_status, latex_stdout = run_command(latex_call) + if latex_status != None: + error("%s failed to compile %s" \ + % (os.path.basename(latex), latex_file)) + + # Run the dvi file through dvips. + dvi_file = latex_file_re.sub(".dvi", latex_file) + ps_file = latex_file_re.sub(".ps", latex_file) + + dvips_call = "%s -o %s %s" % (dvips, ps_file, dvi_file) + + dvips_status, dvips_stdout = run_command(dvips_call) + if dvips_status != None: + error("Failed: %s %s" % (os.path.basename(dvips), dvi_file)) + + # Extract resolution data for gs from the log file. + log_file = latex_file_re.sub(".log", latex_file) + resolution = extract_resolution(log_file, dpi) + + # Older versions of gs have problems with a large degree of + # anti-aliasing at high resolutions + alpha = 4 + if resolution > 150: + alpha = 2 + + # Generate the bitmap images + gs_call = "%s -dNOPAUSE -dBATCH -dSAFER -sDEVICE=pnmraw " \ + "-sOutputFile=%s%%d.ppm " \ + "-dGraphicsAlphaBit=%d -dTextAlphaBits=%d " \ + "-r%f %s" \ + % (gs, latex_file_re.sub("", latex_file), \ + alpha, alpha, resolution, ps_file) + + gs_status, gs_stdout = run_command(gs_call) + if gs_status != None: + error("Failed: %s %s" % (os.path.basename(gs), ps_file)) + + # Crop the images + if pnmcrop != None: + crop_files(pnmcrop, latex_file_re.sub("", latex_file)) + + # Extract metrics info from the log file. + metrics_file = latex_file_re.sub(".metrics", latex_file) + if not extract_metrics_info(log_file, metrics_file): + error("Failed to extract metrics info from %s" % log_file) + + return 0 diff --git a/lib/scripts/lyxpreview2bitmap.py b/lib/scripts/lyxpreview2bitmap.py index ee91964728..5219899d40 100755 --- a/lib/scripts/lyxpreview2bitmap.py +++ b/lib/scripts/lyxpreview2bitmap.py @@ -44,6 +44,7 @@ # the images correctly on the screen. import glob, os, re, string, sys +from legacy_lyxpreview2ppm import legacy_conversion # Pre-compiled regular expressions. @@ -159,7 +160,16 @@ def main(argv): # External programs used by the script. path = string.split(os.getenv("PATH"), os.pathsep) latex = find_exe_or_terminate(["pplatex", "latex2e", "latex"], path) - dvipng = find_exe_or_terminate(["dvipng"], path) + + # This can go once dvipng becomes widespread. + dvipng = find_exe(["dvipng"], path) + if dvipng == None: + if output_format == "ppm": + return legacy_conversion(argv) + else: + error("The old 'dvi->ps->ppm' conversion requires " + "ppm as the output format") + pngtopnm = "" if output_format == "ppm": pngtopnm = find_exe_or_terminate(["pngtopnm"], path) @@ -191,6 +201,7 @@ def main(argv): if output_format == "ppm": convert_to_ppm_format(pngtopnm, latex_file_re.sub("", latex_file)) + return 0 if __name__ == "__main__": main(sys.argv)