diff --git a/cbits/autolink.c b/cbits/autolink.c
--- a/cbits/autolink.c
+++ b/cbits/autolink.c
@@ -149,6 +149,7 @@
   size_t max_rewind = cmark_inline_parser_get_offset(inline_parser);
   uint8_t *data = chunk->data + max_rewind;
   size_t size = chunk->len - max_rewind;
+  int start = cmark_inline_parser_get_column(inline_parser);
 
   size_t link_end;
 
@@ -186,6 +187,13 @@
   text->as.literal =
       cmark_chunk_dup(chunk, (bufsize_t)max_rewind, (bufsize_t)link_end);
   cmark_node_append_child(node, text);
+
+  node->start_line = text->start_line =
+    node->end_line = text->end_line =
+    cmark_inline_parser_get_line(inline_parser);
+
+  node->start_column = text->start_column = start - 1;
+  node->end_column = text->end_column = cmark_inline_parser_get_column(inline_parser) - 1;
 
   return node;
 }
diff --git a/cbits/cmark_export.h b/cbits/cmark_export.h
--- a/cbits/cmark_export.h
+++ b/cbits/cmark_export.h
@@ -7,7 +7,7 @@
 #  define CMARK_NO_EXPORT
 #else
 #  ifndef CMARK_EXPORT
-#    ifdef libcmark_gfm_EXPORTS
+#    ifdef libcmark_EXPORTS
         /* We are building this library */
 #      define CMARK_EXPORT __attribute__((visibility("default")))
 #    else
diff --git a/cbits/cmark_extension_api.h b/cbits/cmark_extension_api.h
--- a/cbits/cmark_extension_api.h
+++ b/cbits/cmark_extension_api.h
@@ -666,6 +666,12 @@
 CMARK_EXPORT
 delimiter *cmark_inline_parser_get_last_delimiter(cmark_inline_parser *parser);
 
