The trick is loop thru the data two lines at a time; storing the values to an array; then outputting a csv at the end (if you want you can print output in the if [ -z "$name" ] block but then you loose the nice headers).
#!/bin/bash
declare -A cell
declare -A head
i=0
while read name
do
    if [ -z "$name" ]
    then
        ((i+=1))
    else
        head[$name]=$name
        read value
        cell[$i,$name]=$value;
    fi
done < "${1:-/dev/stdin}"
printf "%-10s; " "${head[@]}"; echo
printf "%.0s----------; " ${head[@]}; echo 
j=0
until [ $j -gt $i ]; do   
    for name in ${head[@]}
    do
        printf "%-10s; " "${cell[$j,$name]}"
    done
    echo
    ((j+=1))
done
The above script presumes the sets are separated by a single empty line and will return:
$ head data
head1
value1-1
head2
value2-1
head2
value2-2
$ ./csvgen.sh data
head2     ; head3     ; head1     ; head4     ; 
----------; ----------; ----------; ----------; 
value2-1  ;           ; value1-1  ;           ; 
value2-2  ; value3-2  ;           ;           ; 
value2-3  ;           ; value1-3  ; value4-3  ; 
How it works:
loop over each line of either a file or stdin. 
while read name
do
# ...
done < "${1:-/dev/stdin}"
if [ -z "$name" ] # If the line has a length of zero the set has ended
then              # so increse the set index by 1.
    ((i+=1))
else
    head[$name]=$name  # this array contains all the headers we have seen
    read value  # read the next line to $value
    cell[$i,$name]=$value; # save $value in array indexed by set and header
fi
printf "%-10s; " "${head[@]}";  # print each header from 
echo   # the above wont end the line so echo for a "\n"
printf "%.0s----------; " ${head[@]}; # %.0s truncates the input to nothing  
echo                                  # printing only the '----------'
until [  $j -gt $i ]; do     # for each set index
    for name in ${head[@]}   # loop thru the headers
    do
        printf "%-10s; " "${cell[$j,$name]}" # then print the values
    done
    echo # end each set with "\n"
    ((j+=1))
done