• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#
2# Copyright (c) 2015-2018 Samuel Thibault <samuel.thibault@ens-lyon.org>
3#
4# Permission is hereby granted, free of charge, to any person obtaining a copy
5# of this software and associated documentation files (the "Software"), to deal
6# in the Software without restriction, including without limitation the rights
7# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8# copies of the Software, and to permit persons to whom the Software is
9# furnished to do so, subject to the following conditions:
10#
11#
12# The above copyright notice and this permission notice shall be included in
13# all copies or substantial portions of the Software.
14#
15#
16# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
19# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22# THE SOFTWARE.
23#
24
25# Get an attribute from the ppd file
26getAttribute () {
27  ATTRIBUTE=$1
28  VALUE=`grep "^\*$ATTRIBUTE:" "$PPD" | cut -d" " -f2-`
29  VALUE=${VALUE##\"}
30  VALUE=${VALUE%%\"}
31  printf "DEBUG: Attribute $ATTRIBUTE is '%s'\n" "$VALUE" >&2
32  printf "%s" "$VALUE"
33}
34
35# Get an option for the document: either default ppd attribute or user-provided value
36getOption () {
37  OPTION=$1
38  VALUE=$(getAttribute Default$OPTION)
39  printf "DEBUG: Default $OPTION is '%s'\n" "$VALUE" >&2
40
41  if [ -n "$OPTIONS" ]
42  then
43    # Case of the very first option
44    if [ -z "${OPTIONS/$OPTION=*}" ]
45    then
46      VALUE=${OPTIONS#$OPTION=}
47      VALUE=${VALUE%% *}
48      printf "DEBUG: Selected $OPTION is '%s'\n" "$VALUE" >&2
49    fi
50    # Case of other options
51    if [ -z "${OPTIONS/* $OPTION=*}" ]
52    then
53      VALUE=${OPTIONS##* $OPTION=}
54      VALUE=${VALUE%% *}
55      printf "DEBUG: Selected $OPTION is '%s'\n" "$VALUE" >&2
56    fi
57
58    # Boolean options
59    if [ -z "${OPTIONS/* $OPTION *}" ]
60    then
61      VALUE=True
62      printf "DEBUG: Selected $OPTION is '%s'\n" "$VALUE" >&2
63    fi
64    if [ -z "${OPTIONS/* no$OPTION *}" ]
65    then
66      VALUE=False
67      printf "DEBUG: Selected $OPTION is '%s'\n" "$VALUE" >&2
68    fi
69  fi
70
71  printf "%s" "$VALUE"
72}
73
74# Get an option for the document and check that it is a number
75getOptionNumber () {
76  OPTION=$1
77  VALUE=$(getOption $OPTION)
78  VALUE=${VALUE#Custom.}
79  case "$VALUE" in
80    [0-9]*) ;;
81    *) printf "ERROR: Option $OPTION must be a number, got '%s'\n" "$VALUE" >&2
82       exit 1
83       ;;
84  esac
85  printf "%s" "$VALUE"
86}
87
88# Printing options: number of copies and page ranges
89[ -z "$NB" ] && NB=1
90PAGERANGES=$(getOption page-ranges)
91
92#
93# Page size
94# Units in 100th of mm
95#
96
97# TODO: better handle imageable area
98PAGESIZE=$(getOption PageSize)
99case "$PAGESIZE" in
100  Legal)
101    PAGEWIDTH=21590
102    PAGEHEIGHT=35560
103    ;;
104  Letter)
105    PAGEWIDTH=21590
106    PAGEHEIGHT=27940
107    ;;
108  A3)
109    PAGEWIDTH=29700
110    PAGEHEIGHT=42000
111    ;;
112  A4)
113    PAGEWIDTH=21000
114    PAGEHEIGHT=29700
115    ;;
116  A4TF)
117    PAGEWIDTH=21000
118    PAGEHEIGHT=30480
119    ;;
120  A5)
121    PAGEWIDTH=14850
122    PAGEHEIGHT=21000
123    ;;
124  110x115)
125    PAGEWIDTH=27940
126    PAGEHEIGHT=29210
127    ;;
128  110x120)
129    PAGEWIDTH=27940
130    PAGEHEIGHT=30480
131    ;;
132  110x170)
133    PAGEWIDTH=27940
134    PAGEHEIGHT=43180
135    ;;
136  115x110)
137    PAGEWIDTH=29210
138    PAGEHEIGHT=27940
139    ;;
140  120x120)
141    PAGEWIDTH=30480
142    PAGEHEIGHT=30480
143    ;;
144  *)
145    printf "ERROR: Unknown page size '%s'\n" "$PAGESIZE" >&2
146    exit 1
147    ;;
148esac
149
150# Margins as announced by embosser
151HWMARGINS=$(getAttribute HWMargins)
152echo "DEBUG: HW margins are $HWMARGINS" >&2
153HWMARGIN_LEFT="${HWMARGINS%% *}"
154HWMARGINS="${HWMARGINS#* }"
155HWMARGIN_BOTTOM="${HWMARGINS%% *}"
156HWMARGINS="${HWMARGINS#* }"
157HWMARGIN_RIGHT="${HWMARGINS%% *}"
158HWMARGINS="${HWMARGINS#* }"
159HWMARGIN_TOP="${HWMARGINS%% *}"
160
161# Convert from points (1/72 of inch) to 1/100th of mm
162points2mm() {
163  # First get an integer so bash can compute
164  # I.e. convert to 10^15th of point
165  # Note: bash's integer computation will work until about 1000mm, that's plenty :)
166  POINTS="$1"
167  INT_POINTS="${POINTS%.*}"
168  if [ "$INT_POINTS" = "$POINTS" ]
169  then
170    FRAC_POINTS=000000000000000
171  else
172    FRAC_POINTS="${POINTS#*.}000000000000000"
173  fi
174  FRAC_POINTS="${FRAC_POINTS:0:15}"
175  FRAC_POINTS="$INT_POINTS$FRAC_POINTS"
176  # Then we can compute conversion
177  # We round up to be safe
178  FRAC_INCH=$((($FRAC_POINTS + 71) / 72))
179  FRAC_CM=$((($FRAC_INCH * 254 + 99) / 100))
180  HUNDRENDTH_MM=$((($FRAC_CM + 999999999999) / 1000000000000))
181  echo $HUNDRENDTH_MM
182}
183
184MARGIN_LEFT=$(points2mm "$HWMARGIN_LEFT")
185MARGIN_RIGHT=$(points2mm "$HWMARGIN_RIGHT")
186MARGIN_TOP=$(points2mm "$HWMARGIN_TOP")
187MARGIN_BOTTOM=$(points2mm "$HWMARGIN_BOTTOM")
188
189# Margins requested by user
190PAGE_LEFT=$(getOptionNumber page-left)
191[ "$?" = 0 ] || exit 1
192PAGE_LEFT=$(points2mm $PAGE_LEFT)
193PAGE_RIGHT=$(getOptionNumber page-right)
194[ "$?" = 0 ] || exit 1
195PAGE_RIGHT=$(points2mm $PAGE_RIGHT)
196PAGE_TOP=$(getOptionNumber page-top)
197[ "$?" = 0 ] || exit 1
198PAGE_TOP=$(points2mm $PAGE_TOP)
199PAGE_BOTTOM=$(getOptionNumber page-bottom)
200[ "$?" = 0 ] || exit 1
201PAGE_BOTTOM=$(points2mm $PAGE_BOTTOM)
202
203[ -n "$PAGE_LEFT"   -a "$MARGIN_LEFT"   -le "$PAGE_LEFT"   ] || PAGE_LEFT=$MARGIN_LEFT
204[ -n "$PAGE_RIGHT"  -a "$MARGIN_RIGHT"  -le "$PAGE_RIGHT"  ] || PAGE_RIGHT=$MARGIN_RIGHT
205[ -n "$PAGE_TOP"    -a "$MARGIN_TOP"    -le "$PAGE_TOP"    ] || PAGE_TOP=$MARGIN_TOP
206[ -n "$PAGE_BOTTOM" -a "$MARGIN_BOTTOM" -le "$PAGE_BOTTOM" ] || PAGE_BOTTOM=$MARGIN_BOTTOM
207
208echo "DEBUG: hard margins are left $MARGIN_LEFT right $MARGIN_RIGHT top $MARGIN_TOP bottom $MARGIN_BOTTOM" >&2
209echo "DEBUG: graphical margins are left $PAGE_LEFT right $PAGE_RIGHT top $PAGE_TOP bottom $PAGE_BOTTOM" >&2
210
211# This is the hardware-printable area
212PRINTABLEWIDTH=$(($PAGEWIDTH - $MARGIN_LEFT - $MARGIN_RIGHT))
213PRINTABLEHEIGHT=$(($PAGEHEIGHT - $MARGIN_TOP - $MARGIN_BOTTOM))
214
215echo "DEBUG: printable area is ${PRINTABLEWIDTH}x${PRINTABLEHEIGHT}" >&2
216
217#
218# Text spacing
219#
220
221TEXTDOTDISTANCE=$(getOptionNumber TextDotDistance)
222[ "$?" = 0 ] || exit 1
223case "$TEXTDOTDISTANCE" in
224  220) TEXTCELLDISTANCE=310 ;;
225  250) TEXTCELLDISTANCE=350 ;;
226  320) TEXTCELLDISTANCE=525 ;;
227  *)
228    printf "ERROR: Unknown text dot distance '%s'\n" "$TEXTDOTDISTANCE" >&2
229    exit 1
230    ;;
231esac
232
233TEXTDOTS=$(getOptionNumber TextDots)
234[ "$?" = 0 ] || exit 1
235LINESPACING=$(getOptionNumber LineSpacing)
236[ "$?" = 0 ] || exit 1
237
238# Cell dimension, including spacing
239TEXTCELLWIDTH=$(( $TEXTDOTDISTANCE + $TEXTCELLDISTANCE ))
240TEXTCELLHEIGHT=$(( $TEXTDOTDISTANCE * ($TEXTDOTS / 2 - 1) + $LINESPACING ))
241
242# Compute number of printable cells according to page width and height
243PRINTABLETEXTWIDTH=$(( ($PRINTABLEWIDTH + $TEXTCELLDISTANCE) / $TEXTCELLWIDTH  ))
244PRINTABLETEXTHEIGHT=$(( ($PRINTABLEHEIGHT + $LINESPACING) / $TEXTCELLHEIGHT ))
245
246if [ "$(getOption TopMargin)" = "" ]
247then
248  # No margin
249  TEXTWIDTH=$PRINTABLETEXTWIDTH
250  TEXTHEIGHT=$PRINTABLETEXTHEIGHT
251else
252  # Margins in cells
253  TOPMARGIN=$(getOptionNumber TopMargin)
254  [ "$?" = 0 ] || exit 1
255  BOTTOMMARGIN=$(getOptionNumber BottomMargin)
256  [ "$?" = 0 ] || exit 1
257  LEFTMARGIN=$(getOptionNumber LeftMargin)
258  [ "$?" = 0 ] || exit 1
259  RIGHTMARGIN=$(getOptionNumber RightMargin)
260  [ "$?" = 0 ] || exit 1
261  TEXTWIDTH=$(( $PRINTABLETEXTWIDTH - $LEFTMARGIN - $RIGHTMARGIN ))
262  TEXTHEIGHT=$(( $PRINTABLETEXTHEIGHT - $TOPMARGIN - $BOTTOMMARGIN ))
263fi
264
265# Filter that adds top and left margins on the fly, to be used while producing
266# BRF output.
267addmargins() {
268  NEWPAGE=""
269  if [ -n "$TOPMARGIN" ]; then
270    for I in $(seq 1 $TOPMARGIN) ; do
271      NEWPAGE="$NEWPAGE"$'\r'$'\n'
272    done
273  fi
274  NEWPAGESED=${NEWPAGE//$'\n'/\\$'\n'}
275  LEFTSPACES=""
276  if [ -n "$LEFTMARGIN" ]; then
277    for I in $(seq 1 $LEFTMARGIN) ; do
278      LEFTSPACES="$LEFTSPACES "
279    done
280  fi
281
282  echo -n "$NEWPAGE"
283  sed -e '$s/$//' \
284      -e "s/^\(\?\)\([^
285]\)/\1$LEFTSPACES\2/" \
286      -e "s//$NEWPAGESED/"
287  echo -n ""
288}
289
290#
291# Graphic spacing
292#
293
294# Compute number of printable cells according to page size
295GRAPHICDOTDISTANCE=$(getOptionNumber GraphicDotDistance)
296[ "$?" = 0 ] || exit 1
297
298# This is the total area we will send to the embosser
299TOTALGRAPHICWIDTH=$(( ( ($PRINTABLEWIDTH - 160) / $GRAPHICDOTDISTANCE ) / 2 * 2 ))
300TOTALGRAPHICHEIGHT=$(( ( ($PRINTABLEHEIGHT - 160) / $GRAPHICDOTDISTANCE ) / 4 * 4 ))
301
302echo "DEBUG: total graphical: ${TOTALGRAPHICWIDTH}x${TOTALGRAPHICHEIGHT}" >&2
303
304# This is how many dots we have to introduce to respect at least the software left+top margin
305GRAPHICHOFFSET=$(( ($PAGE_LEFT - $MARGIN_LEFT + $GRAPHICDOTDISTANCE - 1) / $GRAPHICDOTDISTANCE ))
306GRAPHICVOFFSET=$(( ($PAGE_TOP - $MARGIN_TOP + $GRAPHICDOTDISTANCE - 1) / $GRAPHICDOTDISTANCE ))
307
308echo "DEBUG: graphical offset: ${GRAPHICHOFFSET}x${GRAPHICVOFFSET}" >&2
309
310# This is the resulting actual margin
311GRAPHICLEFTMARGIN=$(( $MARGIN_LEFT + $GRAPHICHOFFSET * $GRAPHICDOTDISTANCE ))
312GRAPHICTOPMARGIN=$(( $MARGIN_TOP + $GRAPHICVOFFSET * $GRAPHICDOTDISTANCE ))
313
314echo "DEBUG: rounded graphical top-left corner margin: ${GRAPHICLEFTMARGIN}x${GRAPHICTOPMARGIN}" >&2
315
316# Then compute how many dots we can afford until reaching the software right+bottom margin
317GRAPHICWIDTH=$(( ( ( $PAGEWIDTH - $GRAPHICLEFTMARGIN - $PAGE_RIGHT ) - 160) / $GRAPHICDOTDISTANCE ))
318GRAPHICHEIGHT=$(( ( ( $PAGEHEIGHT - $GRAPHICTOPMARGIN - $PAGE_BOTTOM ) - 160) / $GRAPHICDOTDISTANCE ))
319
320echo "DEBUG: resulting graphical area: ${GRAPHICWIDTH}x${GRAPHICHEIGHT}" >&2
321
322#
323# Text translation
324#
325
326TABLESDIR=@TABLESDIR@
327echo "DEBUG: Liblouis table directory is $TABLESDIR" >&2
328
329getOptionLibLouis () {
330  OPTION=$1
331  VALUE=$(getOption $OPTION)
332
333  # Check validity of input
334  case "$VALUE" in
335    [-_.0-9A-Za-z]*) ;;
336    *) printf "ERROR: Option $OPTION must be a valid liblouis table name, got '%s'\n" "$VALUE" >&2
337       exit 1
338       ;;
339  esac
340
341  LOCALE=${LANG%@*}
342  LOCALE=${LOCALE%.*}
343  LANGUAGE=${LOCALE%_*}
344  COUNTRY=${LOCALE#*_}
345  LOUIS_LOCALE=$LANGUAGE-$COUNTRY
346
347  getLibLouisTableScore () {
348    GRADE="$1"
349    printf "DEBUG: looking for locale '%s' and grade '%s' \n" "$LOCALE" "$GRADE" >&2
350    # Try to select a good table from its metadata
351    selected=
352    selectedscore=0
353    for table in "$TABLESDIR/"*.tbl "$TABLESDIR/"*.ctb "$TABLESDIR/"*.utb; do
354      score=0
355      name=${table#$TABLESDIR/}
356
357      if grep -q "^#+locale:$LOUIS_LOCALE$" $table; then
358	printf "DEBUG: %s has correct locale %s\n" "$name" "$LOUIS_LOCALE" >&2
359	score=$((score + 15))
360      elif grep -q "^#+locale:$LANGUAGE$" $table; then
361	printf "DEBUG: %s has correct language %s\n" "$name" "$LANGUAGE" >&2
362	score=$((score + 10))
363      else
364	# Requested language is a must
365	continue
366      fi
367
368      if [ -n "$GRADE" ]; then
369	if grep -q "^#+grade:$GRADE$" $table || \
370	 ( [ "$GRADE" = 0 ] && ( grep -q "^#+contraction:no" $table || grep -q "^#+type:computer" $table ) ) \
371	then
372	  printf "DEBUG: %s has correct grade %s\n" "$name" "$GRADE" >&2
373	  score=$((score + 10))
374	else
375	  # Requested grade is a must
376	  continue
377	fi
378      fi
379
380      # Dot numbers are not always specified in liblouis :/
381      if grep -q "^#+dots:$TEXTDOTS$" $table || \
382	 ( [ "$TEXTDOTS" = 6 ] && grep -q "^#+grade:[1-3]" $table ) \
383      then
384	printf "DEBUG: %s has correct dots %s\n" "$name" "$TEXTDOTS" >&2
385	score=$((score + 2))
386      fi
387
388      if [ $score -gt $selectedscore ]; then
389	printf "DEBUG: %s has better score $score\n" "$name" >&2
390	selected=$name
391	selectedscore=$score
392      fi
393    done
394
395    echo $selected
396  }
397
398  # Check presence of table
399  case "$VALUE" in
400    None)
401      printf None
402      ;;
403    Locale)
404      selected=$(getLibLouisTableScore '')
405      # Try tagged tables before untagged ones
406      if [ -n "$selected" ]; then
407	printf "%s" "$selected"
408      elif [ -f "$TABLESDIR/$LOCALE.tbl" ]; then
409	printf "%s" "$LOCALE.tbl"
410      elif [ -f "$TABLESDIR/$LANGUAGE.tbl" ]; then
411	printf "%s" "$LANGUAGE.tbl"
412      else
413        printf "ERROR: Could not find $OPTION table with locale %s\n" "$LOCALE" >&2
414	printf None
415	exit 1
416      fi
417      ;;
418    Locale-g[0-3])
419      GRADE=${VALUE#Locale-g}
420      selected=$(getLibLouisTableScore $GRADE)
421      if [ -n "$selected" ]; then
422	printf "%s" "$selected"
423	exit 0
424      else
425	for i in "$TABLESDIR/$LOCALE.tbl" "$TABLESDIR/$LOCALE"*.tbl "$TABLESDIR/$LANGUAGE.tbl" "$TABLESDIR/$LANGUAGE"*.tbl
426	do
427	  if grep -q "^#+grade:$GRADE$" "$i"
428	  then
429	    printf "%s" "${i//*\/}"
430	    exit 0
431	  fi
432	done
433      fi
434      printf "ERROR: Could not find $OPTION table with locale %s and grade %s\n" "$LOCALE" "$GRADE" >&2
435      printf None
436      exit 1
437      ;;
438    HyphLocale)
439      if [ -f "$TABLESDIR/hyph_$LOCALE.dic" ]; then
440	printf "%s" "hyph_$LOCALE.dic"
441      elif [ -f "$TABLESDIR/hyph_$LANGUAGE.dic" ]; then
442	printf "%s" "hyph_$LANGUAGE.dic"
443      else
444        printf "WARN: Could not find $OPTION hyphenation table with locale %s\n" "$LOCALE" >&2
445	printf None
446      fi
447      ;;
448    *)
449      [ -f "$TABLESDIR/$VALUE".utb ] && VALUE="$VALUE".utb
450      [ -f "$TABLESDIR/$VALUE".ctb ] && VALUE="$VALUE".ctb
451      if [ ! -f "$TABLESDIR/$VALUE" ]
452      then
453        printf "ERROR: Could not find $OPTION table '%s'\n" "$VALUE" >&2
454        exit 1
455      fi
456
457      printf "%s" "$VALUE"
458      ;;
459  esac
460}
461
462LIBLOUIS1=$(getOptionLibLouis LibLouis)
463[ "$?" = 0 ] || exit 1
464LIBLOUIS2=$(getOptionLibLouis LibLouis2)
465[ "$?" = 0 ] || exit 1
466LIBLOUIS3=$(getOptionLibLouis LibLouis3)
467[ "$?" = 0 ] || exit 1
468LIBLOUIS4=$(getOptionLibLouis LibLouis4)
469[ "$?" = 0 ] || exit 1
470
471echo "DEBUG: Table1 $LIBLOUIS1" >&2
472echo "DEBUG: Table2 $LIBLOUIS2" >&2
473echo "DEBUG: Table3 $LIBLOUIS3" >&2
474echo "DEBUG: Table4 $LIBLOUIS4" >&2
475
476[ "$LIBLOUIS1" != None ] && LIBLOUIS_TABLES="$LIBLOUIS1"
477[ "$LIBLOUIS2" != None ] && LIBLOUIS_TABLES="${LIBLOUIS_TABLES:+$LIBLOUIS_TABLES,}$LIBLOUIS2"
478[ "$LIBLOUIS3" != None ] && LIBLOUIS_TABLES="${LIBLOUIS_TABLES:+$LIBLOUIS_TABLES,}$LIBLOUIS3"
479[ "$LIBLOUIS4" != None ] && LIBLOUIS_TABLES="${LIBLOUIS_TABLES:+$LIBLOUIS_TABLES,}$LIBLOUIS4"
480
481#
482# Checking for presence of tools
483#
484checkTool() {
485  TOOL=$1
486  PACKAGE=$2
487  USE=$3
488  if ! type $TOOL > /dev/null
489  then
490    printf "ERROR: The $PACKAGE package is required for $USE\n" >&2
491    exit 1
492  fi
493}
494