Skip to content

Support HTML#2

Open
Freed-Wu wants to merge 1 commit into
rousbound:mainfrom
Freed-Wu:html
Open

Support HTML#2
Freed-Wu wants to merge 1 commit into
rousbound:mainfrom
Freed-Wu:html

Conversation

@Freed-Wu

Copy link
Copy Markdown

Fix #1

@rousbound

rousbound commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Hello @Freed-Wu,

Thank you for your interest in the project and your PR.

Let me just organize your proposal.

In the original #1 issue, I mentioned that this is the state of the basic/advanced API I am planning.

-- basic API
-- @param file string of typst document 
-- @param input table|nil that corresponds to sys.inputs
typst.compile("file.typ", { data = "Hello world!" })

-- advanced API
-- @param args.file string of typst document
-- @param args.input table|nil that corresponds to sys.inputs
-- @param args.format  string|nil that represents formats, defaults to "pdf"
typst.compile{
 file = "file.typ",
 input = { data = "Hello world!" },
 format = "html" -- or "pdf", "svg", "png",
 -- And maybe other parameters in typst compile cli
 -- such as "ppi", however "format" is the most important for now
}

You are proposing this:

-- basic API
-- @param input string of typst document 
-- @param data table|nil that corresponds to sys.inputs
-- @param format  string|nil that represents formats, defaults to "pdf"
typst.compile("file.typ", { data = "Hello world!" }, "html|pdf|svg|etc")   

-- advanced API
-- no difference from my proposal although you mentioned interest in having
-- a "text" parameter to pass in the bytes of the typst document

I am not sure that choosing right now the third argument of the basic API to be the "format" is an idea that will evolve safely. Right now "format" is the natural third most important thing but this might change in the future and as I'm not the developer of typst I can't foresee it. So committing the third argument to "format" may not be the right call.

I think it would be more prudent to keep the basic API limited to the idea of "this compiles a typst document to PDF and has optional data as input". And let the format argument and other parameters live in the advanced API.

Obviously, I might be acting in an overly cautious way with these considerations, and I am open for debate.

But if you wish to implement the advanced API with the "format" option I think I would be more comfortable.

Also, if we are implementing the "format" argument, it is reasonable to implement all format options in order to not let the user be disappointed in having "html" as a possibility and not "svg", "png", etc implemented.

@Freed-Wu Freed-Wu mentioned this pull request Jul 9, 2026
Closed
@Freed-Wu

Freed-Wu commented Jul 9, 2026

Copy link
Copy Markdown
Author

Now it work for me:

> typst.compile{source = 'hello', format='html'}
_[1] = [[
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
  <body>
    <p>hello</p>
  </body>
</html>
]]

I advise to update typst to 0.15.0. Although it is another PR.

@rousbound

Copy link
Copy Markdown
Owner

Great! I didn't tested it yet but I saw that the other output formats are now implemented.

However, one thing that I would like to point out, If I understood correctly by taking a glance at the code, is that I think you overwrote the basic API with the advanced API.

The idea is to make them live together in a polymorphic way. That is:

  • check the first argument type:
    • if it is a string, then it is basic API
    • if it is a table, then it is the advanced API

@rousbound

Copy link
Copy Markdown
Owner

I advise to update typst to 0.15.0. Although it is another PR.

Yeah, totally! They have also improved typst-kit in 0.15.0 with new resources, which can possibly simplify typst_as_library code, however I didn't look much into it yet.

@Freed-Wu

Freed-Wu commented Jul 9, 2026

Copy link
Copy Markdown
Author

overwrote the basic API

I think old API has something easy to misunderstand. When I try it firstly, I am confused by typst.compile("hello, world") cannot work. Should we really keep the old behavior?

@rousbound

rousbound commented Jul 10, 2026

Copy link
Copy Markdown
Owner

That's interesting. As the developer of the API I had never thought of using it like that.

But given your misunderstanding I think we can reflect on it.

One starting point maybe to consider what I would call the "Lua/CLI API mismatch".

The typst.compile() function tries to stand on the middle between both Lua semantics and the original Typst CLI.

So, the first argument is faithful to the CLI: first argument is the input file.

In the second argument, we strive away from the CLI:

  • The second argument in the CLI is the output file, in Lua we return the bytes, so no need for output parameter.
  • The second argument on Lua API then its the "sys.inputs" data.

