MPlib: Developments
CC Attribution 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal purpose as long as the work is attributed to the author in the manner specified by the author or licensor.
Hoekwater, Taco
The first stage of the MPlib project has resulted in a library that can be used in for instance LuaTeX, and is also the core of the MetaPost program itself. This talk will present the current state of affairs, the conversion process (from pascal to c), and the interface. A roadmap towards MegaPost will be presented too.
 I was going to do the lower tech talk earlier, but it turned out to be easier for Hans and me because we need a meeting here and now we have some extra time to prepare the talks on Thursday. And MPLibs was somewhat easier because it is done already. I prepared this talk for the NTG men meeting last month. It shows immediately because it was prepared for a much smaller screen. You may want to move to the back of the room. The last couple of years I have given a talk that went like metapost developments and what have we done over the past year. All of these talks have been slow progressions of John Hobie's metapost. It has been somewhat unpleasant to work with the Pascal code when you actually try to change or extend something. So, two years ago we thought it would be nice if we could modernize the whole system a little bit at least. Then we got some funding, we developed a project proposal and we came up with a number of goals of things we want to achieve for this basically re-implementation of metapost, which is we want reusable components, something that you can plug in into a different program. We want it to be a re-enter because of that. So, multiple programs can use the same library without having all this code replicated in your system memory. We want all the input and output to be redirectable. Not so much that it's redirected already in the library, that would be a lot of extra work for everybody. If you don't want the library to use the disk, then you can set it up so that it doesn't touch the hard disk ever. We wanted to simplify the generation of external labels to make MPH calls. Some of you may have seen the diagram where I have presented last year, where there are some eight or nine jumps that you have to go from BTECH into a picture, where makeMPX is run by metapost, then MP2 is run by makeMPX, then make it the instance, DVI2MP, then DVI2MP does something, etc. It's all very complicated and it's very hard to explain to users, and it can go wrong in a great many places. So, we want to have an easier thing, especially if you have a library, then you want to ship this library. You don't want to write a 20-page document explaining what all these extra programs do in this executable directory. You may have a collection of these, of like reminding all readers that the library is now closed, the book library is now closed. I'm sure we can get out of here. A little bit of a remnant of the BTECH here is allocation in English, allocation that all these static arrays that are inside of metapost don't work very well if you have a library and somewhere in the middle of the controlling application, certainly the library stops because which story out of memory or an overflow this or that, please recompile metapost. You can't do that, so we want to solve that as well. This overall means that the system should be more flexible than it was so far. I'll try to move a little bit more quickly with all of this talk, it's technical, I'm sorry. It's on the stick because my laptop is notoriously complicated to hook up onto any other beamer, so I send it not to even private, it takes 15 minutes and then it's sort of suboptimal. But it doesn't mean I cannot demonstrate anything, so it's a bit slow now. But metapost is in Pascal web like Techeys, like Metapont is. I'm not going to say it because John Hobbie copied large sections of Metapont the first time he created metapost. But Pascal web is a typical example of an early 1980s construct. It has a huge amount of global variables, all arrays are static. There is a string tool because there is no real string facility in Pascal at the time, so everything is done manually using a separate file and the compilation itself is wildly complicated because these days you don't actually have a Pascal compiler to compile Pascal web, but we use a C compiler to compile Pascal web, which doesn't work. You first have to convert the Pascal to C, so there is a separate program that goes from Pascal to C. And then this program generates C that needs a runtime library. So you have to also link this runtime library with your application. And then some special sub-post processing has to occur to make sure that a couple of Pascal constructs are converted to yet other C constructs. And the whole process takes a long time and involves a dozen programs or something. Why is it? The font inclusion code that I added to Metapost last year is actually in C, but it is borrowed from PDF test, which had borrowed it from a couple of other programs. But it was a bit confusing because it came from three or four separate places. And in itself it was also confusing. And then there is the dependency on the 5C runtime. So I decided to redo the whole thing and all of the Pascal was converted to C web, period. There is no Pascal code in MPlib at all. This replaces both the old Pascal web and the C stuff that I fetched from PDF. So the compilation now uses only C-tangle, followed by the C-compiler. The code has been internally restructured quite a lot. And the executable impulse, I guess many people are familiar with, is now just front-end. It's 200 lines of C code that calls a library that is the hard work. And then the MPX is integrated, which is now also written in C web. And Out of Conf is used for the configuration. Then there's an instant structure so that you can say to the library, give me a new Metapost. The string pool is revised. It's also been strung in. I did not quite correct in these phrases. But it used to be that error messages, status reports, so we're all in the string pool file. This is no longer true. The only things that are still inside a string pool are the user-generated strings, like macro names. And string commands, et cetera. Everything else is just inlined in the C code. It's normal, C string, no-terminated. And the Post-it backend has been isolated, separated from the core that is the type setting. And then the core exports a bunch of objects. And these objects are then converted to Pascal. And that's what it is, something completely different, if you feel like it. All the C functions start with mp underscore, so that there cannot be any conflicts, hopefully with other C code. And we have dynamic memory allocation almost everywhere. There are a few exceptions. I'll get back to them later. All I.O. uses function pointers, so that a function is called to do the actual I.O. Overrule the function, if you feel you have better writing. How would you do that? I'll show you. You start an option structure, then you create an instance with these options. You run some MetaPast code, either a file or a string buffer, and then you finish up. And then you run a couple, you see? So here you, this is the code that creates the option structure. Then one of the options is command line, so you set the option command line to, in this case, the first C argument, which usually will be a file in this case. And you run initialize, this creates an mp for you. This is the actual mp instance. If you have one, you can do mp run, and the library sees that there is a file on the command line. It will read the command line. It will read the file, it will process the MetaPast that's in the file. It will probably end with an end command to the file. And then the library will be done, and then you call mp finish to free all the memory that's there, open files, etc., just in case there is no end in the file. If the file just ends with eof, it's not necessarily clear that this will be the end. So you call mp finish, and then really, really sort of, nothing of the library is still in the memory. The rest is just a bit of a, okay, there could be, you could do more error checking, of course. There can be other options, there's about two dozen options. And about six or seven of them are the fields for functions to overrule the internal IO functions. So you put it on the previous slide, right, yet, there's not just a sub-possess. Now, it's the, we're here. This is the form of file. This is mp run. Sorry. Here. But normally when you create a metapost input file, the last command in the file tends to be end. Right. But not always. It could even be invley. You could do a begin-vig and never have an end-vig. If that happens, then the library is not finished, but it only reaches the next C line. That's why you do mp finish. So it can guaranteed clean up whatever is still in the memory. Okay. And this is the C example. The distribution also contains Lua bindings, which is also the bindings that are used inside Lua. You can use them in pure Lua if you want. You don't need Lua text. You can just build the library's interface to Lua and use it with a stock Lua interpreter. And that would look something like this. This loads the mp.asl library. This replaces mp, the combination of mp initialize and mpoption. And it just says, okay, we're not in the metapost. And the memory we want is plain. So that would be plain.not-mem. I'm going to check it. There could be a problem here, of course. The memory could be not found or something. Or the memory could be exhausted. So you have to do a test to make sure that something valid came. And then you can, in this case, run a buffer instead of a file. And a command for buffer is execute. So you do this is a string in Lua, multi-line. And it just doesn't need to be used. This is a string. This is a string. This is a multi-line Lua string. Two square brackets, multi-line stuff, two water square brackets to end it up. Then this will, in the lower bindings, this will return an L, which is an mplib result structure. It has a number of fields, but the most important one is an array fig. If you have generated a figure, then L, that fig, is an array of figures. You can have begin fig 1, blah, blah, blah, begin fig 2, blah, blah, blah. In that case, there would be an array of two figures here, instead of just one. Then the figures themselves are objects, and you can just ask the post-cris representation of the objects. That's what I said earlier, that the entire back end is isolated from the core engine. So at this point, all that fig is that exported internal graphite, and the frugin-call basket runs the traditional basket back end. All the values within this possible array? Yes. Q. This is a bit of a confusion of the memory between keys and arrays. I think Ruby is nice like a key to an array, but nothing is literally up to say. Is that going to hash in the map? Q. I'm not sure we need to understand whether it's or not standard code. It does help. Q. It's a lot like Paul, basically everything is a hash array. So there's a hash array L, which has a number of fields, one of them is fig. The field fig itself is an array, so you end up with L.fig1. The objects in this array are actual objects, and then you use colonization to get a method on the object. It looks like Ruby. It looks a lot like Ruby, but more the high level syntax. Just a little bit to say about MakeMPX. Then MakeMPX, MP2, DVH1P, and the trough version of DVH1P called DOOM, are all rolled into a sub-ribery. The sub-ribery is actually called MPXOUT.W, and it is linked inside the MPOST executable. It means in practice that if you don't set the MPX command, that MakeMPX will no longer run an external MPX program. The only thing it will run is the tech type setter or the trough type setter to generate the actual pictures, and everything else is handled internally. Well, that's the state so far. The first battery release was planned for our 2008. I uploaded it on Friday. Then I uploaded a second one on Saturday, and a third one early on Sunday morning at around 2 o'clock. So we're now actually in the third battery release. It has the library source, it has the front-end, it has the lower bindings, and it has a documentation file explaining the API in much more detail than in the slides here. It's about 25 pages. When we're confident that most of the books have been removed, and probably maybe a more for 2, 3, this will actually become the next official release of Metapost, and Pascal Metapost will be totally retired. What's sometime in October or something? I'll start working on everything else that I wanted to do and couldn't do because of Pascal, and that I cannot finally get around to. Here's an inner-memory allocation for remaining items, the tricky ones. The extension of the range for numeric values, the extension on the precision, hopefully some more mathematical core functions you can use, and some configuration of the error strategies, some of the errors that you get a report for. You would love to say, I know this is a redundant equation, don't pester me about it, I'll fix it later. I'm trying to find a way that a user can say, I know what I'm doing, or maybe I don't know what I'm doing, but at least don't ask me any questions because you should just run and shut up. And then now that the messages internally are certainly C-streamed, it's also not possible to change the language of these things. When there was an external pool file, you could feasibly convert the pool file into a different language, and it no longer works, so I will add getX support where you can create PL files for Metapost. And then the RP itself is the last item, the RP itself is pretty high level now. You run code, you get images, period. There's no way to say, okay, I have this equation, what is the answer of the equation? And it would be useful if you could get this information, at least on the C level, and use other parts of MPlib for other practical uses in different controlling programs. Then finally there, funded by the user groups. That's all of you basically. Thank you very much. And then here's where you can get yourself. This should be the final slide now. Oh, okay. This is the background. Actually, every page is a different background. They're all generated on the file by MPlib as being run inside water. There's no disk file involved. How would MPlib have an impact on applications that use Metapost such as MetaType 1? You could. It's not under my control, but you could. And it would make it less confusing, I hope, but it has all these gorg-specs and shell-specs and things. Yes, that's one of the planned usages, basically. Could you say something about backwards compatibility? What? What do you want to know about backwards compatibility? Same input, same output. I want to know what everybody wants to know. Will my old documents continue to work the same as before? Yes. Same input, same output. Thank you. Except for some 8-bit switch on. Sorry? 8-bit switch on the command line is always on. So you always get 8-bit input. So if you had previously created a mess of history document just to get errors, this will not be backward compatible anymore. That's about the limit of... One more. You mentioned that the first reference is backwards. Do you have any other slides in mind? Actually, I have on my hard disk already a pure Lua script called mplib to PDF. And it uses the Lua bindings to generate PDF directly. That's one thing I add in. It's pretty easy to do. With the Lua bindings you can basically generate whatever output you want. You could output SVG just as easily. I just happen to know how to generate PDF. But it took me maybe two days to write a thing and it would suggest that SVG would probably be about the same. So, yes. Thank you very much.
 I was going to do the lower tech talk earlier, but it turned out to be easier for Hans and me  because we need a meeting here and now we have some extra time to prepare the talks on Thursday.  And MPLibs was somewhat easier because it is done already.  I prepared this talk for the NTG men meeting last month.  It shows immediately because it was prepared for a much smaller screen.  You may want to move to the back of the room.  The last couple of years I have given a talk that went like metapost developments and what have we done over the past year.  All of these talks have been slow progressions of John Hobie's metapost.  It has been somewhat unpleasant to work with the Pascal code when you actually try to change or extend something.  So, two years ago we thought it would be nice if we could modernize the whole system a little bit at least.  Then we got some funding, we developed a project proposal and we came up with a number of goals of things we want to achieve  for this basically re-implementation of metapost, which is we want reusable components,  something that you can plug in into a different program.  We want it to be a re-enter because of that.  So, multiple programs can use the same library without having all this code replicated in your system memory.  We want all the input and output to be redirectable.  Not so much that it's redirected already in the library, that would be a lot of extra work for everybody.  If you don't want the library to use the disk, then you can set it up so that it doesn't touch the hard disk ever.  We wanted to simplify the generation of external labels to make MPH calls.  Some of you may have seen the diagram where I have presented last year,  where there are some eight or nine jumps that you have to go from BTECH into a picture,  where makeMPX is run by metapost, then MP2 is run by makeMPX,  then make it the instance, DVI2MP, then DVI2MP does something, etc.  It's all very complicated and it's very hard to explain to users, and it can go wrong in a great many places.  So, we want to have an easier thing, especially if you have a library, then you want to ship this library.  You don't want to write a 20-page document explaining what all these extra programs do in this executable directory.  You may have a collection of these, of like reminding all readers that the library is now closed, the book library is now closed.  I'm sure we can get out of here.  A little bit of a remnant of the BTECH here is allocation in English, allocation that all these static arrays that are inside of metapost  don't work very well if you have a library and somewhere in the middle of the controlling application,  certainly the library stops because which story out of memory or an overflow this or that, please recompile metapost.  You can't do that, so we want to solve that as well.  This overall means that the system should be more flexible than it was so far.  I'll try to move a little bit more quickly with all of this talk, it's technical, I'm sorry.  It's on the stick because my laptop is notoriously complicated to hook up onto any other beamer,  so I send it not to even private, it takes 15 minutes and then it's sort of suboptimal.  But it doesn't mean I cannot demonstrate anything, so it's a bit slow now.  But metapost is in Pascal web like Techeys, like Metapont is.  I'm not going to say it because John Hobbie copied large sections of Metapont the first time he created metapost.  But Pascal web is a typical example of an early 1980s construct.  It has a huge amount of global variables, all arrays are static.  There is a string tool because there is no real string facility in Pascal at the time,  so everything is done manually using a separate file and the compilation itself is wildly complicated  because these days you don't actually have a Pascal compiler to compile Pascal web,  but we use a C compiler to compile Pascal web, which doesn't work.  You first have to convert the Pascal to C, so there is a separate program that goes from Pascal to C.  And then this program generates C that needs a runtime library.  So you have to also link this runtime library with your application.  And then some special sub-post processing has to occur to make sure that a couple of Pascal constructs are converted to yet other C constructs.  And the whole process takes a long time and involves a dozen programs or something.  Why is it?  The font inclusion code that I added to Metapost last year is actually in C,  but it is borrowed from PDF test, which had borrowed it from a couple of other programs.  But it was a bit confusing because it came from three or four separate places.  And in itself it was also confusing.  And then there is the dependency on the 5C runtime.  So I decided to redo the whole thing and all of the Pascal was converted to C web, period.  There is no Pascal code in MPlib at all.  This replaces both the old Pascal web and the C stuff that I fetched from PDF.  So the compilation now uses only C-tangle, followed by the C-compiler.  The code has been internally restructured quite a lot.  And the executable impulse, I guess many people are familiar with, is now just front-end.  It's 200 lines of C code that calls a library that is the hard work.  And then the MPX is integrated, which is now also written in C web.  And Out of Conf is used for the configuration.  Then there's an instant structure so that you can say to the library,  give me a new Metapost.  The string pool is revised.  It's also been strung in.  I did not quite correct in these phrases.  But it used to be that error messages, status reports,  so we're all in the string pool file.  This is no longer true.  The only things that are still inside a string pool are the user-generated strings, like macro names.  And string commands, et cetera.  Everything else is just inlined in the C code.  It's normal, C string, no-terminated.  And the Post-it backend has been isolated, separated from the core that is the type setting.  And then the core exports a bunch of objects.  And these objects are then converted to Pascal.  And that's what it is, something completely different, if you feel like it.  All the C functions start with mp underscore, so that there cannot be any conflicts,  hopefully with other C code.  And we have dynamic memory allocation almost everywhere.  There are a few exceptions. I'll get back to them later.  All I.O. uses function pointers, so that a function is called to do the actual I.O.  Overrule the function, if you feel you have better writing.  How would you do that?  I'll show you.  You start an option structure, then you create an instance with these options.  You run some MetaPast code, either a file or a string buffer, and then you finish up.  And then you run a couple, you see?  So here you, this is the code that creates the option structure.  Then one of the options is command line, so you set the option command line to, in this case,  the first C argument, which usually will be a file in this case.  And you run initialize, this creates an mp for you.  This is the actual mp instance.  If you have one, you can do mp run, and the library sees that there is a file on the command line.  It will read the command line.  It will read the file, it will process the MetaPast that's in the file.  It will probably end with an end command to the file.  And then the library will be done, and then you call mp finish to free all the memory that's there,  open files, etc., just in case there is no end in the file.  If the file just ends with eof, it's not necessarily clear that this will be the end.  So you call mp finish, and then really, really sort of, nothing of the library is still in the memory.  The rest is just a bit of a, okay, there could be, you could do more error checking, of course.  There can be other options, there's about two dozen options.  And about six or seven of them are the fields for functions to overrule the internal IO functions.  So you put it on the previous slide, right, yet, there's not just a sub-possess.  Now, it's the, we're here. This is the form of file. This is mp run.  Sorry. Here. But normally when you create a metapost input file, the last command in the file tends to be end.  Right. But not always. It could even be invley. You could do a begin-vig and never have an end-vig.  If that happens, then the library is not finished, but it only reaches the next C line.  That's why you do mp finish. So it can guaranteed clean up whatever is still in the memory.  Okay. And this is the C example. The distribution also contains Lua bindings,  which is also the bindings that are used inside Lua. You can use them in pure Lua if you want.  You don't need Lua text. You can just build the library's interface to Lua and use it with a stock Lua interpreter.  And that would look something like this. This loads the mp.asl library.  This replaces mp, the combination of mp initialize and mpoption.  And it just says, okay, we're not in the metapost. And the memory we want is plain.  So that would be plain.not-mem. I'm going to check it.  There could be a problem here, of course. The memory could be not found or something.  Or the memory could be exhausted. So you have to do a test to make sure that something valid came.  And then you can, in this case, run a buffer instead of a file. And a command for buffer is execute.  So you do this is a string in Lua, multi-line. And it just doesn't need to be used.  This is a string.  This is a string. This is a multi-line Lua string.  Two square brackets, multi-line stuff, two water square brackets to end it up.  Then this will, in the lower bindings, this will return an L, which is an mplib result structure.  It has a number of fields, but the most important one is an array fig.  If you have generated a figure, then L, that fig, is an array of figures.  You can have begin fig 1, blah, blah, blah, begin fig 2, blah, blah, blah.  In that case, there would be an array of two figures here, instead of just one.  Then the figures themselves are objects, and you can just ask the post-cris representation of the objects.  That's what I said earlier, that the entire back end is isolated from the core engine.  So at this point, all that fig is that exported internal graphite, and the frugin-call basket runs the traditional basket back end.  All the values within this possible array?  Yes.  Q. This is a bit of a confusion of the memory between keys and arrays.  I think Ruby is nice like a key to an array, but nothing is literally up to say.  Is that going to hash in the map?  Q. I'm not sure we need to understand whether it's or not standard code.  It does help.  Q. It's a lot like Paul, basically everything is a hash array.  So there's a hash array L, which has a number of fields, one of them is fig.  The field fig itself is an array, so you end up with L.fig1.  The objects in this array are actual objects, and then you use colonization to get a method on the object.  It looks like Ruby.  It looks a lot like Ruby, but more the high level syntax.  Just a little bit to say about MakeMPX.  Then MakeMPX, MP2, DVH1P, and the trough version of DVH1P called DOOM, are all rolled into a sub-ribery.  The sub-ribery is actually called MPXOUT.W, and it is linked inside the MPOST executable.  It means in practice that if you don't set the MPX command,  that MakeMPX will no longer run an external MPX program.  The only thing it will run is the tech type setter or the trough type setter to generate the actual pictures,  and everything else is handled internally.  Well, that's the state so far.  The first battery release was planned for our 2008.  I uploaded it on Friday.  Then I uploaded a second one on Saturday, and a third one early on Sunday morning at around 2 o'clock.  So we're now actually in the third battery release.  It has the library source, it has the front-end, it has the lower bindings,  and it has a documentation file explaining the API in much more detail than in the slides here.  It's about 25 pages.  When we're confident that most of the books have been removed,  and probably maybe a more for 2, 3,  this will actually become the next official release of Metapost, and Pascal Metapost will be totally retired.  What's sometime in October or something?  I'll start working on everything else that I wanted to do and couldn't do because of Pascal,  and that I cannot finally get around to.  Here's an inner-memory allocation for remaining items, the tricky ones.  The extension of the range for numeric values, the extension on the precision,  hopefully some more mathematical core functions you can use,  and some configuration of the error strategies, some of the errors that you get a report for.  You would love to say, I know this is a redundant equation, don't pester me about it, I'll fix it later.  I'm trying to find a way that a user can say, I know what I'm doing,  or maybe I don't know what I'm doing, but at least don't ask me any questions because you should just run and shut up.  And then now that the messages internally are certainly C-streamed,  it's also not possible to change the language of these things.  When there was an external pool file, you could feasibly convert the pool file into a different language,  and it no longer works, so I will add getX support where you can create PL files for Metapost.  And then the RP itself is the last item, the RP itself is pretty high level now.  You run code, you get images, period.  There's no way to say, okay, I have this equation, what is the answer of the equation?  And it would be useful if you could get this information, at least on the C level,  and use other parts of MPlib for other practical uses in different controlling programs.  Then finally there, funded by the user groups.  That's all of you basically. Thank you very much.  And then here's where you can get yourself.  This should be the final slide now.  Oh, okay. This is the background.  Actually, every page is a different background.  They're all generated on the file by MPlib as being run inside water.  There's no disk file involved.  How would MPlib have an impact on applications that use Metapost such as MetaType 1?  You could. It's not under my control, but you could.  And it would make it less confusing, I hope, but it has all these gorg-specs and shell-specs and things.  Yes, that's one of the planned usages, basically.  Could you say something about backwards compatibility?  What? What do you want to know about backwards compatibility?  Same input, same output.  I want to know what everybody wants to know.  Will my old documents continue to work the same as before?  Yes. Same input, same output.  Thank you.  Except for some 8-bit switch on.  Sorry?  8-bit switch on the command line is always on.  So you always get 8-bit input.  So if you had previously created a mess of history document just to get errors,  this will not be backward compatible anymore.  That's about the limit of...  One more.  You mentioned that the first reference is backwards. Do you have any other slides in mind?  Actually, I have on my hard disk already a pure Lua script called mplib to PDF.  And it uses the Lua bindings to generate PDF directly.  That's one thing I add in.  It's pretty easy to do. With the Lua bindings you can basically generate whatever output you want.  You could output SVG just as easily.  I just happen to know how to generate PDF.  But it took me maybe two days to write a thing and it would suggest that SVG would probably be about the same.  So, yes.  Thank you very much.
