Repository: J-F-Liu/lopdf (language: rust)

Issue:
Improve documentation so that even RUST beginners can understand



Discussion / hints:
I would recommend reading through the [PDF reference manual](https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/PDF32000_2008.pdf) **PDF Warning**. It helped me understand a lot of the examples and how they worked. 

Keep in mind, that this is a very low level PDF library, one step above manually writing the pdf data to a file yourself. It takes care of some of the more tedious processes of writing the PDF like handling the document trailer and ID tracking but still expects you to understand the PDF file format specifically. 

If you need something that is more user friendly, I would recommend looking on [crates.io]() for pdf crates that use lopdf as a dependency. There are a few out there that have varying feature sets.

Fix patch:
diff --git a/README.md b/README.md
index 66604ef..aa5f567 100644
--- a/README.md
+++ b/README.md
@@ -6,6 +6,12 @@
 
 A Rust library for PDF document manipulation.
 
+A useful reference for understanding the PDF file format and the
+eventual usage of this libary is the
+[PDF 1.7 Reference Document](https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/PDF32000_2008.pdf).
+The PDF 2.0 specification is an ISO standard that you have to pay for,
+but it is backwards compatible with the open sourced 1.7 version.
+
 ## Example Code
 
 * Create PDF document
@@ -15,50 +21,132 @@ use lopdf::dictionary;
 use lopdf::{Document, Object, Stream};
 use lopdf::content::{Content, Operation};
 
+// with_version specifes the PDF version this document complies with.
 let mut doc = Document::with_version("1.5");
+// Object IDs are used for cross referencing in PDF documents. `lopdf` helps keep track of them
+// for us. They are simple integers.
+// Calls to `doc.new_object_id` and `doc.add_object` return an object id
+
+// pages is the root node of the page tree
 let pages_id = doc.new_object_id();
+
+// fonts are dictionaries. The type, subtype and basefont tags
+// are straight out of the PDF reference manual
+//
+// The dictionary macro is a helper that allows complex
+// key, value relationships to be represented in a simpler
+// visual manner, similar to a match statement.
+// Dictionary is linkedHashMap of byte vector, and object
 let font_id = doc.add_object(dictionary! {
+    // type of dictionary
     "Type" => "Font",
+    // type of font, type1 is simple postscript font
     "Subtype" => "Type1",
+    // basefont is postscript name of font for type1 font.
+    // See PDF reference document for more details
     "BaseFont" => "Courier",
 });
+
+// font dictionaries need to be added into resource dictionaries
+// in order to be used.
+// Resource dictionaries can contain more than just fonts,
+// but normally just contains fonts
+// Only one resource dictionary is allowed per page tree root
 let resources_id = doc.add_object(dictionary! {
+    // fonts are actually triplely nested dictionaries. Fun!
     "Font" => dictionary! {
+        // F1 is the font name used when writing text.
+        // It must be unique in the document. It does not
+        // have to be F1
         "F1" => font_id,
     },
 });
+
+// Content is a wrapper struct around an operations struct that contains a vector of operations
+// The operations struct contains a vector of operations that match up with a particular PDF
+// operator and operands.
+// Reference the PDF reference for more details on these operators and operands.
+// Note, the operators and operands are specified in a reverse order than they
+// actually appear in the PDF file itself.
 let content = Content {
     operations: vec![
+        // BT begins a text element. it takes no operands
         Operation::new("BT", vec![]),
+        // Tf specifies the font and font size. Font scaling is complicated in PDFs. Reference
+        // the reference for more info.
+        // The info() methods are defined based on their paired .from() methods (this
+        // functionality is built into rust), and are converting the provided values into
+        // An enum that represents the basic object types in PDF documents.
         Operation::new("Tf", vec!["F1".into(), 48.into()]),
+        // Td adjusts the translation components of the text matrix. When used for the first
+        // time after BT, it sets the initial text position on the page.
+        // Note: PDF documents have Y=0 at the bottom. Thus 600 to print text near the top.
         Operation::new("Td", vec![100.into(), 600.into()]),
+        // Tj prints a string literal to the page. By default, this is black text that is
+        // filled in. There are other operators that can produce various textual effects and
+        // colors
         Operation::new("Tj", vec![Object::string_literal("Hello World!")]),
+        // ET ends the text element
         Operation::new("ET", vec![]),
     ],
 };
+
+// Streams are a dictionary followed by a sequence of bytes. What that sequence of bytes
+// represents depends on context
+// The stream dictionary is set internally to lopdf and normally doesn't
+// need to be manually nanipulated. It contains keys such as
+// Length, Filter, DecodeParams, etc
+//
+// content is a stream of encoded content data.
 let content_id = doc.add_object(Stream::new(dictionary! {}, content.encode().unwrap()));
