m-fonts-plugins.mkiv / last modification: 2020-01-30 14:16
%D \module
%D   [      file=m-fonts-plugins,
%D        version=2016.10.10,
%D          title=\CONTEXT\ Fonts,
%D       subtitle=Font Engine Plugins,
%D         author=Hans Hagen,
%D           date=\currentdate,
%D      copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
%C
%C This module is part of the \CONTEXT\ macro||package and is
%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
%C details.

%D See source code for comments. I wrote this a follow up on a presentation by
%D Kai Eigner, left it for a while, and sort of finalized it the last quarter of
%D 2016. As I don't use this module, apart from maybe testing something, it is
%D not guaranteed to work. Also, plugins can interfere with other functionality
%D in \CONTEXT\ so don't expect too much support. The two modules mentioned
%D below should work in the generic loader too. It's anyhow an illustration of
%D how \type {ffi} can work be used in a practical application.

% \enabletrackers[resolvers.ffilib]

\registerctxluafile{font-txt}{} % generic text handler
\registerctxluafile{font-phb}{} % harfbuzz plugin

\startluacode

    local function processlist(data)
        local list    = data.list
        local timings = data.results
        for i=1,#list do
            local name = list[i]
            local data = timings[name]
            local none = data["context none"] or 0
            local node = data["context node"] or 0
            if node > 0.1 then
                context.starttabulate { "|l|c|c|c|c|c|" }
                    context.NC() context.bold(name)
                    context.NC() context([[$t$]])
                    context.NC() context([[$t - t_{\hbox{\tx none}}$]])
                    context.NC() context([[$t - t_{\hbox{\tx node}}$]])
                    context.NC() context([[$t / t_{\hbox{\tx node}}$]])
                    context.NC() context([[$\frac{t - t_{\hbox{\txx none}}}{t_{\hbox{\txx node}} - t_{\hbox{\txx none}}}$]])
                    context.NC() context.NR()
                    context.TL()
                    for k, v in table.sortedhash(data) do
                        context.NC() context(k)
                        context.NC() context("%0.2f",v)
                        context.NC() context("%0.2f",v - none)
                        context.NC() context("%0.2f",v - node)
                        context.NC() context("%0.2f",v / node)
                        context.NC() if node ~= none then context("%0.2f",(v-none) / (node-none)) end
                        context.NC() context.NR()
                    end
                context.stoptabulate()
            end
        end
    end

    moduledata.plugins = {
        processlist = processlist,
    }

\stopluacode

\continueifinputfile{m-fonts-plugins.mkiv}

\usemodule[art-01]

\starttext

\edef\tufte{\cldloadfile{tufte.tex}}
\edef\khatt{\cldloadfile{khatt-ar.tex}}

\startbuffer[latin-definitions]
\definefont[TestA][Serif*test]
\definefont[TestB][SerifItalic*test]
\definefont[TestC][SerifBold*test]
\stopbuffer

\startbuffer[latin-text]
\TestA \tufte \par
\TestB \tufte \par
\TestC \tufte \par
\dorecurse {10} {%
    \TestA Fluffy Test Font A
    \TestB Fluffy Test Font B
    \TestC Fluffy Test Font C
}\par
\stopbuffer

\startbuffer[arabic-definitions]
\definedfont[Arabic*test at 14pt]
\setupinterlinespace[line=18pt]
\setupalign[r2l]
\stopbuffer

\startbuffer[arabic-text]
\dorecurse {10} {
    \khatt\space
    \khatt\space
    \khatt
    \blank
}
\stopbuffer

\startbuffer[mixed-definitions]
\definefont[TestL][Serif*test]
\definefont[TestA][Arabic*test at 14pt]
\setupinterlinespace[line=18pt]
\setupalign[r2l]
\stopbuffer

\startbuffer[mixed-text]
\dorecurse {2} {
    {\TestA\khatt\space\khatt\space\khatt}
    {\TestL\lefttoright\tufte}
    \blank
    \dorecurse{10}{%
        {\TestA وَ قَرْمِطْ بَيْنَ الْحُرُوفِ؛ فَإِنَّ}
        {\TestL\lefttoright A snippet text that makes no sense.}
    }
}
\stopbuffer

\definefontfeature
  [test-none]
  [mode=none]

\definefontfeature
  [test-base]
  [mode=base,
   liga=yes,
   kern=yes]

% no tlig and no analyze

\definefontfeature
  [test-node]
  [mode=node,
   script=auto,
   autoscript=position,
   autolanguage=position,
   ccmp=yes,
   liga=yes,
 % rlig=yes,
 % hlig=yes,
 % dlig=yes,
   clig=yes,
   kern=yes,
   mark=yes,
   mkmk=yes,
   curs=yes]

\definefontfeature
  [test-text]
  [mode=plug,
   features=text]

\definefontfeature
  [test-native]
  [mode=plug,
   features=harfbuzz,
  %liga=yes,
  %kern=yes,
   shaper=native]

\definefontfeature
  [test-uniscribe]
  [mode=plug,
   features=harfbuzz,
  %liga=yes,
  %kern=yes,
   shaper=uniscribe]

\definefontfeature
  [test-binary]
  [mode=plug,
   features=harfbuzz,
  %liga=yes,
  %kern=yes,
   shaper=uniscribe,
   method=binary]

\definefontfeature
  [arabic-node]
  [arabic]
  [salt=yes] % seems to be needed for husayni

\definefontfeature
  [arabic-native]
  [mode=plug,
   features=harfbuzz,
 % method=binary,
% method=internal,
   script=arab,language=dflt,
%    ccmp=yes,
%    init=yes,medi=yes,fina=yes,isol=yes,
%    liga=yes,dlig=yes,rlig=yes,clig=yes,calt=yes,
%    mark=yes,mkmk=yes,kern=yes,curs=yes,
   shaper=native]

\definefontfeature
  [arabic-uniscribe]
  [mode=plug,
   features=harfbuzz,
   script=arab,language=dflt,
%    ccmp=yes,
%    init=yes,medi=yes,fina=yes,isol=yes,
%    liga=yes,dlig=yes,rlig=yes,clig=yes,calt=yes,
%    mark=yes,mkmk=yes,kern=yes,curs=yes,
   shaper=uniscribe]

\starttexdefinition RunLatinTest #1#2#3#4#5
    \start
        \dontcomplain
        \definefontfeature[test][test-#4]
        \writestatus{warning}{#1 #3 #4 (1 initial run)}
        \page
        \startluacode
            collectgarbage("collect")
        \stopluacode
        \title{#1 #3 #4}
        \start
            \getbuffer[#5-definitions]
            \showfontkerns
            \showmakeup[discretionary]
          % \enabletrackers[fonts.plugins.hb.colors]%
            \testfeatureonce{1}{
                \getbuffer[#5-text]
            }
        \stop
        \page
        \startluacode
            collectgarbage("collect")
        \stopluacode
        \ifnum#2>1\relax
            \writestatus{warning}{#1 #3 #4 (#2 timing runs)}
            \start
                \getbuffer[#5-definitions]
                \testfeatureonce{#2}{
                    \setbox\scratchbox\hbox{\getbuffer[#5-text]}
                }
            \stop
            \writestatus{warning}{done}
        \fi
        \startluacode
            document.collected_timings.timings["#5"].results["#1"]["#3 #4"] = \elapsedtime\space
            collectgarbage("collect")
        \stopluacode
    \stop
\stoptexdefinition

\starttexdefinition RunArabicTest #1#2#3#4#5
    \start
        \dontcomplain
        \definefontsynonym[Arabic][#1]
        \definefontfeature[test][arabic-#4]
        \writestatus{warning}{#1 #3 #4 #5 (1 initial run)}
        \page
        \startluacode
            collectgarbage("collect")
        \stopluacode
        \title{#1 #3 #4}
        \start
            \getbuffer[#5-definitions]
          % \enabletrackers[fonts.plugins.hb.colors]%
            \testfeatureonce{1}{
                \setupalign[flushleft] % easier to compare
                \getbuffer[#5-text]
            }
            \par
        \stop
        \page
        \ifnum#2>1\relax
            \writestatus{warning}{#1 #3 #4 #5 (#2 timing runs)}
            \start
                \getbuffer[#5-definitions]
                \testfeatureonce{#2}{
                    \setbox\scratchbox\hbox{\getbuffer[#5-text]}
                }
            \stop
            \writestatus{warning}{done}
        \fi
        \startluacode
            document.collected_timings.timings["#5"].results["#1"]["#3 #4"] = \elapsedtime\space
            collectgarbage("collect")
        \stopluacode
    \stop
\stoptexdefinition

\startluacode
    local processlist = moduledata.plugins.processlist

    local data = {
        timings = { },
        engine  = jit and "luajittex" or "luatex",
    }

    document.collected_timings = data

    -- LATIN

    local list = {
       "modern",
       "pagella",
       "dejavu",
       "cambria",
       "ebgaramond",
       "lucidaot"
    }

    data.timings["latin"] = {
        list    = list,
        results = table.setmetatableindex("table"),
    }

    for i=1,#list do

        local name = list[i]

        context.setupbodyfont { name }
        context.RunLatinTest (name, 100, "context",  "none",      "latin")
        context.RunLatinTest (name, 100, "context",  "base",      "latin")
        context.RunLatinTest (name, 100, "context",  "node",      "latin")
        context.RunLatinTest (name, 100, "harfbuzz", "native",    "latin")
        context.RunLatinTest (name, 100, "harfbuzz", "uniscribe", "latin")
     -- context.RunLatinTest (name,   1, "context",  "text",      "latin")
     -- context.RunLatinTest (name,   1, "harfbuzz", "binary",    "latin")

    end

    context(function()
        context.page()
        context.title((jit and "luajittex" or "luatex") .. " latin")
        processlist(data.timings["latin"])
        context.page()
    end)

    -- ARABIC

    local list = {
        "arabtype",
        "husayni",
    }

    data.timings["arabic"] = {
        list    = list,
        results = table.setmetatableindex("table")
    }

    for i=1,#list do

        local name = list[i]

        context.setupbodyfont { name }
        context.RunArabicTest (name, 100, "context",  "none",      "arabic")
        context.RunArabicTest (name, 100, "context",  "base",      "arabic")
        context.RunArabicTest (name, 100, "context",  "node",      "arabic")
        context.RunArabicTest (name, 100, "harfbuzz", "native",    "arabic")
        context.RunArabicTest (name, 100, "harfbuzz", "uniscribe", "arabic")
     -- context.RunArabicTest (name,   1, "context",  "text",      "arabic")
     -- context.RunArabicTest (name,   1, "harfbuzz", "binary",    "arabic")

    end

    context(function()
        context.page()
        context.title((jit and "luajittex" or "luatex") .. " arabic")
        processlist(data.timings["arabic"])
        context.page()
    end)

    -- MIXED

    local list = {
        "arabtype",
        "husayni"
    }

    data.timings["mixed"] = {
        list    = list,
        results = table.setmetatableindex("table")
    }

    for i=1,#list do

        local name = list[i]

        context.setupbodyfont { name }
        context.RunArabicTest (name, 100, "context",  "none",      "mixed")
        context.RunArabicTest (name, 100, "context",  "base",      "mixed")
        context.RunArabicTest (name, 100, "context",  "node",      "mixed")
        context.RunArabicTest (name, 100, "harfbuzz", "native",    "mixed")
        context.RunArabicTest (name, 100, "harfbuzz", "uniscribe", "mixed")
     -- context.RunArabicTest (name,   1, "context",  "text",      "mixed")
     -- context.RunArabicTest (name,   1, "harfbuzz", "binary",    "mixed")

    end

    context(function()
        context.page()
        context.title((jit and "luajittex" or "luatex") .. " mixed")
        processlist(data.timings["mixed"])
        context.page()
    end)

    context(function()
        table.save("m-fonts-plugins-timings-" .. (jit and "luajittex" or "luatex") .. ".lua",data)
    end)

\stopluacode

\stoptext