+CMARK_EXPORT
+int cmark_inline_parser_get_line(cmark_inline_parser *parser);
+
+CMARK_EXPORT
+int cmark_inline_parser_get_column(cmark_inline_parser *parser);
+
 /** Convenience function to scan a given delimiter.
  *
  * 'left_flanking' and 'right_flanking' will be set to true if they
diff --git a/cbits/inlines.c b/cbits/inlines.c
--- a/cbits/inlines.c
+++ b/cbits/inlines.c
@@ -23,9 +23,9 @@
 static const char *RIGHTSINGLEQUOTE = "\xE2\x80\x99";
 
 // Macros for creating various kinds of simple.
-#define make_str(mem, s) make_literal(mem, CMARK_NODE_TEXT, s)
-#define make_code(mem, s) make_literal(mem, CMARK_NODE_CODE, s)
-#define make_raw_html(mem, s) make_literal(mem, CMARK_NODE_HTML_INLINE, s)
+#define make_str(subj, sc, ec, s) make_literal(subj, CMARK_NODE_TEXT, sc, ec, s)
+#define make_code(subj, sc, ec, s) make_literal(subj, CMARK_NODE_CODE, sc, ec, s)
+#define make_raw_html(subj, sc, ec, s) make_literal(subj, CMARK_NODE_HTML_INLINE, sc, ec, s)
 #define make_linebreak(mem) make_simple(mem, CMARK_NODE_LINEBREAK)
 #define make_softbreak(mem) make_simple(mem, CMARK_NODE_SOFTBREAK)
 #define make_emph(mem) make_simple(mem, CMARK_NODE_EMPH)
@@ -46,7 +46,10 @@
 typedef struct subject{
   cmark_mem *mem;
   cmark_chunk input;
+  int line;
   bufsize_t pos;
+  int block_offset;
+  int column_offset;
   cmark_reference_map *refmap;
   delimiter *last_delim;
   bracket *last_bracket;
@@ -63,17 +66,22 @@
 
 static int parse_inline(cmark_parser *parser, subject *subj, cmark_node *parent, int options);
 
-static void subject_from_buf(cmark_mem *mem, subject *e, cmark_strbuf *buffer,
-                             cmark_reference_map *refmap);
+static void subject_from_buf(cmark_mem *mem, int line_number, int block_offset, subject *e,
+                             cmark_strbuf *buffer, cmark_reference_map *refmap);
 static bufsize_t subject_find_special_char(subject *subj, int options);
 
 // Create an inline with a literal string value.
-static CMARK_INLINE cmark_node *make_literal(cmark_mem *mem, cmark_node_type t,
+static CMARK_INLINE cmark_node *make_literal(subject *subj, cmark_node_type t,
+                                             int start_column, int end_column,
                                              cmark_chunk s) {
-  cmark_node *e = (cmark_node *)mem->calloc(1, sizeof(*e));
-  cmark_strbuf_init(mem, &e->content, 0);
+  cmark_node *e = (cmark_node *)subj->mem->calloc(1, sizeof(*e));
+  cmark_strbuf_init(subj->mem, &e->content, 0);
   e->type = (uint16_t)t;
   e->as.literal = s;
+  e->start_line = e->end_line = subj->line;
+  // columns are 1 based.
+  e->start_column = start_column + 1 + subj->column_offset + subj->block_offset;
+  e->end_column = end_column + 1 + subj->column_offset + subj->block_offset;
   return e;
 }
 
@@ -86,14 +94,15 @@
 }
 
 // Like make_str, but parses entities.
-static cmark_node *make_str_with_entities(cmark_mem *mem,
+static cmark_node *make_str_with_entities(subject *subj,
+                                          int start_column, int end_column,
                                           cmark_chunk *content) {
-  cmark_strbuf unescaped = CMARK_BUF_INIT(mem);
+  cmark_strbuf unescaped = CMARK_BUF_INIT(subj->mem);
 
   if (houdini_unescape_html(&unescaped, content->data, content->len)) {
-    return make_str(mem, cmark_chunk_buf_detach(&unescaped));
+    return make_str(subj, start_column, end_column, cmark_chunk_buf_detach(&unescaped));
   } else {
-    return make_str(mem, *content);
+    return make_str(subj, start_column, end_column, *content);
   }
 }
 
@@ -131,23 +140,30 @@
   return cmark_chunk_buf_detach(&buf);
 }
 
-static CMARK_INLINE cmark_node *make_autolink(cmark_mem *mem, cmark_chunk url,
-                                              int is_email) {
-  cmark_node *link = make_simple(mem, CMARK_NODE_LINK);
-  link->as.link.url = cmark_clean_autolink(mem, &url, is_email);
+static CMARK_INLINE cmark_node *make_autolink(subject *subj,
+                                              int start_column, int end_column,
+                                              cmark_chunk url, int is_email) {
+  cmark_node *link = make_simple(subj->mem, CMARK_NODE_LINK);
+  link->as.link.url = cmark_clean_autolink(subj->mem, &url, is_email);
   link->as.link.title = cmark_chunk_literal("");
-  cmark_node_append_child(link, make_str_with_entities(mem, &url));
+  link->start_line = link->end_line = subj->line;
+  link->start_column = start_column + 1;
+  link->end_column = end_column + 1;
+  cmark_node_append_child(link, make_str_with_entities(subj, start_column + 1, end_column - 1, &url));
   return link;
 }
 
-static void subject_from_buf(cmark_mem *mem, subject *e, cmark_strbuf *buffer,
-                             cmark_reference_map *refmap) {
+static void subject_from_buf(cmark_mem *mem, int line_number, int block_offset, subject *e,
+                             cmark_strbuf *buffer, cmark_reference_map *refmap) {
   int i;
   e->mem = mem;
   e->input.data = buffer->ptr;
   e->input.len = buffer->size;
   e->input.alloc = 0;
+  e->line = line_number;
   e->pos = 0;
+  e->block_offset = block_offset;
+  e->column_offset = 0;
   e->refmap = refmap;
   e->last_delim = NULL;
   e->last_bracket = NULL;
@@ -268,7 +284,7 @@
 
   if (endpos == 0) {      // not found
     subj->pos = startpos; // rewind
-    return make_str(subj->mem, openticks);
+    return make_str(subj, subj->pos, subj->pos, openticks);
   } else {
     cmark_strbuf buf = CMARK_BUF_INIT(subj->mem);
 
@@ -277,7 +293,7 @@
     cmark_strbuf_trim(&buf);
     cmark_strbuf_normalize_whitespace(&buf);
 
-    return make_code(subj->mem, cmark_chunk_buf_detach(&buf));
+    return make_code(subj, startpos, endpos - openticks.len - 1, cmark_chunk_buf_detach(&buf));
   }
 }
 
@@ -434,7 +450,7 @@
     contents = cmark_chunk_dup(&subj->input, subj->pos - numdelims, numdelims);
   }
 
-  inl_text = make_str(subj->mem, contents);
+  inl_text = make_str(subj, subj->pos - numdelims, subj->pos - 1, contents);
 
   if ((can_open || can_close) && (!(c == '\'' || c == '"') || smart)) {
     push_delimiter(subj, c, can_open, can_close, inl_text);
@@ -450,7 +466,7 @@
   advance(subj);
 
   if (!smart || peek_char(subj) != '-') {
-    return make_str(subj->mem, cmark_chunk_literal("-"));
+    return make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("-"));
   }
 
   while (smart && peek_char(subj) == '-') {
@@ -483,7 +499,7 @@
     cmark_strbuf_puts(&buf, ENDASH);
   }
 
-  return make_str(subj->mem, cmark_chunk_buf_detach(&buf));
+  return make_str(subj, startpos, subj->pos - 1, cmark_chunk_buf_detach(&buf));
 }
 
 // Assumes we have a period at the current position.
@@ -493,12 +509,12 @@
     advance(subj);
     if (peek_char(subj) == '.') {
       advance(subj);
-      return make_str(subj->mem, cmark_chunk_literal(ELLIPSES));
+      return make_str(subj, subj->pos - 3, subj->pos - 1, cmark_chunk_literal(ELLIPSES));
     } else {
-      return make_str(subj->mem, cmark_chunk_literal(".."));
+      return make_str(subj, subj->pos - 2, subj->pos - 1, cmark_chunk_literal(".."));
     }
   } else {
-    return make_str(subj->mem, cmark_chunk_literal("."));
+    return make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("."));
   }
 }
 
@@ -655,6 +671,10 @@
   }
   cmark_node_insert_after(opener_inl, emph);
 
+  emph->start_line = emph->end_line = subj->line;
+  emph->start_column = opener_inl->start_column + subj->column_offset;
+  emph->end_column = closer_inl->end_column + subj->column_offset;
+
   // if opener has 0 characters, remove it and its associated inline
   if (opener_num_chars == 0) {
     cmark_node_free(opener_inl);
@@ -681,11 +701,11 @@
   if ((parser->backslash_ispunct ? parser->backslash_ispunct : cmark_ispunct)(nextchar)) {
     // only ascii symbols and newline can be escaped
     advance(subj);
-    return make_str(subj->mem, cmark_chunk_dup(&subj->input, subj->pos - 1, 1));
+    return make_str(subj, subj->pos - 2, subj->pos - 1, cmark_chunk_dup(&subj->input, subj->pos - 1, 1));
   } else if (!is_eof(subj) && skip_line_end(subj)) {
     return make_linebreak(subj->mem);
   } else {
-    return make_str(subj->mem, cmark_chunk_literal("\\"));
+    return make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("\\"));
   }
 }
 
@@ -701,10 +721,10 @@
                              subj->input.len - subj->pos);
 
   if (len == 0)
-    return make_str(subj->mem, cmark_chunk_literal("&"));
+    return make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("&"));
 
   subj->pos += len;
-  return make_str(subj->mem, cmark_chunk_buf_detach(&ent));
+  return make_str(subj, subj->pos - 1 - len, subj->pos - 1, cmark_chunk_buf_detach(&ent));
 }
 
 // Clean a URL: remove surrounding whitespace and surrounding <>,
@@ -767,7 +787,7 @@
     contents = cmark_chunk_dup(&subj->input, subj->pos, matchlen - 1);
     subj->pos += matchlen;
 
-    return make_autolink(subj->mem, contents, 0);
+    return make_autolink(subj, subj->pos - 1 - matchlen, subj->pos - 1, contents, 0);
   }
 
   // next try to match an email autolink
@@ -776,7 +796,7 @@
     contents = cmark_chunk_dup(&subj->input, subj->pos, matchlen - 1);
     subj->pos += matchlen;
 
-    return make_autolink(subj->mem, contents, 1);
+    return make_autolink(subj, subj->pos - 1 - matchlen, subj->pos - 1, contents, 1);
   }
 
   // finally, try to match an html tag
@@ -784,7 +804,7 @@
   if (matchlen > 0) {
     contents = cmark_chunk_dup(&subj->input, subj->pos - 1, matchlen + 1);
     subj->pos += matchlen;
-    return make_raw_html(subj->mem, contents);
+    return make_raw_html(subj, subj->pos - 1 - matchlen, subj->pos - 1, contents);
   }
 
   if (liberal_html_tag) {
@@ -792,12 +812,12 @@
     if (matchlen > 0) {
       contents = cmark_chunk_dup(&subj->input, subj->pos - 1, matchlen + 1);
       subj->pos += matchlen;
-      return make_raw_html(subj->mem, contents);
+      return make_raw_html(subj, subj->pos - 1 - matchlen, subj->pos - 1, contents);
     }
   }
 
   // if nothing matches, just return the opening <:
-  return make_str(subj->mem, cmark_chunk_literal("<"));
+  return make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("<"));
 }
 
 // Parse a link label.  Returns 1 if successful.
@@ -912,13 +932,13 @@
   opener = subj->last_bracket;
 
   if (opener == NULL) {
-    return make_str(subj->mem, cmark_chunk_literal("]"));
+    return make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("]"));
   }
 
   if (!opener->active) {
     // take delimiter off stack
     pop_bracket(subj);
-    return make_str(subj->mem, cmark_chunk_literal("]"));
+    return make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("]"));
   }
 
   // If we got here, we matched a potential link/image text.
@@ -996,12 +1016,15 @@
   // If we fall through to here, it means we didn't match a link:
   pop_bracket(subj); // remove this opener from delimiter list
   subj->pos = initial_pos;
-  return make_str(subj->mem, cmark_chunk_literal("]"));
+  return make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("]"));
 
 match:
   inl = make_simple(subj->mem, is_image ? CMARK_NODE_IMAGE : CMARK_NODE_LINK);
   inl->as.link.url = url;
   inl->as.link.title = title;
+  inl->start_line = inl->end_line = subj->line;
+  inl->start_column = opener->inl_text->start_column;
+  inl->end_column = subj->pos + subj->column_offset + subj->block_offset;
   cmark_node_insert_before(opener->inl_text, inl);
   // Add link text:
   tmp = opener->inl_text->next;
@@ -1048,6 +1071,8 @@
   if (peek_at(subj, subj->pos) == '\n') {
     advance(subj);
   }
+  ++subj->line;
+  subj->column_offset = -subj->pos;
   // skip spaces at beginning of line
   skip_spaces(subj);
   if (nlpos > 1 && peek_at(subj, nlpos - 1) == ' ' &&
@@ -1133,7 +1158,7 @@
   cmark_node *new_inl = NULL;
   cmark_chunk contents;
   unsigned char c;
-  bufsize_t endpos;
+  bufsize_t startpos, endpos;
   c = peek_char(subj);
   if (c == 0) {
     return 0;
@@ -1169,7 +1194,7 @@
     break;
   case '[':
     advance(subj);
-    new_inl = make_str(subj->mem, cmark_chunk_literal("["));
+    new_inl = make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("["));
     push_bracket(subj, false, new_inl);
     break;
   case ']':
@@ -1179,10 +1204,10 @@
     advance(subj);
     if (peek_char(subj) == '[') {
       advance(subj);
-      new_inl = make_str(subj->mem, cmark_chunk_literal("!["));
+      new_inl = make_str(subj, subj->pos - 2, subj->pos - 1, cmark_chunk_literal("!["));
       push_bracket(subj, true, new_inl);
     } else {
-      new_inl = make_str(subj->mem, cmark_chunk_literal("!"));
+      new_inl = make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("!"));
     }
     break;
   default:
@@ -1192,6 +1217,7 @@
 
     endpos = subject_find_special_char(subj, options);
     contents = cmark_chunk_dup(&subj->input, subj->pos, endpos - subj->pos);
+    startpos = subj->pos;
     subj->pos = endpos;
 
     // if we're at a newline, strip trailing spaces.
@@ -1199,7 +1225,7 @@
       cmark_chunk_rtrim(&contents);
     }
 
-    new_inl = make_str(subj->mem, contents);
+    new_inl = make_str(subj, startpos, endpos - 1, contents);
   }
   if (new_inl != NULL) {
     cmark_node_append_child(parent, new_inl);
@@ -1214,7 +1240,7 @@
                          cmark_reference_map *refmap,
                          int options) {
   subject subj;
-  subject_from_buf(parser->mem, &subj, &parent->content, refmap);
+  subject_from_buf(parser->mem, parent->start_line, parent->start_column - 1, &subj, &parent->content, refmap);
   cmark_chunk_rtrim(&subj.input);
 
   while (!is_eof(&subj) && parse_inline(parser, &subj, parent, options))
@@ -1253,7 +1279,7 @@
   bufsize_t matchlen = 0;
   bufsize_t beforetitle;
 
-  subject_from_buf(mem, &subj, input, NULL);
+  subject_from_buf(mem, -1, 0, &subj, input, NULL);
 
   // parse label:
   if (!link_label(&subj, &lab) || lab.len == 0)
@@ -1425,6 +1451,10 @@
   parser->pos = offset;
 }
 
+int cmark_inline_parser_get_column(cmark_inline_parser *parser) {
+  return parser->pos + 1 + parser->column_offset + parser->block_offset;
+}
+
 cmark_chunk *cmark_inline_parser_get_chunk(cmark_inline_parser *parser) {
   return &parser->input;
 }
@@ -1452,4 +1482,8 @@
 
 delimiter *cmark_inline_parser_get_last_delimiter(cmark_inline_parser *parser) {
   return parser->last_delim;
+}
+
+int cmark_inline_parser_get_line(cmark_inline_parser *parser) {
+  return parser->line;
 }
diff --git a/cbits/iterator.c b/cbits/iterator.c
--- a/cbits/iterator.c
+++ b/cbits/iterator.c
@@ -111,6 +111,7 @@
       while (tmp && tmp->type == CMARK_NODE_TEXT) {
         cmark_iter_next(iter); // advance pointer
         cmark_strbuf_put(&buf, tmp->as.literal.data, tmp->as.literal.len);
+        cur->end_column = tmp->end_column;
         next = tmp->next;
         cmark_node_free(tmp);
         tmp = next;
diff --git a/cbits/strikethrough.c b/cbits/strikethrough.c
--- a/cbits/strikethrough.c
+++ b/cbits/strikethrough.c
@@ -23,6 +23,8 @@
 
   res = cmark_node_new_with_mem(CMARK_NODE_TEXT, parser->mem);
   cmark_node_set_literal(res, buffer);
+  res->start_line = res->end_line = cmark_inline_parser_get_line(inline_parser);
+  res->start_column = cmark_inline_parser_get_column(inline_parser) - delims;
 
   if (left_flanking || right_flanking) {
     cmark_inline_parser_push_delimiter(inline_parser, character, left_flanking,
@@ -58,6 +60,7 @@
     tmp = next;
   }
 
+  strikethrough->end_column = closer->inl_text->start_column + closer->inl_text->as.literal.len - 1;
   cmark_node_free(closer->inl_text);
 
   delim = closer;
diff --git a/cbits/table.c b/cbits/table.c
--- a/cbits/table.c
+++ b/cbits/table.c
@@ -26,9 +26,16 @@
   bool is_header;
 } node_table_row;
 
+typedef struct {
+  cmark_strbuf *buf;
+  int start_offset, end_offset;
+} node_cell;
+
 static void free_table_cell(cmark_mem *mem, void *data) {
-  cmark_strbuf_free((cmark_strbuf *)data);
-  mem->free(data);
+  node_cell *cell = (node_cell *)data;
+  cmark_strbuf_free((cmark_strbuf *)cell->buf);
+  mem->free(cell->buf);
+  mem->free(cell);
 }
 
 static void free_table_row(cmark_mem *mem, table_row *row) {
@@ -105,14 +112,13 @@
                                   cmark_parser *parser, unsigned char *string,
                                   int len) {
   table_row *row = NULL;
-  bufsize_t cell_matched, pipe_matched, offset = 0;
+  bufsize_t cell_matched, pipe_matched, offset;
 
   row = (table_row *)parser->mem->calloc(1, sizeof(table_row));
   row->n_columns = 0;
   row->cells = NULL;
 
-  if (len > 0 && string[0] == '|')
-    ++offset;
+  offset = scan_table_cell_end(string, len, 0);
 
   do {
     cell_matched = scan_table_cell(string, len, offset);
@@ -122,8 +128,13 @@
       cmark_strbuf *cell_buf = unescape_pipes(parser->mem, string + offset,
           cell_matched);
       cmark_strbuf_trim(cell_buf);
+
+      node_cell *cell = (node_cell *)parser->mem->calloc(1, sizeof(*cell));
+      cell->buf = cell_buf;
+      cell->start_offset = offset;
+      cell->end_offset = offset + cell_matched - 1;
       row->n_columns += 1;
-      row->cells = cmark_llist_append(parser->mem, row->cells, cell_buf);
+      row->cells = cmark_llist_append(parser->mem, row->cells, cell);
     }
 
     offset += cell_matched + pipe_matched;
@@ -202,15 +213,14 @@
 
   parent_container->as.opaque = parser->mem->calloc(1, sizeof(node_table));
 
-
   set_n_table_columns(parent_container, header_row->n_columns);
 
   uint8_t *alignments =
       (uint8_t *)parser->mem->calloc(header_row->n_columns, sizeof(uint8_t));
   cmark_llist *it = marker_row->cells;
   for (i = 0; it; it = it->next, ++i) {
-    cmark_strbuf *node = (cmark_strbuf *)it->data;
-    bool left = node->ptr[0] == ':', right = node->ptr[node->size - 1] == ':';
+    node_cell *node = (node_cell *)it->data;
+    bool left = node->buf->ptr[0] == ':', right = node->buf->ptr[node->buf->size - 1] == ':';
 
     if (left && right)
       alignments[i] = 'c';
@@ -223,8 +233,10 @@
 
   table_header =
       cmark_parser_add_child(parser, parent_container, CMARK_NODE_TABLE_ROW,
-                             cmark_parser_get_offset(parser));
+                             parent_container->start_column);
   cmark_node_set_syntax_extension(table_header, self);
+  table_header->end_column = parent_container->start_column + (int)strlen(parent_string) - 2;
+  table_header->start_line = table_header->end_line = parent_container->start_line;
 
   table_header->as.opaque = ntr = (node_table_row *)parser->mem->calloc(1, sizeof(node_table_row));
   ntr->is_header = true;
@@ -233,10 +245,12 @@
     cmark_llist *tmp;
 
     for (tmp = header_row->cells; tmp; tmp = tmp->next) {
-      cmark_strbuf *cell_buf = (cmark_strbuf *) tmp->data;
+      node_cell *cell = (node_cell *) tmp->data;
       cmark_node *header_cell = cmark_parser_add_child(parser, table_header,
-          CMARK_NODE_TABLE_CELL, cmark_parser_get_offset(parser));
-      cmark_node_set_string_content(header_cell, (char *) cell_buf->ptr);
+          CMARK_NODE_TABLE_CELL, parent_container->start_column + cell->start_offset);
+      header_cell->start_line = header_cell->end_line = parent_container->start_line;
+      header_cell->end_column = parent_container->start_column + cell->end_offset;
+      cmark_node_set_string_content(header_cell, (char *) cell->buf->ptr);
       cmark_node_set_syntax_extension(header_cell, self);
     }
   }
@@ -262,9 +276,9 @@
 
   table_row_block =
       cmark_parser_add_child(parser, parent_container, CMARK_NODE_TABLE_ROW,
-                             cmark_parser_get_offset(parser));
-
+                             parent_container->start_column);
   cmark_node_set_syntax_extension(table_row_block, self);
+  table_row_block->end_column = parent_container->end_column;
   table_row_block->as.opaque = parser->mem->calloc(1, sizeof(node_table_row));
 
   row = row_from_string(self, parser, input + cmark_parser_get_first_nonspace(parser),
@@ -275,17 +289,18 @@
     int i, table_columns = get_n_table_columns(parent_container);
 
     for (tmp = row->cells, i = 0; tmp && i < table_columns; tmp = tmp->next, ++i) {
-      cmark_strbuf *cell_buf = (cmark_strbuf *) tmp->data;
-      cmark_node *cell = cmark_parser_add_child(parser, table_row_block,
-          CMARK_NODE_TABLE_CELL, cmark_parser_get_offset(parser));
-      cmark_node_set_string_content(cell, (char *) cell_buf->ptr);
-      cmark_node_set_syntax_extension(cell, self);
+      node_cell *cell = (node_cell *) tmp->data;
+      cmark_node *node = cmark_parser_add_child(parser, table_row_block,
+          CMARK_NODE_TABLE_CELL, parent_container->start_column + cell->start_offset);
+      node->end_column = parent_container->start_column + cell->end_offset;
+      cmark_node_set_string_content(node, (char *) cell->buf->ptr);
+      cmark_node_set_syntax_extension(node, self);
     }
 
     for (; i < table_columns; ++i) {
-      cmark_node *cell = cmark_parser_add_child(
-          parser, table_row_block, CMARK_NODE_TABLE_CELL, cmark_parser_get_offset(parser));
-      cmark_node_set_syntax_extension(cell, self);
+      cmark_node *node = cmark_parser_add_child(
+          parser, table_row_block, CMARK_NODE_TABLE_CELL, 0);
+      cmark_node_set_syntax_extension(node, self);
     }
   }
 
diff --git a/cbits/tagfilter.c b/cbits/tagfilter.c
--- a/cbits/tagfilter.c
+++ b/cbits/tagfilter.c
@@ -1,5 +1,6 @@
 #include "tagfilter.h"
 #include <parser.h>
+#include <ctype.h>
 
 static const char *blacklist[] = {
     "title",   "textarea", "style",  "xmp",       "iframe",
@@ -23,7 +24,7 @@
     if (*tagname == 0)
       break;
 
-    if (tag_data[i] != *tagname)
+    if (tolower(tag_data[i]) != *tagname)
       return 0;
   }
 
diff --git a/changelog b/changelog
--- a/changelog
+++ b/changelog
@@ -1,3 +1,18 @@
+cmark-gfm 0.1.2 (09 Aug 2017)
+
+  * Update to cmark-gfm 0.28.0.gfm.5.
+
+cmark-gfm 0.1.1 (03 Aug 2017)
+
+  * Automatically register plugins.
+  * Rename alignment values to not clash with `None`, `Left`.
+  * Fix a potential use-after-free.
+
+cmark-gfm 0.1.0 (03 Aug 2017)
+
+  * Update to cmark-gfm (between revisions).
+  * Add extension support.
+
 cmark 0.5.5.1 (02 Aug 2017)
 
   * Update to cmark 0.28.0.
diff --git a/cmark-gfm.cabal b/cmark-gfm.cabal
--- a/cmark-gfm.cabal
+++ b/cmark-gfm.cabal
@@ -1,5 +1,5 @@
 name:                cmark-gfm
-version:             0.1.1
+version:             0.1.2
 synopsis:            Fast, accurate GitHub Flavored Markdown parser and renderer
 description:
   This package provides Haskell bindings for
diff --git a/test/test-cmark.hs b/test/test-cmark.hs
--- a/test/test-cmark.hs
+++ b/test/test-cmark.hs
@@ -20,17 +20,16 @@
   , "<p>dog’s</p>\n" ~=? commonmarkToHtml [optSmart] [] "dog's"
   , "<p><a href=\"\">trick</a></p>\n" ~=? commonmarkToHtml [optSafe] [] "[trick](javascript:alert('hi'))"
   , ".RS\n.PP\nquote\n.RE\n" ~=? commonmarkToMan [] [] Nothing "> quote"
-  , (Node (Just (PosInfo {startLine = 1, startColumn = 1, endLine = 1, endColumn = 13})) DOCUMENT [Node (Just (PosInfo {startLine = 1, startColumn = 1, endLine = 1, endColumn = 13})) PARAGRAPH [Node Nothing (TEXT "Hello ") [],Node Nothing EMPH [Node Nothing (TEXT "world") []]]]) ~=? commonmarkToNode [] [] "Hello *world*"
+  , (Node (Just (PosInfo {startLine = 1, startColumn = 1, endLine = 1, endColumn = 13})) DOCUMENT [Node (Just (PosInfo {startLine = 1, startColumn = 1, endLine = 1, endColumn = 13})) PARAGRAPH [Node (Just (PosInfo {startLine = 1, startColumn = 1, endLine = 1, endColumn = 6})) (TEXT "Hello ") [],Node (Just (PosInfo {startLine = 1, startColumn = 7, endLine = 1, endColumn = 13})) EMPH [Node (Just (PosInfo {startLine = 1, startColumn = 8, endLine = 1, endColumn = 12})) (TEXT "world") []]]]) ~=? commonmarkToNode [] [] "Hello *world*"
   , "> Hello\n> *world*\n" ~=? nodeToCommonmark [] (Just 12) (Node Nothing DOCUMENT [Node Nothing BLOCK_QUOTE [Node Nothing PARAGRAPH [Node Nothing (TEXT "Hello ") [],Node Nothing EMPH [Node Nothing (TEXT "world") []]]]])
   , "<p>~hi~</p>\n" ~=? commonmarkToHtml [] [] "~hi~"
   , "<p><del>hi</del></p>\n" ~=? commonmarkToHtml [] [extStrikethrough] "~hi~"
-  , (Node (Just (PosInfo {startLine = 1, startColumn = 1, endLine = 1, endColumn = 4})) DOCUMENT [Node (Just (PosInfo {startLine = 1, startColumn = 1, endLine = 1, endColumn = 4})) PARAGRAPH [Node Nothing STRIKETHROUGH [Node Nothing (TEXT "hi") []]]]) ~=? commonmarkToNode [] [extStrikethrough] "~hi~"
+  , (Node (Just (PosInfo {startLine = 1, startColumn = 1, endLine = 1, endColumn = 4})) DOCUMENT [Node (Just (PosInfo {startLine = 1, startColumn = 1, endLine = 1, endColumn = 4})) PARAGRAPH [Node (Just (PosInfo {startLine = 1, startColumn = 1, endLine = 1, endColumn = 4})) STRIKETHROUGH [Node (Just (PosInfo {startLine = 1, startColumn = 2, endLine = 1, endColumn = 3})) (TEXT "hi") []]]]) ~=? commonmarkToNode [] [extStrikethrough] "~hi~"
   , "<p>www.google.com</p>\n" ~=? commonmarkToHtml [] [] "www.google.com"
   , "<p><a href=\"http://www.google.com\">www.google.com</a></p>\n" ~=? commonmarkToHtml [] [extAutolink] "www.google.com"
   , "<p>| a |\n| --- |\n| b |</p>\n" ~=? commonmarkToHtml [] [] "| a |\n| --- |\n| b |\n"
   , "<table>\n<thead>\n<tr>\n<th>a</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>b</td>\n</tr></tbody></table>\n" ~=? commonmarkToHtml [] [extTable] "| a |\n| --- |\n| b |\n"
-  , (Node (Just (PosInfo {startLine = 1, startColumn = 1, endLine = 3, endColumn = 17})) DOCUMENT [Node (Just (PosInfo {startLine = 1, startColumn = 1, endLine = 3, endColumn = 17})) (TABLE [LeftAligned,CenterAligned,NoAlignment,RightAligned]) [Node (Just (PosInfo {startLine = 2, startColumn = 0, endLine = 2, endColumn = 0})) TABLE_ROW [Node (Just (PosInfo {startLine = 2, startColumn = 0, endLine = 2, endColumn = 0})) TABLE_CELL [Node Nothing (TEXT "a") []],Node (Just (PosInfo {startLine = 2, startColumn = 0, endLine = 2, endColumn = 0})) TABLE_CELL [Node Nothing (TEXT "b") []],Node (Just (PosInfo {startLine = 2, startColumn = 0, endLine = 2, endColumn = 0})) TABLE_CELL [Node Nothing (TEXT "c") []],Node (Just (PosInfo {startLine = 2, startColumn = 0, endLine = 2, endColumn = 0})) TABLE_CELL [Node Nothing (TEXT "d") []]],Node (Just (PosInfo {startLine = 3, startColumn = 0, endLine = 3, endColumn = 17})) TABLE_ROW [Node (Just (PosInfo {startLine = 3, startColumn = 0, endLine = 3, endColumn = 0})) TABLE_CELL [Node Nothing (TEXT "y") []],Node (Just (PosInfo {startLine = 3, startColumn = 0, endLine = 3, endColumn = 0})) TABLE_CELL [Node Nothing (TEXT "o") []],Node (Just (PosInfo {startLine = 3, startColumn = 0, endLine = 3, endColumn = 0})) TABLE_CELL [Node Nothing (TEXT "s") []],Node (Just (PosInfo {startLine = 3, startColumn = 0, endLine = 3, endColumn = 0})) TABLE_CELL [Node Nothing (TEXT "h") []]]]]) ~=? commonmarkToNode [] [extTable] "| a | b | c | d |\n| :-- | :-: | --- | --: |\n| y | o | s | h |"
+  , Node (Just (PosInfo {startLine = 1, startColumn = 1, endLine = 3, endColumn = 17})) DOCUMENT [Node (Just (PosInfo {startLine = 1, startColumn = 1, endLine = 3, endColumn = 17})) (TABLE [LeftAligned,CenterAligned,NoAlignment,RightAligned]) [Node (Just (PosInfo {startLine = 1, startColumn = 1, endLine = 1, endColumn = 17})) TABLE_ROW [Node (Just (PosInfo {startLine = 1, startColumn = 3, endLine = 1, endColumn = 4})) TABLE_CELL [Node (Just (PosInfo {startLine = 1, startColumn = 3, endLine = 1, endColumn = 3})) (TEXT "a") []],Node (Just (PosInfo {startLine = 1, startColumn = 7, endLine = 1, endColumn = 8})) TABLE_CELL [Node (Just (PosInfo {startLine = 1, startColumn = 7, endLine = 1, endColumn = 7})) (TEXT "b") []],Node (Just (PosInfo {startLine = 1, startColumn = 11, endLine = 1, endColumn = 12})) TABLE_CELL [Node (Just (PosInfo {startLine = 1, startColumn = 11, endLine = 1, endColumn = 11})) (TEXT "c") []],Node (Just (PosInfo {startLine = 1, startColumn = 15, endLine = 1, endColumn = 16})) TABLE_CELL [Node (Just (PosInfo {startLine = 1, startColumn = 15, endLine = 1, endColumn = 15})) (TEXT "d") []]],Node (Just (PosInfo {startLine = 3, startColumn = 1, endLine = 3, endColumn = 17})) TABLE_ROW [Node (Just (PosInfo {startLine = 3, startColumn = 3, endLine = 3, endColumn = 4})) TABLE_CELL [Node (Just (PosInfo {startLine = 3, startColumn = 3, endLine = 3, endColumn = 3})) (TEXT "y") []],Node (Just (PosInfo {startLine = 3, startColumn = 7, endLine = 3, endColumn = 8})) TABLE_CELL [Node (Just (PosInfo {startLine = 3, startColumn = 7, endLine = 3, endColumn = 7})) (TEXT "o") []],Node (Just (PosInfo {startLine = 3, startColumn = 11, endLine = 3, endColumn = 12})) TABLE_CELL [Node (Just (PosInfo {startLine = 3, startColumn = 11, endLine = 3, endColumn = 11})) (TEXT "s") []],Node (Just (PosInfo {startLine = 3, startColumn = 15, endLine = 3, endColumn = 16})) TABLE_CELL [Node (Just (PosInfo {startLine = 3, startColumn = 15, endLine = 3, endColumn = 15})) (TEXT "h") []]]]] ~=? commonmarkToNode [] [extTable] "| a | b | c | d |\n| :-- | :-: | --- | --: |\n| y | o | s | h |"
   , "<xmp>\n" ~=? commonmarkToHtml [] [] "<xmp>"
   , "&lt;xmp>\n" ~=? commonmarkToHtml [] [extTagfilter] "<xmp>"
   ]
-