So on the second argument, we step in the direction of bending the CLI to our new binding semantics in Lua.

In this sense, I think the API that you expected, is to bend more towards Lua semantics, striving away from the CLI.

However, there's a catch, with this API:

-- "Compile Bytes API"
--@param bytes string document content
typst.compile("Hello world!") 

The problem is that we don't have now how to pass the "root dir" to Typst. When we pass a document path(with the current API) as:

-- "Current API"
--@param input string path to input file
typst.compile("helloworld.typ")

we are implicitly passing where to find files/images, etc(root dir is implied in the helloworld.typ location). So typst.compile(bytes) would have no way of knowing where to find resources. Unless we made something like:

-- "Compile Bytes API 2"
--@param bytes string document content
--@param root_dir string path to root dir. Default: current working directory
typst.compile_bytes("Hello world!", "src/templates/") 

But that doesn't feel very ergonomic I think.

I can think of a typst.compile_bytes that specializes in taking the document content from memory instead of filesystem, but that would be a second class function in the module I think, not the main "compile" itself.

My reasoning I think justifies why the API is not like that, however, I still agree that the use typst.compile(bytes) is possibly misleading.

Another alternative is "more polymorphism", if we take inspiration from the typst-py:

# Compile `hello.typ` to PDF and save as `hello.pdf`
typst.compile("hello.typ", output="hello.pdf")

# Or pass `hello.typ` content as bytes
with open("hello.typ", "rb") as f:
    typst.compile(f.read(), output="hello.pdf")

Although I'm not sure how to safely check when the string is a file path and when it is bytes, this seems like a possible idea to be more flexible while keeping the same API.

I am just brainstorming some possibilities based on your experience with the API.

For know, I would like to keep the original API and expand to the advanced one, because deprecating it would be a breaking change.

But I will give more thought about your idea of just deprecating the Basic API and having just the "Advanced one". That might simplify the binding and reduce the possible misleading interpretations caused by the Lua/CLI mismatch.

Thanks for sharing your thoughts about the API.

@Freed-Wu

Copy link
Copy Markdown
Author

how to pass the "root dir" to Typst.

How about $PWD? or just leave it empty, because not all typst files will include external files.

@rousbound

Copy link
Copy Markdown
Owner

how to pass the "root dir" to Typst.

How about $PWD? or just leave it empty, because not all typst files will include external files.

Yeah! I think that's intuitive and a reasonable default to expect. The typst-py API I've mentioned defaults to $PWD. But that's why my example showed a different folder as a root_dir, that is a reasonable functionality the user can expect to have too.

I think the user could use lfs.chdir(), for example, to change the $PWD, but that might have side effects depending on the Lua enviroment you are in.

So what am I saying is that $PWD as default is clearly a right call, but creating an API that take in bytes and not offering a way to change root_dir may be "disappointing" to the user I think.

@Freed-Wu

Copy link
Copy Markdown
Author

creating an API that take in bytes and not offering a way to change root_dir may be "disappointing" to the user I think.

By default, use $PWD as root, if user want to change it,

typst.compile {
  file = 'root/main.typ',
  source = 'hello, world!',
}

@rousbound

Copy link
Copy Markdown
Owner

I think the correct parameter to change root would be just "root". That's how it works on the typst CLI.

That's how I would do it for compiling a document from bytes with different root:

--- Advanced API
--- @param args.file string of typst document
--- @param args.input table|nil that corresponds to sys.inputs
--- @param args.source string|nil raw bytes of document to be compiled
--- @param args.root string|nil path to root_dir. 
---    Defaults: 1.  args.file 2. $PWD
--- @param args.format  string|nil that represents formats, defaults to "pdf"
typst.compile{
 root = "src/templates", 
 source = "Hello world!" 
}

It is relevant to notice that regarding this raw bytes parameter:

  • typst CLI call it 'stdin'(it doesn't make sense for us to call it stdin)
  • typst-py uses polymorphism(accepts bytes from the 'file' parameter)
  • typst-rb call it "body".
  • We are calling it "source" (based on your suggestion, but I think it is worth mentioning other options).

@Freed-Wu

Copy link
Copy Markdown
Author

correct parameter to change root would be just "root"

Agree.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update typst

2 participants