背景
最近发新文章,想画类图,习惯了用PlantUML,但是发现目前博客还不支持渲染PlantUML,于是想要开发支持一波~~
PlantUML简介
PlantUML是一个开源项目,它允许用户使用简单的文本描述语言来生成各种类型的 UML 图表。通过简单的文本描述,你可以创建类图、时序图、用例图、活动图等等。PlantUML 使用简单易懂的语法,让用户可以专注于图表的设计,而不必担心复杂的细节和语法。
PlantUML 的语法是基于文本的,你只需使用类似于代码的语法来描述你想要创建的图表,然后 PlantUML 会将其转换为相应的图像。这种文本描述的方式使得版本控制和团队协作变得非常简单,因为你只需管理文本文件而不是图像文件。
plantuml url生成规则
PlantUML 的 URL 生成规则如下:
- 基础 URL:PlantUML 的基础 URL 通常是
http://www.plantuml.com/plantuml/
。 - 文件类型:在基础 URL 后面加上文件类型(例如
png
、svg
等),以指定生成的图像类型。 - 编码方式:在文件类型后面,如果使用了编码方式(例如
deflate
或huffman
),需要在编码数据之前加上相应的编码标识。如果使用的是 Huffman 编码,需要在编码数据之前加上~1
,如果使用的是 DEFLATE 编码,需要在编码数据之前加上~D
。 - PlantUML 代码:在编码标识后面,添加经过编码的 PlantUML 代码。
举例来说,如果你要生成 PNG 格式的 PlantUML 图像,并且使用 Huffman 编码,URL 的格式如下:
http://www.plantuml.com/plantuml/png/~1[经过编码的PlantUML代码]
如果使用的是 DEFLATE 编码,则需要将 ~1
替换为 ~D
。
DEFLATE 和 Huffman
DEFLATE 和 Huffman 是指对 PlantUML 代码进行的压缩编码方式。
- DEFLATE 编码:DEFLATE 是一种常用的数据压缩算法,它结合了 LZ77 算法和哈夫曼编码。在 PlantUML 中,如果使用 DEFLATE 编码,生成的 URL 会以
~D
开头,表示压缩后的数据使用 DEFLATE 算法进行解压缩。 - Huffman 编码:Huffman 编码是一种用于数据压缩的算法,它根据数据的频率来构建变长编码,使得频率高的字符使用较短的编码,从而实现数据的压缩。在 PlantUML 中,如果使用 Huffman 编码,生成的 URL 会以
~1
开头,表示压缩后的数据使用 Huffman 编码进行解压缩。
这些编码方式通常用于将 PlantUML 代码转换为 URL,以便在网络上传输或嵌入到网页中。通过压缩和编码,可以减小数据的大小,从而提高传输效率。
DEFLATE 和 Huffman 编码各有优劣:
- DEFLATE 编码:
- 优点:DEFLATE 是一种通用且高效的压缩算法,它通常能够提供更好的压缩比。对于大型的 PlantUML 图,使用 DEFLATE 可能会生成更小的文件大小。
- 缺点:DEFLATE 编码的计算成本较高,需要更多的计算资源来进行压缩和解压缩。此外,DEFLATE 编码生成的 URL 可能会稍微长一些。
- Huffman 编码:
- 优点:Huffman 编码相对简单,生成的 URL 通常会比较短。如果你更关注 URL 的长度,或者需要在长度限制较严格的环境中使用,Huffman 编码可能更适合。
- 缺点:Huffman 编码可能会导致稍微较大的文件大小,因为它通常不如 DEFLATE 在压缩效率上表现得那么好。
因此,要选择哪种编码方式取决于你更看重的因素:是压缩效率还是 URL 长度。在大多数情况下,DEFLATE 编码可能是更好的选择,因为它提供了更好的压缩效率,尽管 URL 可能会略长一些。
编码规则
原则
例如下面的uml文本描述:
@startuml
Alice -> Bob: Authentication Request
Bob --> Alice: Authentication Response
@enduml
编码为:
Syp9J4vLqBLJSCfFib9mB2t9ICqhoKnEBCdCprC8IYqiJIqkuGBAAUW2rO0LOr5LN92VLvpA1G00
要实现这样的编码,文本图是:
- 以 UTF-8 编码
- 使用Deflate算法压缩
- 使用接近Base64 的转换以 ASCII 重新编码
为什么不使用 Base64?
主要原因是历史性的:这种格式最初并不是为了公开而创建的。现在想改变已经太晚了。但是,唯一的区别在于字符顺序。
在 base64 中,值 0-63 的映射数组是:
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
对于 PlantUML,值 0-63 的映射数组为:
0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_
代码实现
本文给出python、java和typescript三种实现方案,供参考
pyhton
def encode_plantuml(plantuml_text):
# Encode text in UTF-8
utf8_encoded = plantuml_text.encode('utf-8')
# Compress using Deflate algorithm
compressed = zlib.compress(utf8_encoded)
# Reencode in ASCII using a transformation close to base64
reencoded = base64.b64encode(compressed)
# Convert reencoded to a string if it's not already
# Convert reencoded to a string if it's not already
if isinstance(reencoded, dict):
reencoded_str = ''.join(reencoded.values())
else:
reencoded_str = reencoded
# Replace base64 characters according to PlantUML mapping array
encoded_plantuml = reencoded_str.translate(bytes.maketrans(
b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_"
))
return encoded_plantuml.decode('utf-8')
# Example usage:
plantuml_text = '''
@startuml
Alice -> Bob: Authentication Request
Bob --> Alice: Authentication Response
@enduml
'''
encoded_plantuml = encode_plantuml(plantuml_text)
print(encoded_plantuml)
Java
package com.izhxxx.utils;
import java.util.Base64;
import java.util.zip.Deflater;
/**
* @author :zhanghang(izhxxx@163.com)
*/
public class PlantUmlUtilTest {
public static String encodePlantUml(String plantumlText) {
// Encode text in UTF-8
byte[] utf8Encoded = plantumlText.getBytes();
// Compress using Deflate algorithm
Deflater deflater = new Deflater();
deflater.setInput(utf8Encoded);
deflater.finish();
byte[] compressed = new byte[utf8Encoded.length];
int compressedLength = deflater.deflate(compressed);
deflater.end();
// Reencode in base64
byte[] compressedData = new byte[compressedLength];
System.arraycopy(compressed, 0, compressedData, 0, compressedLength);
byte[] reencoded = Base64.getEncoder().encode(compressedData);
String reencodedString = new String(reencoded);
return translateBase64(reencodedString);
}
private static String translateBase64(String reencodedString) {
// Define mapping arrays
char[] base64Mapping = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
char[] plantUmlMapping = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_".toCharArray();
StringBuilder translated = new StringBuilder();
for (char c : reencodedString.toCharArray()) {
// Find index of character in base64 mapping
int index = indexOf(base64Mapping, c);
// If character found in base64 mapping, replace it with corresponding character from PlantUML mapping
if (index != -1) {
translated.append(plantUmlMapping[index]);
} else {
// If character not found in base64 mapping, keep it as it is
translated.append(c);
}
}
return translated.toString();
}
// Helper method to find index of character in array
private static int indexOf(char[] array, char c) {
for (int i = 0; i < array.length; i++) {
if (array[i] == c) {
return i;
}
}
return -1;
}
public static void main(String[] args) {
// Example usage
String plantUMLText = "@startuml\n" +
"Alice -> Bob: Authentication Request\n" +
"Bob --> Alice: Authentication Response\n" +
"@enduml";
String encodedPlantUML = encodePlantUml(plantUMLText);
System.out.println(encodedPlantUML);
}
}
typescript
import pako from 'pako'
function encodePlantUML(plantumlText: string): string {
// Encode text in UTF-8
const utf8Encoded: Uint8Array = new TextEncoder().encode(plantumlText)
console.log('utf8Encoded: ', Array.from(utf8Encoded))
// Compress using Deflate algorithm
const compressed: Uint8Array = pako.deflate(utf8Encoded)
console.log('compressed: ', Array.from(compressed))
// Reencode in base64
const reencoded: string = btoa(
String.fromCharCode.apply(null, Array.from(compressed))
)
// Replace base64 characters according to PlantUML mapping array
return reencoded.split('').map(translateBase64).join('')
}
function translateBase64(char: string): string {
const base64Mapping =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
const plantUmlMapping =
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_'
const index = base64Mapping.indexOf(char)
if (index !== -1) {
return plantUmlMapping[index]
}
return char
}
function calcPlantUmlSvgUrl(plantumlText: string): string {
const encode: string = encodePlantUML(plantumlText)
return `https://www.plantuml.com/plantuml/svg/~1${encode}`
}
export default calcPlantUmlSvgUrl
plantuml官方文档
https://plantuml.com/zh/text-encoding
仓库地址
代码开源在github
评论区
0/2048