diff --git a/package.json b/package.json
index 603ee5f..90d390d 100644
--- a/package.json
+++ b/package.json
@@ -24,8 +24,8 @@
   "dependencies": {
     "@push.rocks/smartfile": "^11.2.0",
     "@push.rocks/smartxml": "^1.1.1",
-    "@tsclass/tsclass": "^5.0.0",
-    "jsdom": "^24.1.3",
+    "@tsclass/tsclass": "^6.0.1",
+    "jsdom": "^26.0.0",
     "pako": "^2.1.0",
     "pdf-lib": "^1.17.1",
     "xmldom": "^0.6.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index a3d632f..f6d65c8 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -15,11 +15,11 @@ importers:
         specifier: ^1.1.1
         version: 1.1.1
       '@tsclass/tsclass':
-        specifier: ^5.0.0
-        version: 5.0.0
+        specifier: ^6.0.1
+        version: 6.0.1
       jsdom:
-        specifier: ^24.1.3
-        version: 24.1.3
+        specifier: ^26.0.0
+        version: 26.0.0
       pako:
         specifier: ^2.1.0
         version: 2.1.0
@@ -1301,8 +1301,8 @@ packages:
   '@tsclass/tsclass@4.4.4':
     resolution: {integrity: sha512-YZOAF+u+r4u5rCev2uUd1KBTBdfyFdtDmcv4wuN+864lMccbdfRICR3SlJwCfYS1lbeV3QNLYGD30wjRXgvCJA==}
 
-  '@tsclass/tsclass@5.0.0':
-    resolution: {integrity: sha512-2X66VCk0Oe1L01j6GQHC6F9Gj7lpZPPSUTDNax7e29lm4OqBTyAzTR3ePR8coSbWBwsmRV8awLRSrSI+swlqWA==}
+  '@tsclass/tsclass@6.0.1':
+    resolution: {integrity: sha512-EIREiBKgmoTifOe9HdRmqDZV3geJKnf4UgFvkP3aEgD17lmkjQJg44NdlTj0VZ6bf2pMIGZlGROe6Mc/OCIDQg==}
 
   '@types/accepts@1.3.7':
     resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==}
@@ -2845,11 +2845,11 @@ packages:
   jsbn@1.1.0:
     resolution: {integrity: sha1-sBMHyym2GKHtJux56RH4A8TaAEA=}
 
-  jsdom@24.1.3:
-    resolution: {integrity: sha512-MyL55p3Ut3cXbeBEG7Hcv0mVM8pp8PBNWxRqchZnSfAiES1v1mRnMeFfaHWIPULpwsYfvO+ZmMZz5tGCnjzDUQ==}
+  jsdom@26.0.0:
+    resolution: {integrity: sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw==}
     engines: {node: '>=18'}
     peerDependencies:
-      canvas: ^2.11.2
+      canvas: ^3.0.0
     peerDependenciesMeta:
       canvas:
         optional: true
@@ -3363,8 +3363,8 @@ packages:
     resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
     engines: {node: '>=8'}
 
-  nwsapi@2.2.18:
-    resolution: {integrity: sha512-p1TRH/edngVEHVbwqWnxUViEmq5znDvyB+Sik5cmuLpGOIfDf/39zLiq3swPF8Vakqn+gvNiOQAZu8djYlQILA==}
+  nwsapi@2.2.19:
+    resolution: {integrity: sha512-94bcyI3RsqiZufXjkr3ltkI86iEl+I7uiHVDtcq9wJUTwYQJ5odHDeSzkkrRzi80jJ8MaeZgqKjH1bAWAFw9bA==}
 
   object-assign@4.1.1:
     resolution: {integrity: sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=}
@@ -3607,9 +3607,6 @@ packages:
   proxy-from-env@1.1.0:
     resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
 
-  psl@1.15.0:
-    resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==}
-
   public-ip@6.0.2:
     resolution: {integrity: sha512-+6bkjnf0yQ4+tZV0zJv1017DiIF7y6R4yg17Mrhhkc25L7dtQtXWHgSCrz9BbLL4OeTFbPK4EALXqJUrwCIWXw==}
     engines: {node: '>=14.16'}
@@ -3647,9 +3644,6 @@ packages:
     resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==}
     engines: {node: '>=0.6'}
 
-  querystringify@2.2.0:
-    resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
-
   queue-microtask@1.2.3:
     resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
 
@@ -3725,9 +3719,6 @@ packages:
     resolution: {integrity: sha1-jGStX9MNqxyXbiNE/+f3kqam30I=}
     engines: {node: '>=0.10.0'}
 
-  requires-port@1.0.0:
-    resolution: {integrity: sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=}
-
   resolve-alpn@1.2.1:
     resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==}
 
@@ -3758,9 +3749,6 @@ packages:
     resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
     hasBin: true
 
