Skip to content

API Reference

High-level API

Convert Markdown text to LinkedIn-compatible Unicode plain text.

Bold (**text** / __text__), italic (*text* / _text_), and bold-italic (***text*** / ___text___) markers are replaced with their Unicode Mathematical Sans-Serif equivalents so that the styling is preserved when pasting into LinkedIn or other plain-text rich editors.

The following Markdown constructs are also handled:

  • Headers — ATX (#) and setext styles; H1 gets a border.
  • Code spans — backticks stripped; content converted to Unicode Monospace by default (see monospace_code).
  • Fenced code blocks — fences stripped and content converted to Unicode Monospace by default (see monospace_code).
  • Links — stripped to display text by default (see preserve_links).
  • Images — replaced by alt text.
  • Bullet lists- / * / + / (nested).
  • Blockquotes — leading > stripped.
  • HTML spans — unwrapped, inner text kept.
  • HTML entities — decoded to literal characters.
  • Backslash escapes — resolved (\**).
  • Windows line endings — normalised to \n.

Parameters:

Name Type Description Default
text str

The Markdown source string.

required
preserve_links bool

When True, link syntax ([text](url)) is left unchanged in the output instead of being reduced to display text.

False
monospace_code bool

When True (the default), inline code spans and fenced code blocks are rendered in Unicode Mathematical Monospace. When False, inline code is kept as plain text and fenced blocks are preserved verbatim.

True

Returns:

Type Description
str

A plain-text string suitable for pasting into LinkedIn.

Examples:

>>> convert("**Hello**, *world*!")
'𝗛𝗲𝗹𝗹𝗼, 𝘸𝘰𝘳𝘭𝘥!\n'
>>> convert("")
''
Source code in src/md2linkedin/_converter.py
def convert(
    text: str,
    *,
    preserve_links: bool = False,
    monospace_code: bool = True,
) -> str:
    """Convert Markdown text to LinkedIn-compatible Unicode plain text.

    Bold (``**text**`` / ``__text__``), italic (``*text*`` / ``_text_``), and
    bold-italic (``***text***`` / ``___text___``) markers are replaced with
    their Unicode Mathematical Sans-Serif equivalents so that the styling is
    preserved when pasting into LinkedIn or other plain-text rich editors.

    The following Markdown constructs are also handled:

    * **Headers** — ATX (``#``) and setext styles; H1 gets a ``━`` border.
    * **Code spans** — backticks stripped; content converted to Unicode
      Monospace by default (see *monospace_code*).
    * **Fenced code blocks** — fences stripped and content converted to
      Unicode Monospace by default (see *monospace_code*).
    * **Links** — stripped to display text by default (see *preserve_links*).
    * **Images** — replaced by alt text.
    * **Bullet lists** — ``-`` / ``*`` / ``+`` → ``•`` / ``‣`` (nested).
    * **Blockquotes** — leading ``>`` stripped.
    * **HTML spans** — unwrapped, inner text kept.
    * **HTML entities** — decoded to literal characters.
    * **Backslash escapes** — resolved (``\\*`` → ``*``).
    * **Windows line endings** — normalised to ``\\n``.

    Args:
        text: The Markdown source string.
        preserve_links: When ``True``, link syntax (``[text](url)``) is left
            unchanged in the output instead of being reduced to display text.
        monospace_code: When ``True`` (the default), inline code spans and
            fenced code blocks are rendered in Unicode Mathematical Monospace.
            When ``False``, inline code is kept as plain text and fenced
            blocks are preserved verbatim.

    Returns:
        A plain-text string suitable for pasting into LinkedIn.

    Examples:
        >>> convert("**Hello**, *world*!")
        '𝗛𝗲𝗹𝗹𝗼, 𝘸𝘰𝘳𝘭𝘥!\\n'

        >>> convert("")
        ''
    """
    if not text or not text.strip():
        return ""

    text = _normalize_line_endings(text)
    text, placeholders = _protect_code(text)
    text = _strip_html_spans(text)
    text = _strip_images(text)
    text = _convert_bold_italic(text)
    text = _convert_bold(text)
    text = _convert_italic(text)
    text = _convert_headers(text)
    text = _strip_links(text, preserve=preserve_links)
    text = _convert_bullets(text)
    text = _strip_blockquotes(text)
    text = _restore_code(text, placeholders, monospace=monospace_code)
    text = _clean_entities(text)
    text = _clean_escaped_chars(text)
    return _normalize_whitespace(text)

Convert a Markdown file and write the result to a .txt file.

Parameters:

Name Type Description Default
input_path str | Path

Path to the Markdown source file (.md or any text file).

required
output_path str | Path | None

Destination path for the converted output. Defaults to the input path with the extension replaced by .linkedin.txt.

None
preserve_links bool

Passed through to :func:convert.

False
monospace_code bool

Passed through to :func:convert.

True

Returns:

Type Description
Path

The resolved path of the written output file.

Raises:

Type Description
FileNotFoundError

If input_path does not exist.

Examples:

>>> from pathlib import Path
>>> import tempfile, os
>>> with tempfile.NamedTemporaryFile(mode="w", suffix=".md", delete=False) as f:
...     _ = f.write("**bold** and *italic*")
...     tmp = f.name
>>> out = convert_file(tmp)
>>> out.read_text(encoding="utf-8")
'𝗯𝗼𝗹𝗱 and 𝘪𝘵𝘢𝘭𝘪𝘤\n'
>>> os.unlink(tmp)
... os.unlink(str(out))
Source code in src/md2linkedin/_converter.py
def convert_file(
    input_path: str | Path,
    output_path: str | Path | None = None,
    *,
    preserve_links: bool = False,
    monospace_code: bool = True,
) -> Path:
    """Convert a Markdown file and write the result to a ``.txt`` file.

    Args:
        input_path: Path to the Markdown source file (``.md`` or any text
            file).
        output_path: Destination path for the converted output.  Defaults to
            the input path with the extension replaced by
            ``.linkedin.txt``.
        preserve_links: Passed through to :func:`convert`.
        monospace_code: Passed through to :func:`convert`.

    Returns:
        The resolved path of the written output file.

    Raises:
        FileNotFoundError: If *input_path* does not exist.

    Examples:
        >>> from pathlib import Path
        >>> import tempfile, os
        >>> with tempfile.NamedTemporaryFile(mode="w", suffix=".md", delete=False) as f:
        ...     _ = f.write("**bold** and *italic*")
        ...     tmp = f.name
        >>> out = convert_file(tmp)
        >>> out.read_text(encoding="utf-8")
        '𝗯𝗼𝗹𝗱 and 𝘪𝘵𝘢𝘭𝘪𝘤\\n'
        >>> os.unlink(tmp)
        ... os.unlink(str(out))
    """
    input_path = Path(input_path)
    if not input_path.exists():
        msg = f"Input file not found: {input_path}"
        raise FileNotFoundError(msg)

    if output_path is None:
        output_path = input_path.with_suffix("").with_suffix(".linkedin.txt")
    output_path = Path(output_path)

    md_text = input_path.read_text(encoding="utf-8")
    result = convert(
        md_text,
        preserve_links=preserve_links,
        monospace_code=monospace_code,
    )
    output_path.write_text(result, encoding="utf-8")
    return output_path

Unicode Mapping

Apply a Unicode sans-serif style to text.

A convenience dispatcher that routes to the appropriate mapping function based on the requested style. Useful when the style is determined dynamically at runtime.

Parameters:

Name Type Description Default
text str

The input string to convert.

required
style Literal['bold', 'italic', 'bold_italic']

One of "bold", "italic", or "bold_italic".

required

Returns:

Type Description
str

A new string with the requested Unicode style applied.

Raises:

Type Description
ValueError

If style is not one of the three accepted values.

Examples:

>>> apply_style("hello", "bold")
'𝗵𝗲𝗹𝗹𝗼'
>>> apply_style("hello", "italic")
'𝘩𝘦𝘭𝘭𝘰'
>>> apply_style("hello", "bold_italic")
'𝙝𝙚𝙡𝙡𝙤'
Source code in src/md2linkedin/_unicode.py
def apply_style(text: str, style: Literal["bold", "italic", "bold_italic"]) -> str:
    """Apply a Unicode sans-serif style to text.

    A convenience dispatcher that routes to the appropriate mapping function
    based on the requested style. Useful when the style is determined
    dynamically at runtime.

    Args:
        text: The input string to convert.
        style: One of ``"bold"``, ``"italic"``, or ``"bold_italic"``.

    Returns:
        A new string with the requested Unicode style applied.

    Raises:
        ValueError: If *style* is not one of the three accepted values.

    Examples:
        >>> apply_style("hello", "bold")
        '𝗵𝗲𝗹𝗹𝗼'

        >>> apply_style("hello", "italic")
        '𝘩𝘦𝘭𝘭𝘰'

        >>> apply_style("hello", "bold_italic")
        '𝙝𝙚𝙡𝙡𝙤'
    """
    if style == "bold":
        return to_sans_bold(text)
    if style == "italic":
        return to_sans_italic(text)
    if style == "bold_italic":
        return to_sans_bold_italic(text)
    msg = f"Unknown style {style!r}. Expected 'bold', 'italic', or 'bold_italic'."
    raise ValueError(msg)

Convert text to Unicode Mathematical Monospace.

ASCII uppercase letters, lowercase letters, and digits are mapped to their monospace counterparts. All other characters (spaces, punctuation, non-ASCII) pass through unchanged.

Parameters:

Name Type Description Default
text str

The input string to convert.

required

Returns:

Type Description
str

A new string with ASCII alphanumerics replaced by monospace

str

Unicode equivalents.

Examples:

>>> to_monospace("Hello, World! 123")
'𝙷𝚎𝚕𝚕𝚘, 𝚆𝚘𝚛𝚕𝚍! 𝟷𝟸𝟹'
>>> to_monospace("café")
'𝚌𝚊𝚏é'
>>> to_monospace("")
''
Source code in src/md2linkedin/_unicode.py
def to_monospace(text: str) -> str:
    """Convert text to Unicode Mathematical Monospace.

    ASCII uppercase letters, lowercase letters, and digits are mapped to
    their monospace counterparts. All other characters (spaces,
    punctuation, non-ASCII) pass through unchanged.

    Args:
        text: The input string to convert.

    Returns:
        A new string with ASCII alphanumerics replaced by monospace
        Unicode equivalents.

    Examples:
        >>> to_monospace("Hello, World! 123")
        '𝙷𝚎𝚕𝚕𝚘, 𝚆𝚘𝚛𝚕𝚍! 𝟷𝟸𝟹'

        >>> to_monospace("café")
        '𝚌𝚊𝚏é'

        >>> to_monospace("")
        ''
    """
    out: list[str] = []
    for c in text:
        if "A" <= c <= "Z":
            out.append(chr(_MONOSPACE_UPPER + ord(c) - ord("A")))
        elif "a" <= c <= "z":
            out.append(chr(_MONOSPACE_LOWER + ord(c) - ord("a")))
        elif "0" <= c <= "9":
            out.append(chr(_MONOSPACE_DIGIT + ord(c) - ord("0")))
        else:
            out.append(c)
    return "".join(out)

Convert text to Unicode Mathematical Sans-Serif Bold.

ASCII uppercase letters, lowercase letters, and digits are mapped to their bold sans-serif counterparts. All other characters (spaces, punctuation, non-ASCII) pass through unchanged.

Parameters:

Name Type Description Default
text str

The input string to convert.

required

Returns:

Type Description
str

A new string with ASCII alphanumerics replaced by bold sans-serif

str

Unicode equivalents.

Examples:

>>> to_sans_bold("Hello, World! 123")
'𝗛𝗲𝗹𝗹𝗼, 𝗪𝗼𝗿𝗹𝗱! 𝟭𝟮𝟯'
>>> to_sans_bold("café")
'𝗰𝗮𝗳é'
>>> to_sans_bold("")
''
Source code in src/md2linkedin/_unicode.py
def to_sans_bold(text: str) -> str:
    """Convert text to Unicode Mathematical Sans-Serif Bold.

    ASCII uppercase letters, lowercase letters, and digits are mapped to
    their bold sans-serif counterparts. All other characters (spaces,
    punctuation, non-ASCII) pass through unchanged.

    Args:
        text: The input string to convert.

    Returns:
        A new string with ASCII alphanumerics replaced by bold sans-serif
        Unicode equivalents.

    Examples:
        >>> to_sans_bold("Hello, World! 123")
        '𝗛𝗲𝗹𝗹𝗼, 𝗪𝗼𝗿𝗹𝗱! 𝟭𝟮𝟯'

        >>> to_sans_bold("café")
        '𝗰𝗮𝗳é'

        >>> to_sans_bold("")
        ''
    """
    out: list[str] = []
    for c in text:
        if "A" <= c <= "Z":
            out.append(chr(_SANS_BOLD_UPPER + ord(c) - ord("A")))
        elif "a" <= c <= "z":
            out.append(chr(_SANS_BOLD_LOWER + ord(c) - ord("a")))
        elif "0" <= c <= "9":
            out.append(chr(_SANS_BOLD_DIGIT + ord(c) - ord("0")))
        else:
            out.append(c)
    return "".join(out)

Convert text to Unicode Mathematical Sans-Serif Italic.

ASCII uppercase and lowercase letters are mapped to their italic sans-serif counterparts. Digits and all other characters pass through unchanged (there are no italic digit codepoints in this Unicode block).

Parameters:

Name Type Description Default
text str

The input string to convert.

required

Returns:

Type Description
str

A new string with ASCII letters replaced by italic sans-serif

str

Unicode equivalents.

Examples:

>>> to_sans_italic("Hello, World!")
'𝘏𝘦𝘭𝘭𝘰, 𝘞𝘰𝘳𝘭𝘥!'
>>> to_sans_italic("price: $42")
'𝘱𝘳𝘪𝘤𝘦: $42'
>>> to_sans_italic("")
''
Source code in src/md2linkedin/_unicode.py
def to_sans_italic(text: str) -> str:
    """Convert text to Unicode Mathematical Sans-Serif Italic.

    ASCII uppercase and lowercase letters are mapped to their italic
    sans-serif counterparts. Digits and all other characters pass through
    unchanged (there are no italic digit codepoints in this Unicode block).

    Args:
        text: The input string to convert.

    Returns:
        A new string with ASCII letters replaced by italic sans-serif
        Unicode equivalents.

    Examples:
        >>> to_sans_italic("Hello, World!")
        '𝘏𝘦𝘭𝘭𝘰, 𝘞𝘰𝘳𝘭𝘥!'

        >>> to_sans_italic("price: $42")
        '𝘱𝘳𝘪𝘤𝘦: $42'

        >>> to_sans_italic("")
        ''
    """
    out: list[str] = []
    for c in text:
        if "A" <= c <= "Z":
            out.append(chr(_SANS_ITALIC_UPPER + ord(c) - ord("A")))
        elif "a" <= c <= "z":
            out.append(chr(_SANS_ITALIC_LOWER + ord(c) - ord("a")))
        else:
            out.append(c)
    return "".join(out)

Convert text to Unicode Mathematical Sans-Serif Bold Italic.

ASCII uppercase and lowercase letters are mapped to their bold-italic sans-serif counterparts. Digits and all other characters pass through unchanged.

Parameters:

Name Type Description Default
text str

The input string to convert.

required

Returns:

Type Description
str

A new string with ASCII letters replaced by bold-italic sans-serif

str

Unicode equivalents.

Examples:

>>> to_sans_bold_italic("Hello, World!")
'𝙃𝙚𝙡𝙡𝙤, 𝙒𝙤𝙧𝙡𝙙!'
>>> to_sans_bold_italic("")
''
Source code in src/md2linkedin/_unicode.py
def to_sans_bold_italic(text: str) -> str:
    """Convert text to Unicode Mathematical Sans-Serif Bold Italic.

    ASCII uppercase and lowercase letters are mapped to their bold-italic
    sans-serif counterparts. Digits and all other characters pass through
    unchanged.

    Args:
        text: The input string to convert.

    Returns:
        A new string with ASCII letters replaced by bold-italic sans-serif
        Unicode equivalents.

    Examples:
        >>> to_sans_bold_italic("Hello, World!")
        '𝙃𝙚𝙡𝙡𝙤, 𝙒𝙤𝙧𝙡𝙙!'

        >>> to_sans_bold_italic("")
        ''
    """
    out: list[str] = []
    for c in text:
        if "A" <= c <= "Z":
            out.append(chr(_SANS_BOLD_ITALIC_UPPER + ord(c) - ord("A")))
        elif "a" <= c <= "z":
            out.append(chr(_SANS_BOLD_ITALIC_LOWER + ord(c) - ord("a")))
        else:
            out.append(c)
    return "".join(out)