markdown function
Implementation
Uint8List markdown(String md,
{double width = 612.0, double height = 792.0, double margin = 72.0}) {
final doc = TinePDF();
final textW = width - margin * 2;
const double bodySize = 11.0;
final items = <_MdItem>[];
List<String> wrap(String text, double size, double maxW) {
final words = text.split(' ');
final lines = <String>[];
String line = '';
for (var word in words) {
if (TinePDF.measureText(word, size) > maxW) {
if (line.isNotEmpty) {
lines.add(line);
line = '';
}
String chunk = '';
for (int i = 0; i < word.length; i++) {
String ch = word[i];
if (TinePDF.measureText(chunk + ch, size) > maxW &&
chunk.isNotEmpty) {
lines.add(chunk);
chunk = '';
}
chunk += ch;
}
line = chunk;
continue;
}
String test = line.isNotEmpty ? '$line $word' : word;
if (TinePDF.measureText(test, size) <= maxW) {
line = test;
} else {
if (line.isNotEmpty) {
lines.add(line);
}
line = word;
}
}
if (line.isNotEmpty) {
lines.add(line);
}
return lines.isNotEmpty ? lines : [''];
}
String prevType = 'start';
for (var raw in md.split('\n')) {
var line = raw.trimRight();
if (RegExp(r'^#{1,6}\s').hasMatch(line)) {
int rawLvl = RegExp(r'^#+').firstMatch(line)![0]!.length;
int lvl = rawLvl < 3 ? rawLvl : 3;
double size = [22.0, 16.0, 13.0][lvl - 1];
double before = prevType == 'start' ? 0.0 : [14.0, 12.0, 10.0][lvl - 1];
var lines = wrap(line.substring(rawLvl + 1), size, textW);
for (int i = 0; i < lines.length; i++) {
items.add(_MdItem(
text: lines[i],
size: size,
indent: 0,
spaceBefore: i == 0 ? before : 0,
spaceAfter: 4,
color: '#111111'));
}
prevType = 'header';
} else if (RegExp(r'^[-*]\s').hasMatch(line)) {
var lines = wrap(line.substring(2), bodySize, textW - 18);
for (int i = 0; i < lines.length; i++) {
items.add(_MdItem(
text: (i == 0 ? '- ' : ' ') + lines[i],
size: bodySize,
indent: 12,
spaceBefore: 0,
spaceAfter: 2));
}
prevType = 'list';
} else if (RegExp(r'^\d+\.\s').hasMatch(line)) {
String numStr = RegExp(r'^\d+').firstMatch(line)![0]!;
var lines = wrap(line.substring(numStr.length + 2), bodySize, textW - 18);
for (int i = 0; i < lines.length; i++) {
items.add(_MdItem(
text: (i == 0 ? '$numStr. ' : ' ') + lines[i],
size: bodySize,
indent: 12,
spaceBefore: 0,
spaceAfter: 2));
}
prevType = 'list';
} else if (RegExp(r'^(-{3,}|\*{3,}|_{3,})$').hasMatch(line)) {
items.add(_MdItem(
text: '',
size: bodySize,
indent: 0,
spaceBefore: 8,
spaceAfter: 8,
rule: true));
prevType = 'rule';
} else if (line.trim().isEmpty) {
if (prevType != 'start' && prevType != 'blank') {
items.add(_MdItem(
text: '',
size: bodySize,
indent: 0,
spaceBefore: 0,
spaceAfter: 4));
}
prevType = 'blank';
} else {
var lines = wrap(line, bodySize, textW);
for (var l in lines) {
items.add(_MdItem(
text: l,
size: bodySize,
indent: 0,
spaceBefore: 0,
spaceAfter: 4,
color: '#111111'));
}
prevType = 'para';
}
}
final pages = <_MdPage>[];
double y = height - margin;
var pg = <_MdItem>[];
var ys = <double>[];
for (var item in items) {
double needed = item.spaceBefore + item.size + item.spaceAfter;
if (y - needed < margin) {
if (pg.isNotEmpty) {
pages.add(_MdPage(pg, ys));
pg = [];
ys = [];
y = height - margin;
}
}
y -= item.spaceBefore;
ys.add(y);
pg.add(item);
y -= item.size + item.spaceAfter;
}
if (pg.isNotEmpty) pages.add(_MdPage(pg, ys));
if (pages.isEmpty) pages.add(_MdPage([], []));
for (var page in pages) {
doc.page(
width: width,
height: height,
build: (ctx) {
for (int i = 0; i < page.items.length; i++) {
var it = page.items[i];
if (it.rule) {
ctx.line(margin, page.ys[i], width - margin, page.ys[i],
'#e0e0e0', 0.5);
} else if (it.text.isNotEmpty) {
ctx.text(it.text, margin + it.indent, page.ys[i], it.size,
color: it.color ?? '#000000');
}
}
});
}
return doc.build();
}