+
+// Page is a dictionary that represents one page of a PDF file.
+// It has a type, parent and contents
 let page_id = doc.add_object(dictionary! {
     "Type" => "Page",
     "Parent" => pages_id,
     "Contents" => content_id,
 });
+
+// Again, pages is the root of the page tree. The ID was already created
+// at the top of the page, since we needed it to assign to the parent element of the page
+// dictionary
+//
+// This is just the basic requirements for a page tree root object. There are also many
+// additional entries that can be added to the dictionary if needed. Some of these can also be
+// defined on the page dictionary itself, and not inherited from the page tree root.
 let pages = dictionary! {
+    // Type of dictionary
     "Type" => "Pages",
+    // Vector of page IDs in document. Normally would contain more than one ID and be produced
+    // using a loop of some kind
     "Kids" => vec![page_id.into()],
+    // Page count
     "Count" => 1,
+    // ID of resources dictionary, defined earlier
     "Resources" => resources_id,
+    // a rectangle that defines the boundaries of the physical or digital media. This is the
+    // "Page Size"
     "MediaBox" => vec![0.into(), 0.into(), 595.into(), 842.into()],
 };
+
+// using insert() here, instead of add_object() since the id is already known.
 doc.objects.insert(pages_id, Object::Dictionary(pages));
+
+// Creating document catalog.
+// There are many more entries allowed in the catalog dictionary.
 let catalog_id = doc.add_object(dictionary! {
     "Type" => "Catalog",
     "Pages" => pages_id,
 });
+
+// Root key in trailer is set here to ID of document catalog,
+// remainder of trailer is set during doc.save().
 doc.trailer.set("Root", catalog_id);
 doc.compress();
 
 // Store file in current working directory.
-// Note: Line is exclude for when running tests
+// Note: Line is excluded when running tests
 if false {
     doc.save("example.pdf").unwrap();
 }
@@ -214,14 +302,14 @@ fn main() -> std::io::Result<()> {
         }
     }
 
-    // If no "Pages" found abort
+    // If no "Pages" object found abort
     if pages_object.is_none() {
         println!("Pages root not found.");
 
         return Ok(());
     }
 
