Files
pdf/engine/tests/content_builder_test.cpp
T
2026-06-16 19:06:48 +05:30

120 lines
3.9 KiB
C++

#include <gtest/gtest.h>
#include <pdfengine/token.hpp>
#include <pdfengine/ast.hpp>
#include <pdfengine/content_object.hpp>
#include "../src/parser/lexer.hpp"
#include "../src/parser/parser.hpp"
#include "../src/parser/content_builder.hpp"
#include "../src/qpdf/qpdf_extractor.hpp"
#include <filesystem>
#ifndef TEST_CORPUS_DIR
#define TEST_CORPUS_DIR "../../corpus"
#endif
using namespace pdfengine;
TEST(ContentBuilderTest, SimpleTextState) {
Lexer lexer("10 20 Td /F1 12 Tf (Hello) Tj");
auto tokens = lexer.tokenize();
ContentParser parser(tokens);
ContentBuilder builder;
auto ops = parser.parse();
auto objects = builder.build(ops);
ASSERT_EQ(objects.size(), 1);
EXPECT_EQ(objects[0]->getType(), ContentObjectType::Text);
auto* textObj = static_cast<TextObject*>(objects[0].get());
EXPECT_EQ(textObj->text, "Hello");
EXPECT_EQ(textObj->fontName, "F1");
EXPECT_DOUBLE_EQ(textObj->fontSize, 12.0);
EXPECT_DOUBLE_EQ(textObj->tm[4], 10.0);
EXPECT_DOUBLE_EQ(textObj->tm[5], 20.0);
}
TEST(ContentBuilderTest, KerningArrayTJ) {
Lexer lexer("[ (He) 120 (llo) -600 (World) ] TJ");
auto tokens = lexer.tokenize();
ContentParser parser(tokens);
ContentBuilder builder;
auto ops = parser.parse();
auto objects = builder.build(ops);
ASSERT_EQ(objects.size(), 1);
EXPECT_EQ(objects[0]->getType(), ContentObjectType::Text);
auto* textObj = static_cast<TextObject*>(objects[0].get());
// -600 is less than -500, so it inserts a space
EXPECT_EQ(textObj->text, "Hello World");
}
TEST(ContentBuilderTest, RotatedTextMatrix) {
Lexer lexer("0 1 -1 0 100 200 Tm (Rotated) Tj");
auto tokens = lexer.tokenize();
ContentParser parser(tokens);
ContentBuilder builder;
auto ops = parser.parse();
auto objects = builder.build(ops);
ASSERT_EQ(objects.size(), 1);
EXPECT_EQ(objects[0]->getType(), ContentObjectType::Text);
auto* textObj = static_cast<TextObject*>(objects[0].get());
EXPECT_EQ(textObj->text, "Rotated");
EXPECT_DOUBLE_EQ(textObj->tm[0], 0.0);
EXPECT_DOUBLE_EQ(textObj->tm[1], 1.0);
EXPECT_DOUBLE_EQ(textObj->tm[2], -1.0);
EXPECT_DOUBLE_EQ(textObj->tm[3], 0.0);
EXPECT_DOUBLE_EQ(textObj->tm[4], 100.0);
EXPECT_DOUBLE_EQ(textObj->tm[5], 200.0);
}
TEST(ContentBuilderTest, 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);
ContentBuilder builder;
auto ops = parser.parse();
auto objects = builder.build(ops);
// hello_world.pdf has two text lines: "Hello, world!" and "Goodbye, world!"
int textObjectCount = 0;
bool foundHello = false;
bool foundGoodbye = false;
for (const auto& obj : objects) {
if (obj->getType() == ContentObjectType::Text) {
textObjectCount++;
auto* textObj = static_cast<TextObject*>(obj.get());
if (textObj->text == "Hello, world!") {
foundHello = true;
EXPECT_EQ(textObj->fontName, "F1");
EXPECT_DOUBLE_EQ(textObj->fontSize, 12.0);
EXPECT_DOUBLE_EQ(textObj->tm[4], 20.0);
EXPECT_DOUBLE_EQ(textObj->tm[5], 50.0);
} else if (textObj->text == "Goodbye, world!") {
foundGoodbye = true;
EXPECT_EQ(textObj->fontName, "F2");
EXPECT_DOUBLE_EQ(textObj->fontSize, 16.0);
EXPECT_DOUBLE_EQ(textObj->tm[4], 20.0);
EXPECT_DOUBLE_EQ(textObj->tm[5], 100.0);
}
}
}
EXPECT_GE(textObjectCount, 2);
EXPECT_TRUE(foundHello);
EXPECT_TRUE(foundGoodbye);
}