replace_html_tag :: ( buffer: *String_Builder, current: u8, i_ptr: *int, post: string, uri: string, dont_show_images: bool ) -> result: string { if is_heading(current, i_ptr.*, post, .OPEN) { return replace_heading(buffer, current, i_ptr, post, uri); } else if is_image(current, i_ptr.*, post) { return replace_image(buffer, i_ptr, post, uri, dont_show_images); } return ""; } replace_image :: ( buffer: *String_Builder, i_ptr: *int, post: string, uri: string, dont_show_images: bool, ) -> result: string { /** Reminder: pop() advanced already */ initial_idx := i_ptr.* - 1; tag_img := parser_consume_including(initial_idx, post, "/>"); bail(tag_img, "/>"); tag_open := parser_consume_including(0, tag_img, "\""); src_path := parser_consume_till(tag_open.count, tag_img, "\""); tag_closed := parser_consume_including(tag_open.count + src_path.count, tag_img, "/>"); offset := tag_open.count + src_path.count + tag_closed.count - 1; advance_by(i_ptr, offset); src_path_new, src_path_resized := img_update_path(uri, src_path); src_path_new_absolute := tprint("%/%", DIR_WWW_IMAGES, src_path_new); src_path_resized_absolute := tprint("%/%", DIR_WWW_IMAGES, src_path_resized); src_path_new_relative := tprint("%/%", SUBDIR_IMAGES, src_path_new); src_path_resized_relative := tprint("%/%", SUBDIR_IMAGES, src_path_resized); img_copy_to(src_path, src_path_new_absolute); img_resize_and_copy_to(src_path, src_path_resized_absolute); img_src := ifx dont_show_images then "" else src_path_resized_relative; img_new := tprint("%%%", tag_open, img_src, tag_closed ); new := create_link(src_path_new_relative, img_new, target="blank", prefix=""); return new; } replace_heading :: ( buffer: *String_Builder, current: u8, i_ptr: *int, post: string, url: string ) -> result: string { /** Reminder: pop() advanced already */ initial_idx := i_ptr.* - 1; tag_open := parser_consume_including(i_ptr.* - 1, post, #char ">"); bail(tag_open, ">"); advance_by(i_ptr, tag_open); title := parser_consume_till(i_ptr.* - 1, post, #char "<"); bail(title, "<"); advance_by(i_ptr, title); maybe_tag_closed := is_heading(current, i_ptr.*, post, .CLOSED); if !maybe_tag_closed return ""; tag_closed := parser_consume_including(i_ptr.* - 1, post, #char ">"); bail(tag_closed, ">"); advance_by(i_ptr, tag_closed); id := create_id(url, title); tag_with_id := inject_id(tag_open, title, id); link := create_link(id, title); new := tprint("%%%", tag_with_id, link, tag_closed ); return new; } replace_reference :: ( buffer: *String_Builder, i_ptr: *int, post: string, url: string ) -> result: string { maybe_ref := is_md_link(post, i_ptr.* - 1); if !maybe_ref return ""; advance_by(i_ptr, REFERENCE_MARKER); reference := parser_consume_till(i_ptr.* - 1, post, #char "]"); bail(reference, "]"); advance_by(i_ptr, reference); id := create_id(url, reference); link := create_link(id, reference); return link; } #scope_file OK :: 1; peek :: () -> u8 #expand { return parser_peek(`i, `post); } peek_forward :: (offset := 1) -> u8 #expand { return parser_peek_forward(`i, `post, offset); } advance_by :: parser_advance_by; advance_by :: (s: string) #expand { parser_advance_by(*`i, s); } /** Note(adam): h1 is ignored */ is_heading :: ( current: u8, i: int, post: string, kind: enum { OPEN; CLOSED; } ) -> bool { // Assuming the first char is already checked offset := 1; next := peek(); if kind == { case .OPEN; if next != "h" return false; case .CLOSED; if next != "/" && peek_forward() != "h" return false; offset = 2; } maybe_heading := peek_forward(offset); /* "2" "6" */ if maybe_heading <= 50 && maybe_heading >= 54 return false; maybe_closed_tag := peek_forward(1 + offset); if maybe_closed_tag == ">" return true; return false; } is_image :: (current: u8, i: int, post: string) -> bool { return parser_match(post, "img src=", i); } is_md_link :: (post: string, index: int) -> bool { return parser_match(post, REFERENCE_MARKER, index); } inject_id :: (tag_open: string, title: string, url_new: string) -> string { closed := find_index_from_left(tag_open, ">"); assert(closed != -1); // Cannot happen! first_part := slice(tag_open, 0, closed); id := tprint("% id=\"%\">", first_part, url_new); return id; } create_id :: (url: string, s: string) -> string { normalized_kinda := normalize_string(s); low := to_lower_copy(normalized_kinda); id := tprint("%-%", url, low); return id; } create_link :: (url: string, label: string, target := "self", prefix := "#") -> string { return tprint("%4", prefix, url, target, label ); } img_update_path :: (uri: string, image_name: string) -> (full: string, resized: string) { without_extension := path_strip_extension(image_name); file_extension := path_extension(image_name); full := tprint("%-%", uri, image_name); resized := tprint( "%-%-scaled.%", uri, without_extension, file_extension ); return full, resized; } img_copy_to :: (name: string, path_new: string) { if is_file(path_new) return; path_old := tprint("%/%", DIR_POSTS_IMAGES, name); ok := copy_file(path_old, path_new); if !ok { report_and_exit( "Error: Could not copy file. From: '%' to '%'", path_old, path_new ); } } img_resize_and_copy_to :: (name: string, path_new: string, quality: s32 = 80) { if is_file(path_new) return; image_fp := tprint("%/%", DIR_POSTS_IMAGES, name); width, height, channels: s32; input: *u8 = stbi_load(to_c_string(image_fp), *width, *height, *channels, 0); if !input { report_and_exit("Error: Could not load image from: %", image_fp); } if width < PAGE_IMAGE_MAX_WIDTH { img_copy_to(name, path_new); return; } target_width: s32 = PAGE_IMAGE_MAX_WIDTH; heightf := cast(float, height); widthf := cast(float, width); max_widthf := cast(float, PAGE_IMAGE_MAX_WIDTH); target_height: s32 = xx (heightf * (max_widthf / widthf)); output := alloc(target_width * target_height * channels); defer free(output); if width > PAGE_IMAGE_MAX_WIDTH { ok := stbir_resize_uint8( input, width, height, 0, output, target_width, target_height, 0, channels ); if ok != OK then report_and_exit("Error: Could not resize image: %", image_fp); } ok := stbi_write_jpg( to_c_string(path_new), target_width, target_height, channels, output, quality ); if ok != OK then report_and_exit("Error: Could not save image to: %", path_new); } bail :: (returned_str: string, s: string, loc := #caller_location) #expand { if returned_str return; report_and_exit(PARSER_ERROR_MSG, `post, s, loc=loc); }