четверг, 10 ноября 2016 г.

Python: округление

Выяснилась интересная особенность. В python 2.7 округление выполняется как учили в школе, где число x.5 округляется до (x+1):
>>> print round(0.5), round(1.5)
1.0 2.0

В python 3.3 поведение этой функции изменилось:
>>> print ( round(0.5), round(1.5) )
0 2
то есть, во-первых, по умолчанию возвращается целое число, и, главное, выполняется bankers rounding (банковское округление, в котором x.5 округляется до ближайшего четного).

Как эту печаль обойти штатными срадствами, не нашел, поэтому пока пользуюсь таким костыликом:
def round27 (number, ndigits=0):
    result = round(number, ndigits)
    if abs(result) >= abs(number): return result
    delta = abs(number) - abs(result)
    if ndigits >= 0:
        multiplier = 10.0**(ndigits+1)
        if abs(number)*multiplier-abs(result)*multiplier == 5.0:
            result = number + (delta if number >= 0 else -delta)
    else:
        multiplier = 10.0**(-ndigits-1)
        if abs(number)-abs(result) == 5.0*multiplier:
            result = number + (delta if number >= 0 else -delta)
    return result

Вообще, оказывается, есть даже стандарт IEEE 754, в котором все эти правила округления указаны. Надо глянуть, может, и ГОСТ найдётся.

UPD. Нашелся вот такой документ: СТ СЭВ 543-77 Числа. Правила записи и округления
Тут как раз округление по правилу "половины дальше от нуля".

четверг, 3 ноября 2016 г.

Linux: Как пережать PDF

Предположим, у нас есть некий pdf-документ, который надо разослать по электронной почте N адресатам. Казалось бы, не проблема: создавай N писем с вложенным документом и отправляй, благо, слепить на коленке софт под это дело - задача нехитрая.

Основная проблема - размер этого самого pdf-документа. При массовой рассылке объем переданной через почтовый сервер информации возрастёт в N раз, поэтому возникает логичное желание ужать пересылаемый документ как только можно. Сам документ получен со сканера, то есть фактически является увесистым набором изображений. С другой стороны, к счастью, тут качество не сильно важно, достаточно, чтобы текст читался, да подписи с печатями были различимы. Короче, задача сформулировалась так: получить из многостраничного сканированного документа черно-белый факс убогого качества, но читаемый, в формате pdf.

Никуда не деться, взял в качестве образца пятистраничный файл input.pdf размером 923801 байт и принялся экспериментировать.

Должен сказать, что самый правильный путь решения проблемы: воспользоваться Adobe Acrobat-ом. Эта чудесная софтина содержит команду "Optimize scanned PDF", которая при определенных настройках каким-то волшебным образом умудряется превращать исходный файл в жалкие 97416 байтов! К сожалению, в данный момент лицензионного Adobe Acrobat-а под рукой нет, поэтому приходится выкручиваться тем, что есть.

Первый вариант, который советуют знающие люди - использовать ghostscript. Соответствующая команда выглядит так:
gs \
    -sDEVICE=pdfwrite \
    -dCompatibilityLevel=1.4 \
    -dPDFSETTINGS=/screen \
    -dNOPAUSE \
    -dQUIET \
    -dBATCH \
    -sOutputFile=output.pdf 
    input.pdf
Результат получается вполне читаемым, но объем не сильно уменьшился и составил 548870 байтов.

Второй найденный на просторах инета вариант реализует идею уменьшить количество цветов до минимума:
#!/bin/sh

inFile=input.pdf
outFile=output.pdf

pdfimages "$inFile" scan1
for a in scan1*.p*m; do
   convert -depth 2 -colorspace gray $a ${a%.*}.tiff
done

tiffcp scan1*.tiff "scan1-.tiff"
tiff2pdf "scan1-.tiff" -o "$outFile" -p A4 -F -z

rm scan1*.*
Как видим, здесь используется несколько утилит: ImageMagick с его хитрой командой convert, а также LibTIFF (утилиты tiffcp и tiff2pdf). Результат: 380120 байтов. Неплохо, но далеко от рекорда.

Далее возникла мысль вообще перекодировать pdf в монохромный режим. Сделать это просто, достаточно чуть-чуть модифицировать тело цикла в предыдущем варианте:
#!/bin/sh

inFile=input.pdf
outFile=output.pdf

pdfimages "$inFile" scan1
for a in scan1*.p*m; do
    convert -white-threshold 100% -monochrome   $a   ${a%.*}.tiff
done

tiffcp scan1*.tiff "scan1-.tiff"
tiff2pdf "scan1-.tiff" -o "${outFile%.*}.${outFile##*.}" -p A4 -F -z

rm scan1*.*
Результат всё ещё читаем и имеет размер 252072 байта.

Ещё один вариант, который стоило попытаться сделать - это уменьшить в два раза размеры отсканированных изображений перед тем, как переводить их в монохром. Идея показалась здравой, но, будучи применена "в лоб", дала совершенно нечитаемый результат.

Можно попробовать ещё один подход: попытаться векторизовать содержащиеся в исходном файле изображения. Оказывается, есть специальная утилита для этого дела: Potrace. Правда, если применить её так:
#!/bin/sh

inFile=input.pdf
outFile=output.pdf

pdfimages "$inFile" scan1
for a in scan1*.p*m; do
    mkbitmap -f 2 -s 1 -x -t 0.8  $a  -o ${a%.*}-step1.${a##*.}
done

potrace -b pdf -t 5 scan1-*-step1.p*m -o "$outFile"

rm scan1*.*
то результат хоть и выглядит забавно, но оказался совсем тяжелым, 1440526 байтов.

Однако нет худа без добра: в дистрибутиве Potrace обнаружилась замечательная утилита mkbitmap, которая позволяет подправить изображение при преобразовании в монохром. Если её полезные свойства скомбинировать с предыдущими вариантами, то получается такой скрипт:
#!/bin/sh

inFile=input.pdf
outFile=output.pdf

pdfimages "$inFile" scan1
for a in scan1*.p*m; do
   mkbitmap -f 2 -s 1 -x -t 0.8          $a                -o ${a%.*}-step1.pbm
   convert -depth 2 -colorspace gray     ${a%.*}-step1.pbm    ${a%.*}-step2.tiff
   convert -resize 50%                   ${a%.*}-step2.tiff   ${a%.*}-step3.tiff
done

tiffcp scan1*-step3.tiff "scan1-.tiff"
tiff2pdf "scan1-.tiff" -o "$outFile" -p A4 -F -z

rm scan1*.*
и это оказался лучший результат: 180664 байта при всё ещё различимом тексте.

Вывод: конечно, до рекорда Adobe Acrobat-а ещё очень далеко, но, как говорится, забесплатно и уксус сладкий. Буду пока пользоваться этим.

вторник, 1 ноября 2016 г.

ECMAScript 6

Ух ты!.. Немного питона, немного си-шарпа, немного пхп...
Обзор базовых возможностей ES6

Навскидку понравились классы, области видимости переменных через let/const, строковые шаблоны, многострочные литералы, итераторы, вскрытия массивов с помощью троеточия.

Хочу попробовать!