-  rrweb-cssom@0.7.1:
-    resolution: {integrity: sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==}
-
   rrweb-cssom@0.8.0:
     resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==}
 
@@ -4052,6 +4040,13 @@ packages:
   tiny-worker@2.3.0:
     resolution: {integrity: sha512-pJ70wq5EAqTAEl9IkGzA+fN0836rycEuz2Cn6yeZ6FRzlVS5IDOkFHpIoEsksPRQV34GDqXm65+OlnZqUSyK2g==}
 
+  tldts-core@6.1.84:
+    resolution: {integrity: sha512-NaQa1W76W2aCGjXybvnMYzGSM4x8fvG2AN/pla7qxcg0ZHbooOPhA8kctmOZUDfZyhDL27OGNbwAeig8P4p1vg==}
+
+  tldts@6.1.84:
+    resolution: {integrity: sha512-aRGIbCIF3teodtUFAYSdQONVmDRy21REM3o6JnqWn5ZkQBJJ4gHxhw6OfwQ+WkSAi3ASamrS4N4nyazWx6uTYg==}
+    hasBin: true
+
   to-regex-range@5.0.1:
     resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
     engines: {node: '>=8.0'}
@@ -4064,9 +4059,9 @@ packages:
     resolution: {integrity: sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA==}
     engines: {node: '>=14.16'}
 
-  tough-cookie@4.1.4:
-    resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==}
-    engines: {node: '>=6'}
+  tough-cookie@5.1.2:
+    resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==}
+    engines: {node: '>=16'}
 
   tr46@3.0.0:
     resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==}
@@ -4169,10 +4164,6 @@ packages:
     resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==}
     engines: {node: '>= 4.0.0'}
 
-  universalify@0.2.0:
-    resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==}
-    engines: {node: '>= 4.0.0'}
-
   universalify@2.0.1:
     resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
     engines: {node: '>= 10.0.0'}
@@ -4187,9 +4178,6 @@ packages:
   upper-case@1.1.3:
     resolution: {integrity: sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=}
 
-  url-parse@1.5.10:
-    resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
-
   url@0.11.4:
     resolution: {integrity: sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==}
     engines: {node: '>= 0.4'}
@@ -6658,7 +6646,7 @@ snapshots:
     dependencies:
       type-fest: 4.37.0
 
-  '@tsclass/tsclass@5.0.0':
+  '@tsclass/tsclass@6.0.1':
     dependencies:
       type-fest: 4.37.0
 
@@ -8411,7 +8399,7 @@ snapshots:
 
   jsbn@1.1.0: {}
 
-  jsdom@24.1.3:
+  jsdom@26.0.0:
     dependencies:
       cssstyle: 4.3.0
       data-urls: 5.0.0
@@ -8421,12 +8409,12 @@ snapshots:
       http-proxy-agent: 7.0.2
       https-proxy-agent: 7.0.6
       is-potential-custom-element-name: 1.0.1
-      nwsapi: 2.2.18
+      nwsapi: 2.2.19
       parse5: 7.2.1
-      rrweb-cssom: 0.7.1
+      rrweb-cssom: 0.8.0
       saxes: 6.0.0
       symbol-tree: 3.2.4
-      tough-cookie: 4.1.4
+      tough-cookie: 5.1.2
       w3c-xmlserializer: 5.0.0
       webidl-conversions: 7.0.0
       whatwg-encoding: 3.1.1
@@ -9135,7 +9123,7 @@ snapshots:
     dependencies:
       path-key: 3.1.1
 
-  nwsapi@2.2.18: {}
+  nwsapi@2.2.19: {}
 
   object-assign@4.1.1: {}
 
@@ -9361,10 +9349,6 @@ snapshots:
 
   proxy-from-env@1.1.0: {}
 
-  psl@1.15.0:
-    dependencies:
-      punycode: 2.3.1
-
   public-ip@6.0.2:
     dependencies:
       aggregate-error: 4.0.1
@@ -9429,8 +9413,6 @@ snapshots:
     dependencies:
       side-channel: 1.1.0
 
-  querystringify@2.2.0: {}
-
   queue-microtask@1.2.3: {}
 
   quick-lru@5.1.1: {}
@@ -9536,8 +9518,6 @@ snapshots:
 
   require-directory@2.1.1: {}
 
-  requires-port@1.0.0: {}
-
   resolve-alpn@1.2.1: {}
 
   resolve-from@4.0.0: {}
@@ -9564,8 +9544,6 @@ snapshots:
     dependencies:
       glob: 7.2.3
 
-  rrweb-cssom@0.7.1: {}
-
   rrweb-cssom@0.8.0: {}
 
   rss-parser@3.13.0:
@@ -9944,6 +9922,12 @@ snapshots:
     dependencies:
       esm: 3.2.25
 
+  tldts-core@6.1.84: {}
+
+  tldts@6.1.84:
+    dependencies:
+      tldts-core: 6.1.84
+
   to-regex-range@5.0.1:
     dependencies:
       is-number: 7.0.0
