Format for multimedia files?

Where is the description of what the format for multimedia files is? A lot of my early assignments include images and I'd like to be able to look at student text inside the files for analysis and grading.

I can't speak for the file format, but if you want to see the text, it's probably easiest to do so by opening the file in DrRacket and looking at it.

Yeah. I want to programmatically look at the file.

The format is called WXME.

https://docs.racket-lang.org/gui/WXME_Decoding.html

It's extensible in the sense that snips (see snip%) can define how they are read and written.
So, you might need to look at the documentation/code of the snips that are in your file too.

3 Likes

The simplest way I know of to get nearly the plain-text content of a WXME file is to (1) create a text%, (2) load the file into it, and (3) send the-text% get-text to get its content. I say "nearly" because this will render any embedded snips as ., which is not quite accurate and won't parse back in as valid student code, but is also likely good enough for text-processing.

(require racket/gui/base)

(define (file->text filename)
  (define txt (new text%))
  (send txt load-file filename)
  (send txt get-text))

If you need to do more detailed processing, you'll need to actually walk the snip% tree, which is somewhat more tedious. Instead of sending get-text, you'll send find-first-snip (and send that next to get its siblings), and then use is-a? on all the various snip classes you want to handle. I've used the following (believe it or not, edited somewhat for brevity! helper functions not included)

;; display-all : (or/c snip? #f) output-port? -> #f
;; Walks the given snip tree and renders it as plaintext to the given output port.
(define (display-all snip out #:inline-numbers? [inline-numbers? #f])
  (if (not snip)
      out
      (let ((snip-name (send (send snip get-snipclass) get-classname)))
        (cond
          [(is-a? snip string-snip%)
           (display (send snip get-text 0 (send snip get-count))
                    out)]
          [(is-a? snip comment-box:snip%)
           (define comment (open-output-bytes))
           (define contents (display-all (send (send snip get-editor)
                                               find-first-snip)
                                         comment))
           (display (add-comment (get-output-string comment)) out)]
          [(equal? snip-name
                   "(lib \"number-snip.ss\" \"drscheme\" \"private\")")
           (define val (number->string (send snip get-number)))
           (display (if inline-numbers? val (add-number val)) out)]
          [(or (is-a? snip xml-snip<%>)
               (equal? snip-name "(lib \"xml-snipclass.ss\" \"xml\")")
               (equal? snip-name "(lib \"xml-snipclass.rkt\" \"xml\")")
               (equal? snip-name "(lib \"xml.ss\" \"wxme\")"))
           (define xml (open-output-bytes))
           (define contents (display-all (send (send snip get-editor)
                                               find-first-snip)
                                         xml))
           (display (add-xml (get-output-string xml)) out)]
          [(or (equal? snip-name "(lib \"text-snipclass.ss\" \"xml\")")
               (equal? snip-name "(lib \"text-snipclass.rkt\" \"xml\")")
               (equal? snip-name "(lib \"text.ss\" \"wxme\")"))
           (define text (open-output-bytes))
           (define contents (display-all (send (send snip get-editor)
                                               find-first-snip)
                                         text))
           (display (add-text (get-output-string text)) out)]
          [(equal? snip-name (format "~s" '((lib "collapsed-snipclass.ss" "framework")
                                            (lib "collapsed-snipclass-wxme.ss" "framework"))))
           ;; Display collapsed-snips in collapsed form isn't good for grading:
           ;; it hides code, and could hide badly long lines.  So,
           ;; expand the snip to its hidden pieces, instead.
           (for ([kid (in-list (send snip get-saved-snips))])
             (display-all kid out))]
          [(or (is-a? snip scheme-snip<%>)
               (equal? snip-name "(lib \"scheme-snipclass.ss\" \"xml\")")
               (equal? snip-name "(lib \"scheme-snipclass.rkt\" \"xml\")")
               (equal? snip-name "(lib \"scheme.ss\" \"wxme\")"))
           (define rkt (open-output-bytes))
           (define contents (display-all (send (send snip get-editor)
                                               find-first-snip)
                                         rkt))
           (if (send snip get-splice?)
               (display (add-splice (get-output-string rkt)) out)
               (display (add-racket (get-output-string rkt)) out))]
          [(convertible? snip)
           (define bm (new bitmap-dc% [bitmap (make-object bitmap% 1 1)]))
           (define width (box 0))
           (define height (box 0))
           (send snip get-extent bm 0 0 width height)
           (if (or (= (unbox width) 0) (= (unbox height) 0))
               (display (add-empty-image "") out)
               (with-handlers
                   ([exn:fail:contract?
                     (λ(e)
                       (display
                        (add-comment
                         (string-append "Could not render this content;\n"
                                        "please contact a professor."))
                        out))])
                 (display (add-image (convert snip 'png-bytes)) out)))]
          [(syntax? snip)
           (display (syntax->datum snip) out)]
          [else
           (display (format ">?>~a<?<" snip-name) out)
           (display (format ">>>~a<<<" snip) out)]
          )
        (display-all (send snip next) out #:inline-numbers? inline-numbers?))))

(I'd love to know if there are ways to detect number, xml, collapsed and scheme snips without resorting to snip classnames! But this code has worked well for me for years.)

1 Like