@@ -678,3 +678,153 @@ describe("drawField", () => {
678678 expect ( checkbox2 ! . isChecked ( ) ) . toBe ( true ) ;
679679 } ) ;
680680} ) ;
681+
682+ describe ( "removeField" , ( ) => {
683+ it ( "removes a field by instance" , async ( ) => {
684+ const pdf = PDF . create ( ) ;
685+ pdf . addPage ( { size : "letter" } ) ;
686+ const form = pdf . getOrCreateForm ( ) ;
687+ const page = pdf . getPage ( 0 ) ;
688+
689+ const field = form . createTextField ( "name" ) ;
690+ page ! . drawField ( field , { x : 100 , y : 700 , width : 200 , height : 24 } ) ;
691+
692+ expect ( form . hasField ( "name" ) ) . toBe ( true ) ;
693+ expect ( form . fieldCount ) . toBe ( 1 ) ;
694+
695+ const removed = form . removeField ( field ) ;
696+
697+ expect ( removed ) . toBe ( true ) ;
698+ expect ( form . hasField ( "name" ) ) . toBe ( false ) ;
699+ expect ( form . fieldCount ) . toBe ( 0 ) ;
700+ expect ( form . getField ( "name" ) ) . toBeUndefined ( ) ;
701+ } ) ;
702+
703+ it ( "removes a field by name" , async ( ) => {
704+ const pdf = PDF . create ( ) ;
705+ pdf . addPage ( { size : "letter" } ) ;
706+ const form = pdf . getOrCreateForm ( ) ;
707+ const page = pdf . getPage ( 0 ) ;
708+
709+ form . createTextField ( "email" ) ;
710+ page ! . drawField ( form . getTextField ( "email" ) ! , { x : 100 , y : 700 , width : 200 , height : 24 } ) ;
711+
712+ const removed = form . removeField ( "email" ) ;
713+
714+ expect ( removed ) . toBe ( true ) ;
715+ expect ( form . hasField ( "email" ) ) . toBe ( false ) ;
716+ } ) ;
717+
718+ it ( "returns false for non-existent field" , async ( ) => {
719+ const pdf = PDF . create ( ) ;
720+ const form = pdf . getOrCreateForm ( ) ;
721+
722+ const removed = form . removeField ( "nonexistent" ) ;
723+
724+ expect ( removed ) . toBe ( false ) ;
725+ } ) ;
726+
727+ it ( "removes widget annotations from page" , async ( ) => {
728+ const pdf = PDF . create ( ) ;
729+ pdf . addPage ( { size : "letter" } ) ;
730+ const form = pdf . getOrCreateForm ( ) ;
731+ const page = pdf . getPage ( 0 ) ;
732+
733+ const field = form . createTextField ( "name" ) ;
734+ page ! . drawField ( field , { x : 100 , y : 700 , width : 200 , height : 24 } ) ;
735+
736+ // Verify widget was added
737+ expect ( field . getWidgets ( ) ) . toHaveLength ( 1 ) ;
738+
739+ form . removeField ( field ) ;
740+
741+ // After removal, the page should not have form field annotations
742+ // We can verify by saving and reloading
743+ const bytes = await pdf . save ( ) ;
744+ const pdf2 = await PDF . load ( bytes ) ;
745+ const form2 = pdf2 . getForm ( ) ;
746+
747+ // Form should exist but be empty
748+ if ( form2 ) {
749+ expect ( form2 . fieldCount ) . toBe ( 0 ) ;
750+ }
751+ } ) ;
752+
753+ it ( "removes field with multiple widgets across pages" , async ( ) => {
754+ const pdf = PDF . create ( ) ;
755+ pdf . addPage ( { size : "letter" } ) ;
756+ pdf . addPage ( { size : "letter" } ) ;
757+ const form = pdf . getOrCreateForm ( ) ;
758+ const page1 = pdf . getPage ( 0 ) ;
759+ const page2 = pdf . getPage ( 1 ) ;
760+
761+ const field = form . createTextField ( "sharedField" ) ;
762+ page1 ! . drawField ( field , { x : 100 , y : 700 , width : 200 , height : 24 } ) ;
763+ page2 ! . drawField ( field , { x : 50 , y : 500 , width : 300 , height : 30 } ) ;
764+
765+ expect ( field . getWidgets ( ) ) . toHaveLength ( 2 ) ;
766+
767+ form . removeField ( field ) ;
768+
769+ expect ( form . hasField ( "sharedField" ) ) . toBe ( false ) ;
770+ expect ( form . fieldCount ) . toBe ( 0 ) ;
771+ } ) ;
772+
773+ it ( "allows creating a new field with the same name after removal" , async ( ) => {
774+ const pdf = PDF . create ( ) ;
775+ pdf . addPage ( { size : "letter" } ) ;
776+ const form = pdf . getOrCreateForm ( ) ;
777+ const page = pdf . getPage ( 0 ) ;
778+
779+ const field1 = form . createTextField ( "name" , { defaultValue : "First" } ) ;
780+ page ! . drawField ( field1 , { x : 100 , y : 700 , width : 200 , height : 24 } ) ;
781+
782+ form . removeField ( field1 ) ;
783+
784+ // Should be able to create a new field with the same name
785+ const field2 = form . createTextField ( "name" , { defaultValue : "Second" } ) ;
786+ page ! . drawField ( field2 , { x : 100 , y : 700 , width : 200 , height : 24 } ) ;
787+
788+ expect ( form . hasField ( "name" ) ) . toBe ( true ) ;
789+ expect ( form . getTextField ( "name" ) ?. getValue ( ) ) . toBe ( "Second" ) ;
790+ } ) ;
791+
792+ it ( "removes existing field from loaded PDF" , async ( ) => {
793+ const bytes = await loadFixture ( "forms" , "sample_form.pdf" ) ;
794+ const pdf = await PDF . load ( bytes ) ;
795+ const form = pdf . getForm ( ) ;
796+
797+ const initialCount = form ! . fieldCount ;
798+ const fieldToRemove = form ! . getFields ( ) [ 0 ] ;
799+ const fieldName = fieldToRemove . name ;
800+
801+ const removed = form ! . removeField ( fieldToRemove ) ;
802+
803+ expect ( removed ) . toBe ( true ) ;
804+ expect ( form ! . hasField ( fieldName ) ) . toBe ( false ) ;
805+ expect ( form ! . fieldCount ) . toBe ( initialCount - 1 ) ;
806+ } ) ;
807+
808+ it ( "persists removal through save/load cycle" , async ( ) => {
809+ const pdf = PDF . create ( ) ;
810+ pdf . addPage ( { size : "letter" } ) ;
811+ const form = pdf . getOrCreateForm ( ) ;
812+ const page = pdf . getPage ( 0 ) ;
813+
814+ form . createTextField ( "keep" ) ;
815+ form . createTextField ( "remove" ) ;
816+ page ! . drawField ( form . getTextField ( "keep" ) ! , { x : 100 , y : 700 , width : 200 , height : 24 } ) ;
817+ page ! . drawField ( form . getTextField ( "remove" ) ! , { x : 100 , y : 650 , width : 200 , height : 24 } ) ;
818+
819+ form . removeField ( "remove" ) ;
820+
821+ const bytes = await pdf . save ( ) ;
822+ const pdf2 = await PDF . load ( bytes ) ;
823+ const form2 = pdf2 . getForm ( ) ;
824+
825+ expect ( form2 ) . not . toBeNull ( ) ;
826+ expect ( form2 ! . fieldCount ) . toBe ( 1 ) ;
827+ expect ( form2 ! . hasField ( "keep" ) ) . toBe ( true ) ;
828+ expect ( form2 ! . hasField ( "remove" ) ) . toBe ( false ) ;
829+ } ) ;
830+ } ) ;
0 commit comments