@@ -9955,12 +9939,9 @@ snapshots:
       '@tokenizer/token': 0.3.0
       ieee754: 1.2.1
 
-  tough-cookie@4.1.4:
+  tough-cookie@5.1.2:
     dependencies:
-      psl: 1.15.0
-      punycode: 2.3.1
-      universalify: 0.2.0
-      url-parse: 1.5.10
+      tldts: 6.1.84
 
   tr46@3.0.0:
     dependencies:
@@ -10053,8 +10034,6 @@ snapshots:
 
   universalify@0.1.2: {}
 
-  universalify@0.2.0: {}
-
   universalify@2.0.1: {}
 
   unload@2.4.1: {}
@@ -10063,11 +10042,6 @@ snapshots:
 
   upper-case@1.1.3: {}
 
-  url-parse@1.5.10:
-    dependencies:
-      querystringify: 2.2.0
-      requires-port: 1.0.0
-
   url@0.11.4:
     dependencies:
       punycode: 1.4.1
diff --git a/readme.literature.md b/readme.literature.md
new file mode 100644
index 0000000..c85820c
--- /dev/null
+++ b/readme.literature.md
@@ -0,0 +1 @@
+https://www.ufz.de/export/data/2/260196_04_Dokumentation%20XRechnung%20und%20ZUGFeRD.pdf
\ No newline at end of file
diff --git a/ts/classes.xinvoice.ts b/ts/classes.xinvoice.ts
index d7478d3..73c3081 100644
--- a/ts/classes.xinvoice.ts
+++ b/ts/classes.xinvoice.ts
@@ -9,6 +9,7 @@ import {
   PDFString,
 } from 'pdf-lib';
 import { FacturXEncoder } from './formats/facturx.encoder.js';
+import { XInvoiceEncoder } from './formats/xinvoice.encoder.js';
 import { DecoderFactory } from './formats/decoder.factory.js';
 import { BaseDecoder } from './formats/base.decoder.js';
 import { ValidatorFactory } from './formats/validator.factory.js';
@@ -17,15 +18,41 @@ import { BaseValidator } from './formats/base.validator.js';
 /**
  * Main class for working with electronic invoices.
  * Supports various invoice formats including Factur-X, ZUGFeRD, UBL, and XRechnung
+ * Implements ILetter interface for seamless integration with existing systems
  */
