Pull to refresh

JavaScript редактор диаграмм, который открывает диаграммы из PNG картинок (open source)

Reading time3 min
Views7.7K
Рис 1. dgrm.net умеет открывать диаграммы из PNG картинок
Рис 1. dgrm.net умеет открывать диаграммы из PNG картинок

dgrm.net | GitHub

<< предыдущая статья | следующая статья >>

dgrm.net - это редактор диаграмм, с прицелом на трансформацию в карту знаний.

Отличительные особенности:

  • аскетичность,

  • работает на телефонах (одно из немногих web-решений),

  • открытый исходный код.

В процессе разработки появляются интересные моменты. Статья про один из таких моментов: чтение данных из PNG. Исходный код для использования в своих проектах прилагается.

Зачем открывать диаграммы из PNG изображений

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

Все редакторы используют свои файлы проекта. Но это же неудобно:

  • нет превьюшек,

  • при пересылке изображения, надо еще и исходник пересылать.

Удобнее иметь картинку диаграммы, которую при необходимости можно отредактировать.

Глядя на рисунок 1 можно предположить что используется стеганография, или распознавание изображений. На самом деле гораздо проще, и без хаков - формат PNG поддерживает хранение дополнительной информации, например метки времени, имени автора, или любой другой.

dgrm.net записывает в png-файлы JSON с данными диаграммы.

PNG Chunks

Формат PNG хорошо описан на Хабре в статье “PNG — not GIF!”.

Основной момент:

  • png-файлы состоят из блоков, которые называются chunk-и,

  • в файл можно добавлять свои chunk-и.

Рис 2. Структура одного chunk-a PNG (источник “PNG — not GIF!”)
Рис 2. Структура одного chunk-a PNG (источник “PNG — not GIF!”)

Для своих данных имя chunk-a (например “dgRm”):

  • Можно придумать любое, лишь бы оно не было зарезервировано;

  • Длина имени строго 4 латинских буквы;

  • Регистр букв имеет значение. Для самодельных chunk-ов ставьте все буквы в нижнем регистре, а 3-ю в верхнем.

Таким образом, для хранения строки JSON внутри файла PNG нужно добавить в файл свой chunk.

Чтение/запись PNG chunk-ов на JavaScript в браузере

Чтение chunk-а

Chunk-и следуют друг за другом, нужный chunk находится перебором.

Алгоритм поиска chunk-а (листинг 1):

  1. берем название первого chunk;

  2. если название не совпадает с искомым:
    a. берем длину chunk (первые 4 байта см. рис 2);
    b. зная длину chunk смещаем курсор на начало следующего chunk.

  3. повторяем 1 и 2 пока не найдем нужный chunk или 'IEND' (конец файла).

/**
 * @param {ArrayBuffer} pngData
 * @param {number} chunkNameUint32 chunk name as Uint32
 * @returns {DataView | null} chunk data
 */
function chunkGet(pngData, chunkNameUint32) {
    const dataView = new DataView(pngData, 8); // 8 byte - png signature
 
    let chunkPosition = 0;
    let chunkUint = dataView.getUint32(4);
    let chunkLenght;
    while (chunkUint !== 1229278788) { // last chunk 'IEND'
        chunkLenght = dataView.getUint32(chunkPosition);
        if (chunkUint === chunkNameUint32) {
            return new DataView(pngData, chunkPosition + 16, chunkLenght);
        }
        chunkPosition = chunkPosition + 12 + chunkLenght;
        chunkUint = dataView.getUint32(chunkPosition + 4);
    }
    return null;
}

Листинг 1. Функция поиска chunk-а

Краткая справка:

В JavaScript интересный способ работы с бинарными данными.

Цитата:
Объект ArrayBuffer используется для работы с бинарными данными. Он представляет собой ссылку на поток "сырых" двоичных данных, однако работать с ними напрямую возможности не даёт.
developer.mozilla.org

Для чтения данных можно обернуть их в DataView. DataView позволяет прочитать данные в любой позиции в виде числа (с помощью методов getInt8(), getUint32() и др.).

Запись chunk-а

Для записи chunk-а, нужно вставить новый chunk в цепочку. Если chunk с данным именем уже есть - его нужно заменить.

Реализацию можно посмотреть на GitHub - функция chunkSet.

Исходный код

Функции работы с PNG chunk-ами вынесены в один файл. У файла нет зависимостей, поэтому его можно просто скопировать в свой проект.
png-chunk-utils.js

Пример использования:

// Запись chunk-а, на выходе новый blob
const newPngBlob = await pngChunkSet(
    // png-изображение
    pngBlob,
    // имя chunk-а
    'dgRm',
    // значение chunk-а: строка в виде массива byte
    new TextEncoder().encode('...'));
 
 
// чтение chuk-а
const dgrmChunkVal = await pngChunkGet(newPngBlob, 'dgRm');
const str = new TextDecoder().decode(dgrmChunkVal);

Листинг 2. Вызов функций записи и чтения PNG chunk-ов

Другие статьи про dgrm.net

Как поддержать проект

  • Начните использовать, расскажите что думаете.
    Любым способом: комментарии, личные сообщения, на GitHub.
    Все читаю, веду список предложений.

  • Расскажите знакомым.

  • Ставьте звездочки на GitHub.

Tags:
Hubs:
Total votes 36: ↑36 and ↓0+36
Comments20

Articles