commit ad67ab3f8406fb381d6fc5d262d1e1c664889a90 Author: Emery Hemingway Date: Sun Mar 26 22:37:38 2023 -0500 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..344ed92 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/.envrc +/.direnv diff --git a/fontconfig_actor.nimble b/fontconfig_actor.nimble new file mode 100644 index 0000000..e3aeec2 --- /dev/null +++ b/fontconfig_actor.nimble @@ -0,0 +1,13 @@ +# Package + +version = "20230326" +author = "Emery Hemingway" +description = "Syndicate actor for asserting Fontconfig information" +license = "Unlicense" +srcDir = "src" +bin = @["fontconfig_actor"] + + +# Dependencies + +requires "nim >= 1.6.10" diff --git a/protocol.prs b/protocol.prs new file mode 100644 index 0000000..c09424d --- /dev/null +++ b/protocol.prs @@ -0,0 +1,3 @@ +version 1 . +Serve = . +FontAssertion = . diff --git a/src/Tupfile b/src/Tupfile new file mode 100644 index 0000000..b6ec286 --- /dev/null +++ b/src/Tupfile @@ -0,0 +1,3 @@ +include_rules +: foreach ../*.prs |> !preserves_schema_nim |> {schema} +: fontconfig_actor.nim | {schema} $(SYNDICATE_PROTOCOL) |> !nim_bin |> diff --git a/src/fontconfig_actor.nim b/src/fontconfig_actor.nim new file mode 100644 index 0000000..6c97d6a --- /dev/null +++ b/src/fontconfig_actor.nim @@ -0,0 +1,112 @@ +# SPDX-FileCopyrightText: ☭ Emery Hemingway +# SPDX-License-Identifier: Unlicense + +import std/[asyncdispatch, os, osproc, strutils] +import preserves, syndicate, syndicate/[patterns] +import ./protocol + +{.passC: staticExec("pkg-config --cflags fontconfig").} +{.passL: staticExec("pkg-config --libs fontconfig").} + +{.pragma: fcHeader, header: "".} +{.pragma: importFc, fcHeader, importc: "Fc$1".} + +const + FcFalse = cint 0 + + FC_FAMILY = "family" + FC_STYLE = "style" + FC_WEIGHT = "weight" + FC_SIZE = "size" + FC_FILE = "file" + +type + FcChar8* = uint8 + FcBool* = cint + + FcResult {.importc.} = enum + FcResultMatch, FcResultNoMatch, FcResultTypeMismatch, FcResultNoId, + FcResultOutOfMemory + + FcPattern = distinct pointer + + FcConfig = distinct pointer + +proc Init(): FcBool {.importFc.} ## Initialize fontconfig library +proc Fini() {.importFc.} ## Finalize fontconfig library. + +proc PatternCreate(): FcPattern {.importFc.} +proc PatternDestroy(p: FcPattern) {.importFc.} + +proc PatternAddInteger(p: FcPattern; obj: cstring; i: cint): FcBool {.importFc.} +proc PatternAddDouble(p: FcPattern; obj: cstring; d: cdouble): FcBool {.importFc.} +proc PatternAddString(p: FcPattern; obj, s: cstring): FcBool {.importFc.} +proc DefaultSubstitute(p: FcPattern) {.importFc.} +proc PatternPrint(p: FcPattern) {.importFc.} + +proc FontMatch(cfg: FcConfig; p: FcPattern; r: var FcResult): FcPattern {.importFc.} + +proc PatternGetString(p: FcPattern; obj: cstring; n: cint; + s: ptr cstring): FcResult {.importFc.} + +proc findSystemTypeface*(family = ""; style = ""; weight = 0; size = 0.0): string = + ## Find a path to an appropriate system typeface for the given parameters. + ## This proc always returns a path to a typeface file, results may vary. + # TODO: only return font in supported formats + if Init() == FcFalse: + raise newException(IOError, "Failed to initialize FontConfig") + + var pat = PatternCreate() + DefaultSubstitute(pat) + if family != "": + discard PatternAddString(pat, FC_FAMILY, family); + if style != "": + discard PatternAddString(pat, FC_STYLE, style); + if weight != 0: + discard PatternAddInteger(pat, FC_WEIGHT, cint weight); + if size != 0.0: + discard PatternAddDouble(pat, FC_SIZE, size); + + var + res = FcResultNoMatch + font = FontMatch(nil, pat, res) + if res == FcResultMatch: + # PatternPrint(font); + var path: cstring + if PatternGetString(font, FC_FILE, 0, addr path) == FcResultMatch: + result = $path + PatternDestroy(font) + + PatternDestroy(pat) + Fini() + if result == "": + raise newException(IOError, "Failed to find a system typeface") + +proc serve(ds: Ref; turn: var Turn) = + let observation = ?Observe(pattern: !FontAssertion) ?? {0: grabDict()} + during(turn, ds, observation) do (properties: Assertion): + stderr.writeLine "looking for ", properties + +#[ + let propPat = { toSymbol("family", Ref) : ?"Foo" }.dictionaryPattern + let testPat = FontAssertion ? { 0: propPat, 1: grab(), 2: grab() } + + onPublish(turn, ds, testPat) do (file: string, index: int): + stderr.writeLine "found file ", file, " with index ", index + + during(turn, ds, Observe ? { 0: grab() }) do (pat: Assertion): + stderr.writeLine "observed request for ", pat + + during(turn, ds, Observe ? { 0: ?(FontAssertion ? { 0: drop()}) }) do: + stderr.writeLine "fontconfig observe found! " +]# + + +bootDataspace("main") do (root: Ref; turn: var Turn): + connectStdio(root, turn) + if Init() == FcFalse: + quit"Failed to initialize FontConfig" + during(turn, root, ?Serve) do (ds: Ref): + serve(ds, turn) + +runForever() diff --git a/src/protocol.nim b/src/protocol.nim new file mode 100644 index 0000000..265d65f --- /dev/null +++ b/src/protocol.nim @@ -0,0 +1,18 @@ + +import + std/typetraits, preserves, std/tables + +type + Serve* {.preservesRecord: "serve".} = object + `cap`* {.preservesEmbedded.}: Preserve[void] + + FontAssertion* {.preservesRecord: "fontconfig".} = object + `properties`*: Table[Symbol, Preserve[void]] + `file`*: string + `index`*: BiggestInt + +proc `$`*(x: Serve | FontAssertion): string = + `$`(toPreserve(x)) + +proc encode*(x: Serve | FontAssertion): seq[byte] = + encode(toPreserve(x))