MPlib__The_project__the_library_and_the_future_0_611.png MPlib__The_project__the_library_and_the_future_612_2075.png MPlib__The_project__the_library_and_the_future_2076_2339.png MPlib__The_project__the_library_and_the_future_2340_6455.png MPlib__The_project__the_library_and_the_future_6456_7691.png MPlib__The_project__the_library_and_the_future_7692_10235.png MPlib__The_project__the_library_and_the_future_10236_10511.png MPlib__The_project__the_library_and_the_future_10512_10727.png MPlib__The_project__the_library_and_the_future_10728_10883.png MPlib__The_project__the_library_and_the_future_10884_11651.png MPlib__The_project__the_library_and_the_future_11652_13991.png MPlib__The_project__the_library_and_the_future_13992_14135.png MPlib__The_project__the_library_and_the_future_14136_14291.png MPlib__The_project__the_library_and_the_future_14292_14579.png MPlib__The_project__the_library_and_the_future_14580_17075.png MPlib__The_project__the_library_and_the_future_17076_17507.png MPlib__The_project__the_library_and_the_future_17508_18659.png MPlib__The_project__the_library_and_the_future_18660_24479.png MPlib__The_project__the_library_and_the_future_24480_25619.png MPlib__The_project__the_library_and_the_future_25620_25715.png MPlib__The_project__the_library_and_the_future_25716_26435.png MPlib__The_project__the_library_and_the_future_26436_27335.png MPlib__The_project__the_library_and_the_future_27336_27563.png MPlib__The_project__the_library_and_the_future_27564_27755.png MPlib__The_project__the_library_and_the_future_27756_28151.png MPlib__The_project__the_library_and_the_future_28152_28859.png MPlib__The_project__the_library_and_the_future_28860_30419.png MPlib__The_project__the_library_and_the_future_30420_30707.png MPlib__The_project__the_library_and_the_future_30708_35458.png