markdown function

Uint8List markdown(
  1. String md, {
  2. double width = 612.0,
  3. double height = 792.0,
  4. double margin = 72.0,
})

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();
}