Gnuplot пакуем выходной svg в один файл

image-loader.svg
Старый мем на новый лад.

Предыдущая моя статья «Gnuplot и с чем его едят» получила большой отклик и даже была переведена на несколько языков (видел на медиуме, встречал на немецком). Поэтому, раз тема актуальная, решил продолжить.

У меня появилась задача получать данные, а затем на удалённом сервере строить по ним графики и отправлять по почте. Причём графики должны иметь возможность отключать оси, приближать отдельные области графика, включать-выключать сетку. И вы знаете, gnuplot умеет выдавать подобные графики. Он даёт их в формате html или svg. Но вот незадача, вместе с этим файлом надо тащить ещё багаж данных в виде кучи javascript-файликов, картинок, css (в случае html), что сильно сужает применимость при отправке их по почте.

В результате, нашёл-таки решение данной проблемы и продемонстрирую её решение на примере svg-файла, для html будет аналогично. Поскольку нет возможности привести график реальных данных, где было использовано это решение, в пример взял шуточный мем про студентов

Постановка задачи


В gnuplot есть прекрасная возможность генерировать графики в html или svg. Они получаются интерактивными и их можно встраивать в ваши веб-страницы или в файлы отчётов. Существует очень хорошая статья на хабре по данной теме: Gnuplot на домашней страничке. Где весьма подробно было разобрано, как формировать данные в html. Посмотреть, как будут выглядеть подобные графики вживую можно на демонстрационной странице для svg и для html. Рекомендую попробовать пощёлкать мышью, повыделять правой кнопкой мыши области графика, включить-выключить сетку и т.п. Сразу станут ясны возможности.

В чём же собственно говоря кроется проблема? Проблема достаточно простая, для работы svg и html нужны дополнительные файлы. То есть, мы формируем отчёт в красивом файле, не глядя его отправляем, а на том конце он не работает и вместо картинок крестики.

Проще говоря, если открыть полученный html, то там можно будет увидеть следующие строки:





')
        elif 'grid.png' in string:
            start = string.find(IMG_STR_START) + len(IMG_STR_START)
            end = string.find(IMG_STR_END) + len(IMG_STR_END)
            img_in_base64 = '''
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAADFBMVEX///8AAAAAAAAAAAD4jAJN
AAAAA3RSTlMA7Psi0lOqAAAAHElEQVR4AWPABpjgCLsAExMzAjEOMS2MCIQFAABtJADJXH3sOwAA
AABJRU5ErkJggg=='''
            svg_out.append(string[0:start] +
                           'data:image/png;charset=utf-8;base64, ' +
                           img_in_base64 + "'" + string[end:])
        else:
            svg_out.append(string)
    with open('output.svg', 'w') as f:
        f.write('\n'.join(svg_out))


Вначале идут паттерны поиска джаваскрипта и картинки, далее открываем svg-файл и превращаем его в список строк и по этому списку строк бежим. Если у нас не попадает в паттерн, то мы сохраняем строку в новый список, а если попадает, то модифицируем строку и добавляем её в новый список.

После всего сохраняем этот список в файл (можно даже формировать новый).

▍ Функция main


Всё просто:

  • Получаем исходные данные
  • Формируем метки по Y
  • Сохраняем данные в промежуточный файл
  • Строим график
  • Обновляем svg
def main():
    data_for_plot = get_remote_data()
    ylabel = set_ytics_label()
    datafile = create_file_to_plot(data_for_plot)
    title = 'Уровень знаний в течение семестра'
    plot_graph(datafile, ylabel, title)
    os.unlink(datafile)
    update_svg()

Вывод


Для меня было некоторым открытием, что такой известный, старейший инструмент, как gnuplot, который делает потрясающие графики, имеет столько недочётов и требуется делать вот такие вот костыли, чтобы формировать качественный выходной файл. То что не поддерживается кириллица (да и вообще unicode), в html выходном файле — это вообще печаль. Зато это хороший повод вписать своё имя в историю великого пакета. Всё это справедливо для пакета: Version 5.0 patchlevel 3 last modified 2016–02–21. Возможно на данный момент эти проблемы решены.

Касательно html, код выглядит практически также, разве что картинок больше, и их удобнее не хардкодить в тексте, а преобразовывать на лету в base64:

import base64

with open(pngfilename) as pngfile:
    base64img = base64.b64encode(pngfile.read())
html_new.append(string[0:start] + 'data:image/png;base64, '
                + base64img.decode("utf-8) + string[end:])


Ну и следует помнить, что там ещё есть css и несколько js-файлов.
Умышленно не привожу целиком код для html, чтобы был повод размять мозги.

Код этого проекта обитает вот тут: github.com/dlinyj/gnuplot_svg_update

Попробую вставить svg-картинку на хабр с гита, посмотрю что получится.

image

Конечно, скрипты на хабре не работают (и это правильно), но картинка грузится.
Меня могут попрекнуть, мол подобрал бы инструмент, с которым бы не было таких проблем. Всё это хорошо, когда ты админ на машине. А когда у тебя есть суровый питон, запрет на доустановку пакетов и gnuplot, то приходится выкручиваться.

P/s есть ещё парочка хостингов svg, тут и тут (надо регаться).

image-loader.svg

© Habrahabr.ru