-    // Iter over all "Page" and collect with the parent "Pages" created before
+    // Iterate over all "Page" objects and collect into the parent "Pages" created before
     for (object_id, object) in documents_pages.iter() {
         if let Ok(dictionary) = object.as_dict() {
             let mut dictionary = dictionary.clone();
@@ -299,7 +387,7 @@ fn main() -> std::io::Result<()> {
 
     // Save the merged PDF
     // Store file in current working directory.
-    // Note: Line is exclude for when running tests
+    // Note: Line is excluded when running tests
     if false {
         document.save("merged.pdf").unwrap();
     }
@@ -321,7 +409,7 @@ use lopdf::Document;
     doc.version = "1.4".to_string();
     doc.replace_text(1, "Hello World!", "Modified text!");
     // Store file in current working directory.
-    // Note: Line is exclude for when running tests
+    // Note: Line is excluded when running tests
     if false {
         doc.save("modified.pdf").unwrap();
     }
@@ -330,10 +418,10 @@ use lopdf::Document;
 
 ## FAQ
 
-* Why keeping everything in memory as high-level objects until finally serializing the entire document?
+* Why does the library keep everything in memory as high-level objects until finally serializing the entire document?
 
-    Normally a PDF document won't be very large, ranging form tens of KB to hundreds of MB. Memory size is not a bottle neck for today's computer.
-    By keep the whole document in memory, stream length can be pre-calculated, no need to use a reference object for the Length entry,
+    Normally a PDF document won't be very large, ranging from tens of KB to hundreds of MB. Memory size is not a bottle neck for today's computer.
+    By keeping the whole document in memory, stream length can be pre-calculated, no need to use a reference object for the Length entry,
     the resulting PDF file is smaller for distribution and faster for PDF consumers to process.
 
     Producing is a one-time effort, while consuming is many more.
diff --git a/examples/create.rs b/examples/create.rs
index bb33e06..2cb2115 100644
--- a/examples/create.rs
+++ b/examples/create.rs
@@ -4,45 +4,128 @@ use lopdf::content::{Content, Operation};
 use lopdf::{Document, Object, Stream};
 
 fn main() {
+    // with_version specifes the PDF version this document complies with.
     let mut doc = Document::with_version("1.5");
+
+    // Object IDs are used for cross referencing in PDF documents. `lopdf` helps keep track of them
+    // for us. They are simple integers.
+    // Calls to `doc.new_object_id` and `doc.add_object` return an object id
+
+    // pages is the root node of the page tree
     let pages_id = doc.new_object_id();
+
+    // fonts are dictionaries. The type, subtype and basefont tags
+    // are straight out of the PDF reference manual
+    //
+    // The dictionary macro is a helper that allows complex
+    // key, value relationships to be represented in a simpler
+    // visual manner, similar to a match statement.
+    // Dictionary is linkedHashMap of byte vector, and object
     let font_id = doc.add_object(dictionary! {
+        // type of dictionary
         "Type" => "Font",
+        // type of font, type1 is simple postscript font
         "Subtype" => "Type1",
+        // basefont is postscript name of font for type1 font.
+        // See PDF reference document for more details
         "BaseFont" => "Courier",
     });
+
+    // font dictionaries need to be added into resource dictionaries
+    // in order to be used.
+    // Resource dictionaries can contain more than just fonts,
+    // but normally just contains fonts
+    // Only one resource dictionary is allowed per page tree root
     let resources_id = doc.add_object(dictionary! {
+        // fonts are actually triplely nested dictionaries. Fun!
         "Font" => dictionary! {
+            // F1 is the font name used when writing text.
+            // It must be unique in the document. It does not
+            // have to be F1
             "F1" => font_id,
         },
     });
+
+    // Content is a wrapper struct around an operations struct that contains a vector of operations
+    // The operations struct contains a vector of operations that match up with a particular PDF
+    // operator and operands.
+    // Reference the PDF reference for more details on these operators and operands.
+    // Note, the operators and operands are specified in a reverse order than they
+    // actually appear in the PDF file itself.
     let content = Content {
         operations: vec![
+            // BT begins a text element. it takes no operands
             Operation::new("BT", vec![]),
+            // Tf specifies the font and font size. Font scaling is complicated in PDFs. Reference
+            // the reference for more info.
+            // The info() methods are defined based on their paired .from() methods (this
+            // functionality is built into rust), and are converting the provided values into
+            // An enum that represents the basic object types in PDF documents.
             Operation::new("Tf", vec!["F1".into(), 48.into()]),
+            // Td adjusts the translation components of the text matrix. When used for the first
+            // time after BT, it sets the initial text position on the page.
+            // Note: PDF documents have Y=0 at the bottom. Thus 600 to print text near the top.
             Operation::new("Td", vec![100.into(), 600.into()]),
+            // Tj prints a string literal to the page. By default, this is black text that is
+            // filled in. There are other operators that can produce various textual effects and
+            // colors
             Operation::new("Tj", vec![Object::string_literal("Hello World!")]),
+            // ET ends the text element
             Operation::new("ET", vec![]),
         ],
     };
+
+    // Streams are a dictionary followed by a sequence of bytes. What that sequence of bytes
+    // represents depends on context
+    // The stream dictionary is set internally to lopdf and normally doesn't
+    // need to be manually nanipulated. It contains keys such as
+    // Length, Filter, DecodeParams, etc
+    //
+    // content is a stream of encoded content data.
     let content_id = doc.add_object(Stream::new(dictionary! {}, content.encode().unwrap()));
+
+    // Page is a dictionary that represents one page of a PDF file.
+    // It has a type, parent and contents
     let page_id = doc.add_object(dictionary! {
         "Type" => "Page",
         "Parent" => pages_id,
         "Contents" => content_id,
     });
+
+    // Again, pages is the root of the page tree. The ID was already created
+    // at the top of the page, since we needed it to assign to the parent element of the page
+    // dictionary
+    //
+    // This is just the basic requirements for a page tree root object. There are also many
+    // additional entries that can be added to the dictionary if needed. Some of these can also be
+    // defined on the page dictionary itself, and not inherited from the page tree root.
     let pages = dictionary! {
+        // Type of dictionary
         "Type" => "Pages",
+        // Vector of page IDs in document. Normally would contain more than one ID and be produced
+        // using a loop of some kind
         "Kids" => vec![page_id.into()],
+        // Page count
         "Count" => 1,
+        // ID of resources dictionary, defined earlier
         "Resources" => resources_id,
+        // a rectangle that defines the boundaries of the physical or digital media. This is the
+        // "Page Size"
         "MediaBox" => vec![0.into(), 0.into(), 595.into(), 842.into()],
     };
+
+    // using insert() here, instead of add_object() since the id is already known.
     doc.objects.insert(pages_id, Object::Dictionary(pages));
+
+    // Creating document catalog.
+    // There are many more entries allowed in the catalog dictionary.
     let catalog_id = doc.add_object(dictionary! {
         "Type" => "Catalog",
         "Pages" => pages_id,
     });
+
+    // Root key in trailer is set here to ID of document catalog,
+    // remainder of trailer is set during doc.save().
     doc.trailer.set("Root", catalog_id);
     doc.compress();
 


Test patch: