|Welcome to the Monastery|
Just think of each row in your output table as one interation of a single <TMPL_LOOP NAME="..."> ... </TMPL_LOOP>.
Your foreach loop, instead of printing things, will be pushing elements onto an array of hashes, one hash for each iteration / table row, where the hash keys will be the names assigned to the specific TMPL_VAR's that you put inside the TMPL_LOOP portion of the template.
I think the relevant part of your template would probably look something like this:
Then, your foreach loop just needs to make sure that a hash is filled with values for the six TMPL_VAR names (FILEDATE, TEXT_viewer, SIZE_viewer, LINK_viewer, SIZE_ppi, LINK_ppi), and the hash is pushed onto an array for use with the template -- something like this might suffice:
(none of the above has been tested -- updated to fix indenting, and to avoid pushing empty/incomplete hashes onto the array by checking the number of hash keys)
As mentioned in an earlier reply, you should be able to work out the rest by reading the fine manual for HTML::Template.
I saw a lot of unnecessary work going on in the original foreach loop, and tried to eliminate it in the suggested code. I think my version does basically the same thing...
... Except I was baffled by (and did not try to repeat) the OP's use of substr() to create the link targets for the href attributes. As a rule, I'd say "just don't do that." It's really a bad idea, because it creates a sort of brittleness, obfuscation and dependency on external details (like changes to path names) that make the script harder to maintain. (Why use the values 34 or 35? How would they change if the file path changes?)
There is a better way to get the intended result, which does not involve using substr() with "magic numbers".