├── .gitignore ├── LICENSE ├── README.md ├── examples-as-images.rkt ├── examples.rkt ├── image-pack-unpack.rkt ├── image-reader.rkt ├── packing-example.rkt └── test-images.rkt /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | compiled/ 3 | /doc/ 4 | *~ 5 | *.bak 6 | \#* 7 | .\#* 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Shriram Krishnamurthi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # What 2 | 3 | Racket enables you to embed images in the document source. The moment you do, however, the file becomes a different format (WXME). In particular, this format is effectively binary, which means it doesn't work well with tools like `grep`, git, etc. 4 | 5 | A natural solution is to store images outside the file. This is a perfectly good solution, but *sometimes* it's useful to have a single self-contained file with all of its "assets". 6 | 7 | This library provides a "reader extension" to address this problem. Using `#reader "image-reader.rkt" …` makes the subsequent expression (in `…`) use this special reader extension. The reader extension is invoked using the `µ` reader key (on a Mac, you can easily type this as Option-m). The `µ` must be followed by a uuencoded byte-string following a certain format. The reader then automatically turns this into an image value. 8 | 9 | The best way to understand the difference is to look at these two files side-by-side: 10 | 11 | * [examples](examples.rkt) 12 | * [examples as images](examples-as-images.rkt) 13 | 14 | The latter (which you can't meaningfully view outside DrRacket) has embedded images visible *as images* in DrRacket. The former has the same images embedded using this reader. The images are not meaningful without rendering, but *everything else* in the file is readable by all our usual tools (including outside DrRacket). It's worth noting that an embedded image is a (byte-)string, and hence can be skipped/copied/etc. using standard s-expression editor operations. 15 | 16 | As a lovely illustration, when I removed some unnecessary code in the latter file between (commit 8c5573a), all that git could say was: 17 | ``` 18 | > git diff 19 | diff --git a/examples-as-images.rkt b/examples-as-images.rkt 20 | index 939171e..24af6de 100644 21 | Binary files a/examples-as-images.rkt and b/examples-as-images.rkt differ 22 | ``` 23 | 24 | # How 25 | 26 | There are two parts to using this library: embedding images and using them. 27 | 28 | * To use them, follow the examples in [the examples file](examples.rkt). 29 | 30 | * To embed them, you could try to understand the format specified below. However, it would be a lot simpler to run 31 | 32 | ``` 33 | (require "image-pack-unpack.rkt") 34 | (pack ) 35 | ``` 36 | 37 | This will produce a byte-string. This is what you paste into your source file. For instance, you might have 38 | 39 | ``` 40 | (require 2htdp/image) 41 | (require "image-pack-unpack.rkt") 42 | (pack (circle 1 "solid" "red")) 43 | ``` 44 | This will produce the following byte-string: 45 | ``` 46 | #"AgL/AADF/wAAw/8AAMP/AADC" 47 | ``` 48 | You only need the above program to produce the byte string. 49 | 50 | You can then embed this byte string in a source program (which is presumably a completely different module), as follows: 51 | 52 | ``` 53 | #reader "image-reader.rkt" µ#"AgL/AADF/wAAw/8AAMP/AADC" 54 | ``` 55 | (Note the `µ`!) When you run this, you will get a tiny red dot! 56 | 57 | Of course, in the example above, we generated the little dot image programmatically; at that point we could just use the program instead of embedding its output (unless it would take a very long time to run…). 58 | 59 | Consider instead [this file](packing-example.rkt)–which, again, you *can't* usefully view except inside DrRacket, that being the point of this library—which runs `pack` on an embedded instance of the Racket "running man" image to produce a byte-string. This is how we obtained the byte-string to embedded in [the examples file](examples.rkt). Once we've embedded the byte-string, we no longer need the original program (except for future modifications): all the information is in the byte-string. 60 | 61 | # Errors 62 | 63 | The error-handling code is crap. I ran out of time to understand all the ins-and-outs of `read` vs `read-syntax` errors and how to make everything pretty. People are welcome to fix that (and to correspondingly improve the tests). 64 | 65 | # Extensions 66 | 67 | - This [packer](image-pack-unpack.rkt) produces single-line output. This has the virtue of not producing a large block of text, at the cost of making extremely wide lines. This was an intentional decision, but it could cause problems in some contexts. It would be nice to extend the packer to take flags that let the user determine whether they want wide or tall packed text. 68 | 69 | - It appears there's a general trend towards [base64 over uunencode](https://retrocomputing.stackexchange.com/questions/3019/why-did-base64-win-against-uuencode) (HT ednl). So a serious version of this should probably use base64 instead. 70 | 71 | - There is nothing *image*-specific about this. It could embed *any* binary datum. This particular pack/unpack duo is image-centric: images have a height and width, and this is recorded in the byte-string. But other entities (like Comment Boxes) also cause DrRacket to save in WMXE format. So a more general version would need a tag indicating what kind of datum is being saved. 72 | -------------------------------------------------------------------------------- /examples-as-images.rkt: -------------------------------------------------------------------------------- 1 | #reader(lib"read.ss""wxme")WXME0111 ## 2 | #| 3 | This file uses the GRacket editor format. 4 | Open this file in DrRacket version 8.15 or later to read it. 5 | 6 | Most likely, it was created by saving a program in DrRacket, 7 | and it probably contains a program with non-text elements 8 | (such as images or comment boxes). 9 | 10 | http://racket-lang.org/ 11 | |# 12 | 34 7 #"wxtext\0" 13 | 3 1 6 #"wxtab\0" 14 | 1 1 8 #"wximage\0" 15 | 2 0 8 #"wxmedia\0" 16 | 4 1 34 #"(lib \"syntax-browser.ss\" \"mrlib\")\0" 17 | 1 0 36 #"(lib \"cache-image-snip.ss\" \"mrlib\")\0" 18 | 1 0 68 19 | (0 68 20 | ((lib "image-core.ss" "mrlib") (lib "image-core-wxme.rkt" "mrlib")) 21 | ) 1 0 16 #"drscheme:number\0" 22 | 3 0 44 #"(lib \"number-snip.ss\" \"drscheme\" \"private\")\0" 23 | 1 0 36 #"(lib \"comment-snip.ss\" \"framework\")\0" 24 | 1 0 79 25 | (1 79 26 | ((lib "srcloc-snip.rkt" "framework") (lib "wxme-srcloc-snip.rkt" "framework")) 27 | ) 1 0 93 28 | (2 93 29 | ((lib "collapsed-snipclass.ss" "framework") (lib "collapsed-snipclass-wxme.ss" "framework")) 30 | ) 0 0 43 #"(lib \"collapsed-snipclass.ss\" \"framework\")\0" 31 | 0 0 19 #"drscheme:sexp-snip\0" 32 | 0 0 29 #"drscheme:bindings-snipclass%\0" 33 | 1 0 101 34 | (3 101 35 | ((lib "ellipsis-snip.rkt" "drracket" "private") (lib "ellipsis-snip-wxme.rkt" "drracket" "private")) 36 | ) 2 0 88 37 | (4 88 38 | ((lib "pict-snip.rkt" "drracket" "private") (lib "pict-snip.rkt" "drracket" "private")) 39 | ) 1 0 55 40 | #"((lib \"snip.rkt\" \"pict\") (lib \"snip-wxme.rkt\" \"pict\"))\0" 41 | 1 0 34 #"(lib \"bullet-snip.rkt\" \"browser\")\0" 42 | 0 0 25 #"(lib \"matrix.ss\" \"htdp\")\0" 43 | 1 0 22 #"drscheme:lambda-snip%\0" 44 | 1 0 29 #"drclickable-string-snipclass\0" 45 | 0 0 26 #"drracket:spacer-snipclass\0" 46 | 0 0 57 47 | #"(lib \"hrule-snip.rkt\" \"macro-debugger\" \"syntax-browser\")\0" 48 | 1 0 26 #"drscheme:pict-value-snip%\0" 49 | 0 0 45 #"(lib \"image-snipr.ss\" \"slideshow\" \"private\")\0" 50 | 1 0 38 #"(lib \"pict-snipclass.ss\" \"slideshow\")\0" 51 | 2 0 55 #"(lib \"vertical-separator-snip.ss\" \"stepper\" \"private\")\0" 52 | 1 0 18 #"drscheme:xml-snip\0" 53 | 1 0 31 #"(lib \"xml-snipclass.ss\" \"xml\")\0" 54 | 1 0 21 #"drscheme:scheme-snip\0" 55 | 2 0 34 #"(lib \"scheme-snipclass.ss\" \"xml\")\0" 56 | 1 0 10 #"text-box%\0" 57 | 1 0 32 #"(lib \"text-snipclass.ss\" \"xml\")\0" 58 | 1 0 1 6 #"wxloc\0" 59 | 0 0 66 0 1 #"\0" 60 | 0 75 1 #"\0" 61 | 0 12 90 -1 90 -1 3 -1 0 1 0 1 0 0 0 0 0 1.0 0 0 0 1.0 0 0 0 0.0 255 255 62 | 255 0.0 1 -1 0 9 #"Standard\0" 63 | 0 75 6 #"Menlo\0" 64 | 0 19 90 -1 90 -1 3 -1 0 1 0 1 0 0 0 0 0 1.0 0 0 0 1.0 0 0 0 0.0 255 255 65 | 255 0.0 1 -1 2 1 #"\0" 66 | 0 -1 1 #"\0" 67 | 1 0 -1 -1 -1 -1 -1 -1 0 0 0 0 0 0 1 1 1 1.0 1 1 1 1.0 0 0 0 0.0 0 0 0 68 | 0.0 -1 -1 2 24 #"framework:default-color\0" 69 | 0 -1 1 #"\0" 70 | 1 0 -1 -1 -1 -1 -1 -1 0 0 0 0 0 0 0 0 0 0.0 1 1 1 1.0 0 0 0 1.0 0 0 0 71 | 0.0 -1 -1 2 1 #"\0" 72 | 0 -1 1 #"\0" 73 | 1 0 -1 -1 -1 -1 -1 -1 0 0 0 0 0 0 0 0 0 0.0 1 1 1 1.0 150 0 150 1.0 0 0 74 | 0 0.0 -1 -1 2 15 #"text:ports out\0" 75 | 0 -1 1 #"\0" 76 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 150 0 150 1.0 0 0 77 | 0 0.0 -1 -1 2 1 #"\0" 78 | 0 -1 1 #"\0" 79 | 1.0 0 -1 -1 93 -1 -1 -1 0 0 0 0 0 0 0 0 0 0.0 1.0 1.0 1.0 1.0 255 0 0 80 | 1.0 0 0 0 0.0 -1 -1 2 15 #"text:ports err\0" 81 | 0 -1 1 #"\0" 82 | 1 0 -1 -1 93 -1 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 255 0 0 1.0 0 0 0 83 | 0.0 -1 -1 2 1 #"\0" 84 | 0 -1 1 #"\0" 85 | 1 0 -1 -1 -1 -1 -1 -1 0 0 0 0 0 0 0 0 0 0.0 1 1 1 1.0 0 0 175 1.0 0 0 0 86 | 0.0 -1 -1 2 17 #"text:ports value\0" 87 | 0 -1 1 #"\0" 88 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 0 0 175 1.0 0 0 0 89 | 0.0 -1 -1 2 1 #"\0" 90 | 0 -1 1 #"\0" 91 | 1.0 0 92 -1 -1 -1 -1 -1 0 0 0 0 0 0 0 0 0 0.0 1.0 1.0 1.0 1.0 34 139 34 92 | 1.0 0 0 0 0.0 -1 -1 2 27 #"Matching Parenthesis Style\0" 93 | 0 -1 1 #"\0" 94 | 1.0 0 92 -1 -1 -1 -1 -1 0 0 0 0 0 0 0 0 0 0.0 1.0 1.0 1.0 1.0 34 139 34 95 | 1.0 0 0 0 0.0 -1 -1 2 1 #"\0" 96 | 0 -1 1 #"\0" 97 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 38 38 128 1.0 0 0 98 | 0 0.0 -1 -1 2 37 #"framework:syntax-color:scheme:symbol\0" 99 | 0 -1 1 #"\0" 100 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 38 38 128 1.0 0 0 101 | 0 0.0 -1 -1 2 38 #"framework:syntax-color:scheme:keyword\0" 102 | 0 -1 1 #"\0" 103 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 38 38 128 1.0 0 0 104 | 0 0.0 -1 -1 2 1 #"\0" 105 | 0 -1 1 #"\0" 106 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 194 116 31 1.0 0 0 107 | 0 0.0 -1 -1 2 38 #"framework:syntax-color:scheme:comment\0" 108 | 0 -1 1 #"\0" 109 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 194 116 31 1.0 0 0 110 | 0 0.0 -1 -1 2 1 #"\0" 111 | 0 -1 1 #"\0" 112 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 41 128 38 1.0 0 0 113 | 0 0.0 -1 -1 2 37 #"framework:syntax-color:scheme:string\0" 114 | 0 -1 1 #"\0" 115 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 41 128 38 1.0 0 0 116 | 0 0.0 -1 -1 2 35 #"framework:syntax-color:scheme:text\0" 117 | 0 -1 1 #"\0" 118 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 41 128 38 1.0 0 0 119 | 0 0.0 -1 -1 2 39 #"framework:syntax-color:scheme:constant\0" 120 | 0 -1 1 #"\0" 121 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 41 128 38 1.0 0 0 122 | 0 0.0 -1 -1 2 1 #"\0" 123 | 0 -1 1 #"\0" 124 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 132 60 36 1.0 0 0 125 | 0 0.0 -1 -1 2 49 #"framework:syntax-color:scheme:hash-colon-keyword\0" 126 | 0 -1 1 #"\0" 127 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 132 60 36 1.0 0 0 128 | 0 0.0 -1 -1 2 42 #"framework:syntax-color:scheme:parenthesis\0" 129 | 0 -1 1 #"\0" 130 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 132 60 36 1.0 0 0 131 | 0 0.0 -1 -1 2 1 #"\0" 132 | 0 -1 1 #"\0" 133 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 255 0 0 1.0 0 0 0 134 | 0.0 -1 -1 2 36 #"framework:syntax-color:scheme:error\0" 135 | 0 -1 1 #"\0" 136 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 255 0 0 1.0 0 0 0 137 | 0.0 -1 -1 2 1 #"\0" 138 | 0 -1 1 #"\0" 139 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 0 0 0 1.0 0 0 0 140 | 0.0 -1 -1 2 36 #"framework:syntax-color:scheme:other\0" 141 | 0 -1 1 #"\0" 142 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 0 0 0 1.0 0 0 0 143 | 0.0 -1 -1 2 16 #"Misspelled Text\0" 144 | 0 -1 1 #"\0" 145 | 1 0 -1 -1 -1 -1 -1 -1 0 0 0 0 0 0 0 0 0 0.0 1 1 1 1.0 0 0 0 1.0 0 0 0 146 | 0.0 -1 -1 2 1 #"\0" 147 | 0 -1 1 #"\0" 148 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 81 112 203 1.0 0 0 149 | 0 0.0 -1 -1 2 38 #"drracket:check-syntax:lexically-bound\0" 150 | 0 -1 1 #"\0" 151 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 81 112 203 1.0 0 0 152 | 0 0.0 -1 -1 2 1 #"\0" 153 | 0 -1 1 #"\0" 154 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 178 34 34 1.0 0 0 155 | 0 0.0 -1 -1 2 28 #"drracket:check-syntax:set!d\0" 156 | 0 -1 1 #"\0" 157 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 178 34 34 1.0 0 0 158 | 0 0.0 -1 -1 2 37 #"drracket:check-syntax:unused-require\0" 159 | 0 -1 1 #"\0" 160 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 255 0 0 1.0 0 0 0 161 | 0.0 -1 -1 2 36 #"drracket:check-syntax:free-variable\0" 162 | 0 -1 1 #"\0" 163 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 255 0 0 1.0 0 0 0 164 | 0.0 -1 -1 2 1 #"\0" 165 | 0 -1 1 #"\0" 166 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 68 0 203 1.0 0 0 0 167 | 0.0 -1 -1 2 31 #"drracket:check-syntax:imported\0" 168 | 0 -1 1 #"\0" 169 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 68 0 203 1.0 0 0 0 170 | 0.0 -1 -1 2 47 #"drracket:check-syntax:my-obligation-style-pref\0" 171 | 0 -1 1 #"\0" 172 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 178 34 34 1.0 0 0 173 | 0 0.0 -1 -1 2 1 #"\0" 174 | 0 -1 1 #"\0" 175 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 0 116 0 1.0 0 0 0 176 | 0.0 -1 -1 2 50 #"drracket:check-syntax:their-obligation-style-pref\0" 177 | 0 -1 1 #"\0" 178 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 0 116 0 1.0 0 0 0 179 | 0.0 -1 -1 2 48 #"drracket:check-syntax:unk-obligation-style-pref\0" 180 | 0 -1 1 #"\0" 181 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 0 0 0 1.0 0 0 0 182 | 0.0 -1 -1 2 1 #"\0" 183 | 0 -1 1 #"\0" 184 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 139 142 28 1.0 0 0 185 | 0 0.0 -1 -1 2 49 #"drracket:check-syntax:both-obligation-style-pref\0" 186 | 0 -1 1 #"\0" 187 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 139 142 28 1.0 0 0 188 | 0 0.0 -1 -1 2 26 #"plt:htdp:test-coverage-on\0" 189 | 0 -1 1 #"\0" 190 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 0 0 0 1.0 0 0 0 191 | 0.0 -1 -1 2 1 #"\0" 192 | 0 -1 1 #"\0" 193 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 1 0 0 0 0.0 0 0 0 0.0 255 165 0 1.0 0 0 194 | 0 1.0 -1 -1 2 27 #"plt:htdp:test-coverage-off\0" 195 | 0 -1 1 #"\0" 196 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 1 0 0 0 0.0 0 0 0 0.0 255 165 0 1.0 0 0 197 | 0 1.0 -1 -1 4 1 #"\0" 198 | 0 70 1 #"\0" 199 | 1.0 0 -1 -1 -1 -1 -1 -1 0 0 0 0 0 0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 0 0 200 | 0 0.0 0 0 0 0.0 -1 -1 4 4 #"XML\0" 201 | 0 70 1 #"\0" 202 | 1.0 0 -1 -1 -1 -1 -1 -1 0 0 0 0 0 0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 0 0 203 | 0 0.0 0 0 0 0.0 -1 -1 2 1 #"\0" 204 | 0 -1 1 #"\0" 205 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 0 100 0 1.0 0 0 0 206 | 0.0 -1 -1 2 61 207 | #"drracket:language name and memory use at top of interactions\0" 208 | 0 -1 1 #"\0" 209 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 0 100 0 1.0 0 0 0 210 | 0.0 -1 -1 2 37 #"plt:module-language:test-coverage-on\0" 211 | 0 -1 1 #"\0" 212 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0 0 0 0.0 1 1 1 1.0 0 0 0 1.0 0 0 0 213 | 0.0 -1 -1 2 38 #"plt:module-language:test-coverage-off\0" 214 | 0 -1 1 #"\0" 215 | 1 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 1 0 0 0 0.0 0 0 0 0.0 255 165 0 1.0 0 0 216 | 0 1.0 -1 -1 0 36 #"mrlib/syntax-browser:subtitle-color\0" 217 | 0 -1 1 #"\0" 218 | 1 0 -1 -1 -1 -1 -1 -1 0 0 0 0 0 1 0 0 0 0.0 0 0 0 0.0 36 36 140 1.0 255 219 | 255 255 1.0 -1 -1 0 42 #"mrlib/syntax-browser:focused-syntax-color\0" 220 | 0 -1 1 #"\0" 221 | 1 0 -1 -1 -1 -1 -1 -1 0 0 0 0 0 1 0 0 0 0.0 0 0 0 0.0 34 139 34 1.0 255 222 | 255 255 1.0 -1 -1 0 1 #"\0" 223 | 0 75 6 #"Menlo\0" 224 | 0.0 19 90 -1 90 -1 3 -1 0 1 0 1 0 0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0 0 0 225 | 0 0.0 255 255 255 0.0 1 -1 2 1 #"\0" 226 | 0 -1 1 #"\0" 227 | 1.0 0 -1 -1 -1 -1 -1 -1 0 0 0 0 0 0 0.0 0.0 0.0 0.0 1.0 1.0 1.0 1.0 0 0 228 | 0 1.0 0 0 0 0.0 -1 -1 2 1 #"\0" 229 | 0 -1 1 #"\0" 230 | 1.0 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0.0 0.0 0.0 0.0 1.0 1.0 1.0 1.0 150 231 | 0 150 1.0 0 0 0 0.0 -1 -1 2 1 #"\0" 232 | 0 -1 1 #"\0" 233 | 1.0 0 -1 -1 93 -1 -1 -1 0 1 0 0 0 0 0.0 0.0 0.0 0.0 1.0 1.0 1.0 1.0 255 234 | 0 0 1.0 0 0 0 0.0 -1 -1 2 1 #"\0" 235 | 0 -1 1 #"\0" 236 | 1.0 0 -1 -1 -1 93 -1 -1 0 1 0 0 0 0 0.0 0.0 0.0 0.0 1.0 1.0 1.0 1.0 0 0 237 | 175 1.0 0 0 0 0.0 -1 -1 0 1 #"\0" 238 | 0 -1 1 #"\0" 239 | 1.0 0 -1 -1 -1 -1 -1 -1 0 0 0 0 0 1 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 36 240 | 36 140 1.0 255 255 255 1.0 -1 -1 0 1 #"\0" 241 | 0 -1 1 #"\0" 242 | 1.0 0 -1 -1 -1 -1 -1 -1 0 0 0 0 0 1 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 34 243 | 139 34 1.0 255 255 255 1.0 -1 -1 4 1 #"\0" 244 | 0 71 1 #"\0" 245 | 1.0 0 -1 -1 -1 -1 -1 -1 0 0 0 0 0 0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 0 0 246 | 0 0.0 0 0 0 0.0 -1 -1 4 1 #"\0" 247 | 0 -1 1 #"\0" 248 | 1.0 0 -1 -1 -1 -1 -1 -1 1 0 0 0 0 0 0.0 0.0 0.0 0.0 1.0 1.0 1.0 1.0 0 0 249 | 255 1.0 0 0 0 0.0 -1 -1 4 1 #"\0" 250 | 0 71 1 #"\0" 251 | 1.0 0 -1 -1 -1 -1 -1 -1 1 0 0 0 0 0 0.0 0.0 0.0 0.0 1.0 1.0 1.0 1.0 0 0 252 | 255 1.0 0 0 0 0.0 -1 -1 50 1 #"\0" 253 | 0 71 1 #"\0" 254 | 1.0 0 -1 -1 -1 -1 -1 -1 0 0 0 0 0 0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 0 0 255 | 0 0.0 0 0 0 0.0 -1 -1 0 115 0 28 3 12 #"#lang racket" 256 | 0 0 24 29 1 #"\n" 257 | 0 0 24 29 1 #"\n" 258 | 0 0 24 3 1 #"(" 259 | 0 0 15 3 7 #"require" 260 | 0 0 24 3 1 #" " 261 | 0 0 14 3 8 #"rackunit" 262 | 0 0 24 3 1 #")" 263 | 0 0 24 29 1 #"\n" 264 | 0 0 24 29 1 #"\n" 265 | 0 0 24 3 1 #"(" 266 | 0 0 15 3 6 #"define" 267 | 0 0 24 3 1 #" " 268 | 0 0 14 3 12 #"3x3-red-rect" 269 | 0 0 24 3 1 #" " 270 | 0 6 4 21 279 271 | (5 279 272 | (#(struct:translate 3/2 3/2 #(struct:flip #f #(struct:bitmap #(#"\211PNG\r\n\32\n\0\0\0\rIHDR\0\0\0\3\0\0\0\3\b\6\0\0\0V(\265\277\0\0\0\25IDAT\b\231c\374\317\300\360\237\1\n\230\30\220\0\n\a\0IS\2\4\326T\304\326\0\0\0\0IEND\256B`\202" 1.0) 0 1 1 #hash()))) #(struct:bb 3 3 3) #f) 273 | ) 0 0 24 3 1 #")" 274 | 0 0 24 29 1 #"\n" 275 | 0 0 24 3 1 #"(" 276 | 0 0 15 3 6 #"define" 277 | 0 0 24 3 1 #" " 278 | 0 0 14 3 8 #"red-rect" 279 | 0 0 24 3 1 #" " 280 | 0 6 4 21 318 281 | (6 318 282 | (#(struct:translate 15 10 #(struct:flip #f #(struct:bitmap #(#"\211PNG\r\n\32\n\0\0\0\rIHDR\0\0\0\36\0\0\0\24\b\6\0\0\0\232\253\215\304\0\0\0%IDATH\211\355\3151\1\0\0\b\3\240i\377\316\32c\17\24`.\271\24l#\25\213\305b\261X,\356x\227\276\2&\25:O\261\0\0\0\0IEND\256B`\202" 1.0) 0 1 1 #hash()))) #(struct:bb 30 20 20) #f) 283 | ) 0 0 24 3 1 #")" 284 | 0 0 24 29 1 #"\n" 285 | 0 0 24 3 1 #"(" 286 | 0 0 15 3 6 #"define" 287 | 0 0 24 3 1 #" " 288 | 0 0 14 3 19 #"red-rect/green-circ" 289 | 0 0 24 3 1 #" " 290 | 0 6 4 21 1722 291 | (7 1722 292 | (#(struct:translate 20 20 #(struct:flip #f #(struct:bitmap #(#"\211PNG\r\n\32\n\0\0\0\rIHDR\0\0\0(\0\0\0(\b\6\0\0\0\214\376\270m\0\0\2(IDATX\205\315\231\261n\23A\24E\317L\210\213\210*\tEd\aDC\350\215\240&\377\200\202\340\v\"\301o\0=\211\234(\22]j\n\b\rR>\09\37\20*p\34\"\201\241\201\"\333\344R\214\327Z/\3132\273\304;s\257\246\360\356Hs\364\254\235y\357\215\221DU\31\314\34p\27\270\17t\201\333\300\npu<\345\27\360\58\6\216\200C\340\203\320E\345\305$y\17\304*\342\5b\210*\373\4\361\34\321\251\266\246\37\3305\304.\"\251\1\226w\202\350!\226/\5\20\361\b\361\375\22\300\362\36!\36\326\6D\314#\366f\0\226\367\16b\276\22 b\1\361\256\1\270\324\a\210\5/\300q\344\232\204K\375\36\321\362\1l\342o\375\233{\245\200\210\307\1\341Ro\24\2\"\226\20\337\202\343\271\35c\271\bp7\235\"\302\214\214\267\247\0\21\0352\233p\4\200\t\342\272$\354\370\304{\2\264*\237\223\263S\v\330\00408\310\317@'}+\23\206\312L\347-\247\300\r\v\334#\3\27\221\332@\327\342R\246X\265n\201;\241)J\324\265\300\255\320\24%Z\263\270L8V\255\30DBn\213\211\344+\6Hl\301\274\250dq\5N\254\372i\201\263\320\24%:\263\300\307\320\24%:\266@?4E\211\372\26WT\307\252\3034Y\370\4\254\246O#\331f\6\300M+\327\216\330\17BT\256}\241\213X\23\326sD{\222\260\n\r\201W\201#\226\325\236\320)0U\223,FR4\215\262E\323\344\250\23\372\1<\r\25\262\2146\205F\223_\5\205\373N\300\350m\371t\26\346\20\257\3\300\275E\\\251\322<:h\20\356\r\276\315\243\\\23\251\327\0\334VQ\344\252407\230\315\327\375\25\361\240v\0033\a\271\204\330\306m\240\377\353s\304K\304\242\337\332\36\2232\240m\\#|P\3l\200x\306\370\204\360\35F\372\263\20\370\227\f&-W\327q\327\20k\270B;{\r1\304\345\232}\\\306t\244\32\327\20\277\1%\377)\234\277\334\333\270\0\0\0\0IEND\256B`\202" 1.0) 0 1 1 #hash()))) #(struct:bb 40 40 40) #f) 293 | ) 0 0 24 3 1 #")" 294 | 0 0 24 29 1 #"\n" 295 | 0 0 24 29 1 #"\n" 296 | 0 0 17 3 49 #"; This will produce the Racket \"running man\" icon" 297 | 0 0 24 29 1 #"\n" 298 | 0 6 4 21 1064 299 | (8 1064 300 | (#(struct:translate 17/2 17/2 #(struct:bitmap #(#"\211PNG\r\n\32\n\0\0\0\rIHDR\0\0\0\21\0\0\0\21\b\6\0\0\0;mG\372\0\0\1AIDAT8\215\225\323\261jUA\20\306\361\337\310\r\26\22Dnc!\310\5\363\0\32\264\212\370\2\1\305B\214E\24\353\200\4\302\355\354\4I\221\312\312\3122\2206\352\23\210\370\6\232.H@\b(iD\324$\223\302!.\301s\314\35\230bgg\376\373}\347\354Ff\232$\"b\210\205Z\256g\346W\231y\352\304\20\333\310\312m\f\a\23\311\370\243\3402\346j\375\16\vg\376#\375ZD\214#\342B/\272G\372yl\225\354\357x\211\27\215\225c;]\200\300&~\342Q\1~\324\340{,U\0163S\227\235\307\230\307\23l\340\0g\261\217+\330\301,\266\"b\261K\311\b\17q\35\237\n\262\212\233\215\225ox\205Q\3377Y\303\257\362}\253\251/\343.\246\216k\35\200\e\315\211\237\361\f3\235\av@\336`\27\17\360\32\277\v\270|*\b\356\340\20\343\246v\273 \367{!e\341m\1\366p\256\331{^\277{\272C\271\253\315\360.\306-\240\232>\342\v\236b\364/\310JI\33589\3344\355U\317\1\26O\356\a\6\370\200\213\231y\251\343\362\365\306 3\367#\342\236\277/s\3428\2\n\301m\274Um\0\205\0\0\0\0IEND\256B`\202" 1.0) 0 1 1 #hash())) #(struct:bb 17 17 17) #f) 301 | ) 0 0 24 29 1 #"\n" 302 | 0 0 24 29 1 #"\n" 303 | 0 0 17 3 56 #";; You can also embed images in the middle of other data" 304 | 0 0 24 29 1 #"\n" 305 | 0 0 24 29 1 #"\n" 306 | 0 0 17 3 48 #"; This will produce (list 1 2 4)" 307 | 0 0 24 29 1 #"\n" 308 | 0 0 24 3 1 #"(" 309 | 0 0 14 3 4 #"list" 310 | 0 0 24 3 1 #" " 311 | 0 0 21 3 1 #"1" 312 | 0 0 24 3 1 #" " 313 | 0 0 21 3 1 #"2" 314 | 0 0 24 3 1 #" " 315 | 0 6 4 21 279 5 0 0 24 3 1 #" " 316 | 0 0 21 3 1 #"4" 317 | 0 0 24 3 1 #")" 318 | 0 0 24 29 1 #"\n" 319 | 0 0 24 29 1 #"\n" 320 | 0 0 17 3 89 321 | (9 89 322 | ; This will produce (list ) 323 | ) 0 0 24 29 1 #"\n" 324 | 0 0 24 3 1 #"(" 325 | 0 0 14 3 4 #"list" 326 | 0 0 24 3 1 #" " 327 | 0 6 4 21 396 328 | (10 396 329 | (#(struct:translate 3 5 #(struct:bitmap #(#"\211PNG\r\n\32\n\0\0\0\rIHDR\0\0\0\6\0\0\0\n\b\6\0\0\0\227\16/3\0\0\0FIDAT\b\231\245\317\261\r\2000\0\3\301K(\231\205\206\225\"\1\3031\35\3`:\4\22\251\370\366e\313.I|Q\302\200\31\vN\311\n\25#\32\246g\242J\16\311\206\375-:\374\27\367\366\322\373\321\255\272\0E\357\22\245\314\221\303Q\0\0\0\0IEND\256B`\202" 1.0) 0 1 1 #hash())) #(struct:bb 6 10 10) #f) 330 | ) 0 0 24 3 1 #" " 331 | 0 6 4 21 525 332 | (11 525 333 | (#(struct:translate 3 5 #(struct:bitmap #(#"\211PNG\r\n\32\n\0\0\0\rIHDR\0\0\0\6\0\0\0\n\b\6\0\0\0\227\16/3\0\0\0{IDAT\b\231m\314\261I\203a\0\204\341\347\323@\370m\255\323\244L\2255\4{7p\tk\v\311\"b\227M\334 MJq\200\4|\323$` /\\qw\334\251T\344Q\356/~\310\22_\370\305\21\337\351\215\274\313\346\274\232\344 \v\362 \323\271x\221\235\214\313\377\\>\344G\236*\263aL\330\342\200u\332\303\f\257\220\236]\221OI\376\376i5*\267\270\273\231\342\4\242\275Iq\330\235-h\0\0\0\0IEND\256B`\202" 1.0) 0 1 1 #hash())) #(struct:bb 6 10 10) #f) 334 | ) 0 0 24 3 1 #" " 335 | 0 6 4 21 537 336 | (12 537 337 | (#(struct:translate 3 5 #(struct:bitmap #(#"\211PNG\r\n\32\n\0\0\0\rIHDR\0\0\0\6\0\0\0\n\b\6\0\0\0\227\16/3\0\0\0\202IDAT\b\231e\316!N\3\1\24E\3213c\352\301\222v\5\4\315\2P\335\0\222T\324UU\0226\205f\a8\22X@\23\252\320\b\314E0\23\32x\352\337\374\334\274\247R\241\363\371\376a]\322\v=\322\23\335\315\217{\332L\326\232\336*'j{z\245\207\312\3507\v|\342f\342ni9Yg\364E\313\21\327\330\r\203\21+|\340\35]\3213\35\351@\333\277\345\0274\316 4) 15 | #reader "image-reader.rkt" (list 1 2 µ#"AwP/AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP8" 4) 16 | 17 | ; This will produce (list ) 18 | #reader "image-reader.rkt" 19 | (list µ#"Bgr///8A////AP///wD///8A////AP///wD/AAAD/wAANf8AAJv/AACZ////AP///wD/AAAO/wAAmP8AAMj/AACZ////AP///wD///8A////AP8AAHT/AACZ////AP///wD///8A////AP8AAHT/AACZ////AP///wD///8A////AP8AAHT/AACZ////AP///wD///8A////AP8AAHT/AACZ////AP///wD///8A////AP8AAHT/AACY////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wA" 20 | µ#"Bgr///8A////AAD/ABMA/wAD////AP///wAA/wAiAP8AzAD/ALoA/wC1AP8Ahf///wAA/wCFAP8Ai////wAA/wAJAP8A+gD/AB0A/wAKAP8ACf///wAA/wBXAP8A3wD/AAH///8AAP8ABwD/AIkA/wDsAP8ASf///wAA/wAJAP8AugD/ALQA/wAc////AP///wAA/wBvAP8Auv///wD///8A////AP///wAA/wCoAP8A/wD/AP4A/wD+AP8A/gD/ACr///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wA" 21 | µ#"Bgr///8A////AAAA/xP///8A////AP///wAAAP8tAAD/0gAA/7IAAP+8AAD/YP///wAAAP97AAD/Yv///wAAAP9LAAD/1v///wD///8A////AAAA/3MAAP/VAAD/ff///wD///8A////AAAA/3oAAP/LAAD/wv///wAAAP9YAAD/Hv///wAAAP8SAAD/+gAA/x4AAP+RAAD/iwAA/wIAAP8xAAD/5gAA/wMAAP8vAAD/ywAA/+YAAP/iAAD/Zv///wD///8A////AAAA/xwAAP8C////AP///wD///8A////AP///wD///8A////AP///wA") 22 | 23 | (module+ test 24 | (require 2htdp/image) 25 | (require "test-images.rkt") 26 | 27 | (check-equal? 3x3-red-rect 3x3-rr) 28 | (check-equal? red-rect rr) 29 | (check-equal? red-rect/green-circ rr/gc) 30 | ) 31 | -------------------------------------------------------------------------------- /image-pack-unpack.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (provide pack unpack) 4 | 5 | (require 2htdp/image) 6 | (require base64) 7 | (require rackunit) 8 | 9 | (define (pack i) 10 | (base64-encode 11 | (list->bytes 12 | (list* (image-width i) 13 | (image-height i) 14 | (apply append 15 | (map (lambda (c) 16 | (list (color-red c) 17 | (color-green c) 18 | (color-blue c) 19 | (color-alpha c))) 20 | (image->color-list i))))))) 21 | 22 | (define (unpack ip) 23 | (let* ([bl (bytes->list (base64-decode ip))] 24 | [ln (first bl)] 25 | [wd (second bl)] 26 | [payload (rest (rest bl))] 27 | [converted (let loop ([rem payload]) 28 | (if (empty? rem) 29 | empty 30 | (let ([r (first rem)] 31 | [g (second rem)] 32 | [b (third rem)] 33 | [alpha (fourth rem)]) 34 | (cons (color r g b alpha) 35 | (loop (rest (rest (rest (rest rem)))))))))]) 36 | (color-list->bitmap converted ln wd))) 37 | 38 | (module+ test 39 | 40 | (require "test-images.rkt") 41 | 42 | (define p0 (pack 3x3-rr)) 43 | (define p1 (pack rr)) 44 | (define p2 (pack gc)) 45 | (define p3 (pack rr/gc)) 46 | 47 | (check-equal? (unpack p0) 3x3-rr) 48 | (check-equal? (unpack p1) rr) 49 | (check-equal? (unpack p2) gc) 50 | ) 51 | -------------------------------------------------------------------------------- /image-reader.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (provide (rename-out [µ-read read] 4 | [µ-read-syntax read-syntax])) 5 | 6 | (require "image-pack-unpack.rkt") 7 | 8 | (define read-µ 9 | (let* ([validate-and-unpack 10 | (lambda (v src) 11 | (if (bytes? v) 12 | (with-handlers ([exn:fail? 13 | (lambda (e) (error "unable to decode, check using `unpack`:" v))]) 14 | (unpack v)) 15 | (error "µ must be followed by a byte string, not" v)))]) 16 | (case-lambda 17 | [(ch in) 18 | (validate-and-unpack (read in) (object-name in))] 19 | [(ch in src line col pos) 20 | (validate-and-unpack (read in) src)]))) 21 | 22 | (define (make-µ-readtable) 23 | (make-readtable (current-readtable) 24 | #\µ 'non-terminating-macro read-µ)) 25 | 26 | (define (µ-read in) 27 | (parameterize ([current-readtable (make-µ-readtable)]) 28 | (read in))) 29 | 30 | (define (µ-read-syntax src in) 31 | (parameterize ([current-readtable (make-µ-readtable)]) 32 | (read-syntax src in))) 33 | -------------------------------------------------------------------------------- /packing-example.rkt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shriram/racket-embed-images-in-source-text/663b1003ebca8609cc89f722a7ec7550ccc8722d/packing-example.rkt -------------------------------------------------------------------------------- /test-images.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (provide [all-defined-out]) 4 | 5 | (require 2htdp/image) 6 | 7 | (define 3x3-rr (rectangle 3 3 "solid" "red")) 8 | (define rr (rectangle 30 20 "solid" "red")) 9 | (define gc (circle 20 "solid" "green")) 10 | (define rr/gc (overlay rr gc)) 11 | --------------------------------------------------------------------------------