{"id":26697793,"url":"https://github.com/otovo/python-portabletext-html","last_synced_at":"2025-04-13T04:36:11.994Z","repository":{"id":37415780,"uuid":"357665277","full_name":"otovo/python-portabletext-html","owner":"otovo","description":"Generate HTML from Portable Text","archived":false,"fork":false,"pushed_at":"2022-11-18T21:24:50.000Z","size":139,"stargazers_count":23,"open_issues_count":2,"forks_count":0,"subscribers_count":10,"default_branch":"main","last_synced_at":"2025-04-09T02:48:26.893Z","etag":null,"topics":["block-content","html-renderer","portable-text","sanity","sanity-io"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/otovo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-04-13T19:22:36.000Z","updated_at":"2024-07-01T21:42:22.000Z","dependencies_parsed_at":"2023-01-20T21:18:58.721Z","dependency_job_id":null,"html_url":"https://github.com/otovo/python-portabletext-html","commit_stats":null,"previous_names":["otovo/python-sanity-html"],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/otovo%2Fpython-portabletext-html","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/otovo%2Fpython-portabletext-html/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/otovo%2Fpython-portabletext-html/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/otovo%2Fpython-portabletext-html/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/otovo","download_url":"https://codeload.github.com/otovo/python-portabletext-html/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248665368,"owners_count":21142119,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["block-content","html-renderer","portable-text","sanity","sanity-io"],"created_at":"2025-03-26T21:30:08.784Z","updated_at":"2025-04-13T04:36:11.974Z","avatar_url":"https://github.com/otovo.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![pypi](https://img.shields.io/pypi/v/portabletext-html.svg)](https://pypi.org/project/portabletext-html/)\n[![test](https://github.com/otovo/python-portabletext-html/actions/workflows/test.yml/badge.svg)](https://github.com/otovo/python-portabletext-html/actions/workflows/test.yml)\n[![code coverage](https://codecov.io/gh/otovo/python-portabletext-html/branch/main/graph/badge.svg)](https://codecov.io/gh/otovo/python-portabletext-html)\n[![supported python versions](https://img.shields.io/badge/python-3.7%2B-blue)](https://pypi.org/project/python-portabletext-html/)\n\n# Portable Text HTML Renderer for Python\n\nThis package generates HTML from [Portable Text](https://github.com/portabletext/portabletext).\n\nFor the most part, it mirrors [Sanity's](https://www.sanity.io/) own [block-content-to-html](https://www.npmjs.com/package/%40sanity/block-content-to-html) NPM library.\n\n## Installation\n\n```\npip install portabletext-html\n```\n\n## Usage\n\nInstantiate the `PortableTextRenderer` class with your content and call the `render` method.\n\nThe following content\n\n```python\nfrom portabletext_html import PortableTextRenderer\n\nrenderer = PortableTextRenderer({\n    \"_key\": \"R5FvMrjo\",\n    \"_type\": \"block\",\n    \"children\": [\n        {\"_key\": \"cZUQGmh4\", \"_type\": \"span\", \"marks\": [\"strong\"], \"text\": \"A word of\"},\n        {\"_key\": \"toaiCqIK\", \"_type\": \"span\", \"marks\": [\"strong\"], \"text\": \" warning;\"},\n        {\"_key\": \"gaZingsA\", \"_type\": \"span\", \"marks\": [], \"text\": \" Sanity is addictive.\"}\n    ],\n    \"markDefs\": [],\n    \"style\": \"normal\"\n})\nrenderer.render()\n```\n\nGenerates this HTML\n```html\n\u003cp\u003e\u003cstrong\u003eA word of warning;\u003c/strong\u003e Sanity is addictive.\u003c/p\u003e\n```\n\n### Supported types\n\nThe `block` and `span` types are supported out of the box.\n\n### Custom types\n\nBeyond the built-in types, you have the freedom to provide\nyour own serializers to render any custom `_type` the way you\nwould like to.\n\nTo illustrate, if you passed this data to the renderer class:\n\n```python\nfrom portabletext_html import PortableTextRenderer\n\nrenderer = PortableTextRenderer({\n    \"_type\": \"block\",\n    \"_key\": \"foo\",\n    \"style\": \"normal\",\n    \"children\": [\n        {\n            \"_type\": \"span\",\n            \"text\": \"Press, \"\n        },\n        {\n            \"_type\": \"button\",\n            \"text\": \"here\"\n        },\n        {\n            \"_type\": \"span\",\n            \"text\": \", now!\"\n        }\n    ]\n})\nrenderer.render()\n```\n\nThe renderer would actually throw an error here, since `button`\ndoes not have a corresponding built-in type serializer by default.\n\nTo render this text you must provide your own serializer, like this:\n\n```python\nfrom portabletext_html import PortableTextRenderer\n\n\ndef button_serializer(node: dict, context: Optional[Block], list_item: bool):\n    return f'\u003cbutton\u003e{node[\"text\"]}\u003c/button\u003e'\n\n\nrenderer = PortableTextRenderer(\n    ...,\n    custom_serializers={'button': button_serializer}\n)\noutput = renderer.render()\n```\n\nWith the custom serializer provided, the renderer would now successfully\noutput the following HTML:\n\n```html\n\u003cp\u003ePress \u003cbutton\u003ehere\u003c/button\u003e, now!\u003c/p\u003e\n```\n\n### Supported mark definitions\n\nThe package provides several built-in marker definitions and styles:\n\n**decorator marker definitions**\n\n- `em`\n- `strong`\n- `code`\n- `underline`\n- `strike-through`\n\n**annotation marker definitions**\n\n- `link`\n- `comment`\n\n### Custom mark definitions\n\nLike with custom type serializers, additional serializers for\nmarker definitions and styles can be passed in like this:\n\n```python\nfrom portabletext_html import PortableTextRenderer\n\nrenderer = PortableTextRenderer(\n    ...,\n    custom_marker_definitions={'em': ComicSansEmphasis}\n)\nrenderer.render()\n```\n\nThe primary difference between a type serializer and a mark definition serializer\nis that the latter uses a class structure, and has three required methods.\n\nHere's an example of a custom style, adding an extra font\nto the built-in equivalent serializer:\n\n```python\nfrom portabletext_html.marker_definitions import MarkerDefinition\n\n\nclass ComicSansEmphasis(MarkerDefinition):\n    tag = 'em'\n\n    @classmethod\n    def render_prefix(cls, span: Span, marker: str, context: Block) -\u003e str:\n        return f'\u003c{cls.tag} style=\"font-family: \"Comic Sans MS\", \"Comic Sans\", cursive;\"\u003e'\n\n    @classmethod\n    def render_suffix(cls, span: Span, marker: str, context: Block) -\u003e str:\n        return f'\u003c/{cls.tag}\u003e'\n\n    @classmethod\n    def render_text(cls, span: Span, marker: str, context: Block) -\u003e str:\n        # custom rendering logic can be placed here\n        return str(span.text)\n\n    @classmethod\n    def render(cls, span: Span, marker: str, context: Block) -\u003e str:\n        result = cls.render_prefix(span, marker, context)\n        result += str(span.text)\n        result += cls.render_suffix(span, marker, context)\n        return result\n```\n\nSince the `render_suffix` and `render` methods here are actually identical to the base class,\nthey do not need to be specified, and the whole example can be reduced to:\n\n```python\nfrom portabletext_html.marker_definitions import MarkerDefinition  # base\nfrom portabletext_html import PortableTextRenderer\n\n\nclass ComicSansEmphasis(MarkerDefinition):\n    tag = 'em'\n\n    @classmethod\n    def render_prefix(cls, span: Span, marker: str, context: Block) -\u003e str:\n        return f'\u003c{cls.tag} style=\"font-family: \"Comic Sans MS\", \"Comic Sans\", cursive;\"\u003e'\n\n\nrenderer = PortableTextRenderer(\n    ...,\n    custom_marker_definitions={'em': ComicSansEmphasis}\n)\nrenderer.render()\n```\n\n\n### Supported styles\n\nBlocks can optionally define a `style` tag. These styles are supported:\n\n- `h1`\n- `h2`\n- `h3`\n- `h4`\n- `h5`\n- `h6`\n- `blockquote`\n- `normal`\n\n## Missing features\n\nFor anyone interested, we would be happy to see a\ndefault built-in serializer for the `image` type added.\nIn the meantime, users should be able to serialize image types by passing a custom serializer.\n\n## Contributing\n\nContributions are always appreciated 👏\n\nFor details, see the [CONTRIBUTING.md](https://github.com/otovo/python-portabletext-html/blob/main/CONTRIBUTING.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fotovo%2Fpython-portabletext-html","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fotovo%2Fpython-portabletext-html","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fotovo%2Fpython-portabletext-html/lists"}