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