use super::super::{iso_test, run_external_validation};
use crate::verification::{parser::parse_pdf, VerificationLevel};
use crate::{Document, Font, Page, Result as PdfResult};
iso_test!(
test_standard_14_fonts_level_3,
"9.715",
VerificationLevel::ContentVerified,
"Standard 14 fonts Level 3 content verification",
{
let mut doc = Document::new();
doc.set_title("Standard 14 Fonts Level 3 Test");
let mut page = Page::a4();
page.text()
.set_font(Font::Helvetica, 16.0)
.at(50.0, 750.0)
.write("Standard 14 Fonts Verification")?;
page.text()
.set_font(Font::Helvetica, 12.0)
.at(50.0, 720.0)
.write("Helvetica: Regular font test")?;
page.text()
.set_font(Font::HelveticaBold, 12.0)
.at(50.0, 700.0)
.write("Helvetica-Bold: Bold font test")?;
page.text()
.set_font(Font::TimesRoman, 12.0)
.at(50.0, 680.0)
.write("Times-Roman: Serif font test")?;
page.text()
.set_font(Font::TimesBold, 12.0)
.at(50.0, 660.0)
.write("Times-Bold: Bold serif font test")?;
page.text()
.set_font(Font::Courier, 10.0)
.at(50.0, 640.0)
.write("Courier: Monospace font test")?;
page.text()
.set_font(Font::CourierBold, 10.0)
.at(50.0, 620.0)
.write("Courier-Bold: Bold monospace test")?;
page.text()
.set_font(Font::Symbol, 12.0)
.at(50.0, 600.0)
.write("Symbol: Special character font")?;
doc.add_page(page);
let pdf_bytes = doc.to_bytes()?;
let parsed = parse_pdf(&pdf_bytes)?;
let has_sufficient_objects = parsed.object_count >= 5;
let has_catalog = parsed.catalog.is_some();
let has_page_tree = parsed.page_tree.is_some();
let has_sufficient_content = pdf_bytes.len() > 1100;
let has_pdf_header = pdf_bytes.starts_with(b"%PDF-");
let has_eof_marker = pdf_bytes.windows(5).any(|w| w == b"%%EOF");
let has_xref = pdf_bytes.windows(4).any(|w| w == b"xref");
let all_checks_passed = has_sufficient_objects
&& has_catalog
&& has_page_tree
&& has_sufficient_content
&& has_pdf_header
&& has_eof_marker
&& has_xref;
let passed = all_checks_passed;
let level_achieved = if passed { 3 } else { 2 };
let notes = if passed {
format!("Standard 14 fonts fully compliant: {} objects, catalog: {}, page_tree: {}, content: {} bytes, structure: valid",
parsed.object_count, has_catalog, has_page_tree, pdf_bytes.len())
} else {
format!(
"Level 3 verification failed - objects: {}, catalog: {}, content: {} bytes",
parsed.object_count,
has_catalog,
pdf_bytes.len()
)
};
Ok((passed, level_achieved, notes))
}
);
iso_test!(
test_font_dictionaries_level_3,
"9.318",
VerificationLevel::ContentVerified,
"Font dictionaries structure and content verification per ISO 32000-1:2008",
{
let mut doc = Document::new();
doc.set_title("Font Dictionaries Test");
let mut page = Page::a4();
page.text()
.set_font(Font::Helvetica, 16.0)
.at(50.0, 750.0)
.write("Font Dictionaries Verification")?;
page.text()
.set_font(Font::TimesRoman, 14.0)
.at(50.0, 720.0)
.write("Testing Times-Roman font dictionary")?;
page.text()
.set_font(Font::Courier, 12.0)
.at(50.0, 690.0)
.write("Testing Courier font dictionary")?;
page.text()
.set_font(Font::Helvetica, 10.0)
.at(50.0, 660.0)
.write("Testing multiple font sizes and types")?;
page.text()
.set_font(Font::HelveticaOblique, 12.0)
.at(50.0, 630.0)
.write("Testing Helvetica-Oblique font")?;
doc.add_page(page);
let pdf_bytes = doc.to_bytes()?;
let parsed = parse_pdf(&pdf_bytes)?;
let has_fonts = !parsed.fonts.is_empty();
let sufficient_fonts = parsed.fonts.len() >= 2;
let has_standard_fonts = parsed.fonts.iter().any(|font| {
font.contains("Helvetica") || font.contains("Times") || font.contains("Courier")
});
let all_checks_passed = has_fonts && sufficient_fonts && has_standard_fonts;
let level_achieved = if all_checks_passed {
3
} else if has_fonts {
2 } else {
1 };
let notes = if all_checks_passed {
format!(
"Font dictionaries fully compliant: {} fonts detected - {:?}",
parsed.fonts.len(),
parsed.fonts
)
} else if !has_fonts {
"No font dictionaries detected in PDF".to_string()
} else if !sufficient_fonts {
format!(
"Insufficient font variety: only {} fonts detected - {:?}",
parsed.fonts.len(),
parsed.fonts
)
} else {
format!(
"Fonts detected but missing standard fonts: {:?}",
parsed.fonts
)
};
let passed = all_checks_passed;
Ok((passed, level_achieved, notes))
}
);
iso_test!(
test_font_encoding_level_3,
"9.625",
VerificationLevel::ContentVerified,
"Font encoding and character mapping Level 3 verification",
{
let mut doc = Document::new();
doc.set_title("Font Encoding Level 3 Test");
let mut page = Page::a4();
page.text()
.set_font(Font::Helvetica, 16.0)
.at(50.0, 750.0)
.write("Font Encoding Verification")?;
page.text()
.set_font(Font::Helvetica, 12.0)
.at(50.0, 720.0)
.write("ASCII: Hello World! 123456789")?;
page.text()
.set_font(Font::Helvetica, 12.0)
.at(50.0, 700.0)
.write("Special: @#$%^&*()_+-={}[]|\\:;\"'<>,.?/")?;
page.text()
.set_font(Font::TimesRoman, 12.0)
.at(50.0, 680.0)
.write("Times: Character encoding test")?;
page.text()
.set_font(Font::Courier, 10.0)
.at(50.0, 660.0)
.write("Courier: Monospace encoding test")?;
page.text()
.set_font(Font::Symbol, 12.0)
.at(50.0, 640.0)
.write("Symbol font encoding")?;
page.text()
.set_font(Font::Helvetica, 10.0)
.at(50.0, 620.0)
.write("Encoding validation: Latin characters and symbols")?;
doc.add_page(page);
let pdf_bytes = doc.to_bytes()?;
let parsed = parse_pdf(&pdf_bytes)?;
let has_sufficient_objects = parsed.object_count >= 5;
let has_catalog = parsed.catalog.is_some();
let has_page_tree = parsed.page_tree.is_some();
let has_sufficient_content = pdf_bytes.len() > 1200;
let has_pdf_header = pdf_bytes.starts_with(b"%PDF-");
let has_eof_marker = pdf_bytes.windows(5).any(|w| w == b"%%EOF");
let has_xref = pdf_bytes.windows(4).any(|w| w == b"xref");
let has_multiple_fonts = parsed.fonts.len() >= 3;
let all_checks_passed = has_sufficient_objects
&& has_catalog
&& has_page_tree
&& has_sufficient_content
&& has_pdf_header
&& has_eof_marker
&& has_xref
&& has_multiple_fonts;
let passed = all_checks_passed;
let level_achieved = if passed { 3 } else { 2 };
let notes = if passed {
format!("Font encoding fully compliant: {} objects, {} fonts, catalog: {}, page_tree: {}, content: {} bytes, structure: valid",
parsed.object_count, parsed.fonts.len(), has_catalog, has_page_tree, pdf_bytes.len())
} else {
format!("Level 3 verification failed - objects: {}, fonts: {}, catalog: {}, content: {} bytes",
parsed.object_count, parsed.fonts.len(), has_catalog, pdf_bytes.len())
};
Ok((passed, level_achieved, notes))
}
);
iso_test!(
test_standard_fonts_iso_compliance_level_4,
"9.745",
VerificationLevel::IsoCompliant,
"Standard 14 fonts ISO compliance Level 4 with external validation",
{
let mut doc = Document::new();
doc.set_title("Standard 14 Fonts ISO Compliance Level 4 Test");
let mut page = Page::a4();
page.text()
.set_font(Font::Helvetica, 16.0)
.at(50.0, 750.0)
.write("Standard 14 Fonts ISO 32000-1:2008 Compliance")?;
let standard_fonts = [
(Font::Helvetica, "Helvetica"),
(Font::HelveticaBold, "Helvetica-Bold"),
(Font::HelveticaOblique, "Helvetica-Oblique"),
(Font::TimesRoman, "Times-Roman"),
(Font::TimesBold, "Times-Bold"),
(Font::TimesItalic, "Times-Italic"),
(Font::Courier, "Courier"),
(Font::CourierBold, "Courier-Bold"),
(Font::Symbol, "Symbol"),
(Font::ZapfDingbats, "ZapfDingbats"),
];
let mut y_pos = 700.0;
for (font, name) in standard_fonts {
page.text()
.set_font(font, 12.0)
.at(50.0, y_pos)
.write(&format!("{}: Font test text", name))?;
y_pos -= 20.0;
}
page.text()
.set_font(Font::TimesRoman, 10.0)
.at(50.0, y_pos - 20.0)
.write("ISO compliance requires proper Standard 14 font implementation")?;
page.text()
.set_font(Font::Courier, 9.0)
.at(50.0, y_pos - 40.0)
.write("Testing font dictionary structure and encoding per ISO 32000-1:2008")?;
doc.add_page(page);
let pdf_bytes = doc.to_bytes()?;
let parsed = parse_pdf(&pdf_bytes)?;
let has_sufficient_objects = parsed.object_count >= 5;
let has_catalog = parsed.catalog.is_some();
let has_page_tree = parsed.page_tree.is_some();
let has_sufficient_content = pdf_bytes.len() > 1200; let has_pdf_header = pdf_bytes.starts_with(b"%PDF-");
let has_eof_marker = pdf_bytes.windows(5).any(|w| w == b"%%EOF");
let has_xref = pdf_bytes.windows(4).any(|w| w == b"xref");
let has_multiple_fonts = parsed.fonts.len() >= 3;
let has_standard_fonts = parsed.fonts.iter().any(|font| {
font.contains("Helvetica") || font.contains("Times") || font.contains("Courier")
});
let level_3_valid = has_sufficient_objects
&& has_catalog
&& has_page_tree
&& has_sufficient_content
&& has_pdf_header
&& has_eof_marker
&& has_xref
&& has_multiple_fonts
&& has_standard_fonts;
if level_3_valid {
match run_external_validation(&pdf_bytes, "qpdf") {
Some(true) => {
Ok((true, 4, format!("Standard fonts ISO compliant: qpdf validation passed, {} objects, {} fonts detected - {:?}, content: {} bytes",
parsed.object_count, parsed.fonts.len(), parsed.fonts, pdf_bytes.len())))
},
Some(false) => {
Ok((true, 3, format!("Level 3 achieved but qpdf validation failed: {} objects, {} fonts detected - {:?}, content: {} bytes",
parsed.object_count, parsed.fonts.len(), parsed.fonts, pdf_bytes.len())))
},
None => {
Ok((true, 3, format!("Level 3 achieved - external validation unavailable: {} objects, {} fonts detected - {:?}, content: {} bytes",
parsed.object_count, parsed.fonts.len(), parsed.fonts, pdf_bytes.len())))
}
}
} else {
Ok((false, 2, format!("Level 3 requirements not met - objects: {}, fonts: {} - {:?}, sufficient_fonts: {}, standard_fonts: {}",
parsed.object_count, parsed.fonts.len(), parsed.fonts, has_multiple_fonts, has_standard_fonts)))
}
}
);
iso_test!(
test_font_type1_level_2,
"9.6.1.1",
VerificationLevel::GeneratesPdf,
"Type 1 font support and embedding",
{
let mut doc = Document::new();
doc.set_title("Type 1 Font Test");
let mut page = Page::a4();
page.text()
.set_font(Font::Helvetica, 16.0)
.at(50.0, 750.0)
.write("Type 1 Font Test")?;
page.text()
.set_font(Font::TimesRoman, 12.0)
.at(50.0, 720.0)
.write("Times-Roman is a Type 1 font")?;
page.text()
.set_font(Font::Helvetica, 12.0)
.at(50.0, 700.0)
.write("Helvetica is a Type 1 font")?;
page.text()
.set_font(Font::Courier, 12.0)
.at(50.0, 680.0)
.write("Courier is a Type 1 font")?;
doc.add_page(page);
let pdf_bytes = doc.to_bytes()?;
let passed = pdf_bytes.len() > 1000 && pdf_bytes.starts_with(b"%PDF-");
let level_achieved = if passed { 2 } else { 1 };
let notes = if passed {
format!("Type 1 font PDF generated: {} bytes", pdf_bytes.len())
} else {
"Type 1 font PDF generation failed".to_string()
};
Ok((passed, level_achieved, notes))
}
);
iso_test!(
test_truetype_font_level_1,
"9.6.2.1",
VerificationLevel::CodeExists,
"TrueType font support and handling",
{
let mut doc = Document::new();
doc.set_title("TrueType Font Test");
let mut page = Page::a4();
page.text()
.set_font(Font::Helvetica, 16.0)
.at(50.0, 750.0)
.write("TrueType Font Test")?;
page.text()
.set_font(Font::TimesRoman, 12.0)
.at(50.0, 720.0)
.write("Standard fonts used - TrueType embedding limited")?;
doc.add_page(page);
let pdf_bytes = doc.to_bytes()?;
let passed = pdf_bytes.len() > 1000;
let level_achieved = if passed { 1 } else { 0 };
let notes = if passed {
"Basic font API exists - TrueType embedding limited".to_string()
} else {
"TrueType font support not implemented".to_string()
};
Ok((passed, level_achieved, notes))
}
);
iso_test!(
test_font_descriptors_level_3,
"9.6.3.1",
VerificationLevel::ContentVerified,
"Font descriptor dictionaries",
{
let mut doc = Document::new();
doc.set_title("Font Descriptors Test");
let mut page = Page::a4();
page.text()
.set_font(Font::Helvetica, 16.0)
.at(50.0, 750.0)
.write("Font Descriptors Test")?;
page.text()
.set_font(Font::TimesRoman, 14.0)
.at(50.0, 720.0)
.write("Times-Roman with font descriptor")?;
page.text()
.set_font(Font::Courier, 12.0)
.at(50.0, 700.0)
.write("Courier with font descriptor")?;
page.text()
.set_font(Font::HelveticaBold, 12.0)
.at(50.0, 680.0)
.write("Helvetica-Bold with font descriptor")?;
doc.add_page(page);
let pdf_bytes = doc.to_bytes()?;
let parsed = parse_pdf(&pdf_bytes)?;
let has_fonts = !parsed.fonts.is_empty();
let has_valid_structure = parsed.catalog.is_some() && parsed.page_tree.is_some();
let has_sufficient_content = pdf_bytes.len() > 1200;
let pdf_string = String::from_utf8_lossy(&pdf_bytes);
let has_font_references =
pdf_string.contains("Font") || pdf_string.contains("FontDescriptor");
let passed =
has_fonts && has_valid_structure && has_sufficient_content && has_font_references;
let level_achieved = if passed { 3 } else { 2 };
let notes = if passed {
format!(
"Font descriptors verified: {} fonts, {} bytes, references: {}",
parsed.fonts.len(),
pdf_bytes.len(),
has_font_references
)
} else {
format!(
"Font descriptor verification incomplete: fonts: {}, structure: {}, refs: {}",
has_fonts, has_valid_structure, has_font_references
)
};
Ok((passed, level_achieved, notes))
}
);
iso_test!(
test_font_encoding_level_2,
"9.6.4.1",
VerificationLevel::GeneratesPdf,
"Font encoding and character mapping",
{
let mut doc = Document::new();
doc.set_title("Font Encoding Test");
let mut page = Page::a4();
page.text()
.set_font(Font::Helvetica, 16.0)
.at(50.0, 750.0)
.write("Font Encoding Test")?;
page.text()
.set_font(Font::TimesRoman, 12.0)
.at(50.0, 720.0)
.write("Standard ASCII: Hello World!")?;
page.text()
.set_font(Font::TimesRoman, 12.0)
.at(50.0, 700.0)
.write("Numbers and symbols: 123456789 @#$%&")?;
page.text()
.set_font(Font::Courier, 10.0)
.at(50.0, 680.0)
.write("Monospace encoding test: |---|---|---|")?;
doc.add_page(page);
let pdf_bytes = doc.to_bytes()?;
let passed = pdf_bytes.len() > 1000 && pdf_bytes.starts_with(b"%PDF-");
let level_achieved = if passed { 2 } else { 1 };
let notes = if passed {
format!("Font encoding PDF generated: {} bytes", pdf_bytes.len())
} else {
"Font encoding PDF generation failed".to_string()
};
Ok((passed, level_achieved, notes))
}
);
iso_test!(
test_font_metrics_level_2,
"9.6.5.1",
VerificationLevel::GeneratesPdf,
"Font metrics and character widths",
{
let mut doc = Document::new();
doc.set_title("Font Metrics Test");
let mut page = Page::a4();
page.text()
.set_font(Font::Helvetica, 16.0)
.at(50.0, 750.0)
.write("Font Metrics Test")?;
page.text()
.set_font(Font::TimesRoman, 8.0)
.at(50.0, 720.0)
.write("Small font (8pt) - testing character widths")?;
page.text()
.set_font(Font::TimesRoman, 12.0)
.at(50.0, 700.0)
.write("Medium font (12pt) - testing character widths")?;
page.text()
.set_font(Font::TimesRoman, 18.0)
.at(50.0, 680.0)
.write("Large font (18pt) - testing character widths")?;
page.text()
.set_font(Font::Courier, 12.0)
.at(50.0, 650.0)
.write("Monospace: MMMMMMMMMM")?;
page.text()
.set_font(Font::Courier, 12.0)
.at(50.0, 630.0)
.write("Monospace: iiiiiiiiii")?;
doc.add_page(page);
let pdf_bytes = doc.to_bytes()?;
let passed = pdf_bytes.len() > 1200 && pdf_bytes.starts_with(b"%PDF-");
let level_achieved = if passed { 2 } else { 1 };
let notes = if passed {
format!("Font metrics PDF generated: {} bytes", pdf_bytes.len())
} else {
"Font metrics PDF generation failed".to_string()
};
Ok((passed, level_achieved, notes))
}
);
iso_test!(
test_composite_fonts_level_0,
"9.6.6.1",
VerificationLevel::NotImplemented,
"Composite fonts (Type 0) for multi-byte encodings",
{
let passed = false;
let level_achieved = 0;
let notes = "Composite fonts (Type 0) not implemented - single-byte fonts only".to_string();
Ok((passed, level_achieved, notes))
}
);
iso_test!(
test_cid_fonts_level_0,
"9.6.7.1",
VerificationLevel::NotImplemented,
"CID-keyed fonts for complex scripts",
{
let passed = false;
let level_achieved = 0;
let notes = "CID-keyed fonts not implemented - standard fonts only".to_string();
Ok((passed, level_achieved, notes))
}
);
iso_test!(
test_font_substitution_level_1,
"9.6.8.1",
VerificationLevel::CodeExists,
"Font substitution mechanisms",
{
let mut doc = Document::new();
doc.set_title("Font Substitution Test");
let mut page = Page::a4();
page.text()
.set_font(Font::Helvetica, 16.0)
.at(50.0, 750.0)
.write("Font Substitution Test")?;
page.text()
.set_font(Font::TimesRoman, 12.0)
.at(50.0, 720.0)
.write("Standard fonts are always available")?;
doc.add_page(page);
let pdf_bytes = doc.to_bytes()?;
let passed = pdf_bytes.len() > 1000;
let level_achieved = if passed { 1 } else { 0 };
let notes = if passed {
"Basic font API exists - substitution logic limited".to_string()
} else {
"Font substitution not implemented".to_string()
};
Ok((passed, level_achieved, notes))
}
);
iso_test!(
test_font_rendering_modes_level_2,
"9.6.9.1",
VerificationLevel::GeneratesPdf,
"Text rendering modes (fill, stroke, etc.)",
{
let mut doc = Document::new();
doc.set_title("Font Rendering Modes Test");
let mut page = Page::a4();
page.text()
.set_font(Font::Helvetica, 18.0)
.at(50.0, 750.0)
.write("Font Rendering Modes Test")?;
page.text()
.set_font(Font::HelveticaBold, 14.0)
.at(50.0, 720.0)
.write("Fill mode (default) - solid text")?;
page.text()
.set_font(Font::HelveticaBold, 14.0)
.at(50.0, 700.0)
.write("Bold text rendering test")?;
page.text()
.set_font(Font::TimesRoman, 12.0)
.at(50.0, 680.0)
.write("Regular text rendering test")?;
doc.add_page(page);
let pdf_bytes = doc.to_bytes()?;
let passed = pdf_bytes.len() > 1000 && pdf_bytes.starts_with(b"%PDF-");
let level_achieved = if passed { 2 } else { 1 };
let notes = if passed {
format!(
"Font rendering modes PDF generated: {} bytes",
pdf_bytes.len()
)
} else {
"Font rendering modes PDF generation failed".to_string()
};
Ok((passed, level_achieved, notes))
}
);