Encode Diagrams
Node.js
To compress our diagram with deflate algorithm, we are going to use pako — a high speed zlib port to JavaScript.
Once pako
is installed, you can use the deflate
method:
const pako = require('pako')
const diagramSource = `digraph G {
Hello->World
}`
const data = Buffer.from(diagramSource, 'utf8') (1)
const compressed = pako.deflate(data, { level: 9 }) (2)
const result = Buffer.from(compressed)
.toString('base64') (3)
.replace(/\+/g, '-').replace(/\//g, '_') (4)
console.log(result)
1 | Create a Buffer from the diagram source using the default UTF-8 encoding |
2 | Compress data with deflate algorithm using a compression level of 9 (best compression). |
3 | Create a Buffer from the compressed data and encode to Base64 |
4 | Replace + and / characters to make it "URL safe" |
JavaScript
We recommend encoding your diagram as UTF-8.
To do so, we can use TextEncoder
:
new TextEncoder('utf-8').encode(diagramSource)
Unfortunately TextEncoder
is not yet available on all browsers.
To workaround this issue, we can define a function to encode our diagram:
function textEncode(str) {
if (window.TextEncoder) {
return new TextEncoder('utf-8').encode(str);
}
var utf8 = unescape(encodeURIComponent(str));
var result = new Uint8Array(utf8.length);
for (var i = 0; i < utf8.length; i++) {
result[i] = utf8.charCodeAt(i);
}
return result;
}
The above code will use TextEncoder
if it’s available and otherwise it will use a fallback implementation.
Now that our diagram is encoded as UTF-8 in a Uint8Array
, we can use pako to compress it.
As a reminder, pako is a high speed zlib port to JavaScript:
<script src="https://unpkg.com/pako@1.0.10/dist/pako_deflate.min.js"></script>
To compress our diagram diagram we are going to use the deflate
method provided by pako
:
var diagramSource = 'digraph G { Hello->World }'
var data = textEncode(diagramSource) (1)
var compressed = pako.deflate(data, { level: 9, to: 'string' }) (2)
var result = btoa(compressed) (3)
.replace(/\+/g, '-').replace(/\//g, '_') (4)
console.log(result)
1 | Encode the diagram as UTF8 in a Uint8Array (using the textEncode function declared above) |
2 | Compress data with deflate algorithm using a compression level of 9 (best compression).
pako is available as a global variable. |
3 | Encode to Base64 using btoa global function |
4 | Replace + and / characters to make it "URL safe" |
Python
import sys;
import base64;
import zlib;
print(base64.urlsafe_b64encode(zlib.compress(sys.stdin.read(), 9)))
Java
package main;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.zip.Deflater;
public class Encode {
public static byte[] encode(String decoded) throws IOException {
return Base64.getUrlEncoder().encode(compress(decoded.getBytes()));
}
private static byte[] compress(byte[] source) throws IOException {
Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION);
deflater.setInput(source);
deflater.finish();
byte[] buffer = new byte[2048];
int compressedLength = deflater.deflate(buffer);
byte[] result = new byte[compressedLength];
System.arraycopy(buffer, 0, result, 0, compressedLength);
return result;
}
}
Kotlin
package main
import java.util.Base64
import java.util.zip.Deflater
object Encode {
fun encode(decoded: String): String =
String(Base64.getUrlEncoder().encode(compress(decoded.toByteArray())), Charsets.UTF_8)
private fun compress(source: ByteArray): ByteArray {
val deflater = Deflater()
deflater.setInput(source)
deflater.finish()
val bytesCompressed = ByteArray(Short.MAX_VALUE.toInt())
val numberOfBytesAfterCompression = deflater.deflate(bytesCompressed)
val returnValues = ByteArray(numberOfBytesAfterCompression)
System.arraycopy(bytesCompressed, 0, returnValues, 0, numberOfBytesAfterCompression)
return returnValues
}
}
Go
package main
import (
"bytes"
"compress/zlib"
"encoding/base64"
"github.com/pkg/errors"
)
// Encode takes a string and returns an encoded string in deflate + base64 format
func Encode(input string) (string, error) {
var buffer bytes.Buffer
writer, err := zlib.NewWriterLevel(&buffer, 9)
if err != nil {
return "", errors.Wrap(err, "fail to create the writer")
}
_, err = writer.Write([]byte(input))
writer.Close()
if err != nil {
return "", errors.Wrap(err, "fail to create the payload")
}
result := base64.URLEncoding.EncodeToString(buffer.Bytes())
return result, nil
}
PHP
<?php
function base64url_encode($data) {
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
function encode($data) {
return base64url_encode(gzcompress($data));
}
Tcl
# assuming the diagram code is stored in the variable named `diagramSource`:
puts [string map {+ - / _} [binary encode base64 [zlib compress $diagramSource]]]
Also, see the Tcl package dia2kroki which has procedures to both decode and encode the diagram source code and a GUI to create diagrams with a live preview.
C#
using System;
using System.IO;
using System.IO.Compression;
using System.Text;
class Program
{
// uses System.IO, System.IO.Compression
// Reference: https://yal.cc/cs-deflatestream-zlib/#code
static byte[] Deflate(byte[] data, CompressionLevel? level = null)
{
byte[] newData;
using (var memStream = new MemoryStream())
{
// write header:
memStream.WriteByte(0x78);
switch (level)
{
case CompressionLevel.NoCompression:
case CompressionLevel.Fastest:
memStream.WriteByte(0x01);
break;
case CompressionLevel.Optimal:
memStream.WriteByte(0xDA);
break;
default:
memStream.WriteByte(0x9C);
break;
}
// write compressed data (with Deflate headers):
using (var dflStream = level.HasValue
? new DeflateStream(memStream, level.Value)
: new DeflateStream(memStream, CompressionMode.Compress
)) dflStream.Write(data, 0, data.Length);
//
newData = memStream.ToArray();
}
// compute Adler-32:
uint a1 = 1, a2 = 0;
foreach (byte b in data)
{
a1 = (a1 + b) % 65521;
a2 = (a2 + a1) % 65521;
}
// append the checksum-trailer:
var adlerPos = newData.Length;
Array.Resize(ref newData, adlerPos + 4);
newData[adlerPos] = (byte)(a2 >> 8);
newData[adlerPos + 1] = (byte)a2;
newData[adlerPos + 2] = (byte)(a1 >> 8);
newData[adlerPos + 3] = (byte)a1;
return newData;
}
public static void Main(string[] args)
{
var compressedBytes = Deflate(Encoding.UTF8.GetBytes("digraph G {Hello->World}"));
var encodedOutput = Convert.ToBase64String(compressedBytes).Replace('+', '-').Replace('/', '_');
Console.WriteLine($"https://kroki.io/graphviz/svg/{encodedOutput}");
}
}
Please note that we cannot use we cannot use System.IO.Compression.DeflateStream because it does not include ZLIB header/trailer.
To learn more, please read: yal.cc/cs-deflatestream-zlib/#code
|