I ended up writing a function for this. First it reads the region into a list, splitting it by newline character. It then loops over the list, getting the length of the longest line. It loops over the list of lines again, for each line adding enough spaces to reach the max line length, and then adding that padded line to a new list. This new list is then joined, with new line characters as separators. The region is replaced with this string.
I'm sure its not very efficient, but lisp is still a mystery to me so I don't know how to edit in place. It's fast enough for my needs, though.
(defun fill-ws (beginning end)
; Grab active region as string
(setq region (buffer-substring-no-properties beginning end))
; Split region string by new-line character
(setq lines (split-string region "
; Get the length of the longest line in the region
(setq max_len 0)
(dolist (element lines)
(setq max_len (max max_len (length element)))
; For each line, pad it with enough spaces to reach length max_len
; Then, add it to a new list.
(setq padded '() )
(dolist (element lines padded)
(if (= (length element) max_len)
(setq padded_line element) ; If the string is of length max_len, no need to pad
; If its shorter than max, concatenate with (max-len - len(element)) spaces
(setq padded_line (concat element (make-string (- max_len (length element)) ? )))
; Append the padded value to the list
(setq padded (append padded (make-list 1 padded_line)))
; Join the new list to create replacement for region
(setq replace (string-join padded "
; Replace the region
(delete-region beginning end)