From e2c54f4d84587ad40d86211511eaf0780f1c5b14 Mon Sep 17 00:00:00 2001 From: Francois Pelletier Date: Sat, 11 Feb 2023 22:04:35 -0500 Subject: [PATCH] =?UTF-8?q?ajout=20du=20filtre=20pour=20les=20emojis=20et?= =?UTF-8?q?=20changement=20des=20marges=20par=20d=C3=A9faut?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- latex-emoji.lua | 248 ++++++++++++++++++++++++++++++ main.py | 17 +- styles/lcm/format_parameters.json | 16 +- test_main.http | 23 ++- 4 files changed, 282 insertions(+), 22 deletions(-) create mode 100644 latex-emoji.lua diff --git a/latex-emoji.lua b/latex-emoji.lua new file mode 100644 index 0000000..7db9f32 --- /dev/null +++ b/latex-emoji.lua @@ -0,0 +1,248 @@ +-- latex-emoji.lua +-- +-- @copyright 2020 Takayuki YATO (aka. "ZR") +-- GitHub: https://github.com/zr-tex8r +-- Twitter: @zr_tex8r +-- This program is distributed under the MIT License. +-- +local filter_name = 'latex-emoji' +---------------------------------------- helpers + +--- Show debug log? +local show_log = true +--- The default emoji font +local default_emojifont = 'TwemojiMozilla.ttf' + +--- Use bxcoloremoji package? +local bxcoloremoji = false +--- The emoji font to use +local emojifont, emojifontoptions = nil +--- All used codepoints +local ucs_used = {} +--- The number of emoji text spans. +local text_count = 0 + +local utils = require 'pandoc.utils' +local concat, insert, pack, unpack = + table.concat, table.insert, table.pack, table.unpack + +--- Shows a debug log. +local function log(fmt, ...) + if not show_log then return end + io.stderr:write(filter_name..": "..fmt:format(...).."\n") +end + +--- Aborts with an error message. +local function abort(fmt, ...) + error(filter_name..": "..fmt:format(...)) +end + +--- Returns the Pandoc-or-ordinary type of v. +-- @return A string that says type name. +local function pantype(v) + local t = type(v) + return (t == 'table') and v.t or t +end + +--- Makes a comma-separated value string. +-- @return A string. +local function clist(...) + local t, u = pack(...), {} + for i = 1, t.n do + local v = (t[i] == nil) and '' or tostring(t[i]) + if v ~= '' then insert(u, v) end + end + return concat(u, ',') +end + +--- Makes the sorted sequence of all keys of a given table. +-- @return A sequence of strings. +local function keys(t) + local u = {} + for k in pairs(t) do insert(u, k) end + table.sort(u) + return u +end + +--- Converts a singleton sequence to its element. +-- @return The sole element of v if v is a singleton; +-- v if v is not a table; otherwise an error is issued. +local function tosingle(v, l) + if type(v) ~= 'table' then return v end + if #v == 1 then return tosingle(v[1], l) end + abort("multiple values given: %s", l) +end + +--- Converts a value to a singleton sequence. +-- @return The empty table if v is nil; v if v is a table; +-- otherwise the singleton of v. +local function toseq(v) + if v == nil then return {} + elseif type(v) == 'table' then return v + else return {v} + end +end + +--- Converts MetaInlines values inside a MetaValue to strings. +-- @return The converted value. (v is not modified.) +local function tostring_meta(v, l) + if type(v) ~= 'table' then return v end + if v.t == 'MetaList' or v.t == nil then + local r = {} + for k, e in pairs(v) do r[k] = tostring_meta(e, l) end + return r + elseif v.t == 'MetaInlines' then + return utils.stringify(v) + else abort("cannot stringify: %s", v.t, l) + end +end + +--- Gets the source to go into the header. +-- @return LaTeX source string +local function get_header() + if not bxcoloremoji or not next(ucs_used) then + return nil + end + return ([[ +\usepackage[%s]{bxcoloremoji} +\newcommand*{\panEmoji}{\coloremoji} +]]):format(clist(emojifont, unpack(emojifontoptions))) +end + +--- Gets the source to go into the head of body. +-- @return LaTeX source string +local function get_prologue() + if bxcoloremoji or not next(ucs_used) then + return nil + end + local fname = emojifont or default_emojifont + local fopts = clist('Renderer=HarfBuzz', unpack(emojifontoptions)); + local ucs = keys(ucs_used) + for i = 1, #ucs do + ucs[i] = ('"%X'):format(ucs[i]) + end + local dcrsrc = concat(ucs, ',\n') + return ([[ +\makeatletter +\ifnum0\ifdefined\directlua\directlua{ + if ("\luaescapestring{\luatexbanner}"):match("LuaHBTeX") then tex.write("1") end + }\fi>\z@ %% LuaHBTeX is ok + \setfontface\p@emoji@font{%s}[%s] +\else + \@latex@error{You must install a new TeX system (TeX Live 2020)\MessageBreak + and then use 'lualatex' engine to print emoji} + {The compilation will be aborted.} + \let\p@emoji@font\relax +\fi +\ifdefined\ltjdefcharrange +\ltjdefcharrange{208}{ +%s} +\ltjsetparameter{jacharrange={-208}} +\fi +\newcommand*{\panEmoji}[1]{{\p@emoji@font#1}} +\makeatother +]]):format(fname, fopts, dcrsrc) +end + +--- For debug. +local function inspect(v) + local t = type(v) + if t == 'userdata' or t == 'function' or t == 'nil' then return t + elseif t == 'table' then + local u, tag = {}, (v.t or 'table') + if tag == 'Str' then return tag..'{'..v.text..'}' end + for i = 1, #v do u[i] = inspect(v[i]) end + return tag..'{'..concat(u, ';')..'}' + else return tostring(v) + end +end + +---------------------------------------- phase 'readmeta' + +--- For Meta elements. +local function readmeta_Meta (meta) + -- bxcoloremoji + if meta.bxcoloremoji == nil then + bxcoloremoji = false + elseif type(meta.bxcoloremoji) == 'boolean' then + bxcoloremoji = meta.bxcoloremoji + else + abort("not a boolean value: bxcoloremoji") + end + log('bxcoloremoji = %s', bxcoloremoji) + -- emojifont + emojifont = tostring_meta(meta.emojifont, "emojifont") + emojifont = tosingle(emojifont, "emojifont") + log('emojifont = %s', emojifont) + -- emojifontoptions + emojifontoptions = tostring_meta(meta.emojifontoptions, "emojifontoptions") + emojifontoptions = toseq(emojifontoptions) + for i in ipairs(emojifontoptions) do + emojifontoptions[i] = tosingle(emojifontoptions[i], "emojifontoptions element") + log('emojifontoptions = %s', emojifontoptions[i]) + end +end + +---------------------------------------- phase 'mainproc' + +--- For Span element. +local function mainproc_Span(span) + if span.classes:includes('emoji', 1) then + text_count = text_count + 1 + local str = utils.stringify(span.content) + for p, uc in utf8.codes(str) do + if not ucs_used[uc] and uc >= 0x100 then + log("emoji character: U+%04X", uc) + ucs_used[uc] = true + end + end + insert(span.content, 1, pandoc.RawInline('latex', [[\panEmoji{]])) + insert(span.content, pandoc.RawInline('latex', [[}]])) + return span.content + end +end + +--- For Meta elements. +local function mainproc_Meta(meta) + local src = get_header() + if src then + local headers = meta['header-includes'] + if headers == nil then + headers = pandoc.MetaList({}) + elseif pantype(headers) == 'MetaList' then + abort("unexpected metavalue type: header-includes") + end + insert(headers, pandoc.MetaBlocks{pandoc.RawBlock('latex', src)}) + meta['header-includes'] = headers + log("header successfully appended") + return meta + end +end + +--- For the whole document. +local function mainproc_Pandoc(doc) + log("number of emoji spans: %s", text_count) + local src = get_prologue() + if src then + insert(doc.blocks, 1, pandoc.RawBlock('latex', src)) + log("prologue successfully inserted") + return doc + end +end + +---------------------------------------- the filter +if FORMAT == 'latex' then + return { + {-- phase 'readmeta' + Meta = readmeta_Meta; + }; + {-- phase 'mainproc' + Span = mainproc_Span; + Meta = mainproc_Meta; + Pandoc = mainproc_Pandoc; + }; + } +else + log("format '%s' in not supported", FORMAT) +end +---------------------------------------- done \ No newline at end of file diff --git a/main.py b/main.py index da3fb6e..4a9fd2a 100644 --- a/main.py +++ b/main.py @@ -122,7 +122,7 @@ async def generer(specs: DocumentSpecs): datef = datetime.datetime.now().strftime("%m-%d-%Y") os.makedirs("out", exist_ok=True) output_file = f"./out/{specs.style}-{specs.format}-{datef}-output.pdf" - filters = [] + filters = ['latex-emoji.lua'] pdoc_args = [ f'--include-in-header={header_file}', f'--include-after-body={cover_file}', @@ -144,7 +144,7 @@ async def generer(specs: DocumentSpecs): result = pypandoc.convert_text(source=specs.content, to='pdf', - format='markdown+implicit_figures+smart', + format='markdown+implicit_figures+smart+emoji', encoding='utf-8', extra_args=pdoc_args, filters=filters, @@ -157,13 +157,18 @@ async def generer(specs: DocumentSpecs): except OSError as oerr: logging.exception(oerr) if specs.extension in ["png", "jpg"]: - zip_filename = os.path.splitext(os.path.basename(output_file))[0] - png_output_dir = "./png_output" + zip_filename = os.path.join("out", os.path.splitext(os.path.basename(output_file))[0]) + png_output_dir = "./out/png_output" if not os.path.exists(png_output_dir): os.mkdir(png_output_dir) try: - convert_pdf(output_file, specs.extension, png_output_dir, resolution=300) - shutil.make_archive(zip_filename, 'zip', png_output_dir) + convert_pdf(output_file, + specs.extension, + png_output_dir, + resolution=300) + shutil.make_archive(base_name=zip_filename, + format='zip', + root_dir=png_output_dir) shutil.rmtree(png_output_dir) except Exception as e: logging.exception(e) diff --git a/styles/lcm/format_parameters.json b/styles/lcm/format_parameters.json index 362e38c..41acc47 100644 --- a/styles/lcm/format_parameters.json +++ b/styles/lcm/format_parameters.json @@ -8,7 +8,7 @@ "paperheight": 3507, "ratio": 100, "margin": 248, - "vmargin": 350, + "vmargin": 525, "extension": "pdf" }, "lettre": { @@ -20,7 +20,7 @@ "paperheight": 3300, "ratio": 100, "margin": 255, - "vmargin": 330, + "vmargin": 495, "extension": "pdf" }, "linkedin": { @@ -31,8 +31,8 @@ "paperwidth": 1200, "paperheight": 1200, "ratio": 100, - "margin": 120, - "vmargin": 120, + "margin": 180, + "vmargin": 180, "extension": "jpg" }, "instagram": { @@ -44,7 +44,7 @@ "paperheight": 1920, "ratio": 100, "margin": 108, - "vmargin": 192, + "vmargin": 244, "extension": "jpg" }, "pinterest": { @@ -56,7 +56,7 @@ "paperheight": 1102, "ratio": 100, "margin": 75, - "vmargin": 110, + "vmargin": 165, "extension": "jpg" }, "slide43": { @@ -68,7 +68,7 @@ "paperheight": 1920, "ratio": 100, "margin": 256, - "vmargin": 192, + "vmargin": 288, "extension": "pdf" }, "slide169": { @@ -80,7 +80,7 @@ "paperheight": 1440, "ratio": 100, "margin": 256, - "vmargin": 144, + "vmargin": 216, "extension": "pdf" } } diff --git a/test_main.http b/test_main.http index 2d397aa..9994685 100644 --- a/test_main.http +++ b/test_main.http @@ -10,11 +10,13 @@ Accept: application/zip "linkcolor": "blue", "tocdepth": 2, "pdfengine": "lualatex", - "content": "# Ceci est un titre\n## Ceci est un sous-titre\n\nCeci est un paragraphe\n\n## Ceci est un autre sous-titre\n\n> Ceci est du code", + "content": "# Ceci est un titre\n## Ceci est un sous-titre\n\nCeci est un paragraphe\n\n## Ceci est un autre sous-titre\n\n> Ceci est du code\n\nCeci est un emoji :heart_eyes:", "fontsize": 14, "paperwidth": 1080, "paperheight": 1920, + "ratio": 100, "margin": 180, + "vmargin": 180, "extension": "jpg" } @@ -30,11 +32,13 @@ Accept: application/pdf "linkcolor": "blue", "tocdepth": 2, "pdfengine": "lualatex", - "content": "# Ceci est un titre\n## Ceci est un sous-titre\n\nCeci est un paragraphe\n\n## Ceci est un autre sous-titre\n\n> Ceci est du code", + "content": "# Ceci est un titre\n## Ceci est un sous-titre\n\nCeci est un paragraphe\n\n## Ceci est un autre sous-titre\n\n> Ceci est du code\n\nCeci est un emoji :heart_eyes:", "fontsize": 14, "paperwidth": 1080, "paperheight": 1080, + "ratio": 100, "margin": 90, + "vmargin": 90, "extension": "pdf" } @@ -50,10 +54,11 @@ Accept: application/pdf "linkcolor": "blue", "tocdepth": 2, "pdfengine": "lualatex", - "content": "# Comment améliorer ma sécurité informatique\n\n20 défis gratuits de 2 minutes chacun !", + "content": "# Ceci est un titre\n## Ceci est un sous-titre\n\nCeci est un paragraphe\n\n## Ceci est un autre sous-titre\n\n> Ceci est du code\n\nCeci est un emoji :heart_eyes:", "fontsize": 20, "paperwidth": 1080, "paperheight": 1080, + "ratio": 100, "margin": 180, "extension": "jpg", "vmargin": 300 @@ -67,15 +72,17 @@ Accept: application/pdf { "format": "slide169", - "style": "jevalideca", + "style": "lcm", "linkcolor": "blue", "tocdepth": 2, "pdfengine": "lualatex", - "content": "# Ceci est un titre\n## Ceci est un sous-titre\n\nCeci est un paragraphe\n\n## Ceci est un autre sous-titre\n\n> Ceci est du code", + "content": "# Ceci est un titre\n## Ceci est un sous-titre\n\nCeci est un paragraphe\n\n## Ceci est un autre sous-titre\n\n> Ceci est du code\n\nCeci est un emoji :heart_eyes:", "fontsize": 14, - "paperwidth": 1920, - "paperheight": 1080, - "margin": 90, + "paperwidth": 2560, + "paperheight": 1440, + "ratio": 100, + "margin": 256, + "vmargin": 216, "extension": "jpg" }