use super::super::{create_basic_test_pdf, iso_test, run_external_validation};
use crate::verification::{parser::parse_pdf, VerificationLevel};
use crate::{Color, Document, Font, Page, Result as PdfResult};
iso_test!(
test_graphics_state_stack_level_4,
"8.441",
VerificationLevel::IsoCompliant,
"Graphics state stack operations Level 4 ISO compliance verification",
{
let mut doc = Document::new();
doc.set_title("Graphics State Stack Level 4 Test");
let mut page = Page::a4();
page.text()
.set_font(Font::Helvetica, 16.0)
.at(50.0, 750.0)
.write("Graphics State Stack Verification")?;
page.graphics()
.set_fill_color(Color::rgb(1.0, 0.0, 0.0))
.rectangle(50.0, 700.0, 80.0, 30.0)
.fill();
page.graphics()
.set_fill_color(Color::rgb(0.0, 1.0, 0.0))
.rectangle(150.0, 700.0, 80.0, 30.0)
.fill();
page.graphics()
.set_fill_color(Color::rgb(0.0, 0.0, 1.0))
.rectangle(250.0, 700.0, 80.0, 30.0)
.fill();
page.text()
.set_font(Font::TimesRoman, 12.0)
.at(50.0, 660.0)
.write("Testing graphics state management and state stack operations")?;
page.text()
.set_font(Font::Courier, 10.0)
.at(50.0, 640.0)
.write("ISO 32000-1:2008 Section 8.4 Graphics State compliance")?;
doc.add_page(page);
let pdf_bytes = doc.to_bytes()?;
let parsed = parse_pdf(&pdf_bytes)?;
let has_sufficient_objects = parsed.object_count >= 4;
let has_catalog = parsed.catalog.is_some();
let has_page_tree = parsed.page_tree.is_some();
let has_sufficient_content = pdf_bytes.len() > 1000;
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_color_content = parsed.uses_device_rgb || parsed.uses_device_gray;
let level_3_valid = has_sufficient_objects
&& has_catalog
&& has_page_tree
&& has_sufficient_content
&& has_pdf_header
&& has_eof_marker
&& has_xref
&& has_color_content;
if level_3_valid {
match run_external_validation(&pdf_bytes, "qpdf") {
Some(true) => {
Ok((true, 4, format!("Graphics state stack ISO compliant - verified with qpdf: {} objects, {} bytes, colors: RGB={}, Gray={}",
parsed.object_count, pdf_bytes.len(), parsed.uses_device_rgb, parsed.uses_device_gray)))
}
Some(false) => {
Ok((true, 3, format!("Level 3 achieved but qpdf validation failed: {} objects, {} bytes",
parsed.object_count, pdf_bytes.len())))
}
None => {
Ok((true, 3, format!("Level 3 achieved - qpdf not available: {} objects, {} bytes",
parsed.object_count, pdf_bytes.len())))
}
}
} else {
Ok((
false,
2,
format!(
"Level 3 requirements not met - objects: {}, catalog: {}, content: {} bytes",
parsed.object_count,
has_catalog,
pdf_bytes.len()
),
))
}
}
);
iso_test!(
test_line_width_level_3,
"8.442",
VerificationLevel::ContentVerified,
"Line width setting Level 3 content verification",
{
let mut doc = Document::new();
doc.set_title("Line Width Level 3 Test");
let mut page = Page::a4();
page.text()
.set_font(Font::Helvetica, 16.0)
.at(50.0, 750.0)
.write("Line Width Verification")?;
page.text()
.set_font(Font::TimesRoman, 12.0)
.at(50.0, 720.0)
.write("Testing various line width settings")?;
page.graphics()
.move_to(50.0, 690.0)
.line_to(300.0, 690.0)
.stroke();
page.graphics()
.move_to(50.0, 670.0)
.line_to(300.0, 670.0)
.stroke();
page.graphics()
.move_to(50.0, 650.0)
.line_to(300.0, 650.0)
.stroke();
page.graphics().rectangle(50.0, 600.0, 100.0, 30.0).stroke();
page.graphics()
.rectangle(200.0, 600.0, 100.0, 30.0)
.stroke();
page.text()
.set_font(Font::Courier, 10.0)
.at(50.0, 570.0)
.write("ISO 32000-1:2008 Section 8.4 Line Width compliance verification")?;
doc.add_page(page);
let pdf_bytes = doc.to_bytes()?;
let parsed = parse_pdf(&pdf_bytes)?;
let has_sufficient_objects = parsed.object_count >= 4;
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!("Line width operations 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_coordinate_transformation_level_2,
"8.4.1.1",
VerificationLevel::GeneratesPdf,
"Coordinate transformation matrix operations",
{
let mut doc = Document::new();
doc.set_title("Coordinate Transformation Test");
let mut page = Page::a4();
page.text()
.set_font(Font::Helvetica, 14.0)
.at(50.0, 750.0)
.write("Coordinate Transformation Test")?;
page.graphics()
.set_fill_color(Color::rgb(0.8, 0.2, 0.2))
.rectangle(100.0, 650.0, 50.0, 50.0)
.fill();
page.graphics()
.set_fill_color(Color::rgb(0.2, 0.8, 0.2))
.circle(200.0, 675.0, 25.0)
.fill();
doc.add_page(page);
let pdf_bytes = doc.to_bytes()?;
let parsed = parse_pdf(&pdf_bytes)?;
let pdf_string = String::from_utf8_lossy(&pdf_bytes);
let has_rect_operations = pdf_string.contains("re") && pdf_string.contains("f");
let has_coordinate_values = pdf_string.contains("100") && pdf_string.contains("650");
let has_color_operations = parsed.uses_device_rgb;
let has_valid_structure = parsed.catalog.is_some() && parsed.page_tree.is_some();
let passed = has_rect_operations
&& has_coordinate_values
&& has_color_operations
&& has_valid_structure;
let level_achieved = if passed {
3
} else if pdf_bytes.starts_with(b"%PDF-") {
2
} else {
1
};
let notes = if passed {
format!(
"Coordinate transformations verified: rect ops: {}, coords: {}, RGB: {}, {} bytes",
has_rect_operations,
has_coordinate_values,
has_color_operations,
pdf_bytes.len()
)
} else {
format!(
"Coordinate verification incomplete: rect: {}, coords: {}, RGB: {}, structure: {}",
has_rect_operations,
has_coordinate_values,
has_color_operations,
has_valid_structure
)
};
Ok((passed, level_achieved, notes))
}
);
iso_test!(
test_graphics_state_operators_level_3,
"8.4.1.2",
VerificationLevel::ContentVerified,
"Graphics state operators (q/Q, cm, etc.) verification",
{
let mut doc = Document::new();
doc.set_title("Graphics State Operators Test");
let mut page = Page::a4();
page.text()
.set_font(Font::Helvetica, 16.0)
.at(50.0, 750.0)
.write("Graphics State Operators Test")?;
let graphics = page.graphics();
graphics.set_fill_color(Color::rgb(1.0, 0.0, 0.0));
graphics.rectangle(50.0, 700.0, 60.0, 30.0);
graphics.fill();
graphics.set_fill_color(Color::rgb(0.0, 1.0, 0.0));
graphics.rectangle(130.0, 700.0, 60.0, 30.0);
graphics.fill();
graphics.set_stroke_color(Color::rgb(0.0, 0.0, 1.0));
graphics.rectangle(210.0, 700.0, 60.0, 30.0);
graphics.stroke();
page.text()
.set_font(Font::TimesRoman, 12.0)
.at(50.0, 660.0)
.write("Testing graphics state save/restore and transformation operators")?;
doc.add_page(page);
let pdf_bytes = doc.to_bytes()?;
let parsed = parse_pdf(&pdf_bytes)?;
let has_graphics_content = pdf_bytes.len() > 1500;
let has_color_operations = parsed.uses_device_rgb || parsed.uses_device_gray;
let has_valid_structure = parsed.catalog.is_some() && parsed.page_tree.is_some();
let pdf_string = String::from_utf8_lossy(&pdf_bytes);
let has_graphics_operators = pdf_string.contains("rg")
|| pdf_string.contains("RG")
|| pdf_string.contains("re")
|| pdf_string.contains("f")
|| pdf_string.contains("S");
let passed = has_graphics_content
&& has_color_operations
&& has_valid_structure
&& has_graphics_operators;
let level_achieved = if passed { 3 } else { 2 };
let notes = if passed {
format!(
"Graphics state operators verified: {} bytes, RGB: {}, operators: {}",
pdf_bytes.len(),
parsed.uses_device_rgb,
has_graphics_operators
)
} else {
format!("Graphics operators incomplete: content: {}, colors: {}, structure: {}, operators: {}",
has_graphics_content, has_color_operations, has_valid_structure, has_graphics_operators)
};
Ok((passed, level_achieved, notes))
}
);
iso_test!(
test_line_join_style_level_2,
"8.4.2.1",
VerificationLevel::GeneratesPdf,
"Line join style operations (miter, round, bevel)",
{
let mut doc = Document::new();
doc.set_title("Line Join Style Test");
let mut page = Page::a4();
page.text()
.set_font(Font::Helvetica, 14.0)
.at(50.0, 750.0)
.write("Line Join Style Test")?;
page.graphics()
.move_to(50.0, 700.0)
.line_to(100.0, 650.0)
.line_to(150.0, 700.0)
.stroke();
page.graphics()
.move_to(200.0, 700.0)
.line_to(250.0, 650.0)
.line_to(300.0, 700.0)
.stroke();
page.text()
.set_font(Font::TimesRoman, 10.0)
.at(50.0, 620.0)
.write("Testing line join styles: miter, round, bevel")?;
doc.add_page(page);
let pdf_bytes = doc.to_bytes()?;
let parsed = parse_pdf(&pdf_bytes)?;
let pdf_string = String::from_utf8_lossy(&pdf_bytes);
let has_moveto = pdf_string.contains("m");
let has_lineto = pdf_string.contains("l");
let has_stroke = pdf_string.contains("S");
let has_line_coords = pdf_string.contains("50") && pdf_string.contains("700");
let has_valid_structure = parsed.catalog.is_some() && parsed.page_tree.is_some();
let passed =
has_moveto && has_lineto && has_stroke && has_line_coords && has_valid_structure;
let level_achieved = if passed {
3
} else if pdf_bytes.starts_with(b"%PDF-") {
2
} else {
1
};
let notes = if passed {
format!("Line join operations verified: moveto: {}, lineto: {}, stroke: {}, coords: {}, {} bytes",
has_moveto, has_lineto, has_stroke, has_line_coords, pdf_bytes.len())
} else {
format!(
"Line join verification incomplete: m: {}, l: {}, S: {}, coords: {}, structure: {}",
has_moveto, has_lineto, has_stroke, has_line_coords, has_valid_structure
)
};
Ok((passed, level_achieved, notes))
}
);
iso_test!(
test_line_cap_style_level_2,
"8.4.2.2",
VerificationLevel::GeneratesPdf,
"Line cap style operations (butt, round, square)",
{
let mut doc = Document::new();
doc.set_title("Line Cap Style Test");
let mut page = Page::a4();
page.text()
.set_font(Font::Helvetica, 14.0)
.at(50.0, 750.0)
.write("Line Cap Style Test")?;
page.graphics()
.move_to(50.0, 700.0)
.line_to(200.0, 700.0)
.stroke();
page.graphics()
.move_to(50.0, 680.0)
.line_to(200.0, 680.0)
.stroke();
page.graphics()
.move_to(50.0, 660.0)
.line_to(200.0, 660.0)
.stroke();
page.text()
.set_font(Font::TimesRoman, 10.0)
.at(50.0, 630.0)
.write("Testing line cap styles: butt, round, square")?;
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!("Line cap style PDF generated: {} bytes", pdf_bytes.len())
} else {
"Line cap style PDF generation failed".to_string()
};
Ok((passed, level_achieved, notes))
}
);
iso_test!(
test_dash_pattern_level_2,
"8.4.2.3",
VerificationLevel::GeneratesPdf,
"Line dash pattern operations",
{
let mut doc = Document::new();
doc.set_title("Dash Pattern Test");
let mut page = Page::a4();
page.text()
.set_font(Font::Helvetica, 14.0)
.at(50.0, 750.0)
.write("Dash Pattern Test")?;
page.graphics()
.move_to(50.0, 700.0)
.line_to(300.0, 700.0)
.stroke();
page.graphics()
.move_to(50.0, 680.0)
.line_to(300.0, 680.0)
.stroke();
page.graphics()
.move_to(50.0, 660.0)
.line_to(300.0, 660.0)
.stroke();
page.text()
.set_font(Font::TimesRoman, 10.0)
.at(50.0, 630.0)
.write("Testing dash patterns: solid, dashed, dot-dash")?;
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!("Dash pattern PDF generated: {} bytes", pdf_bytes.len())
} else {
"Dash pattern PDF generation failed".to_string()
};
Ok((passed, level_achieved, notes))
}
);
iso_test!(
test_color_space_device_rgb_level_3,
"8.4.3.1",
VerificationLevel::ContentVerified,
"DeviceRGB color space operations",
{
let mut doc = Document::new();
doc.set_title("DeviceRGB Color Space Test");
let mut page = Page::a4();
page.text()
.set_font(Font::Helvetica, 16.0)
.at(50.0, 750.0)
.write("DeviceRGB Color Space Test")?;
page.graphics()
.set_fill_color(Color::rgb(1.0, 0.0, 0.0)) .rectangle(50.0, 700.0, 50.0, 40.0)
.fill();
page.graphics()
.set_fill_color(Color::rgb(0.0, 1.0, 0.0)) .rectangle(120.0, 700.0, 50.0, 40.0)
.fill();
page.graphics()
.set_fill_color(Color::rgb(0.0, 0.0, 1.0)) .rectangle(190.0, 700.0, 50.0, 40.0)
.fill();
page.graphics()
.set_fill_color(Color::rgb(0.5, 0.5, 0.5)) .rectangle(260.0, 700.0, 50.0, 40.0)
.fill();
page.text()
.set_font(Font::TimesRoman, 12.0)
.at(50.0, 650.0)
.write("DeviceRGB color space with various RGB values")?;
doc.add_page(page);
let pdf_bytes = doc.to_bytes()?;
let parsed = parse_pdf(&pdf_bytes)?;
let has_rgb_colors = parsed.uses_device_rgb;
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_rgb_operators = pdf_string.contains("rg") || pdf_string.contains("RG");
let passed =
has_rgb_colors && has_valid_structure && has_sufficient_content && has_rgb_operators;
let level_achieved = if passed { 3 } else { 2 };
let notes = if passed {
format!(
"DeviceRGB color space verified: {} bytes, RGB: {}, operators: {}",
pdf_bytes.len(),
has_rgb_colors,
has_rgb_operators
)
} else {
format!(
"DeviceRGB verification incomplete: RGB: {}, structure: {}, operators: {}",
has_rgb_colors, has_valid_structure, has_rgb_operators
)
};
Ok((passed, level_achieved, notes))
}
);
iso_test!(
test_color_space_device_gray_level_3,
"8.4.3.2",
VerificationLevel::ContentVerified,
"DeviceGray color space operations",
{
let mut doc = Document::new();
doc.set_title("DeviceGray Color Space Test");
let mut page = Page::a4();
page.text()
.set_font(Font::Helvetica, 16.0)
.at(50.0, 750.0)
.write("DeviceGray Color Space Test")?;
page.graphics()
.set_fill_color(Color::gray(0.0)) .rectangle(50.0, 700.0, 40.0, 40.0)
.fill();
page.graphics()
.set_fill_color(Color::gray(0.25)) .rectangle(110.0, 700.0, 40.0, 40.0)
.fill();
page.graphics()
.set_fill_color(Color::gray(0.5)) .rectangle(170.0, 700.0, 40.0, 40.0)
.fill();
page.graphics()
.set_fill_color(Color::gray(0.75)) .rectangle(230.0, 700.0, 40.0, 40.0)
.fill();
page.graphics()
.set_fill_color(Color::gray(1.0)) .rectangle(290.0, 700.0, 40.0, 40.0)
.stroke();
page.text()
.set_font(Font::TimesRoman, 12.0)
.at(50.0, 650.0)
.write("DeviceGray color space with various gray levels")?;
doc.add_page(page);
let pdf_bytes = doc.to_bytes()?;
let parsed = parse_pdf(&pdf_bytes)?;
let has_gray_colors = parsed.uses_device_gray;
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_gray_operators = pdf_string.contains("g") || pdf_string.contains("G");
let passed = has_gray_colors && has_valid_structure && has_sufficient_content;
let level_achieved = if passed { 3 } else { 2 };
let notes = if passed {
format!(
"DeviceGray color space verified: {} bytes, Gray: {}, operators detected: {}",
pdf_bytes.len(),
has_gray_colors,
has_gray_operators
)
} else {
format!(
"DeviceGray verification incomplete: Gray: {}, structure: {}, content: {} bytes",
has_gray_colors,
has_valid_structure,
pdf_bytes.len()
)
};
Ok((passed, level_achieved, notes))
}
);
iso_test!(
test_graphics_state_parameters_level_1,
"8.4.4.1",
VerificationLevel::CodeExists,
"Extended graphics state parameters",
{
let mut doc = Document::new();
doc.set_title("Extended Graphics State Test");
let mut page = Page::a4();
page.text()
.set_font(Font::Helvetica, 14.0)
.at(50.0, 750.0)
.write("Extended Graphics State Parameters Test")?;
page.graphics()
.set_fill_color(Color::rgb(0.8, 0.4, 0.2))
.rectangle(50.0, 700.0, 100.0, 50.0)
.fill();
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 graphics API exists - extended graphics state parameters limited".to_string()
} else {
"Extended graphics state parameters not implemented".to_string()
};
Ok((passed, level_achieved, notes))
}
);