#import "mod.typ": *

#show: book-page.with(title: "Exporting to Other Markup Formats")

#note-box[
  This feature is currently in early development.
]

#github-link("/crates/typlite/")[typlite] is a pure Rust library for converting Typst documents to other markup formats.


typlite's goal is to convert docstrings in typst packages to LSP docs (Markdown format). To achieve this, it runs HTML export and extracts semantic information from the HTML document for markup conversion.

#let out-format = box.with(width: 5em)
#let typlite-convert-graph(theme) = {
  let (colors, node, edge) = fletcher-ctx(theme)
  diagram(
    node-stroke: 1pt,
    edge-stroke: 1pt,
    node((0.5, 0), [Typst Source Code]),
    edge("-|>", link("https://typst.app/docs/reference/html/")[HTML Export]),
    node((3, 0), [```xml <xml:typlite/>```]),
    edge("-|>"),
    node((5, 0), out-format[LaTeX]),
    edge((3, 0), (5, -0.7), "-|>"),
    node((5, -0.7), out-format[Markdown]),
    edge((3, 0), (5, 0.7), "-|>"),
    node((5, 0.7), out-format[DocX]),
  )
}

#figure(
  cond-image(typlite-convert-graph),
  caption: [The conversion path from typst source code to other markup formats, by typlite.],
) <fig:typlite-conversion>

= TodoList

- [ ] Renders figures into PDF instead of SVG.
- [ ] Converts typst equations, might use #link("https://github.com/jgm/texmath")[texmath] or #link("https://codeberg.org/akida/mathyml")[mathyml] plus #link("https://github.com/davidcarlisle/web-xslt/tree/main/pmml2tex")[pmml2tex].

= Example: Writing README in typst

To export to Markdown, run the command:

```bash
typlite README.typ README.md --assets-path assets
```

== Assets Path

You might love to use `html.frame` to render typst examples in `README.md`. By default, the examples are embedded in the output by data url. To externalize them, please specify the `--assets-path` option.

== Implementing target-aware functions

typlite will set `sys.inputs.x-target` to `md` if it is exporting to Markdown. You can use this variable to implement target-aware functions in your typst documents.

```typ
#let x-target = sys.inputs.at("x-target", default: "pdf")
#let is-md-target = x-target == "md"
```

For example, you can implement a GitHub link function, which determines the link based on the target:
```typ
#let current-revision = read("/.git/" + read("/.git/HEAD").trim().slice(5)).trim()

#let github-link(path, body, kind: none) = {
  let dest = if is-md-target {
    path
  } else {
    if kind == none {
      kind = if path.ends-with("/") { "tree" } else { "blob" }
    }
    (remote, kind, current-revision, path).join("/")
  }

  link(dest, body)
}

```

= Example: Styling a Typst Document by IEEE LaTeX Template

#let paper-file-link(link, body) = github-link("/editors/vscode/e2e-workspaces/ieee-paper" + link, body)

The `main.typ` in the #paper-file-link("/")[Sample Workspace: IEEE Paper] can be converted perfectly.

- Run the command： ```bash
  typlite main.typ main.tex --processor "/ieee-tex.typ"
  ```
- Create a project on Overleaf, using the #link("https://www.overleaf.com/latex/templates/ieee-demo-template-for-computer-society-conferences/hzzszpqfkqky")[IEEE LaTeX Template.]
- Upload the `main.tex` file and exported PDF assets and it will get rendered and ready to submit.

== Processor Scripts

The CLI command in the previous example uses the `--processor "/ieee-tex.typ"` option, which is not a flag of the official `typst-cli`. The option tells typlite to use a processor script to process the HTML export result for LaTeX export.

typlite will show your main documents with the `article` function obtained from the processor file.

````typst
#let verbatim(body) = {
  show raw.where(lang: "tex"): it => html.elem("m1verbatim", attrs: (src: it.text))
  body
}
#let article(body) = {
  verbatim(```tex
  %% Generated by typlite
  %% Please report to https://github.com/Myriad-Dreamin/tinymist if you find any bugs.

  \begin{document}
  Hey,
  ```)
  body
  verbatim(```tex
  \end{document}
  ```)
}
````