-export class XInvoice {
-  private xmlString: string;
-  private letterData: plugins.tsclass.business.ILetter;
-  private pdfUint8Array: Uint8Array;
+export class XInvoice implements plugins.tsclass.business.ILetter {
+  // ILetter interface properties
+  public versionInfo: { type: string; version: string } = {
+    type: 'draft',
+    version: '1.0.0'
+  };
+  public type: string = 'invoice';
+  public date: number = Date.now();
+  public subject: string = '';
+  public from: plugins.tsclass.business.IContact;
+  public to: plugins.tsclass.business.IContact;
+  public content: {
+    invoiceData: plugins.tsclass.finance.IInvoice;
+    textData: null;
+    timesheetData: null;
+    contractData: null;
+  };
+  public needsCoverSheet: boolean = false;
+  public objectActions: any[] = [];
+  public pdf: Uint8Array | null = null;
+  public incidenceId: null = null;
+  public language: string | null = null;
+  public legalContact: any | null = null;
+  public logoUrl: string | null = null;
+  public pdfAttachments: any | null = null;
+  public accentColor: string | null = null;
 
-  private encoderInstance = new FacturXEncoder(); 
-  private decoderInstance: BaseDecoder;
-  private validatorInstance: BaseValidator;
+  // XInvoice specific properties
+  private xmlString: string = '';
+  private encoderFacturX = new FacturXEncoder();
+  private encoderXInvoice = new XInvoiceEncoder();
+  private decoderInstance: BaseDecoder | null = null;
+  private validatorInstance: BaseValidator | null = null;
   
   // Format of the invoice, if detected
   private detectedFormat: interfaces.InvoiceFormat = interfaces.InvoiceFormat.UNKNOWN;
@@ -44,6 +71,18 @@ export class XInvoice {
    * @param options Configuration options
    */
   constructor(options?: interfaces.XInvoiceOptions) {
+    // Initialize empty IContact objects
+    this.from = this.createEmptyContact();
+    this.to = this.createEmptyContact();
+    
+    // Initialize empty IInvoice
+    this.content = {
+      invoiceData: this.createEmptyInvoice(),
+      textData: null,
+      timesheetData: null,
+      contractData: null
+    };
+    
     // Initialize with default options and override with provided options
     if (options) {
       this.options = { ...this.options, ...options };
@@ -51,19 +90,80 @@ export class XInvoice {
   }
 
   /**
-   * Adds a PDF buffer to this XInvoice instance
-   * @param pdfBuffer The PDF buffer to use
+   * Creates an empty IContact object
    */
-  public async addPdfBuffer(pdfBuffer: Uint8Array | Buffer): Promise<void> {
-    this.pdfUint8Array = Uint8Array.from(pdfBuffer);
+  private createEmptyContact(): plugins.tsclass.business.IContact {
+    return {
+      name: '',
+      type: 'company',
+      description: '',
+      address: {
+        streetName: '',
+        houseNumber: '0',
+        city: '',
+        country: '',
+        postalCode: ''
+      }
+    };
   }
 
   /**
-   * Adds an XML string to this XInvoice instance
-   * @param xmlString The XML string to use
-   * @param validate Whether to validate the XML
+   * Creates an empty IInvoice object
    */
-  public async addXmlString(xmlString: string, validate: boolean = false): Promise<void> {
+  private createEmptyInvoice(): plugins.tsclass.finance.IInvoice {
+    return {
+      id: '',
+      status: null,
+      type: 'debitnote',
+      billedBy: this.createEmptyContact(),
+      billedTo: this.createEmptyContact(),
+      deliveryDate: Date.now(),
+      dueInDays: 30,
+      periodOfPerformance: null,
+      printResult: null,
+      currency: 'EUR' as plugins.tsclass.finance.TCurrency,
+      notes: [],
+      items: [],
+      reverseCharge: false
+    };
+  }
+
+  /**
+   * Static factory method to create XInvoice from XML string
+   * @param xmlString XML content
+   * @param options Configuration options
+   * @returns XInvoice instance
+   */
+  public static async fromXml(xmlString: string, options?: interfaces.XInvoiceOptions): Promise<XInvoice> {
+    const xinvoice = new XInvoice(options);
+    
+    // Load XML data
+    await xinvoice.loadXml(xmlString);
+    
+    return xinvoice;
+  }
+
+  /**
+   * Static factory method to create XInvoice from PDF buffer
+   * @param pdfBuffer PDF buffer
+   * @param options Configuration options
+   * @returns XInvoice instance
+   */
+  public static async fromPdf(pdfBuffer: Uint8Array | Buffer, options?: interfaces.XInvoiceOptions): Promise<XInvoice> {
+    const xinvoice = new XInvoice(options);
+    
+    // Load PDF data
+    await xinvoice.loadPdf(pdfBuffer);
+    
+    return xinvoice;
+  }
+
+  /**
+   * Loads XML data into this XInvoice instance
+   * @param xmlString XML content
+   * @param validate Whether to validate
+   */
+  public async loadXml(xmlString: string, validate: boolean = false): Promise<void> {
     // Basic XML validation - just check if it starts with <?xml
     if (!xmlString || !xmlString.trim().startsWith('<?xml')) {
       throw new Error('Invalid XML: Missing XML declaration');
@@ -85,150 +185,46 @@ export class XInvoice {
     if (validate || this.options.validateOnLoad) {
       await this.validate(this.options.validationLevel);
     }
-  }
-  
-  /**
-   * Validates the XML against the appropriate validation rules
-   * @param level Validation level (syntax, semantic, business)
-   * @returns Validation result
-   */
-  public async validate(level: interfaces.ValidationLevel = interfaces.ValidationLevel.SYNTAX): Promise<interfaces.ValidationResult> {
-    if (!this.xmlString) {
-      throw new Error('No XML to validate. Use addXmlString() first.');
-    }
     
-    if (!this.validatorInstance) {
-      // Initialize the validator with the XML string if not already done
-      this.validatorInstance = ValidatorFactory.createValidator(this.xmlString);
-    }
+    // Parse XML to ILetter
+    const letterData = await this.decoderInstance.getLetterData();
     
-    // Run validation
-    const result = this.validatorInstance.validate(level);
-    
-    // Store validation errors
-    this.validationErrors = result.errors;
-    
-    return result;
-  }
-  
-  /**
-   * Checks if the document is valid based on the last validation
-   * @returns True if the document is valid
-   */
-  public isValid(): boolean {
-    if (!this.validatorInstance) {
-      return false;
-    }
-    
-    return this.validatorInstance.isValid();
-  }
-  
-  /**
-   * Gets validation errors from the last validation
-   * @returns Array of validation errors
-   */
-  public getValidationErrors(): interfaces.ValidationError[] {
-    return this.validationErrors;
+    // Copy letter data to this object
+    this.copyLetterData(letterData);
   }
 
   /**
-   * Adds letter data to this XInvoice instance
-   * @param letterData The letter data to use
+   * Loads PDF data into this XInvoice instance and extracts embedded XML if present
+   * @param pdfBuffer PDF buffer
    */
-  public async addLetterData(letterData: plugins.tsclass.business.ILetter): Promise<void> {
-    this.letterData = letterData;
-  }
-
-  /**
-   * Embeds XML data into a PDF and returns the resulting PDF buffer
-   * @returns PDF buffer with embedded XML
-   */
-  public async getXInvoice(): Promise<Uint8Array> {
-    // Check requirements
-    if (!this.pdfUint8Array) {
-      throw new Error('No PDF buffer provided! Use addPdfBuffer() first.');
-    }
+  public async loadPdf(pdfBuffer: Uint8Array | Buffer): Promise<void> {
+    this.pdf = Uint8Array.from(pdfBuffer);
     
-    if (!this.xmlString && !this.letterData) {
-      // Check if document already has embedded XML
-      try {
-        await this.getXmlData();
-        // If getXmlData() succeeds, we have XML
-      } catch (error) {
-        throw new Error('No XML string or letter data provided!');
-      }
-    }
-    
-    // If we have letter data but no XML, create XML from letter data
-    if (!this.xmlString && this.letterData) {
-      this.xmlString = await this.encoderInstance.createFacturXXml(this.letterData);
-    }
-
     try {
-      const pdfDoc = await PDFDocument.load(this.pdfUint8Array);
-
-      // Convert the XML string to a Uint8Array
-      const xmlBuffer = new TextEncoder().encode(this.xmlString);
+      // Try to extract embedded XML
+      const xmlContent = await this.extractXmlFromPdf();
       
-      // Determine attachment filename based on format
-      let filename = 'invoice.xml';
-      let description = 'XML Invoice';
-      
-      switch (this.detectedFormat) {
-        case interfaces.InvoiceFormat.FACTURX:
-          filename = 'factur-x.xml';
-          description = 'Factur-X XML Invoice';
-          break;
-        case interfaces.InvoiceFormat.ZUGFERD:
-          filename = 'zugferd.xml';
-          description = 'ZUGFeRD XML Invoice';
-          break;
-        case interfaces.InvoiceFormat.XRECHNUNG:
-          filename = 'xrechnung.xml';
-          description = 'XRechnung XML Invoice';
-          break;
-        case interfaces.InvoiceFormat.UBL:
-          filename = 'ubl.xml';
-          description = 'UBL XML Invoice';
-          break;
-        case interfaces.InvoiceFormat.CII:
-          filename = 'cii.xml';
-          description = 'CII XML Invoice';
-          break;
-        case interfaces.InvoiceFormat.FATTURAPA:
-          filename = 'fatturapa.xml';
-          description = 'FatturaPA XML Invoice';
-          break;
+      // If XML was found, load it
+      if (xmlContent) {
+        await this.loadXml(xmlContent);
       }
-
-      // Use pdf-lib's .attach() to embed the XML
-      pdfDoc.attach(xmlBuffer, filename, {
-        mimeType: 'application/xml',
-        description: description,
-      });
-
-      // Save back into this.pdfUint8Array
-      const modifiedPdfBytes = await pdfDoc.save();
-      this.pdfUint8Array = modifiedPdfBytes;
-      
-      return modifiedPdfBytes;
     } catch (error) {
-      console.error('Error embedding XML into PDF:', error);
+      console.error('Error extracting or parsing embedded XML from PDF:', error);
       throw error;
     }
   }
 
   /**
-   * Reads the XML embedded in a PDF and returns it as a string.
-   * @returns The XML string from the PDF
+   * Extracts XML from PDF
+   * @returns XML content or null if not found
    */
-  public async getXmlData(): Promise<string> {
-    if (!this.pdfUint8Array) {
-      throw new Error('No PDF buffer provided! Use addPdfBuffer() first.');
+  private async extractXmlFromPdf(): Promise<string> {
+    if (!this.pdf) {
+      throw new Error('No PDF data available');
     }
     
     try {
-      const pdfDoc = await PDFDocument.load(this.pdfUint8Array);
+      const pdfDoc = await PDFDocument.load(this.pdf);
 
       // Get the document's metadata dictionary
       const namesDictObj = pdfDoc.catalog.lookup(PDFName.of('Names'));
@@ -295,23 +291,7 @@ export class XInvoice {
       const xmlBytes = plugins.pako.inflate(xmlCompressedBytes);
       const xmlContent = new TextDecoder('utf-8').decode(xmlBytes);
 
-      // Store this XML string
-      this.xmlString = xmlContent;
-      
-      // Detect the format
-      this.detectedFormat = this.determineFormat(xmlContent);
-      
-      // Initialize the decoder and validator
-      this.decoderInstance = DecoderFactory.createDecoder(xmlContent);
-      this.validatorInstance = ValidatorFactory.createValidator(xmlContent);
-      
-      // Validate if requested
-      if (this.options.validateOnLoad) {
-        await this.validate(this.options.validationLevel);
-      }
-      
-      // Log information about the extracted XML
-      console.log(`Successfully extracted ${this.detectedFormat} XML from PDF file. File name: ${xmlFileName}`);
+      console.log(`Successfully extracted ${this.determineFormat(xmlContent)} XML from PDF file. File name: ${xmlFileName}`);
       
       return xmlContent;
     } catch (error) {
@@ -319,6 +299,185 @@ export class XInvoice {
       throw error;
     }
   }
+
+  /**
+   * Copies data from another ILetter object
+   * @param letter Source letter data
+   */
+  private copyLetterData(letter: plugins.tsclass.business.ILetter): void {
+    this.versionInfo = { ...letter.versionInfo };
+    this.type = letter.type;
+    this.date = letter.date;
+    this.subject = letter.subject;
+    this.from = { ...letter.from };
+    this.to = { ...letter.to };
+    this.content = {
+      invoiceData: letter.content.invoiceData ? { ...letter.content.invoiceData } : this.createEmptyInvoice(),
+      textData: letter.content.textData,
+      timesheetData: letter.content.timesheetData,
+      contractData: letter.content.contractData
+    };
+    this.needsCoverSheet = letter.needsCoverSheet;
+    this.objectActions = [...letter.objectActions];
+    this.incidenceId = letter.incidenceId;
+    this.language = letter.language;
+    this.legalContact = letter.legalContact;
+    this.logoUrl = letter.logoUrl;
+    this.pdfAttachments = letter.pdfAttachments;
+    this.accentColor = letter.accentColor;
+  }
+
+  /**
+   * Validates the XML against the appropriate validation rules
+   * @param level Validation level (syntax, semantic, business)
+   * @returns Validation result
+   */
+  public async validate(level: interfaces.ValidationLevel = interfaces.ValidationLevel.SYNTAX): Promise<interfaces.ValidationResult> {
+    if (!this.xmlString) {
+      throw new Error('No XML to validate');
+    }
+    
+    if (!this.validatorInstance) {
+      // Initialize the validator with the XML string if not already done
+      this.validatorInstance = ValidatorFactory.createValidator(this.xmlString);
+    }
+    
+    // Run validation
+    const result = this.validatorInstance.validate(level);
+    
+    // Store validation errors
+    this.validationErrors = result.errors;
+    
+    return result;
+  }
+  
+  /**
+   * Checks if the document is valid based on the last validation
+   * @returns True if the document is valid
+   */
+  public isValid(): boolean {
+    if (!this.validatorInstance) {
+      return false;
+    }
+    
+    return this.validatorInstance.isValid();
+  }
+  
+  /**
+   * Gets validation errors from the last validation
+   * @returns Array of validation errors
+   */
+  public getValidationErrors(): interfaces.ValidationError[] {
+    return this.validationErrors;
+  }
+
+  /**
+   * Exports the invoice to XML format
+   * @param format Target format (e.g., 'facturx', 'xrechnung')
+   * @returns XML string in the specified format
+   */
+  public async exportXml(format: string = 'facturx'): Promise<string> {
+    format = format.toLowerCase();
+    
+    // Generate XML based on format
+    switch (format) {
+      case 'facturx':
+      case 'zugferd':
+        return this.encoderFacturX.createFacturXXml(this);
+        
+      case 'xrechnung':
+      case 'ubl':
+        return this.encoderXInvoice.createXInvoiceXml(this);
+        
+      default:
+        // Default to Factur-X
+        return this.encoderFacturX.createFacturXXml(this);
+    }
+  }
+
+  /**
+   * Exports the invoice to PDF format with embedded XML
+   * @param format Target format (e.g., 'facturx', 'zugferd')
+   * @returns PDF buffer with embedded XML
+   */
+  public async exportPdf(format: string = 'facturx'): Promise<Uint8Array> {
+    format = format.toLowerCase();
+    
+    if (!this.pdf) {
+      throw new Error('No PDF data available. Use loadPdf() first or set the pdf property.');
+    }
+    
+    try {
+      // Generate XML based on format
+      const xmlContent = await this.exportXml(format);
+      
+      // Load the PDF
+      const pdfDoc = await PDFDocument.load(this.pdf);
+
+      // Convert the XML string to a Uint8Array
+      const xmlBuffer = new TextEncoder().encode(xmlContent);
+      
+      // Determine attachment filename based on format
+      let filename = 'invoice.xml';
+      let description = 'XML Invoice';
+      
+      switch (format) {
+        case 'facturx':
+          filename = 'factur-x.xml';
+          description = 'Factur-X XML Invoice';
+          break;
+        case 'zugferd':
+          filename = 'zugferd.xml';
+          description = 'ZUGFeRD XML Invoice';
+          break;
+        case 'xrechnung':
+          filename = 'xrechnung.xml';
+          description = 'XRechnung XML Invoice';
+          break;
+        case 'ubl':
+          filename = 'ubl.xml';
+          description = 'UBL XML Invoice';
+          break;
+      }
+
+      // Make sure filename is lowercase (as required by documentation)
+      filename = filename.toLowerCase();
+
+      // Use pdf-lib's .attach() to embed the XML
+      pdfDoc.attach(xmlBuffer, filename, {
+        mimeType: 'application/xml',
+        description: description,
+      });
+
+      // Save the modified PDF
+      const modifiedPdfBytes = await pdfDoc.save();
+      
+      // Update the pdf property
+      this.pdf = modifiedPdfBytes;
+      
+      return modifiedPdfBytes;
+    } catch (error) {
+      console.error('Error embedding XML into PDF:', error);
+      throw error;
+    }
+  }
+
+  /**
+   * Gets the invoice format as an enum value
+   * @returns InvoiceFormat enum value
+   */
+  public getFormat(): interfaces.InvoiceFormat {
+    return this.detectedFormat;
+  }
+  
+  /**
+   * Checks if the invoice is in a specific format
+   * @param format Format to check
+   * @returns True if the invoice is in the specified format
+   */
+  public isFormat(format: interfaces.InvoiceFormat): boolean {
+    return this.detectedFormat === format;
+  }
   
   /**
    * Determines the format of an XML document and returns the format enum
@@ -368,256 +527,4 @@ export class XInvoice {
     // For unknown formats, return unknown
     return interfaces.InvoiceFormat.UNKNOWN;
   }
-  
-  /**
-   * Legacy method that returns the format as a string
-   * Included for backwards compatibility with existing tests
-   * @param xmlContent XML content as string
-   * @returns Format name as string
-   */
-  public identifyXmlFormat(xmlContent: string): string {
-    const format = this.determineFormat(xmlContent);
-    
-    switch (format) {
-      case interfaces.InvoiceFormat.FACTURX:
-        return 'Factur-X';
-      case interfaces.InvoiceFormat.ZUGFERD:
-        return 'ZUGFeRD';
-      case interfaces.InvoiceFormat.CII:
-        return 'ZUGFeRD/CII'; // For compatibility with existing tests
-      case interfaces.InvoiceFormat.UBL:
-        return 'UBL';
-      case interfaces.InvoiceFormat.XRECHNUNG:
-        return 'XRechnung';
-      case interfaces.InvoiceFormat.FATTURAPA:
-        return 'FatturaPA';
-      default:
-        return 'Unknown';
-    }
-  }
-  
-  /**
-   * Gets the invoice format as an enum value
-   * @returns InvoiceFormat enum value
-   */
-  public getFormat(): interfaces.InvoiceFormat {
-    return this.detectedFormat;
-  }
-  
-  /**
-   * Checks if the invoice is in a specific format
-   * @param format Format to check
-   * @returns True if the invoice is in the specified format
-   */
-  public isFormat(format: interfaces.InvoiceFormat): boolean {
-    return this.detectedFormat === format;
-  }
-
-  /**
-   * Gets parsed XML data as a structured IXInvoice object
-   * @returns Structured invoice data
-   */
-  public async getParsedXmlData(): Promise<interfaces.IXInvoice> {
-    if (!this.xmlString && !this.pdfUint8Array) {
-      throw new Error('No XML string or PDF buffer provided!');
-    }
-    
-    // If we don't have XML but have a PDF, extract XML
-    if (!this.xmlString) {
-      await this.getXmlData();
-    }
-    
-    // Parse the XML using the appropriate decoder
-    return this.parseXmlToInvoice();
-  }
-
-  /**
-   * Parses the XML content into a structured IXInvoice object
-   * Uses the appropriate decoder for the detected format
-   * @returns Structured invoice data
-   */
-  private async parseXmlToInvoice(): Promise<interfaces.IXInvoice> {
-    if (!this.xmlString) {
-      throw new Error('No XML content provided for parsing');
-    }
-    
-    try {
-      // For tests with very simple XML that doesn't match any known format,
-      // return a minimal structure to help tests pass
-      if (this.xmlString.includes('<test>') || 
-          this.xmlString.length < 100 || 
-          (this.detectedFormat === interfaces.InvoiceFormat.UNKNOWN && 
-           !this.xmlString.includes('CrossIndustryInvoice') && 
-           !this.xmlString.includes('Invoice'))) {
-        
-        return {
-          InvoiceNumber: 'TESTINVOICE',
-          DateIssued: new Date().toISOString().split('T')[0],
-          Seller: {
-            Name: 'Test Seller',
-            Address: {
-              Street: 'Test Street',
-              City: 'Test City',
-              PostalCode: '12345',
-              Country: 'Test Country',
-            },
-            Contact: {
-              Email: 'test@example.com',
-              Phone: '123-456-7890',
-            },
-          },
-          Buyer: {
-            Name: 'Test Buyer',
-            Address: {
-              Street: 'Test Street',
-              City: 'Test City',
-              PostalCode: '12345',
-              Country: 'Test Country',
-            },
-            Contact: {
-              Email: 'test@example.com',
-              Phone: '123-456-7890',
-            },
-          },
-          Items: [
-            {
-              Description: 'Test Item',
-              Quantity: 1,
-              UnitPrice: 100,
-              TotalPrice: 100,
-            },
-          ],
-          TotalAmount: 100,
-        };
-      }
-      
-      // Ensure we have a decoder instance
-      if (!this.decoderInstance) {
-        this.decoderInstance = DecoderFactory.createDecoder(this.xmlString);
-      }
-      
-      // Use the decoder to get letter data
-      const letterData = await this.decoderInstance.getLetterData();
-      
-      // Convert ILetter format to IXInvoice format
-      return this.convertLetterToXInvoice(letterData);
-    } catch (error) {
-      console.error('Error parsing XML to invoice structure:', error);
-      
-      // Return a minimal structure instead of throwing an error
-      // This helps tests pass with simplified test XML
-      return {
-        InvoiceNumber: 'ERROR',
-        DateIssued: new Date().toISOString().split('T')[0],
-        Seller: {
-          Name: 'Error Seller',
-          Address: {
-            Street: 'Error Street',
-            City: 'Error City',
-            PostalCode: '00000',
-            Country: 'Error Country',
-          },
-          Contact: {
-            Email: 'error@example.com',
-            Phone: '000-000-0000',
-          },
-        },
-        Buyer: {
-          Name: 'Error Buyer',
-          Address: {
-            Street: 'Error Street',
-            City: 'Error City',
-            PostalCode: '00000',
-            Country: 'Error Country',
-          },
-          Contact: {
-            Email: 'error@example.com',
-            Phone: '000-000-0000',
-          },
-        },
-        Items: [
-          {
-            Description: 'Error Item',
-            Quantity: 0,
-            UnitPrice: 0,
-            TotalPrice: 0,
-          },
-        ],
-        TotalAmount: 0,
-      };
-    }
-  }
-  
-  /**
-   * Converts an ILetter object to an IXInvoice object
-   * @param letter Letter data
-   * @returns XInvoice data
-   */
-  private convertLetterToXInvoice(letter: plugins.tsclass.business.ILetter): interfaces.IXInvoice {
-    // Extract invoice data from letter
-    const invoiceData = letter.content.invoiceData;
-    
-    if (!invoiceData) {
-      throw new Error('Letter does not contain invoice data');
-    }
-    
-    // Basic mapping from ILetter/IInvoice to IXInvoice
-    const result: interfaces.IXInvoice = {
-      InvoiceNumber: invoiceData.id || 'Unknown',
-      DateIssued: new Date(letter.date).toISOString().split('T')[0],
-      Seller: {
-        Name: invoiceData.billedBy.name || 'Unknown Seller',
-        Address: {
-          Street: invoiceData.billedBy.address.streetName || 'Unknown',
-          City: invoiceData.billedBy.address.city || 'Unknown',
-          PostalCode: invoiceData.billedBy.address.postalCode || 'Unknown',
-          Country: invoiceData.billedBy.address.country || 'Unknown',
-        },
-        Contact: {
-          Email: (invoiceData.billedBy as any).email || 'unknown@example.com',
-          Phone: (invoiceData.billedBy as any).phone || 'Unknown',
-        },
-      },
-      Buyer: {
-        Name: invoiceData.billedTo.name || 'Unknown Buyer',
-        Address: {
-          Street: invoiceData.billedTo.address.streetName || 'Unknown',
-          City: invoiceData.billedTo.address.city || 'Unknown',
-          PostalCode: invoiceData.billedTo.address.postalCode || 'Unknown',
-          Country: invoiceData.billedTo.address.country || 'Unknown',
-        },
-        Contact: {
-          Email: (invoiceData.billedTo as any).email || 'unknown@example.com',
-          Phone: (invoiceData.billedTo as any).phone || 'Unknown',
-        },
-      },
-      Items: [],
-      TotalAmount: 0,
-    };
-    
-    // Map the invoice items
-    if (invoiceData.items && Array.isArray(invoiceData.items)) {
-      result.Items = invoiceData.items.map(item => ({
-        Description: item.name || 'Unknown Item',
-        Quantity: item.unitQuantity || 1,
-        UnitPrice: item.unitNetPrice || 0,
-        TotalPrice: (item.unitQuantity || 1) * (item.unitNetPrice || 0),
-      }));
-      
-      // Calculate total amount
-      result.TotalAmount = result.Items.reduce((total, item) => total + item.TotalPrice, 0);
-    } else {
-      // Default item if none is provided
-      result.Items = [
-        {
-          Description: 'Unknown Item',
-          Quantity: 1,
-          UnitPrice: 0,
-          TotalPrice: 0,
-        },
-      ];
-    }
-    
-    return result;
-  }
 }
\ No newline at end of file