On Mon, 1 Dec 2003, Xavier Fernández i Marín wrote: > Is there any application to export MySQL results of queries to LaTeX > tables? (preferably under GPL)
Xavier, I have a hack for one of my applications. You can use this as a starting point, but you will have to adjust directories. It converts to TeX (not LaTeX) but prints very fancy tables (within my scope). Note: it has to fit on one page with (using landscape mode). There is a limit on the numer of columns which can be printed, depending on what data they contain. Beware: this script can be dead slow. Someone should recode this in perl. Thomas Spahni -- filter: MySQL, Query Shell script: #!/bin/bash ######################################## # printtable ######################################## # # usage: printtable <filename> [<title_to_print>] # # use a select query to generate desired <filename> # example: # echo "SELECT * FROM mytable;" | mysql --batch mydbase > filename # DB_VERSION='0.9.20 of 2002-01-07' PROG_VERSION='1.0.0' # # print nice listings from tab delimited table data # ## -------------------------------------------------------------- ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License ## as published by the Free Software Foundation; either version 2 ## of the License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## -------------------------------------------------------------- # # In a first run everything is deleted except delimiting TABs # and these TABs are converted to "X"s; # Then sort rows and eliminate duplicates; # One single line should remain, otherwise the # dataset is inconsistent. # For the time we do not handle escaped TAB's within data # or TAB's within quoted datafields ;-(( # They appear as \t and will be treated as a space. # ---- Section 1 # some user definable values # how to print # get a default; use global variable if set # adjust argument to PRINTOPT as desired if test -n "$PRINTER" ; then PRINTOPT="-P$PRINTER" else PRINTOPT="-Plp" fi # Temporary space; must be writeable for all users! TMPDIR=/tmp # A place to keep user's output and a personal copy # of listing.hyph PRINT_DIR=~/.myprinttable # Where the TeX templates can be found # This is possibly changed by make during installation TEMPLATES=/var/tmp/db # printtable will load macros for some multilanguage characters # by default and a package named german.sty. # Make sure that the required hyphenation tables are loaded. TEXPROG=tex # Style file to process national language TEXSTYLE=german.sty # Space distribution mode; # If it doesn't fit otherwise this may be set to "tight" #SPACEMODE="normal" SPACEMODE="narrow" #SPACEMODE="tight" # Turning this on will print some internal data on the screen # Values: on / off DEBUG=on # ----- END of user configuration if test "$1" = -h -o "$1" = --help -o "$1" = "?" -o "$1" = "-?" \ -o "$1" = -V -o "$1" = -v -o "$1" = --version ; then echo "printtable Version $PROG_VERSION" echo "Print pretty formatted tables from TAB delimited data" echo "" echo "usage: printtable -h | --help | ? | -? | -V | -v | --version" echo " printtable <file> [\"Title for the table\"]" exit 0 fi # ---- Section 2 Check for required files/directories # test for temporary space if ! test -w "$TMPDIR" ; then echo "[$0] ERROR: can not write to tmp dir ${TMPDIR}; giving up" exit 1 fi # If this user has no private print directory it is created # with permissions restricted to the user. Then writability # is tested for. if ! test -d "$PRINT_DIR" ; then mkdir -p -m 0700 "$PRINT_DIR" fi if ! test -w "$PRINT_DIR" ; then echo "[$0] ERROR: can not write to personal dir ${$PRINT_DIR}; giving up" exit 1 fi # Having a running TeX installation is a must # let's check for it if ! $TEXPROG --version >/dev/null 2>&1 ; then echo "[$0] ERROR: no executable $TEXPROG found; giving up" exit 1 fi # See if there is the TeX style file at the right place if ! test -r "${PRINT_DIR}/$TEXSTYLE" ; then if test -r "${TEMPLATES}/$TEXSTYLE" ; then cp "${TEMPLATES}/$TEXSTYLE" "${PRINT_DIR}/$TEXSTYLE" echo "installing $TEXSTYLE in $PRINT_DIR" else echo "[$0] WARNING: no TeX style $TEXSTYLE found; continuing ..." fi fi # See if there is a local list of additional hyphenations if ! test -r "${PRINT_DIR}/listing.hyph" ; then # no personal hyphenations if test -r "${TEMPLATES}/listing.hyph" ; then # but systemwide hyphen patterns; # use these. cp ${TEMPLATES}/listing.hyph ${PRINT_DIR}/aux.hyph else echo "% This file contains additional hyphenation templates" \ > "${PRINT_DIR}/listing.hyph" fi else # there are personal hyphenations if test -r "${TEMPLATES}/listing.hyph" ; then # and systemwide hyphen patterns; # join these together. cp ${TEMPLATES}/listing.hyph ${PRINT_DIR}/aux.hyph cat ${PRINT_DIR}/listing.hyph | sed -e "/^%/ d" \ >> ${PRINT_DIR}/aux.hyph else cp ${PRINT_DIR}/listing.hyph ${PRINT_DIR}/aux.hyph fi fi # ---- Section 3 do the job # user may give a title for this table as second command # line argument. if test -n "$2" ; then PRINTTITLE="$2" else PRINTTITLE="$1" fi # create a somewhat unique filename for temporary storage TMPFILE="lstg.$$" TARGET=${PRINT_DIR}/${TMPFILE}.tex if test $DEBUG = "on" ; then if test -n "$1" ; then INSOURCE="$1" else INSOURCE=testdata.tab fi else if test -n "$1" ; then INSOURCE="$1" else echo "[$0] ERROR: missing file argument" exit 1 fi fi cat $INSOURCE | sed -e "s/[^ ]//g" -e "s/ /X/g" \ | sort -u > $TMPDIR/${TMPFILE}.tmp NTLINES=$(cat $TMPDIR/${TMPFILE}.tmp | wc -l) if test $(($NTLINES)) -gt $((1)) ; then echo "Dataset is inconsistent; number of TABs varies" echo "between rows" cat $TMPDIR/${TMPFILE}.tmp exit 1 fi NOFFLDS=$(( $(cat $TMPDIR/${TMPFILE}.tmp | wc -L) + 1)) rm $TMPDIR/${TMPFILE}.tmp #echo "$NOFFLDS fields in dataset" # Columns containing no data eat up paper space for the # column name but are otherwise quite useless; # they are silently discarded. FLDNUM=1 NONEMPTYFLDS="" REMAINFLDS=0 while test $(($FLDNUM)) -le $(($NOFFLDS)) ; do NRW=$(cat "$INSOURCE" \ | sed "1 d" \ | cut -f $FLDNUM \ | sed "/^$/d" \ | wc -l) if test $(($NRW)) -gt $((0)) ; then NONEMPTYFLDS="${NONEMPTYFLDS}, ${FLDNUM}" REMAINFLDS=$(($REMAINFLDS + 1)) fi FLDNUM=$(($FLDNUM + 1)) done NOFFLDS=$REMAINFLDS NONEMPTYFLDS=$(echo "$NONEMPTYFLDS" | sed -e "s/^,//" -e "s/ //g") if test $DEBUG = "on" ; then echo "$REMAINFLDS non empty columns are remaining" echo $NONEMPTYFLDS fi cat "$INSOURCE" | cut -f "$NONEMPTYFLDS" > $TMPDIR/${TMPFILE}.tmp # convert reserved TeX characters cat $TMPDIR/${TMPFILE}.tmp | sed \ -e "s/\\\\t/ /g" \ -e "s/\\\\/|\\\\\\\\backslash|/g" \ -e "s/#/\\\\\\\\#/g" \ -e "s/%/\\\\\\\\%/g" \ -e "s/_/\\\\\\\\_/g" \ -e "s/\\$/\\\\\\\\$/g" \ -e "s/&/\\\\\\\\\\&/g" \ -e "s/\"/''/g" \ -e "s/|/$/g" > $TMPDIR/${TMPFILE}.cln # stuff the first row into a variable; we use this later COLUMNNAMES="$(cat $TMPDIR/${TMPFILE}.cln | head -n 1)" # keep the rest of this table and discard row #1 sed -e "1 d" <$TMPDIR/${TMPFILE}.cln >$TMPDIR/${TMPFILE}.tmp rm -f $TMPDIR/${TMPFILE}.cln mv $TMPDIR/${TMPFILE}.tmp $TMPDIR/${TMPFILE}.cln # Let's have a look at how many data rows there are DATAROWS=$(cat $TMPDIR/${TMPFILE}.cln | wc -l) # Define constants for the space distribution modes # if test $SPACEMODE = "tight" ; then # Baselength reduced/normal BLRN=12 BLNN=16 BLAN=$(( $BLNN - $BLRN )) elif test $SPACEMODE = "narrow" ; then # Baselength reduced/normal BLRN=14 BLNN=18 BLAN=$(( $BLNN - $BLRN )) else #$SPACEMODE = "normal" # Baselength reduced/normal BLRN=15 BLNN=20 BLAN=$(( $BLNN - $BLRN )) fi # Now things get tricky: we try to allocate useful paper space # for all columns and then guess the appropriate fontsize. # The result is a relative distribution of space. # # Columns with data using consistently the same space for every row # will be given just this space. For each column the maximum length # and an average length is determined. Empty fields and the header # don't count. if test $DEBUG = "on" ; then echo -e "Rows\t%-full\tchrs\tmaxline\taverage\tbox" fi FLDNUM=1 WIDTHTBL="" while test $(($FLDNUM)) -le $(($NOFFLDS)) ; do NRW=$(cat $TMPDIR/${TMPFILE}.cln \ | cut -f $FLDNUM \ | sed "/^$/d" \ | wc -l -L -c) NPROWS=$(echo $NRW | sed -e "s/ .*//") TOTALSIZE=$(echo $NRW | sed -e "s/^[^ ]* *//" -e "s/ .*$//") MAXLINE=$(echo $NRW | sed -e "s/^.* //") # # here comes the formula to determine the relative size # of space given to this column for printing # For each row subtract one character (LF) from the total # and divide the number of characters by the number of rows # # A column with empty data in most rows gets a lower # priority for paperspace. Here we determine how many # percent of all rows are non-empty for this column. # EMPTYFACTOR=$(( $NPROWS * 100 / $DATAROWS )) # adjust "constant" values according to EMPTYFACTOR if test $(($EMPTYFACTOR)) -le $((50)) ; then BASELENGTH=$(( $BLRN + ($BLAN * $EMPTYFACTOR / 100) )) else BASELENGTH=$BLNN fi AVERAGE=$(( ($TOTALSIZE - $NPROWS) / $NPROWS )) if test $(($AVERAGE)) -eq $(($MAXLINE)) ; then # columns in each row have the same length # give them full needed space unless they are too wide if test $(($AVERAGE)) -gt $(($BASELENGTH)) ; then CHRWIDTH=$BASELENGTH else CHRWIDTH=$AVERAGE fi else # not all rows of this column have the same length if test $(($MAXLINE)) -lt $(($BASELENGTH)) ; then # these are all relatively short columns # without very long maxline values if test $(($MAXLINE - $AVERAGE)) -lt $((6)) ; then # short column, average close to maxline # give full width CHRWIDTH=$MAXLINE else # some maxlines have to be folded CHRWIDTH=$(( $AVERAGE + (($MAXLINE - $AVERAGE) / 3) )) fi else # some long maxlines occur if test $(($AVERAGE)) -gt $(($BASELENGTH)) ; then # even the average length is larger than baselength # this has to be folded CHRWIDTH=$(( $BASELENGTH + ( $AVERAGE / 8 ) )) else # average length is small but some very long # lines may occur CHRWIDTH=$(( $AVERAGE + (($MAXLINE - $AVERAGE) / 6) )) fi fi fi WIDTHTBL="${WIDTHTBL} ${CHRWIDTH}" if test $DEBUG = "on" ; then echo -e \ "$NPROWS\t$EMPTYFACTOR\t$TOTALSIZE\t$MAXLINE\t$AVERAGE\t$CHRWIDTH" fi FLDNUM=$(($FLDNUM + 1)) done if test $DEBUG = "on" ; then echo fi # Add up all field widths attributed for one line LINEWIDTH=0 for IW in $WIDTHTBL ; do LINEWIDTH=$(( $LINEWIDTH + $IW )) done # landscape page width is 266.2 mm or 266200 thousands # This is how much space 100 characters (numbers) take # scale is mm * 1000 NINEPTSPACE=162550 EIGHTPTSPACE=149373 SEVENHALFPT=140036 SEVENPTSPACE=130701 NINEPTFONT=niness EIGHTPTFONT=eightss SEVENHALFFONT=sevenhalfss SEVENPTFONT=sevenss NINEPTSKIP=9.5pt EIGHTPTSKIP=8.5pt SEVENHALFSKIP=8pt SEVENPTSKIP=7.5pt PSPACE=266200 BORDER=1200 # we increase BORDERS by 0.8 pt to account for two \vrule's # within the \hbox but outside the \vbox'es # beware: you have to change this value of 800 for any \vrule # other than 0.4pt wide ZERODOTEIGHTPT=$(( 800 * 2540 / 7227 )) BSPACE=$((2 * $BORDER)) BORDERS=$(( ($NOFFLDS-1) * $BSPACE + $ZERODOTEIGHTPT )) USPACE=$(($PSPACE - $BORDERS)) # Double border space in mm with decimal point BSPACEMM=$(echo "$BSPACE" | sed -e "s/[0-9][0-9][0-9]$/.&/") # calculate actual widths for column boxes # short 1 or 2 character boxes get an extra bonus as they # may contain wide characters like M or W # This space must be taken away from the largest boxes # # Set aside a certain percentage of space as glue for # redistribution. It is given back "per column" thus # favouring short columns. GLUEPART=10 # % (in percent of line width) GLUESPACE=$(( $USPACE * $GLUEPART / 100 )) USERSPACE=$(( $USPACE - $GLUESPACE )) GLUEDISTR=$(( $GLUESPACE / $NOFFLDS )) BOXES="" ABOX=0 for IW in $WIDTHTBL ; do BW=$(( ($USERSPACE * $IW / $LINEWIDTH) + $GLUEDISTR )) ABOX=$(( $ABOX + $BW )) BOXES="${BOXES} $BW" done # try to determine the fontsizes automatically # # default is the smallest fontsize FONTSIZE="$SEVENPTFONT" COUNTER=1 FONTNUMBER=0 for FS in $SEVENPTSPACE $SEVENHALFPT $EIGHTPTSPACE $NINEPTSPACE ; do REQLENGTH=$(( $LINEWIDTH * $FS / 100 )) RELLENGTH=$(( $REQLENGTH * 100 / $USPACE )) if test $COUNTER = 1 ; then FIRSTRELLENGTH=$RELLENGTH fi if test $(($RELLENGTH)) -lt $((100)) ; then FONTNUMBER=$COUNTER EFFRELLENGTH=$RELLENGTH EFFREQLENGTH=$REQLENGTH fi COUNTER=$(( $COUNTER + 1 )) done if test $FONTNUMBER = 0 ; then echo "Cannot fit data on one page; giving up" echo "(One row takes ${FIRSTRELLENGTH}\% of available space)" exit 1 fi COUNTER=1 for FS in $SEVENPTFONT $SEVENHALFFONT $EIGHTPTFONT $NINEPTFONT ; do if test $(($COUNTER)) -eq $(($FONTNUMBER)) ; then FONTSCALE=$FS fi COUNTER=$(( $COUNTER + 1 )) done case $FONTSCALE in niness) FPT="9 pt" ;; eightss) FPT="8 pt" ;; sevenhalfss) FPT="7.5 pt" ;; sevenss) FPT="7 pt" ;; esac echo "using font size $FPT" COUNTER=1 for FS in $SEVENPTSKIP $SEVENHALFSKIP $EIGHTPTSKIP $NINEPTSKIP ; do if test $(($COUNTER)) -eq $(($FONTNUMBER)) ; then BASESKIP=$FS fi COUNTER=$(( $COUNTER + 1 )) done if test "$DEBUG" = "on" ; then echo -e "Total line length is :\t$LINEWIDTH characters" echo -e "Using font of size :\t$FPT" echo -e "Used percent of horiz. space:\t${EFFRELLENGTH}%" echo -e "Total requ. boxspace :\t$EFFREQLENGTH" echo -e "Total distributed boxes :\t$ABOX" echo -e "Space used up by borders :\t$BORDERS" echo -e "Making up this line width :\t$(($ABOX+$BORDERS))" fi # insert decimal points into $BOXES BOXES=$(echo "$BOXES" | sed -e "s/[0-9]$/& /" \ -e "s/[0-9][0-9][0-9] /.&/g") # and into TOTALLINE TOTALLINE=$(echo $(($ABOX+$BORDERS)) | sed -e "s/[0-9]$/& /" \ -e "s/[0-9][0-9][0-9] /.&/g") #echo $BOXES #read DOCONT # cat ${TEMPLATES}/listing.hdr.tex > $TARGET cat > $TARGET <<-'XXX' % listing.hdr.tex for dbtool \message{A4 landscape paper size}% % % comment the following two lines if you do not have or use % the german.sty package available from % http://www.tex.ac.uk/tex-archive/language/german/ \input german.sty \selectlanguage{german} % % define page geometry for European A4 paper in landscape mode \vsize 159.2mm %210mm - 1in * 2 for margins % we steal 10mm of horizontal space from each margin % \hsize 246.2mm %297mm - 1in * 2 for margins \hsize 266.2mm \hoffset=-10mm \parindent 0pt \parskip 0pt \tolerance 10000 \raggedright \raggedbottom \overfullrule = 0 pt % disable slugs \hyphenpenalty=0 \doublehyphendemerits=0 \finalhyphendemerits=0 \hfuzz=2pt % \newbox\upperstrutbox \setbox\upperstrutbox=\hbox{\vrule height8.5pt depth0pt width0pt} \def\upperstrut{\relax\ifmmode\copy\upperstrutbox \else\unhcopy\upperstrutbox\fi} % \newbox\lowerstrutbox \setbox\lowerstrutbox=\hbox{\vrule height0pt depth4pt width0pt} \def\lowerstrut{\relax\ifmmode\copy\lowerstrutbox \else\unhcopy\lowerstrutbox\fi} % \hyphenation{neu-tra-le} % % Define special characters for direct use \catcode`Ç=\active \defÇ{\c{C}} \catcode`ü=\active \defü{\"u} \catcode`é=\active \defé{\'e} \catcode`â=\active \defâ{\^a} \catcode`ä=\active \defä{\"a} \catcode`à=\active \defà{\`a} \catcode`å=\active \defå{{\aa}} \catcode`ç=\active \defç{\c{c}} \catcode`ê=\active \defê{\^e} \catcode`ë=\active \defë{\"e} \catcode`è=\active \defè{\`e} \catcode`ï=\active \defï{{\"\i}} \catcode`î=\active \defî{{\^\i}} \catcode`ì=\active \defì{{\`\i}} \catcode`Ä=\active \defÄ{\"A} \catcode`Å=\active \defÅ{{\AA}} \catcode`É=\active \defÉ{\'E} \catcode`æ=\active \defæ{{\ae}} \catcode`Æ=\active \defÆ{{\AE}} \catcode`ô=\active \defô{\^o} \catcode`ö=\active \defö{\"o} \catcode`ò=\active \defò{\`o} \catcode`û=\active \defû{\^u} \catcode`ù=\active \defù{\`u} \catcode`Ö=\active \defÖ{\"O} \catcode`Ü=\active \defÜ{\"U} \catcode`á=\active \defá{\'a} \catcode`í=\active \defí{{\'\i}} \catcode`ó=\active \defó{\'o} \catcode`ú=\active \defú{\'u} \catcode`ñ=\active \defñ{\~n} \catcode`Ñ=\active \defÑ{\~N} \catcode`ß=\active \defß{{\ss}} \def\quest{?} % % quite a small fontsize to ensure that > 190 characters % make up one line \font\ssi=cmssi9 % slanted for titles \font\niness=cmss9 % normal sans serif \font\eightss=cmss8 % small sans serif \font\sevenhalfss=cmss8 at 7.5pt % semi-tiny sans serif \font\sevenss=cmss8 at 7pt % tiny sans serif % \footline={\ssi\hfil - \the\pageno\ -\hfil} % XXX echo "\\baselineskip=$BASESKIP" >> $TARGET echo "\\$FONTSCALE" >> $TARGET if test -n "$PRINTTITLE" ; then TITLETEXT=$(echo "$PRINTTITLE" | sed \ -e "s/_/\\\\_/g") echo "\\headline={\ssi\hfil $TITLETEXT\hfil}" >> $TARGET fi # mark additional hyphens # this copies your data around and is dead slow! # if test $DEBUG = on ; then echo "Adding hyphenation patterns :" ; fi while read HYPH ; do if test -z "$HYPH" ; then HYPH='%' ; fi if test $(echo "$HYPH" | cut -c 1) != '%' ; then HYPHA=$(echo "$HYPH" | sed -e "s/|.*$//") HYPHB=$(echo "$HYPH" | sed -e "s/^[^|]*|//") cat $TMPDIR/${TMPFILE}.cln | sed -e "s|$HYPHA|$HYPHB|g" \ > $TMPDIR/${TMPFILE}.tmp rm -f $TMPDIR/${TMPFILE}.cln mv $TMPDIR/${TMPFILE}.tmp $TMPDIR/${TMPFILE}.cln fi if test "$DEBUG" = "on" ; then echo -n "." ; fi done < ${PRINT_DIR}/aux.hyph #mv $TMPDIR/${TMPFILE}.tmp $TMPDIR/${TMPFILE}.cln if test "$DEBUG" = "on" ; then echo "" ; fi (IFS=' ' # TAB is no longer a field separator, just space and newline if test $DEBUG = "on" ; then echo "Converting data to TeX" fi while read DATALINE ; do COLNBR=1 echo "\\hrule\\noindent\\hbox{\\upperstrut\\vrule" >> $TARGET for IW in $BOXES ; do echo "$DATALINE" | cut -f $COLNBR \ | sed -e "s/^.*$/\ \\\\vtop{\\\\hsize ${IW}mm &\\\\lowerstrut\\\\hfill}\\\\relax/" >> $TARGET if test $(($COLNBR)) -lt $(($NOFFLDS)) ; then echo "\\vtop{\\hsize ${BSPACEMM}mm\\hfill}\\relax" >> $TARGET fi COLNBR=$(($COLNBR + 1)) done echo "\\hfill\\vrule}" >> $TARGET if test $DEBUG = "on" ; then echo -n "." fi done <$TMPDIR/${TMPFILE}.cln ) echo "\\noindent\\hrule\\par" >> $TARGET if test $DEBUG = "on" ; then echo fi # cat ${TEMPLATES}/listing.end.tex >> $TARGET cat >> $TARGET <<-'XXX' % listing.end.tex \vfill\eject \end XXX (cd $PRINT_DIR && $TEXPROG $TARGET >/dev/null 2>&1) TARGETLOG=$(echo $TARGET | sed -e "s/tex$/log/") TARGETDVI=$(echo $TARGET | sed -e "s/tex$/dvi/") TARGETPS=$(echo $TARGET | sed -e "s/tex$/ps/") echo "could not hyphenate:" # report overfull boxes only cat $TARGETLOG | sed \ -e "/^Underfull/,/^\\[\\]/ d" \ -e "/^\\[\\]/ !d" \ -e "s/^\\[\\][^ ]* //" \ -e "s/| *$//" \ | sort -u # Print a message what has been written # like: Output written on lstg.30419.dvi (6 pages, 37884 bytes). tail -n 1 $TARGETLOG read -e -p "Send to printer with 'lpr $PRINTOPT' (Y/N) " RESP case "$RESP" in y|Y) ( cd $PRINT_DIR && dvips -t landscape $TARGETDVI >/dev/null 2>&1 ) lpr $PRINTOPT $TARGETPS ;; *) echo "Aborting print" ;; esac if test "$DEBUG" = "off" ; then rm -f $TMPDIR/${TMPFILE}.cln \ $TARGET $TARGETLOG $TARGETDVI $TARGETPS fi sync exit 0 -- MySQL General Mailing List For list archives: http://lists.mysql.com/mysql To unsubscribe: http://lists.mysql.com/[EMAIL PROTECTED]