It's impossible to accomplish what you want by only granting/denying permissions.
UPDATE it's sufficient to make a user the member of
db_denydatawriter fixed database role, and even if the user has some of these permissions inherited from membership in other roles or windows groups mapped to this database deny will win.
But to allow any changes to your tables, your user has to have
ALTER TABLE permission or be a member of
db_ddladmin role gives you more than just altering tables, it gives your user the permissions to create/alter any other object(but not principal) in the database, not only tables, but maybe it's what you want.
In order to not allow
TRUNCATE you should deny the same
ALTER TABLE permission so here you should decide, or your user has this permission and can
alter your tables,
truncate inclusive, or has not.
The solution here may be to use
ddl-trigger where you manually rollback any
DROP TABLE issued by unwanted user.
To change ddl-trigger your user should have
ALTER ANY DATABASE DDL TRIGGER and it will not have it if it is only a member of
There is no way to disallow
truncate table while preserving
ALTER TABLE permission as
ddl-trigger will not catch it.