lualatex / luamplib

generic TeX package - including MetaPost code in LuaTeX documents
http://ctan.org/pkg/luamplib
16 stars 11 forks source link

Suggestion includemplibcode macro #82

Closed sjnam closed 5 years ago

sjnam commented 5 years ago

Sometimes, the metapost code in the mplibcode environment is quite long. In such a case, it would be convenient to have a macro like "\ includemplibcode". Its usage is identical to the "\includegraphics" of the graphics package. E.g,

\includemplibcode[width=2cm]{test.mp}

Furthermore, the luamplib package only runs in luatex, and lua has a "socket.http" module, the following commands are also possible:

\includemplibcode[width=3cm]{http://example.com/foo/bar.mp}

Thank you.

dohyunkim commented 5 years ago

As for the first part of the issue, the following code seems to give the wished result, currently the only option being "width". Implementing "height" option would not be difficult.

\documentclass{article}
\usepackage{luamplib}
\protected\def\includemplibcode#1#{%
  \begingroup
  \mplibverbatim{disable}\everymplib{}\everyendmplib{}%
  \ifx\empty#1\empty
    \dimen0=0pt
  \else
    \dimen0=\includemplibcodewidth #1%
  \fi
  \doincludemplibcode }
\def\includemplibcodewidth[#1width#2=#3]{#3}
\def\doincludemplibcode#1{%
  \begin{mplibcode}
    beginfig(0);
    input #1;
    \ifdim\dimen0 > 0pt
      currentpicture := currentpicture scaled
      (\mpdim{\dimen0}/(xpart lrcorner currentpicture - xpart llcorner currentpicture));
    \fi
    endfig;
  \end{mplibcode}
  \endgroup }
\begin{document}

\includemplibcode[width=3cm]{test.mp}

\end{document}

How do you think about including code like the above into luamplib.sty? Will it be better to let users do their own job? Or, will it be better for luamplib to provide the code?

dohyunkim commented 5 years ago

Or we can use graphicx package:

\documentclass{article}
\usepackage{luamplib,graphicx}
\protected\def\includemplibcode#1#{%
  \begingroup
  \ifx\empty#1\empty
    \def\option{[width=!,height=!,]}%
  \else
    \edef\option{\includemplibcodeoption#1}%
  \fi
  \edef\width{\expandafter\includemplibcodewidth\option}%
  \edef\height{\expandafter\includemplibcodeheight\option}%
  \doincludemplibcode }
\def\includemplibcodeoption[#1]{[#1,width=!,height=!,]}
\def\includemplibcodewidth [#1width#2=#3,#4]{#3}
\def\includemplibcodeheight[#1height#2=#3,#4]{#3}
\def\doincludemplibcode#1{%
  \edef\x{{\width}{\height}}%
  \expandafter\resizebox\x{\begin{mplibcode} input #1; \end{mplibcode}}%
  \endgroup }
\begin{document}
\includemplibcode[width=2cm]{test.mp}
\end{document}
sjnam commented 5 years ago

The second alternative looks better to me. I thought this would only be possible with the lua code, which is surprising to be solved by only TeX code. Thank you.

dohyunkim commented 5 years ago

as for second part, I just pushed an update. The diff of luamplib.lua is:

--- /usr/local/texlive/2019/texmf-dist/tex/luatex/luamplib/luamplib.lua 2019-03-27 06:01:21.000000000 +0900
+++ src/upload-texlive/luamplib/luamplib.lua    2019-04-01 22:06:24.000000000 +0900
@@ -218,12 +218,29 @@
   enc = "enc files",
 }

+local http, ltn12
+
 local function finder(name, mode, ftype)
   if mode == "w" then
     return name
   else
     ftype = special_ftype[ftype] or ftype
-    local file = mpkpse:find_file(name,ftype)
+    local file
+    if name:find("^http://") then
+      if not http then
+        http  = require"socket.http"
+        ltn12 = require"ltn12"
+      end
+      file = (luamplib.cachedir or outputdir).."/"..name:gsub("%W","_")
+      local fh = ioopen(file,"wb")
+      local r, e = http.request{
+        url  = name,
+        sink = ltn12.sink.file(fh),
+      }
+      if not r then err(format("`%s': %s",name,e)) end
+    else
+      file = mpkpse:find_file(name,ftype)
+    end
     if file then
       if not lfstouch or ftype ~= "mp" or noneedtoreplace[name] then
         return file

Currently, every time luatex is executed, the URL is fetched and saved as a local file. I have no idea how to know in advance whether the URL target has changed or not without fetching the body of URL. Any suggestion?

sjnam commented 5 years ago

HTTP has several request methods. GET, POST, HEAD, PUT, and so on. Among them, the HEAD method fetches only the http response headers not body. For example,

-- load the http module
http = require("socket.http")

-- Requests information about a document, without downloading it.
-- Useful, for example, if you want to display a download gauge and need
-- to know the size of the document in advance
r, c, h = http.request {
  method = "HEAD",
  url = "http://example.com"
}
-- r is 1, c is 200, and h would return the following headers:
-- h = {
--   date = "Mon, 01 Apr 2019 17:30:19 GMT",
--   server = "ECS (sjc/4E3E)",
--   ["last-modified"] = "Fri, 09 Aug 2013 23:54:35 GMT",
--   ["content-length"] = 1270,
--   ["connection"] = "close",
--   ["content-Type"] = "text/html; charset=UTF-8"
-- }

You only need to check the value of c, which is the http status code. As you know, 200 means "OK", 404 measn "NOT FOUND".

dohyunkim commented 5 years ago

Thanks a lot. But as you may agree, this feature seems to bring us more headache than useful. I will revert the last push.

Closing the issue.