字幕列表 影片播放 列印英文字幕 COLTON OGDEN: Hello, world. This is CS50 on Twitch. My name is Colton Ogden. And I'm joined today by-- DAVID MALAN: I'm David Malan. Nice to see everyone again, for real this time. It's not just a pop-in. I'm here the whole time. COLTON OGDEN: The whole time, yeah. This a very special appearance today. What are we talking about today? DAVID MALAN: So we thought we'd introduce render50, which is a command line tool that CS50 uses internally, and some of our high school teachers use as well, to turn source code into PDFs. COLTON OGDEN: And what are some of the ways that you use this yourself? DAVID MALAN: So over the years for CS50, especially for our undergrads and our extension school students here and off campus, we have typically given feedback, of course, on students' code when they submit it for grades and also for qualitative feedback. And years ago, back in my day, so this is like late '90s, the technology we used was paper. And so literally, part of the process of submitting your homework, your CS50 problem sets, was literally print it out, in addition to submitting the files, and we would have used big ASCII. You print out a big sheet of paper and like Malan would be printed in big M's and big A's and big L's all over the paper to make clear to your teaching fellow whose it is. And then he or she would write comments physically on the paper. COLTON OGDEN: Is this also the era of the paper with holes on the sides of it? DAVID MALAN: Yes, dot matrix. Actually, these were laser printers, but I think those were still, for sure, around. So that's obviously behind us now. And so over the years, we transitioned to other techniques where we would somehow generate PDFs out of students' code and then use things like Adobe Acrobat, or Apple Preview, or Bluebeam PDF Revu was another tool we used for some time. So you could actually type the comments. And actually, we went through a phase, thanks to some friends at Microsoft a few years ago, where they kindly let all of our teaching Fellows here on campus use tab-- they weren't called tablets yet-- what were they even called? COLTON OGDEN: The like PalmPilots, like the-- DAVID MALAN: Touchscreens. Well, they were the first laptops with touchscreens. But I don't think they were called tablets yet. I'm totally blanking now on the technology. COLTON OGDEN: I don't remember either. DAVID MALAN: So you could draw on the screen. And this was marginally better, because then you could circle things and be like good, or better, could be this, and so forth. And just handwrite it as well. And then most recently did we just write our own tool for generating the PDFs, because then we can get them just right. We can syntax highlight them. We can do it automatically, programmatically. And so thus was born render50, a tool that just makes it super easy to render PDFs out of code. And then also, unfortunately, to render files side by side in cases of academic dishonesty. If we suspect that a student has unduly copied someone else's work, it's often helpful for folks on campus to go to see their code in the GitHub repo or something else side by side. So we use this same tool to do that. So you can just very easily, especially if you're less technical, look at the code on a PDF or a printout as opposed to like a diff or GitHub or something like that. COLTON OGDEN: Nice. And another use case that, for example, to illustrate, we're using it today here-- DAVID MALAN: Ironically, yes. COLTON OGDEN: --for notes. DAVID MALAN: This is very meta. COLTON OGDEN: You probably won't be able to see it too well in the stream. DAVID MALAN: This is my cheat sheet for today. COLTON OGDEN: I've used it in prior streams as well for code bases where I just want to have something to look at as a reference, or in case I forget how I wrote some function or something. And you use it for lecture notes too. DAVID MALAN: All the time. Like literally every lecture and any stream where I need some code to reference, just really as a cheat sheet to remind myself so I'm not futzing with the computer too much. I just run code through render50, get a nice pretty printed, syntax highlighted, colorful PDF. And then we just send it to a color printer. And then do it old school like this. COLTON OGDEN: Is this related at all to the old Annotate 50 that we were working on back in the day? DAVID MALAN: This is like a simplistic version of that where you rely on actual human hands for the annotations or existing PDF tools. We did have a tool in the past that was more digital, but we've since deprecated that. COLTON OGDEN: OK, cool. Let's make sure that we're keeping up with the chat. We have a lot of new names today that I saw. DAVID MALAN: Sure, let's say hello. [INTERPOSING VOICES] COLTON OGDEN: So BELLA [INAUDIBLE] is a regular. Hello, Bella, good to have you. DROP4, VDHUG, again. HASSAN-- the Twitch font on this sometimes is absurdly difficult to read against the-- DAVID MALAN: It's very bright green. COLTON OGDEN: I have it on light mode for some reason, but HASSAN [INAUDIBLE] says hello. DAVID MALAN: Hello. COLTON OGDEN: Good to have you, Hassan. I think that's the first time I've seen your name in the chat. So Brenda, shout out to Brenda. DAVID MALAN: Brenda, nice to see you. Brenda tuning in, of course, from New Zealand, one of our furthest away team members. COLTON OGDEN: Good to have you, Brenda. SHAMXR says good evening. MATTHEWTHEGOODMAN. DAVID MALAN: Montreal, nice. COLTON OGDEN: In from Montreal. So very good global representation. DAVID MALAN: And Pakistan, the other direction. Wow. COLTON OGDEN: Yeah. DAVID MALAN: Good night, nice to see you. Thanks for tuning in before bed. COLTON OGDEN: Yeah, awesome. Some of the folks here are watching us like 4 or 5 o'clock in the morning. DAVID MALAN: Yeah, well, it must be. COLTON OGDEN: Which is awesome. DAVID MALAN: Well, I'm up at that hour, too, so we might as well just do the streams then too. COLTON OGDEN: VDHUG acre in the middle of the Amazon forest, interesting. DAVID MALAN: Wow, all right. VDHUG, you're going to have to elaborate on that one and how that's working. COLTON OGDEN: TWINTOWERPOWER, generic third quarter of the day greeting from a country on one of the continents of earth. Nice. DAVID MALAN: OK, this is a puzzle, isn't it? COLTON OGDEN: That's almost like a very political statement, like a very-- DAVID MALAN: Very generic, yes. Hello, earthling. COLTON OGDEN: Waves at folks from around the globe, says Brenda. [INAUDIBLE] hello, everybody. Hello, [INAUDIBLE], good to have you. GARETHBUTLER2, hello. That's a new name as well. So many new names today. POULTON1987, hello. Hello, hello. DAVID MALAN: Hello, everyone. Yeah, the green really doesn't work very well. COLTON OGDEN: Yeah, it's a little bit-- DAVID MALAN: [INAUDIBLE],, we've been chatting online too, often. Nice to see you. COLTON OGDEN: Yeah, yeah. OK. And VERONI, VERONI was here in yesterday's chat. Hello, VERONI. DAVID MALAN: Nice. COLTON OGDEN: LITTLEJR, hello from Brazil. [INAUDIBLE], hello. Unspecified location from TJ. And-- DAVID MALAN: This is great. Siberia, Russia. That's terrific. Also far away, and pretty cold though. Frankly, it's been pretty darn cold here lately. COLTON OGDEN: Thankfully, today and yesterday not as bad. We had that blizzard last weekend. That was terrible. That was horrible. DAVID MALAN: Though I don't know if we'd compare it to Siberia. COLTON OGDEN: Yeah, Siberia's pretty rough. That's like a summer. Or to them, that's like a summer. But you know. And then MADKINGVALLA says hoy. DAVID MALAN: Nice. Nice haircut. Nice haircut comment. COLTON OGDEN: I haven't gotten it yet. Their joking. I told them I was going to get my hair cut. I think today I told them I was going to get my hair cut, but [INAUDIBLE].. DAVID MALAN: All right, so I guess it's being sarcastic. So not actually very nice. COLTON OGDEN: No, it's not. But it's OK. It's very soon, I promise. Actual promise. OK, I think we're all caught up on the comments, so why don't we-- I'm going to transition to your laptop so you can start taking [INAUDIBLE].. DAVID MALAN: OK, here we go. COLTON OGDEN: Hopefully-- oh, is it the right size? Everything good? DAVID MALAN: Yeah, we can zoom in a little. Folks want to see a little better. COLTON OGDEN: Trying make sure that the actual editor-- this wasn't cutting off your screen. DAVID MALAN: Nice. I hope everyone enjoyed. Does anyone have any questions about how the technology works, any of the code that we've looked at, or where you can go from here? COLTON OGDEN: This will be amazing when I re-splice it. Sorry about that. Facebook booted us. It booted our server. So we're cross-streaming right now to Twitch. Were cross-streaming to Facebook and to YouTube. Right now we're just to YouTube and to Twitch, but it looks like Facebook kicked us for some reason. So if you're watching, or were watching on Facebook, sorry about that. DAVID MALAN: All right. But hopefully, we're now back and we can dive in. COLTON OGDEN: Yeah, let me just make sure that we-- people, if they had any questions before we started. DAVID MALAN: Sure, yeah. We were. COLTON OGDEN: Looks like, no. I guess we'll do one question from HASSAN. Which language do you prefer between C and C++, before we begin? DAVID MALAN: I'd say C. C++ certainly gets useful when you want to do something object oriented, obviously, by definition. I find it a little messy, though it's one of the earlier OO languages, at least, that really was popularized for software development. But it's also just a little messy syntactically. It's kind of a pain, I think. So nah, I never really loved it. COLTON OGDEN: OK. Cool, cool. And then everybody in the stream, definitely let us know whether or not you can still hear us, whether or not everything looks good. I'm going to refresh. Looks like everything's live on our end, but let us know if you can hear us, just as a sanity check for us too. DAVID MALAN: All right, wonderful. COLTON OGDEN: 100% now, so I think we're OK. DAVID MALAN: All right, so if you'd like to play along in any way, let me suggest you go to any of a couple of URLs. We can paste them in here. If you want to go to cs50.readthedocs.io/render50, we have some documentation there as to how to use the tool itself. I mean, if you'd like to look at the source code, as well, I have that opened up in another tab here, github.com/cs50/render50. And what we'll do, I think, is a mix of kind of making some things from scratch, maybe look at the actual source code toward the end to point you out at the various features and how we did things. But what I think is cool about render50, even though to be fair I wrote most of it, is that it's really a mesh of various technologies and techniques. So the goal at hand, quite simply, was we've got some source code, one or more files in C, Python, JavaScript, or whatever, and the goal is to turn it into a PDF. How do we go about doing that? Well, let me propose this. Let me actually go ahead and just download some code that we've already written. So github.com/cs50. How about libcs50? This is the so-called CS50 library. And it's got a few files in here, but I'm just going to go to cs50.c, which you might not have actually ever seen, but is, in fact, the code that implements CS50 library, like GetString and GetInt and GetFloat and so forth. And I'm just going to go ahead on GitHub and save this, just so that I have a big file locally that I might want to print out so that my TF, for instance, could comment on, give me feedback. Sort of an in-person code review in written form. So let me go ahead and save this on my Mac here as cs50.c in my Downloads folder. And let me do-- let me try printing this as a PDF in the simplest way possible. I can go ahead and open up this file on a Mac with something like TextEdit. Super simple text program. It's the simplest of text editors. And you'll notice it doesn't even support syntax highlighting. So all right, fine, I'll sacrifice that. So let me go to File, Print. COLTON OGDEN: Fun fact-- I think that was one of the first text editors I ever used. DAVID MALAN: TextEdit? Ooh, I'm sorry. COLTON OGDEN: Just in plain text mode, not in rich text mode. DAVID MALAN: Oh, yeah. Rich text would really mess you up. COLTON OGDEN: That would be pretty bad. DAVID MALAN: And so you'll see you can kind of see a preview here in macOS of what this thing is going to look like. It's not going to look good. But on macOS, you can actually PDF things pretty easily. In Windows, you might have to jump through a couple more steps. But I'm going to go to PDF, Save as PDF. I'm going to call this cs50.pdf. Author is John Harvard, sure. And then I'm going to go ahead and open this PDF in my Downloads folder. And you'll see that, wow, this is really just a mess here. I've opened up Acrobat, and the comments are wrapping. It's obviously just black and white. Acrobat is doing its stupid thing here. Now the spinning beach ball. So we're just filled with problems here. And it just kind of goes on and on. And this isn't bad. You could certainly write comments on this. You could certainly type comments on this, but it's not all that pretty. COLTON OGDEN: I really love these comments down here with the line breaks. DAVID MALAN: Yeah, well, I wasn't designing for 8.5 inch width. So we could, of course, do this a little better. I could go in here, for instance, and say-- well, let's not do that. Let's actually print it again. Let's go to Show Details. And let's do one step better-- landscape mode. COLTON OGDEN: Hey, that looks a lot better. DAVID MALAN: Game changer. So let's go ahead and do this. And I'll just open the PDF directly this time. And OK, so we're actually in better shape, because it looks like 11 inches for US letter paper, so to speak, is actually pretty good, at least for my commenting style. And so it seems to be better. But what do you think? What more could we do here? COLTON OGDEN: Well, it looks like some comments, for example right here, still do break, so you're not immune to the problem. I can imagine a large Boolean expression or the like also taking up more than 11 inches worth of characters. But I mean, it just looks ugly, like it's just black text on a white background. As a programmer now, I'm more used to the syntax highlighted IDE, some kind of VS code, Atom, Sublime, or CS50 IDE. And frankly, I'm not a huge fan also of-- I guess render50 normally does white background and colored text. But it would be kind of impractical to do black background for printed-- DAVID MALAN: Yeah, that would probably be pretty expensive ink-wise. So you mentioned Atom, and there's VS code, and there's Sublime and whatnot. So let's try that. Let me go ahead and open up my source file in a more powerful program like Atom, which is going to give me syntax highlighting, among other features. And so this is nice here, because if I start scrolling down, OK, it looks beautiful. And you'd think, well, let's just print this, hopefully without the black background. So we can go up to File-- no, we can't. So you can't even print from Atom. And this is probably true of many text editors, because why would you print your code? So to be fair, the use case in question is somewhat narrow. It's pretty academic. We want to print code. But honestly, I use render50 multiple times per week. And I do think teachers more generally, especially in high school, do find this methodology still pretty compelling. COLTON OGDEN: Yeah, I would imagine. I am pretty surprised, because I could definitely see it, especially now that computer science is pretty popular in most high school and colleges, like the fact that text editors don't include a print feature seems a little bit weird to me. DAVID MALAN: Yeah, even for print to PDF, right? Because printing to paper is pretty silly. And I only do this so that the computer doesn't fail on me when looking something up. But you could imagine wanting to print to PDF, because then you can email the PDF. Anyone can open it. You can type in it. You can write on it. It's pretty versatile. So it's pretty compelling final format. OK, so this isn't working, so we could maybe screenshot the code. Maybe screenful at a time, and start printing, and start printing these screenshots as PDFs, but that's, of course, kind of insane. COLTON OGDEN: Yeah, yeah, that is. DAVID MALAN: Those are just images then. It's going to be a huge amount of black. You can't highlight it if it's a ping and not actual PDF. So we quickly realized, like we're going to have to write a bit of code to actually solve this problem. COLTON OGDEN: Cool, yeah. DAVID MALAN: All right, so how do we do that? So we kind of adopted internally with cs50 Python as our language the past few years. COLTON OGDEN: Little better, thank goodness. DAVID MALAN: Yeah, so we figured, all right, let's just start with that constraint. Doesn't have to be written in Python. We could use JavaScript or PHP or whatever. COLTON OGDEN: I mean, the original version of render50 was PHP, wasn't it? DAVID MALAN: It was, actually, yeah. COLTON OGDEN: We had the printer issue with the library we were using. DAVID MALAN: Yeah, eventually broke, because yeah, the library we were using that converts from text to PDF was running into some weird errors. COLTON OGDEN: It had an unconventional PDF format issue, like it had some bytes in the header or something were messed up. And printers were expecting that, and so it wasn't printing [INAUDIBLE].. DAVID MALAN: Yeah, literally we could make the PDF, but we would go to print it and can't print it, which was literally the point of the program. So that was kind of hurting us. So yeah, we rewrote it in Python a bit ago. And so the first question was, well, how do you create PDFs in Python? And you, frankly, I think one of us probably just did PDFs in-- whoops-- PDFs in Python. And kind of started here. COLTON OGDEN: This is modern programming. DAVID MALAN: Yeah. So-- COLTON OGDEN: [INAUDIBLE] unfamiliar with the process. DAVID MALAN: There we go, automate the boring stuff with Python. I don't know if that existed at the time. So there's a lot of search results, a lot about reading PDFs. And long story short, we found our way to a few libraries. And one of them we settled on. So I'm going to go ahead and Google that one directly. It's called WeasyPrint. It's a free open source library that's been continually getting improved. In fact, the author has been receptive to pull requests, even, for bug fixes and performance improvements. COLTON OGDEN: I'm a big fan of their web page. It's very-- if you want to refresh the animation, like the-- that thing. DAVID MALAN: Yeah, it's pretty cool. This has nothing to do with PDFs, but it does have to do with CSS and JavaScript, probably. COLTON OGDEN: And the top little bit there, that's cute. DAVID MALAN: Yeah. COLTON OGDEN: That's cool. DAVID MALAN: So what's nice is this is freely available on GitHub. You can PIP install it, which is the Python Package manager. And so this just lets you take specifically HTML to, per this little demo, PDFs. And so this is what was a new, interesting experience for me. It turns out you can, and some people do, use CSS, not just for printing to screens, but also printing to paper. Books can be laid out with HTML and CSS in a very resizable way. Like here in the US, we use 8 and 1/2 by 11 inches for paper. Folks abroad might use other measurements as well-- A4 or something like that. And so there is actually, built into CSS, support for printing that we actually leverage for render50. So let me go ahead and do this. In advance, what I've done is this. I've got a terminal window here. I happen to be using cli50, which is CS50's command line interface based on Docker. For more on that, check out our stream from the other week. But I'm using that just so that I have all of my libraries and stuff pre-installed. But you can do this on a Mac or PC. You're just going to want to install Python 3. And that's about it, initially, and PIP if it doesn't come with your distribution. And what I'm going to go ahead here and do is this. If I type ls, I've got my cs50.c file in my terminal environment. And I'm actually going to go ahead and run WeasyPrint of cs50.c. And then we'll call it cs50.pdf. COLTON OGDEN: This WeasyPrint, this is a Python library that's been exposed or been-- DAVID MALAN: Indeed. COLTON OGDEN: --aliased as a sort of a Linux program. DAVID MALAN: It comes as both. So it's both a library and it's a program that you can actually use. So if you just run WeasyPrint, then the name of the input file, then the name of the output file, it's going to take that input and turn it into a PDF using the WeasyPrint library. So let me go ahead and hit Enter. And we'll wait for a second. And there we go. We're back at the prompt. If I type ls, we'll see that we now have see cs50.c and cs50.pdf. Let me go into my Downloads folder there and choose cs50.pdf. And we'll see here, once this opens, lovely. COLTON OGDEN: No, it looks beautiful. DAVID MALAN: Oh, my goodness. It's actually worse-- COLTON OGDEN: I'm so glad we downloaded that. DAVID MALAN: --than before. So what do you think is going on here? COLTON OGDEN: That's a good question. Is it rendering it as just plain text? DAVID MALAN: Yeah, pretty much. I mean, what's obviously missing? COLTON OGDEN: I mean, it's not-- there's no new lines at all. DAVID MALAN: No new lines, yeah. COLTON OGDEN: No syntax highlighting. Does need to be in a specific format, or does it need to have a style sheet that gets referenced, or-- DAVID MALAN: That's exactly right. So WeasyPrint is all about converting, not text to PDF per se, but HTML and CSS to PDF. COLTON OGDEN: Oh, that's right. That's right. Yeah. DAVID MALAN: Right. Well, so this is kind of cool, because even from CS50 or any prior background you all might have with web design, it's pretty simple to make monospaced font in HTML. It's pretty easy to start colorizing things if you want. So we just have to figure out now like how to stitch these ideas together, like write HTML, add some CSS, colorize the code, just like Atom is for us, and then go ahead and turn it into a PDF. COLTON OGDEN: So essentially, the first step, I guess, would be take someone's source code and make an HTML representation of that source code. DAVID MALAN: Yeah, exactly. So honestly, one of the simplest ways we could do this is this. Let me go ahead and copy cs50.c and just literally call it cs50.html, same file. And now let me go ahead and open this up in my text editor. I'll use vim at the command line here, cs50.html. And you'll see, of course, just text. This is not HTML. COLTON OGDEN: Right. DAVID MALAN: But it could be. Let me just go ahead here and enact HTML doc type. And then say, hey browser, here comes my HTML. And then, hey browser, here comes the head of my page. And then, hey browser, here comes the title. And we'll just say CS50 library, for instance. Close my title. Close my head. Hey browser, here comes the body of my page. And then I could do something like, hey browser, here come some preformatted text, which typically is displayed in monospace font. I'm going to go ahead, then, and fast forward to the end of the file. And the syntax highlighting you're seeing now is from Vim. It's not HTML or anything, or CSS or anything like that. Down here, I'm going to go ahead and say, OK, well that's it for my pre tag. That's it for my body tag. And this is it for my HTML tag. COLTON OGDEN: OK, so just wrap everything and pre, make it monospaced, and then that's it. DAVID MALAN: Yeah, so let's see what happens here. Let me go ahead and save this. And now let me go ahead and run WeasyPrint, which again is just a program. And as an aside, what I did earlier was PIP install WeasyPrint in order to install that on my Mac or your Linux box or your Windows machine, so long as you have PIP and Python installed. OK, but here I want to go ahead and run WeasyPrint of cs50.html is the input now, not dot c. And then output again cs50.pdf, so we'll blow over the other one. All right, it's taking a little longer this time, but there's more thinking, there's more structure there. COLTON OGDEN: Parsing the HTML, converting that into the PDF elements. DAVID MALAN: Exactly, so kind of behaving like a browser would. Let's go back into my PDF and click this and open it with preview, which gets rid of some of the menus that Acrobat earlier had. And OK, like we're in better shape. It's not beautiful. And we still have some line wrap issues. COLTON OGDEN: Yeah, it looks like it does-- I guess it would be overflow. What would that-- what CSS attribute would that be where it just keeps going, wrap [INAUDIBLE]? DAVID MALAN: No, this would be overflow none. So you just chop it off, yeah. That's what's happening by default here. But of course, there are some bugs. Come back to that in a second. COLTON OGDEN: Oh, 'cause yeah, it's thinking that those are HTML elements. Because in C, when you do an include, you need to use the angle brackets. DAVID MALAN: Exactly. COLTON OGDEN: So it's thinking that's a tag, but it's an invalid tag. And so it doesn't know how to parse it. DAVID MALAN: So this, of course, would be a nightmare if like we, the programmers or the teaching Fellows, needed to go in and change their code. So let's try to fix this. Let me go in here and let me go into cs50.html. And I could, for instance, when we get to these includes, do something like, well, let's go ahead and change all instances of hash include space open bracket to the same thing, but do-- let's see, that's a less than. So ampersand lt semicolon. And then do this globally. Enter. OK, so now I actually screwed that up, because in Vim, I think ampersand has special meaning. So let's undo that. Let's try again. Let's go ahead and replace that with ampersand lt semicolon globally. And I think I probably have to escape this. I'm not sure offhand, so we'll try. Yeah, OK. COLTON OGDEN: Nice. You can do the same thing for the greater than in this case too, with that escape character. DAVID MALAN: Yeah, so let's do that. So let's save that. Oops, sorry, let's substitute. Now say dot h. So literal dot instead of a wildcard, h, angle bracket. And then change this now to a dot h, and then ampersand greater than semicolon globally. But let's escape that again. Cross your fingers, because I rarely do this. OK, I think that's better. COLTON OGDEN: For more, see the regular expression stream that you did. DAVID MALAN: Yes, also. Look at this. We're coming full circle with everything. So many reasons to tune in. COLTON OGDEN: Want to shout out also, MATTHEWTHEGOODMAN, MASHABANA-- DAVID MALAN: Nice, some new followers. COLTON OGDEN: And [INAUDIBLE]. DAVID MALAN: Nice to see you all. COLTON OGDEN: [INAUDIBLE], yeah, OK bro. [INAUDIBLE] and NABSLACK, thank you very much. DAVID MALAN: Nice. Nice, and we'll take a couple of questions in just a moment too. Let's go ahead and save this in just demo. So saving the file. Let's go now back to my prompt where I'm going to run WeasyPrint of cs50.html. Output cs50.pdf, Enter. It's taking a second too. All right, now I'm going to go ahead and reopen that in Preview on Mac OS, but you could use Acrobat or whatever on Windows. And scrolling down, crossing my fingers, OK. So better. COLTON OGDEN: Nice, OK. DAVID MALAN: But this, of course, is going to be super annoying to the TF staff to manually change their code. But honestly, we can probably do that with code, like with Python. COLTON OGDEN: Yeah, I can see it being troublesome for Boolean expressions where you're trying to compare less than or greater than. I mean, if you were doing it in the context of a Boolean expression that did a less than and then a greater than check [INAUDIBLE].. DAVID MALAN: Oh, absolutely. We haven't caught those. I was very specifically just fixing a few of these. COLTON OGDEN: And that would be hard to, I think, maybe necessarily fix-- I guess you could-- I guess you could. Well, no, it'd be kind of tough, because people with different spacing conventions in the context of different letters, different numbers. I think that could be very difficult. DAVID MALAN: No, you pretty much want to do a global replace of all HTML entities that might otherwise be confused as actual HTML. Should we take a couple questions? COLTON OGDEN: Yeah, we had a couple. Actually, very few questions, but a few comments. So SAMCODE says hi, David. DAVID MALAN: Hi, SAMCODE. COLTON OGDEN: The classic [INAUDIBLE]. DAVID MALAN: Hello from DAVIDCODE. COLTON OGDEN: They're saying that-- VERONI was saying that this is, I think, spaghetti code in a nutshell when you were writing up the HTML page with the HTML or [INAUDIBLE].. DAVID MALAN: Yeah, pretty much. And actually, there was one up above. Did we give the URL of the CS50 library here? Down here. COLTON OGDEN: CS50 library [INAUDIBLE]. DAVID MALAN: So HASSAN asked for that. COLTON OGDEN: Oh, did we-- DAVID MALAN: The CS50 library. So that's github.com/cs50-- can't type-- /libcs50. That's the C library that we're just using for demonstration sake. That has nothing to do with render50, per se. COLTON OGDEN: What's the topic? I am on limbo, says RABIN. So RABIN, we're talking about render50 today. So the Python application we use to basically print source code to PDF, which is a surprisingly difficult thing to get with most modern text editors. DAVID MALAN: Yep. And BRENDA and [INAUDIBLE] kindly elaborated on that too. So I think we're in a good place now. All right. All right, so we could do better than this, but we at least now have a tool, WeasyPrint, that can clearly take code, or more specifically HTML, to the final output. So what comes next? So why don't we start to clean up the formatting here and the page size, and get just a basic PDF right. And then we'll come to syntax highlighting. COLTON OGDEN: So would that be-- OK, so I guess maybe one of the first things, would that be like a CSS style sheet, like you said. DAVID MALAN: Yeah. COLTON OGDEN: This HTML needs the styling. Would we fix these comments with something like overflow wrap? DAVID MALAN: We could, indeed. Let me actually pull out my cheat sheet so I don't forget some of the details. COLTON OGDEN: Sure. It's a surprisingly large application. DAVID MALAN: Yeah, render50, in the end, is currently 515 lines. But we're not going to go through all of those today. Let me just actually find the lines I care about here. COLTON OGDEN: That'd be a super stream. That'd be like a whole weekend's worth of line by line analysis. DAVID MALAN: Indeed. So what I'm going to go here is show a very simplified version of some of the CSS we use in render50 now to start solving these problems. And to be clear, the problems I claim are, this is still messy, even though it's better. I'm chopping off some of the code on the right. It's portrait mode, which means there's like no room anywhere for any comments, like you might have with landscape mode, whether letter or A4. So let's see if we can clean that up. Let me go ahead and go back to my HTML page for now. Again, doing things manually just to demonstrate how to do this. Then we'll consider how to automate all of this with code. And I'm going to go up here, and of course, say well, in my head of my web page, I can also have some style tags. Vim's being a little uncooperative, so let me go ahead and re-indent that. COLTON OGDEN: So that's cool. It'll actually look at the style within the HTML. You don't need an external style sheet to get it to work just like a web browser. DAVID MALAN: You can just do it all self-contained. And this is nice, because it's really easy for us in code, ultimately, to just generate a temporary HTML file with CSS and code in it, render it, and then throw it away. COLTON OGDEN: This is cool, yeah. I like it. DAVID MALAN: So all right, so what do we want to go about doing here? Well, let's go ahead and fix-- how about the size of the paper? So it turns out that there's some special directives in CSS that apply not to web pages, but literally to printouts. And you can literally start your CSS by saying page, @page. And in here are all of the properties that are somehow going to relate to the whole page when printed out. So I'm going to go ahead and do a couple of things here. Let me go ahead and say a margin, like in the US, it's pretty common to have 0.5 inches for margins. And even though in web design you typically wouldn't use inches or points, that's generally for the print world. Well, that's our goal, PDFs and printing, so I'm going to use inches here. But you in your country could use whatever is conventional there as well. Let me just go ahead and add some paper size. So it turns out for paper size, you can literally say something like letter, landscape. And these are well-defined values in CSS. In fact, let me go ahead and open a browser and Google CSS page size. See if we get some helpful results. I like to go to Mozilla's MDN, Mozilla Developer Network. And you'll see here that this defines the size and orientation of the box which is used to represent a page. And so you can, more literally, this size corresponds to the target size of the printed page, if applicable. And if we look down here on this site, you can see some sample syntax. You can just say auto, which is what I was getting earlier. Just default to letter in the US and portrait mode. But you can also specify landscape mode. You can specify the size of the page. So if we had smaller paper that we wanted to print to, or want smaller or bigger PDFs, you can specify. And then it understands A4. So if you're abroad, you could say A4 landscape as well. Whatever suits your paper best. COLTON OGDEN: Nice. Something that you don't often see in the world of web programming. You dive into this rabbit hole and-- DAVID MALAN: I don't even know if I knew this beforehand. And so we actually had a problem to solve with it. COLTON OGDEN: Very flexible, but also GOAL1 says, will use it. Very helpful. Thanks for sharing. DAVID MALAN: Nice, nice. Cool. COLTON OGDEN: And MDN is the best, says RABIN as well. DAVID MALAN: All right, so let's see. So I've specified a margin, which just gives me a white border, just to keep things away from the printer. Because a lot of physical printers can't print full bleed, so to speak, where the ink bleeds to the edge of the page. So we'll give it at least a half an inch. And then letter is, for those unfamiliar, 8.5 inches wide and 11 inches tall. But landscape means rotate that 90 degrees. COLTON OGDEN: Short those, yeah. DAVID MALAN: OK, so let's see if we've gotten a little farther. Let me save this. And let me go ahead and rerun WeasyPrint cs50.html, cs50.pdf. Give it a second or so. Might have to do a little more thinking. All right, now let me go back to preview. And notice, it's automatically reloaded and it's already in better pedagogical shape. Like I now have some actual margins on the right-hand side here, where I could write comments with a pen or type them in Acrobat or something. And what do you think so far? COLTON OGDEN: It looks good. It looks a lot better. I mean, it still needs a lot of colorization, I think, huge part of it. DAVID MALAN: And what do-- you're not going to like this. COLTON OGDEN: We do still have the wrapping issue. And earlier we didn't see it, but we can see it here for this particularly long line. DAVID MALAN: Yeah, I'm not liking that. COLTON OGDEN: That's still a problem. DAVID MALAN: So let's go in there. And let me go in here and say now, on cs50.html, let's say-- not inside the page, because that's paper specific. Now this is more of an HTML thing, because I have certain tags in there, like my pre tag that I want to wrap around. So I'm going to go ahead and say pre, specifically, which is just the standard CSS selector. And then inside here-- COLTON OGDEN: And pre, in this case, this is our entire source code. DAVID MALAN: Whole source code. Whole source code. I'm already getting for free the monospace font, but I could change it here using this. Font size, let me go ahead and be specific, say something like font size. I think I like 10 point for printouts, we've decided, just by trial and error. Looks pretty good and is pretty readable. Let me go ahead, then, and say-- and this is to your point earlier-- overflow wrap. I'm going to say break word. COLTON OGDEN: OK. DAVID MALAN: So this is going to help break the code, especially if you have a long string that's not really English or any other language, you need to help the computer know where it's allowed to displace it. COLTON OGDEN: So this is different than overflow colon wrap? DAVID MALAN: This-- overflow. Yes. So this is a little different, specifically for this kind of problem. And then I'm going to specify whitespace pre wrap. So by default, here's how you should handle whitespace. Should be unnecessary because we're already in a pre, but in render50, we actually use divs and some other tags too, so this helps with that problem as well. All right, so let's go ahead and save this, and cross my fingers and hope that this actually works. Let me go ahead and rerun this. And I'm going to stop typing the same commands. If you're unfamiliar, in Linux or Mac OS, or the Bash subsystem in Windows, you can do bang, or exclamation point, and then like the last few letters of a command you typed, even just !w, and that's going to rerun the previous command that matches if you just hit Enter. COLTON OGDEN: That's cool. This time it looked like it was faster, just a little bit, for some reason. DAVID MALAN: Yeah, it might-- I think that's probably a coincidence, to be-- or my Mac had a little [INAUDIBLE] cycles there. COLTON OGDEN: Maybe. DAVID MALAN: All right, so let's go back into there. You'll see it's a little smaller because I changed the font size to be 10 point. Let's scroll up. And now more fits on the screen. So actually, I probably shouldn't have tricked you by both shrinking the font and then claiming to solve the problem. So let's actually undo that. Let me go back in here and not change the font size and see if we're actually solving a problem. WeasyPrint-- oops. So !wa, command not found, because I mistyped that. !we for WeasyPrint does match. COLTON OGDEN: This time it looks like it was slower, so maybe I'm not sure. DAVID MALAN: So let's see here. And now, let's scroll a little, see if we can find that line. OK. COLTON OGDEN: Oh good. OK, nice. DAVID MALAN: So now try-- it's ugly, but if you're printing to paper, you've got to accept either cut it off or wrap it forcibly. COLTON OGDEN: Yeah, certain drawbacks. DAVID MALAN: Yeah. Any questions come in? COLTON OGDEN: [INAUDIBLE] is just making fun of you for not using Fish Shell, which is just another shell. DAVID MALAN: Oh, we use Bash for everything for CS50, but you can use whatever you'd like. COLTON OGDEN: Yeah, there's a lot of-- so these are private comments when they do this, which means they don't want to show up on the stream. You'll see dot-- DAVID MALAN: OK, so we won't read those verbally. COLTON OGDEN: Yeah, we don't read those verbally. Otherwise, I think we're all caught up. DAVID MALAN: All right, cool. So I feel like what we're lacking-- so we now have landscape mode. We can shrink the font, which just makes it a little more pleasant for the teaching Fellows. But we're lacking in syntax highlighting, which means it's just a little annoying to look at. And it's worse than, of course, a text editor. So let's bring that back. How do we do that? COLTON OGDEN: We probably don't do it manually, because I feel like that would be terrible. DAVID MALAN: But-- COLTON OGDEN: We have to write a grammar to do that, I imagine, and it would be a bit complicated. But I'm sure there's probably a library that lets us do that. DAVID MALAN: We could. Let me try this. Let me do this. Just for the sake of demonstration, let me go into the file, scroll down to CS50 library. And what if I were to do this-- span, style, color, red, close quote, close bracket. And then close the span here, because recall that this is just HTML now, even though it still looks like code because it's in my pre tag. Let me save that. And let me go ahead and rerun WeasyPrint, which we can do with our bang trick now. Second to do that. And so here's like kind of a germ of an idea. COLTON OGDEN: Yeah, now I would imagine that would be-- how ultimately it would have to happen. But us doing that ourselves would be nightmarish, I feel like. DAVID MALAN: That's fair to say, yeah. And so there's a bunch of ways to do this. And you allude to the most robust, like any programming language has what's called a grammar, which is a very formal specification for what-- not even what the functions are or what the arguments are, but rather what the structure of any expression has to be. If you take a CS theory course, you can actually see cool techniques for defining these things. But in a nutshell, every language has a grammar. And one of the tools you can use to parse languages according to a grammar is this free library called ANTLR, which we've actually used a little bit in the past. COLTON OGDEN: We used this for some of our more recent projects [INAUDIBLE].. I'm not too familiar with them, but some of the teaching staff have definitely mentioned it. DAVID MALAN: Yeah, it's very robust. It's another tool for language recognition. And it's got support for lots of different languages. In fact, I'll try to pull them up in just a moment. The catch is, it's very computationally expensive, because-- COLTON OGDEN: That was another thing that one of our teaching staff was mentioning. DAVID MALAN: In fact, I think we started using it and then dropped it in favor of another technique that render50, coincidentally, uses-- well, not coincidentally-- uses as well. Because this does it 100% correctly and robustly. The problem is when your code gets bigger and bigger and bigger, it just gets harder and harder and more time consuming to parse. COLTON OGDEN: What's the technique that we use instead of ANTLR? DAVID MALAN: So we use regular expressions, which are not as robust. COLTON OGDEN: Oh, for check50, the checks. DAVID MALAN: For render50. COLTON OGDEN: Oh, for render50, OK. DAVID MALAN: But it gets the job done in almost every case. And so it's-- the performance boost we get on the order of seconds, if not minutes, is certainly super compelling. But just for folks that are curious, because this is a handy library in case you want to do something formal with grammar sometime, let me go to the GitHub link here, browse source tree. And then if I recall here, let's see if the grammars are built in here. I'm trying to remember where they are. Scripts, runtime, let's look in here. Yeah, maybe CPP. No, these are not them. Let me see if I can find them, the grammars. Grammar, additional grammars. Here we go. COLTON OGDEN: [INAUDIBLE] DAVID MALAN: There we go, that's funny. This repository. OK, so here are all of the languages that people have written grammars for. Let's go to C right here. And I think it's in this G4 file. Is it? And that's just comments. Yeah, so this is the syntax that they adopted for defining a grammar for C. Won't get into the weeds of this, because it's crazy long. But that's why it's expensive computationally to actually parse this, but you'll see, like a block comment is something that starts with forward slash star, ends with star forward slash, and then has zero or more characters in between. Or you can just skip this block altogether. COLTON OGDEN: [INAUDIBLE] compilation of simple regex pieces. DAVID MALAN: Sort of, yeah. Exactly. But that's ANTLR. We don't use it, but it's one way. You could think about, well gee, how do I go about figuring out what should be red, what should be blue? I mean, how does Atom do it, how does Sublime do it? They all do something like this. COLTON OGDEN: So RABIN was asking, is ANTLR used for compiler design as well? DAVID MALAN: I think so, because you can use it to parse any language into an AST abstract syntax tree, like literally a tree structure that represents it. And all compiler really does is transform a tree in one language to a tree in another using different symbols. COLTON OGDEN: Should have a stream on that at some point. DAVID MALAN: Yeah, you've got to get someone more qualified for that one. So what we actually use, spoiler, is Pygments. Pygments is a very popular Python library for syntax highlighting. It's at pygments.org, if you'd like to play along. And you'll see that it, like WeasyPrint, can be installed very easily with PIP Install, Pygments. Honestly, it's a little overwhelming, that documentation, like it's such a rich library that there's a lot to wrap your mind around. It took me some googling and some reading to figure it out. But we can at least try it out first. Let's do this. So-- COLTON OGDEN: That's cool. They have a little built-in. DAVID MALAN: Yeah, let me go ahead and just let me go ahead and open this file. So cat is a handy tool if you just want to dump the contents of a file, concatenate them onto the screen. So I'm going to go ahead and highlight all of the code from libcs50 onto my clipboard. COLTON OGDEN: Bella kindly plugged pygments.org [INAUDIBLE].. DAVID MALAN: Nice, thank you Bella. Let's go ahead and paste here. Code description, cs50 library I'll call it, just because they're asking me. And then the language, you can see here, there's some mention of ANTLR, so they apparently might use this underneath the hood, or the syntax maybe. I've not looked closely. But I know it's C, so I'll choose C. But you'll see, oh, there's some NSFW languages on the list, folks. But we'll just move on. I'll choose the language as C. COLTON OGDEN: So [INAUDIBLE]. DAVID MALAN: We'll leave it to the reader at home to zoom in on there. And let's go ahead and click highlight. And you'll see now, if we scroll down, that we have some minimalist highlighting. And it's honestly really hard to see here. Let's zoom in on this. COLTON OGDEN: Yeah, I'm not a huge fan of that theme. DAVID MALAN: Yeah, this is awful. This is like dark green. This is black. This is dark red. This is light blue. So actually, let me see where it is in the UI. Yeah, here we go. Use this style. So friendly, ironically, is pretty unfriendly. COLTON OGDEN: Not friendly, yeah. DAVID MALAN: And I don't remember which one we use offhand. So-- COLTON OGDEN: Monica-- I mean, Monica has a good one for text-- a lot of text editors will use that one. DAVID MALAN: OK, so let's use that one. COLTON OGDEN: But we probably-- for printing, we probably don't use [INAUDIBLE]. DAVID MALAN: But this is awful. This will cost us a fortune in ink. But it's a lot more readable. So if you ever wondered where night mode or dark mode comes from, it's just another CSS theme, especially in Atom which is based on a web browser, based on HTML. So that's looks nice. Let's see if we can find one that is appropriate for printing. Maybe Xcode for Mac OS. They have-- COLTON OGDEN: Oh yeah, that's true. They have a pretty good life theme. DAVID MALAN: All right, so a little bit. It's a little subtle. Not loving it. But long story short, you can play with the themes. You can come up with your custom theme. And I think what we actually do is we downloaded our own theme that mimics GitHub, just because it's popular and familiar to look at. Let's look at one more. You want to pick one here? COLTON OGDEN: Let's do Fruity, very bottom. DAVID MALAN: Fruity at the bottom. Hopefully that's nice and colorful. It is, but going to cost us a fortune. That is an awful red. COLTON OGDEN: It's hard on the eyes. DAVID MALAN: And pink. You know, it's funny though, I've done this before for render50, going through all the themes. Like they're all awful. COLTON OGDEN: Yeah, it's hard to find a good-- like VS codes is nice. I have it on-- I mean, I could pull it up on mine, but the stream wouldn't be able to see it. But a lot of the main text editors, like Atom, VS, Sublime, they all have pretty good native-- DAVID MALAN: Yeah, I know. And I don't know why the libraries that are out there just have awful code, awful syntax. COLTON OGDEN: I don't know, yeah. I can't imagine coding something like this. I feel like I would lose my vision. DAVID MALAN: Yeah, so-- but long story short, we can do this, but we can do this programmatically now with some code. So should we try to weave this in somehow? COLTON OGDEN: Yeah, let's do that. Let's integrate Pygments. DAVID MALAN: All right, so let-- COLTON OGDEN: Is it "pig-ments" or "pygments"? DAVID MALAN: Pig-- It's probably pigments, even though it's Python, so yeah. Since "pigments" is an actual word for color. COLTON OGDEN: Well, [INAUDIBLE] was saying Vibrant Ink is a good one, or [INAUDIBLE].. DAVID MALAN: Oh, yeah? You want to try that? Vibrant Ink, Vibrant Ink. COLTON OGDEN: It might not be-- DAVID MALAN: Oh, but it's not supported by this one at least. COLTON OGDEN: Deep ocean, is that one? DAVID MALAN: Deep Ocean. COLTON OGDEN: And why are they not sorted? I mean, honestly. [INAUDIBLE] random sort, yeah. DAVID MALAN: OK, so in any case, don't let this discourage you. Pygments can-- really, the powerful part of Pygments is that it can do the syntax highlighting. The colors is just an aesthetic CSS detail. COLTON OGDEN: [INAUDIBLE] asking, what about formatting the code, adding prettier format on save? And I think that's what we're essentially getting to is the syntax highlighting aspect. DAVID MALAN: But only syntax highlighting. render50 is not going to fix your style. It's not going to do like style50 and tell you what's wrong. It's not going to change indentation. It's just going to print your code, but syntax highlighting. COLTON OGDEN: Do we have a tool that actually does manually change the code or does render50 only-- or style50 only prescribe the changes that you need to make? DAVID MALAN: Just the latter, and that's deliberate, because we didn't want students to get into the habit lazily of just know fix my code for me, fix my code for me. The whole point is to get muscle memory for doing it yourself. COLTON OGDEN: Makes sense. DAVID MALAN: But there is a tool underneath the hood of style50 called astyle. It's an older program that's great for C-- we use different tools for different languages-- that actually could do that for you if you wanted. COLTON OGDEN: OK, that's pretty cool. DAVID MALAN: All right, so let's see if we can't start to stylize things a little bit better. So I'm going to go in now. It's time to, I think, write a Python program here so that we're actually doing this programmatically and stop doing this manually. So let's go ahead and do this. Let me go ahead and save that last version. Let's go ahead and write a program called render50.py, for instance. And let's go ahead and start to do this as follows. Import argv, or no, rather sys, so that I have access to argv. And then let's just do something simple like this. Let's call it input is going to be sys.argv bracket 1. And then my output file is going to be sys.argv bracket 2, for instance. And then just as a sanity check, let's go ahead and say up here, if length of sys.argv does not equal 3, I believe, let's go ahead and sys.exit, saying usage is Python render50.py of your input and your output file, like this. So what I do when programming, honestly, even something like this, because frankly it's been a few months since I wrote Python, I'm going to go ahead and save the file. And I'm just going to run it. Let's see if I screwed up already. And indeed, I did, it would seem, because it's detecting this. Let's say my input's going to be foo-- OK, still not cooperating, but that's to be expected-- bar. So I think I'm OK. It hasn't done anything, but the next sanity check I would probably do for myself is this. Once I've gotten these variables, I might say something like, let's just print out input is, and then I can go ahead and put a placeholder here and say input. And then I can do another one that says output is. COLTON OGDEN: We do something very similar to this in the a lot of the game streams too. DAVID MALAN: Yeah, like baby steps, if you will. COLTON OGDEN: Sanity checking variables, yeah. DAVID MALAN: And let's format those strings so that Python 3.5 knows what to do, or higher. Let's go ahead and rerun this. Python of render50, foo bar. OK, so like correct. Like five out of five for correctness, even though it doesn't do anything useful. But honestly, to this day, this is how I start writing code. Because if you just start blindly writing lots of code, you then waste more of your time debugging than coding. COLTON OGDEN: That scaffolding is kind of like test-driven development too. DAVID MALAN: Yeah, no. I mean, absolutely. I could actually write tests now that pass in foo and bar and make sure that foo and bar are printed, for instance. COLTON OGDEN: We have a couple people make [INAUDIBLE] also. REMOTE says hello. DAVID MALAN: Hello REMOTE. COLTON OGDEN: Hello, R3MOTE. DAVID MALAN: R3MOTE. COLTON OGDEN: And then we have, NOTSUREOFCOURSE say, I see Vim, I follow. So you've got to follow [INAUDIBLE]. DAVID MALAN: OK, we'll just start-- this is CS50's Vim stream, all the things you can do with Vim. COLTON OGDEN: Yeah, NOTSUREOFCOURSE, and R3MOTE, thank you very much for following. Appreciate it. DAVID MALAN: OK, cool. All right, so let's see if we can't now automate or encode what we were doing with WeasyPrint, then add some syntax highlighting, and then we're pretty close to having render50. COLTON OGDEN: To the actual application, yeah. DAVID MALAN: Even though the actual one took 500 lines and a lot of weeks or days. OK, so let's go back into my code. Oops, that's my other file. Let's do Vim of cs50.-- nope-- of render50.py. Get rid of these test lines. And now let's go ahead and actually do something interesting here. So I want to actually use WeasyPrint to take as-- input that file, and I want to go ahead and output that file as well. So we're going to have to read in the file. So how, in Python, would you read in the text of a file? COLTON OGDEN: With open. We're using a context manager, so [INAUDIBLE] name of the file. DAVID MALAN: With open, and open input, and read. COLTON OGDEN: As f, or file, and then that way it'll close automatically when you're done with it. You don't have to manually close the file. And whatever work you need to do on this, if you're going to read the lines or read it as text, you could say file.read, or file.readlines. DAVID MALAN: OK, so do that. Lines gets file.read, and just read the whole thing in. And then honestly, just as a sanity check here, let's just print out the line. So if I run this program now, hopefully I'll see the entire contents of my input, if we did this right. COLTON OGDEN: As one string in this case, yeah. DAVID MALAN: So let's do this. And Python of render50.py. That's my usage. My input is going to be cs50.c. So I'm not using that intermediate HTML file anymore. And eventually, I want my output to be cs50.pdf, even though I'm not going to get there yet. Enter. OK. COLTON OGDEN: Well, it seems to work. DAVID MALAN: So we're baby steps, and we're one step closer to having the program. So that's good. So now we somehow need to tell WeasyPrint to actually turn this string into a PDF. But we also want to wrap it with some HTML. So there's a bunch of ways we could do this. But just for the sake of simplicity, let's go ahead and just start this. So let's go ahead and say the HTML that I actually want is going to be literally a doc type. And then let me go ahead and concatenate onto that. For instance-- actually, let's give myself a new line. COLTON OGDEN: You can also use triple quoted string to-- DAVID MALAN: Yeah, we could do that. COLTON OGDEN: If you wanted to do that. DAVID MALAN: Yeah, let's just show it this way. And then we can-- eventually, we're going to clean it up, 'cause you're not going to want all this concatenation in your file. But it's perhaps the simplest way to show it also line by line. COLTON OGDEN: OK. DAVID MALAN: So let's, hey browser, here comes my HTML tag. Hey browser, here comes my head tag, and new line, just for good measure, even though strictly speaking, we don't need these last two new lines. HTML plus equals, now let me give me-- honestly, I don't need the title because I'm making a PDF, so I'm going to leave that alone. I am going to give myself a style tag here. And I'm going to go ahead and close that tag, even though we won't put anything in it yet. And I'm going to go ahead and close the head tag now, even though there's nothing in it. And I'm going to go ahead and start now a body tag. And now I'm going to go ahead and concatenate on the lines. And then I'm going to go ahead and concatenate on close body, and a new line. And then-- oops, I'm using C. Then-- though incorrectly. Then let's go ahead and concatenate one last thing-- HTML. And just to be nit picky, a new line. COLTON OGDEN: So this is unenclosed with a pre, too, so this isn't even going to be monospaced. This is just literally the text of your-- DAVID MALAN: Indeed. This is like version 1 where we started without the pre tag, I think. Or now, even without a tag. So let's just do a sanity check now. Let's actually print out the HTML mail that I've just dynamically constructed. Save that. Python of render50.py, passing in cs50.c. Outputting cs50.pdf. Let me clear my screen. So here we go. Run. All right, so it's almost the same, but sure enough, there are those tags. I didn't bother indenting them, but it doesn't matter for HTML. And if we scroll up, up, up, up, up, keep going, this is all my C code, C code, C code, C code, thick, C code comments. There we go. There's all that starter. COLTON OGDEN: Very poorly formatted, by the way. DAVID MALAN: Yeah, but honestly, it really doesn't matter in this context, because a human, and a browser even, are never going to see. It's all going through the library. COLTON OGDEN: [INAUDIBLE] this HTML. DAVID MALAN: So let's go back here and let's add the pre tag, like you alluded, so that we're ready for that. So plus equals-- oh, quote it, pre, backslash n. And then let's close that over here. And as Colton said, we don't need all of these lines of plus equals plus equals. Could use a triple coded string and just do it manually. But honestly, once we really get clean here, we're probably going to want to use a templating library maybe. Or honestly, in render50, what I do is I just have long single lines of HTML, because it really is just throw away stuff that I'm generating quickly. And I didn't want to overengineer it by having a whole template for, honestly, what's a pretty small file. OK, so let's go ahead now and run this. Oh, right. Let's still print the HTML. Let's run this one last time. And I'm just going to copy my command from before. And now we have a dynamically generated HTML file, but using code, not using Vim and manually editing the C file. COLTON OGDEN: Yeah. DAVID MALAN: So we've already made progress, even though we're reinventing a wheel here. COLTON OGDEN: What comes after CS50, and can foreigners participate? DAVID MALAN: Really good question. We can paste a few URLs here, actually, and field this. CS50 has a few follow-on courses now, one taught by Colton. So if you want to go to edx.org/cs50, you'll see-- COLTON OGDEN: [INAUDIBLE] in there. And then-- oh, [INAUDIBLE]. DAVID MALAN: That has all of them, actually. Oh, actually, Brenda posted it there. So let's see what Colton just posted. COLTON OGDEN: Oh, nice. I posted the [INAUDIBLE]. DAVID MALAN: OK, we'll paste all of them. So we have /web for the web course, and /mobile for Jordan [INAUDIBLE] react native course as well. So those are three good follow-on courses. COLTON OGDEN: They love VS code. Just type exclamation point and that's it. Oh yeah, because there are certain text editors-- I don't know if maybe Vim has something like this. You can type, basically, in VS code you can just type HTML tab, and it'll just give you an entire template for free. DAVID MALAN: Oh, yeah. COLTON OGDEN: Vim probably has something like that too. DAVID MALAN: I'm sure. I just don't have fancy Vim plugins. I do it old school. COLTON OGDEN: Yeah. And aside from that, I think we're-- oh, and JMC516 saying, yay, David is here. DAVID MALAN: Nice to see you too, JMC. COLTON OGDEN: Hope your week was good. DAVID MALAN: And these courses, are they equally as short-- oh, OK. So that's being asked-- we'll answer that publicly anyway. Are these three other courses equally as short as CS50? Well, I'm not sure I would call CS50 short, but they're no longer than CS50. And they're probably a little less work, because there are fewer projects over multiple weeks, whereas CS50 is week after week after week of work, at least if you're doing it in real time. COLTON OGDEN: It prepares you for anything, any course. DAVID MALAN: Perhaps, indeed. Yeah. COLTON OGDEN: Why are there sometimes-- why is it sometimes that I have to specifically read a file in bytes with rb instead of just r? DAVID MALAN: Yeah, so rb specifies binary file instead of Ascii. And this has to do with encoding issues, because if you have actual text in a file and you want Python to be able to distinguish Ascii characters from longer Unicode characters and different encodings, you have to help the computer do that. If you just tell it binary, it doesn't know if this byte or these bytes is a character or binary data. And so long story short, when it is truly just binary data, zeros and ones, you tell that to Python so that it doesn't try interpreting the characters as letters or symbols on a keyboard. COLTON OGDEN: If you read like an executable file-- DAVID MALAN: An executable file, a forensic file, image file, a video file, anything that's not text. COLTON OGDEN: Anything that's not encoded, I guess, as text, yeah. And there is a binding in VS code for the templatization, et cetera. And the key bindings autoformat in Vim, actually. DAVID MALAN: Yeah, Vim keystrokes can be used in lots of different editors here. All right, so shall we go ahead and start to use WeasyPrint programmatically? Then we'll figure out how to get Pygments in there. And then I think we can kind of look at it altogether in render50 itself. COLTON OGDEN: Sounds good. DAVID MALAN: All right, so with WeasyPrint, let's see if, actually, we can give folks a good starting point. Because honestly, it's been so long, I don't even remember without looking at my code. I'm going to go back to WeasyPrint.org, which is this the website for this free library that we use. It is both a command in Linux and Mac OS and Windows that you can use to generate PDFs, but it's also a library. And that's the part I want to remember now. So I'm going to go to Documentation up at the top here. And the author is really into their animations here. COLTON OGDEN: It's a nice looking website, though. I do like it. DAVID MALAN: It's very pretty. I just really want to get to the good stuff. Beautiful examples, source files. So here's how you install it. And I think we mentioned this earlier-- PIP Install WeasyPrint, pretty straightforward. Oh, and as an aside, you can use it to print web pages, too. COLTON OGDEN: Oh, that's cool. It will actually use like wget, or underneath, curl under the hood. Or I guess [INAUDIBLE] wget, probably, right? DAVID MALAN: Well, the equivalent. It will grab all the source code and then render it for you. which is pretty cool. If you wanted to like make sort of a PDF screenshot of a web page at a command line. So let's go to, let's see, Installation Guide. The one catch with WeasyPrint is that it does have a bunch of dependencies. And so let me defer to the documentation as to what those are. Frankly, this is one of the reasons why I tend to run it in CLI50, because our own command line environment has all this stuff packaged up for you. However, render50 does pull these things in as needed as dependencies, but here's where you might run into some hiccups, especially on Windows, I think especially, and maybe even Mac OS, where there's just not as rich of an ecosystem for some of these libraries. COLTON OGDEN: WEBSTREAM23, Nate says, I'm here. Sorry I was late. Hey, David. What is-- I'm sorry, but what is render50? In a nutshell-- DAVID MALAN: OK, Brenda's pasted the URL there. Just a command line tool for creating PDFs out of source code, in a very pretty and useful way. COLTON OGDEN: Yeah, definitely check out the early part of the VOD too, if you want to see our scaffolding from nothing to here. DAVID MALAN: Indeed. All right, so let's see if we can go to the API here. And here's where I spent a bunch of my time trying to figure out what functions are available in WeasyPrint. And so if you read through the API page on their documentation, you'll see how to use the command line API. But if we scroll down to Python API, you can actually see where the actual functions are here too. So let's try to start integrating this into our code. I'm to go back to my render50.py. And at the top, I'm going to have another import here. I'm going to go ahead and say, from WeasyPrint, import CSS and HTML, both of which are classes that come with WeasyPrint. I'm going to skip some error checking here, because I think that's sufficient to get us started here. And let's go ahead now and actually use WeasyPrint to create a PDF dynamically out of this code. So here, let me just scroll down to where we do this in render50. And let me pull up the corresponding documentation. I think if we go back a bit, let's see if we have a simple example here. Documentation. Oh, that's where we were. Documentation, Design, Full Documentation. Do I want to go to Samples? Let's just see the samples and see if they give us some sample Python code. Nope, these are just sample HTML files and the resulting PDFs, which are pretty nice. So I'm, indeed, going to go back to Documentation and go to Read the Docs. Not ours, but theirs. And let's see, if I scroll here, here we go, Tutorial as a Python library. That's what I want. So this is exactly where I just started. So from WeasyPrint, import HTML. And you'll see that it can be as simple as this, calling the HTML class, the constructor, passing in the URL, which isn't what we want, but this is one usage. And then just calling a write PDF method inside of it. COLTON OGDEN: So it looks like it takes either a URL or a file. So it can't take, in this case, necessarily, a block of string data. DAVID MALAN: Not just yet. It can, it can. But that's not what they're actually doing here. So let's see, what we want to do here is this is opening a file. And that's the same as actually using a named parameter. Same as using a URL here. And here we're just doing standard in. But now it's getting interesting. If you have a byte string or Unicode string already in memory, you can pass that in, but the argument has to be named. So using literally string, you can pass in. And this is great, because this is what you and I have been making. You can pass in a raw HTML. COLTON OGDEN: Nice, I like it. DAVID MALAN: All right, so let's go ahead and pull all this together. Down here, I'm going to go ahead and not print that out anymore. I'm instead going to go ahead and call this my document. And I'm going to go ahead and instantiate this HTML class. And I'm going to pass in a string of HTML, which is exactly what I constructed. And then we just have to figure out how we take now this object that we have in memory and actually convert it to a PDF. And you'll see here that write PDF will do that for us. COLTON OGDEN: OK. DAVID MALAN: So let's go ahead and use that. And let's see, write PDF takes a couple of arguments. One is the file name. One is optionally the CSS that we want to use, but we won't even bother with any CSS. So let's just try that. COLTON OGDEN: And it looks like it's implying you can use multiple style sheets too. DAVID MALAN: Yeah, a whole list of them, which is nice. Which is consistent with having multiple CSS files. All right, so let's go ahead and do that. So let's call now document.write pdf. I want to make the file name be ultimately cs50.pdf, but remember that we took that as an argument. So we can just say write pdf output. And then let's go ahead and say done, and see if we're actually done with this. All right, let's go ahead and save this. I'm going to clear my screen. And I'm going to go ahead and run Python of render50, cs50.c, cs50.pdf. Cross my fingers, as I always do when coding. It's taking a moment, taking a moment. Maybe it's done. We shall see. Let's go into Preview. And OK. COLTON OGDEN: Nice. DAVID MALAN: Now, it's ugly. COLTON OGDEN: Back to where we were, but we've reduced our workload by a tremendous amount. DAVID MALAN: Yeah, by writing code for like half an hour, but yeah. COLTON OGDEN: It's all automated now, which is nice. DAVID MALAN: Right, but we've reintroduced those same problems. So we need some CSS. We want to rotate this thing 90 degrees. And notice that other problem is back, which is here. COLTON OGDEN: Yeah, we're not escaping the certain tags, yeah. DAVID MALAN: Yeah, so let's fix the tags thing first, because this feels like it should be pretty easy. How do I go about doing this in Python? Do you know? COLTON OGDEN: Getting rid of the tag, or are you going to approach it from the regular expressions point of view? DAVID MALAN: Well, ideally. But we want to use, ideally, a library to do this. COLTON OGDEN: Right, so you could use read, yeah, in this case. DAVID MALAN: Oh, well not even. We don't have to do it. COLTON OGDEN: Oh, escaping HTML, yeah, yeah. OK, sorry. I thought they meant the same that we did algorithmically before. DAVID MALAN: No, there's a few ways we can do this, for instance. So this library will apparently escape code for us. And I'm trying to remember what we do in render50. Let me just do a quick check, because it'd be nice to be consistent. COLTON OGDEN: WEBSTREAM is saying, finally, an example of HTML in Python, very excited. Is there a reason, says MKLOPPENBURG, for importing the libraries in a try in render50 instead of doing this on top? DAVID MALAN: In a-- oh. COLTON OGDEN: Like a try [INAUDIBLE]. DAVID MALAN: Oh, are you looking-- is [INAUDIBLE] looking at the actual-- COLTON OGDEN: Yeah, MKLOPPENBURG is referring to, I think, the actual final distro. DAVID MALAN: Yes, because we wanted-- in the actual render50 source code, which we'll look at in a bit, we actually try to import some libraries, because we want to detect in a user friendly way if the user has forgotten to install some of the dependencies. And we can do that best by trying to import and then responding in a useful way if the human has forgotten. COLTON OGDEN: Makes sense. It there something for Bash, like what we did in Docker file for Docker, write a file and it downloads every dependency that's needed? Brian showed something like that for Heroku in one of the recent streams. DAVID MALAN: Indeed. Skipping ahead a bit. We'll come back to this. But let me pluck that off now. Yes, in render50 itself, in the actual distribution, which we'll look at in just a bit, there's a setup.py file, which is a standard Python packaging file. And you can specify literally this. What does this installation require? And you'll see a list of all of the libraries, the dependencies that we want Python to pull in for you. This is similar in spirit to requirements.text, which PIP uses as well. So yes, this can all be automated. COLTON OGDEN: Something like node modules, too, in like a node environment even. Like it's a pretty common paradigm. DAVID MALAN: Exactly. And let me just search for one more thing, because I want to be consistent. But we don't necessarily have to be. I'm just flipping through the source code here for-- do you want to take a look at any comments that are still coming in? COLTON OGDEN: I think we've plucked off all of the current questions. I think we're all caught up today. DAVID MALAN: All right. So I'm going to go ahead here, and what do we want to do? OK, let's just do this another way. Or even if it's not quite consistent. String literal, here we go. Our old friend Stack Overflow. CGI escape it looks like we can use. So let's see if this is good enough. But this is not, I think, what I use in render50. We do it a little more robustly. But let's go ahead and try this. Let me go into my code. And at the top, I'm going to open up render50.py again. At the top I'm going to go ahead and import CGI, Common Gateway Interface. And then in here, when I concatenate on the lines, I'm going to do cgi escape of those lines. So I pass as input the lines, which are from the raw file, which we read in, and hopefully escape all those dangerous characters. Let's go ahead and rerun that same render50 command as before. OK, so this is why I didn't use cgi escape. It's deprecated. COLTON OGDEN: It's html.escape. Well, that would cause issues, though, because we're already using-- I guess you could do from html import escape, and then I would [INAUDIBLE].. DAVID MALAN: Yeah, that's fine. So let's do that, actually. Let's do this properly, like they're saying. So from html import escape. And you're right, now down here I don't say cgi escape, I just say escape. COLTON OGDEN: Yep. DAVID MALAN: Let's save that. Clear my screen and rerun Python on render50. COLTON OGDEN: [INAUDIBLE] DAVID MALAN: There you go. Just a rabbit's hole. OK, done, question mark. That's for me. That's not my interpreter questioning my abilities here. Let's go back to this. And scrolling, scrolling, scrolling. All right. COLTON OGDEN: Nice, looks good. DAVID MALAN: So programmatically, right? No find and replace like we did in Vim before. COLTON OGDEN: Yeah. This will fix all issues. This won't be just the context of include statements. DAVID MALAN: Exactly. COLTON OGDEN: This will work for Boolean expressions and other areas. DAVID MALAN: Anywhere there's an angle bracket that's dangerous. Yep. All right, so now let's fix the CSS style. And then we're almost there. And frankly, in retrospect, I'm wondering why I struggled so long to write render50. COLTON OGDEN: That's OK. DAVID MALAN: But here we go. Let's go in here and add some CSS. So I'm going to go ahead and preemptively say that my CSS is going to have a few strings. And what did we want earlier? Well, we had like the app page sign. And I think we had a margin of 0.5 inches. And we had a page size of letter, landscape for the US here. And I'm just doing this as a one-liner. I could pretty print it like I did before, but again it's code. I could put this in a separate file, a template. But honestly, it's so short, that ah, it just feels fine, to me at least, to bake this into my program itself. COLTON OGDEN: Yeah, if this were a massive set of style, probably the separate page, or a separate file would make sense. DAVID MALAN: Yeah, and I'm never going to change this. It's not like I'm collaborating with a colleague who's going to make the CSS change over time. This is kind of a one-off, so I'm OK with this if you are. But I am going to specify that line break stuff. So I want to concatenate onto this for my pre tag that I want to do-- what were those lines that we had before? COLTON OGDEN: It was-- I forget that exact style. DAVID MALAN: Yeah, me too. Here we go. COLTON OGDEN: [INAUDIBLE] use it. DAVID MALAN: It was overflow wrap. So overflow wrap is going to be break on words when you can. And then lastly was white space-- oops, white-- it's got weird dashes in weird places. White space is pre wrap. COLTON OGDEN: Tune in next week for our CSS stream, which we actually are having on Thursday. DAVID MALAN: Oh, yeah. That would have been a lot-- this is something called CSS, Cascading Style Sheets. So this will make sense next week, I guess. OK, I think that's enough. And then as I recall, this thing takes what, style sheets? Was that the argument? COLTON OGDEN: Yeah. And then it takes in a list. DAVID MALAN: In a list. So I'm going to use square brackets because I only have a list of one thing. I could actually have declared two variables, like CSS1 and CSS2, though that feels a little messy, and passed in a list. But I'm just going to make a list of one string. COLTON OGDEN: We're on CSS3 currently. DAVID MALAN: That's funny. Version 3 of CSS. All right, now I'm going to-- oh, wait. But that goes in the print function, the write function. So let me move this over here. I goofed. I think that goes here instead. COLTON OGDEN: JSNOBS, I love in line CSS. DAVID MALAN: Here we go. I'm going to now exclamatory emotion, done, hopefully. This is where I'm going to screw up my [INAUDIBLE].. COLTON OGDEN: You might be shooting yourself in the foot. DAVID MALAN: I know, I'm going to screw up now. Here we go. Dammit! OK, so what did I do wrong here? No such file or directory. Oh. COLTON OGDEN: I think it might expect it to be a file reference there, maybe, instead of a string. You might have-- oh, you have to do the CSS constructor, remember, for the CSS object. DAVID MALAN: You're quite right. COLTON OGDEN: Documentation. DAVID MALAN: You're quite right. Let's go back to the documentation. See, I naively-- I looked into the-- stared into the sun. COLTON OGDEN: I mean, I screwed up. I forgot as well. DAVID MALAN: I totally forgot this step. As Colton's pointing out, we need a CSS object. So let's go ahead and do that. Let's go ahead and call these properties, just to distinguish them. Properties. And then let's, yes, go ahead and create a CSS object called CSS. And the input is called string. And that string is going to be my properties. And now, down here, what I pass in is that CSS object. Thank you. So in retrospect, I don't like the variable names I've come up with here. Like I used document for HTML and HTML here. So we'll clean that up in just a bit. Let me save this. Let me go ahead and clear my screen and rerun it. And definitely-- I didn't cross my fingers before, that was the problem. COLTON OGDEN: That was your problem. DAVID MALAN: All right, so now let's go back to Preview to look at the PDF. And OK, now it's getting pretty cool. COLTON OGDEN: Yeah, so definitely landscape. DAVID MALAN: Landscape mode. And decrease the font size, just to demonstrate one other property. Let's go into that. COLTON OGDEN: We can test the wrapping too if you go down a little bit. DAVID MALAN: Oh, yeah. Let's keep going, lest we be sweeping something under the rug here. Yep, it's wrapping down and toward the middle there. All right, let's just make a font size now of font size 10 point, we decided. Now even Vim is wrapping, but that's fine. Let me go ahead and re-render this. OK, reload. And now it's just a little smaller. COLTON OGDEN: Nice, looks great. So CSS is working. DAVID MALAN: Yes, so I think we just need to integrate Pygments and then we're kind of done. COLTON OGDEN: Yeah, yeah. DAVID MALAN: So how do we do this? Well, Pygments has a pretty rich API too. If we go into Pygments here, let's go under Docs. And Quick Start I like the sound of. I tend to be a little impatient. I don't want to read all the documentation, so just give me a quick demo to get me up and going. Let's zoom out a bit. Oh, look at this. Perfect. So here's an example for how you highlight Python code. That's perfect, except C code. So we import a highlight function. We import what's called a lexer. And we import what's called a formatter. So a lexer is kind of like a parser. it's one step in parsing that actually tokenizes the input into recognizable, distinct thing. So like an argument to a function, a variable declaration, a function call, parentheses, any colons. So those kinds of units of logic in a language. COLTON OGDEN: It's going to sort of map those, like the HTML formatter is going to look at all those individual tokens, wrap them with the HTML styling that it needs to, or whatever, however that's going to work. DAVID MALAN: Yeah, exactly. So let's go ahead and copy some of this. But we don't have Python code, so we're going to have to look up something to figure out how to do this and see. I'm going to go into my code up here. And I'm going to go ahead and import from Pygments those three things. Maybe it's called C lexer, but I'm not sure. So I'm going to go into Lexer and Formatter Help. And I'm going to go down here. I want to see available lexers, but I don't think I'm going to see it on this page. Where are my lexers? COLTON OGDEN: There's a guess lexer function which will sort of try and predict. DAVID MALAN: There is. And that's what we actually do in render50. We infer with high probability what the language is. We don't require you to tell us. OK, lexers for C and C++ language. OK, it is called C lexer. So I could've guessed. COLTON OGDEN: CPP would have been a little bit trickier to guess. Maybe somebody might do CPP all caps. DAVID MALAN: Indeed. So let's see if we can copy that and go back up here. It's probably exported here, but let's see. I might need to be a little more specific as to where we're importing it from. But we'll come back to that. And let me clean up some of my variable names, because I really don't like the mess that I made here. So this, yes, is my HTML object. But what can we go ahead and call this? This is some of my properties. Let's go-- COLTON OGDEN: To be clear, this isn't your HTML object. This is your string block. DAVID MALAN: Exactly. COLTON OGDEN: Document is your HTML-- DAVID MALAN: So I'm going to call this markup here. And let me go ahead and do this programmatically. So HTML-- whoops. Do it again. HTML space, a literal plus character. Let me go ahead and change this to markup and then a literal plus character. There-- COLTON OGDEN: Dammit. DAVID MALAN: OK. I'm not very good with my Vim regular expression sometimes. So we'll just fix that. Sometimes doing things manually is faster. And then down here, let me just fix this. My string should be my markup. So I like this now. COLTON OGDEN: I like that better, yeah. DAVID MALAN: My HTML-- and then actually, let's call this my HTML variable for concordance with the classes in WeasyPrint that I'm using. So now my HTML variable is the return value of my HTML function call. My CSS variable is the same for my CSS function call. And then I've got markup and properties as my sort of intermediate steps. I'm more OK with that. COLTON OGDEN: Well, the nouns are correct. DAVID MALAN: Yes. All right, so we've cleaned up the code. Now we need to actually use Pygments. And let's go back to that demonstration a moment ago. Looks pretty simple. We just need the code like this, and then we need to go ahead and highlight it in some way. Print highlight code. So how do I go about doing this? So let me go ahead and say-- we don't want to print the HTML just yet. We want to go ahead and syntax highlight it. So I'm going to call it, say highlighted. COLTON OGDEN: Do we want to-- do we want to do this before it gets passed to markup? DAVID MALAN: Yes, indeed. Right? Because we don't want to syntax highlight my HTML. Very good point. So let's go ahead and do that. I'm going to go ahead and escape my code, yes. But I want to go ahead and highlight that resulting code also, passing in the code itself. Now I need to specify my lexer, so C lexer. COLTON OGDEN: Will it be messed up if you escape it first? Because it's going to replace the less thans with the less than-- DAVID MALAN: Potentially, depending on the lexer. But the problem here is if we render it-- if we syntax highlighted it, we're going to have a whole bunch of spans we're going to see. And the problem is if we escape that, we're going to escape all of the spans. And we're literally going to see all of the HTML that Pygments is generating. So we shall see. And this is why it's not necessarily 100% robust. But let's see how far this gets us. So the reason I'm doing this is I'm passing to highlight function the code that I want to highlight, saying parse it as though it's C, and use the HTML formatter as the output. Why? Well, I'm just following the documentation here, per this example. COLTON OGDEN: Yeah, that takes in the lexer and the formatter constructors as second and third parameters. DAVID MALAN: Exactly. And you know what? I think I can retract. Now I know why I couldn't figure out before what function I'm using to escape code. I'm using Pygments. I just realized. So notice that, per the documentation, it says that sample code like that is going to print something like this. So a div with a highlight class, then the pre, then some spans with these weird class names surrounding things like the function, surrounding things like the strings. And notice, it's giving me the quotes for free. COLTON OGDEN: It already does it for you. DAVID MALAN: So I'm going to go in and tidy this up. I don't need the pre, because I'm getting that for myself. And I don't need the escape anymore. I can just pass in the lines of code, which means I don't need to import this library. Oh, I also broke it anyway up there. And now, in retrospect, I realize this is why when I was Control-F-ing before in the GitHub repo, I couldn't find it because I'm getting that functionality from Pygments. COLTON OGDEN: That makes sense, OK. That's much simpler. DAVID MALAN: So let's save this and go ahead and run it. All right, so HTML formatter is not defined. I think I just must have made a typo then. So oh, it's lowercase html. So that's my fault. I got a little too nit-picky myself. COLTON OGDEN: I would have done it the way you did, all caps, but I guess that makes sense. DAVID MALAN: All right, so let's now save it and rerun it again. Oh, cross my fingers. OK, good. COLTON OGDEN: Good thing you did that. DAVID MALAN: Just in time. Open this up. And now, interesting. Not actually working. COLTON OGDEN: Yeah, it's not syntax highlighted at all. DAVID MALAN: All right, so this is interesting. But how do we go about solving this? Well, you know what, if the Pygments library's purpose in life-- that was an amazing alliteration-- does not-- it's supposed to generate HTML for me. let's introduce a new baby step where we print out, like we did a little bit ago, like the actual HTML we think we're turning into a PDF. COLTON OGDEN: OK. DAVID MALAN: So rather than just write this PDF now, let's go ahead and print out first, if we may, the markup. And then let's just sys.exit right here and just get out. OK, no done message this time. All right, OK. I've just printed it out. And notice there's a huge amount of CSS here-- or of HTML here now. And it's weird stuff. All these spans with all these classes. But that's what Pygments is doing for you. It is using regular expressions to match what looks like C code. And its just surrounding them with these weirdly named classes, which you can think of as just red, green, blue, purple, yellow. But they're configurable. And I don't know what the specific class names mean. They're short and succinct, so that you're not bloating the code too much, but that's what Pygments is doing for you. So this is good. This suggests that we are, in fact, printing out-- generating actually-- all of this HTML. But there's no colorization happening. So what might be missing? COLTON OGDEN: So I'm guessing we haven't defined the colors for the classes. DAVID MALAN: Yeah. COLTON OGDEN: So the classes are saying-- DAVID MALAN: Yeah, we don't have a style sheet. COLTON OGDEN: --we're here, but yeah, we don't have a style sheet. DAVID MALAN: Indeed. So the way we do this-- let me see how we do this in render50 for consistency. If I go in here and go into render50 itself, actually, I do have it here. Let me see if I can find it real quick. COLTON OGDEN: MKLOPPENBURG was hypothesizing, are the comments messing it up? Not quite. DAVID MALAN: Are the comments messing-- nope. And in fact, this is just now one lack of piece of code from me. And actually, here I just found it. So I'm going to go into my code here. I need another style sheet. Right here I have just my one that I had been making from before. So I'm going to go ahead and call this, let's say, CSS-- we'll call it CSS1 for now. And we're going to do one other, CSS2, get CSS. And now I want to pass into this a CSS that I'm going to get dynamically from Pygments itself. COLTON OGDEN: In the URL? DAVID MALAN: Using-- no, it doesn't have to be a URL. COLTON OGDEN: [INAUDIBLE] DAVID MALAN: Yep, I'm going to go ahead and say this-- string equals HTML formatter, get style depths of the, quote unquote, highlight class, using the highlight class. So what does this mean? Let me go ahead and just now go ahead and print out CSS2 and see what we have. And then exit immediately. So I'm asking, hey Pygments, in your HTML formatter class, go ahead and get all of your style definitions. COLTON OGDEN: Specifically for the highlight class only, though. DAVID MALAN: Indeed. So let me go ahead and save this and rerun my code. Oh, and it's printing this out. How best to pluck this out? Let us see here. That's going to go ahead and do that. Let's see, line numbers, text lexer, HTML formatter. All right, just trying to figure out the easiest way to get this stuff. Let's go ahead, and can I print that? Let me try casting it to a string and see what happens, but I'm not sure this is going to work. Let me go ahead and run this. No, I want to see the actual HT-- oh, I'm being stupid here. Let's just do this. Let's just call this my actual string, S. COLTON OGDEN: Oh, yeah. We're wrapping in the constructor for CSS. Right, that makes sense. DAVID MALAN: And let me just do this. OK, so now I have a temporary variable called S. I was overthinking that. My apologies. And let's just see what comes out of this. So now if I rerun my program, ah-- COLTON OGDEN: There we go. DAVID MALAN: There I now have a default style sheet. So this has nothing to do with themes yet. This is just some default colorizations for C. And you can see that everything inside this dot highlight class, which is on my div per the documentation, there's all these one and two character class names that now have these hex codes for color. COLTON OGDEN: So highlight wraps the entire thing, and then it has subclasses, the MH and the MI? DAVID MALAN: In a sense, yes. COLTON OGDEN: Or not subclasses, but like sibling classes. DAVID MALAN: Yeah, this wrapper class highlight you can change to whatever-- dot foo, dot bar. The motivation is so that you can use Pygments in a subset of your document and isolate it to just one div, for instance. All right, so what is the point of this? I can clearly extract from Pygments, by default, all of the styles that I want to apply. But previously I wasn't. So in short, I didn't have Pygments fully talking to WeasyPrint just yet. So let's go ahead and fix this. I'm going to go back to the way it was before, where I had my second CSS object-- CSS1 and CSS2. I'm going to go ahead and get rid of my prints. And I'm going to pass in now CSS1 and CSS2, thereby telling WeasyPrint, here's my CSS, and also, here's Pygments' CSS, please combine them together, just as CSS supports. COLTON OGDEN: Nice. DAVID MALAN: All right, so now I'm going to be really bold here-- double exclams. Save it. Clear the screen. Double crossed fingers. OK, there we go. Two of us. COLTON OGDEN: A lot of crossed fingers. DAVID MALAN: All right, that's a bold claim. Here we go. Oh, my goodness. COLTON OGDEN: That looks nice. DAVID MALAN: Beautiful. Nicer, how about we'll say? COLTON OGDEN: Even have a little bit of a background too. DAVID MALAN: Yeah, which actually I find hideous. So we'll have to fix that. And actually, render50 does fix that. I just think this is stupid that there's a background color when you're printing on paper. COLTON OGDEN: It's a lot of ink, too, over time. DAVID MALAN: But this is easy. And you'll see more on this next week in our CSS stream. You can do like a background dash color property and just change it to white, or get rid of it all together. COLTON OGDEN: Yeah, we'll do something as complicated as background color in our CSS. DAVID MALAN: And so suffice it to say, you could change those styles. You could download different style sheets in order to get slightly different colors, just as we do. But in this case, you're getting some defaults. COLTON OGDEN: You probably have to-- if you have a particular style sheet of your own, you probably have to process it, right? Because it's going to expect the certain classes to exist. DAVID MALAN: Yeah. It does have to-- you do have to define it in terms of those classes. But this is all well documented in Pygments. COLTON OGDEN: OK, that's very satisfying. DAVID MALAN: Any questions we should pluck off here? COLTON OGDEN: A lot of people thinking that it's really cool. DAVID MALAN: Oh, thank you for crossing your fingers too. COLTON OGDEN: And then BELLA is asking what CSS [INAUDIBLE]---- DAVID MALAN: Yeah, just did that. COLTON OGDEN: --style sheets, which we did. MKLOPPENBURG was hypothesizing that comments messed it up. VERONI was saying that it's great that you explain everything in detail. DAVID MALAN: Oh, good. Hope you appreciate when I screw up too. COLTON OGDEN: And that was about where we stopped reading. DAVID MALAN: OK. COLTON OGDEN: So everybody, we've got a POG. So this is what's called a POG or POG champ, very excited. DAVID MALAN: Nice. COLTON OGDEN: And everyone is saying it's really cool. DAVID MALAN: Cool, all right. So we're-- I mean, we're kind of there. Let me propose that this very simple render50 gets us what, like 80% of the way there. Because we've rotated to landscape mode. The motivation for which is just to have room on the paper for a teacher to write or type comments. I don't love the page background, but you know what, let's just get rid of that because it's bothering me already. COLTON OGDEN: You could easily do that by swapping the CSS order, couldn't you, and defining background color in your own properties? DAVID MALAN: We can. Yeah, indeed. So let me go ahead and do that. Let me go ahead and say-- how did we do this here? We stacked this. Let me go ahead and do-- how best to do this cleanly? Let's see what happens when we go ahead and just add it here. But more on this next week too. I'm going to say inherit. Give me the default page color, whatever that is, which is white by convention. Let me go ahead now and rerun the program. No fingers crossed, so no promises. Reload. It's still there, but it's-- Colton's alluding, cascading style sheets is all about cascading. And because my styles came before Pygments', Pygments is overriding mine. So honestly, the simplest fix right now might be just to reverse those. Let Pygments do its thing first and then let me override anything that I want. Let's save that. Reload the PDF after re-rendering it. Go back here. Oh, and it's still here. So why is that? COLTON OGDEN: Strange. Is it a different property? It's not background color. DAVID MALAN: Possibly. Let's see. I can-- background color. Let me change it to-- oh, you know what it must be. Background color must not be applied to the pre. It's probably applied to maybe the div that's surrounding that. So we're going to have to fix this in a slightly different way. COLTON OGDEN: Oh, I see. Yeah, that would probably be the div, yeah. DAVID MALAN: Let me do properties plus equals, and then I'm going to say dot highlight. And then, let ,e go ahead in here and say-- oops-- background color inherit. And actually, let's override the whole background. Inherit whatever the default is. And let's see if this fixes it. Honestly, I'm guessing now. I'm just inferring based on the documentation. We could also just print out all the HTML and figure this out ourselves. We could even output the HTML, save it and an actual HTML file, open it in Chrome, and then tinker with it in Chrome with developer tools. Let's rerun this. Let's go ahead and reload. And voila! COLTON OGDEN: Nice. DAVID MALAN: It's subtle, but now there's no more gray box, which is definitely better for printing sake, and so forth. COLTON OGDEN: [INAUDIBLE],, what are you guys building now? And we just finished, but it's a source code to PDF file. Can be used for a lot of different purposes. DAVID MALAN: But you know, can we show off one more feature? COLTON OGDEN: Yeah, let's do it. DAVID MALAN: It turns out that this highlight function that comes with Pygments has some fancier features too that we make use of in render50. So let me go into my highlight function, which is up here. And in addition to passing in those arguments, my code, my lexer, my HTML formatter, let me also go ahead and say, line numbers equals-- I think I can say true to give myself some line numbers. COLTON OGDEN: Oh, this is cool. Yeah, this is nice. DAVID MALAN: And let me go ahead and save that. And if I did this right, rerun that. OK, got unexpected keyword line numbers. Oh, I'm sorry. That is an argument to the HTML formatter. Line numbers equals true. So that makes sense now, because you're adding the line numbers to the HTML file. So that was just user error. Now let me go ahead and rerun Python. OK. COLTON OGDEN: No errors is always nice. DAVID MALAN: And nice. OK, it's a little weird. There's a little bit of a weird offset here. COLTON OGDEN: That's strange. DAVID MALAN: And in fact, now I really broke things. Notice-- look, I can't scroll. There's no more pages. COLTON OGDEN: Ooh, I have no idea what that is. DAVID MALAN: So this is why I think render50 took me many days, if not weeks, early on, because I ran into headaches like this. So let me propose this. Instead of saying true-- actually, let me show you-- what's the best way to do this? Let me go ahead and show you what is being outputted right now before we actually look at this. COLTON OGDEN: Oh, [INAUDIBLE] was saying we didn't cross fingers this time. DAVID MALAN: Oh, well that also was the bug, yes. Let's go ahead here and just do a sys.exit after printing my markup that results from calling that highlight function. So again, we're just debugging now. Let me go ahead and rerun Python. Whole bunch of stuff on the screen. And let's go up to the top where we can see the first line first, just to make this a little more straightforward. And you can see this. So you see all of the line numbers inside of-- if we go up high enough-- a table. COLTON OGDEN: Oh, is it going to be like floated left or something, and we're advancing this to get it to work? DAVID MALAN: Related in spirit. COLTON OGDEN: It's absolute position or something like that. DAVID MALAN: WeasyPrint print does not handle HTML objects that are taller than the page. I mean, it's as simple as that. And because this file is so long, and because we have so many line numbers, what's happening is the whole second page and beyond are just getting chopped off. And just weird stuff is happening. So there's another way. And thank god that Pygments handles this, because that would've been a big pain to fix. Let me go ahead and change line numbers to be, quote unquote, in line instead. Instead of true. True gave me a table. And that table was too tall. And just because of the library, it breaks on that. So let me go ahead and save this, rerun it, and look at this HTML now. And you'll see now-- COLTON OGDEN: OK, you have a span with the class line number before everything. DAVID MALAN: Exactly. So in this way, you don't have this massively tall table. You still just have individual lines. And WeasyPrint is really good at breaking on lines, really bad at breaking on really tall tables. COLTON OGDEN: Yeah, apparently. DAVID MALAN: So let's go ahead and not just print that, of course. So let's get rid of my debugging lines. Let's go ahead and let it finish its thing. Let's be really bold now, three exclams. I'm really done now. And go ahead and run this. Oh, three fingers, quick. COLTON OGDEN: I've got them. DAVID MALAN: OK. And now let's open the PDF. Now, that's pretty good. COLTON OGDEN: That looks great. OK, cool. That must have been satisfying when you discovered that. Did that cause you a lot of trouble when you first ran into it? DAVID MALAN: If by trouble you mean tears, yeah, absolutely. Because that's the kind of thing where you feel like you're doing everything right. You've read the documentation, and it's still not working. And so honestly-- but how did I go about doing this? It's been a while, but I'm pretty sure I went to WeasyPrint's GitHub repo. And honestly, I started searching through the issues, open and closed, to see if other people have reported this bug. And I do believe this is well documented. Just doesn't handle really tall objects like that. So you have to work around it. COLTON OGDEN: I don't know when you would ever want to use the other form instead of this one. This seems just objectively better in every way. DAVID MALAN: Well, web pages. So for instance, let me think about a context for this. COLTON OGDEN: [INAUDIBLE] is maybe. DAVID MALAN: Well, so in web-- actually, I can give you a very specific scenario. When you're rendering code, syntax highlighted in a web page, and you want the human to be able to highlight the code, you want to use a table so that all of your numbers are in separate TDs, so that when you're clicking and dragging you have the ability to highlight just the code and not the numbers. Because in this version, these are just spans all together. There is no way to highlight just the code. Now, we claim that in a PDF, not a big deal. You're printing it, you're writing on it. You're not highlighting and copying. After all, you have the original source code. But on a web page, like this would be darn annoying. COLTON OGDEN: I understand now. DAVID MALAN: GitHub had to deal with the same thing with their UI as well. COLTON OGDEN: But in that case, I guess, does it have the same issue in the web if you use the table? Does it have the-- does it truncate your line numbers if you do it pages long-- or a web page that's certain wide-- DAVID MALAN: No, HTML is fine. It's the PDF, because WeasyPrint and libraries like it have to do a bunch of arithmetic to figure out, for a given letter size page, where do you chop it? What does it mean, though, to chop a table right there? It just gets confused. COLTON OGDEN: Real curious here, Brenda was saying, if by trouble you mean tears-- quoted that. And then MKLOPPENBURG followed up by David showing his soft side right there. DAVID MALAN: Yeah. Oh, that's happened. Late at night, 4:00 AM, you'll see me curled up in a ball on the floor. So here we have a very simple render50. It's not that feature full. So why don't we, in our remaining time, take a look at the real render50 for just a couple minutes and see what it does differently. COLTON OGDEN: Yeah, let's do it. DAVID MALAN: All right, so I'm going to go over to GitHub here where we have the source code for actual render50. And you'll see that there's a few files in this repo. GetIgnore, which is get specific, just prevents us from checking in specific files. Travis.yml, which is a CI/CD technology, Continuous Integration/Continuous Deployment technology, a third party cloud service that we use to automatically deploy new versions of render50. Readme is just a readme, of course. Setup.py I mentioned earlier, has to do with Python packaging so that when you install render50 with PIP, it knows to pull in those dependencies. And then the real script is right here, render50 itself. COLTON OGDEN: Nice. And you don't even have an extension on it just so that you can run it at the command prompt like a Unix application. DAVID MALAN: With a shebang, yeah. This first line here, a hash bang or shebang actually says, please use Python 3 to execute this file. COLTON OGDEN: Nice. DAVID MALAN: And yes, hello to [INAUDIBLE].. Sorry for mispronouncing. Best of luck with Pset 5. And here we have render50. Feel free to take a look at this in more slow detail at your leisure. But you'll see that we're importing a whole bunch of libraries to solve different problems that we didn't even solve today. But there's some familiar ones, like here, for instance, is all the Pygments stuff for importing lexers. Colton mentioned that you can guess the lexer. So I don't even have C lexer or Python lexer. I have all the lectures assembled here. COLTON OGDEN: Yeah, because you don't know, somebody might want to use this for a language we don't teach or we haven't used before. DAVID MALAN: Yeah, and Pygments supports dozens, if not hundreds, of languages. So render50 actually supports dozens or hundreds as well. There's another PDF library in here-- fun fact-- for rendering PDFs side by side. One here, one here on the same page. We actually use a separate PDF library that Colton actually figured out, because the goal is to essentially render two PDFs and then concatenate them together so that each one, in the US, would be 5 and 1/2 inches wide. And then you jam them together like this. So it was kind of a pain. Can't do that alone with WeasyPrint. Can't really do it well with HTML. So we actually, for render50, create two PDFs if you do side by side mode. And then combine them. COLTON OGDEN: I'm just now remembering adding that feature. I had totally forgotten to this point. DAVID MALAN: Yeah, so you know this code. COLTON OGDEN: A little bit. DAVID MALAN: We have a whole bunch of stuff here for error checking and line wrapping. We use Beautiful Soup too, to extract information as needed from some HTML if we want. And a few other features like argument parser to automate the process of dealing with sys.argv, which I just very easily did manually. Here, for instance, we're requiring Python 3.6, just because that's what we do in CS50 now so that we can assume certain language features. COLTON OGDEN: We're up to 7, though, now. DAVID MALAN: We are, but we don't really use any of its features. COLTON OGDEN: 3.7 doesn't have any compelling features for us. DAVID MALAN: Get the version-- this is just so that if you do -v, render50 can tell you what version is installed. We have some other nice things. Here's our main function. We handle Control-C, so if you want to abort if it's taking too long, that'll handle that cleanly. Here's all the code we have for parsing command line arguments. And you'll notice there's a bunch of features we didn't even talk about today. We have a browser mode with render50 so that you can go fetch, just like WeasyPrint can, a URL and download it. We use it for downloading GitHub code or Gist, if you want to render a PDF of a gist. You can turn off color if you just don't want it, and if on your black and white printer, grayscale isn't very nice. You can include multiple files or-- you can include multiple files so as to render multiple files at once. You can specify the size of the page, depending on your country. We default, I think, to letter just because we are biased in the US. But you can override that in any country. And then -y, like with diff, is a side by side mode, which similarly lets you do two side by side. COLTON OGDEN: Best feature in the repo. Just going to say that. DAVID MALAN: Indeed, well, why don't we show that off. So let's go into now-- COLTON OGDEN: I think we're good [INAUDIBLE] time here. DAVID MALAN: No, we're not. So let's go ahead and do this, and do render50, the real render50, which is installed in CS50 CLI for you. I'm just going to go and say this, and cs50.c. And let's just do the same file twice side by side. And then -O. We'll call it colton.pdf because he made this possible. Enter. It's rendering cs50.c. Rendering cs50.c. So you'll notice I added some nice pretty printing of progress messages. Let's go into my Downloads folder. Colton.pdf. We'll go ahead and open it with Preview. And voila. COLTON OGDEN: It gets ugly a little bit with the-- because the comments break so much when it's that narrow. But-- DAVID MALAN: But we would do this not-- to be clear-- to render the same file side by side. It's usually a file that's been submitted as homework, and then another file that that student's work is awfully similar to. And we don't use this so much internally. This is when we refer to other administrators at the university, if they-- often as less technical folks who wouldn't be as comfortable using a command line software that we provide and such. Can just literally print this out or on their own Macs or PCs just open it up. And even though it's not perfect, it fits on a traditional page. And therefore, just has a nice compatibility with the target audience. COLTON OGDEN: Easy to print and whatnot. DAVID MALAN: But this is, essentially to Colton's points to his feature, two PDFs that we've just concatenated together at the right size. COLTON OGDEN: Yeah, makes it pretty easy, the py PDF 2 library. DAVID MALAN: Indeed. And you'll notice a couple other things, if I may. If we use the real render50, and let's execute it not just on two files, but just instead on one, outputting cs50.pdf this time. You'll notice that we have features like, do you want to overwrite the existing file, just as a user friendly feature. And telling you, again in green, what you've done. And if I go and open cs50.pdf, this is slightly cleaner, I think, than the ones we created today for the following reasons. So one, I found it very obnoxious how big and black the line numbers were. They were kind of a distraction when you really care about the code. So render50 makes them gray. We have this little border at the top, a line with the file name, just to make it obvious, honestly, for people like me when I'm trying to figure out what file am I looking at. COLTON OGDEN: Yeah, because in lectures for example, too, you mentioned printing out a list of files, you'll glob like an entire list of maybe C or HTML files that you want to print onto your thing. And then you staple them all together. It's easy to lose track of what you're doing. DAVID MALAN: Indeed. And we actually did address that highlighting feature. Well, you'll see this still happens in the PDFs here. But notice we do have a second page. So we are using the inline line numbers to avoid that same issue. But beyond that, we pretty much implemented render50 entirely. We just don't have additional support for web pages and such. But we can do that. Let me go into, say, how about CS50's Python library, called Python CS50. Let me go into the source directory and grab cs50.py. COLTON OGDEN: Show those to Kareem [INAUDIBLE],, by the way, [INAUDIBLE].. DAVID MALAN: Kareem is one of our big contributors here. You'll see that this on GitHub is just the source code now for our library for Python, which has GetString and GetInt and GetFloat, and a couple of others. Let me go ahead now and just copy this URL. And if we're not getting too greedy, let's go ahead and render a whole URL. Oops-- let's go ahead and render a whole URL. COLTON OGDEN: That's weird. DAVID MALAN: My terminal is messed up. Let's see if we can do that again. render50 of that whole URL. It's ugly, but that's just-- COLTON OGDEN: It's like an NES type bug. DAVID MALAN: Yeah, it's a Docker bug, actually, in this case. Let me go ahead-- COLTON OGDEN: It looks like it in NES, you would like-- tiles from different maps would like move over to the other side of the screen. Yeah, it was a bit weird. DAVID MALAN: But the code is hopefully still working. Yes, let's overwrite it. And notice it's grabbing that URL via HTTP. And if I open this PDF now, it looks similar, but you'll notice we've rendered an actual URL from the web. And that URL is even clickable too. So this is all thanks to HTML and CSS. COLTON OGDEN: That's awesome. I love it. DAVID MALAN: So that, then, is render50. And I think what's cool about this is, honestly, not the specific lines and the specific use case, because not many people in the world have a need for syntax highlighted PDFs. But I think it's this combination of technologies, like you understand a bit of HTML, a bit of CSS. You need a library for generating the HTML, the syntax highlighting. You need to understand CSS to stylize it the way you want. And then you need a library for turning that text, or HTML and CSS, into PDF. And that's what WeasyPrint does for us. COLTON OGDEN: Yeah, I think it's great. Learning the basics of Python is one thing, but actually going and using it to accomplish something. And something tangible too. This is something that's physical. I think it's great. Yeah, it's awesome. DAVID MALAN: So if you'd like to play along at home or learn further, again, the first URL we started with today, we can type it one more time for new folks. cs50.readthedocs.io/render50. You can install it there on your own Mac or PC or Linux box, so long as you have Python and PIP installed. There's some documentation there as to how it works. And then you can follow the link there at the bottom source code. Go to the GitHub repo if you want just kind of look through. And suggest if you find bugs, or missing features, or poor design, by all means, submit a pull request if you know how on GitHub. But that, then, is render50. COLTON OGDEN: That was awesome. Thanks so much for coming in and showing it. DAVID MALAN: Thanks for hosting this. Really nice to see everyone out there. COLTON OGDEN: Yeah, absolutely. There's a few comments that we didn't grab. DAVID MALAN: Sure, let's plus those off. 1:00 AM in Nepal. Wow, OK. Thanks for staying awake. COLTON OGDEN: [INAUDIBLE]. And then [INAUDIBLE] was very happy. Gave us a holiday present. Very cute. And then [INAUDIBLE] was saying, used to find cheating. And you're referring to the two PDFs side to side. DAVID MALAN: Yeah, not so much used to find it. We use other software to actually find the similarities. We then use render50 to show the similarities. COLTON OGDEN: Right. Nice. If anybody has any last questions, definitely let us know. But it's been about a little over an hour and a half, actually. It's been closer to two hours. It cut after the first drop in the stream. So we've actually been streaming for, I think, an hour and 50 or something like that. What time is it? 2:40. Hour and 40, I guess. DAVID MALAN: Yeah. COLTON OGDEN: Yeah, let us know if you have any questions. Otherwise, next week, so you and I are going to be doing the code review on [INAUDIBLE].. DAVID MALAN: Yeah, looking forward to that. COLTON OGDEN: That'll be awesome. DAVID MALAN: Send us your submissions. Do we have that URL just in case? COLTON OGDEN: So bit.ly/cs50twitchcodereview. DAVID MALAN: OK. That's our long URL. COLTON OGDEN: Yeah, I couldn't find a good short one that actually made sense. DAVID MALAN: That's OK. COLTON OGDEN: But if you go there, you'll be able to submit some source code that you have on GitHub, or a gist. I mean, both of them, you'll find on GitHub. Either a repo or a gist. We'll take a look at it. It's catered towards beginner/intermediate programmers more so than somebody who's maybe working on a massive code based in a more professional environment. But even if you are, we're happy to take a look at it. But it's more of a style and design grade. We're not going to be doing bug testing or the like on camera. But yeah, definitely let us know if you want some feedback on your source code, and we'll give it to you next Friday. DAVID MALAN: Sure. And there was-- COLTON OGDEN: [INAUDIBLE] thereafter. DAVID MALAN: --one other question, very library specific. [INAUDIBLE] asked, is does GetString in cs50.c, which we've been printing today as a PDF, free up allocated memory at the end? Yes, we actually take advantage of some more modern features of compilers to actually do garbage collection of memory so that students do not need to know about free in the first few weeks of CS50. COLTON OGDEN: Cool, cool. BELLA'S saying, thank you, David and Colton. Stream was awesome. Thanks BELLA for joining. And thanks BARTLED for the follow. And HEXADECIMAL. I like that. I like the 16 in the hexadecimal there. That's awesome. DAVID MALAN: That is cool. COLTON OGDEN: [INAUDIBLE] not just on the turntable. Nice, OK. Thinking [INAUDIBLE] referred to DJing. DAVID MALAN: Colton does some DJing too. COLTON OGDEN: A little bit. It's been a little while. Thanks for the awesome stream says [INAUDIBLE].. Thank you for tuning in. Thanks everybody who tuned in today. We had very consistent and good viewership that ramped up. R3MOTE, thanks for the stream. Awesome. Oh yeah, and next week-- so we have the code review. On Monday, I'll be doing Minesweeper part 2, so I'll finish up Minesweeper. We did a good solid 50% to 60% of it on the last stream. Kareem's going to do Flask next week. And shoot, I'm blanking on the other stream. DAVID MALAN: CSS. COLTON OGDEN: Yes, I will be doing CSS for next week as well. OK. And these all go on the Facebook page. You'll get event notifications if you're on that. So go to facebook.com/cs50 to keep up with all of our Facebook and live video. And cross your fingers that Facebook doesn't keep blocking our videos after today. I have to figure out what caused that, caused the dropped stream. If you're watching this on YouTube, follow us on Twitch, twitch.tv/cs50tv. And subscribe to our YouTube channel as well. Anything else? DAVID MALAN: No, I think that's it. Thanks so much for having me. COLTON OGDEN: Cool, I'm going to switch us back here. DAVID MALAN: Nice, this is-- oop, there we go. COLTON OGDEN: Let me make sure we didn't miss anything here. [INAUDIBLE] will be on the Facebook soon as well? Yes, MKLOPPENBURG [INAUDIBLE] will be on Facebook as well. I will get all of them posted today, actually. And then this video will also go posted onto-- oh, nice. This is-- I like that. Very nice. What's the name of the woman who does Wheel of Fortune? DAVID MALAN: Vanna White? COLTON OGDEN: Yeah. DAVID MALAN: Vanna White, there we go. But all the letters are already there, although my head is perfectly over the zero, so this is CS5. Today will be a more introductory stream. COLTON OGDEN: OK, time for breakfast. Have a great night everyone, says Brenda. Thanks for joining, Brenda. DAVID MALAN: Take care, Brenda. Bye everyone. COLTON OGDEN: All right, everybody. Have a great weekend, a great rest of your day. And we'll see you next Monday for some Minesweeper. This was CS50 on Twitch.
B1 中級 用PYTHON生成PDF - CS50在Twitch上,EP。29 (GENERATING PDFS WITH PYTHON - CS50 on Twitch, EP. 29) 7 1 林宜悉 發佈於 2021 年 01 月 14 日 更多分享 分享 收藏 回報 影片單字