{"id":46135825,"url":"https://github.com/nich-cebolla/autohotkey-maketable","last_synced_at":"2026-03-02T05:01:11.790Z","repository":{"id":323971894,"uuid":"1095451489","full_name":"Nich-Cebolla/AutoHotkey-MakeTable","owner":"Nich-Cebolla","description":"An AutoHotkey (AHK) class that converts an input string into a markdown table, html table, or pretty-aligned plain text table.","archived":false,"fork":false,"pushed_at":"2025-11-13T05:01:48.000Z","size":30,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-11-13T06:13:58.884Z","etag":null,"topics":["ahk","autohotkey","automation","format","html","markdown","string","table","tbl","text"],"latest_commit_sha":null,"homepage":"","language":"AutoHotkey","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Nich-Cebolla.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-13T04:19:15.000Z","updated_at":"2025-11-13T04:58:27.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/Nich-Cebolla/AutoHotkey-MakeTable","commit_stats":null,"previous_names":["nich-cebolla/autohotkey-maketable"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/Nich-Cebolla/AutoHotkey-MakeTable","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nich-Cebolla%2FAutoHotkey-MakeTable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nich-Cebolla%2FAutoHotkey-MakeTable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nich-Cebolla%2FAutoHotkey-MakeTable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nich-Cebolla%2FAutoHotkey-MakeTable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Nich-Cebolla","download_url":"https://codeload.github.com/Nich-Cebolla/AutoHotkey-MakeTable/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nich-Cebolla%2FAutoHotkey-MakeTable/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29993021,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-02T01:47:34.672Z","status":"online","status_checked_at":"2026-03-02T02:00:07.342Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ahk","autohotkey","automation","format","html","markdown","string","table","tbl","text"],"created_at":"2026-03-02T05:01:11.062Z","updated_at":"2026-03-02T05:01:11.777Z","avatar_url":"https://github.com/Nich-Cebolla.png","language":"AutoHotkey","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# Table of contents\n\n\u003col type=\"I\"\u003e\n  \u003cli\u003e\u003ca href=\"#maketable\"\u003eMakeTable\u003c/a\u003e\u003c/li\u003e\n  \u003cli\u003e\u003ca href=\"#ahk-forum-post\"\u003eAHK forum post\u003c/a\u003e\u003c/li\u003e\n  \u003cli\u003e\u003ca href=\"#reddit-post\"\u003eReddit post\u003c/a\u003e\u003c/li\u003e\n  \u003cli\u003e\u003ca href=\"#usage\"\u003eUsage\u003c/a\u003e\u003c/li\u003e\n  \u003col type=\"A\"\u003e\n    \u003cli\u003e\u003ca href=\"#clone-the-repo\"\u003eClone the repo\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"#what-to-use-as-the-input-string\"\u003eWhat to use as the input string\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"#parameters\"\u003eParameters\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"#the-maketable-object\"\u003eThe MakeTable object\u003c/a\u003e\u003c/li\u003e\n  \u003c/ol\u003e\n  \u003cli\u003e\u003ca href=\"#maketableprototypegetmarkdown\"\u003eMakeTable.Prototype.GetMarkdown\u003c/a\u003e\u003c/li\u003e\n  \u003col type=\"A\"\u003e\n    \u003cli\u003e\u003ca href=\"#parameters-1\"\u003eParameters\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"#returns\"\u003eReturns\u003c/a\u003e\u003c/li\u003e\n  \u003c/ol\u003e\n  \u003cli\u003e\u003ca href=\"#maketableprototypegethtml\"\u003eMakeTable.Prototype.GetHtml\u003c/a\u003e\u003c/li\u003e\n  \u003col type=\"A\"\u003e\n    \u003cli\u003e\u003ca href=\"#parameters-2\"\u003eParameters\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"#example-without-attributes\"\u003eExample without attributes\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"#example-with-attributes\"\u003eExample with attributes\u003c/a\u003e\u003c/li\u003e\n  \u003c/ol\u003e\n  \u003cli\u003e\u003ca href=\"#changelog\"\u003eChangelog\u003c/a\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003cul\u003e\u003ci\u003e\u003csub\u003eTable of contents generated by \u003ca href=\"https://github.com/Nich-Cebolla/AutoHotkey-LibV2/blob/main/Headers2ToC.ahk\"\u003eHeaders2ToC.ahk\u003c/a\u003e\u003c/i\u003e\u003c/sub\u003e\u003c/ul\u003e\n\n# MakeTable\n\nAn AutoHotkey (AHK) class that takes your csv-style text and converts it to one of the following:\n- A Markdown-formatted table.\n- An html-formatted table.\n- A pretty-aligned plain text table using character count to manage table width (for use with monospace fonts).\n\n# AHK forum post\n\nhttps://www.autohotkey.com/boards/viewtopic.php?f=83\u0026t=139518\n\n# Reddit post\n\nhttps://www.reddit.com/r/AutoHotkey/comments/1ovrn3t/maketable_a_class_that_converts_an_input_string/\n\n# Usage\n\nThe examples in this document use the following input:\n\n```ahk\nstr := \"\n(\ncalldate,src,dst,dcontext,channel\n07/14/2025 02:43:44,5555557485,17,play-system-recording,PJSIP/Cox_Trunk-0000d212\n07/14/2025 05:58:22,5555557984,s,ivr-6,PJSIP/Cox_Trunk-0000d213\n07/14/2025 06:36:41,5555559989,s,ivr-6,PJSIP/Cox_Trunk-0000d214\n07/14/2025 06:47:11,5555552202,91017,ext-queues,PJSIP/Cox_Trunk-0000d215\n)\"\n```\n\nBasic usage:\n\n```ahk\n#include \u003cMakeTable\u003e\nstr := \"\n(\ncalldate,src,dst,dcontext,channel\n07/14/2025 02:43:44,5555557485,17,play-system-recording,PJSIP/Cox_Trunk-0000d212\n07/14/2025 05:58:22,5555557984,s,ivr-6,PJSIP/Cox_Trunk-0000d213\n07/14/2025 06:36:41,5555559989,s,ivr-6,PJSIP/Cox_Trunk-0000d214\n07/14/2025 06:47:11,5555552202,91017,ext-queues,PJSIP/Cox_Trunk-0000d215\n)\"\noptions := {\n    AddHeaderSeparator: true\n  , InputColumnSeparator: ','\n  , LinePrefix: \"|  \"\n  , LineSuffix: \"  |\"\n  , OutputColumnSeparator: \"|\"\n}\ntbl := MakeTable(str, options)\n\ng := Gui()\n; We need a monospaced font for the pretty-aligned text to look pretty\ng.SetFont(\"s11 q5\", \"Cascadia Mono\")\ng.Add(\"Edit\", \"w1200 r8 -Wrap\", tbl.Value)\ng.Show()\n\n; write to file\nf := FileOpen(A_Temp \"\\MakeTable-output.md\", \"w\")\nf.Write(tbl.Value)\nf.Close()\n```\n\n## Clone the repo\n\n- Clone the repository.\n  ```cmd\n  git clone https://github.com/Nich-Cebolla/AutoHotkey-MakeTable\n  ```\n- Make a copy of the cloned repository and work with the copy. This is to avoid a situation where\n  pulling an update breaks our scripts; by using a separate copy we can give ourselves time to review\n  updates before updating the active copy.\n  ```cmd\n  xcopy AutoHotkey-MakeTable AutoHotkey-MakeTable-Active /I /E\n  ```\n- Add a file MakeTable.ahk to your [lib folder](https://www.autohotkey.com/docs/v2/Scripts.htm#lib).\n  In the file is a single statement.\n  ```ahk\n  #include C:\\users\\you\\path\\to\\AutoHotkey-MakeTable-Active\\src\\MakeTable.ahk\n  ```\n\nWith this setup, we can use `#include \u003cMakeTable\u003e` from any other script.\n\n## What to use as the input string\n\nThe text must be able to be divided into rows and cells using a character or regex pattern. For\nexample, a common csv without quoted fields is viable as an input string. However, csv with\nquoted fields is not viable if the fields contain commas, because `StrSplit` will split at every\ncomma. You can use [ParseCsv](https://github.com/Nich-Cebolla/ParseCsv-AutoHotkey) to parse the csv\nand then recreate the csv using any character that is wholly absent from the text to separate the\nfields, then use that as input for `MakeTable`.\n\n`MakeTable` accepts regex patterns to identify the boundaries between each row and each cell, so you\nare not limited to only csv.\n\nDefine `Options.InputColumnSeparator` and `Options.InputRowSeparator` with the patterns. The\ndefault values are listed below.\n\nPass the options object to `MakeTable.Prototype.__New` to get a `MakeTable` object.\n\nThe `MakeTable` object will have the pretty-aligned text set to property \"Value\". You also have\ntwo additional methods available, `MakeTable.Prototype.GetMarkdown` and `MakeTable.Prototype.GetHtml`.\n\nIf you use a very large input (e.g. 100k+ lines), `MakeTable` will finish the job but it might take\na minute or two. Let it run and set a MsgBox to alert you when its finished.\n\nThere are various options to customize the output. Here's a few examples using various configurations.\n\n\u003cpre\u003e\ncalldate               src           dst      dcontext                 channel\n--------------------------------------------------------------------------------------------------------------------\n07/14/2025 02:43:44    5555557485    17       play-system-recording    PJSIP/Cox_Trunk-0000d-212-1080-@from-internal\n--------------------------------------------------------------------------------------------------------------------\n07/14/2025 05:58:22    5555557984    s        ivr-6                    PJSIP/Cox_Trunk-0000d-213-1080-@from-internal\n--------------------------------------------------------------------------------------------------------------------\n07/14/2025 06:36:41    5555559989    s        ivr-6                    PJSIP/Cox_Trunk-0000d-214-1080-@from-internal\n--------------------------------------------------------------------------------------------------------------------\n07/14/2025 06:47:11    5555552202    91017    ext-queues               PJSIP/Cox_Trunk-0000d-215-1080-@from-internal\n\u003c/pre\u003e\n\n\u003cpre\u003e\n|  calldate            |  src         |  dst    |  dcontext          |  channel           |\n|  --------------------|--------------|---------|--------------------|------------------  |\n|  07/14/2025          |  5555557485  |  17     |  play-system-reco  |  PJSIP/Cox_Trunk-  |\n|  02:43:44            |              |         |  rding             |  0000d-212-1080-@  |\n|                      |              |         |                    |  from-internal     |\n|  07/14/2025          |  5555557984  |  s      |  ivr-6             |  PJSIP/Cox_Trunk-  |\n|  05:58:22            |              |         |                    |  0000d-213-1080-@  |\n|                      |              |         |                    |  from-internal     |\n|  07/14/2025          |  5555559989  |  s      |  ivr-6             |  PJSIP/Cox_Trunk-  |\n|  06:36:41            |              |         |                    |  0000d-214-1080-@  |\n|                      |              |         |                    |  from-internal     |\n|  07/14/2025          |  5555552202  |  91017  |  ext-queues        |  PJSIP/Cox_Trunk-  |\n|  06:47:11            |              |         |                    |  0000d-215-1080-@  |\n|                      |              |         |                    |  from-internal     |\n\u003c/pre\u003e\n\n\u003cpre\u003e\n|  calldate            |  src         |  dst    |  dcontext          |  channel           |\n|  --------------------|--------------|---------|--------------------|------------------  |\n|  07/14/2025          |  5555557485  |  17     |  play-system-reco  |  PJSIP/Cox_Trunk-  |\n|  02:43:44            |              |         |  rding             |  0000d-212-1080-@  |\n|                      |              |         |                    |  from-internal     |\n|  --------------------|--------------|---------|--------------------|------------------  |\n|  07/14/2025          |  5555557984  |  s      |  ivr-6             |  PJSIP/Cox_Trunk-  |\n|  05:58:22            |              |         |                    |  0000d-213-1080-@  |\n|                      |              |         |                    |  from-internal     |\n|  --------------------|--------------|---------|--------------------|------------------  |\n|  07/14/2025          |  5555559989  |  s      |  ivr-6             |  PJSIP/Cox_Trunk-  |\n|  06:36:41            |              |         |                    |  0000d-214-1080-@  |\n|                      |              |         |                    |  from-internal     |\n|  --------------------|--------------|---------|--------------------|------------------  |\n|  07/14/2025          |  5555552202  |  91017  |  ext-queues        |  PJSIP/Cox_Trunk-  |\n|  06:47:11            |              |         |                    |  0000d-215-1080-@  |\n|                      |              |         |                    |  from-internal     |\n\u003c/pre\u003e\n\n## Parameters\n\n1. **{String}** - The input string.\n2. **{Object}** - The options object. The options are:\n- **{Boolean}** [ `Options.AddHeaderSeparator = true` ] - If true, adds a separator between\nthe first row and second row.\n\n- **{String}** [ `` Options.ColumnPadding = \"`s`s\" `` ] - The literal string that is added to the\nleft and right side of every column, EXCEPT the left side of the first column and the right\nside of the last column. Use `Options.LinePrefix` and `Options.LineSuffix` to adjust the\nleft side of the first column and the right side of the last column.\n\n- **{String}** [ `` Options.InputColumnSeparator = \"`t\" `` ] - A RegEx pattern that identifies the\nboundary between columns.\n\n- **{String}** [ `Options.InputRowSeparator = \"\\R\"` ] - A RegEx pattern that identifies the\nboundary between rows.\n\n- **{String}** [ `Options.LinePrefix = \"\"` ] - The literal string that is added before every line.\n\n- **{String}** [ `Options.LineSuffix = \"\"` ] - The literal string that is added after every line.\n\n- **{Integer|Integer[]}** [ `Options.MaxWidths = \"\"` ] - If in use, `Options.MaxWidths` is an array of\nintegers which define the maximum allowable width per column. If the text in a cell exceeds the\nmaximum, `MakeTable` breaks the text into multiple lines. If `Options.MaxWidths` is an integer,\nthat value is applied as the max width of all columns.\n\n- **{String[]}** [ `Options.OutputColumnPrefix = \"\"` ] - Used to specify strings that prefix\ncolumns. The difference between `Options.OutputColumnPrefix` and `Options.OutputColumnSeparator`\nis that `Options.OutputColumnSeparator` is applied to all columns, whereas\n`Options.OutputColumnPrefix` is used to prefix every line of a specific column with a specific\nstring. The value of this option, if used, should be an array of strings. The indices of the\narray are associated with the column index that is to be prefixed by the string at that index.\nFor example, if I have a table that is three columns, and I want to prefix each line of the\nthird column with \"; \", then I would set `Options.ColumnPrefix := [\"\", \"\", \"; \"]`. I added\nthis option with the intent of using it to comment out portions of text when using `MakeTable`\nto generate pretty-formatted code.\n\n- **{Boolean}** [ `Options.OutputColumnPrefixSkipFirstRow = false` ] - If true, the first\nline is not affected by `Options.OutputColumnPrefix`.\n\n- **{String}** [ `Options.OutputColumnSeparator = \"\"` ] - The literal string that is used to\nseparate cells.\n\n- **{Boolean}** [ `Options.OutputLineBetweenRows = false` ] - If true, there will be an extra line\nseparating the rows. The line will look just like the header separator seen in the above\nexamples.\n\n- **{String}** [ `` Options.OutputRowSeparator = \"`n\" `` ] - The literal string that is used to\nseparate rows.\n\n- **{String}** [ `` Options.TrimCharacters = \"`s\" `` ] - The value passed to the third parameter\nof `StrSplit` when breaking the input text into rows and cells. `Options.TrimCharactersRow`\nand `Options.TrimCharactersCell` supercede `Options.TrimCharacters`. For example, if\nyou set `` Options.TrimCharacters := \"`s\" `` and `Options.TrimCharactersRow := \"$\"`, the characters\ntrimmed from the left and right side of each line is \"$\" and the characters trimmed from the\nleft and right side of each cell is \"`s\" (space).\n[https://www.autohotkey.com/docs/v2/lib/StrSplit.htm](https://www.autohotkey.com/docs/v2/lib/StrSplit.htm).\n\n- **{String}** [ `Options.TrimCharactersRow = \"\"` ] - The value passed to the third parameter\nof `StrSplit` when breaking the input text into lines. `Options.TrimCharactersRow`\nand `Options.TrimCharactersCell` supercede `Options.TrimCharacters`. For example, if\nyou set `` Options.TrimCharacters := \"`s\" `` and `Options.TrimCharactersRow := \"$\"`, the characters\ntrimmed from the left and right side of each line is \"$\" and the characters trimmed from the\nleft and right side of each cell is \"`s\" (space).\n[https://www.autohotkey.com/docs/v2/lib/StrSplit.htm](https://www.autohotkey.com/docs/v2/lib/StrSplit.htm).\n\n- **{String}** [ `Options.TrimCharactersCell = \"\"` ] - The value passed to the third parameter\nof `StrSplit` when breaking the text lines into individual cells. `Options.TrimCharactersRow`\nand `Options.TrimCharactersCell` supercede `Options.TrimCharacters`. For example, if\nyou set `` Options.TrimCharacters := \"`s\" `` and `Options.TrimCharactersRow := \"$\"`, the characters\ntrimmed from the left and right side of each line is \"$\" and the characters trimmed from the\nleft and right side of each cell is \"\\`s\" (space).\n[https://www.autohotkey.com/docs/v2/lib/StrSplit.htm](https://www.autohotkey.com/docs/v2/lib/StrSplit.htm).\n\n## The MakeTable object\n\nThe `MakeTable` object is an array of arrays of arrays of strings.\n\nEach item of the `MakeTable` array is a `MakeTable_Row` representing a row in the table.\n\nEach item of the `MakeTable_Row` arrays is a `MakeTable_Cell` object representing\na cell in the table. `MakeTable_Cell` objects are arrays of strings, where each item\nrepresent a line of text for that cell. If you call the object, i.e.\n`MakeTable_Cell.Prototype.Call`, it will return the full text within the cell.\n\nIf `Options.MaxWidths` is not in use, then the length of the `MakeTable_Cell` array is always 1 (no cells\nare broken into multiple lines). If `Options.MaxWidths` is in use, then the length of the array\nis the number of lines that the text was broken into for that cell. The property\n\"RowLines\" is an array of integers representing the number of lines each\nrow occupies, which is the greatest number of lines into which the text within any cell in that\nrow was split.\n\nAccessing a string value in the array looks like this:\n\n```ahk\n; Assume `inputStr` and `options` are appropriately defined.\ntbl := MakeTable(inputStr, options)\nr := 1 ; row index\nc := 1 ; column index\nk := 1 ; line index\ntext := tbl[r][c][k]\n```\n\nAccessing the text in a cell looks like this:\n\n```ahk\n; Assume `inputStr` and `options` are appropriately defined.\ntbl := MakeTable(inputStr, options)\nr := 1 ; row index\nc := 1 ; column index\ncell := tbl[r][c]\ntext := cell() ; call the cell object to get the entire cell's text content\n```\n\nYou can produce a markdown table that is both pretty-aligned and valid markdown. To do that,\nuse the following options (in addition to any other options you might want). We can't use\n`Options.MaxWidths` when producing markdown output because the line breaks will disrupt the markdown\nsyntax. `Options.MaxWidths` is disabled by default. Use `MakeTable.Prototype.GetMarkdown` to\ninclude line breaks in your markdown table.\n\n```ahk\noptions := {\n    AddHeaderSeparator: true\n  , InputColumnSeparator: ',' ; set to whatever character / pattern identifies the boundary between each column\n  , LinePrefix: \"|  \"\n  , LineSuffix: \"  |\"\n  , OutputColumnSeparator: \"|\"\n}\ntbl := MakeTable(inputString, options)\n```\n\nThe above options will yield output like this:\n\n```markdown\n|  calldate             |  src         |  dst    |  dcontext               |  channel                                        |\n|  ---------------------|--------------|---------|-------------------------|-----------------------------------------------  |\n|  07/14/2025 02:43:44  |  5555557485  |  17     |  play-system-recording  |  PJSIP/Cox_Trunk-0000d-212-1080-@from-internal  |\n|  07/14/2025 05:58:22  |  5555557984  |  s      |  ivr-6                  |  PJSIP/Cox_Trunk-0000d-213-1080-@from-internal  |\n|  07/14/2025 06:36:41  |  5555559989  |  s      |  ivr-6                  |  PJSIP/Cox_Trunk-0000d-214-1080-@from-internal  |\n|  07/14/2025 06:47:11  |  5555552202  |  91017  |  ext-queues             |  PJSIP/Cox_Trunk-0000d-215-1080-@from-internal  |\n```\n\n# MakeTable.Prototype.GetMarkdown\n\nUse `MakeTable.Prototype.GetMarkdown` to produce a markdown table.\n\n## Parameters\n\n- **{String}** [ `` LineSeparator = \"`n\" `` ] - The string that separates each line in the output\nstring.\n\n- **{String}** [ `InnerLineSeparator = \"\"` ] - The value passed to the parameter\n`Separator` which is used to separate each line of text within a cell when `Options.MaxWidths` is\nused. For example, if your cells contain very long strings that would look better broken up into\nmultiple lines, you can set the `Options.MaxWidths` with an integer or array of integers. This will\nbreak up the text into multiple lines for cells that exceed the maximum. Then, when you call\n`MakeTable.Prototype.GetMarkdown`, you can set parameter `InnerLineSeparator` with a string to\nseparate those lines. Use \"\u0026lt;br\u003e\" to separate the lines with line breaks.\n\n## Returns\n\n**{String}** - The markdown table.\n\n`MakeTable.Prototype.GetMarkdown` has one benefit that is not available directly from the `MakeTable`\ncore process - with `MakeTable.Prototype.GetMarkdown` we can also include \u0026lt;br\u003e tags in-between\nlong lines of text. We do that by setting the [InnerLineSeparator](#maketableprototypegetmarkdown)\nparameter with \"\u0026lt;br\u003e\", yielding an output like the below table, which will render correctly and\nwill include line breaks at the \u0026lt;br\u003e tags.\n\n```markdown\n|calldate|src|dst|dcontext|channel|\n|-|-|-|-|-|\n|07/14/2025\u003cbr\u003e02:43:44|5555557485|17|play-system-reco\u003cbr\u003erding|PJSIP/Cox_Trunk-\u003cbr\u003e0000d-212-1080-@\u003cbr\u003efrom-internal|\n|07/14/2025\u003cbr\u003e05:58:22|5555557984|s|ivr-6|PJSIP/Cox_Trunk-\u003cbr\u003e0000d-213-1080-@\u003cbr\u003efrom-internal|\n|07/14/2025\u003cbr\u003e06:36:41|5555559989|s|ivr-6|PJSIP/Cox_Trunk-\u003cbr\u003e0000d-214-1080-@\u003cbr\u003efrom-internal|\n|07/14/2025\u003cbr\u003e06:47:11|5555552202|91017|ext-queues|PJSIP/Cox_Trunk-\u003cbr\u003e0000d-215-1080-@\u003cbr\u003efrom-internal|\n```\n\n# MakeTable.Prototype.GetHtml\n\nUse `MakeTable.Prototype.GetHtml` to produce an html table.\n\n## Parameters\n\n- **{String}** [ `` Options.IndentChar = \"`s\" `` ] - The character that is used for indentation.\n\n- **{Integer}** [ `` Options.IndentLen = 2 `` ] - The number of `Options.IndentChar` that is used\nfor one level of indentation.\n\n- **{Integer}** [ `` Options.InitialIndent = 0 `` ] - The initial indentation level of the output\nstring.\n\n- **{String}** [ `` Options.InnerLineSeparator = \"\" `` ] - The value passed to the parameters\n`MakeTable_Cell.Prototype.Call~Separator` which is used to separate each line of text\nwithin a cell when `MakeTable.Prototype.__New~Options.MaxWidths` is used. For example,\nif your cells contain very long strings that would look better broken up into multiple lines,\nyou can set the \"MaxWidths\" property of the options object that you pass to\n`MakeTable.Prototype.__New`. This will break up the text into multiple lines for cells\nthat exceed the maximum. Then, when you call `MakeTable.Prototype.GetHtml`, you can\nset `Options.InnerLineSeparator` with a substring to separate those lines. A good option\nis \"\u0026lt;br\u003e\". An empty string is also an appropriate option.\n\n- **{String}** [ `` Options.LineSeparator = \"`n\" `` ] - The literal string that separates each line\nin the output string.\n\n- **{String}** [ `` Options.TableAttribute = \"\" `` ] - Any attributes you want included with the\n\u0026lt;table\u003e element, e.g. \"class=`\"tbl-class`\" border=`\"1`\"\". Separate each individual attribute\nwith a space character.\n\n- **{String}** [ `` Options.TableStyle = \"\" `` ] - The style attribute value that will be\nincluded with the \u0026lt;table\u003e elements. Only include the value that goes between the quotation\nmarks of the style attribute, e.g. \"color:red;\".\n\n- **{String|String[]|String[][]}** [ `` Options.TdAttribute = \"\" `` ] - If a string, the attribute\nthat will be included with all \u0026lt;td\u003e elements.\n  - If an array of strings, each index in the array corresponds to a row in the table (not\nincluding headers) and that string is applied as the attributes for each cell in the row.\nFor example, index 1 is the first row after the headers.\n  - If an array of arrays of strings, each index of the outer array corresponds with a row in the\ntable (not including headers). For example, index 1 is the first row after the headers. Each\nindex of the inner array corresponds with a cell in that row. For example, index 1 is the\nfirst cell in the row. The items of the inner array are strings that wil be applied as the\nattributes for the corresponding cell.\n  - The arrays must have the correct number of items for the table content. If there are too few\nitems, an IndexError will occur.\n  - Separate each individual attribute with a space character.\n  - For example, these are each valid, assuming the table has 3 data rows and 3 columns:\n    ```ahk\n    options := { TdAttribute: \"class=`\"td-class`\"\" }\n    options := { TdAttribute: [ \"class=`\"td-class-1`\"\", \"class=`\"td-class-2`\"\", \"class=`\"td-class-3`\"\" ] }\n    options := { TdAttribute: [\n        [ \"class=`\"td-class-1-1`\"\", \"class=`\"td-class-1-2`\"\", \"class=`\"td-class-1-3`\"\" ]\n      , [ \"class=`\"td-class-2-1`\"\", \"class=`\"td-class-2-2`\"\", \"class=`\"td-class-2-3`\"\" ]\n      , [ \"class=`\"td-class-3-1`\"\", \"class=`\"td-class-3-2`\"\", \"class=`\"td-class-3-3`\"\" ]\n    ]}\n    ```\n\n- **{String}** [ `` Options.TdStyle = \"\" `` ] - If a string, the style attribute value that will be\nincluded with all \u0026lt;td\u003e elements. Only include the value that goes between the quotation marks\nof the style attribute, e.g. \"color:red;\".\n  - If an array of strings, each index in the array corresponds to a row in the table (not\nincluding headers) and that string is applied as the attributes for each cell in the row.\nFor example, index 1 is the first row after the headers.\n  - If an array of arrays of strings, each index of the outer array corresponds with a row in the\ntable (not including headers). For example, index 1 is the first row after the headers. Each\nindex of the inner array corresponds with a cell in that row. For example, index 1 is the\nfirst cell in the row. The items of the inner array are strings that wil be used as the style\nattribute value for the corresponding cell.\n  - The arrays must have the correct number of items for the table content. If there are too few\nitems, an IndexError will occur.\n  - For example, these are each valid, assuming the table has 3 data rows and 3 columns:\n    ```ahk\n    options := { TdStyle: \"color:red;\" }\n    options := { TdStyle: [ \"color:red;\", \"color:green;\", \"color:blue;\" ] }\n    options := { TdStyle: [\n        [ \"color:red;\", \"color:green;\", \"color:blue;\" ]\n      , [ \"color:blue;\", \"color:red;\", \"color:green;\" ]\n      , [ \"color:green;\", \"color:blue;\", \"color:red;\" ]\n    ]}\n    ```\n\n- **{String|String[]}** [ `` Options.ThAttribute = \"\" `` ] - If a string, the attribute\nthat will be included with all \u0026lt;th\u003e elements.\n  - If an array of strings, each index in the array corresponds to an individual header.\n  - The arrays must have the correct number of items for the table content. If there are too few\nitems, an IndexError will occur.\n  - Separate each individual attribute with a space character.\n  - For example, these are each valid, assuming the table has a header row with 3 headers.\n    ```ahk\n    options := { ThAttribute: \"class=`\"th-class`\"\" }\n    options := { ThAttribute: [ \"class=`\"th-class-1`\"\", \"class=`\"th-class-2`\"\", \"class=`\"th-class-3`\"\" ] }\n    ```\n\n- **{String}** [ `` Options.ThStyle = \"\" `` ] - If a string, the style attribute value that will be\nincluded with all \u0026lt;th\u003e elements. Only include the value that goes between the quotation marks\nof the style attribute, e.g. \"color:red;\".\n  - If an array of strings, each index in the array corresponds to a row in the table (not including\nheaders).\n  - The arrays must have the correct number of items for the table content. If there are too few\nitems, an IndexError will occur.\n  - For example, these are each valid, assuming the table has a header row with 3 headers.\n    ```ahk\n    options := { ThStyle: \"color:red;\" }\n    options := { ThStyle: [ \"color:red;\", \"color:green;\", \"color:blue;\" ] }\n    ```\n\n- **{String}** [ `` Options.TrAttribute = \"\" `` ] - If a string, the attribute\nthat will be included with all \u0026lt;tr\u003e elements.\n  - If an array of strings, each index in the array corresponds to a row in the table (not including\nheaders).\n  - The arrays must have the correct number of items for the table content. If there are too few\nitems, an IndexError will occur.\n  - Separate each individual attribute with a space character.\n  - For example, these are each valid, assuming the table has 3 rows.\n    ```ahk\n    options := { TrAttribute: \"class=`\"tr-class`\"\" }\n    options := { TrAttribute: [ \"class=`\"tr-class-1`\"\", \"class=`\"tr-class-2`\"\", \"class=`\"tr-class-3`\"\" ] }\n    ```\n\n- **{String}** [ `` Options.TrStyle = \"\" `` ] - If a string, the style attribute value that will be\nincluded with all \u0026lt;tr\u003e elements. Only include the value that goes between the quotation marks\nof the style attribute, e.g. \"color:red;\".\n  - If an array of strings, each index in the array corresponds to a row in the table (not including\nheaders).\n  - The arrays must have the correct number of items for the table content. If there are too few\nitems, an IndexError will occur.\n  - For example, these are each valid, assuming the table has 3 rows.\n    ```ahk\n    options := { TrStyle: \"color:red;\" }\n    options := { TrStyle: [ \"color:red;\", \"color:green;\", \"color:blue;\" ] }\n    ```\n\n## Example without attributes\n\n```html\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003cth\u003ecalldate\u003c/th\u003e\n    \u003cth\u003esrc\u003c/th\u003e\n    \u003cth\u003edst\u003c/th\u003e\n    \u003cth\u003edcontext\u003c/th\u003e\n    \u003cth\u003echannel\u003c/th\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e07/14/2025 02:43:44\u003c/td\u003e\n    \u003ctd\u003e5555557485\u003c/td\u003e\n    \u003ctd\u003e17\u003c/td\u003e\n    \u003ctd\u003eplay-system-recording\u003c/td\u003e\n    \u003ctd\u003ePJSIP/Cox_Trunk-0000d-212-1080-@from-internal\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e07/14/2025 05:58:22\u003c/td\u003e\n    \u003ctd\u003e5555557984\u003c/td\u003e\n    \u003ctd\u003es\u003c/td\u003e\n    \u003ctd\u003eivr-6\u003c/td\u003e\n    \u003ctd\u003ePJSIP/Cox_Trunk-0000d-213-1080-@from-internal\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e07/14/2025 06:36:41\u003c/td\u003e\n    \u003ctd\u003e5555559989\u003c/td\u003e\n    \u003ctd\u003es\u003c/td\u003e\n    \u003ctd\u003eivr-6\u003c/td\u003e\n    \u003ctd\u003ePJSIP/Cox_Trunk-0000d-214-1080-@from-internal\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e07/14/2025 06:47:11\u003c/td\u003e\n    \u003ctd\u003e5555552202\u003c/td\u003e\n    \u003ctd\u003e91017\u003c/td\u003e\n    \u003ctd\u003eext-queues\u003c/td\u003e\n    \u003ctd\u003ePJSIP/Cox_Trunk-0000d-215-1080-@from-internal\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n```\n\n## Example with attributes\n\n```html\n\u003ctable class=\"table\" style=\"color:red;\"\u003e\n  \u003ctr class=\"tr1\" style=\"color:red;\"\u003e\n    \u003cth class=\"th1\" style=\"color:red;\"\u003ecalldate\u003c/th\u003e\n    \u003cth class=\"th2\" style=\"color:green;\"\u003esrc\u003c/th\u003e\n    \u003cth class=\"th3\" style=\"color:blue;\"\u003edst\u003c/th\u003e\n    \u003cth class=\"th4\" style=\"color:pink;\"\u003edcontext\u003c/th\u003e\n    \u003cth class=\"th5\" style=\"color:purple;\"\u003echannel\u003c/th\u003e\n  \u003c/tr\u003e\n  \u003ctr class=\"tr2\" style=\"color:green;\"\u003e\n    \u003ctd class=\"td2-1\" style=\"color:purple;\"\u003e07/14/2025 02:43:44\u003c/td\u003e\n    \u003ctd class=\"td2-2\" style=\"color:red;\"\u003e5555557485\u003c/td\u003e\n    \u003ctd class=\"td2-3\" style=\"color:green;\"\u003e17\u003c/td\u003e\n    \u003ctd class=\"td2-4\" style=\"color:blue;\"\u003eplay-system-recording\u003c/td\u003e\n    \u003ctd class=\"td2-5\" style=\"color:pink;\"\u003ePJSIP/Cox_Trunk-0000d-212-1080-@from-internal\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr class=\"tr3\" style=\"color:blue;\"\u003e\n    \u003ctd class=\"td3-1\" style=\"color:pink;\"\u003e07/14/2025 05:58:22\u003c/td\u003e\n    \u003ctd class=\"td3-2\" style=\"color:purple;\"\u003e5555557984\u003c/td\u003e\n    \u003ctd class=\"td3-3\" style=\"color:red;\"\u003es\u003c/td\u003e\n    \u003ctd class=\"td3-4\" style=\"color:green;\"\u003eivr-6\u003c/td\u003e\n    \u003ctd class=\"td3-5\" style=\"color:blue;\"\u003ePJSIP/Cox_Trunk-0000d-213-1080-@from-internal\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr class=\"tr4\" style=\"color:pink;\"\u003e\n    \u003ctd class=\"td4-1\" style=\"color:blue;\"\u003e07/14/2025 06:36:41\u003c/td\u003e\n    \u003ctd class=\"td4-2\" style=\"color:pink;\"\u003e5555559989\u003c/td\u003e\n    \u003ctd class=\"td4-3\" style=\"color:purple;\"\u003es\u003c/td\u003e\n    \u003ctd class=\"td4-4\" style=\"color:red;\"\u003eivr-6\u003c/td\u003e\n    \u003ctd class=\"td4-5\" style=\"color:green;\"\u003ePJSIP/Cox_Trunk-0000d-214-1080-@from-internal\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr class=\"tr5\" style=\"color:purple;\"\u003e\n    \u003ctd class=\"td5-1\" style=\"color:green;\"\u003e07/14/2025 06:47:11\u003c/td\u003e\n    \u003ctd class=\"td5-2\" style=\"color:blue;\"\u003e5555552202\u003c/td\u003e\n    \u003ctd class=\"td5-3\" style=\"color:pink;\"\u003e91017\u003c/td\u003e\n    \u003ctd class=\"td5-4\" style=\"color:purple;\"\u003eext-queues\u003c/td\u003e\n    \u003ctd class=\"td5-5\" style=\"color:red;\"\u003ePJSIP/Cox_Trunk-0000d-215-1080-@from-internal\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n```\n\n# Changelog\n\n- **2025-11-14**\n  - Added property `MakeTableObj.ColumnWidths`, which is an array of integers representing the width,\n    in character count, of each column.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnich-cebolla%2Fautohotkey-maketable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnich-cebolla%2Fautohotkey-maketable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnich-cebolla%2Fautohotkey-maketable/lists"}