Python - 525 491 478 430 bytes
r=range def t(s): m=s.find("\n")+1 for q in r(4): try: for i in r(-q%2,m-1,2): for j in r(-q/2,len(s)/m,2): k,g=j*m+i,"" b=[k,k+1,k+m,k+m+1] for z in b:g+=a(s,z) for z in b: if a(s,z)!="d":s=s[:z]+`dict(dddd=0,zzdd=3,ddzz=2,zzzd=7,zzdz=6,zdzz=5,dzzz=4, zzzz=1)[g]`+s[z+1:] return s except:d def a(v,i): try: if v[i]!="\n":return v[i] return "d" except:return "d" Explanation: This is my first code golf, so it might not be optimal, but here's how it works. The function t(s) gives the result for the string passed in. First, it finds the number of columns, then goes through the four possible distinct translations by 1 (none, left, up, up-left) and tries to solve it for each one. It looks at each 2x2 block and maps it to a valid block number, given by a dictionary, and changes the zeros to the number.
If it finds one not in the dictionary, it abandons that specific offset and starts over with the next one. If it goes through all 4 offsets without finding a valid solution, it ends without outputting anything. a(v, i) allows for the default value outside the string, and ignores newline characters. Although it may end up with partial solutions through the duration of the run, it will always override them with the final correct one if it exists.
Edit: A different mapping of characters is used: . -> d, 0 -> z, all other numbers go to themselves. This applies to both input and output.