+2004-04-15 Angus Leeming <leeming@lyx.org>
+
+ * scripts/lyxpreview2bitmap.sh: removed.
+ * scripts/lyxpreview2bitmap.py: added. Uses Jan-Åke Larsson's dvipng
+ to perform the conversion.
+
2004-04-12 Georg Baum <Georg.Baum@post.rwth-aachen.de>
* configure.m4: merge \viewer and \format. Add editor to \format
--- /dev/null
+#! /usr/bin/env python
+
+# file lyxpreview2bitmap.py
+# This file is part of LyX, the document processor.
+# Licence details can be found in the file COPYING.
+
+# author Angus Leeming
+# with much advice from members of the preview-latex project:
+# David Kastrup, dak@gnu.org and
+# Jan-Åke Larsson, jalar@mai.liu.se.
+
+# Full author contact details are available in file CREDITS
+
+# This script takes a LaTeX file and generates a collection of
+# png or ppm image files, one per previewed snippet.
+
+# Pre-requisites:
+# * A latex executable;
+# * preview.sty;
+# * dvipng;
+# * pngtoppm (if outputing ppm format images).
+
+# preview.sty and dvipng are part of the preview-latex project
+# http://preview-latex.sourceforge.net/
+
+# preview.sty can alternatively be obtained from
+# CTAN/support/preview-latex/
+
+# Example usage:
+# lyxpreview2bitmap.py png 0lyxpreview.tex 128 000000 faf0e6
+
+# This script takes five arguments:
+# FORMAT: either 'png' or 'ppm'. The desired output format.
+# TEXFILE: the name of the .tex file to be converted.
+# DPI: a scale factor, passed to dvipng.
+# FG_COLOR: the foreground color as a hexadecimal string, eg '000000'.
+# BG_COLOR: the background color as a hexadecimal string, eg 'faf0e6'.
+
+# Decomposing TEXFILE's name as DIR/BASE.tex, this script will,
+# if executed successfully, leave in DIR:
+# * a (possibly large) number of image files with names
+# like BASE[0-9]+.png
+# * a file BASE.metrics, containing info needed by LyX to position
+# the images correctly on the screen.
+
+import glob, os, re, string, sys
+
+
+# Pre-compiled regular expressions.
+hexcolor_re = re.compile("^[0-9a-fA-F]{6}$")
+latex_file_re = re.compile("\.tex$")
+
+
+def usage(prog_name):
+ return "Usage: %s <latex file> <dpi> <fg color> <bg color>\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 make_texcolor(hexcolor):
+ # Test that the input string contains 6 hexadecimal chars.
+ if not hexcolor_re.match(hexcolor):
+ error("Cannot convert color '%s'" % hexcolor)
+
+ red = float(string.atoi(hexcolor[0:2], 16)) / 255.0
+ green = float(string.atoi(hexcolor[2:4], 16)) / 255.0
+ blue = float(string.atoi(hexcolor[4:6], 16)) / 255.0
+
+ return "rgb %f %f %f" % (red, green, blue)
+
+
+def extract_metrics_info(dvipng_stdout, metrics_file):
+ metrics = open(metrics_file, 'w')
+ metrics_re = re.compile("\[([0-9]+) depth=([0-9]+) height=([0-9]+)\]")
+
+ success = 0
+ pos = 0
+ while 1:
+ match = metrics_re.search(dvipng_stdout, pos)
+ if match == None:
+ break
+ success = 1
+
+ # Calculate the 'ascent fraction'.
+ descent = string.atof(match.group(2))
+ ascent = string.atof(match.group(3))
+ frac = 0.5
+ if abs(ascent + descent) > 0.1:
+ frac = ascent / (ascent + descent)
+
+ metrics.write("Snippet %s %f\n" % (match.group(1), frac))
+ pos = match.end(3) + 2
+
+ return success
+
+
+def convert_to_ppm_format(pngtopnm, basename):
+ png_file_re = re.compile("\.png$")
+
+ for png_file in glob.glob("%s*.png" % basename):
+ ppm_file = png_file_re.sub(".ppm", png_file)
+
+ p2p_cmd = "%s %s" % (pngtopnm, png_file)
+ p2p_status, p2p_stdout = run_command(p2p_cmd)
+ if p2p_status != None:
+ error("Unable to convert %s to ppm format" % png_file)
+
+ ppm = open(ppm_file, 'w')
+ ppm.write(p2p_stdout)
+ os.remove(png_file)
+
+
+def main(argv):
+ # Parse and manipulate the command line arguments.
+ if len(argv) != 6:
+ error(usage(argv[0]))
+
+ output_format = string.lower(argv[1])
+
+ dir, latex_file = os.path.split(argv[2])
+ if len(dir) != 0:
+ os.chdir(dir)
+
+ dpi = string.atoi(argv[3])
+ fg_color = make_texcolor(argv[4])
+ bg_color = make_texcolor(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)
+ dvipng = find_exe_or_terminate(["dvipng"], path)
+ pngtopnm = ""
+ if output_format == "ppm":
+ pngtopnm = find_exe_or_terminate(["pngtopnm"], path)
+
+ # 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 dvipng.
+ dvi_file = latex_file_re.sub(".dvi", latex_file)
+ dvipng_call = "%s -Ttight -depth -height -D %d -fg '%s' -bg '%s' %s" \
+ % (dvipng, dpi, fg_color, bg_color, dvi_file)
+
+ dvipng_status, dvipng_stdout = run_command(dvipng_call)
+ if dvipng_status != None:
+ error("%s failed to generate images from %s" \
+ % (os.path.basename(dvipng), dvi_file))
+
+ # Extract metrics info from dvipng_stdout.
+ metrics_file = latex_file_re.sub(".metrics", latex_file)
+ if not extract_metrics_info(dvipng_stdout, metrics_file):
+ error("Failed to extract metrics info from dvipng")
+
+ # Convert images to ppm format if necessary.
+ if output_format == "ppm":
+ convert_to_ppm_format(pngtopnm, latex_file_re.sub("", latex_file))
+
+
+if __name__ == "__main__":
+ main(sys.argv)
+++ /dev/null
-#! /bin/sh
-
-# file lyxpreview2bitmap.sh
-# This file is part of LyX, the document processor.
-# Licence details can be found in the file COPYING.
-#
-# author Angus Leeming
-# with much advice from David Kastrup, david.kastrup@t-online.de.
-#
-# Full author contact details are available in file CREDITS
-
-# This script takes a LaTeX file and generates bitmap image files,
-# one per page.
-
-# The idea is to use it with preview.sty from the preview-latex project
-# (http://preview-latex.sourceforge.net/) to create small bitmap
-# previews of things like math equations.
-
-# preview.sty can be obtained from
-# CTAN/macros/latex/contrib/supported/preview.
-
-# This script takes three arguments:
-# TEXFILE: the name of the .tex file to be converted.
-# SCALEFACTOR: a scale factor, used to ascertain the resolution of the
-# generated image which is then passed to gs.
-# OUTPUTFORMAT: the format of the output bitmap image files.
-# Two formats are recognised: "ppm" and "png".
-
-# If successful, this script will leave in dir ${DIR}:
-# a (possibly large) number of image files with names like
-# ${BASE}\([0-9]*\).${SUFFIX} where SUFFIX is ppm or png.
-# a file containing info needed by LyX to position the images correctly
-# on the screen.
-# ${BASE}.metrics
-# All other files ${BASE}* will be deleted.
-
-# A quick note on the choice of OUTPUTFORMAT:
-
-# In general files in PPM format are 10-100 times larger than the
-# equivalent files in PNG format. Larger files results in longer
-# reading and writing times as well as greater disk usage.
-
-# However, whilst the Qt image loader can load files in PNG format
-# without difficulty, the xforms image loader cannot. They must first
-# be converted to a loadable format (eg PPM!). Thus, previews will take
-# longer to appear if the xforms loader is used to load snippets in
-# PNG format.
-
-# You can always experiment by adding a line to your
-# ${LYXUSERDIR}/preferences file
-# \converter lyxpreview ${FORMAT} "lyxpreview2bitmap.sh" ""
-# where ${FORMAT} is either ppm or png.
-
-# These four programs are used by the script.
-# Adjust their names to suit your setup.
-test -n "$LATEX" || LATEX=latex
-DVIPS=dvips
-GS=gs
-PNMCROP=pnmcrop
-readonly LATEX DVIPS GS PNMCROP
-
-# Three helper functions.
-FIND_IT ()
-{
- test $# -eq 1 || exit 1
-
- type $1 > /dev/null || {
- echo "Unable to find \"$1\". Please install."
- exit 1
- }
-}
-
-BAIL_OUT ()
-{
- test $# -eq 1 && echo $1
-
- # Remove everything except the original .tex file.
- FILES=`ls ${BASE}* | sed -e "/${BASE}\.tex/d"`
- rm -f ${FILES} texput.log
- echo "Leaving ${BASE}.tex in ${DIR}"
- exit 1
-}
-
-REQUIRED_VERSION ()
-{
- test $# -eq 1 || exit 1
-
- echo "We require preview.sty version 0.73 or newer. You're using"
- grep 'Package: preview' $1
-}
-
-# Preliminary check.
-if [ $# -ne 3 ]; then
- exit 1
-fi
-
-# We use latex, dvips and gs, so check that they're all there.
-FIND_IT ${LATEX}
-FIND_IT ${DVIPS}
-FIND_IT ${GS}
-
-# Extract the params from the argument list.
-DIR=`dirname $1`
-BASE=`basename $1 .tex`
-
-SCALEFACTOR=$2
-
-if [ "$3" = "ppm" ]; then
- GSDEVICE=pnmraw
- GSSUFFIX=ppm
-elif [ "$3" = "png" ]; then
- GSDEVICE=png16m
- GSSUFFIX=png
-else
- BAIL_OUT "Unrecognised output format ${OUTPUTFORMAT}. \
- Expected either \"ppm\" or \"png\"."
-fi
-
-# Initialise some variables.
-TEXFILE=${BASE}.tex
-LOGFILE=${BASE}.log
-DVIFILE=${BASE}.dvi
-PSFILE=${BASE}.ps
-METRICSFILE=${BASE}.metrics
-readonly TEXFILE LOGFILE DVIFILE PSFILE METRICSFILE
-
-# LaTeX -> DVI.
-cd ${DIR}
-${LATEX} ${TEXFILE} ||
-{
- BAIL_OUT "Failed: ${LATEX} ${TEXFILE}"
-}
-
-# Parse ${LOGFILE} to obtain bounding box info to output to
-# ${METRICSFILE}.
-# This extracts lines starting "Preview: Tightpage" and
-# "Preview: Snippet".
-grep -E 'Preview: [ST]' ${LOGFILE} > ${METRICSFILE} ||
-{
- REQUIRED_VERSION ${LOGFILE}
- BAIL_OUT "Failed: grep -E 'Preview: [ST]' ${LOGFILE}"
-}
-
-# Parse ${LOGFILE} to obtain ${RESOLUTION} for the gs process to follow.
-# 1. Extract font size from a line like "Preview: Fontsize 20.74pt"
-# Use grep for speed and because it gives an error if the line is
-# not found.
-LINE=`grep 'Preview: Fontsize' ${LOGFILE}` ||
-{
- REQUIRED_VERSION ${LOGFILE}
- BAIL_OUT "Failed: grep 'Preview: Fontsize' ${LOGFILE}"
-}
-# The sed script strips out everything that won't form a decimal number
-# from the line. It bails out after the first match has been made in
-# case there are multiple lines "Preview: Fontsize". (There shouldn't
-# be.)
-# Note: use "" quotes in the echo to preserve newlines.
-LATEXFONT=`echo "${LINE}" | sed 's/[^0-9\.]//g; 1q'`
-
-# 2. Extract magnification from a line like
-# "Preview: Magnification 2074"
-# If no such line found, default to MAGNIFICATION=1000.
-LINE=`grep 'Preview: Magnification' ${LOGFILE}`
-if LINE=`grep 'Preview: Magnification' ${LOGFILE}`; then
- # Strip out everything that won't form an /integer/.
- MAGNIFICATION=`echo "${LINE}" | sed 's/[^0-9]//g; 1q'`
-else
- MAGNIFICATION=1000
-fi
-
-# 3. Compute resolution.
-# "bc" allows floating-point arithmetic, unlike "expr" or "dc".
-RESOLUTION=`echo "scale=2; \
- ${SCALEFACTOR} * (10/${LATEXFONT}) * (1000/${MAGNIFICATION})" \
- | bc`
-
-# DVI -> PostScript
-${DVIPS} -o ${PSFILE} ${DVIFILE} ||
-{
- BAIL_OUT "Failed: ${DVIPS} -o ${PSFILE} ${DVIFILE}"
-}
-
-# PostScript -> Bitmap files
-# Older versions of gs have problems with a large degree of
-# anti-aliasing at high resolutions
-
-# test expects integer arguments.
-# ${RESOLUTION} may be a float. Truncate it.
-INT_RESOLUTION=`echo "${RESOLUTION} / 1" | bc`
-
-ALPHA=4
-if [ ${INT_RESOLUTION} -gt 150 ]; then
- ALPHA=2
-fi
-
-${GS} -q -dNOPAUSE -dBATCH -dSAFER \
- -sDEVICE=${GSDEVICE} -sOutputFile=${BASE}%d.${GSSUFFIX} \
- -dGraphicsAlphaBit=${ALPHA} -dTextAlphaBits=${ALPHA} \
- -r${RESOLUTION} ${PSFILE} ||
-{
- BAIL_OUT "Failed: ${GS} ${PSFILE}"
-}
-
-# All has been successful, so remove everything except the bitmap files
-# and the metrics file.
-FILES=`ls ${BASE}* | sed -e "/${BASE}.metrics/d" \
- -e "/${BASE}\([0-9]*\).${GSSUFFIX}/d"`
-rm -f ${FILES} texput.log
-
-# The bitmap files can have large amounts of whitespace to the left and
-# right. This can be cropped if so desired.
-CROP=1
-type ${PNMCROP} > /dev/null || CROP=0
-
-# There's no point cropping the image if using PNG images. If you want to
-# crop, use PPM.
-# Apparently dvipng will support cropping at some stage in the future...
-if [ ${CROP} -eq 1 -a "${GSDEVICE}" = "pnmraw" ]; then
- for FILE in ${BASE}*.${GSSUFFIX}
- do
- if ${PNMCROP} -left ${FILE} 2> /dev/null |\
- ${PNMCROP} -right 2> /dev/null > ${BASE}.tmp; then
- mv ${BASE}.tmp ${FILE}
- else
- rm -f ${BASE}.tmp
- fi
- done
- rm -f ${BASE}.tmp
-fi
-
-echo "Previews generated!"
+2004-04-15 Angus Leeming <leeming@lyx.org>
+
+ * PreviewLoader.C (startLoading): change arguments passed to the
+ preview-generation script to include the foreground and background
+ colours.
+ (dumpPreamble): no longer write the foreground and background
+ colours to the latex file as PostScript specials.
+ (setConverter): consider only those 'to' formats that are
+ loadable natively by the GUI library, rather than all formats
+ for which a converter exists.
+ (setAscentFractions): re-written to parse much simplified metrics
+ file.
+
2004-04-13 Angus Leeming <leeming@lyx.org>
* PreviewLoader.C (dumpPreamble):
#include "PreviewLoader.h"
#include "PreviewImage.h"
+#include "GraphicsCache.h"
#include "buffer.h"
#include "converter.h"
// The conversion command.
ostringstream cs;
- cs << pconverter_->command << ' ' << latexfile << ' '
- << int(font_scaling_factor_) << ' ' << pconverter_->to;
+ cs << pconverter_->command << ' ' << pconverter_->to << ' '
+ << latexfile << ' ' << int(font_scaling_factor_) << ' '
+ << lyx_gui::hexname(LColor::preview) << ' '
+ << lyx_gui::hexname(LColor::background);
- string const command = "sh " + support::LibScriptSearch(cs.str());
+ string const command = support::LibScriptSearch(cs.str());
// Initiate the conversion from LaTeX to bitmap images files.
support::Forkedcall::SignalTypePtr
// Use the preview style file to ensure that each snippet appears on a
// fresh page.
os << "\n"
- << "\\usepackage[active,delayed,dvips,tightpage,showlabels,lyx]{preview}\n"
+ << "\\usepackage[active,delayed,dvips,showlabels,lyx]{preview}\n"
<< "\n";
-
- // This piece of PostScript magic ensures that the foreground and
- // background colors are the same as the LyX screen.
- string fg = lyx_gui::hexname(LColor::preview);
- if (fg.empty()) fg = "000000";
-
- string bg = lyx_gui::hexname(LColor::background);
- if (bg.empty()) bg = "ffffff";
-
- os << "\\AtBeginDocument{\\AtBeginDvi{%\n"
- << "\\special{!userdict begin/bop-hook{//bop-hook exec\n"
- << '<' << fg << bg << ">{255 div}forall setrgbcolor\n"
- << "clippath fill setrgbcolor}bind def end}}}\n";
}
{
string const from = "lyxpreview";
- Formats::FormatList::const_iterator it = formats.begin();
- Formats::FormatList::const_iterator end = formats.end();
+ typedef vector<string> FmtList;
+ typedef lyx::graphics::Cache GCache;
+ FmtList const loadableFormats = GCache::get().loadableFormats();
+ FmtList::const_iterator it = loadableFormats.begin();
+ FmtList::const_iterator const end = loadableFormats.end();
for (; it != end; ++it) {
- string const to = it->name();
+ string const to = *it;
if (from == to)
continue;
bool error = false;
- // Tightpage dimensions affect all subsequent dimensions
- int tp_ascent;
- int tp_descent;
+ int snippet_counter = 1;
+ while (!in.eof() && it != end) {
+ string snippet;
+ int id;
+ double ascent_fraction;
- int snippet_counter = 0;
- while (!in.eof()) {
- // Expecting lines of the form
- // Preview: Tightpage tp_bl_x tp_bl_y tp_tr_x tp_tr_y
- // Preview: Snippet id ascent descent width
- string preview;
- string type;
- in >> preview >> type;
+ in >> snippet >> id >> ascent_fraction;
if (!in.good())
// eof after all
break;
- error = preview != "Preview:"
- || (type != "Tightpage" && type != "Snippet");
+ error = snippet != "Snippet";
if (error)
break;
- if (type == "Tightpage") {
- int dummy;
- in >> dummy >> tp_descent >> dummy >> tp_ascent;
-
- error = !in.good();
- if (error)
- break;
-
- } else {
- int dummy;
- int snippet_id;
- int ascent;
- int descent;
- in >> snippet_id >> ascent >> descent >> dummy;
-
- error = !in.good() || ++snippet_counter != snippet_id;
- if (error)
- break;
-
- double const a = ascent + tp_ascent;
- double const d = descent - tp_descent;
+ error = id != snippet_counter;
+ if (error)
+ break;
- if (!support::float_equal(a + d, 0, 0.1))
- *it = a / (a + d);
+ *it = ascent_fraction;
- if (++it == end)
- break;
- }
+ ++snippet_counter;
+ ++it;
}
if (error) {