Decode Arabic to Arabic Presentation Form
JavaScript (ES6),  231 ... 148  146 bytes
I/O format: list of code points

a=&gt;a.map(t=(c,i)=&gt;c%32?(g=A=&gt;n^c%49?g([x=n%32&lt;26,k*x,x&amp;=454642&gt;&gt;n++*25%54%26,x].map(x=&gt;x&amp;&amp;++k)):A[t=t&gt;1^2*(a[i+1]&gt;1569)]||A[t^=2])(n=k=0)+65151:c)

Try it online!
a =&gt;                             // a[] = input array
a.map(t = (c, i) =&gt;              // for each code point c at position i in a[]:
  c % 32 ?                       //   if c is not 0x0640:
    ( g = A =&gt;                   //     g is a recursive function taking a list A[]
        n ^ c % 49 ?             //       if we haven't reached the correct character:
          g(                     //         do a recursive call:
            [                    //           build a list describing the existence of:
              x = n % 32 &lt; 26,   //           - isolated: if not in range 0x063B-0x0640
              k * x,             //           - final: same as isolated, except for the
                                 //             first entry which is undefined
              x &amp;=               //           - initial: we use a lookup bit-mask
                454642 &gt;&gt; n++    //             with the following hash formula:
                * 25 % 54 % 26,  //               ((n * 25) mod 54) mod 26
              x                  //           - medial: same as initial
            ].map(x =&gt; x &amp;&amp; ++k) //           turn it into a list of indices
          )                      //         end of recursive call
        :                        //       else:
          A[                     //         output the relevant entry from A[]:
            t =                  //           update t:
              t &gt; 1 ^ 2 * (      //             (previous was initial or medial) XOR
                a[i + 1] &gt; 1569  //             2 * (this is not the last letter 
              )                  //             and the next letter is not 0x0621)
          ] ||                   //         if A[t] is not defined:
            A[t ^= 2]            //           use A[t ^ 2] instead
    )(n = k = 0)                 //     initial call to g with n = k = 0
    + 65151                      //     add 0xFE7F
  :                              //   else:
    c                            //     output c unchanged
)                                // end of map()