#include #include #include #include "../src/parser/parser.hpp" #include "../src/parser/lexer.hpp" #include "../src/qpdf/qpdf_extractor.hpp" #include #ifndef TEST_CORPUS_DIR #define TEST_CORPUS_DIR "../../corpus" #endif using namespace pdfengine; TEST(ParserTest, SimpleOperation) { Lexer lexer("10 20 Td"); auto tokens = lexer.tokenize(); ContentParser parser(tokens); auto ops = parser.parse(); ASSERT_EQ(ops.size(), 1); EXPECT_EQ(ops[0].op, "Td"); ASSERT_EQ(ops[0].operands.size(), 2); EXPECT_EQ(ops[0].operands[0]->type, AstNodeType::Number); EXPECT_DOUBLE_EQ(ops[0].operands[0]->numberValue, 10.0); EXPECT_EQ(ops[0].operands[1]->type, AstNodeType::Number); EXPECT_DOUBLE_EQ(ops[0].operands[1]->numberValue, 20.0); } TEST(ParserTest, ArraysAndDicts) { Lexer lexer("<< /Type /Page >> [ 1 2 ] (Text) Tj"); auto tokens = lexer.tokenize(); ContentParser parser(tokens); auto ops = parser.parse(); ASSERT_EQ(ops.size(), 1); EXPECT_EQ(ops[0].op, "Tj"); ASSERT_EQ(ops[0].operands.size(), 3); auto dictNode = ops[0].operands[0]; EXPECT_EQ(dictNode->type, AstNodeType::Dictionary); ASSERT_TRUE(dictNode->dictItems.find("Type") != dictNode->dictItems.end()); EXPECT_EQ(dictNode->dictItems["Type"]->stringValue, "Page"); auto arrayNode = ops[0].operands[1]; EXPECT_EQ(arrayNode->type, AstNodeType::Array); ASSERT_EQ(arrayNode->arrayItems.size(), 2); EXPECT_DOUBLE_EQ(arrayNode->arrayItems[0]->numberValue, 1.0); EXPECT_DOUBLE_EQ(arrayNode->arrayItems[1]->numberValue, 2.0); auto strNode = ops[0].operands[2]; EXPECT_EQ(strNode->type, AstNodeType::String); EXPECT_EQ(strNode->stringValue, "Text"); } TEST(ParserTest, MultipleOperations) { Lexer lexer("BT /F1 12 Tf (Hello) Tj ET"); auto tokens = lexer.tokenize(); ContentParser parser(tokens); auto ops = parser.parse(); ASSERT_EQ(ops.size(), 4); EXPECT_EQ(ops[0].op, "BT"); EXPECT_TRUE(ops[0].operands.empty()); EXPECT_EQ(ops[1].op, "Tf"); ASSERT_EQ(ops[1].operands.size(), 2); EXPECT_EQ(ops[1].operands[0]->stringValue, "F1"); EXPECT_DOUBLE_EQ(ops[1].operands[1]->numberValue, 12.0); EXPECT_EQ(ops[2].op, "Tj"); ASSERT_EQ(ops[2].operands.size(), 1); EXPECT_EQ(ops[2].operands[0]->stringValue, "Hello"); EXPECT_EQ(ops[3].op, "ET"); EXPECT_TRUE(ops[3].operands.empty()); } TEST(ParserTest, IntegrationHelloWorld) { pdfengine::qpdf_layer::QpdfExtractor extractor; std::filesystem::path path = std::filesystem::path(TEST_CORPUS_DIR) / "basic" / "hello_world.pdf"; auto stream = extractor.extractPageStream(path.string(), 0); ASSERT_TRUE(stream.has_value()); Lexer lexer(stream->decodedContent); auto tokens = lexer.tokenize(); ContentParser parser(tokens); auto ops = parser.parse(); bool foundTj = false; for (const auto& op : ops) { if (op.op == "Tj" || op.op == "TJ") { ASSERT_EQ(op.operands.size(), 1); if (op.operands[0]->type == AstNodeType::String && op.operands[0]->stringValue == "Hello, world!") { foundTj = true; break; } } } EXPECT_TRUE(foundTj) << "Failed to parse (Hello, world!) Tj operation from hello_world.pdf stream"; }