einvoice/test/suite/einvoice_pdf-operations/test.pdf-05.pdfa3-creation.ts

790 lines
30 KiB
TypeScript
Raw Normal View History

2025-05-25 19:45:37 +00:00
import { tap, expect } from '@git.zone/tstest/tapbundle';
import * as plugins from '../../../ts/plugins.ts';
import { EInvoice } from '../../../ts/classes.xinvoice.ts';
import { CorpusLoader } from '../../helpers/corpus.loader.ts';
import { PerformanceTracker } from '../../helpers/performance.tracker.ts';
const testTimeout = 300000; // 5 minutes timeout for PDF processing
// PDF-05: PDF/A-3 Creation
// Tests creation of PDF/A-3 compliant documents with embedded XML attachments
// according to ISO 19005-3 standard and ZUGFeRD/Factur-X requirements
tap.test('PDF-05: PDF/A-3 Creation - Basic PDF/A-3 Generation', async (tools) => {
const startTime = Date.now();
// Test basic PDF/A-3 creation functionality
try {
const sampleXml = `<?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
<ID>PDFA3-TEST-001</ID>
<IssueDate>2024-01-01</IssueDate>
<InvoiceTypeCode>380</InvoiceTypeCode>
<DocumentCurrencyCode>EUR</DocumentCurrencyCode>
<AccountingSupplierParty>
<Party>
<PartyName>
<Name>PDF/A-3 Test Supplier</Name>
</PartyName>
<PostalAddress>
<StreetName>Test Street 123</StreetName>
<CityName>Test City</CityName>
<PostalZone>12345</PostalZone>
<Country>
<IdentificationCode>DE</IdentificationCode>
</Country>
</PostalAddress>
</Party>
</AccountingSupplierParty>
<AccountingCustomerParty>
<Party>
<PartyName>
<Name>PDF/A-3 Test Customer</Name>
</PartyName>
</Party>
</AccountingCustomerParty>
<InvoiceLine>
<ID>1</ID>
<InvoicedQuantity unitCode="C62">1</InvoicedQuantity>
<LineExtensionAmount currencyID="EUR">100.00</LineExtensionAmount>
<Item>
<Name>PDF/A-3 Test Item</Name>
</Item>
<Price>
<PriceAmount currencyID="EUR">100.00</PriceAmount>
</Price>
</InvoiceLine>
<TaxTotal>
<TaxAmount currencyID="EUR">19.00</TaxAmount>
</TaxTotal>
<LegalMonetaryTotal>
<LineExtensionAmount currencyID="EUR">100.00</LineExtensionAmount>
<TaxExclusiveAmount currencyID="EUR">100.00</TaxExclusiveAmount>
<TaxInclusiveAmount currencyID="EUR">119.00</TaxInclusiveAmount>
<PayableAmount currencyID="EUR">119.00</PayableAmount>
</LegalMonetaryTotal>
</Invoice>`;
const invoice = new EInvoice();
const parseResult = await invoice.fromXmlString(sampleXml);
expect(parseResult).toBeTruthy();
// Test PDF/A-3 creation if supported
if (typeof invoice.createPdfA3 === 'function') {
tools.log('Testing PDF/A-3 creation...');
const outputPath = plugins.path.join(process.cwd(), '.nogit', 'test-pdfa3-basic.pdf');
await plugins.fs.ensureDir(plugins.path.dirname(outputPath));
try {
const pdfA3Options = {
outputPath: outputPath,
xmlContent: sampleXml,
attachmentName: 'ZUGFeRD-invoice.xml',
pdfA3Compliance: true,
title: 'Electronic Invoice PDFA3-TEST-001',
author: 'EInvoice Test Suite',
subject: 'PDF/A-3 compliant invoice',
keywords: 'invoice, electronic, PDF/A-3, ZUGFeRD'
};
const creationResult = await invoice.createPdfA3(pdfA3Options);
if (creationResult) {
tools.log('✓ PDF/A-3 creation completed');
// Verify output file
const outputExists = await plugins.fs.pathExists(outputPath);
if (outputExists) {
const outputStats = await plugins.fs.stat(outputPath);
tools.log(`✓ PDF/A-3 file created: ${(outputStats.size / 1024).toFixed(1)}KB`);
// Basic PDF validation (check if it starts with PDF header)
const pdfHeader = await plugins.fs.readFile(outputPath, { encoding: 'binary' });
if (pdfHeader.startsWith('%PDF-')) {
tools.log('✓ Valid PDF header detected');
// Check for PDF/A-3 markers if possible
const pdfContent = pdfHeader.substring(0, 1024);
if (pdfContent.includes('PDF/A-3') || pdfContent.includes('PDFA-3')) {
tools.log('✓ PDF/A-3 markers detected');
}
} else {
tools.log('⚠ Invalid PDF header');
}
// Test XML extraction from created PDF/A-3
try {
const extractionInvoice = new EInvoice();
const extractionResult = await extractionInvoice.fromFile(outputPath);
if (extractionResult) {
const extractedXml = await extractionInvoice.toXmlString();
if (extractedXml.includes('PDFA3-TEST-001')) {
tools.log('✓ XML successfully extracted from PDF/A-3');
} else {
tools.log('⚠ Extracted XML does not contain expected content');
}
} else {
tools.log('⚠ Could not extract XML from created PDF/A-3');
}
} catch (extractionError) {
tools.log(`⚠ XML extraction test failed: ${extractionError.message}`);
}
// Clean up
await plugins.fs.remove(outputPath);
} else {
tools.log('⚠ PDF/A-3 file not created');
}
} else {
tools.log('⚠ PDF/A-3 creation returned no result');
}
} catch (creationError) {
tools.log(`⚠ PDF/A-3 creation failed: ${creationError.message}`);
}
} else if (typeof invoice.toPdf === 'function') {
tools.log('⚠ Specific PDF/A-3 creation not available, testing general PDF creation...');
try {
const pdfResult = await invoice.toPdf({
pdfACompliance: 'PDF/A-3'
});
if (pdfResult) {
tools.log('✓ General PDF creation with PDF/A-3 compliance completed');
}
} catch (pdfError) {
tools.log(`⚠ General PDF creation failed: ${pdfError.message}`);
}
} else {
tools.log('⚠ PDF/A-3 creation functionality not available');
}
} catch (error) {
tools.log(`Basic PDF/A-3 creation test failed: ${error.message}`);
}
const duration = Date.now() - startTime;
PerformanceTracker.recordMetric('pdfa3-creation-basic', duration);
});
tap.test('PDF-05: PDF/A-3 Creation - Compliance Levels', async (tools) => {
const startTime = Date.now();
// Test different PDF/A-3 compliance levels (A, B, U)
const complianceLevels = [
{
level: 'PDF/A-3B',
description: 'PDF/A-3 Level B (visual appearance)',
strictness: 'medium'
},
{
level: 'PDF/A-3A',
description: 'PDF/A-3 Level A (accessibility)',
strictness: 'high'
},
{
level: 'PDF/A-3U',
description: 'PDF/A-3 Level U (Unicode)',
strictness: 'medium'
}
];
const testXml = `<?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
<ID>COMPLIANCE-TEST-001</ID>
<IssueDate>2024-01-01</IssueDate>
<InvoiceTypeCode>380</InvoiceTypeCode>
<DocumentCurrencyCode>EUR</DocumentCurrencyCode>
<LegalMonetaryTotal>
<PayableAmount currencyID="EUR">100.00</PayableAmount>
</LegalMonetaryTotal>
</Invoice>`;
for (const compliance of complianceLevels) {
tools.log(`Testing ${compliance.description}...`);
try {
const invoice = new EInvoice();
await invoice.fromXmlString(testXml);
if (typeof invoice.createPdfA3 === 'function') {
const outputPath = plugins.path.join(process.cwd(), '.nogit', `test-${compliance.level.toLowerCase().replace(/\//g, '-')}.pdf`);
await plugins.fs.ensureDir(plugins.path.dirname(outputPath));
const complianceOptions = {
outputPath: outputPath,
xmlContent: testXml,
attachmentName: 'invoice.xml',
complianceLevel: compliance.level,
title: `${compliance.level} Test Invoice`,
validateCompliance: true
};
try {
const creationResult = await invoice.createPdfA3(complianceOptions);
if (creationResult) {
tools.log(`${compliance.level} creation completed`);
const outputExists = await plugins.fs.pathExists(outputPath);
if (outputExists) {
const outputStats = await plugins.fs.stat(outputPath);
tools.log(` File size: ${(outputStats.size / 1024).toFixed(1)}KB`);
// Basic compliance validation
const pdfContent = await plugins.fs.readFile(outputPath, { encoding: 'binary' });
const headerSection = pdfContent.substring(0, 2048);
// Look for PDF/A compliance indicators
if (headerSection.includes('PDF/A-3') ||
headerSection.includes('PDFA-3') ||
headerSection.includes(compliance.level)) {
tools.log(`${compliance.level} compliance indicators found`);
} else {
tools.log(`${compliance.level} compliance indicators not clearly detected`);
}
// Clean up
await plugins.fs.remove(outputPath);
} else {
tools.log(`${compliance.level} file not created`);
}
} else {
tools.log(`${compliance.level} creation returned no result`);
}
} catch (complianceError) {
tools.log(`${compliance.level} creation failed: ${complianceError.message}`);
}
} else {
tools.log(`${compliance.level} creation not supported`);
}
} catch (error) {
tools.log(`${compliance.level} test failed: ${error.message}`);
}
}
const duration = Date.now() - startTime;
PerformanceTracker.recordMetric('pdfa3-creation-compliance-levels', duration);
});
tap.test('PDF-05: PDF/A-3 Creation - ZUGFeRD Profile Creation', async (tools) => {
const startTime = Date.now();
// Test PDF/A-3 creation with specific ZUGFeRD/Factur-X profiles
const zugferdProfiles = [
{
profile: 'MINIMUM',
xml: `<?xml version="1.0" encoding="UTF-8"?>
<CrossIndustryInvoice xmlns="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100">
<ExchangedDocumentContext>
<GuidelineSpecifiedDocumentContextParameter>
<ID>urn:cen.eu:en16931:2017#compliant#urn:zugferd.de:2p1:minimum</ID>
</GuidelineSpecifiedDocumentContextParameter>
</ExchangedDocumentContext>
<ExchangedDocument>
<ID>ZUGFERD-MIN-001</ID>
<TypeCode>380</TypeCode>
<IssueDateTime>
<DateTimeString format="102">20240101</DateTimeString>
</IssueDateTime>
</ExchangedDocument>
<SupplyChainTradeTransaction>
<ApplicableHeaderTradeSettlement>
<InvoiceCurrencyCode>EUR</InvoiceCurrencyCode>
<SpecifiedTradeSettlementHeaderMonetarySummation>
<DuePayableAmount>100.00</DuePayableAmount>
</SpecifiedTradeSettlementHeaderMonetarySummation>
</ApplicableHeaderTradeSettlement>
</SupplyChainTradeTransaction>
</CrossIndustryInvoice>`
},
{
profile: 'BASIC',
xml: `<?xml version="1.0" encoding="UTF-8"?>
<CrossIndustryInvoice xmlns="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100">
<ExchangedDocumentContext>
<GuidelineSpecifiedDocumentContextParameter>
<ID>urn:cen.eu:en16931:2017#compliant#urn:zugferd.de:2p1:basic</ID>
</GuidelineSpecifiedDocumentContextParameter>
</ExchangedDocumentContext>
<ExchangedDocument>
<ID>ZUGFERD-BASIC-001</ID>
<TypeCode>380</TypeCode>
<IssueDateTime>
<DateTimeString format="102">20240101</DateTimeString>
</IssueDateTime>
</ExchangedDocument>
<SupplyChainTradeTransaction>
<ApplicableHeaderTradeAgreement>
<SellerTradeParty>
<Name>ZUGFeRD Test Supplier</Name>
</SellerTradeParty>
<BuyerTradeParty>
<Name>ZUGFeRD Test Customer</Name>
</BuyerTradeParty>
</ApplicableHeaderTradeAgreement>
<ApplicableHeaderTradeSettlement>
<InvoiceCurrencyCode>EUR</InvoiceCurrencyCode>
<SpecifiedTradeSettlementHeaderMonetarySummation>
<TaxBasisTotalAmount>100.00</TaxBasisTotalAmount>
<TaxTotalAmount currencyID="EUR">19.00</TaxTotalAmount>
<GrandTotalAmount>119.00</GrandTotalAmount>
<DuePayableAmount>119.00</DuePayableAmount>
</SpecifiedTradeSettlementHeaderMonetarySummation>
</ApplicableHeaderTradeSettlement>
</SupplyChainTradeTransaction>
</CrossIndustryInvoice>`
},
{
profile: 'COMFORT',
xml: `<?xml version="1.0" encoding="UTF-8"?>
<CrossIndustryInvoice xmlns="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100">
<ExchangedDocumentContext>
<GuidelineSpecifiedDocumentContextParameter>
<ID>urn:cen.eu:en16931:2017#compliant#urn:zugferd.de:2p1:comfort</ID>
</GuidelineSpecifiedDocumentContextParameter>
</ExchangedDocumentContext>
<ExchangedDocument>
<ID>ZUGFERD-COMFORT-001</ID>
<TypeCode>380</TypeCode>
<IssueDateTime>
<DateTimeString format="102">20240101</DateTimeString>
</IssueDateTime>
</ExchangedDocument>
<SupplyChainTradeTransaction>
<IncludedSupplyChainTradeLineItem>
<AssociatedDocumentLineDocument>
<LineID>1</LineID>
</AssociatedDocumentLineDocument>
<SpecifiedTradeProduct>
<Name>ZUGFeRD Test Product</Name>
</SpecifiedTradeProduct>
<SpecifiedLineTradeAgreement>
<NetPriceProductTradePrice>
<ChargeAmount>100.00</ChargeAmount>
</NetPriceProductTradePrice>
</SpecifiedLineTradeAgreement>
<SpecifiedLineTradeSettlement>
<SpecifiedTradeSettlementLineMonetarySummation>
<LineTotalAmount>100.00</LineTotalAmount>
</SpecifiedTradeSettlementLineMonetarySummation>
</SpecifiedLineTradeSettlement>
</IncludedSupplyChainTradeLineItem>
<ApplicableHeaderTradeSettlement>
<InvoiceCurrencyCode>EUR</InvoiceCurrencyCode>
<SpecifiedTradeSettlementHeaderMonetarySummation>
<LineTotalAmount>100.00</LineTotalAmount>
<TaxBasisTotalAmount>100.00</TaxBasisTotalAmount>
<TaxTotalAmount currencyID="EUR">19.00</TaxTotalAmount>
<GrandTotalAmount>119.00</GrandTotalAmount>
<DuePayableAmount>119.00</DuePayableAmount>
</SpecifiedTradeSettlementHeaderMonetarySummation>
</ApplicableHeaderTradeSettlement>
</SupplyChainTradeTransaction>
</CrossIndustryInvoice>`
}
];
for (const zugferdTest of zugferdProfiles) {
tools.log(`Testing ZUGFeRD ${zugferdTest.profile} profile PDF/A-3 creation...`);
try {
const invoice = new EInvoice();
await invoice.fromXmlString(zugferdTest.xml);
if (typeof invoice.createPdfA3 === 'function') {
const outputPath = plugins.path.join(process.cwd(), '.nogit', `test-zugferd-${zugferdTest.profile.toLowerCase()}.pdf`);
await plugins.fs.ensureDir(plugins.path.dirname(outputPath));
const zugferdOptions = {
outputPath: outputPath,
xmlContent: zugferdTest.xml,
attachmentName: 'ZUGFeRD-invoice.xml',
zugferdProfile: zugferdTest.profile,
zugferdVersion: '2.1',
complianceLevel: 'PDF/A-3B',
title: `ZUGFeRD ${zugferdTest.profile} Invoice`,
conformanceLevel: 'PDFA_3B'
};
try {
const creationResult = await invoice.createPdfA3(zugferdOptions);
if (creationResult) {
tools.log(`✓ ZUGFeRD ${zugferdTest.profile} PDF/A-3 creation completed`);
const outputExists = await plugins.fs.pathExists(outputPath);
if (outputExists) {
const outputStats = await plugins.fs.stat(outputPath);
tools.log(` File size: ${(outputStats.size / 1024).toFixed(1)}KB`);
// Test round-trip (extraction from created PDF)
try {
const extractionInvoice = new EInvoice();
const extractionResult = await extractionInvoice.fromFile(outputPath);
if (extractionResult) {
const extractedXml = await extractionInvoice.toXmlString();
const expectedId = `ZUGFERD-${zugferdTest.profile}-001`;
if (extractedXml.includes(expectedId)) {
tools.log(` ✓ Round-trip successful - extracted XML contains ${expectedId}`);
} else {
tools.log(` ⚠ Round-trip issue - expected ID ${expectedId} not found`);
}
// Check for profile-specific elements
if (zugferdTest.profile === 'COMFORT' && extractedXml.includes('IncludedSupplyChainTradeLineItem')) {
tools.log(` ✓ COMFORT profile line items preserved`);
}
} else {
tools.log(` ⚠ Round-trip failed - could not extract XML`);
}
} catch (extractionError) {
tools.log(` ⚠ Round-trip test failed: ${extractionError.message}`);
}
// Clean up
await plugins.fs.remove(outputPath);
} else {
tools.log(` ⚠ ZUGFeRD ${zugferdTest.profile} file not created`);
}
} else {
tools.log(`⚠ ZUGFeRD ${zugferdTest.profile} creation returned no result`);
}
} catch (creationError) {
tools.log(`⚠ ZUGFeRD ${zugferdTest.profile} creation failed: ${creationError.message}`);
}
} else {
tools.log(`⚠ ZUGFeRD ${zugferdTest.profile} PDF/A-3 creation not supported`);
}
} catch (error) {
tools.log(`✗ ZUGFeRD ${zugferdTest.profile} test failed: ${error.message}`);
}
}
const duration = Date.now() - startTime;
PerformanceTracker.recordMetric('pdfa3-creation-zugferd-profiles', duration);
});
tap.test('PDF-05: PDF/A-3 Creation - Metadata and Accessibility', async (tools) => {
const startTime = Date.now();
// Test PDF/A-3 creation with comprehensive metadata and accessibility features
const testXml = `<?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
<ID>METADATA-ACCESSIBILITY-001</ID>
<IssueDate>2024-01-01</IssueDate>
<InvoiceTypeCode>380</InvoiceTypeCode>
<DocumentCurrencyCode>EUR</DocumentCurrencyCode>
<LegalMonetaryTotal>
<PayableAmount currencyID="EUR">100.00</PayableAmount>
</LegalMonetaryTotal>
</Invoice>`;
const metadataTests = [
{
name: 'Comprehensive Metadata',
options: {
title: 'Electronic Invoice METADATA-ACCESSIBILITY-001',
author: 'EInvoice Test Suite',
subject: 'PDF/A-3 compliant invoice with comprehensive metadata',
keywords: 'invoice, electronic, PDF/A-3, ZUGFeRD, accessible',
creator: 'EInvoice PDF Generator',
producer: 'EInvoice Test Framework',
creationDate: new Date('2024-01-01'),
modificationDate: new Date(),
language: 'en-US'
}
},
{
name: 'Accessibility Features',
options: {
title: 'Accessible Electronic Invoice',
tagged: true, // Structured PDF for screen readers
displayDocTitle: true,
linearized: true, // Fast web view
complianceLevel: 'PDF/A-3A', // Accessibility compliance
structuredPdf: true
}
},
{
name: 'Internationalization',
options: {
title: 'Elektronische Rechnung / Facture Électronique',
language: 'de-DE',
keywords: 'Rechnung, elektronisch, PDF/A-3, ZUGFeRD, Factur-X',
unicodeSupport: true,
characterEncoding: 'UTF-8'
}
}
];
for (const metadataTest of metadataTests) {
tools.log(`Testing ${metadataTest.name}...`);
try {
const invoice = new EInvoice();
await invoice.fromXmlString(testXml);
if (typeof invoice.createPdfA3 === 'function') {
const outputPath = plugins.path.join(process.cwd(), '.nogit', `test-${metadataTest.name.toLowerCase().replace(/\s+/g, '-')}.pdf`);
await plugins.fs.ensureDir(plugins.path.dirname(outputPath));
const creationOptions = {
outputPath: outputPath,
xmlContent: testXml,
attachmentName: 'invoice.xml',
complianceLevel: 'PDF/A-3B',
...metadataTest.options
};
try {
const creationResult = await invoice.createPdfA3(creationOptions);
if (creationResult) {
tools.log(`${metadataTest.name} PDF/A-3 creation completed`);
const outputExists = await plugins.fs.pathExists(outputPath);
if (outputExists) {
const outputStats = await plugins.fs.stat(outputPath);
tools.log(` File size: ${(outputStats.size / 1024).toFixed(1)}KB`);
// Basic metadata validation by reading PDF content
const pdfContent = await plugins.fs.readFile(outputPath, { encoding: 'binary' });
// Check for metadata presence (simplified check)
if (metadataTest.options.title && pdfContent.includes(metadataTest.options.title)) {
tools.log(` ✓ Title metadata preserved`);
}
if (metadataTest.options.author && pdfContent.includes(metadataTest.options.author)) {
tools.log(` ✓ Author metadata preserved`);
}
if (metadataTest.options.keywords && metadataTest.options.keywords.split(',').some(keyword =>
pdfContent.includes(keyword.trim()))) {
tools.log(` ✓ Keywords metadata preserved`);
}
// Check for accessibility features
if (metadataTest.options.tagged && (pdfContent.includes('/StructTreeRoot') || pdfContent.includes('/Marked'))) {
tools.log(` ✓ PDF structure/tagging detected`);
}
// Check for compliance level
if (metadataTest.options.complianceLevel && pdfContent.includes(metadataTest.options.complianceLevel)) {
tools.log(` ✓ Compliance level preserved`);
}
// Clean up
await plugins.fs.remove(outputPath);
} else {
tools.log(`${metadataTest.name} file not created`);
}
} else {
tools.log(`${metadataTest.name} creation returned no result`);
}
} catch (creationError) {
tools.log(`${metadataTest.name} creation failed: ${creationError.message}`);
}
} else {
tools.log(`${metadataTest.name} PDF/A-3 creation not supported`);
}
} catch (error) {
tools.log(`${metadataTest.name} test failed: ${error.message}`);
}
}
const duration = Date.now() - startTime;
PerformanceTracker.recordMetric('pdfa3-creation-metadata-accessibility', duration);
});
tap.test('PDF-05: PDF/A-3 Creation - Performance and Size Optimization', async (tools) => {
const startTime = Date.now();
// Test PDF/A-3 creation performance with different optimization settings
const optimizationTests = [
{
name: 'Standard Quality',
options: {
imageQuality: 'standard',
compression: 'standard',
optimizeFor: 'balanced'
}
},
{
name: 'High Quality',
options: {
imageQuality: 'high',
compression: 'minimal',
optimizeFor: 'quality'
}
},
{
name: 'Small Size',
options: {
imageQuality: 'medium',
compression: 'maximum',
optimizeFor: 'size'
}
},
{
name: 'Fast Generation',
options: {
imageQuality: 'medium',
compression: 'fast',
optimizeFor: 'speed'
}
}
];
const testXml = `<?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
<ID>PERFORMANCE-TEST-001</ID>
<IssueDate>2024-01-01</IssueDate>
<InvoiceTypeCode>380</InvoiceTypeCode>
<DocumentCurrencyCode>EUR</DocumentCurrencyCode>
<LegalMonetaryTotal>
<PayableAmount currencyID="EUR">100.00</PayableAmount>
</LegalMonetaryTotal>
</Invoice>`;
const performanceResults = [];
for (const optimizationTest of optimizationTests) {
tools.log(`Testing ${optimizationTest.name} optimization...`);
try {
const invoice = new EInvoice();
await invoice.fromXmlString(testXml);
if (typeof invoice.createPdfA3 === 'function') {
const outputPath = plugins.path.join(process.cwd(), '.nogit', `test-${optimizationTest.name.toLowerCase().replace(/\s+/g, '-')}.pdf`);
await plugins.fs.ensureDir(plugins.path.dirname(outputPath));
const creationStartTime = Date.now();
const creationOptions = {
outputPath: outputPath,
xmlContent: testXml,
attachmentName: 'invoice.xml',
complianceLevel: 'PDF/A-3B',
title: `Performance Test - ${optimizationTest.name}`,
...optimizationTest.options
};
try {
const creationResult = await invoice.createPdfA3(creationOptions);
const creationTime = Date.now() - creationStartTime;
if (creationResult) {
const outputExists = await plugins.fs.pathExists(outputPath);
if (outputExists) {
const outputStats = await plugins.fs.stat(outputPath);
const fileSizeKB = outputStats.size / 1024;
const result = {
name: optimizationTest.name,
creationTimeMs: creationTime,
fileSizeKB: fileSizeKB,
...optimizationTest.options
};
performanceResults.push(result);
tools.log(` Creation time: ${creationTime}ms`);
tools.log(` File size: ${fileSizeKB.toFixed(1)}KB`);
tools.log(` Performance ratio: ${(creationTime / fileSizeKB).toFixed(2)}ms/KB`);
// Clean up
await plugins.fs.remove(outputPath);
} else {
tools.log(`${optimizationTest.name} file not created`);
}
} else {
tools.log(`${optimizationTest.name} creation returned no result`);
}
} catch (creationError) {
tools.log(`${optimizationTest.name} creation failed: ${creationError.message}`);
}
} else {
tools.log(`${optimizationTest.name} PDF/A-3 creation not supported`);
}
} catch (error) {
tools.log(`${optimizationTest.name} test failed: ${error.message}`);
}
}
// Analyze performance results
if (performanceResults.length > 0) {
tools.log(`\nPDF/A-3 Performance Analysis:`);
const fastestCreation = performanceResults.reduce((min, r) => r.creationTimeMs < min.creationTimeMs ? r : min);
const smallestFile = performanceResults.reduce((min, r) => r.fileSizeKB < min.fileSizeKB ? r : min);
const avgCreationTime = performanceResults.reduce((sum, r) => sum + r.creationTimeMs, 0) / performanceResults.length;
const avgFileSize = performanceResults.reduce((sum, r) => sum + r.fileSizeKB, 0) / performanceResults.length;
tools.log(`- Fastest creation: ${fastestCreation.name} (${fastestCreation.creationTimeMs}ms)`);
tools.log(`- Smallest file: ${smallestFile.name} (${smallestFile.fileSizeKB.toFixed(1)}KB)`);
tools.log(`- Average creation time: ${avgCreationTime.toFixed(1)}ms`);
tools.log(`- Average file size: ${avgFileSize.toFixed(1)}KB`);
// Performance expectations
expect(avgCreationTime).toBeLessThan(5000); // 5 seconds max average
expect(avgFileSize).toBeLessThan(500); // 500KB max average
}
const duration = Date.now() - startTime;
PerformanceTracker.recordMetric('pdfa3-creation-performance-optimization', duration);
});
tap.test('PDF-05: Performance Summary', async (tools) => {
const operations = [
'pdfa3-creation-basic',
'pdfa3-creation-compliance-levels',
'pdfa3-creation-zugferd-profiles',
'pdfa3-creation-metadata-accessibility',
'pdfa3-creation-performance-optimization'
];
tools.log(`\n=== PDF/A-3 Creation Performance Summary ===`);
for (const operation of operations) {
const summary = await PerformanceTracker.getSummary(operation);
if (summary) {
tools.log(`${operation}:`);
tools.log(` avg=${summary.average}ms, min=${summary.min}ms, max=${summary.max}ms, p95=${summary.p95}ms`);
}
}
tools.log(`\nPDF/A-3 creation testing completed.`);
});