### Build, Test, and Install PDF-Writer Source: https://github.com/galkahana/pdf-writer/blob/main/readme.md Standard build process using CMake. Ensure you have a compatible compiler and CMake installed. The `--config Release` flag is recommended for optimized builds. ```bash mkdir build && cd build cmake .. cmake --build . --config Release ctest --test-dir . -C Release cmake --install . --prefix ./install --config Release ``` -------------------------------- ### Installing Package Configuration Files Source: https://github.com/galkahana/pdf-writer/blob/main/CMakeLists.txt Installs the generated CMake configuration files (PDFHummusConfig.cmake and PDFHummusConfigVersion.cmake) into the appropriate directory for CMake to find them. ```cmake install(FILES ${CMAKE_CURRENT_BINARY_DIR}/PDFHummusConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/PDFHummusConfigVersion.cmake DESTINATION lib/cmake/PDFHummus ) ``` -------------------------------- ### Exporting Targets for Installation Source: https://github.com/galkahana/pdf-writer/blob/main/CMakeLists.txt Exports the PDFHummus targets for installation, creating a CMake configuration file to help other projects find and use the installed library. ```cmake install(EXPORT PDFHummusTargets FILE PDFHummusTargets.cmake DESTINATION lib/cmake/PDFHummus NAMESPACE PDFHummus:: ) ``` -------------------------------- ### Package Configuration Setup Source: https://github.com/galkahana/pdf-writer/blob/main/CMakeLists.txt Configures CPack for generating distributable packages. Sets license file and version information, and specifies ZIP as the generator. ```cmake include(InstallRequiredSystemLibraries) set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") set(CPACK_PACKAGE_VERSION_MAJOR "${PDFHummus_VERSION_MAJOR}") set(CPACK_PACKAGE_VERSION_MINOR "${PDFHummus_VERSION_MINOR}") set(CPACK_SOURCE_GENERATOR "ZIP") set(CPACK_GENERATOR "ZIP") include(CPack) ``` -------------------------------- ### Merge PDF Page Content Example Source: https://github.com/galkahana/pdf-writer/wiki/PDF-Embedding Example demonstrating how to use PDFPageMergingHelper to merge the first page of a PDF file into a target page. ```cpp PDFPage* myPage; PDFPageMergingHelper(myPage).MergePageContent(pdfWriter,"c:\\mySourceFile.pdf",0); ``` -------------------------------- ### Example: Shutdown and Continue PDFWriter Source: https://github.com/galkahana/pdf-writer/wiki/State-Saving Demonstrates a typical workflow for shutting down a PDFWriter instance and continuing the PDF generation with a new instance using saved state. ```cpp PDFWriter pdfWriter; pdfWriterA.StartPDF("C:\\MyPDF.PDF",ePDFVersion13); /// write some PDF writing code with pdfWriterA pdfWriterA.Shutdown("C:\\ShutDownRestartState.txt"); PDFWriter pdfWriterB; pdfWriterB.ContinuePDF("C:\\MyPDF.PDF",L"C:\\ShutDownRestartState.txt"); // write some more PDF writing code, now with pdfWriterB pdfWriterB.EndPDF(); ``` -------------------------------- ### Start PDF Output to Stream Source: https://github.com/galkahana/pdf-writer/wiki/Custom-input-and-output Use this method to begin PDF creation when the output should be directed to an IByteWriterWithPosition stream instead of a file. Ensure the stream implementation supports reading the current position. ```cpp EStatusCode StartPDFForStream(IByteWriterWithPosition* inOutputStream, EPDFVersion inPDFVersion, const LogConfiguration& inLogConfiguration, const PDFCreationSettings& inPDFCreationSettings); ``` -------------------------------- ### Package PDF-Writer using CPack Source: https://github.com/galkahana/pdf-writer/blob/main/readme.md Generates an installer package for PDF-Writer using CPack. This is typically done after the library has been built. ```bash cd build && cpack . ``` -------------------------------- ### Text Rendering with Fonts in PDFWriter Source: https://context7.com/galkahana/pdf-writer/llms.txt Shows how to load and use TrueType, OpenType, and Type1 fonts for text rendering. Includes examples of both low-level PDF operators (BT, Tf, Tm, Tj) and high-level convenience functions (WriteText). Ensure font files are accessible. ```cpp #include "PDFWriter.h" #include "PDFPage.h" #include "PageContentContext.h" using namespace PDFHummus; int main() { PDFWriter pdfWriter; pdfWriter.StartPDF("TextDemo.pdf", ePDFVersion13, LogConfiguration::DefaultLogConfiguration(), PDFCreationSettings(true, true /*embedFonts*/)); PDFPage* page = new PDFPage(); page->SetMediaBox(PDFRectangle(0, 0, 595, 842)); PageContentContext* cxt = pdfWriter.StartPageContentContext(page); // Load a TrueType font PDFUsedFont* font = pdfWriter.GetFontForFile("fonts/arial.ttf"); // Low-level text operators cxt->BT(); // begin text object cxt->k(0, 0, 0, 1); // CMYK black cxt->Tf(font, 1); // set font reference and unit size cxt->Tm(30, 0, 0, 30, 78, 662); // text matrix: scale 30pt, position at (78, 662) EStatusCode enc = cxt->Tj("Hello World"); // UTF-8 text if (enc != eSuccess) ; // some glyphs may be missing from the font cxt->ET(); // end text object // High-level convenience - font, size, color space, color value AbstractContentContext::TextOptions opt(font, 18, AbstractContentContext::eRGB, 0x0000FF); cxt->WriteText(78, 600, "Blue text at 18pt", opt); // Type1 font (requires .PFB and optional .PFM) PDFUsedFont* type1Font = pdfWriter.GetFontForFile( "fonts/HLB_____.PFB", "fonts/HLB_____.PFM"); AbstractContentContext::TextOptions type1Opt(type1Font, 12, AbstractContentContext::eGray, 0); cxt->WriteText(78, 550, "Type1 font text", type1Opt); pdfWriter.EndPageContentContext(cxt); pdfWriter.WritePageAndRelease(page); pdfWriter.EndPDF(); return 0; } ``` -------------------------------- ### Run PDFParser Fuzzing with AFL++ Source: https://github.com/galkahana/pdf-writer/wiki/Fuzz-Testing-Of-PDFParser Start fuzzing the PDFParser using AFL++. Specify input corpus, output directory, dictionary, and the fuzzing harness executable. ```bash afl-fuzz -i ../PDFWriterTesting/Materials/fuzzing/MinimalFuzzingCorpus -o fuzzing-out -x ../PDFWriterTesting/Materials/fuzzing/pdf.dict -t 50 -- ./PDFParserFuzzingHarness ``` -------------------------------- ### ZLIB Dependency Handling Source: https://github.com/galkahana/pdf-writer/blob/main/CMakeLists.txt Handles ZLIB dependency, either by finding an installed package or adding a subdirectory. Sets a flag if ZLIB is successfully included. ```cmake if(NOT USE_BUNDLED) # zlib if(USE_UNBUNDLED_FALLBACK_BUNDLED) find_package (ZLIB) if(ZLIB_FOUND) set (USING_UNBUNDLED_ZLIB TRUE) else(ZLIB_FOUND) ADD_SUBDIRECTORY(Zlib) endif(ZLIB_FOUND) else(USE_UNBUNDLED_FALLBACK_BUNDLED) find_package (ZLIB REQUIRED) set (USING_UNBUNDLED_ZLIB TRUE) endif(USE_UNBUNDLED_FALLBACK_BUNDLED) if(USING_UNBUNDLED_ZLIB) set(PDFHUMMUS_DEPENDS_ZLIB "find_dependency(ZLIB)") endif() ``` ```cmake else(NOT USE_BUNDLED) # zlib ADD_SUBDIRECTORY(Zlib) # freetype ADD_SUBDIRECTORY(FreeType) # libaesgm if(NOT USE_OPENSSL_AES) ADD_SUBDIRECTORY(LibAesgm) endif() # libjpeg if(NOT PDFHUMMUS_NO_DCT) ADD_SUBDIRECTORY(LibJpeg) endif(NOT PDFHUMMUS_NO_DCT) # libtiff if(NOT PDFHUMMUS_NO_TIFF) ADD_SUBDIRECTORY(LibTiff) endif(NOT PDFHUMMUS_NO_TIFF) # libpng ``` -------------------------------- ### Stream PDF Output Source: https://github.com/galkahana/pdf-writer/wiki/Custom-input-and-output Methods for starting and ending PDF generation directly to an IByteWriterWithPosition stream. ```APIDOC ## StartPDFForStream ### Description Initializes PDF generation to a specified output stream. ### Method ```cpp EStatusCode StartPDFForStream(IByteWriterWithPosition* inOutputStream, EPDFVersion inPDFVersion, const LogConfiguration& inLogConfiguration, const PDFCreationSettings& inPDFCreationSettings) ``` ### Parameters - **inOutputStream** (IByteWriterWithPosition*) - Required - A pointer to the output stream implementation. - **inPDFVersion** (EPDFVersion) - Required - The PDF version to use. - **inLogConfiguration** (LogConfiguration&) - Required - Configuration for logging. - **inPDFCreationSettings** (PDFCreationSettings&) - Required - Settings for PDF creation. ## EndPDFForStream ### Description Finalizes the PDF generation process when outputting to a stream. ### Method ```cpp EStatusCode EndPDFForStream() ``` ## ContinuePDFForStream ### Description Resumes PDF generation from a saved state directly to an output stream. ### Method ```cpp EStatusCode ContinuePDFForStream(IByteWriterWithPosition* inOutputStream, const string& inStateFilePath, const LogConfiguration& inLogConfiguration) ``` ### Parameters - **inOutputStream** (IByteWriterWithPosition*) - Required - A pointer to the output stream implementation. - **inStateFilePath** (string&) - Required - The path to the saved state file. - **inLogConfiguration** (LogConfiguration&) - Required - Configuration for logging. ``` -------------------------------- ### find_package for PDF-Writer Integration Source: https://github.com/galkahana/pdf-writer/blob/main/readme.md Alternative method for integrating PDF-Writer using CMake's `find_package`. This requires PDF-Writer to be installed on the system or discoverable by CMake. ```cmake find_package(PDFHummus REQUIRED) target_link_libraries(YourTarget PDFHummus::PDFWriter) ``` -------------------------------- ### Using RefCountPtr with GetTrailer via Assignment Source: https://github.com/galkahana/pdf-writer/wiki/PDF-Parsing For 'Get' methods like GetTrailer, use the assignment operator to initialize the RefCountPtr. This ensures AddRef is called upon assignment and Release is called when the pointer is destroyed, correctly managing the object's lifetime. ```cpp RefCountPtr theTrailerSmartPtr; theTrailerSmartPtr = parser.GetTrailer(); ``` -------------------------------- ### Freetype Dependency Handling Source: https://github.com/galkahana/pdf-writer/blob/main/CMakeLists.txt Handles Freetype dependency, either by finding an installed package or adding a subdirectory. Sets a flag if Freetype is successfully included. ```cmake # freetype if(USE_UNBUNDLED_FALLBACK_BUNDLED) find_package (Freetype) if(Freetype_FOUND) set (USING_UNBUNDLED_Freetype TRUE) else(Freetype_FOUND) ADD_SUBDIRECTORY(FreeType) endif(Freetype_FOUND) else(USE_UNBUNDLED_FALLBACK_BUNDLED) find_package (Freetype REQUIRED) set (USING_UNBUNDLED_Freetype TRUE) endif(USE_UNBUNDLED_FALLBACK_BUNDLED) if(USING_UNBUNDLED_Freetype) set(PDFHUMMUS_DEPENDS_FREETYPE "find_dependency(Freetype)") endif() ``` -------------------------------- ### Aesgm Dependency Handling Source: https://github.com/galkahana/pdf-writer/blob/main/CMakeLists.txt Handles Aesgm dependency, checking for its availability and conditionally adding the subdirectory if not found and OpenSSL is not used for AES. Installs CMake module for Aesgm. ```cmake # libaesgm if(USE_UNBUNDLED_FALLBACK_BUNDLED) find_package (Aesgm) if(aesgm_FOUND) set (USING_UNBUNDLED_aesgm TRUE) else(aesgm_FOUND) if(NOT USE_OPENSSL_AES) ADD_SUBDIRECTORY(LibAesgm) endif() endif(aesgm_FOUND) else(USE_UNBUNDLED_FALLBACK_BUNDLED) find_package (Aesgm REQUIRED) set (USING_UNBUNDLED_aesgm TRUE) endif(USE_UNBUNDLED_FALLBACK_BUNDLED) if(USING_UNBUNDLED_aesgm) install( FILES ${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindAesgm.cmake DESTINATION lib/cmake/PDFHummus/cmake ) set(PDFHUMMUS_APPEND_MODULE "list(APPEND CMAKE_MODULE_PATH \"${CMAKE_CURRENT_LIST_DIR}/cmake\")") set(PDFHUMMUS_DEPENDS_AESGM "find_dependency(Aesgm)") endif() ``` -------------------------------- ### Initialize PDF Parser Source: https://github.com/galkahana/pdf-writer/wiki/PDF-Parsing Demonstrates how to initialize the PDFParser with a file stream. Requires opening the file using InputFile and obtaining its input stream. ```cpp InputFile pdfFile; PDFParser parser; pdfFile.OpenFile("C:\\Test.PDF"); parser.StartPDFParsing(pdfFile.GetInputStream()); ``` -------------------------------- ### Get Primitive Values from PDF Object Source: https://github.com/galkahana/pdf-writer/wiki/PDF-Parsing Use ParsedPrimitiveHelper to quickly retrieve double or integer values from a PDFObject. Also provides methods to check if an object is a number or get its string representation. ```cpp PDFObject* myPDFObject; double aDoubleValue = ParsedPrimitiveHelper(myPDFObject).GetAsDouble(); long long aLongLongValue= ParsedPrimitiveHelper(myPDFObject).GetAsInteger(); ``` -------------------------------- ### StartPDFParsing Source: https://github.com/galkahana/pdf-writer/wiki/PDF-Parsing Initializes the PDF parsing process for a given input stream. This is the first method to call. ```APIDOC ## StartPDFParsing ### Description Initializes parsing for the PDF represented by the input stream. ### Method `EStatusCode StartPDFParsing(IByteReaderWithPosition* inSourceStream)` ### Parameters * **inSourceStream** (IByteReaderWithPosition*) - The input stream to parse. ``` -------------------------------- ### Create a New PDF File with PDF-Writer Source: https://context7.com/galkahana/pdf-writer/llms.txt This snippet demonstrates how to create a new PDF file, add a page, draw a filled rectangle using raw PDF operators, and finalize the PDF. Ensure file paths are UTF-8 encoded. Handles errors during PDF creation. ```cpp #include "PDFWriter.h" #include "PDFPage.h" #include "PDFRectangle.h" #include "PageContentContext.h" using namespace PDFHummus; int main() { PDFWriter pdfWriter; EStatusCode status; // Start a new PDF at version 1.3, with stream compression and font embedding enabled status = pdfWriter.StartPDF( "output.pdf", ePDFVersion13, LogConfiguration::DefaultLogConfiguration(), PDFCreationSettings(true /*compressStreams*/, true /*embedFonts*/) ); if (status != eSuccess) { /* handle error */ return 1; } // Create an A4 page (595x842 points) PDFPage* page = new PDFPage(); page->SetMediaBox(PDFRectangle(0, 0, 595, 842)); PageContentContext* cxt = pdfWriter.StartPageContentContext(page); // Draw a filled cyan rectangle using raw PDF operators cxt->q(); cxt->k(100, 0, 0, 0); // CMYK: 100% cyan cxt->re(100, 500, 100, 100); // x, y, width, height cxt->f(); cxt->Q(); status = pdfWriter.EndPageContentContext(cxt); status = pdfWriter.WritePageAndRelease(page); // writes page and frees memory status = pdfWriter.EndPDF(); // Returns eSuccess (0) on success, eFailure (1) on error return status == eSuccess ? 0 : 1; } ``` -------------------------------- ### Object Parsing Events Source: https://github.com/galkahana/pdf-writer/wiki/PDF-Parsing Implement these methods to be notified when indirect objects start and end parsing, which is crucial for object decryption. ```APIDOC ## OnObjectStart and OnObjectEnd ### Description `OnObjectStart` is called when an indirect object begins parsing, providing its ID and generation number. `OnObjectEnd` is called when parsing of an indirect object completes. These events are essential for tracking the current object being parsed, which is often needed for decryption. It is recommended to maintain a stack of object IDs and generation numbers. ### Methods virtual void OnObjectStart(long long inObjectID, long long inGenerationNumber); virtual void OnObjectEnd(PDFObject* inObject); ### Note Take care not to attempt decryption of the **Encrypt** dictionary content. ``` -------------------------------- ### Get Image Dimensions Source: https://github.com/galkahana/pdf-writer/wiki/Images-Support Retrieve the width and height of an image using the GetImageDimensions method of the PDFWriter object. The dimensions are returned as a DoubleAndDoublePair. ```cpp DoubleAndDoublePair jpgDimensions = pdfWriter.GetImageDimensions("soundcloud_logo.jpg"); ``` -------------------------------- ### Build PDFParser Fuzzing Harness with Clang Source: https://github.com/galkahana/pdf-writer/wiki/Fuzz-Testing-Of-PDFParser Compile the project with clang to enable the fuzzing harness. Ensure clang and clang++ are set as C and C++ compilers respectively. ```bash mkdir build cd build cmake -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DBUILD_FUZZING_HARNESS=ON .. cmake --build . -- -j$(nproc) ``` -------------------------------- ### High-Level Drawing with AbstractContentContext Source: https://context7.com/galkahana/pdf-writer/llms.txt Demonstrates drawing various shapes (rectangle, square, circle, polygon) and writing text using high-level drawing helpers. Uses GraphicOptions and TextOptions for styling. Ensure necessary headers are included. ```cpp #include "PDFWriter.h" #include "PDFPage.h" #include "PageContentContext.h" #include "AbstractContentContext.h" using namespace PDFHummus; int main() { PDFWriter pdfWriter; pdfWriter.StartPDF("HighLevelShapes.pdf", ePDFVersion13); PDFPage* page = new PDFPage(); page->SetMediaBox(PDFRectangle(0, 0, 595, 842)); PageContentContext* cxt = pdfWriter.StartPageContentContext(page); // Fill a rectangle with CMYK color AbstractContentContext::GraphicOptions fillOpt( AbstractContentContext::eFill, AbstractContentContext::eCMYK, 0xFF000000 // 100% black in CMYK packed as 0xCCMMYYKK ); // Stroke a rectangle using a named color AbstractContentContext::GraphicOptions strokeOpt( AbstractContentContext::eStroke, AbstractContentContext::eRGB, AbstractContentContext::ColorValueForName("DarkMagenta"), 4.0 // stroke width ); // Draw shapes cxt->DrawRectangle(375, 220, 50, 160, fillOpt); // (x, y, width, height) cxt->DrawSquare(375, 440, 120, strokeOpt); // (x, y, edge, options) cxt->DrawCircle(149, 300, 80, fillOpt); // (centerX, centerY, radius, options) // Draw a polygon path DoubleAndDoublePairList points; points.push_back(DoubleAndDoublePair(75, 640)); points.push_back(DoubleAndDoublePair(149, 800)); points.push_back(DoubleAndDoublePair(225, 640)); cxt->DrawPath(points, fillOpt); // Write text using a font loaded from a file PDFUsedFont* font = pdfWriter.GetFontForFile("fonts/arial.ttf"); AbstractContentContext::TextOptions textOpt(font, 14, AbstractContentContext::eGray, 0); cxt->WriteText(75, 805, "Hello PDF-Writer", textOpt); cxt->WriteText(375, 805, "Squares", textOpt); pdfWriter.EndPageContentContext(cxt); pdfWriter.WritePageAndRelease(page); pdfWriter.EndPDF(); return 0; } ``` -------------------------------- ### Create and Use Form XObjects Source: https://context7.com/galkahana/pdf-writer/llms.txt Define reusable graphical elements as Form XObjects using StartFormXObject/EndFormXObjectAndRelease. Place them on pages using the Do operator. ```cpp #include "PDFWriter.h" #include "PDFPage.h" #include "PDFRectangle.h" #include "PageContentContext.h" #include "PDFFormXObject.h" #include "XObjectContentContext.h" using namespace PDFHummus; int main() { PDFWriter pdfWriter; pdfWriter.StartPDF("FormXObjects.pdf", ePDFVersion13); PDFPage* page = new PDFPage(); page->SetMediaBox(PDFRectangle(0, 0, 595, 842)); PageContentContext* pageCxt = pdfWriter.StartPageContentContext(page); // Draw some content before the form pageCxt->q(); pageCxt->k(100, 0, 0, 0); pageCxt->re(100, 500, 100, 100); pageCxt->f(); pageCxt->Q(); // Pause page stream to define a form XObject pdfWriter.PausePageContentContext(pageCxt); // Define a 200x100 form xobject with a red rectangle PDFFormXObject* form = pdfWriter.StartFormXObject(PDFRectangle(0, 0, 200, 100)); ObjectIDType formID = form->GetObjectID(); // save ID for later placement XObjectContentContext* formCxt = form->GetContentContext(); formCxt->q(); formCxt->k(0, 100, 100, 0); // red in CMYK formCxt->re(0, 0, 200, 100); formCxt->f(); formCxt->Q(); pdfWriter.EndFormXObjectAndRelease(form); // Resume page and place the form at two positions std::string formName = page->GetResourcesDictionary().AddFormXObjectMapping(formID); pageCxt->q(); pageCxt->cm(1, 0, 0, 1, 200, 600); pageCxt->Do(formName); pageCxt->Q(); pageCxt->q(); pageCxt->cm(1, 0, 0, 1, 200, 200); pageCxt->Do(formName); pageCxt->Q(); pdfWriter.EndPageContentContext(pageCxt); pdfWriter.WritePageAndRelease(page); // Reuse the same form on a second page PDFPage* page2 = new PDFPage(); page2->SetMediaBox(PDFRectangle(0, 0, 595, 842)); PageContentContext* cxt2 = pdfWriter.StartPageContentContext(page2); std::string formName2 = page2->GetResourcesDictionary().AddFormXObjectMapping(formID); cxt2->q(); cxt2->cm(1, 0, 0, 1, 300, 500); cxt2->Do(formName2); cxt2->Q(); pdfWriter.EndPageContentContext(cxt2); pdfWriter.WritePageAndRelease(page2); pdfWriter.EndPDF(); return 0; } ``` -------------------------------- ### StartReadingFromStream Source: https://github.com/galkahana/pdf-writer/wiki/PDF-Parsing Creates an IByteReader for stream content and positions the stream for immediate reading. Caller owns and must delete the returned object. ```APIDOC ## StartReadingFromStream ### Description This method is similar to `CreateInputStreamReader`, however it does also position the stream position for reading. This is very good in case you want to create a stream reader and start reading write after it. ### Method `IByteReader* StartReadingFromStream(PDFStreamInput* inStream)` ### Parameters * **inStream** (PDFStreamInput*) - The stream input to create a reader for. ### Returns IByteReader* - A new IByteReader object for the stream, positioned for reading. Caller must delete this object. ``` -------------------------------- ### Run PDF-Writer Tests Source: https://github.com/galkahana/pdf-writer/blob/main/readme.md Execute the test suite for PDF-Writer. The `-j8` flag enables parallel test execution, speeding up the process. Test output files are located in `./build/Testing/Output`. ```bash ctest --test-dir build -C Release -j8 ``` -------------------------------- ### Set Transparency with ExtGState in PDF-Writer Source: https://context7.com/galkahana/pdf-writer/llms.txt Demonstrates how to set transparency (alpha) using ExtGState dictionary objects and apply it to page content. Ensure the ExtGState is registered in the page's resource dictionary and activated with the 'gs' operator. ```cpp #include "PDFWriter.h" #include "PDFPage.h" #include "DictionaryContext.h" #include "PageContentContext.h" #include "AbstractContentContext.h" using namespace PDFHummus; int main() { PDFWriter pdfWriter; pdfWriter.StartPDF("Transparency.pdf", ePDFVersion14, LogConfiguration::DefaultLogConfiguration(), PDFCreationSettings(false, true)); // no stream compression for clarity ObjectsContext& objCtx = pdfWriter.GetObjectsContext(); // Create an ExtGState dictionary with 50% opacity ObjectIDType gsID = objCtx.StartNewIndirectObject(); DictionaryContext* dict = objCtx.StartDictionary(); dict->WriteKey("type"); dict->WriteNameValue("ExtGState"); dict->WriteKey("ca"); // fill opacity dict->WriteDoubleValue(0.5); dict->WriteKey("CA"); // stroke opacity dict->WriteDoubleValue(0.5); objCtx.EndDictionary(dict); objCtx.EndIndirectObject(); PDFPage* page = new PDFPage(); page->SetMediaBox(PDFRectangle(0, 0, 595, 842)); std::string gsName = page->GetResourcesDictionary().AddExtGStateMapping(gsID); PageContentContext* cxt = pdfWriter.StartPageContentContext(page); // Draw opaque background AbstractContentContext::GraphicOptions bgOpt(AbstractContentContext::eFill, AbstractContentContext::eCMYK, 0xFF00FF00); cxt->DrawRectangle(50, 400, 200, 200, bgOpt); // Apply transparency state, then draw semi-transparent text on top cxt->q(); cxt->gs(gsName); // activate the ExtGState PDFUsedFont* font = pdfWriter.GetFontForFile("fonts/arial.ttf"); cxt->BT(); cxt->k(0, 100, 100, 0); cxt->Tf(font, 30); cxt->Tm(1, 0, 0, 1, 100, 500); cxt->Tj("WATERMARK"); cxt->ET(); cxt->Q(); pdfWriter.EndPageContentContext(cxt); pdfWriter.WritePageAndRelease(page); pdfWriter.EndPDF(); return 0; } ``` -------------------------------- ### Get Source Document Stream Handle in C++ Source: https://github.com/galkahana/pdf-writer/wiki/PDF-Embedding Obtain a handle to the source document stream. This is useful for parsing the document while copying it, particularly for stream reading operations. ```cpp IByteReaderWithPosition* GetSourceDocumentStream(); ``` -------------------------------- ### Conditional Testing Subdirectory Inclusion Source: https://github.com/galkahana/pdf-writer/blob/main/CMakeLists.txt Includes the PDFWriterTesting subdirectory only when the project is at the top level and the testing directory exists. This prevents testing components from being built or installed when PDF-Writer is included as a subproject. ```cmake if(PROJECT_IS_TOP_LEVEL AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/PDFWriterTesting) # avoid installing the testing lib altogether when included in another project. # it's annoying when in parent all, and more annoying to then get the tests added # to the parent project ctest. # option specific for testing option(BUILD_FUZZING_HARNESS "Build the fuzzing harness, requires LLVM's clang") enable_testing() ADD_SUBDIRECTORY(PDFWriterTesting) endif() ``` -------------------------------- ### Build PDF-Writer with CMake Source: https://github.com/galkahana/pdf-writer/blob/main/CLAUDE.md Standard and feature-specific build commands using CMake. Use flags like -DPDFHUMMUS_NO_OPENSSL=TRUE to disable optional dependencies. ```bash cmake .. cmake --build . --config Release ``` ```bash cmake .. -DPDFHUMMUS_NO_OPENSSL=TRUE ``` ```bash cmake .. -DPDFHUMMUS_NO_PNG=TRUE ``` ```bash cmake .. -DPDFHUMMUS_NO_DCT=TRUE ``` ```bash cmake .. -DPDFHUMMUS_NO_TIFF=TRUE ``` ```bash ctest --test-dir . -C Release ``` -------------------------------- ### Access Internal Unicode Representation (C++) Source: https://github.com/galkahana/pdf-writer/wiki/Unicode-and-UnicodeString-class Use these methods to get direct access to the internal 4-byte representation of Unicode, which can be treated as UCS4/UTF32. This is useful for direct manipulation of Unicode data. ```cpp const ULongList& GetUnicodeList() const; ULongList& GetUnicodeList(); ``` -------------------------------- ### Run PDFParser Fuzzing Harness with libFuzzer Source: https://github.com/galkahana/pdf-writer/wiki/Fuzz-Testing-Of-PDFParser Execute the compiled fuzzing harness using libFuzzer. Provide the path to the minimal fuzzing corpus as an input directory. ```bash ./PDFWriterTesting/PDFParserFuzzingHarness ../PDFWriterTesting/Materials/fuzzing/MinimalFuzzingCorpus ``` -------------------------------- ### Get Source Document Parser in C++ Source: https://github.com/galkahana/pdf-writer/wiki/PDF-Embedding Retrieve the parser object for the input PDF. This parser contains the interpreted xref and a list of page IDs, allowing access to PDF file objects for further processing. ```cpp PDFParser* GetSourceDocumentParser(); ``` -------------------------------- ### CMake FetchContent for PDF-Writer Integration Source: https://github.com/galkahana/pdf-writer/blob/main/readme.md Recommended method for integrating PDF-Writer into your CMake project. This fetches the library directly from its Git repository. Ensure the `GIT_TAG` matches the desired version. ```cmake include(FetchContent) FetchContent_Declare( PDFHummus GIT_REPOSITORY https://github.com/galkahana/PDF-Writer.git GIT_TAG v4.6.2 FIND_PACKAGE_ARGS ) FetchContent_MakeAvailable(PDFHummus) target_link_libraries(YourTarget PDFHummus::PDFWriter) ``` -------------------------------- ### Get Copied Object ID in C++ Source: https://github.com/galkahana/pdf-writer/wiki/PDF-Embedding Determine the corresponding object ID in the result PDF for a given source PDF object ID. Returns a status code and the target object ID if the object was successfully copied. ```cpp EStatusCodeAndObjectIDType GetCopiedObjectID(ObjectIDType inSourceObjectID); ``` -------------------------------- ### Incorrect RefCountPtr Initialization with GetTrailer Source: https://github.com/galkahana/pdf-writer/wiki/PDF-Parsing Avoid initializing a RefCountPtr using the equality operator with a 'Get' method like GetTrailer. This performs initialization, not assignment, and does not automatically call AddRef, requiring manual AddRef calls to prevent memory leaks. ```cpp RefCountPtr theTrailerSmartPtr = parser.GetTrailer(); // NOT GOOD!!!! ``` -------------------------------- ### Create PDF Copying Context from Stream Source: https://github.com/galkahana/pdf-writer/wiki/Custom-input-and-output Initialize a PDF copying context using an input stream. This is a prerequisite for certain PDF manipulation operations involving streams. ```cpp PDFDocumentCopyingContext* CreatePDFCopyingContext(IByteReaderWithPosition* inPDFStream); ``` -------------------------------- ### Create PDF Copying Context Source: https://github.com/galkahana/pdf-writer/wiki/PDF-Embedding Create a copying context for a source PDF. This context will be used for all subsequent copying operations from that PDF. ```cpp copyingContext = pdfWriter.CreatePDFCopyingContext("C:\\PDFLibTests\\TestMaterials\\BasicTIFFImagesTest.PDF"); ``` -------------------------------- ### Custom Log Output Source: https://github.com/galkahana/pdf-writer/wiki/Custom-input-and-output Configuration for directing library log traces to custom IByteWriter streams. ```APIDOC ## Log Configuration ### Description Customize log output to any `IByteWriter` implementation, such as storing logs in a database. ### Usage Set the `LogStream` member of the `LogConfiguration` structure, which is passed to the PDF starting methods (e.g., `StartPDFForStream`). An appropriate constructor for `LogConfiguration` can be used to specify the desired stream. ``` -------------------------------- ### Append PDF Pages and Create Form XObjects Source: https://context7.com/galkahana/pdf-writer/llms.txt Demonstrates appending pages from one PDF to another, creating Form XObjects from PDF pages for reuse, and merging content of a single page into a new page. Supports file paths or IByteReader streams. ```cpp #include "PDFWriter.h" #include "PDFPage.h" #include "PDFRectangle.h" #include "PageContentContext.h" #include "PDFEmbedParameterTypes.h" using namespace PDFHummus; int main() { PDFWriter pdfWriter; pdfWriter.StartPDF("CombinedOutput.pdf", ePDFVersion13); // 1. Append ALL pages from another PDF pdfWriter.AppendPDFPagesFromPDF("source.pdf", PDFPageRange()); // 2. Append only pages 0-2 and 4-8 (0-based, inclusive ranges) PDFPageRange partialRange; partialRange.mType = PDFPageRange::eRangeTypeSpecific; partialRange.mSpecificRanges.push_back(ULongAndULong(0, 2)); partialRange.mSpecificRanges.push_back(ULongAndULong(4, 8)); pdfWriter.AppendPDFPagesFromPDF("source.pdf", partialRange); // 3. Use pages from a 2-page PDF as Form XObjects for imposition (2-up layout) EStatusCodeAndObjectIDTypeList forms = pdfWriter.CreateFormXObjectsFromPDF( "twopages.pdf", PDFPageRange(), ePDFPageBoxMediaBox); PDFPage* imposePage = new PDFPage(); imposePage->SetMediaBox(PDFRectangle(0, 0, 595, 842)); PageContentContext* cxt = pdfWriter.StartPageContentContext(imposePage); // Place first source page top-left (scaled 50%) cxt->q(); cxt->cm(0.5, 0, 0, 0.5, 0, 421); cxt->Do(imposePage->GetResourcesDictionary().AddFormXObjectMapping(forms.second.front())); cxt->Q(); // Place second source page bottom-right (scaled 50%) cxt->q(); cxt->cm(0.5, 0, 0, 0.5, 297.5, 0); cxt->Do(imposePage->GetResourcesDictionary().AddFormXObjectMapping(forms.second.back())); cxt->Q(); pdfWriter.EndPageContentContext(cxt); pdfWriter.WritePageAndRelease(imposePage); // 4. Merge (inline) a single page's content into a new page PDFPage* mergePage = new PDFPage(); mergePage->SetMediaBox(PDFRectangle(0, 0, 595, 842)); PDFPageRange singlePage; singlePage.mType = PDFPageRange::eRangeTypeSpecific; singlePage.mSpecificRanges.push_back(ULongAndULong(0, 0)); pdfWriter.MergePDFPagesToPage(mergePage, "source.pdf", singlePage); pdfWriter.WritePageAndRelease(mergePage); pdfWriter.EndPDF(); return 0; } ``` -------------------------------- ### Configuring Package Information Source: https://github.com/galkahana/pdf-writer/blob/main/CMakeLists.txt Configures the main package configuration file (PDFHummusConfig.cmake) and version file (PDFHummusConfigVersion.cmake) using helper functions. These files are essential for CMake's package management system. ```cmake include(CMakePackageConfigHelpers) configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/PDFHummusConfig.cmake" INSTALL_DESTINATION "lib/cmake/PDFHummus" ) write_basic_package_version_file( "${CMAKE_CURRENT_BINARY_DIR}/PDFHummusConfigVersion.cmake" VERSION "${PDFHummus_VERSION_MAJOR}.${PDFHummus_VERSION_MINOR}" COMPATIBILITY AnyNewerVersion ) ``` -------------------------------- ### Image Placement Options Source: https://github.com/galkahana/pdf-writer/wiki/Images-Support Customize image placement using the ImageOptions struct. Options include specifying image index for multi-page TIFFs, applying matrix transformations for scaling, and fitting images within bounding boxes. ```cpp //usage 1 AbstractContentContext::ImageOptions opt1; opt1.imageIndex = 2; cxt->DrawImage(10,10,"multipage.tif",opt1); ``` ```cpp //usage 2 AbstractContentContext::ImageOptions opt2; opt2.transformationMethod = AbstractContentContext::eMatrix; opt2.matrix[0] = opt2.matrix[3] = 0.25; cxt->DrawImage(10,10,"soundcloud_logo.jpg",opt2); ``` ```cpp //usage 3 AbstractContentContext::ImageOptions opt3; opt3.transformationMethod = AbstractContentContext::eFit; opt3.boundingBoxHeight = 100; opt3.boundingBoxWidth = 100; cxt->DrawImage(0,0,"XObjectContent.PDF",opt3); ``` ```cpp //usage 4 opt3.fitProportional = true; cxt->DrawImage(100,100,"XObjectContent.PDF",opt3); ``` -------------------------------- ### Create Filter for Stream Source: https://github.com/galkahana/pdf-writer/wiki/PDF-Parsing Implement this method if you need to add new filters for stream support. If not adding filters, return the input stream. ```cpp virtual IByteReader* CreateFilterForStream(IByteReader* inStream, PDFName* inFilterName, PDFDictionary* inDecodeParams); ``` -------------------------------- ### Continue PDF Creation to Stream Source: https://github.com/galkahana/pdf-writer/wiki/Custom-input-and-output Use this method to resume PDF creation when the output is directed to a stream, typically after using the state saving feature. It requires an IByteWriterWithPosition stream as the first parameter. ```cpp EStatusCode ContinuePDFForStream(IByteWriterWithPosition* inOutputStream, const string& inStateFilePath, const LogConfiguration& inLogConfiguration); ``` -------------------------------- ### Create and Place Form XObjects from PDF Pages Source: https://github.com/galkahana/pdf-writer/wiki/PDF-Embedding Use CreateFormXObjectsFromPDF to convert PDF pages into Form XObjects, then place them on a new page using content streams and transformation matrices. Ensure the PDFWriter and necessary page objects are initialized. ```cpp PDFWriter pdfWriter; pdfWriter.StartPDF("C:\\MyPDF.PDF",ePDFVersion13); EStatusCodeAndObjectIDTypeList result = pdfWriter.CreateFormXObjectsFromPDF( "C:\\Other2PagePDF.PDF", PDFPageRange(), ePDFPageBoxMediaBox); PDFPage* page = new PDFPage(); page->SetMediaBox(PDFRectangle(0,0,595,842)); PageContentContext* contentContext = pdfWriter.StartPageContentContext(page); // place the first page in the top left corner of the document contentContext->q(); contentContext->cm(0.5,0,0,0.5,0,421); contentContext->Do(page->GetResourcesDictionary().AddFormXObjectMapping(result.second.front())); contentContext->Q(); // place the second page in the bottom right corner of the document contentContext->q(); contentContext->cm(0.5,0,0,0.5,297.5,0); contentContext->Do(page->GetResourcesDictionary().AddFormXObjectMapping(result.second.back())); contentContext->Q(); pdfWriter.EndPageContentContext(contentContext); pdfWriter.WritePageAndRelease(page); pdfWriter.EndPDF(); ``` -------------------------------- ### Build PDFParser Fuzzing Harness with AFL++ Source: https://github.com/galkahana/pdf-writer/wiki/Fuzz-Testing-Of-PDFParser Compile the project with AFL++ compilers for advanced fuzzing features. Use afl-clang-fast and afl-clang-fast++ for C and C++ compilation. ```bash mkdir build cd build cmake -DCMAKE_C_COMPILER=afl-clang-fast -DCMAKE_CXX_COMPILER=afl-clang-fast++ -DBUILD_FUZZING_HARNESS=ON .. cmake --build . -- -j$(nproc) ``` -------------------------------- ### Continue PDF Writing Session Source: https://github.com/galkahana/pdf-writer/wiki/State-Saving Use `ContinuePDF` with a new `PDFWriter` instance to resume a previously saved PDF writing session. Provide the output file path, the state file path created by `Shutdown`, and optionally a log configuration. ```cpp EStatusCode ContinuePDF(const string& inOutputFilePath, const string& inStateFilePath, const LogConfiguration& inLogConfiguration = LogConfiguration::DefaultLogConfiguration); ``` -------------------------------- ### Create PDF Copying Context Source: https://context7.com/galkahana/pdf-writer/llms.txt Use `CreatePDFCopyingContext` to efficiently copy pages from a source PDF, enabling operations like appending, form XObject creation, and merging. Supports password-protected PDFs via `PDFParsingOptions`. ```cpp #include "PDFWriter.h" #include "PDFDocumentCopyingContext.h" #include "PDFParsingOptions.h" using namespace PDFHummus; int main() { PDFWriter pdfWriter; pdfWriter.StartPDF("CopyResult.pdf", ePDFVersion13); // Open a (possibly password-protected) source PDF PDFDocumentCopyingContext* ctx = pdfWriter.CreatePDFCopyingContext( "source.pdf", PDFParsingOptions("userpassword") // omit for unencrypted files ); if (!ctx) return 1; unsigned long pageCount = ctx->GetSourceDocumentParser()->GetPagesCount(); for (unsigned long i = 0; i < pageCount; ++i) { // Append each page as a full page in the output EStatusCodeAndObjectIDType result = ctx->AppendPDFPageFromPDF(i); if (result.first != eSuccess) { /* handle error */ break; } } // Or create a form XObject from a specific page for reuse EStatusCodeAndObjectIDType formResult = ctx->CreateFormXObjectFromPDFPage(0, ePDFPageBoxMediaBox); // formResult.second is the ObjectIDType of the form // Iterate copied object ID mappings MapIterator it = ctx->GetCopiedObjectsMappingIterator(); while (it.MoveNext()) { ObjectIDType src = it.GetKey(); ObjectIDType dst = it.GetValue(); // src -> dst mapping } delete ctx; pdfWriter.EndPDF(); return 0; } ``` -------------------------------- ### Write PDF to a Memory Stream with PDF-Writer Source: https://context7.com/galkahana/pdf-writer/llms.txt This snippet shows how to generate a PDF directly into an in-memory buffer using `PDFWriter::StartPDFForStream` and `OutputStringBufferStream`. Remember to use `EndPDFForStream` for finalization when writing to a stream. ```cpp #include "PDFWriter.h" #include "OutputStringBufferStream.h" #include "PDFPage.h" #include "PDFRectangle.h" #include "PageContentContext.h" using namespace PDFHummus; int main() { PDFWriter pdfWriter; OutputStringBufferStream outputStream; // in-memory buffer EStatusCode status = pdfWriter.StartPDFForStream( &outputStream, ePDFVersion13, LogConfiguration::DefaultLogConfiguration(), PDFCreationSettings(true, true) ); if (status != eSuccess) return 1; PDFPage* page = new PDFPage(); page->SetMediaBox(PDFRectangle(0, 0, 595, 842)); PageContentContext* cxt = pdfWriter.StartPageContentContext(page); cxt->q(); cxt->rg(1, 0, 0); // RGB red fill cxt->re(50, 50, 200, 200); cxt->f(); cxt->Q(); pdfWriter.EndPageContentContext(cxt); pdfWriter.WritePageAndRelease(page); pdfWriter.EndPDFForStream(); // must use EndPDFForStream, not EndPDF // outputStream now contains the complete PDF bytes std::string pdfBytes = outputStream.ToString(); return 0; } ``` -------------------------------- ### PDF Copying Context Source: https://context7.com/galkahana/pdf-writer/llms.txt Demonstrates how to create a PDFDocumentCopyingContext for efficient page-by-page copying from a source PDF. This context allows for mixing append, form XObject, and merge operations, and supports password-protected PDFs. ```APIDOC ## PDF Copying Context `PDFWriter::CreatePDFCopyingContext` creates a `PDFDocumentCopyingContext` for fine-grained, efficient page-by-page copying from a source PDF. Enables mixing append, form XObject, and merge operations against a single parsed source, with object sharing. Supports password-protected PDFs via `PDFParsingOptions`. ### Method `PDFWriter::CreatePDFCopyingContext(const string& inFileName, const PDFParsingOptions& inParsingOptions = PDFParsingOptions())` ### Parameters * **inFileName** (string) - Required - The path to the source PDF file. * **inParsingOptions** (PDFParsingOptions) - Optional - Options for parsing the PDF, including password. ### Example Usage ```cpp #include "PDFWriter.h" #include "PDFDocumentCopyingContext.h" #include "PDFParsingOptions.h" using namespace PDFHummus; // ... inside main function ... PDFWriter pdfWriter; pdfWriter.StartPDF("CopyResult.pdf", ePDFVersion13); // Open a (possibly password-protected) source PDF PDFDocumentCopyingContext* ctx = pdfWriter.CreatePDFCopyingContext( "source.pdf", PDFParsingOptions("userpassword") // omit for unencrypted files ); if (!ctx) { /* handle error */ } // ... rest of the copying logic ... delete ctx; pdfWriter.EndPDF(); ``` ### Related Operations * `PDFDocumentCopyingContext::AppendPDFPageFromPDF`: Appends a page from the source PDF to the output. * `PDFDocumentCopyingContext::CreateFormXObjectFromPDFPage`: Creates a form XObject from a specific page. * `PDFDocumentCopyingContext::GetCopiedObjectsMappingIterator`: Iterates through mappings of copied object IDs. ``` -------------------------------- ### Create Form XObjects Using Media Box Source: https://github.com/galkahana/pdf-writer/wiki/PDF-Embedding This function converts pages from a specified PDF file into Form XObjects, using the page's MediaBox as the form's bounding box. It returns a status code and a list of object IDs for the created forms. ```cpp EStatusCodeAndObjectIDTypeList result = pdfWriter.CreateFormXObjectsFromPDF( "C:\\Other2PagePDF.PDF", PDFPageRange(), ePDFPageBoxMediaBox); ``` -------------------------------- ### Build and Test PDF-Writer Target Source: https://github.com/galkahana/pdf-writer/blob/main/readme.md Builds a specific target (`pdfWriterCheck`) and runs tests. This is useful for continuous integration or specific testing scenarios. ```bash cmake --build build --target pdfWriterCheck --config Release ```