#note-box[
  Currently, ```typc html.elem("m1verbatim")``` is the only `xml` element can be used by processor scripts. When seeing a `<m1verbatim/>` element, typlite writes the inner content to output directly.
]

It will output like that:

```tex
%% Generated by typlite
%% Please report to https://github.com/Myriad-Dreamin/tinymist if you find any bugs.

\begin{document}
Hey, [CONTENT generated according to body]
\end{document}
```

You can implement the `article` function for different markup formats, such as LaTeX, Markdown, DocX, and Plain Text.

== Using Processor Packages

The processor script can be not only a file, but also a package:

- From current workspace: ```typc "/ieee-tex.typ"``` is the file relative to the current workspace root.
- From a package: ```typc "@local/ieee-tex:0.1.0"``` or ```typc "@preview/ieee-tex:0.1.0"``` can be used to get functions from local packages or #link("https://typst.app/universe/")[typst universe.]

= Perfect Conversion

typlite is called "-ite" because it only ensures that nice docstrings are converted perfectly. Similarly, if your document looks nice, typlite can also convert it to other markup formats perfectly.

This introduces concept of _Semantic Typst._ To help conversion, you should separate styling scripts and semantic content in your typst documents.

A good example in HTML is ```html <strong>``` v.s. ```html <b>```. Written in typst,

```typ
#strong[Good Content]
#text(weight: 700)[Bad Content]
```

typlite can convert "Good Content" perfectly, but not "Bad Content". This is because we can attach markup-specific styles to "Good Content" then, but "Bad Content" may be broken by some reasons, such as failing to find font weight when rendering the content.

To style your typst documents, rewriting the bad content by show rule is suggested:

```typ
#show strong: it => if is-md-target {
  // style for Markdown target, for example:
  html.span(class: "my-strong", it.body)
} else { // style for builtin Typst targets:
  text(weight: 700, it.body)
}
#strong[Bad Content]
```

typlite will feel happy and make perfect conversion if you keep aware of keep pure semantics of `main.typ` documents in the above ways.

= Implementing `abstract` for IEEE LaTeX Template

Let's explain how #paper-file-link("/ieee-tex.typ")[`/ieee-tex.typ`] works by the `abstract` example. First, we edit #paper-file-link("/ieee-tex.typ")[`/ieee-template.typ`] to store the abstract in state:

```typ
#let abstract-state = state("tex:abstract", "")
#let abstract(body) = if is-md-target {
  abstract-state.update(_ => body)
} else {
    // fallback to regular abstract
}
```

#note-box[
  `is-md-target` already distinguishes regular typst PDF export and typlite export (which uses HTML export). We haven't decide a way to let your template aware of LaTeX export.

  Luckily, typst has `sys.input` mechanism so you can distinguish it by yourself:

  ```typ
  // typst compile or typlite main.typ --input exporter=typlite-tex
  #let exporter = sys.inputs.at("exporter", default: "typst")
  #exporter // "typst" or "typlite-tex"
  ```

  Or define a state that shares between the template and the processor script:

  ```typ
  #let is-typlite = state("is-typlite", false)
  // processor.typ
  #let article(body) = {
    is-typlite.update(_ => true)
    body
  }
  // template.typ
  #let is-typlite = ...
  #let abstract(body) = context if is-typlite {
    abstract-state.update(_ => body)
  } else {
    // fallback to regular abstract
  }
  ```
]

Next, in the `ieee-tex.typ`, we can get the `abstract` material from the state and render it in the LaTeX template:

````typ
#let abstract = context {
  let abstract-body = state("tex:abstract", "").final()
  verbatim(```tex
  % As a general rule, do not put math, special symbols or citations
  % in the abstract
  \begin{abstract}
  ```)
  abstract-body
  verbatim(```tex
  \end{abstract}
  ```)
}
````

#note-box[
  We don't extracts content from `abstract-body`. Inteadly, we put the body directly in the document, to let typlite process the equations in `abstract-body` and convert them to LaTeX.
]
