md2linkedin is a Python library and CLI tool that converts Markdown text to LinkedIn-compatible Unicode plain text. Bold and italic formatting is preserved using Unicode Mathematical Sans-Serif characters so styling survives when pasting into platforms that do not support Markdown. ## Home documentation ### index # md2linkedin [![PyPI version](https://img.shields.io/pypi/v/md2linkedin.png)](https://pypi.org/project/md2linkedin/) ![Python versions](https://img.shields.io/pypi/pyversions/md2linkedin.png) [![PyPI Downloads](https://img.shields.io/pypi/dm/md2linkedin.png)](https://pypistats.org/packages/md2linkedin) `md2linkedin` converts Markdown text to LinkedIn-compatible plain text by replacing bold, italic, and bold-italic markers with Unicode Mathematical Sans-Serif characters. This preserves visual formatting when pasting into platforms like LinkedIn that do not support Markdown natively. ## Installation | Package Manager | Installation Command | |-----------------|---------------------------| | pip | `pip install md2linkedin` | | uv | `uv add md2linkedin` | > **Tip:** Run the CLI without installing it with > `uvx md2linkedin post.md`. ## Usage ### Python API ``` python # @pyodide from md2linkedin import convert md = """ # Exciting News I'm thrilled to share that **we just launched** a new product! Key highlights: - **Performance**: *3x faster* than the previous version - **Reliability**: ***zero downtime*** deployments - **Developer UX**: clean, intuitive API Check it out and let me know what you think. """ print(convert(md)) ``` ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 𝗘𝗫𝗖𝗜𝗧𝗜𝗡𝗚 𝗡𝗘𝗪𝗦 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ I'm thrilled to share that 𝘄𝗲 𝗷𝘂𝘀𝘁 𝗹𝗮𝘂𝗻𝗰𝗵𝗲𝗱 a new product! Key highlights: • 𝗣𝗲𝗿𝗳𝗼𝗿𝗺𝗮𝗻𝗰𝗲: 3𝘹 𝘧𝘢𝘴𝘵𝘦𝘳 than the previous version • 𝗥𝗲𝗹𝗶𝗮𝗯𝗶𝗹𝗶𝘁𝘆: 𝙯𝙚𝙧𝙤 𝙙𝙤𝙬𝙣𝙩𝙞𝙢𝙚 deployments • 𝗗𝗲𝘃𝗲𝗹𝗼𝗽𝗲𝗿 𝗨𝗫: clean, intuitive API Check it out and let me know what you think. ### CLI ``` bash # Convert a Markdown file (output: post.linkedin.txt) md2linkedin post.md # Specify output path md2linkedin post.md -o linkedin_post.txt # Pipe from stdin echo "**Hello**, *world*!" | md2linkedin # Keep link URLs in the output md2linkedin post.md --preserve-links # Disable monospace code rendering md2linkedin post.md --no-monospace-code ``` ## Key Features - **Bold**: `**text**` or `__text__` → Unicode Sans-Serif Bold (𝗯𝗼𝗹𝗱) - **Italic**: `*text*` or `_text_` → Unicode Sans-Serif Italic (𝘪𝘵𝘢𝘭𝘪𝘤) - **Bold-italic**: `***text***` or `___text___` → Unicode Sans-Serif Bold Italic (𝙗𝙤𝙡𝙙-𝙞𝙩𝙖𝙡𝙞𝙘) - **Headers**: `#`/`##`/etc. styled with bold Unicode; H1 gets a `━` border - **Code spans**: backticks stripped, content rendered in Unicode Monospace (𝚌𝚘𝚍𝚎) by default; `--no-monospace-code` keeps plain text - **Fenced code blocks**: fences stripped and content rendered in Unicode Monospace by default; `--no-monospace-code` preserves verbatim - **Links**: stripped to display text by default; `--preserve-links` retains URLs - **Images**: replaced by alt text - **Bullet lists**: `-`/`*`/`+` → `•`; nested items → `‣` - **Blockquotes**: leading `>` stripped - **HTML spans**: unwrapped, inner text preserved - **HTML entities**: decoded (`&` → `&`, etc.) - **Backslash escapes**: resolved (`\*` → `*`) - **Windows line endings**: normalised automatically - **Emojis & non-ASCII**: pass through unchanged — no accidental corruption ## Limitations Converting Markdown to LinkedIn-friendly text relies on Unicode Mathematical Alphanumeric Symbols to simulate styling. This approach has notable limitations: - **Code Blocks**: Monospace Unicode characters do not enforce true fixed-width alignment on proportional fonts (like LinkedIn’s default font). As a result, indentation and column alignment in code blocks will often break visually. - **Tables**: Markdown tables are not converted — they pass through as raw pipe syntax (`| col | col |`), which LinkedIn does not render, producing unreadable output. - **Accessibility**: Screen readers often read Unicode mathematical characters aloud individually (e.g., “mathematical sans-serif bold b”) instead of as complete words, making the content difficult for visually impaired users to understand. - **Searchability**: Text styled with these Unicode characters may not be indexed properly by LinkedIn’s search algorithm, meaning people searching for your keywords might not find your post. For more examples, check out the package documentation at: ## License This project is licensed under the MIT License. ## Code of Conduct Please note that the md2linkedin project is released with a [Contributor Code of Conduct](https://www.contributor-covenant.org/version/3/0/code_of_conduct/). By contributing to this project, you agree to abide by its terms. ## Acknowledgements Hex sticker font is `Rubik`. Icons are sourced from [Flaticon](https://www.flaticon.com/): - Markdown icon by [Freepik](https://www.flaticon.com/authors/freepik) - LinkedIn icon by [Freepik](https://www.flaticon.com/authors/freepik) - Arrow/conversion icon by [Freepik](https://www.flaticon.com/authors/freepik) ## Advanced usage ### advanced # Advanced Usage ## Programmatic Use ### Converting Strings in Memory The simplest entry point is `convert()`, which accepts a Markdown string and returns a plain-text string ready for LinkedIn: ```python from md2linkedin import convert result = convert("**Hello**, *world*!") print(result) # 𝗛𝗲𝗹𝗹𝗼, 𝘸𝘰𝘳𝘭𝘥! ``` ### Converting Files Use `convert_file()` to read a `.md` file and write the output to a `.txt` file. The function returns the `Path` of the written file so you can chain further operations: ```python from md2linkedin import convert_file out = convert_file("post.md") print(f"Written to: {out}") # post.linkedin.txt # Override the output path out = convert_file("post.md", "my_post.txt") ``` ### Stripping and Preserving Links By default, Markdown link syntax is stripped to display text only (`[GitHub](https://github.com)` → `GitHub`). This is useful for removing clickable URLs from your output, keeping only the descriptive text. Pass `preserve_links=True` to retain the full link syntax: ```python result = convert("[GitHub](https://github.com)", preserve_links=True) # [GitHub](https://github.com) result = convert_file("post.md", preserve_links=True) ``` ### Direct Unicode Mapping The Unicode mapping functions are public and useful for applying a specific style to a plain string programmatically: ```python from md2linkedin import ( to_sans_bold, to_sans_italic, to_sans_bold_italic, to_monospace, apply_style, ) to_sans_bold("Open to Work") # 𝗢𝗽𝗲𝗻 𝘁𝗼 𝗪𝗼𝗿𝗸 to_sans_italic("3 years of exp") # 3 𝘺𝘦𝘢𝘳𝘴 𝘰𝘧 𝘦𝘹𝘱 to_sans_bold_italic("Key insight") # 𝙆𝙚𝙮 𝙞𝙣𝙨𝙞𝙜𝙝𝙩 to_monospace("print('hi')") # 𝚙𝚛𝚒𝚗𝚝('𝚑𝚒') # Dynamic dispatch apply_style("Hiring!", "bold") ``` --- ## What Gets Transformed (and What Doesn't) | Markdown construct | Output | |--------------------|--------| | `**bold**` / `__bold__` | Unicode 𝗯𝗼𝗹𝗱 | | `*italic*` / `_italic_` | Unicode 𝘪𝘵𝘢𝘭𝘪𝘤 | | `***bold-italic***` / `___bold-italic___` | Unicode 𝙗𝙤𝙡𝙙-𝙞𝙩𝙖𝙡𝙞𝙘 | | `` `inline code` `` | Unicode 𝚖𝚘𝚗𝚘𝚜𝚙𝚊𝚌𝚎 (backticks stripped) | | ` ```fenced block``` ` | Unicode 𝚖𝚘𝚗𝚘𝚜𝚙𝚊𝚌𝚎 (fences stripped) | | `# H1` | Bold Unicode + `━` border | | `## H2`–`###### H6` | Bold Unicode, no border | | `[text](url)` | `text` (URL discarded) | | `![alt](url)` | `alt` text (URL discarded) | | `- item` | `• item` | | ` - nested` | ` ‣ nested` | | `> blockquote` | `> ` prefix removed | | `...` | Tags removed, text kept | | `&` / `>` etc. | Decoded to `&` / `>` | | `\*` backslash escapes | Resolved to literal `*` | | Emojis, accented chars | Passed through unchanged | | Digits inside bold | Also converted (`**123**` → `𝟭𝟮𝟯`) | | `_snake_case_` in middle of word | **Not** italicised | --- ## Handling Edge Cases ### Nested Formatting `md2linkedin` handles nesting by processing bold-italic (`***`) first, ensuring it is not accidentally consumed piecemeal: ```python convert("***very important***") # → bold-italic Unicode convert("**bold and *italic* inside**") # → bold wrapping italic ``` ### Code Is Rendered in Monospace By default, inline code and fenced code blocks are converted to Unicode Mathematical Monospace characters. Only ASCII letters and digits are mapped; all other characters (including Markdown syntax like `**`) pass through unchanged — no nested processing is performed: ```python convert("Use `**bold**` in Markdown") # → Use **𝚋𝚘𝚕𝚍** in Markdown (backticks stripped, letters monospaced, ** kept) convert("```\nprint('hi')\n```") # → 𝚙𝚛𝚒𝚗𝚝('𝚑𝚒') (fences stripped, content monospaced) ``` To disable monospace rendering and restore the previous plain-text behavior: ```python convert("Use `**bold**` in Markdown", monospace_code=False) # → Use **bold** in Markdown (backticks stripped, content as plain text) convert("```\n**not bold**\n```", monospace_code=False) # → ```\n**not bold**\n``` (fenced block fully preserved) ``` ### Underscore Italic vs. Snake Case Single underscores are italicised only when they sit at word boundaries: ```python convert("_italic_") # → italic Unicode convert("snake_case_variable") # → unchanged ``` ### Unmatched / Orphaned Markers Unmatched asterisks or underscores are left untouched — `md2linkedin` never crashes on malformed input: ```python convert("price: $10 * 3") # → price: $10 * 3 (no crash) convert("under_score alone") # → unchanged ``` ### Large Documents `convert()` is a pure-Python, single-pass function with no I/O. It scales linearly with input length and is safe to call in hot paths or on large files: ```python big_md = Path("quarterly_report.md").read_text() result = convert(big_md) ``` ### Emojis and Non-ASCII Characters Emojis and accented characters pass through unchanged — only ASCII letters and digits are remapped to Unicode Mathematical variants: ```python from md2linkedin import convert result = convert("**Excited to share** 🎉 *check this out*!") print(result) # 𝗘𝘅𝗰𝗶𝘁𝗲𝗱 𝘁𝗼 𝘀𝗵𝗮𝗿𝗲 🎉 𝘤𝘩𝘦𝘤𝘬 𝘵𝘩𝘪𝘴 𝘰𝘂𝘵! ``` --- ## Running Without Installing (uvx) If you only need `md2linkedin` occasionally, you can run it directly with [`uvx`](https://docs.astral.sh/uv/guides/tools/) without a permanent installation: ```bash # Convert a Markdown file (output: post.linkedin.txt) uvx md2linkedin post.md # Pipe from stdin echo "**bold** and *italic*" | uvx md2linkedin # Preserve link syntax uvx md2linkedin --preserve-links post.md # Explicit output path uvx md2linkedin post.md -o linkedin_post.txt # Disable monospace code rendering uvx md2linkedin --no-monospace-code post.md ``` `uvx` downloads and caches the package in an isolated environment, so subsequent runs are fast. --- ## Integration with Pandoc (LaTeX → LinkedIn) If you write in LaTeX (e.g. a résumé), you can pipe through `pandoc` first: ```bash pandoc --from=latex --to=markdown_strict --wrap=none resume.tex | md2linkedin ``` Or in Python: ```python import subprocess from md2linkedin import convert result = subprocess.run( ["pandoc", "--from=latex", "--to=markdown_strict", "--wrap=none"], input=Path("resume.tex").read_text(), capture_output=True, text=True, check=True, ) linkedin_text = convert(result.stdout) ``` --- ## CLI Reference ``` Usage: md2linkedin [OPTIONS] [INPUT_FILE] Convert Markdown to LinkedIn-friendly Unicode text. Options: -o, --output PATH Output file path. --preserve-links Keep link syntax in output. --no-monospace-code Disable monospace Unicode rendering for code. -V, --version Show the version and exit. -h, --help Show this message and exit. ``` ## API documentation ### converter # API Reference ## High-level API ::: md2linkedin.convert ::: md2linkedin.convert_file ## Unicode Mapping ::: md2linkedin.apply_style ::: md2linkedin.to_monospace ::: md2linkedin.to_sans_bold ::: md2linkedin.to_sans_italic ::: md2linkedin.to_sans_bold_italic ## Changelog ### changelog --8<-- "CHANGELOG.md"