C functions
420 functions, extracted from the source at build time so this list cannot drift from it.
Bidi
The Unicode bidirectional algorithm, for text that mixes left-to-right and right-to-left scripts. Even embedding levels run left to right, odd levels right to left.
daegun_bidi_runs_atdaegun_status daegun_bidi_runs_at( const daegun_bidi_runs *runs, size_t index, const daegun_run **out_run, uint8_t *out_level, const size_t **out_chars, size_t *out_chars_count );The run is BORROWED, valid until the set is freed. Do not pass it to daegun_run_free.
daegun_bidi_runs_countdaegun_status daegun_bidi_runs_count( const daegun_bidi_runs *runs, size_t *out );How many direction runs the text resolved into, written to
out.Walk them with
daegun_bidi_runs_at. They come back in the order they should be drawn.daegun_bidi_runs_freevoid daegun_bidi_runs_free(daegun_bidi_runs *runs);Frees a
daegun_bidi_runs.Every run borrowed through
daegun_bidi_runs_atbecomes invalid.daegun_font_shape_bididaegun_status daegun_font_shape_bidi( const daegun_font *font, const char *text, const daegun_axis *axes, size_t axes_len, int32_t base, daegun_bidi_runs **out );base: 0 left-to-right, 1 right-to-left, -1 to let the first strong character decide. That is how C says Option<bool>, and it recurs everywhere a direction may be unstated.
daegun_font_shape_bidi_withdaegun_status daegun_font_shape_bidi_with( const daegun_font *font, const char *text, const daegun_axis *axes, size_t axes_len, int32_t base, const daegun_shape_options *opts, daegun_bidi_runs **out );Resolves mixed-direction text and shapes each run with the full shape options.
The same as
daegun_font_shape_bidibut applying yourdaegun_shape_optionsto every run, so features and language tags carry through.baseselects the paragraph direction: negative to detect it, 0 for left to right, 1 for right to left. Free withdaegun_bidi_runs_free.
Color
Fonts that carry their own color: COLR layer graphs, CPAL palettes and bitmap strikes. A color glyph renders to RGBA pixels rather than to single-channel coverage.
daegun_batch_appenddaegun_status daegun_batch_append( daegun_batch *batch, const float *quads, size_t count, daegun_glyph_slot *out );Adds raw quadratic geometry to a batch directly, producing a slot that draws it.
For drawing shapes of your own through the same GPU path glyphs use.
quadsis a flat array of quadratic control points andcountis the number of quads. The array is copied, so you may free or reuse it immediately.daegun_batch_band_curvesconst uint32_t *daegun_batch_band_curves( const daegun_batch *batch, size_t *out_count );The band-curve index of a batch, borrowed, with the count written to
out_count.Curve indices grouped by band, which the bands point into. The pointer dies with the handle, so copy it out if you need it to outlive one. NULL if either argument is NULL.
daegun_batch_bandsconst daegun_band *daegun_batch_bands( const daegun_batch *batch, size_t *out_count );The bands of a batch, borrowed, with the count written to
out_count.A band is a horizontal or vertical slice of a glyph naming a run in the band-curve index; the shader uses them to skip curves that cannot affect a pixel. The pointer dies with the handle, so copy it out if you need it to outlive one. NULL if either argument is NULL.
daegun_batch_cleardaegun_status daegun_batch_clear(daegun_batch *batch);Empties a batch, keeping its allocation so it can be refilled without reallocating.
Any slot obtained from the batch before clearing no longer refers to anything.
daegun_batch_curvesconst daegun_curve_point *daegun_batch_curves( const daegun_batch *batch, size_t *out_count );The four buffers, borrowed. Valid until the batch is CHANGED or freed – appending a glyph may reallocate, so holding one of these across a draw is holding a dangling pointer. daegun_batch_revision is how to tell.
daegun_batch_freevoid daegun_batch_free(daegun_batch *batch);Frees a GPU batch.
Every slot and every buffer taken from it becomes invalid, including geometry already uploaded from it.
daegun_batch_hullsconst daegun_hull_vertex *daegun_batch_hulls( const daegun_batch *batch, size_t *out_count );The hull vertices of a batch, borrowed, with the count written to
out_count.The coarse geometry drawn to invoke the fragment shader over the area a glyph can cover. The pointer dies with the handle, so copy it out if you need it to outlive one. NULL if either argument is NULL.
daegun_batch_newdaegun_status daegun_batch_new(daegun_batch **out);Creates a GPU batch, the buffer that accumulates glyph geometry for upload.
The GPU path stores each glyph once in em space and draws it at any size, so a batch is filled with curves, bands and hulls, uploaded once, and then drawn from with per-placement instances. Free with
daegun_batch_free.daegun_batch_revisiondaegun_status daegun_batch_revision( const daegun_batch *batch, uint64_t *out );Bumped whenever the buffers change, so you know when to re-upload.
daegun_color_slots_dataconst daegun_color_slot *daegun_color_slots_data( const daegun_color_slots *slots, size_t *out_count );The slots inside a
daegun_color_slots, borrowed, with the count written toout_count.Valid only until the handle is freed. Draw them in this order, back to front.
daegun_color_slots_freevoid daegun_color_slots_free(daegun_color_slots *slots);Frees a
daegun_color_slots. The array taken from it becomes invalid.daegun_colr_layers_dataconst daegun_colr_layer *daegun_colr_layers_data( const daegun_colr_layers *layers, size_t *out_count );The layers inside a
daegun_colr_layers, borrowed, with the count written toout_count.They arrive in painting order, back to front, and each carries its glyph, its color and the
is_foregroundflag. Valid only until the layers handle is freed.daegun_colr_layers_freevoid daegun_colr_layers_free(daegun_colr_layers *layers);Frees a
daegun_colr_layers. The layer array taken from it becomes invalid.daegun_drawn_bitmapdaegun_status daegun_drawn_bitmap( const daegun_drawn *d, const daegun_bitmap **out );BORROWED. Do not free these separately – they belong to the draw result.
daegun_drawn_color_slotsconst daegun_color_slot *daegun_drawn_color_slots( const daegun_drawn *d, size_t *out_count );The per-layer GPU slots of a drawn color glyph, borrowed, with the count written to
out_count.Valid only when the kind is
DAEGUN_DRAWN_GPU_COLOR. Draw them in the order returned – back to front, since the shader does no depth testing.daegun_drawn_freevoid daegun_drawn_free(daegun_drawn *d);Frees a
daegun_drawn.The slot, scene or bitmap it carried becomes invalid.
daegun_drawn_is_okdaegun_status daegun_drawn_is_ok(const daegun_drawn *d, bool *out);Whether the request was served, written to
out.False only for a refusal or a full batch. Note that "nothing drawn" counts as ok – a space has no ink and was handled correctly.
daegun_drawn_kinddaegun_status daegun_drawn_kind(const daegun_drawn *d, int32_t *out);Which path a drawn glyph actually took, written to
outas aDAEGUN_DRAWN_*value.The router decides per glyph, so this is how you find out what you got: a CPU bitmap, a GPU slot, color slots, a finished scene, nothing at all, a full batch, or a refusal.
daegun_drawn_scenedaegun_status daegun_drawn_scene( const daegun_drawn *d, const daegun_scene **out );The rendered scene inside a drawn color glyph, borrowed.
Valid only when the kind is
DAEGUN_DRAWN_SCENE. It dies with thedaegun_drawn.daegun_drawn_slotdaegun_status daegun_drawn_slot( const daegun_drawn *d, daegun_glyph_slot *out );The GPU slot inside a drawn glyph, written to
out.Valid only when
daegun_drawn_kindreportsDAEGUN_DRAWN_GPU; anything else returns a failure status rather than a meaningless slot.daegun_font_colr_layersdaegun_status daegun_font_colr_layers( const daegun_font *font, uint16_t gid, daegun_colr_layers **out );The COLR v0 layers of a glyph – a stack of ordinary glyphs, each with a palette color.
Draw them in the order returned, back to front. A layer with
is_foregroundset takes your text color rather than one from the palette, which is how a font offers a shape that tints with surrounding text. Colors come from palette 0; usedaegun_font_colr_layers_for_paletteto choose another. Free withdaegun_colr_layers_free.daegun_colr_layers *ls = NULL; if (daegun_font_colr_layers(font, gid, &ls) == DAEGUN_OK) { size_t n = 0; const daegun_colr_layer *l = daegun_colr_layers_data(ls, &n); for (size_t i = 0; i < n; i++) /* back to front */ draw(l[i].gid, l[i].is_foreground ? text_color : rgba(l[i])); daegun_colr_layers_free(ls); }daegun_font_colr_layers_for_palettedaegun_status daegun_font_colr_layers_for_palette( const daegun_font *font, uint16_t gid, uint16_t palette_index, daegun_colr_layers **out );The COLR v0 layers of a glyph with colors taken from a specific palette.
The same as
daegun_font_colr_layersbut reading the palette you name – use it to honour a font's dark-mode palette, whichdaegun_font_palette_infowill identify.daegun_font_colr_v1_paintdaegun_status daegun_font_colr_v1_paint( const daegun_font *font, uint16_t gid, const daegun_axis *axes, size_t axes_len, uint16_t palette_index, daegun_paint **out );The COLR v1 paint graph for a glyph, unrendered, as an owned handle.
COLR v1 describes a glyph as a tree of paint operations – solid fills, gradients, transforms, clips and blends. This hands you that tree for your own compositor. If you just want pixels, use
daegun_font_render_colr_glyphinstead.daegun_font_draw_glyphdaegun_status daegun_font_draw_glyph( const daegun_font *font, daegun_batch *batch, const void *device, const daegun_policy *policy, uint16_t gid, float px, const daegun_axis *axes, size_t axes_len, const daegun_raster_options *opts, int32_t palette, daegun_drawn **out );Draws one glyph wherever the policy and the device say it belongs. THERE IS NO DRAW TARGET HANDLE, and that is deliberate. DrawTarget borrows the batch mutably, and a handle would let you outlive the borrow – a use-after-free C cannot see. Building it inside the call makes that unrepresentable: a NULL device is the CPU-only arrangement, a non-NULL one is the device-aware one, and the policy is a parameter rather than something you configure and keep. palette is the color palette index, or -1 for none. opts, policy and device may all be NULL.
daegun_font_glyph_bitmapdaegun_status daegun_font_glyph_bitmap( const daegun_font *font, uint16_t gid, uint16_t target_ppem, daegun_glyph_bitmap **out );An embedded bitmap, as PNG BYTES rather than pixels – the face stores them that way and daegun does not decode images. You already have a PNG decoder or do not want one.
daegun_font_gpu_color_glyphdaegun_status daegun_font_gpu_color_glyph( const daegun_font *font, daegun_batch *batch, uint16_t gid, const daegun_axis *axes, size_t axes_len, uint16_t palette_index, daegun_color_slots **out );The color equivalent of
daegun_font_gpu_glyph: one slot per flat-colored layer, each with its tint.Draw them in the order returned – they paint back to front and the shader does no depth testing.
palette_indexselects which CPAL palette supplies the colors. Free withdaegun_color_slots_free.daegun_font_gpu_glyphdaegun_status daegun_font_gpu_glyph( const daegun_font *font, daegun_batch *batch, uint16_t gid, const daegun_axis *axes, size_t axes_len, daegun_glyph_slot *out );Adds a glyph's curves to a batch and writes the slot that identifies it.
One slot serves every size on screen, because the shader evaluates coverage from the curves rather than sampling an atlas – so there is no resolution to choose. Upload the batch once, then build one instance per placement.
daegun_batch *batch = NULL; daegun_batch_new(&batch); daegun_glyph_slot slot; daegun_font_gpu_glyph(font, batch, gid, NULL, 0, &slot); /* upload the batch's curves, bands and hulls, then instance the slot per placement */daegun_font_palette_countdaegun_status daegun_font_palette_count( const daegun_font *font, uint16_t *out );How many CPAL palettes the font carries, or 0 if it carries none.
Most color fonts ship exactly one. Any index below this count is valid wherever a palette index is taken.
daegun_font_palette_infodaegun_status daegun_font_palette_info( const daegun_font *font, daegun_palettes **out );What each palette is for, including whether it suits a light or a dark background.
Fonts may label their palettes with usability flags. This surfaces them, so you can pick the palette matching your background rather than always taking 0. Free with
daegun_palettes_free.daegun_font_render_colr_glyphdaegun_status daegun_font_render_colr_glyph( const daegun_font *font, uint16_t gid, float px, const daegun_axis *axes, size_t axes_len, uint16_t palette_index, daegun_scene **out );out_skipped_ops is how many paint operations the renderer could not carry out. Non-zero means the image is incomplete rather than wrong, and anything showing it should know.
daegun_glyph_bitmap_freevoid daegun_glyph_bitmap_free(daegun_glyph_bitmap *b);Frees a
daegun_glyph_bitmap.The PNG bytes taken from it become invalid.
daegun_glyph_bitmap_pngconst uint8_t *daegun_glyph_bitmap_png( const daegun_glyph_bitmap *b, size_t *out_len, uint16_t *out_ppem, int16_t *out_origin_x, int16_t *out_origin_y );An embedded bitmap strike as PNG bytes, with its resolution and origin.
Some color fonts ship photographic bitmaps rather than outlines. The data is PNG, not raw pixels – decode it before use.
out_ppemis the strike's own resolution, which may differ from the size you asked for, so scale accordingly. The origin places the image relative to the pen.daegun_paint_childrenconst uint32_t *daegun_paint_children( const daegun_paint *p, size_t *out_count );A node's children are the child_count entries at child_start.
daegun_paint_freevoid daegun_paint_free(daegun_paint *p);Frees a paint graph.
The node array taken from it becomes invalid.
daegun_paint_nodesconst daegun_paint_node *daegun_paint_nodes( const daegun_paint *p, size_t *out_count );The nodes of a paint graph, borrowed, with the count written to
out_count.The tree flattened into an array, with nodes referring to each other by index. Valid only until the paint handle is freed.
daegun_paint_stopsdaegun_status daegun_paint_stops( const daegun_paint *p, size_t *out_count, const double **out_offsets, const uint8_t **out_colors );Every gradient's stops in one run: a node's are the stops_count entries at stops_start, and out_colors holds FOUR bytes per stop, so stop i is at 4 * i.
daegun_palettes_dataconst daegun_palette_info *daegun_palettes_data( const daegun_palettes *p, size_t *out_count );The palette entries inside a
daegun_palettes, borrowed, with the count written toout_count.One entry per palette, in the order the font declares them, each carrying the usability flags that say whether it suits a light or a dark background. Valid only until the palettes handle is freed.
daegun_palettes_freevoid daegun_palettes_free(daegun_palettes *p);Frees a
daegun_palettes. The array taken from it becomes invalid.daegun_policy_defaultdaegun_status daegun_policy_default(daegun_policy *out);Fills a
daegun_policywith its defaults – how the router chooses between CPU and GPU.Call this rather than zeroing the struct. The defaults are a reasonable middle: very large glyphs favor the GPU, very small ones favor the CPU where hinting helps most.
daegun_scene_freevoid daegun_scene_free(daegun_scene *s);Frees a rendered scene.
The RGBA pixel pointer taken from it becomes invalid.
daegun_scene_rgbaconst uint8_t *daegun_scene_rgba( const daegun_scene *s, size_t *out_len, size_t *out_width, size_t *out_height, int32_t *out_left, int32_t *out_top, size_t *out_skipped_ops );A rendered scene's pixels, borrowed, with its size, position and how many operations were skipped.
Straight alpha RGBA8, not premultiplied, four bytes per pixel.
out_leftandout_topplace the image relative to the origin you rendered at.out_skipped_opscounts paint operations the renderer could not carry out – non-zero means the glyph used something unsupported and the result is incomplete, which is worth checking rather than assuming. Any out parameter may be NULL.
D3D11 backend
Direct3D 11 on Windows. The same 19 functions every backend has, opened by name at run time.
daegun_d3d11_drawdaegun_status daegun_d3d11_draw( const daegun_d3d11_renderer *renderer, daegun_d3d11_target *target, const daegun_d3d11_geometry *geometry, const daegun_glyph_instance *instances, size_t instance_count, const daegun_subpixel_params *subpixel, int32_t mode );Draws glyph instances into a Direct3D 11 target.
Takes the geometry and an array of instances, each placing one glyph at a position, scale and color. Instance offsets are in device pixels with y up from the bottom of the target, which is the opposite of the top-down convention most 2D layout uses – getting this wrong draws the text mirrored vertically. Use
_draw_withto choose the blending mode.daegun_d3d11_draw_withdaegun_status daegun_d3d11_draw_with( const daegun_d3d11_renderer *renderer, daegun_d3d11_target *target, const daegun_d3d11_geometry *geometry, const daegun_glyph_instance *instances, size_t instance_count, const daegun_subpixel_params *subpixel, int32_t mode, const float *projection );Under a projection of your own – column-major, NULL for the default.
daegun_d3d11_geometry_freevoid daegun_d3d11_geometry_free(daegun_d3d11_geometry *geometry);Frees Direct3D 11 geometry and the device resources behind it.
Free it before the renderer it was created from.
daegun_d3d11_geometry_newdaegun_status daegun_d3d11_geometry_new( const daegun_d3d11_renderer *renderer, const daegun_batch *batch, daegun_d3d11_geometry **out );Uploads a batch's curves, bands and hulls to the Direct3D 11 device as drawable geometry.
This is the step that moves glyph data onto the GPU. Build the batch first, then create geometry from it once and draw from that geometry many times – rebuilding it per frame throws away the whole point of the GPU path.
_geometry_revisiontells you when the batch has changed enough to need a fresh upload.daegun_d3d11_geometry_revisiondaegun_status daegun_d3d11_geometry_revision( const daegun_d3d11_geometry *geometry, uint64_t *out );Compare against daegun_batch_revision to know whether this upload is stale.
daegun_d3d11_orthodaegun_status daegun_d3d11_ortho( uint32_t width, uint32_t height, float *out );The default projection, column-major. Needs no device: it depends on this API's clip-space * convention and nothing else, so it can be built before anything is opened.
daegun_d3d11_read_pixelsconst uint8_t *daegun_d3d11_read_pixels( const daegun_d3d11_renderer *renderer, daegun_d3d11_target *target, size_t *out_count );Waits, copies off the device, and hands back BGRA. BORROWED, valid until the target is drawn * into again or freed. NULL on failure, with the reason in daegun_last_error. * * THIS IS THE EXPENSIVE CALL. On a discrete GPU the readback dominates: 489 us of a 513 us * frame at 1024x1024, measured. A caller that can consume the target on the device should not * call it at all.
daegun_d3d11_renderer_device_namedaegun_status daegun_d3d11_renderer_device_name( const daegun_d3d11_renderer *renderer, daegun_text **out );Free with daegun_text_free.
daegun_d3d11_renderer_freevoid daegun_d3d11_renderer_free(daegun_d3d11_renderer *renderer);Safe to call while a target or geometry is still alive; the device goes with the last.
daegun_d3d11_renderer_newdaegun_status daegun_d3d11_renderer_new(daegun_d3d11_renderer **out);DAEGUN_UNSUPPORTED when there is no such device, which is an answer and not a failure.
daegun_d3d11_renderer_profiledaegun_status daegun_d3d11_renderer_profile( const daegun_d3d11_renderer *renderer, daegun_device_profile **out );Free with daegun_device_profile_free.
daegun_d3d11_renderer_supports_subpixeldaegun_status daegun_d3d11_renderer_supports_subpixel( const daegun_d3d11_renderer *renderer, int32_t *out );Whether this Direct3D 11 device can do subpixel rendering, written to
out.Subpixel output needs dual-source blending, which not every device and driver exposes. When this is false, use grayscale coverage instead – the text is still correct, just without the threefold horizontal resolution an LCD stripe would allow.
daegun_d3d11_target_freevoid daegun_d3d11_target_free(daegun_d3d11_target *target);Frees a Direct3D 11 target.
Any pixel pointer taken from it becomes invalid.
daegun_d3d11_target_heightdaegun_status daegun_d3d11_target_height( const daegun_d3d11_target *target, uint32_t *out );The height of a Direct3D 11 target in pixels, written to
out.daegun_d3d11_target_newdaegun_status daegun_d3d11_target_new( const daegun_d3d11_renderer *renderer, uint32_t width, uint32_t height, daegun_d3d11_target **out );Creates a Direct3D 11 render target of the given size.
The target owns its pixels, which you read back with
_target_pixelsor_read_pixels. Free it with_target_freebefore the renderer it came from.daegun_d3d11_target_pixeldaegun_status daegun_d3d11_target_pixel( const daegun_d3d11_target *target, uint32_t x, uint32_t y, uint8_t *out );One pixel as BGRA into four bytes. DAEGUN_RANGE outside the target.
daegun_d3d11_target_pixelsconst uint8_t *daegun_d3d11_target_pixels( const daegun_d3d11_target *target, size_t *out_count );BORROWED, BGRA. What the last _read_pixels left behind, without the round trip. Valid until * the target is drawn into again or freed.
daegun_d3d11_target_widthdaegun_status daegun_d3d11_target_width( const daegun_d3d11_target *target, uint32_t *out );The width of a Direct3D 11 target in pixels, written to
out.daegun_d3d11_waitdaegun_status daegun_d3d11_wait( const daegun_d3d11_renderer *renderer, daegun_d3d11_target *target );Blocks until the Direct3D 11 device has finished the work already submitted.
Needed before reading pixels back, and at shutdown. On a normal render loop you do not want this – it gives up the parallelism between CPU and GPU that makes the GPU path worth using.
D3D12 backend
Direct3D 12 on Windows. The same 19 functions every backend has, opened by name at run time.
daegun_d3d12_drawdaegun_status daegun_d3d12_draw( const daegun_d3d12_renderer *renderer, daegun_d3d12_target *target, const daegun_d3d12_geometry *geometry, const daegun_glyph_instance *instances, size_t instance_count, const daegun_subpixel_params *subpixel, int32_t mode );Draws glyph instances into a Direct3D 12 target.
Takes the geometry and an array of instances, each placing one glyph at a position, scale and color. Instance offsets are in device pixels with y up from the bottom of the target, which is the opposite of the top-down convention most 2D layout uses – getting this wrong draws the text mirrored vertically. Use
_draw_withto choose the blending mode.daegun_d3d12_draw_withdaegun_status daegun_d3d12_draw_with( const daegun_d3d12_renderer *renderer, daegun_d3d12_target *target, const daegun_d3d12_geometry *geometry, const daegun_glyph_instance *instances, size_t instance_count, const daegun_subpixel_params *subpixel, int32_t mode, const float *projection );Under a projection of your own – column-major, NULL for the default.
daegun_d3d12_geometry_freevoid daegun_d3d12_geometry_free(daegun_d3d12_geometry *geometry);Frees Direct3D 12 geometry and the device resources behind it.
Free it before the renderer it was created from.
daegun_d3d12_geometry_newdaegun_status daegun_d3d12_geometry_new( const daegun_d3d12_renderer *renderer, const daegun_batch *batch, daegun_d3d12_geometry **out );Uploads a batch's curves, bands and hulls to the Direct3D 12 device as drawable geometry.
This is the step that moves glyph data onto the GPU. Build the batch first, then create geometry from it once and draw from that geometry many times – rebuilding it per frame throws away the whole point of the GPU path.
_geometry_revisiontells you when the batch has changed enough to need a fresh upload.daegun_d3d12_geometry_revisiondaegun_status daegun_d3d12_geometry_revision( const daegun_d3d12_geometry *geometry, uint64_t *out );Compare against daegun_batch_revision to know whether this upload is stale.
daegun_d3d12_orthodaegun_status daegun_d3d12_ortho( uint32_t width, uint32_t height, float *out );The default projection, column-major. Needs no device: it depends on this API's clip-space * convention and nothing else, so it can be built before anything is opened.
daegun_d3d12_read_pixelsconst uint8_t *daegun_d3d12_read_pixels( const daegun_d3d12_renderer *renderer, daegun_d3d12_target *target, size_t *out_count );Waits, copies off the device, and hands back BGRA. BORROWED, valid until the target is drawn * into again or freed. NULL on failure, with the reason in daegun_last_error. * * THIS IS THE EXPENSIVE CALL. On a discrete GPU the readback dominates: 489 us of a 513 us * frame at 1024x1024, measured. A caller that can consume the target on the device should not * call it at all.
daegun_d3d12_renderer_device_namedaegun_status daegun_d3d12_renderer_device_name( const daegun_d3d12_renderer *renderer, daegun_text **out );Free with daegun_text_free.
daegun_d3d12_renderer_freevoid daegun_d3d12_renderer_free(daegun_d3d12_renderer *renderer);Safe to call while a target or geometry is still alive; the device goes with the last.
daegun_d3d12_renderer_newdaegun_status daegun_d3d12_renderer_new(daegun_d3d12_renderer **out);DAEGUN_UNSUPPORTED when there is no such device, which is an answer and not a failure.
daegun_d3d12_renderer_profiledaegun_status daegun_d3d12_renderer_profile( const daegun_d3d12_renderer *renderer, daegun_device_profile **out );Free with daegun_device_profile_free.
daegun_d3d12_renderer_supports_subpixeldaegun_status daegun_d3d12_renderer_supports_subpixel( const daegun_d3d12_renderer *renderer, int32_t *out );Whether this Direct3D 12 device can do subpixel rendering, written to
out.Subpixel output needs dual-source blending, which not every device and driver exposes. When this is false, use grayscale coverage instead – the text is still correct, just without the threefold horizontal resolution an LCD stripe would allow.
daegun_d3d12_target_freevoid daegun_d3d12_target_free(daegun_d3d12_target *target);Frees a Direct3D 12 target.
Any pixel pointer taken from it becomes invalid.
daegun_d3d12_target_heightdaegun_status daegun_d3d12_target_height( const daegun_d3d12_target *target, uint32_t *out );The height of a Direct3D 12 target in pixels, written to
out.daegun_d3d12_target_newdaegun_status daegun_d3d12_target_new( const daegun_d3d12_renderer *renderer, uint32_t width, uint32_t height, daegun_d3d12_target **out );Creates a Direct3D 12 render target of the given size.
The target owns its pixels, which you read back with
_target_pixelsor_read_pixels. Free it with_target_freebefore the renderer it came from.daegun_d3d12_target_pixeldaegun_status daegun_d3d12_target_pixel( const daegun_d3d12_target *target, uint32_t x, uint32_t y, uint8_t *out );One pixel as BGRA into four bytes. DAEGUN_RANGE outside the target.
daegun_d3d12_target_pixelsconst uint8_t *daegun_d3d12_target_pixels( const daegun_d3d12_target *target, size_t *out_count );BORROWED, BGRA. What the last _read_pixels left behind, without the round trip. Valid until * the target is drawn into again or freed.
daegun_d3d12_target_widthdaegun_status daegun_d3d12_target_width( const daegun_d3d12_target *target, uint32_t *out );The width of a Direct3D 12 target in pixels, written to
out.daegun_d3d12_waitdaegun_status daegun_d3d12_wait( const daegun_d3d12_renderer *renderer, daegun_d3d12_target *target );Blocks until the Direct3D 12 device has finished the work already submitted.
Needed before reading pixels back, and at shutdown. On a normal render loop you do not want this – it gives up the parallelism between CPU and GPU that makes the GPU path worth using.
Font
Opening and closing a font. A daegun_font * is the handle everything else takes. It is safe to use from several threads at once, and it must be freed exactly once with daegun_font_free.
daegun_font_buffer_freevoid daegun_font_buffer_free(uint8_t *data, size_t len);Frees a buffer you took but never handed over. Null is a no-op. Do NOT call this after daegun_font_open_owned – that took ownership, whether it succeeded or not.
daegun_font_buffer_newuint8_t *daegun_font_buffer_new(size_t len);A buffer for a font, allocated by daegun so it can be handed back without a copy. daegun_font_open copies your bytes so you may free them the moment it returns. This is the way around that copy – take a buffer, read the file into it, hand it back: uint8_t *buf = daegun_font_buffer_new(len); fread(buf, 1, len, fp); daegun_font *font = NULL; daegun_status st = daegun_font_open_owned(buf, len, &font); // buf belongs to the font now, pass or fail. Do not free it, do not read it. NULL for a zero length. This is not an ordinary pointer: never free() it.
daegun_font_freevoid daegun_font_free(daegun_font *font);Frees a font. NULL is a no-op, as free(NULL) is.
daegun_font_glyph_iddaegun_status daegun_font_glyph_id( const daegun_font *font, uint32_t codepoint, uint16_t *out );The glyph a Unicode codepoint maps to. DAEGUN_ABSENT when the font has none.
daegun_font_num_glyphsdaegun_status daegun_font_num_glyphs( const daegun_font *font, uint16_t *out );How many glyphs the face has.
daegun_font_opendaegun_status daegun_font_open( const uint8_t *data, size_t len, daegun_font **out );Opens a font. The bytes are COPIED, so you may free them the moment this returns.
daegun_font_open_collectiondaegun_status daegun_font_open_collection( const uint8_t *data, size_t len, size_t index, daegun_font **out );The same, for one face of a .ttc collection.
daegun_font_open_owneddaegun_status daegun_font_open_owned( uint8_t *data, size_t len, daegun_font **out );Opens a font from a daegun buffer, taking ownership of it. Copies nothing. The buffer belongs to the font afterwards either way: freed with daegun_font_free, or immediately if the bytes did not parse.
lenmust be exactly what you passed to daegun_font_buffer_new.daegun_font_upmdaegun_status daegun_font_upm(const daegun_font *font, uint16_t *out);Units per em: the denominator every em-space figure in this API is in.
daegun_ttc_font_countdaegun_status daegun_ttc_font_count( const uint8_t *data, size_t len, size_t *out );How many faces a .ttc holds. Zero for data that is not a collection. Takes bytes rather than a font, because it answers what a caller asks before deciding which face to open.
Glyphs
Going from characters to glyph ids, and asking about one glyph. A glyph id indexes this font and means something different in any other.
daegun_font_advance_widthsdaegun_status daegun_font_advance_widths( const daegun_font *font, const uint16_t *gids, size_t gids_len, const daegun_axis *axes, size_t axes_len, daegun_f64_list **out );The advance width of each given glyph, on the 1000-unit em, returned as an owned
daegun_f64_list.Batched because the values are cached per axis position, so asking for many at once is much cheaper than one call each. These are raw per-glyph widths with no kerning or positioning applied – to measure text, use the advances from a shaped run instead. Free the list with
daegun_f64_list_free.daegun_f64_list *w = NULL; daegun_font_advance_widths(font, gids, n, NULL, 0, &w); size_t count = 0; const double *vals = daegun_f64_list_data(w, &count); daegun_f64_list_free(w);daegun_font_caret_positionsdaegun_status daegun_font_caret_positions( const daegun_font *font, const char *text, const daegun_axis *axes, size_t axes_len, bool vertical, daegun_f64_list **out );Every position a cursor may occupy in a string, one per character boundary, on the 1000-unit em.
Shapes the text and works out where each character boundary landed – which is harder than it sounds, because ligatures collapse several characters into one glyph, marks take no width, and right-to-left runs advance leftward. Returns one more value than there are characters: the position after the last. Free with
daegun_f64_list_free.daegun_f64_list *carets = NULL; daegun_font_caret_positions(font, "office", NULL, 0, false, &carets); size_t n = 0; const double *pos = daegun_f64_list_data(carets, &n); /* pos[2] sits between the two f's, though they shaped to one glyph */ daegun_f64_list_free(carets);daegun_font_codepointsdaegun_status daegun_font_codepoints( const daegun_font *font, daegun_u32_list **out );Every code point the font supports, as an owned
daegun_u32_list.The
cmapflattened, without the glyph ids. Build a set from it once if you are testing many characters. Free withdaegun_u32_list_free.daegun_font_coveragedaegun_status daegun_font_coverage( const daegun_font *font, daegun_u32_list **out_codepoints, daegun_u16_list **out_gids );Every codepoint the cmap maps, with the glyph each reaches, at matching indices.
daegun_font_default_vertical_origindaegun_status daegun_font_default_vertical_origin( const daegun_font *font, int32_t *out );The vertical origin the font uses for glyphs that do not specify their own, on the 1000-unit em.
Writes 0 for a TrueType outline font, which derives origins per glyph instead.
daegun_font_glyph_boundsdaegun_status daegun_font_glyph_bounds( const daegun_font *font, uint16_t gid, const daegun_axis *axes, size_t axes_len, double *out );A glyph's tight ink box at a location: out receives FOUR doubles, [xmin, ymin, xmax, ymax]. DAEGUN_ABSENT for a glyph that draws nothing – a space has no ink, which differs from a box of zero size.
daegun_font_glyph_classdaegun_status daegun_font_glyph_class( const daegun_font *font, uint16_t gid, int32_t *out );What kind of glyph this is – base, ligature, mark or component – written to
outas aDAEGUN_GLYPH_CLASS_*value.From the
GDEFtable. Chiefly used to tell a combining mark from a spacing glyph, since a mark takes no advance of its own. ReturnsDAEGUN_ABSENTwhen the font does not classify the glyph, which is common and not an error.daegun_font_glyph_idsdaegun_status daegun_font_glyph_ids( const daegun_font *font, const char *text, daegun_u16_list **out_gids, daegun_blob **out_present );The glyph each character of a string maps to. TWO lists, because entries may be absent: out_gids holds one id per character, out_present one byte per character, non-zero where the font actually has a glyph. A sentinel would not work – 0 is .notdef and every other uint16_t is a real glyph id in some face. Either may be NULL.
daegun_font_glyph_namedaegun_status daegun_font_glyph_name( const daegun_font *font, uint16_t gid, daegun_text **out );The PostScript name of one glyph, as an owned
daegun_text *.Many fonts ship a version 3
posttable, which stores no names at all, soDAEGUN_ABSENTis common and expected. Read the string withdaegun_text_strand free withdaegun_text_free. Names are for debugging and PDF, never for identifying a glyph programmatically – use the id.daegun_text *name = NULL; if (daegun_font_glyph_name(font, gid, &name) == DAEGUN_OK) { daegun_str s; daegun_text_str(name, &s); printf("%.*s\n", (int)s.len, s.data); daegun_text_free(name); }daegun_font_glyph_namesdaegun_status daegun_font_glyph_names( const daegun_font *font, daegun_str_list **out_names, daegun_blob **out_present );Two lists again: an empty string is a name a font can genuinely state, so out_present says which entries mean anything.
daegun_font_has_glyphdaegun_status daegun_font_has_glyph( const daegun_font *font, uint32_t codepoint, bool *out );Whether the font can render a code point, written to
out.Useful for choosing a fallback font before committing to one.
bool ok = false; daegun_font_has_glyph(font, 0x5B57, &ok); /* U+5B57 字 */daegun_font_ligature_caretsdaegun_status daegun_font_ligature_carets( const daegun_font *font, uint16_t gid, const daegun_axis *axes, size_t axes_len, daegun_f64_list **out );Where a text cursor may sit inside a ligature, on the 1000-unit em, as an owned
daegun_f64_list.When "fi" becomes one glyph, a cursor still has to be placeable between the f and the i. The positions are offset from the ligature's own origin. Empty for an ordinary glyph. For cursor positions across a whole string, use
daegun_font_caret_positions.daegun_font_mark_attachment_classdaegun_status daegun_font_mark_attachment_class( const daegun_font *font, uint16_t gid, uint16_t *out );The mark attachment class of a glyph, or 0 when it has none.
Fonts group marks into classes so a positioning rule can apply to a whole group. The number is meaningful only against the rules in that same font.
daegun_font_variation_glyph_iddaegun_status daegun_font_variation_glyph_id( const daegun_font *font, uint32_t base, uint32_t selector, uint16_t *out );Resolves a base character plus a Unicode variation selector to a glyph id.
Variation sequences let one character request a specific form: U+FE0E asks for the text presentation of an emoji and U+FE0F for the emoji presentation. Returns
DAEGUN_ABSENTwhen the font does not map the pair. Shaping applies these already, so use this only when handling selectors yourself.daegun_font_vertical_advancedaegun_status daegun_font_vertical_advance( const daegun_font *font, uint16_t gid, const daegun_axis *axes, size_t axes_len, uint32_t *out );The vertical advance of one glyph on the 1000-unit em, for text set top to bottom.
How far the pen moves down after this glyph. Writes 0 for a glyph id past the end of the font – a deliberate refusal, since other tables will answer confidently for ids that do not exist.
daegun_font_vertical_origindaegun_status daegun_font_vertical_origin( const daegun_font *font, uint16_t gid, const daegun_axis *axes, size_t axes_len, int32_t *out );The y origin used when a glyph is set vertically, on the 1000-unit em.
In vertical text a glyph hangs from an origin above the baseline rather than sitting on it. Returns
DAEGUN_ABSENTfor a glyph id past the end of the font rather than a plausible wrong number.daegun_u32_list_dataconst uint32_t *daegun_u32_list_data( const daegun_u32_list *list, size_t *out_count );The elements of a
daegun_u32_list, borrowed, with the count written toout_count.The pointer is valid only until the list is freed. Copy the elements out if you need them to outlive it. Returns NULL if either argument is NULL.
daegun_u32_list_freevoid daegun_u32_list_free(daegun_u32_list *list);Frees a
daegun_u32_list. Any pointer obtained from it becomes invalid.
Hinting
Grid fitting, which nudges outlines onto pixel boundaries so stems stay crisp at small sizes.
daegun_cff_hints_freevoid daegun_cff_hints_free(daegun_cff_hints *hints);Frees a
daegun_cff_hints.The stem positions taken from it become invalid.
daegun_cff_hints_stemsconst double *daegun_cff_hints_stems( const daegun_cff_hints *hints, size_t *out_count );THREE doubles per stem – is_vertical as 0 or 1, then the edge positions min and max, so a stem's width is max - min. out_count receives the number of STEMS, not of doubles.
daegun_font_cff_hintsdaegun_status daegun_font_cff_hints( const daegun_font *font, uint16_t gid, daegun_cff_hints **out );The hint data a CFF glyph carries – its stem positions and any hint replacement.
CFF fonts declare stems declaratively rather than running bytecode. Returns
DAEGUN_ABSENTfor TrueType outlines, which hint through instructions instead.daegun_font_hinted_glyphdaegun_status daegun_font_hinted_glyph( const daegun_font *font, uint16_t gid, float px, const daegun_axis *axes, size_t axes_len, int32_t hint_mode, daegun_hinted_outline **out );A glyph's outline after hinting has moved it onto the pixel grid, without rasterizing it.
Hinting normally happens inside rasterizing; this exposes the result for inspection or for a rasterizer of your own. The outline is in pixel space at the size you asked for, not in font units, because grid fitting is only meaningful at a size.
daegun_hinted_outline_contoursconst size_t *daegun_hinted_outline_contours( const daegun_hinted_outline *outline, size_t *out_count );Where each contour ends, as an index one past its last point.
daegun_hinted_outline_freevoid daegun_hinted_outline_free(daegun_hinted_outline *outline);Frees a hinted outline.
The grid-fitted points become invalid; replay them onto a pen first if you need them.
daegun_hinted_outline_pointsdaegun_status daegun_hinted_outline_points( const daegun_hinted_outline *outline, size_t *out_count, const int32_t **out_x, const int32_t **out_y, const uint8_t **out_flags );Every pointer but out_count may be NULL.
Justification
Fitting a line to a width using what the font offers – elongating Arabic kashida, substituting alternate forms – rather than only stretching the spaces.
daegun_font_justification_extendersdaegun_status daegun_font_justification_extenders( const daegun_font *font, const char *script_tag, daegun_u16_list **out );The glyphs a script uses to stretch a line, such as the Arabic kashida.
From the
JSTFtable. Empty for scripts that do not justify this way and for fonts that do not say. Needed only when implementing justification yourself. Free withdaegun_u16_list_free.daegun_font_justification_prioritiesdaegun_status daegun_font_justification_priorities( const daegun_font *font, const char *script_tag, const char *lang_sys_tag, daegun_jstf_priorities **out );lang_sys_tag may be NULL.
daegun_font_justifydaegun_status daegun_font_justify( const daegun_font *font, const char *text, const daegun_axis *axes, size_t axes_len, bool vertical, const char *script_tag, const char *lang_sys_tag, double target_width, double tolerance, daegun_justified **out );Shapes a string and fits it to a target width using the font's own justification rules.
The call to use for justified text. It prefers what the font was designed to do – elongating Arabic kashida, substituting alternate forms described in
JSTF– and falls back to spacing adjustments.target_widthandtoleranceare on the 1000-unit em like every other measurement, so divide a pixel target bypx / 1000.0on the way in.script_tagselects the rules, since Arabic and Latin justify quite differently;lang_sys_tagmay be NULL. Free the result withdaegun_justified_free.double px = 16.0, scale = px / 1000.0; daegun_justified *j = NULL; daegun_status st = daegun_font_justify( font, line, NULL, 0, false, "latn", NULL, 320.0 / scale, /* a 320px measure, on the 1000-unit em */ 0.05, &j); if (st == DAEGUN_OK) { const daegun_run *run = daegun_justified_run(j); /* draw the run, then */ daegun_justified_free(j); }daegun_font_shape_justifieddaegun_status daegun_font_shape_justified( const daegun_font *font, const char *text, const daegun_axis *axes, size_t axes_len, bool vertical, const daegun_jstf_mods *mods, bool shrink, daegun_run **out );Shapes a string and applies the font's justification rules to stretch or shrink it.
Rather than only adding space between words, this uses what the font offers – elongating kashida, substituting wider or narrower forms.
modsselects which of those to apply andshrinkchooses tightening over stretching.daegun_font_justifyis the higher-level call that takes a target width instead.daegun_jstf_priorities_atdaegun_status daegun_jstf_priorities_at( const daegun_jstf_priorities *p, size_t index, const daegun_jstf_mods **out );One level, BORROWED – valid until the priorities are freed, and there is nothing to free.
daegun_jstf_priorities_countdaegun_status daegun_jstf_priorities_count( const daegun_jstf_priorities *p, size_t *out );How many justification priority levels the font describes, written to
out.Fonts describe justification as an ordered sequence of adjustments to try. This is how many steps that sequence has.
daegun_jstf_priorities_freevoid daegun_jstf_priorities_free(daegun_jstf_priorities *p);Frees a
daegun_jstf_priorities.The priority levels taken from it become invalid.
daegun_justified_freevoid daegun_justified_free(daegun_justified *j);Frees a
daegun_justified.The shaped run borrowed through
daegun_justified_runbecomes invalid.daegun_justified_infodaegun_status daegun_justified_info( const daegun_justified *j, bool *out_has_level, size_t *out_level, bool *out_shrink, double *out_width, bool *out_best_effort );What justification achieved: the level applied, the direction, the width reached, and whether it fell short.
out_widthis on the 1000-unit em.out_best_effortis set when the target could not be met exactly – a line with nothing stretchable in it cannot always be made to fit.out_shrinksays the line was tightened rather than stretched. Any out parameter may be NULL.daegun_justified_runconst daegun_run *daegun_justified_run(const daegun_justified *j);The shaped run inside a justification result, borrowed.
Valid only until the
daegun_justifiedis freed.
Layout
Breaking and positioning a whole paragraph, plus the Unicode segmentation the process needs. Sizes in and out are on the 1000-unit em.
daegun_bidi_paragraph_base_leveldaegun_status daegun_bidi_paragraph_base_level( const daegun_bidi_paragraph *p, uint8_t *out );The paragraph's base embedding level: 0 for left to right, 1 for right to left.
What direction resolution settled on, whether you supplied it or let it be detected.
daegun_bidi_paragraph_freevoid daegun_bidi_paragraph_free(daegun_bidi_paragraph *p);Frees a
daegun_bidi_paragraph.daegun_font_layoutdaegun_status daegun_font_layout( const daegun_font *font, const char *text, const daegun_axis *axes, size_t axes_len, const daegun_layout_options *opts, daegun_layout **out );Shapes, breaks and positions a whole paragraph into lines in one call.
The complete paragraph path: it shapes the text, finds break opportunities, chooses where to break, and positions each line. Every size in and out is on the 1000-unit em, so a pixel measure goes in as
px_value / (px / 1000.0). Fill the options withdaegun_layout_options_defaultfirst. Free the result withdaegun_layout_free.daegun_layout_options opts; daegun_layout_options_default(&opts); double px = 16.0, scale = px / 1000.0; opts.max_inline_size = 320.0 / scale; opts.strategy = DAEGUN_BREAK_OPTIMAL; daegun_layout *layout = NULL; if (daegun_font_layout(font, text, NULL, 0, &opts, &layout) == DAEGUN_OK) { size_t lines = 0; double iw = 0, bh = 0; bool trunc = false; size_t tcount = 0; daegun_layout_info(layout, &lines, &iw, &bh, &trunc, &tcount); daegun_layout_free(layout); }daegun_layout_freevoid daegun_layout_free(daegun_layout *layout);Frees a laid-out paragraph.
Every line and every positioned run borrowed from it becomes invalid.
daegun_layout_infodaegun_status daegun_layout_info( const daegun_layout *layout, size_t *out_line_count, double *out_inline_size, double *out_block_size, bool *out_has_truncated, size_t *out_truncated );The summary of a laid-out paragraph: line count, its measured size, and whether anything was truncated.
Sizes are on the 1000-unit em.
out_has_truncatedandout_truncatedreport text that did not fit within a line limit, so you can show an ellipsis or grow the box. Any out parameter may be NULL if you do not want that value.daegun_layout_linedaegun_status daegun_layout_line( const daegun_layout *layout, size_t index, size_t *out_run_count, size_t *out_char_start, size_t *out_char_end, double *out_baseline, double *out_inline_size, double *out_ascent, double *out_descent, bool *out_hard_break );One line of a laid-out paragraph by index.
Lines are numbered from 0 up to the count
daegun_layout_inforeports.daegun_layout_options_defaultdaegun_status daegun_layout_options_default(daegun_layout_options *out);Fills a
daegun_layout_optionswith its defaults.Call this rather than zeroing the struct. A zeroed struct means a measure of zero, which wraps every word onto its own line.
daegun_layout_rundaegun_status daegun_layout_run( const daegun_layout *layout, size_t line, size_t index, const daegun_run **out_run, double *out_offset_x, double *out_offset_y, uint8_t *out_level, size_t *out_char_start, size_t *out_char_end, bool *out_upright );The run is BORROWED, valid until the layout is freed.
daegun_script_is_rtldaegun_status daegun_script_is_rtl(uint16_t script, bool *out);DAEGUN_ABSENT where the script has no inherent direction.
daegun_script_namedaegun_status daegun_script_name(uint16_t script, daegun_text **out);The human-readable name of a script code, as an owned
daegun_text.For display and debugging. Free with
daegun_text_free.daegun_text_bidi_paragraphdaegun_status daegun_text_bidi_paragraph( const char *text, int32_t base, daegun_bidi_paragraph **out );The same bidi resolution as
daegun_text_resolve_bidi, kept in a handle rather than copied out.Use this when you will ask several questions of the same paragraph – splitting successive lines into visual runs, for instance – so the work is done once. Free with
daegun_bidi_paragraph_free.daegun_text_grapheme_boundariesdaegun_status daegun_text_grapheme_boundaries( const char *text, daegun_u32_list **out );Every position where a user-perceived character begins, as byte offsets in an owned
daegun_u32_list.What a person calls "a character" is a grapheme cluster, often several code points: a letter plus accents, a regional indicator pair, an emoji with a skin tone modifier. These are the boundaries cursor movement, selection and deletion should step by. Free with
daegun_u32_list_free.daegun_text_line_break_opportunitiesdaegun_status daegun_text_line_break_opportunities( const char *text, daegun_u32_list **out_at, daegun_blob **out_mandatory );Two lists at matching indices: byte offsets, and one byte each, non-zero for a break the text demands rather than merely allows.
daegun_text_line_visual_runsdaegun_status daegun_text_line_visual_runs( const daegun_bidi_paragraph *p, size_t start, size_t end, daegun_visual_runs **out );start and end are CHARACTER indices into the paragraph, not bytes.
daegun_text_resolve_bididaegun_status daegun_text_resolve_bidi( const char *text, int32_t base, uint8_t *out_base_level, daegun_blob **out_levels, daegun_u32_list **out_visual_order );Runs the Unicode bidirectional algorithm, writing each character's level and the visual order.
Mixed Arabic and Latin text is stored in reading order but drawn in another, and working out that order is a real algorithm rather than a reversal.
out_levelsis a byte per character – even is left to right, odd is right to left – andout_visual_orderlists character indices in drawing order.baseselects the paragraph direction: negative to detect it, 0 for left to right, 1 for right to left. Free both outputs.daegun_text_script_runsdaegun_status daegun_text_script_runs( const char *text, daegun_u32_list **out );Where each run of a single script begins, as byte offsets.
Shaping works on one script at a time, so mixed text must be split first. Common characters – spaces, digits, punctuation – fold into the surrounding run rather than starting a new one.
daegun_text_word_boundariesdaegun_status daegun_text_word_boundaries( const char *text, daegun_u32_list **out );Every position where a word begins or ends, as byte offsets in an owned
daegun_u32_list.Unicode word segmentation, for double-click selection and word-wise cursor movement. It handles what splitting on spaces does not: contractions stay whole, numbers with separators stay whole, and scripts written without spaces follow their own rules.
daegun_visual_runs_atdaegun_status daegun_visual_runs_at( const daegun_visual_runs *runs, size_t index, uint8_t *out_level, const size_t **out_chars, size_t *out_chars_count );One visual run by index: its embedding level and the characters it covers.
Even levels run left to right, odd levels right to left.
out_charsborrows from the runs handle. Runs are indexed in the order they should be drawn, so walking 0 upward lays the line out correctly without further reordering.daegun_visual_runs_countdaegun_status daegun_visual_runs_count( const daegun_visual_runs *runs, size_t *out );How many single-direction runs a line splits into, written to
out.daegun_visual_runs_freevoid daegun_visual_runs_free(daegun_visual_runs *runs);Frees a
daegun_visual_runs. The run array taken from it becomes invalid.daegun_writing_mode_is_verticalbool daegun_writing_mode_is_vertical(int32_t mode);Whether a writing mode runs top to bottom.
False for horizontal, true for both vertical modes. The two vertical modes differ only in whether successive lines advance left or right; both run their glyphs downward.
Library
Version and error reporting. Check daegun_abi_version against the header you compiled against before anything else, and read daegun_last_error after a call that failed.
daegun_abi_versionuint32_t daegun_abi_version(void);The ABI this library was built with, as (major << 16) | (minor << 8) | patch. Refuse a library whose major does not match what you compiled against. A struct that grew a field is not detectable any other way.
daegun_last_errordaegun_str daegun_last_error(void);What the last failing call ON THIS THREAD said, as UTF-8. Empty when nothing has failed. Per-thread, like errno, because handles are shared and a global slot would hand one thread another's failure. Valid until the next failing call on this thread.
Metal backend
Metal on Apple platforms. Every backend exposes the same 19 functions with the same semantics, so code written against one ports to another by changing the prefix. Available only where the platform is; a machine without it answers DAEGUN_UNSUPPORTED rather than failing to load.
daegun_metal_drawdaegun_status daegun_metal_draw( const daegun_metal_renderer *renderer, daegun_metal_target *target, const daegun_metal_geometry *geometry, const daegun_glyph_instance *instances, size_t instance_count, const daegun_subpixel_params *subpixel, int32_t mode );Draws glyph instances into a Metal target.
Takes the geometry and an array of instances, each placing one glyph at a position, scale and color. Instance offsets are in device pixels with y up from the bottom of the target, which is the opposite of the top-down convention most 2D layout uses – getting this wrong draws the text mirrored vertically. Use
_draw_withto choose the blending mode.daegun_metal_draw_withdaegun_status daegun_metal_draw_with( const daegun_metal_renderer *renderer, daegun_metal_target *target, const daegun_metal_geometry *geometry, const daegun_glyph_instance *instances, size_t instance_count, const daegun_subpixel_params *subpixel, int32_t mode, const float *projection );Under a projection of your own – column-major, NULL for the default.
daegun_metal_geometry_freevoid daegun_metal_geometry_free(daegun_metal_geometry *geometry);Frees Metal geometry and the device resources behind it.
Free it before the renderer it was created from.
daegun_metal_geometry_newdaegun_status daegun_metal_geometry_new( const daegun_metal_renderer *renderer, const daegun_batch *batch, daegun_metal_geometry **out );Uploads a batch's curves, bands and hulls to the Metal device as drawable geometry.
This is the step that moves glyph data onto the GPU. Build the batch first, then create geometry from it once and draw from that geometry many times – rebuilding it per frame throws away the whole point of the GPU path.
_geometry_revisiontells you when the batch has changed enough to need a fresh upload.daegun_metal_geometry_revisiondaegun_status daegun_metal_geometry_revision( const daegun_metal_geometry *geometry, uint64_t *out );Compare against daegun_batch_revision to know whether this upload is stale.
daegun_metal_orthodaegun_status daegun_metal_ortho( uint32_t width, uint32_t height, float *out );The default projection, column-major. Needs no device: it depends on this API's clip-space * convention and nothing else, so it can be built before anything is opened.
daegun_metal_read_pixelsconst uint8_t *daegun_metal_read_pixels( const daegun_metal_renderer *renderer, daegun_metal_target *target, size_t *out_count );Waits, copies off the device, and hands back BGRA. BORROWED, valid until the target is drawn * into again or freed. NULL on failure, with the reason in daegun_last_error. * * THIS IS THE EXPENSIVE CALL. On a discrete GPU the readback dominates: 489 us of a 513 us * frame at 1024x1024, measured. A caller that can consume the target on the device should not * call it at all.
daegun_metal_renderer_device_namedaegun_status daegun_metal_renderer_device_name( const daegun_metal_renderer *renderer, daegun_text **out );Free with daegun_text_free.
daegun_metal_renderer_freevoid daegun_metal_renderer_free(daegun_metal_renderer *renderer);Safe to call while a target or geometry is still alive; the device goes with the last.
daegun_metal_renderer_newdaegun_status daegun_metal_renderer_new(daegun_metal_renderer **out);DAEGUN_UNSUPPORTED when there is no such device, which is an answer and not a failure.
daegun_metal_renderer_profiledaegun_status daegun_metal_renderer_profile( const daegun_metal_renderer *renderer, daegun_device_profile **out );Free with daegun_device_profile_free.
daegun_metal_renderer_supports_subpixeldaegun_status daegun_metal_renderer_supports_subpixel( const daegun_metal_renderer *renderer, int32_t *out );Whether this Metal device can do subpixel rendering, written to
out.Subpixel output needs dual-source blending, which not every device and driver exposes. When this is false, use grayscale coverage instead – the text is still correct, just without the threefold horizontal resolution an LCD stripe would allow.
daegun_metal_target_freevoid daegun_metal_target_free(daegun_metal_target *target);Frees a Metal target.
Any pixel pointer taken from it becomes invalid.
daegun_metal_target_heightdaegun_status daegun_metal_target_height( const daegun_metal_target *target, uint32_t *out );The height of a Metal target in pixels, written to
out.daegun_metal_target_newdaegun_status daegun_metal_target_new( const daegun_metal_renderer *renderer, uint32_t width, uint32_t height, daegun_metal_target **out );Creates a Metal render target of the given size.
The target owns its pixels, which you read back with
_target_pixelsor_read_pixels. Free it with_target_freebefore the renderer it came from.daegun_metal_target_pixeldaegun_status daegun_metal_target_pixel( const daegun_metal_target *target, uint32_t x, uint32_t y, uint8_t *out );One pixel as BGRA into four bytes. DAEGUN_RANGE outside the target.
daegun_metal_target_pixelsconst uint8_t *daegun_metal_target_pixels( const daegun_metal_target *target, size_t *out_count );BORROWED, BGRA. What the last _read_pixels left behind, without the round trip. Valid until * the target is drawn into again or freed.
daegun_metal_target_widthdaegun_status daegun_metal_target_width( const daegun_metal_target *target, uint32_t *out );The width of a Metal target in pixels, written to
out.daegun_metal_waitdaegun_status daegun_metal_wait( const daegun_metal_renderer *renderer, daegun_metal_target *target );Blocks until the Metal device has finished the work already submitted.
Needed before reading pixels back, and at shutdown. On a normal render loop you do not want this – it gives up the parallelism between CPU and GPU that makes the GPU path worth using.
Options
Filling an options struct with its defaults. Always call the _default function rather than zeroing the struct yourself: zero is a meaningful value for several fields, and the defaults are not all zero.
daegun_raster_options_defaultdaegun_status daegun_raster_options_default(daegun_raster_options *out);Fills a
daegun_raster_optionswith its defaults.Call this rather than zeroing the struct. You can also pass NULL for the options to any call that takes them, which means exactly these defaults – so you need not build the struct at all unless you are changing something.
daegun_raster_options opts; daegun_raster_options_default(&opts); opts.hinting = DAEGUN_HINT_AUTO;
Outlines
Walking a glyph's outline through callbacks, and warming the outline cache ahead of time. Outline points are in font units, not on the 1000-unit em – divide by daegun_font_upm to get ems.
daegun_font_clear_prewarmdaegun_status daegun_font_clear_prewarm(const daegun_font *font);Drops every cached outline, leaving the rasterized glyph cache alone.
The two caches are independent:
daegun_font_clear_glyph_cacheclears the rasterized bitmaps, this clears the outlines.daegun_font_outline_glyphdaegun_status daegun_font_outline_glyph( const daegun_font *font, uint16_t gid, const daegun_pen *pen );The stored outline – the default instance, whatever location you are working at.
daegun_font_outline_glyph_instanceddaegun_status daegun_font_outline_glyph_instanced( const daegun_font *font, uint16_t gid, const daegun_axis *axes, size_t axes_len, const daegun_pen *pen );The same, resolving variation deltas at a location first.
daegun_font_prewarmdaegun_status daegun_font_prewarm( const daegun_font *font, const uint16_t *gids, size_t gids_len, const daegun_axis *axes, size_t axes_len, size_t *out_added );Loads and caches outlines for a set of glyphs ahead of time, writing how many were newly added.
Parsing and instancing an outline is the expensive part of drawing a glyph the first time. Doing it up front keeps that cost off the frame that needs the glyph. It caches outlines, not rasterized bitmaps, so it helps at every size. Glyphs already cached are skipped, hence the count.
Owned results
daegun returns lists and blobs behind opaque handles rather than as raw pointers, so that ownership is never in doubt. Each has a _data accessor that borrows – the pointer stays valid only until the handle is freed – and a _free that releases it. Copy the elements out if you need them to outlive the handle.
daegun_blob_dataconst uint8_t *daegun_blob_data(const daegun_blob *blob, size_t *out_count);The bytes of a
daegun_blob, borrowed, with the count written toout_count.Raw bytes daegun assembled for you – an instanced table, or a built font file. The pointer dies with the handle, so copy it out if you need it to outlive one. NULL if either argument is NULL.
daegun_blob_freevoid daegun_blob_free(daegun_blob *blob);Frees a
daegun_blob. Any pointer obtained from it becomes invalid.daegun_f64_list_dataconst double *daegun_f64_list_data( const daegun_f64_list *list, size_t *out_count );The elements of a
daegun_f64_list, borrowed, with the count written toout_count.Doubles on the 1000-unit em: advances, caret positions, ligature carets. The pointer dies with the handle, so copy it out if you need it to outlive one. NULL if either argument is NULL.
daegun_f64_list_freevoid daegun_f64_list_free(daegun_f64_list *list);Frees a
daegun_f64_list. Any pointer obtained from it becomes invalid.daegun_glyph_value_list_dataconst daegun_glyph_value *daegun_glyph_value_list_data( const daegun_glyph_value_list *list, size_t *out_count );The glyph and value pairs of a
daegun_glyph_value_list, borrowed, with the count written toout_count.Each entry pairs a glyph id with the value a table assigns it, in table order. The pointer dies with the handle, so copy it out if you need it to outlive one. NULL if either argument is NULL.
daegun_glyph_value_list_freevoid daegun_glyph_value_list_free(daegun_glyph_value_list *list);Frees a
daegun_glyph_value_list. Any pointer obtained from it becomes invalid.daegun_i32_list_dataconst int32_t *daegun_i32_list_data( const daegun_i32_list *list, size_t *out_count );The elements of a
daegun_i32_list, borrowed, with the count written toout_count.Signed 32-bit values, used for anything measured in whole font units. The pointer dies with the handle, so copy it out if you need it to outlive one. NULL if either argument is NULL.
daegun_i32_list_freevoid daegun_i32_list_free(daegun_i32_list *list);Frees a
daegun_i32_list. Any pointer obtained from it becomes invalid.daegun_str_list_atdaegun_status daegun_str_list_at( const daegun_str_list *list, size_t index, daegun_str *out );DAEGUN_RANGE past the end, rather than an empty string – a font may hold an empty name and you must be able to tell the two apart.
daegun_str_list_countdaegun_status daegun_str_list_count( const daegun_str_list *list, size_t *out );How many strings a
daegun_str_listholds, written toout.Pair it with
daegun_str_list_atto walk the list.daegun_str_list_freevoid daegun_str_list_free(daegun_str_list *list);Frees a
daegun_str_list.Every string borrowed from it becomes invalid.
daegun_text_freevoid daegun_text_free(daegun_text *text);Frees a
daegun_text. Anydaegun_strtaken from it becomes invalid.daegun_text_strdaegun_status daegun_text_str(const daegun_text *text, daegun_str *out);Borrows the string inside a
daegun_textas a pointer and length.The
daegun_strpoints into the text object and is valid only until it is freed. The string is both NUL-terminated and length-carrying, so either convention works – but prefer the length, since a font's name table may contain an interior NUL.daegun_u16_list_dataconst uint16_t *daegun_u16_list_data( const daegun_u16_list *list, size_t *out_count );The elements, borrowed. NULL if the list or the count pointer is NULL.
daegun_u16_list_freevoid daegun_u16_list_free(daegun_u16_list *list);Frees a
daegun_u16_list. Any pointer obtained from it becomes invalid.daegun_usize_list_dataconst size_t *daegun_usize_list_data( const daegun_usize_list *list, size_t *out_count );The elements of a
daegun_usize_list, borrowed, with the count written toout_count.Sizes and offsets, such as the per-glyph positions the
locatable stores. The pointer dies with the handle, so copy it out if you need it to outlive one. NULL if either argument is NULL.daegun_usize_list_freevoid daegun_usize_list_free(daegun_usize_list *list);Frees a
daegun_usize_list. Any pointer obtained from it becomes invalid.
Paths and stroking
Building a path by hand and stroking it. daegun_path * is an owned handle: create it, add verbs, use it, free it.
daegun_hinted_outline_drawdaegun_status daegun_hinted_outline_draw( const daegun_hinted_outline *outline, const daegun_pen *pen );Replays a hinted glyph onto a pen, converting F26Dot6 to whole pixels on the way. The other half of daegun_font_hinted_glyph: that one grid-fits, this turns the result back into geometry.
daegun_path_as_pendaegun_status daegun_path_as_pen(daegun_path *path, daegun_pen *out);A pen that appends to this path – the other direction through daegun_pen, so a glyph outline can be captured as a value. BORROWS the path: valid until the path is freed.
daegun_path_boundsdaegun_status daegun_path_bounds( const daegun_path *path, double *out_min_x, double *out_min_y, double *out_max_x, double *out_max_y );DAEGUN_ABSENT when the path has no points.
daegun_path_closedaegun_status daegun_path_close(daegun_path *path);Closes the current contour, joining its end back to its start.
Fill rules need closed contours to decide inside from outside, so close every contour you intend to fill.
daegun_path_costdaegun_status daegun_path_cost(const daegun_path *path, size_t *out);What the path costs to fill, in the engine's own units – the number daegun_policy compares against to decide whether a glyph is worth a GPU round trip.
daegun_path_curve_todaegun_status daegun_path_curve_to( daegun_path *path, float c1x, float c1y, float c2x, float c2y, float x, float y );Adds a cubic Bézier through two control points.
The curve CFF and PostScript outlines use.
daegun_path_freevoid daegun_path_free(daegun_path *path);Frees a path.
daegun_path_is_emptydaegun_status daegun_path_is_empty(const daegun_path *path, int32_t *out);Whether the path holds no segments, written to
outas 0 or 1.A glyph with no ink – a space – produces an empty path, which is normal rather than an error.
daegun_path_line_todaegun_status daegun_path_line_to(daegun_path *path, float x, float y);Adds a straight segment from the current point to a new one.
daegun_path_move_todaegun_status daegun_path_move_to(daegun_path *path, float x, float y);Starts a new contour at a point, ending any contour in progress.
Coordinates are in whatever space you are working in; daegun does not impose one here.
daegun_path_newdaegun_path *daegun_path_new(void);Creates an empty path, or NULL if allocation fails.
An owned handle: add verbs to it, use it, then free it with
daegun_path_free. Check for NULL – unlike the fallible calls this returns the pointer directly and has no status to report through.daegun_path *p = daegun_path_new(); if (!p) return 1; daegun_path_move_to(p, 0.0f, 0.0f); daegun_path_line_to(p, 100.0f, 0.0f); daegun_path_close(p); daegun_path_free(p);daegun_path_pointsdaegun_status daegun_path_points( const daegun_path *path, float *out_x, float *out_y, size_t capacity, size_t *out_count );Copies a path's points into arrays you supply, writing how many there are.
A copy rather than a borrow, so you own the result. Pass
capacityas the size of your arrays;out_countreports the true number of points, which may exceed it. Call once with a capacity of 0 to learn the count, then allocate and call again.size_t n = 0; daegun_path_points(path, NULL, NULL, 0, &n); /* how many? */ float *xs = malloc(n * sizeof *xs), *ys = malloc(n * sizeof *ys); daegun_path_points(path, xs, ys, n, &n);daegun_path_quad_todaegun_status daegun_path_quad_to( daegun_path *path, float cx, float cy, float x, float y );Adds a quadratic Bézier through one control point.
The curve TrueType outlines are made of, and the only curve the GPU path takes, so a shape built from quadratics can be drawn either way.
daegun_path_replaydaegun_status daegun_path_replay( const daegun_path *path, const double *transform, const daegun_pen *pen );Replays onto a pen, optionally through a 2x3 transform [a, b, c, d, e, f], or NULL for none.
daegun_path_strokedaegun_status daegun_path_stroke( const daegun_path *path, const daegun_stroke_style *style, float tolerance, const daegun_pen *pen );toleranceis how far the flattened curves may sit from the true ones, in the path's units.daegun_path_stroke_simplifieddaegun_status daegun_path_stroke_simplified( const daegun_path *path, const daegun_stroke_style *style, float tolerance, const daegun_pen *pen );The same, with the outline's self-intersections resolved into one boundary – what a filler that does not do non-zero winding needs, since a stroke overlaps itself at every join.
daegun_path_verbsdaegun_status daegun_path_verbs( const daegun_path *path, uint8_t *out, size_t capacity, size_t *out_count );The verbs and points, COPIED into your buffers – the one place this ABI copies rather than borrowing. Call with capacity 0 to learn the count, then again to fill.
daegun_scene_builder_filldaegun_status daegun_scene_builder_fill( daegun_scene_builder *b, size_t path_id, const uint8_t rgba[4], int32_t rule, const double transform[6] );rgbais four bytes,transformsix doubles [a, b, c, d, e, f] for x' = a*x + c*y + e.ruleis DAEGUN_FILL_NONZERO or DAEGUN_FILL_EVENODD. DAEGUN_RANGE for an unknown path id, an unknown rule, or a transform that is not finite.daegun_scene_builder_freevoid daegun_scene_builder_free(daegun_scene_builder *b);Frees a scene builder.
daegun_scene_builder_is_emptydaegun_status daegun_scene_builder_is_empty( const daegun_scene_builder *b, bool *out );Reading a builder back.
op_countis how many fills it holds;pathhands back a copy of one you pushed, which you free with daegun_path_free.daegun_scene_builder_newdaegun_scene_builder *daegun_scene_builder_new(void);Creates a scene builder, for composing a drawing of your own rather than receiving one from a color glyph.
Push paths into it, add fills referring to them, then render. Free with
daegun_scene_builder_free. Returns NULL if allocation fails, so check the pointer.daegun_scene_builder_op_countdaegun_status daegun_scene_builder_op_count( const daegun_scene_builder *b, size_t *out );How many drawing operations a scene builder holds, written to
out.daegun_scene_builder_pathdaegun_status daegun_scene_builder_path( const daegun_scene_builder *b, size_t path_id, daegun_path **out );One path from a scene builder by the id it was pushed under.
Returns
DAEGUN_RANGEfor an id the builder does not hold.daegun_scene_builder_push_pathdaegun_status daegun_scene_builder_push_path( daegun_scene_builder *b, const daegun_path *path, size_t *out_id );Copies the path in, so you may free or reuse yours immediately.
daegun_scene_builder_renderdaegun_status daegun_scene_builder_render( const daegun_scene_builder *b, float px, float upem, daegun_scene **out );The canvas is fitted to the ink, so fill a full-bleed background first if you want a fixed size. Free the result with daegun_scene_free.
Rasterizing
Turning a glyph into pixels. The result is coverage – one byte per pixel saying how much of it the glyph covers – which you tint yourself. It is a mask, not a color.
daegun_bitmap_freevoid daegun_bitmap_free(daegun_bitmap *bitmap);Frees a bitmap. The pixel pointer taken from it becomes invalid.
daegun_bitmap_metricsdaegun_status daegun_bitmap_metrics( const daegun_bitmap *bitmap, daegun_metrics *out );Where a rasterized bitmap sits and how big it is.
widthandheightare in pixels;xminandyminplace the bitmap relative to the pen, in pixels, with y up. Rows in the pixel data run top to bottom.daegun_metrics m; daegun_bitmap_metrics(bmp, &m); size_t len = 0; const uint8_t *px = daegun_bitmap_pixels(bmp, &len); /* m.width x m.height, one coverage byte each */daegun_bitmap_pixelsconst uint8_t *daegun_bitmap_pixels( const daegun_bitmap *bitmap, size_t *out_len );The coverage, borrowed. One byte per pixel for grayscale, three for a subpixel layout – the LENGTH is what says which, so divide by width * height rather than tracking what you asked for.
daegun_font_rasterize_glyphdaegun_status daegun_font_rasterize_glyph( const daegun_font *font, uint16_t gid, float px, const daegun_axis *axes, size_t axes_len, daegun_bitmap **out );DAEGUN_ABSENT for a glyph that draws nothing. A space rasterizes to no pixels, which is an answer rather than a failure.
daegun_font_rasterize_glyph_withdaegun_status daegun_font_rasterize_glyph_with( const daegun_font *font, uint16_t gid, float px, const daegun_axis *axes, size_t axes_len, const daegun_raster_options *opts, daegun_bitmap **out );A NULL opts means the defaults, so you need not build the struct to get them.
Raw tables and font building
The font's tables as bytes, and assembling a font file from tables of your own. For anything daegun does not model.
daegun_font_has_tabledaegun_status daegun_font_has_table( const daegun_font *font, const char *tag, int32_t *out );Whether the font contains a table with the given tag, written to
outas 0 or 1.Cheaper than fetching the table when you only want to know it is there – testing
"glyf"against"CFF "to tell TrueType outlines from CFF ones, for instance. Tags are four characters, padded with spaces.daegun_font_instance_tabledaegun_status daegun_font_instance_table( const daegun_font *font, const daegun_axis *axes, size_t axis_count, const char *tag, daegun_blob **out );One table of the font pinned to
axes, copied. Free with daegun_blob_free.daegun_font_instance_tablesdaegun_status daegun_font_instance_tables( const daegun_font *font, const daegun_axis *axes, size_t axis_count, daegun_table_map **out );Every table of the font pinned to
axes. DAEGUN_ABSENT only when a variable font's own variation tables do not parse. The bytes are COPIED out of the font. The Rust call borrows wherever a table passes through untouched, and that borrow cannot cross into C – nothing would stop the font being freed while the map still pointed into it. Use daegun_font_instance_table when only one table is wanted; it keeps the saving.daegun_font_tabledaegun_status daegun_font_table( const daegun_font *font, const char *tag, daegun_bytes *out );One table's bytes, exactly as the file stores them. BORROWED: valid until the font is freed.
tagis the four-character name – "GSUB", "cmap", "OS/2" – including any trailing space, since "cvt " and "CFF " really are spelled that way. DAEGUN_ABSENT when the font has no such table, which is not the same as a table that is empty. These are the font's bytes, not an instance's: a variable font'sglyfhere is the stored default shape. daegun_font_instance_table is what resolves a location.daegun_font_table_tagsdaegun_status daegun_font_table_tags( const daegun_font *font, daegun_str_list **out );Every table the font carries, in sorted order. Free with daegun_str_list_free.
daegun_outline_glyf_bytesdaegun_status daegun_outline_glyf_bytes( const uint8_t *glyf, size_t glyf_len, const size_t *loca, size_t loca_len, uint16_t glyph, const daegun_pen *pen );Draws one glyph straight out of
glyfbytes, composites resolved – the escape hatch for a table that did not come from a font this ABI opened.daegun_parse_locadaegun_status daegun_parse_loca( const uint8_t *loca, size_t len, int16_t format, size_t num_glyphs, daegun_usize_list **out );The offsets
locastores, one per glyph plus a terminator.formatis head's indexToLocFormat: 0 for the short form, 1 for the long one. Free with daegun_usize_list_free.daegun_table_map_builddaegun_status daegun_table_map_build( const daegun_table_map *map, daegun_blob **out );Assembles the map into an sfnt: the directory, the offsets, and the checksums. An empty map is DAEGUN_RANGE rather than a header describing nothing. Free with daegun_blob_free.
daegun_table_map_bytes_atdaegun_status daegun_table_map_bytes_at( const daegun_table_map *map, size_t index, daegun_bytes *out );One table's bytes by index, borrowed from the map.
Valid only until the map is freed. Index from 0 to the count minus one.
daegun_table_map_countdaegun_status daegun_table_map_count( const daegun_table_map *map, size_t *out );How many tables the map holds.
daegun_table_map_freevoid daegun_table_map_free(daegun_table_map *map);Frees a table map and everything added to it.
daegun_table_map_getdaegun_status daegun_table_map_get( const daegun_table_map *map, const char *tag, daegun_bytes *out );One table's bytes by tag, borrowed from the map.
Returns
DAEGUN_ABSENTwhen the map holds no table with that tag.daegun_table_map_newdaegun_table_map *daegun_table_map_new(void);Creates an empty table map, for assembling a font file from tables of your own.
Returns NULL if allocation fails. Free with
daegun_table_map_free.daegun_table_map_removedaegun_status daegun_table_map_remove( daegun_table_map *map, const char *tag );Removes a table from a map by tag.
Returns
DAEGUN_ABSENTwhen the map holds no table with that tag. Any bytes borrowed for that table become invalid.daegun_table_map_setdaegun_status daegun_table_map_set( daegun_table_map *map, const char *tag, const uint8_t *data, size_t len );Copies the bytes.
daegun_table_map_tag_atdaegun_status daegun_table_map_tag_at( const daegun_table_map *map, size_t index, daegun_str *out );BORROWED, and invalidated by _set and _remove as well as by _free.
Reading a table by hand
Bounds-checked big-endian readers and writers. OpenType stores everything big-endian, and these do the byte order and the range check together, so a truncated table produces a refusal rather than a read past the end.
daegun_aat_lookup_entriesdaegun_status daegun_aat_lookup_entries( const daegun_aat_lookup *lookup, daegun_glyph_value_list **out );Every mapping. Free with daegun_glyph_value_list_free.
daegun_aat_lookup_freevoid daegun_aat_lookup_free(daegun_aat_lookup *lookup);Frees an AAT lookup handle.
daegun_aat_lookup_opendaegun_status daegun_aat_lookup_open( const uint8_t *data, size_t len, uint16_t num_glyphs, daegun_aat_lookup **out );Opens an Apple Advanced Typography lookup table for reading.
AAT lookups map glyph ids to values in one of several formats.
num_glyphsbounds the lookup so a malformed table cannot claim entries beyond the font. Free withdaegun_aat_lookup_free.daegun_aat_lookup_valuedaegun_status daegun_aat_lookup_value( const daegun_aat_lookup *lookup, uint16_t glyph, uint16_t *out );DAEGUN_ABSENT when the lookup maps nothing to this glyph.
daegun_aat_state_table_classdaegun_status daegun_aat_state_table_class( const daegun_aat_state_table *table, uint16_t glyph, uint16_t *out );Out-of-bounds glyphs get the table's own out-of-bounds class.
daegun_aat_state_table_entrydaegun_status daegun_aat_state_table_entry( const daegun_aat_state_table *table, uint16_t state, uint16_t class_, daegun_aat_entry *out );DAEGUN_RANGE when the table has no such cell.
daegun_aat_state_table_freevoid daegun_aat_state_table_free(daegun_aat_state_table *table);Frees an Apple state machine table.
The state and entry arrays taken from it become invalid.
daegun_aat_state_table_opendaegun_status daegun_aat_state_table_open( const uint8_t *data, size_t len, size_t extra_words, uint16_t num_glyphs, daegun_aat_state_table **out );extra_wordsis how many type-specific words each entry carries: none for rearrangement, one for ligature and contextual substitution.daegun_ankr_anchor_pointdaegun_status daegun_ankr_anchor_point( const daegun_ankr *ankr, uint16_t glyph, uint16_t index, int16_t *out_x, int16_t *out_y );DAEGUN_ABSENT when the glyph has no anchor at that index.
daegun_ankr_control_pointdaegun_status daegun_ankr_control_point( const uint8_t *data, size_t len, size_t at, int16_t *out_x, int16_t *out_y );One control point read straight out of a buffer, without an ankr around it.
daegun_ankr_freevoid daegun_ankr_free(daegun_ankr *ankr);Frees an
ankrhandle.Anchor points read from it become invalid.
daegun_ankr_opendaegun_status daegun_ankr_open( const uint8_t *data, size_t len, uint16_t num_glyphs, daegun_ankr **out );Opens an Apple
ankranchor point table for reading.Anchor points are where marks attach in Apple's positioning model.
num_glyphsbounds the table so a malformed one cannot claim entries beyond the font. Free withdaegun_ankr_free.daegun_ankr_point_countdaegun_status daegun_ankr_point_count( const daegun_ankr *ankr, uint16_t glyph, uint32_t *out );How many anchor points a glyph has in an
ankrtable.Anchor points are where marks attach in Apple's positioning model.
daegun_ankr_versiondaegun_status daegun_ankr_version( const uint8_t *data, size_t len, uint16_t *out );The version of an Apple
ankranchor point table.Reads the version from raw bytes, bounds-checked, so a truncated table produces a refusal rather than a read past the end.
daegun_bytes_windowconst uint8_t *daegun_bytes_window( const uint8_t *data, size_t len, size_t off, size_t n );nbytes atoff, or NULL when they do not fit. BORROWSdata. The Rust form is const-generic – window<4> – and C has no such thing, so the width is an argument; what survives is the point of it: one bounds check, and a pointer either good for n bytes or NULL.daegun_coverage_glyphsdaegun_status daegun_coverage_glyphs( const uint8_t *buf, size_t len, size_t off, daegun_u16_list **out );Every glyph a coverage table covers, in table order. Free with daegun_u16_list_free.
daegun_coverage_indexdaegun_status daegun_coverage_index( const uint8_t *data, size_t len, uint16_t glyph, uint16_t *out );A glyph's index within a coverage table, or DAEGUN_ABSENT when it is not covered.
daegun_ot_roundint32_t daegun_ot_round(double value);The font-unit rounding the whole spec is written in: floor(v + 0.5).
daegun_read_i16_bedaegun_status daegun_read_i16_be( const uint8_t *data, size_t len, size_t off, int16_t *out );Reads a signed 16-bit big-endian value at an offset, bounds-checked.
Returns
DAEGUN_RANGErather than reading past the end, which is what makes it safe to point at a font table whose length you have not verified.daegun_read_offset24daegun_status daegun_read_offset24( const uint8_t *data, size_t len, size_t off, size_t *out );Reads a 24-bit offset at an offset, bounds-checked, as a
size_t.The same bytes
daegun_read_u24_bereads, typed for use as an offset into the same buffer.daegun_read_u16_bedaegun_status daegun_read_u16_be( const uint8_t *data, size_t len, size_t off, uint16_t *out );The engine's own bounds-checked readers, for a private table, a vendor extension, or a field daegun has no opinion about. Every one answers DAEGUN_RANGE rather than reading past the end.
daegun_read_u24_bedaegun_status daegun_read_u24_be( const uint8_t *data, size_t len, size_t off, uint32_t *out );Reads an unsigned 24-bit big-endian value at an offset, bounds-checked.
OpenType uses 24-bit fields in a few places, chiefly offsets inside large tables.
daegun_read_u32_bedaegun_status daegun_read_u32_be( const uint8_t *data, size_t len, size_t off, uint32_t *out );Reads an unsigned 32-bit big-endian value at an offset, bounds-checked.
daegun_records_fitint32_t daegun_records_fit( size_t start, size_t count, size_t stride, size_t len );Whether
countrecords ofstridebytes starting atstartfit withinlen. Reports the answer directly: an overflow in that arithmetic is one of the things it exists to catch.daegun_search_recordsdaegun_status daegun_search_records( size_t count, uint32_t target, int32_t (*key_at)(size_t index, void *user, uint32_t *out_key), void *user, size_t *out_index, int32_t *out_found );Binary-searches
countrecords whose keys you read out.key_atis called with an index and must write the key through out_key and return non-zero; returning zero means the record could not be read, and the search answers DAEGUN_ABSENT. It must not longjmp, throw, or free anything this call holds. On DAEGUN_OK, *out_found is non-zero whentargetwas present and *out_index is where; zero when it was not, and *out_index is where it would be inserted.daegun_write_i16_bedaegun_status daegun_write_i16_be( uint8_t *data, size_t len, size_t off, int16_t value );Writes a signed 16-bit big-endian value at an offset, bounds-checked.
Returns
DAEGUN_RANGErather than writing past the end of the buffer.daegun_write_offset24daegun_status daegun_write_offset24( uint8_t *data, size_t len, size_t off, size_t value );Writes a 24-bit offset at an offset, bounds-checked.
daegun_write_u16_bedaegun_status daegun_write_u16_be( uint8_t *data, size_t len, size_t off, uint16_t value );The writers. The Rust ones return nothing and no-op out of range; these say so instead, because a caller writing past the end of its own buffer wants to be told.
daegun_write_u32_bedaegun_status daegun_write_u32_be( uint8_t *data, size_t len, size_t off, uint32_t value );Writes an unsigned 32-bit big-endian value at an offset, bounds-checked.
Shaping
Turning a string into positioned glyphs using the font's own rules. The resulting daegun_run * holds glyphs, advances and offsets in parallel arrays of the same length, borrowed from the run until it is freed.
daegun_font_measure_widthdaegun_status daegun_font_measure_width( const daegun_font *font, const char *text, const daegun_axis *axes, size_t axes_len, double font_size, double *out );The width of a string in points at a given font size, without producing glyphs.
Note the units: this one takes a point size and answers in the same units, because it exists for measuring rather than drawing. It still shapes internally, so ligatures and kerning are accounted for. If you are about to draw the text anyway, shape once and sum the advances instead of calling this as well.
daegun_font_shapedaegun_status daegun_font_shape( const daegun_font *font, const char *text, const daegun_axis *axes, size_t axes_len, bool vertical, daegun_run **out );Shapes a string into positioned glyphs, applying the font's ligature, kerning and reordering rules.
The call most work starts from. The resulting
daegun_run *holds glyphs, advances and offsets in parallel arrays of equal length, which you read withdaegun_run_glyphs,daegun_run_advancesanddaegun_run_offsets. Those pointers are borrowed from the run and stop being valid the moment you free it. Advances are on the 1000-unit em, so a pen position in pixels isadvance * px / 1000.0. Passtrueforverticalto set the text top to bottom. The glyph count is not the character count: ligatures reduce it and reordering can raise it.daegun_run *run = NULL; if (daegun_font_shape(font, "Waffle", NULL, 0, false, &run) != DAEGUN_OK) return 1; size_t n = 0; const uint16_t *gids = daegun_run_glyphs(run, &n); const double *adv = daegun_run_advances(run, &n); double x = 0.0, px = 32.0; for (size_t i = 0; i < n; i++) { double pen_x = x * px / 1000.0; /* draw gids[i] at pen_x */ x += adv[i]; } daegun_run_free(run); /* gids and adv are invalid from here */daegun_font_shape_with_featuresdaegun_status daegun_font_shape_with_features( const daegun_font *font, const char *text, const daegun_axis *axes, size_t axes_len, bool vertical, const char *script, const daegun_feature *features, size_t features_len, daegun_run **out );script may be NULL, which is how C says None.
daegun_font_shape_with_languagedaegun_status daegun_font_shape_with_language( const daegun_font *font, const char *text, const daegun_axis *axes, size_t axes_len, bool vertical, const char *language, daegun_run **out );Shapes a string with an explicit language tag, so language-specific forms are applied.
Some fonts draw differently by language within one script: Serbian Cyrillic wants different italic forms from Russian, and Turkish needs the dotless i handled correctly.
languageis a BCP 47 code such as"sr"or"tr". Pass NULL to use the font's default rules.daegun_run *run = NULL; daegun_font_shape_with_language(font, "бити", NULL, 0, false, "sr", &run);daegun_font_shape_with_optionsdaegun_status daegun_font_shape_with_options( const daegun_font *font, const char *text, const daegun_axis *axes, size_t axes_len, bool vertical, const daegun_shape_options *opts, daegun_run **out );A NULL opts means the defaults.
daegun_run_advancesconst double *daegun_run_advances(const daegun_run *run, size_t *out_count);The advance of each glyph in a shaped run, borrowed, with the count written to
out_count.On the 1000-unit em, so a pen step in pixels is
advance * px / 1000.0. Index-aligned withdaegun_run_glyphsanddaegun_run_offsets. Valid only until the run is freed.daegun_run_clustersconst uint32_t *daegun_run_clusters( const daegun_run *run, size_t *out_count );Which byte of the input each glyph came from. Several glyphs may share a cluster, and one glyph may span several characters.
daegun_run_completedaegun_status daegun_run_complete(const daegun_run *run, bool *out);Whether shaping finished without running into a limit, written to
out.False means the shaper stopped early – a resource bound was reached on pathological input. The run is still valid and drawable; it may simply not represent the entire string. Worth checking when the text came from somewhere you do not control.
daegun_run_freevoid daegun_run_free(daegun_run *run);Frees a shaped run.
Every array borrowed from it – glyphs, advances, offsets, clusters and the shaping flags – becomes invalid.
daegun_run_glyphsconst uint16_t *daegun_run_glyphs(const daegun_run *run, size_t *out_count);Borrowed views into a run, all valid until it is freed.
daegun_run_has_broken_syllabledaegun_status daegun_run_has_broken_syllable( const daegun_run *run, bool *out );Whether the text contained a syllable the script's rules could not form correctly, written to
out.Indic and Southeast Asian scripts build syllables from ordered parts, and text can be malformed – a vowel sign with nothing to attach to, for instance. When this is true the shaper has produced its best effort, usually including a dotted circle marking the problem.
daegun_run_offsetsconst double *daegun_run_offsets(const daegun_run *run, size_t *out_count);TWO doubles per glyph, x then y, so glyph i is at 2 * i. out_count is the number of DOUBLES.
daegun_run_safe_to_insert_tatweelconst uint8_t *daegun_run_safe_to_insert_tatweel( const daegun_run *run, size_t *out_count );One flag per glyph: whether an Arabic tatweel may be inserted here, borrowed.
Justifying Arabic by elongation means inserting tatweel into the joins, but not every join tolerates it. Reported only when the shape options ask for it.
daegun_run_shaperdaegun_status daegun_run_shaper(const daegun_run *run, daegun_str *out);Which shaping model the run went through – the script's own, or the general one.
daegun_run_unsafe_to_breakconst uint8_t *daegun_run_unsafe_to_break( const daegun_run *run, size_t *out_count );One flag per glyph: whether breaking the line here would change the shaping, borrowed.
Shaping a string in one piece and shaping it in two can differ – a ligature spanning the break would not form. A set flag means this position is unsafe to break at, so a line breaker that wants to reuse shaping results must reshape from an earlier safe point.
daegun_run_unsafe_to_concatconst uint8_t *daegun_run_unsafe_to_concat( const daegun_run *run, size_t *out_count );One flag per glyph: whether this run can be joined to an adjacent one without reshaping, borrowed.
The complement of the break flags, for the case where you are stitching runs together rather than splitting them. Reported only when the shape options ask for it.
daegun_shape_options_defaultdaegun_status daegun_shape_options_default(daegun_shape_options *out);Fills a
daegun_shape_optionswith its defaults.Call this rather than zeroing the struct. Several fields have meaningful non-zero defaults, and a zeroed struct will not shape the way the plain
daegun_font_shapedoes. Fill it, then change only the fields you care about.daegun_shape_options opts; daegun_shape_options_default(&opts); opts.language = "tr"; opts.features = (daegun_feature[]){ { "liga", 0 } }; opts.features_len = 1;
Subsetting
Cutting a font down to the glyphs a page actually uses. Subsetting by text is the call to reach for, because it shapes first and so keeps the glyphs that ligatures and joining actually produced.
daegun_font_base_infodaegun_status daegun_font_base_info( const daegun_font *font, const char *script_tag, bool vertical, daegun_text **out_default_baseline, daegun_str_list **out_baseline_tags, daegun_f64_list **out_baseline_coords );What BASE says for one script. Any out-parameter may be NULL; a script that names no default baseline writes a NULL handle rather than failing.
daegun_font_base_is_glyph_freedaegun_status daegun_font_base_is_glyph_free( const daegun_font *font, bool *out );Whether the font's
BASEtable describes baselines without referring to specific glyphs.A
BASEtable may define baselines by pointing at glyphs, which makes it unusable after subsetting removes them. This tells you whether that dependency exists.daegun_font_glyph_closuredaegun_status daegun_font_glyph_closure( const daegun_font *font, const uint16_t *gids, size_t gids_len, const daegun_axis *axes, size_t axes_len, daegun_u16_list **out );Expands a set of glyphs to include everything the font's substitution rules could turn them into.
Follows
GSUBfrom the ids you give to find ligatures they could form, alternates a feature could select, and marks that could be substituted in. Subsetting to the closure rather than the bare set is what keeps a subset from breaking when a feature fires. Free the result withdaegun_u16_list_free.daegun_font_math_constantdaegun_status daegun_font_math_constant( const daegun_font *font, int32_t which, double *out );One math layout constant by index, on the 1000-unit em.
Superscript and subscript shifts, fraction bar thickness and gaps, radical clearances, the axis height that fraction bars center on.
whichruns from 0 todaegun_math_constant_countminus one. ReturnsDAEGUN_ABSENTfor a font with noMATHtable – only fonts built for mathematics carry one.daegun_font_math_glyph_variantsdaegun_status daegun_font_math_glyph_variants( const daegun_font *font, uint16_t gid, bool vertical, daegun_math_construction **out );The larger or assembled forms of a growing symbol, as an owned
daegun_math_construction.A bracket around a tall expression grows in two stages: first through discrete larger variants, then, when none is big enough, by assembling repeating pieces. This returns both.
verticalpicks the growth direction. Free withdaegun_math_construction_free.daegun_font_math_is_extended_shapedaegun_status daegun_font_math_is_extended_shape( const daegun_font *font, uint16_t gid, bool *out );Whether a glyph is tall enough that math layout should treat it as an extended shape.
Extended shapes – big integrals, tall brackets – take different superscript and subscript positioning from ordinary letters.
daegun_font_math_italics_correctiondaegun_status daegun_font_math_italics_correction( const daegun_font *font, uint16_t gid, double *out );The italic correction for a glyph, on the 1000-unit em.
A slanted glyph leans past its advance, so anything set immediately after it – a superscript especially – needs nudging right by this much. Ignoring it is why naive math rendering collides subscripts with the italic letters before them.
daegun_font_math_kerndaegun_status daegun_font_math_kern( const daegun_font *font, uint16_t gid, int32_t corner, double height, double *out );An unrecognized corner is DAEGUN_RANGE rather than a default: the four are not interchangeable, so guessing one would be a wrong answer rather than a fallback.
daegun_font_math_min_connector_overlapdaegun_status daegun_font_math_min_connector_overlap( const daegun_font *font, double *out );How much adjacent pieces of an assembled glyph must overlap, on the 1000-unit em.
When stacking the pieces of a tall bracket, butting them end to end leaves visible seams. The font states a minimum overlap and this returns it.
daegun_font_math_top_accent_attachmentdaegun_status daegun_font_math_top_accent_attachment( const daegun_font *font, uint16_t gid, double *out );Where an accent should be centered over a glyph, horizontally, on the 1000-unit em.
Centring on the bounding box looks wrong over a slanted or asymmetric letter, so the font names the correct point instead.
daegun_font_stat_infodaegun_status daegun_font_stat_info( const daegun_font *font, daegun_stat **out );The
STATtable, describing how this font sits within its wider family, as an owned handle.Names the family's design axes and the value this face takes on each, so a font menu can group separate files into one family. Returns
DAEGUN_ABSENTfor a font without the table.daegun_font_subsetdaegun_status daegun_font_subset( const daegun_font *font, const uint16_t *gids, size_t gids_len, const daegun_axis *axes, size_t axes_len, daegun_subset **out );Cuts the font down to a specific set of glyph ids, producing a complete font file.
Use this when you already know the glyphs. It takes ids rather than text, so it cannot know about substitutions you have not accounted for – run
daegun_font_glyph_closurefirst if the set came from plaincmaplookups. Preferdaegun_font_subset_textwhen you have the text.daegun_font_subset_textdaegun_status daegun_font_subset_text( const daegun_font *font, const char *text, const daegun_axis *axes, size_t axes_len, daegun_subset **out );Cuts the font down to what is needed to render the given text, producing a complete font file.
The call to reach for. It shapes the text first, then keeps the glyphs shaping actually produced – so ligatures, joined Arabic forms and reordered Indic clusters survive, which a naive character-to-glyph subset would break. The result carries a real
cmap, so it drops straight into@font-face. Free withdaegun_subset_free.daegun_subset *sub = NULL; if (daegun_font_subset_text(font, "Type is the voice of the page.", NULL, 0, &sub) == DAEGUN_OK) { size_t len = 0; const uint8_t *ttf = daegun_subset_ttf(sub, &len); fwrite(ttf, 1, len, out); daegun_subset_free(sub); /* ttf is invalid from here */ }daegun_math_constant_countint32_t daegun_math_constant_count(void);How many math layout constants exist, so a caller can walk them all.
The
MATHtable holds dozens of measurements. Rather than a struct with dozens of fields, the C API exposes them by index: this gives the upper bound anddaegun_font_math_constantreads one.for (int32_t i = 0; i < daegun_math_constant_count(); i++) { double v = 0; if (daegun_font_math_constant(font, i, &v) == DAEGUN_OK) { /* ... */ } }daegun_math_construction_assemblydaegun_status daegun_math_construction_assembly( const daegun_math_construction *c, double *out_italics_correction, size_t *out_part_count, const uint16_t **out_part_gids, const double **out_part_values );The assembly, if there is one. out_part_values holds FOUR doubles per part – start connector, end connector, full advance, and is_extender as 0 or 1 – so part i begins at 4 * i. DAEGUN_ABSENT where the construction carries no assembly.
daegun_math_construction_freevoid daegun_math_construction_free(daegun_math_construction *c);Frees a
daegun_math_construction.daegun_math_construction_variantsdaegun_status daegun_math_construction_variants( const daegun_math_construction *c, size_t *out_count, const uint16_t **out_gids, const double **out_advances );The discrete variants: their glyph ids, with advances at matching indices.
daegun_stat_axesdaegun_status daegun_stat_axes( const daegun_stat *stat, size_t *out_count, daegun_str_list **out_tags, const uint16_t **out_orderings );The axes a
STATtable declares: how many, their tags, and their ordering.The tag list is owned and must be freed with
daegun_str_list_free; the orderings array is borrowed from thedaegun_statand dies with it. The ordering controls the sequence axes should be presented in, which is not always the order they are stored in.daegun_stat_elided_fallback_namedaegun_status daegun_stat_elided_fallback_name( const daegun_stat *stat, daegun_text **out );The name to use when every axis sits at its default, as an owned
daegun_text.Usually "Regular". Free with
daegun_text_free.daegun_stat_freevoid daegun_stat_free(daegun_stat *stat);Frees a
daegun_stat.The axis tags, orderings and combination values taken from it become invalid.
daegun_stat_value_countdaegun_status daegun_stat_value_count(const daegun_stat *stat, size_t *out);How many axis values STAT names. The values themselves are not exposed yet.
daegun_subset_freevoid daegun_subset_free(daegun_subset *subset);Frees a subset. The TTF bytes and gid map taken from it become invalid.
daegun_subset_gid_mapconst uint16_t *daegun_subset_gid_map( const daegun_subset *subset, size_t *out_len );How old glyph ids map to new ones in the subset, borrowed, with the length written to
out_len.Subsetting renumbers glyphs. Index this by an old glyph id to get its id in the subset. Valid only until the subset is freed.
daegun_subset_ttfconst uint8_t *daegun_subset_ttf( const daegun_subset *subset, size_t *out_len );The subset font's bytes, borrowed, with the length written to
out_len.A complete, valid font file. The pointer belongs to the subset and is invalid once the subset is freed, so write it out or copy it first.
The atlas packer, and rules
A shelf packer for building glyph atlases, plus small predicates over the enums this API uses.
daegun_cluster_level_is_graphemesdaegun_status daegun_cluster_level_is_graphemes( int32_t level, int32_t *out );Two of the four cluster levels group by grapheme, and two are monotone – so neither question is a comparison against one constant.
daegun_cluster_level_is_monotonedaegun_status daegun_cluster_level_is_monotone(int32_t level, int32_t *out);Whether a cluster level guarantees cluster values never decrease, written to
out.Monotone clusters can be binary searched, which matters for hit testing a long line. The non-monotone levels allow values to decrease across a reordering, so they cannot.
daegun_device_profile_from_d3ddaegun_status daegun_device_profile_from_d3d( int32_t software, int32_t uma, const char *name, daegun_device_profile **out );A device profile from what Direct3D or Metal reports.
umais a tri-state – negative unknown, zero discrete, positive unified – because the Rust argument is Option<bool> and the third state is the one that matters: whether a readback crosses a bus is not something to guess at.daegun_device_profile_from_metaldaegun_status daegun_device_profile_from_metal( int32_t uma, const char *name, daegun_device_profile **out );Builds a device profile for a Metal device, given whether it has unified memory.
On Apple silicon the GPU shares memory with the CPU, which changes what is worth uploading. Pass
umanon-zero for unified memory. The profile is a description – it neither owns nor touches your device, which is what keeps this API free ofunsafe.daegun_device_profile_is_softwaredaegun_status daegun_device_profile_is_software( const daegun_device_profile *profile, int32_t *out );Whether this is a software rasterizer pretending to be a GPU. Not kind == DAEGUN_DEVICE_SOFTWARE, which is what you would write and what breaks the day a second kind counts.
daegun_glyph_slot_instance_affinedaegun_status daegun_glyph_slot_instance_affine( const daegun_glyph_slot *slot, const float *offset, float scale, const float *transform, const float *tint, daegun_glyph_instance *out );The affine variant of daegun_glyph_slot_instance:
transformis the 2x2 part [a, b, c, d], and the em-space extent each axis needs is the length of its column. For a rotation or a skew.daegun_hint_mode_may_autohintdaegun_status daegun_hint_mode_may_autohint(int32_t mode, int32_t *out);Whether a hint mode may run the autohinter. Two of the five may; which two is daegun's rule, and a caller that wrote the comparison out is the one that breaks when a third joins them.
daegun_line_metrics_heightdaegun_status daegun_line_metrics_height( const daegun_line_metrics *metrics, double *out );Baseline to baseline: ascent - descent + line_gap. A call rather than a subtraction you write, because
descentis negative and the obviousascent + descent + line_gapis off by twice it while looking plausible on every font.daegun_script_is_context_dependentdaegun_status daegun_script_is_context_dependent( uint16_t script, int32_t *out );Whether a script takes its identity from what surrounds it rather than standing alone: true for Common (punctuation, digits, spaces), Inherited (combining marks), and unknown ids. A comma between two Arabic words belongs to that run; between two Latin words, to that one. NOT about contextual shaping – Arabic joins and Devanagari reorders, and both are scripts in their own right.
daegun_script_opentype_tagsdaegun_status daegun_script_opentype_tags( uint16_t script, daegun_str_list **out );The OpenType tags that correspond to a script code.
A script can map to more than one tag – Devanagari has both the old
devaand the newerdev2, with different shaping rules – so this returns a list. Free withdaegun_str_list_free.daegun_shelf_packer_freevoid daegun_shelf_packer_free(daegun_shelf_packer *packer);Frees a shelf packer.
daegun_shelf_packer_insertdaegun_status daegun_shelf_packer_insert( daegun_shelf_packer *packer, size_t width, size_t height, daegun_rect *out );Finds room for a rectangle in the atlas and writes where it went.
Returns a failure status when the atlas is full, which is your signal to start a new one or evict. Insert glyphs in descending height order for a tighter pack.
daegun_shelf_packer_newdaegun_shelf_packer *daegun_shelf_packer_new(size_t width, size_t height);Creates a shelf packer for building a glyph atlas of the given size.
Shelf packing places rectangles in rows, which suits glyphs well because they are similar in height within a size. Returns NULL if allocation fails. Free with
daegun_shelf_packer_free.daegun_shelf_packer *p = daegun_shelf_packer_new(1024, 1024); daegun_rect r; if (daegun_shelf_packer_insert(p, w, h, &r) == DAEGUN_OK) { /* blit the glyph at r.x, r.y */ } daegun_shelf_packer_free(p);daegun_shelf_packer_resetdaegun_status daegun_shelf_packer_reset(daegun_shelf_packer *packer);Empties the atlas, keeping its size.
daegun_stat_combo_valuesconst daegun_axis_value *daegun_stat_combo_values( const daegun_stat *stat, size_t *out_count );The
STATformat 4 records, which name combinations across several axes at once.Some families name a whole combination – "Display Condensed" – rather than one axis value. Borrowed from the
daegun_statand valid only until it is freed.daegun_stat_value_atdaegun_status daegun_stat_value_at( const daegun_stat *stat, size_t index, daegun_stat_value *out );One value record from a
STATtable by index.Each record ties an axis position to a name, which is what lets a font menu label a weight slider with real names rather than numbers.
daegun_stat_value_namedaegun_status daegun_stat_value_name( const daegun_stat *stat, size_t index, daegun_str *out );BORROWED, valid until the STAT handle is freed – unlike most strings here, which come back as a daegun_text you free. These are already owned by the handle. DAEGUN_ABSENT when unnamed.
daegun_subpixel_layout_keydaegun_status daegun_subpixel_layout_key(int32_t layout, uint64_t *out);A named layout's identity, for use as a cache key: two layouts that filter identically share one, so a glyph cached under either is valid under the other.
daegun_subpixel_params_dilationdaegun_status daegun_subpixel_params_dilation( const daegun_subpixel_params *params, float *out );How far past its box a glyph's coverage reaches, per axis. Two of these, and they are different: _dilation is fractional pixels, _pad is the whole pixels an atlas slot must grow by, rounding up on the negative origin only.
daegun_subpixel_params_from_weightsdaegun_status daegun_subpixel_params_from_weights( uint8_t oversample_x, uint8_t oversample_y, uint8_t taps_x, uint8_t taps_y, int8_t origin_x, int8_t origin_y, const float *weights, daegun_subpixel_params *out );A filter of your own: three arrays of taps_x * taps_y weights, one per channel, laid end to end. DAEGUN_RANGE past DAEGUN_MAX_SUBPIXEL_TAPS or DAEGUN_MAX_SUPERSAMPLE.
daegun_subpixel_params_paddaegun_status daegun_subpixel_params_pad( const daegun_subpixel_params *params, size_t *out );How many extra pixels of margin a subpixel filter needs on each side, written to
out.The filter spreads coverage into neighboring pixels, so a glyph rendered with one needs a wider buffer than its outline suggests. Add this to each side or the edges will be clipped.
daegun_subpixel_params_with_supersamplingdaegun_status daegun_subpixel_params_with_supersampling( const daegun_subpixel_params *params, uint32_t n, daegun_subpixel_params *out );The same filter, supersampled n times.
daegun_subset_new_giddaegun_status daegun_subset_new_gid( const daegun_subset *subset, uint16_t old_gid, uint16_t *out );What a glyph id became after subsetting, DAEGUN_ABSENT if the subset dropped it. NOT the same as indexing daegun_subset_gid_map yourself, and that is the point: an empty map means every glyph kept its old id, and a zero for any id but zero means dropped rather than mapped to .notdef. Get either wrong and glyphs render as the wrong shape, silently.
The cache
The font memoizes rasterized glyphs so drawing the same text twice does not rasterize it twice. These calls size that cache, empty it, and report what it holds.
daegun_font_clear_glyph_cachedaegun_status daegun_font_clear_glyph_cache(const daegun_font *font);Drops every cached glyph, keeping the byte bound.
daegun_font_glyph_cache_statsdaegun_status daegun_font_glyph_cache_stats( const daegun_font *font, size_t *out_count, size_t *out_bytes );How many glyphs the cache holds, and how many bytes that is. Either pointer may be NULL.
daegun_font_set_glyph_cache_bytesdaegun_status daegun_font_set_glyph_cache_bytes( const daegun_font *font, size_t bytes );Resizes the rasterized-glyph cache, in bytes. Zero turns caching off. Bounded by bytes rather than entries because one glyph at 4096px outweighs thousands at 12px, so a count would let a single large render blow the budget.
The tag inventory
Which scripts, languages and features the font has layout rules for. These are OpenType's own four-character tags, not BCP 47 language codes.
daegun_font_feature_tagsdaegun_status daegun_font_feature_tags( const daegun_font *font, const char *script, const char *language, daegun_str_list **out );Either tag may be NULL, which is how C says None – the Rust signature takes Option for both.
daegun_font_justification_glyphsdaegun_status daegun_font_justification_glyphs( const daegun_font *font, const char *script_tag, daegun_u16_list **out );The glyphs a script nominates for justification, as an owned
daegun_u16_list.From the
JSTFtable – chiefly the Arabic kashida, which may be inserted or elongated to stretch a line. Needed only when implementing justification yourself.daegun_font_language_tagsdaegun_status daegun_font_language_tags( const daegun_font *font, const char *script, daegun_str_list **out );The languages a given script has specific rules for.
Tags are OpenType's own, such as
"SRB "and"TRK "– note the padding to four characters – not BCP 47 codes.daegun_font_shape_with_languagetakes BCP 47 and maps it for you.daegun_font_script_tagsdaegun_status daegun_font_script_tags( const daegun_font *font, daegun_str_list **out );Every script the font has layout rules for, as four-character OpenType tags.
Tags such as
latn,arab,deva. This is what the font hasGSUBandGPOSrules for, which is not the same as what it has glyphs for. Free withdaegun_str_list_free.
Variation data
The machinery underneath variable fonts: item variation stores, delta set index maps, and feature variations. Reach for these only when implementing variation behavior yourself.
daegun_char_general_categorydaegun_status daegun_char_general_category( uint32_t codepoint, int32_t *out );The Unicode general category of a code point, written to
out.Letter, mark, number, punctuation, symbol, separator and the rest – the classification the text algorithms are built on. Exposed because a caller doing its own segmentation needs the same data daegun uses.
daegun_char_is_uprightdaegun_status daegun_char_is_upright( uint32_t codepoint, int32_t has_vertical_form, int32_t *out );Whether a character stands upright in vertical text.
has_vertical_formis what daegun_char_vertical_form answers for the same character: the two characters whose orientation is "rotated unless a vertical form exists" need it, and asking keeps this from looking the substitution up twice.daegun_char_vertical_formdaegun_status daegun_char_vertical_form(uint32_t codepoint, uint32_t *out);The vertical presentation form, or DAEGUN_ABSENT when there is none – an em dash becomes a vertical one, an ideographic comma moves to the corner of its box.
daegun_d3d11_feature_leveldaegun_status daegun_d3d11_feature_level( const daegun_d3d11_renderer *renderer, daegun_text **out );Direct3D only: the feature level as a string like "11_1", and whether the adapter is WARP – which daegun_policy's avoid_software_gpu exists to steer away from.
daegun_d3d11_is_softwaredaegun_status daegun_d3d11_is_software( const daegun_d3d11_renderer *renderer, int32_t *out );Whether a D3D11 renderer is running on the software rasterizer rather than real hardware.
Windows falls back to WARP when no suitable GPU is available. It works, but far slower than hardware, so the CPU path may be the better choice.
daegun_d3d12_feature_leveldaegun_status daegun_d3d12_feature_level( const daegun_d3d12_renderer *renderer, daegun_text **out );The Direct3D feature level of a D3D12 renderer, as an owned
daegun_text.For reporting and for deciding what a device can be asked to do. Free with
daegun_text_free.daegun_d3d12_is_softwaredaegun_status daegun_d3d12_is_software( const daegun_d3d12_renderer *renderer, int32_t *out );Whether a D3D12 renderer is running on the software rasterizer rather than real hardware.
Windows falls back to WARP when no suitable GPU is available. It works, but it is far slower than hardware, so a caller may prefer the CPU path instead of pretending it has a GPU.
daegun_delta_set_index_map_countdaegun_status daegun_delta_set_index_map_count( const daegun_delta_set_index_map *map, size_t *out );How many entries a delta set index map holds.
daegun_delta_set_index_map_freevoid daegun_delta_set_index_map_free(daegun_delta_set_index_map *map);Frees a delta set index map.
Entries read from it become invalid.
daegun_delta_set_index_map_lookupdaegun_status daegun_delta_set_index_map_lookup( const daegun_delta_set_index_map *map, size_t index, size_t *out_outer, size_t *out_inner );An index past the end is not an error: the map clamps to its last entry, which is what the spec says a map shorter than the item count means.
daegun_delta_set_index_map_parsedaegun_status daegun_delta_set_index_map_parse( const uint8_t *buf, size_t len, size_t base, daegun_delta_set_index_map **out );Parses a delta set index map from a buffer at a given offset.
Maps an item – a glyph, a metric – to its entry in an item variation store, so several items can share one set of deltas. Free with
daegun_delta_set_index_map_free.daegun_device_profile_freevoid daegun_device_profile_free(daegun_device_profile *profile);Frees a device profile.
The name taken from it is owned separately and is not affected.
daegun_device_profile_from_vulkandaegun_status daegun_device_profile_from_vulkan( int32_t device_type, const char *name, daegun_device_profile **out );From a VkPhysicalDeviceType, for a caller that already called vkGetPhysicalDeviceProperties.
daegun_device_profile_kinddaegun_status daegun_device_profile_kind( const daegun_device_profile *profile, int32_t *out );What sort of device a profile describes, written to
outas aDAEGUN_DEVICE_*value.Discrete, integrated, virtual, software, or unknown.
daegun_device_profile_namedaegun_status daegun_device_profile_name( const daegun_device_profile *profile, daegun_text **out );The name a device profile was created with, as an owned
daegun_text.Free with
daegun_text_free.daegun_device_profile_newdaegun_status daegun_device_profile_new( int32_t kind, const char *name, daegun_device_profile **out );Describes a GPU to the router, so it can decide which glyphs are worth sending there.
kindsays what sort of device it is andnameis for reporting. The profile does not own or touch your device – it is a description, which is what keeps this API free ofunsafe.daegun_feature_variations_atdaegun_status daegun_feature_variations_at( const uint8_t *layout, size_t len, size_t at, daegun_feature_variations **out );The same, at an offset you already know. Always succeeds: the Rust
atis infallible and lets the accessors bounds-check, so reporting a failure here would mean inventing one.daegun_feature_variations_finddaegun_status daegun_feature_variations_find( const daegun_feature_variations *vars, const int32_t *coords, size_t coord_count, uint16_t *out );Which variation record applies at
coords– normalized axis coordinates in 2.14 fixed point, as daegun_font_normalized_axes produces. DAEGUN_ABSENT when none does.daegun_feature_variations_freevoid daegun_feature_variations_free(daegun_feature_variations *vars);Frees a
daegun_feature_variations.Substitution records taken from it become invalid.
daegun_feature_variations_opendaegun_status daegun_feature_variations_open( const uint8_t *layout, size_t len, daegun_feature_variations **out );DAEGUN_ABSENT when the layout table carries none, which most do.
daegun_feature_variations_substitutedaegun_status daegun_feature_variations_substitute( const daegun_feature_variations *vars, uint16_t variation, uint16_t feature, size_t *out );The alternate feature table a variation substitutes for
feature, as an offset.daegun_glyph_slot_instancedaegun_status daegun_glyph_slot_instance( const daegun_glyph_slot *slot, const float *offset, float scale, const float *em_pixels, const float *tint, daegun_glyph_instance *out );Builds a glyph's instance data from where its geometry landed in the batch. Use this rather than filling daegun_glyph_instance in yourself.
inv_scalemust be zero and not infinity at a zero scale, or the coordinate the fragment shader rebuilds stops being finite; andbands_per_axisis one number because both axes are sliced by the same n, which is not something the struct can say.offsetandem_pixelsare two floats,tintis four, RGBA.daegun_ivs_axis_countdaegun_status daegun_ivs_axis_count(const daegun_ivs *ivs, size_t *out);How many axes an item variation store covers.
daegun_ivs_deltadaegun_status daegun_ivs_delta( const daegun_ivs *ivs, size_t outer, size_t inner, const double *scalars, size_t scalar_count, double *out );One delta, interpolated from those scalars.
daegun_ivs_freevoid daegun_ivs_free(daegun_ivs *ivs);Frees an item variation store.
Every delta row borrowed from it becomes invalid.
daegun_ivs_ivd_countdaegun_status daegun_ivs_ivd_count(const daegun_ivs *ivs, size_t *out);How many ItemVariationData subtables, and how many delta rows in one.
daegun_ivs_ivd_region_indicesconst size_t *daegun_ivs_ivd_region_indices( const daegun_ivs *ivs, size_t ivd, size_t *out_count );BORROWED. Valid until the store is freed. NULL on a bad index.
daegun_ivs_ivd_rowconst int32_t *daegun_ivs_ivd_row( const daegun_ivs *ivs, size_t ivd, size_t inner, size_t *out_count );One row of deltas from an item variation data subtable, borrowed.
The deltas line up with this subtable's own region list, not with the store's global region numbering – read that list from
daegun_ivs_ivd_region_indicesand map through it before callingdaegun_ivs_region_axis. Scale each delta by how strongly its region applies at the axis position you want, then sum: that is variable font interpolation in one line.daegun_ivs_ivd_rowsdaegun_status daegun_ivs_ivd_rows( const daegun_ivs *ivs, size_t ivd, size_t *out );How many rows one item variation data subtable holds, written to
out.A store is divided into subtables, each a matrix of deltas: one row per item, one column per region.
ivdselects the subtable.daegun_ivs_parsedaegun_status daegun_ivs_parse( const uint8_t *buf, size_t len, size_t base, daegun_ivs **out );Parses an item variation store from a buffer at a given offset.
The structure underneath every variable font: it holds the deltas that move values as axes change.
baseis the offset withinbufwhere the store begins. Free withdaegun_ivs_free. You need this only when implementing variation behavior yourself.daegun_ivs_region_axisdaegun_status daegun_ivs_region_axis( const daegun_ivs *ivs, size_t region, size_t axis, daegun_region_axis *out );One axis of one region: its start, peak and end coordinates.
The delta applies fully at the peak and falls to nothing at the start and end, which is how a variable font interpolates smoothly between masters.
daegun_ivs_region_countdaegun_status daegun_ivs_region_count(const daegun_ivs *ivs, size_t *out);How many variation regions the store defines.
A region is a portion of the design space over which one set of deltas applies, given as a start, peak and end per axis.
daegun_ivs_region_scalarsdaegun_status daegun_ivs_region_scalars( const daegun_ivs *ivs, const double *location, size_t axis_count, daegun_f64_list **out );Each region's scalar at
location, computed once for a whole run of deltas – the scalars depend only on the location, so a caller resolving a thousand glyphs at one location computes them once. Free with daegun_f64_list_free.daegun_metal_geometry_syncdaegun_status daegun_metal_geometry_sync( daegun_metal_geometry *geometry, const daegun_metal_renderer *renderer, const daegun_batch *batch );Metal only: re-uploads into an existing geometry if the batch changed, and does nothing if it did not – so it may be called every frame. The plain _geometry call allocates a new upload each time. The other three backends have no equivalent to translate.
daegun_routedaegun_status daegun_route( int32_t attempt, const daegun_request *request, const daegun_device_profile *device, const daegun_policy *policy, int32_t *out );The routing decision on its own, without drawing anything – for a caller with its own drawing to do that wants only the policy answered.
devicemay be NULL, meaning there is no GPU.daegun_shader_sourcedaegun_status daegun_shader_source( int32_t language, int32_t stage, daegun_text **out );Free with daegun_text_free. An owned string rather than a borrowed view of the static source, because every shader compiler wants either a NUL or a length and daegun_text carries both.
daegun_subpixel_params_from_layoutdaegun_status daegun_subpixel_params_from_layout( int32_t layout, daegun_subpixel_params *out );layoutis one of the DAEGUN_LAYOUT_* constants; DAEGUN_LAYOUT_GRAYSCALE is the default.
Variations
Variable font axes and the named positions along them. Every call that takes const daegun_axis *axes takes a count beside it, and passing NULL with a count of 0 means the default position.
daegun_font_axesdaegun_status daegun_font_axes( const daegun_font *font, daegun_str_list **out_tags, daegun_f64_list **out_ranges );The declared axes. Three things per axis, so two lists: out_tags gets the tags, out_ranges gets [min, default, max] per axis – axis i is at 3 * i. Either may be NULL.
daegun_font_instancedaegun_status daegun_font_instance( const daegun_font *font, const daegun_axis *axes, size_t axes_len, daegun_blob **out );The face instanced at a location, as a complete font file.
daegun_font_named_instancedaegun_status daegun_font_named_instance( const daegun_font *font, size_t index, daegun_text **out_name, daegun_text **out_postscript_name, daegun_str_list **out_coord_tags, daegun_f64_list **out_coord_values );One named instance. Any out-parameter may be NULL. An instance that states no name writes a NULL handle rather than failing, so one absent name does not hide the coordinates you asked for in the same call. DAEGUN_RANGE past the end.
daegun_font_named_instance_countdaegun_status daegun_font_named_instance_count( const daegun_font *font, size_t *out );How many named positions the designer defined in the variation space.
Named instances are the positions worth naming – "Bold", "Condensed Light" – and are what a font menu should offer, even though the axes themselves are continuous. Zero for a static font.
daegun_font_normalized_axesdaegun_status daegun_font_normalized_axes( const daegun_font *font, const daegun_axis *axes, size_t axes_len, daegun_f64_list **out );One -1..=1 coordinate per fvar axis, in the font's own axis order.
Vulkan backend
Vulkan, available anywhere the loader finds a driver. The same 19 functions every backend has. Opened by name at run time, so it adds nothing to link.
daegun_vulkan_drawdaegun_status daegun_vulkan_draw( const daegun_vulkan_renderer *renderer, daegun_vulkan_target *target, const daegun_vulkan_geometry *geometry, const daegun_glyph_instance *instances, size_t instance_count, const daegun_subpixel_params *subpixel, int32_t mode );Draws glyph instances into a Vulkan target.
Takes the geometry and an array of instances, each placing one glyph at a position, scale and color. Instance offsets are in device pixels with y up from the bottom of the target, which is the opposite of the top-down convention most 2D layout uses – getting this wrong draws the text mirrored vertically. Use
_draw_withto choose the blending mode.daegun_vulkan_draw_withdaegun_status daegun_vulkan_draw_with( const daegun_vulkan_renderer *renderer, daegun_vulkan_target *target, const daegun_vulkan_geometry *geometry, const daegun_glyph_instance *instances, size_t instance_count, const daegun_subpixel_params *subpixel, int32_t mode, const float *projection );Under a projection of your own – column-major, NULL for the default.
daegun_vulkan_geometry_freevoid daegun_vulkan_geometry_free(daegun_vulkan_geometry *geometry);Frees Vulkan geometry and the device resources behind it.
Free it before the renderer it was created from.
daegun_vulkan_geometry_newdaegun_status daegun_vulkan_geometry_new( const daegun_vulkan_renderer *renderer, const daegun_batch *batch, daegun_vulkan_geometry **out );Uploads a batch's curves, bands and hulls to the Vulkan device as drawable geometry.
This is the step that moves glyph data onto the GPU. Build the batch first, then create geometry from it once and draw from that geometry many times – rebuilding it per frame throws away the whole point of the GPU path.
_geometry_revisiontells you when the batch has changed enough to need a fresh upload.daegun_vulkan_geometry_revisiondaegun_status daegun_vulkan_geometry_revision( const daegun_vulkan_geometry *geometry, uint64_t *out );Compare against daegun_batch_revision to know whether this upload is stale.
daegun_vulkan_orthodaegun_status daegun_vulkan_ortho( uint32_t width, uint32_t height, float *out );The default projection, column-major. Needs no device: it depends on this API's clip-space * convention and nothing else, so it can be built before anything is opened.
daegun_vulkan_read_pixelsconst uint8_t *daegun_vulkan_read_pixels( const daegun_vulkan_renderer *renderer, daegun_vulkan_target *target, size_t *out_count );Waits, copies off the device, and hands back BGRA. BORROWED, valid until the target is drawn * into again or freed. NULL on failure, with the reason in daegun_last_error. * * THIS IS THE EXPENSIVE CALL. On a discrete GPU the readback dominates: 489 us of a 513 us * frame at 1024x1024, measured. A caller that can consume the target on the device should not * call it at all.
daegun_vulkan_renderer_device_namedaegun_status daegun_vulkan_renderer_device_name( const daegun_vulkan_renderer *renderer, daegun_text **out );Free with daegun_text_free.
daegun_vulkan_renderer_freevoid daegun_vulkan_renderer_free(daegun_vulkan_renderer *renderer);Safe to call while a target or geometry is still alive; the device goes with the last.
daegun_vulkan_renderer_newdaegun_status daegun_vulkan_renderer_new(daegun_vulkan_renderer **out);DAEGUN_UNSUPPORTED when there is no such device, which is an answer and not a failure.
daegun_vulkan_renderer_profiledaegun_status daegun_vulkan_renderer_profile( const daegun_vulkan_renderer *renderer, daegun_device_profile **out );Free with daegun_device_profile_free.
daegun_vulkan_renderer_supports_subpixeldaegun_status daegun_vulkan_renderer_supports_subpixel( const daegun_vulkan_renderer *renderer, int32_t *out );Whether this Vulkan device can do subpixel rendering, written to
out.Subpixel output needs dual-source blending, which not every device and driver exposes. When this is false, use grayscale coverage instead – the text is still correct, just without the threefold horizontal resolution an LCD stripe would allow.
daegun_vulkan_target_freevoid daegun_vulkan_target_free(daegun_vulkan_target *target);Frees a Vulkan target.
Any pixel pointer taken from it becomes invalid.
daegun_vulkan_target_heightdaegun_status daegun_vulkan_target_height( const daegun_vulkan_target *target, uint32_t *out );The height of a Vulkan target in pixels, written to
out.daegun_vulkan_target_newdaegun_status daegun_vulkan_target_new( const daegun_vulkan_renderer *renderer, uint32_t width, uint32_t height, daegun_vulkan_target **out );Creates a Vulkan render target of the given size.
The target owns its pixels, which you read back with
_target_pixelsor_read_pixels. Free it with_target_freebefore the renderer it came from.daegun_vulkan_target_pixeldaegun_status daegun_vulkan_target_pixel( const daegun_vulkan_target *target, uint32_t x, uint32_t y, uint8_t *out );One pixel as BGRA into four bytes. DAEGUN_RANGE outside the target.
daegun_vulkan_target_pixelsconst uint8_t *daegun_vulkan_target_pixels( const daegun_vulkan_target *target, size_t *out_count );BORROWED, BGRA. What the last _read_pixels left behind, without the round trip. Valid until * the target is drawn into again or freed.
daegun_vulkan_target_widthdaegun_status daegun_vulkan_target_width( const daegun_vulkan_target *target, uint32_t *out );The width of a Vulkan target in pixels, written to
out.daegun_vulkan_waitdaegun_status daegun_vulkan_wait( const daegun_vulkan_renderer *renderer, daegun_vulkan_target *target );Blocks until the Vulkan device has finished the work already submitted.
Needed before reading pixels back, and at shutdown. On a normal render loop you do not want this – it gives up the parallelism between CPU and GPU that makes the GPU path worth using.
What it says
What the font claims about itself: its names, its style, its vertical metrics. Every measurement is on a 1000-unit em whatever the font's own units are, so a value in pixels is value * px / 1000.0.
daegun_font_ascenderdaegun_status daegun_font_ascender(const daegun_font *font, int32_t *out);The ascender on the 1000-unit em: how far the font rises above the baseline.
Resolved respecting the font's own preference between its typographic and Windows metrics. For laying out lines use
daegun_font_line_metrics, which gives ascent, descent and line gap together.daegun_font_bboxdaegun_status daegun_font_bbox( const daegun_font *font, daegun_i32_list **out );[xmin, ymin, xmax, ymax], in font units.
daegun_font_cap_heightdaegun_status daegun_font_cap_height(const daegun_font *font, int32_t *out);The height of a flat capital on the 1000-unit em.
Taken from OS/2. When the font does not carry the value this writes the ascender instead, which is larger than a real cap height, so treat it as a best effort. To be certain, measure a capital with
daegun_font_glyph_bounds, which is on the same 1000-unit em.daegun_font_descenderdaegun_status daegun_font_descender(const daegun_font *font, int32_t *out);The descender on the 1000-unit em, as a negative number.
Negative because it is a coordinate below the baseline rather than a distance. Line height is therefore
ascender - descender + line_gap.daegun_font_family_namedaegun_status daegun_font_family_name( const daegun_font *font, daegun_text **out );DAEGUN_ABSENT where the face states none.
daegun_font_flagsdaegun_status daegun_font_flags(const daegun_font *font, uint32_t *out);PDF font descriptor flags for this font, ready to write into a
/Flagsentry.Bit 0 fixed pitch, bit 1 serif, bit 3 script, bit 5 nonsymbolic, bit 6 italic. Bit 5 is always set. Serif and script are inferred from the OS/2 family class, italic from the italic angle.
daegun_font_is_bolddaegun_status daegun_font_is_bold(const daegun_font *font, bool *out);Whether the font declares itself bold, from bit 5 of the OS/2
fsSelectionfield.A declaration rather than a weight. On a variable font it reflects the default instance, so a face you have moved along
wghtstill answers about where it started.daegun_font_is_italicdaegun_status daegun_font_is_italic(const daegun_font *font, bool *out);The five selection predicates. All five are DAEGUN_ABSENT where the face states no OS/2 table.
daegun_font_is_obliquedaegun_status daegun_font_is_oblique(const daegun_font *font, bool *out);Whether the font declares itself oblique, from bit 9 of the OS/2
fsSelectionfield.Oblique means slanted upright forms; italic means redrawn letterforms. Many faces set only the italic bit regardless of which they are.
daegun_font_is_regulardaegun_status daegun_font_is_regular(const daegun_font *font, bool *out);Whether the font declares itself the regular member of its family, from bit 6 of
fsSelection.Well-behaved families set this on exactly one face, which makes it the cleanest way to pick a default from a directory of files without parsing style names.
daegun_font_is_variabledaegun_status daegun_font_is_variable(const daegun_font *font, bool *out);Whether the font has variation axes, written to
out.True when the font carries an
fvartable. When false, passing axes to any call is harmless and simply has no effect.daegun_font_italic_angledaegun_status daegun_font_italic_angle( const daegun_font *font, double *out );The italic angle in degrees, counter-clockwise from vertical.
Negative for the usual forward slant, so an italic reports something near -12. Zero means upright. Unlike the OS/2 italic bit this is a measurement rather than a declaration, which makes it the better test when a font sets its bits carelessly.
daegun_font_line_metricsdaegun_status daegun_font_line_metrics( const daegun_font *font, bool vertical, daegun_line_metrics *out );Ascent, descent and line gap together, on the 1000-unit em, for horizontal or vertical text.
The call to use when setting lines. It resolves the disagreement between the font's typographic and Windows metrics according to the font's own flag, so you do not have to. Pass
trueforverticalto read the vertical metrics instead. Descent is negative, so line height isascent - descent + line_gap.daegun_line_metrics m; daegun_font_line_metrics(font, false, &m); double line_height_px = (m.ascent - m.descent + m.line_gap) * 16.0 / 1000.0;daegun_font_name_stringdaegun_status daegun_font_name_string( const daegun_font *font, uint16_t name_id, daegun_text **out );One string from the
nametable by its id, as an owneddaegun_text.Familiar ids: 1 family, 2 subfamily, 4 full name, 6 PostScript name, 8 manufacturer, 13 license, 14 license URL. Returns
DAEGUN_ABSENTwhen the font does not carry that id. Free withdaegun_text_free.daegun_text *ps = NULL; if (daegun_font_name_string(font, 6, &ps) == DAEGUN_OK) { daegun_str s; daegun_text_str(ps, &s); printf("PostScript name: %.*s\n", (int)s.len, s.data); daegun_text_free(ps); }daegun_font_namesdaegun_status daegun_font_names( const daegun_font *font, daegun_u16_list **out_ids, daegun_str_list **out_strings );Every name the
nametable holds. Two lists rather than a map, because C has no map: the id at index i in out_ids belongs to the string at index i in out_strings. Either may be NULL.daegun_font_os2_infodaegun_status daegun_font_os2_info( const daegun_font *font, daegun_os2_info *out );The OS/2 table as a struct: version, family class, selection bits and both sets of vertical metrics.
Returns
DAEGUN_ABSENTfor a font with no OS/2 table, which some converted CFF fonts lack. Theselectionfield carries the rawfsSelectionbits: 0x0001 italic, 0x0020 bold, 0x0040 regular, 0x0080 use typographic metrics, 0x0200 oblique.daegun_font_styledaegun_status daegun_font_style(const daegun_font *font, daegun_text **out);Either
"italic"or"normal", as an owneddaegun_text.A deliberately narrow answer covering the case CSS cares about. It reports italic when either the OS/2 italic bit or the
headitalic bit is set, so a font that sets only one is still caught. Free withdaegun_text_free.daegun_font_trackingdaegun_status daegun_font_tracking( const daegun_font *font, double ptem, bool horizontal, double *out );Tracking at a point size, in font units.
daegun_font_typographic_metricsdaegun_status daegun_font_typographic_metrics( const daegun_font *font, const daegun_axis *axes, size_t axes_len, daegun_typographic_metrics *out );x-height, underline, strikeout, and subscript and superscript placement, at a given axis position.
Everything needed to decorate text correctly rather than guessing. Drawing an underline at a fixed offset looks wrong across fonts; these are the values the designer chose. Because it takes axes, the result reflects any variation of those metrics. All on the 1000-unit em.
daegun_font_uses_typo_metricsdaegun_status daegun_font_uses_typo_metrics( const daegun_font *font, bool *out );Whether the font asks you to prefer its typographic metrics over its Windows metrics, from bit 7 of
fsSelection.A font carries two sets of vertical metrics that frequently disagree.
daegun_font_line_metricsalready applies this rule for you – read this only if you are choosing between them yourself.