### anytomd - Quick Start (Rust) Source: https://docs.rs/anytomd Provides a quick start example in Rust demonstrating how to use the `anytomd` library to convert CSV data and a DOCX file to Markdown. ```rust use anytomd::{convert_bytes, ConversionOptions}; // Convert raw bytes with an explicit format let options = ConversionOptions::default(); let csv_data = b"Name,Age\nAlice,30\nBob,25"; let result = convert_bytes(csv_data, "csv", &options).unwrap(); println!("{}", result.markdown); use anytomd::{convert_file, ConversionOptions}; let options = ConversionOptions::default(); let result = convert_file("document.docx", &options).unwrap(); println!("{}", result.markdown); ``` -------------------------------- ### Quick Start Examples Source: https://docs.rs/anytomd/1.2.1/anytomd Provides basic usage examples for converting CSV data and DOCX files to Markdown. ```APIDOC ## Quick Start ### Convert Bytes (CSV Example) ```rust use anytomd::{convert_bytes, ConversionOptions}; let options = ConversionOptions::default(); let csv_data = b"Name,Age\nAlice,30\nBob,25"; let result = convert_bytes(csv_data, "csv", &options).unwrap(); println!("{}", result.markdown); ``` ### Convert File (DOCX Example) ```rust use anytomd::{convert_file, ConversionOptions}; let options = ConversionOptions::default(); let result = convert_file("document.docx", &options).unwrap(); println!("{}", result.markdown); ``` ``` -------------------------------- ### Initialize GeminiDescriber Source: https://docs.rs/anytomd/1.2.1/anytomd/converter/gemini/struct.GeminiDescriber.html Examples for creating a new GeminiDescriber instance using a direct API key or the GEMINI_API_KEY environment variable. ```rust use anytomd::gemini::GeminiDescriber; let describer = GeminiDescriber::new("your-api-key".to_string()); // or from the GEMINI_API_KEY environment variable: let describer = GeminiDescriber::from_env().unwrap(); ``` -------------------------------- ### Initialize PPTX Table Test Source: https://docs.rs/anytomd/1.2.1/src/anytomd/converter/pptx.rs.html Starts the definition of a test case for basic table structures in a PPTX slide. ```rust #[test] fn test_pptx_table_basic() { let data = build_test_pptx(&[TestSlide { title: Some("Data"), ``` -------------------------------- ### AnytoMD Conversion Functions Source: https://docs.rs/anytomd/1.2.1/index.html Examples of how to use the `anytomd` library to convert document content to Markdown. ```APIDOC ## Convert Bytes to Markdown ### Description Converts raw bytes of a document into Markdown format. Requires specifying the document's format explicitly. ### Method `convert_bytes` ### Parameters - `data` (bytes) - The raw byte content of the document. - `format` (string) - The explicit format of the document (e.g., "csv", "docx"). - `options` (`ConversionOptions`) - Options to control the conversion process. ### Request Example ```rust use anytomd::{convert_bytes, ConversionOptions}; let options = ConversionOptions::default(); let csv_data = b"Name,Age\nAlice,30\nBob,25"; let result = convert_bytes(csv_data, "csv", &options).unwrap(); println!("{}", result.markdown); ``` ### Response #### Success Response (200) - `markdown` (string) - The converted Markdown content. - `warnings` (array of `ConversionWarning`) - Any warnings encountered during conversion. #### Response Example ```json { "markdown": "| Name | Age |\n|-------|-------|\n| Alice | 30 |\n| Bob | 25 |\n", "warnings": [] } ``` ## Convert File to Markdown ### Description Converts the content of a file at a given path into Markdown format. The library attempts to detect the file type automatically. ### Method `convert_file` ### Parameters - `file_path` (string) - The path to the document file. - `options` (`ConversionOptions`) - Options to control the conversion process. ### Request Example ```rust use anytomd::{convert_file, ConversionOptions}; let options = ConversionOptions::default(); let result = convert_file("document.docx", &options).unwrap(); println!("{}", result.markdown); ``` ### Response #### Success Response (200) - `markdown` (string) - The converted Markdown content. - `warnings` (array of `ConversionWarning`) - Any warnings encountered during conversion. #### Response Example ```json { "markdown": "# Document Title\n\nThis is the content of the document...", "warnings": [] } ``` ``` -------------------------------- ### Resolve Relative Path Against Directory Source: https://docs.rs/anytomd/1.2.1/src/anytomd/converter/ooxml_utils.rs.html Resolves a target path relative to a base directory path within an OOXML package. It handles cases where the target is absolute (starts with '/') or relative, and normalizes the resulting path. For example, resolving '../media/image1.png' against 'xl/drawings' yields 'xl/media/image1.png'. ```rust pub(crate) fn resolve_relative_path(base_dir: &str, target: &str) -> String { let joined = if target.starts_with('/') || base_dir.is_empty() { target.to_string() } else { format!("{base_dir}/{target}") }; normalize_package_path(&joined) } ``` -------------------------------- ### Constructing DOCX Package Files Source: https://docs.rs/anytomd/1.2.1/src/anytomd/converter/docx.rs.html Demonstrates writing XML components and image data into a zip archive to form a valid DOCX structure. ```rust let ct = r#""#; zip.start_file("[Content_Types].xml", opts).unwrap(); zip.write_all(ct.as_bytes()).unwrap(); zip.start_file("_rels/.rels", opts).unwrap(); zip.write_all( br#""#, ).unwrap(); zip.start_file("word/document.xml", opts).unwrap(); zip.write_all(document_xml.as_bytes()).unwrap(); zip.start_file("word/_rels/document.xml.rels", opts) .unwrap(); zip.write_all(rels_xml.as_bytes()).unwrap(); for (path, data) in images { zip.start_file(*path, opts).unwrap(); zip.write_all(data).unwrap(); } let cursor = zip.finish().unwrap(); cursor.into_inner() ``` -------------------------------- ### GeminiDescriber Initialization Source: https://docs.rs/anytomd/1.2.1/anytomd/converter/gemini/struct.GeminiDescriber.html Demonstrates how to create a new instance of GeminiDescriber, either with an API key or by reading from an environment variable. ```APIDOC ## GeminiDescriber Initialization ### Description Provides methods to initialize the `GeminiDescriber`. ### Methods #### `new(api_key: String) -> Self` Create a new `GeminiDescriber` with the given API key. #### `from_env() -> Result` Create a new `GeminiDescriber` by reading the `GEMINI_API_KEY` environment variable. ### Request Example ```rust use anytomd::gemini::GeminiDescriber; // Using an API key let describer = GeminiDescriber::new("your-api-key".to_string()); // Using environment variable let describer = GeminiDescriber::from_env().unwrap(); ``` ``` -------------------------------- ### Handle XML Start Events for Document Elements Source: https://docs.rs/anytomd/1.2.1/src/anytomd/converter/docx.rs.html Processes start tags for elements like hyperlinks, text runs, and drawing properties to extract attributes and update state. ```rust let val = String::from_utf8_lossy(&attr.value).to_string(); // numId "0" means no numbering if val != "0" { current_num_id = Some(val); } } } } "hyperlink" if in_paragraph => { in_hyperlink = true; hyperlink_runs.clear(); hyperlink_runs_plain.clear(); current_hyperlink_url = None; for attr in e.attributes().flatten() { let key = std::str::from_utf8(attr.key.as_ref()).unwrap_or(""); if key == "r:id" || key.ends_with(":id") { let rid = String::from_utf8_lossy(&attr.value).to_string(); current_hyperlink_url = resolve_hyperlink_url(&rid, relationships, &mut warnings); } } } "r" if in_paragraph => { in_run = true; current_run_bold = false; current_run_italic = false; } "rPr" if in_run => { in_run_properties = true; } "b" if in_run_properties => { // Bold: or // Check for explicit false current_run_bold = !is_val_false(e); } "i" if in_run_properties => { current_run_italic = !is_val_false(e); } "t" if in_run => { in_text = true; } "drawing" if in_run => { in_drawing = true; current_image_alt = None; current_image_rel_id = None; } "docPr" if in_drawing => { // for attr in e.attributes().flatten() { let local_name = attr.key.local_name(); let k = std::str::from_utf8(local_name.as_ref()).unwrap_or(""); if k == "descr" { let val = String::from_utf8_lossy(&attr.value).to_string(); if !val.is_empty() { current_image_alt = Some(val); } } } } "blip" if in_drawing => { // for attr in e.attributes().flatten() { let key = std::str::from_utf8(attr.key.as_ref()).unwrap_or(""); if key == "r:embed" || key.ends_with(":embed") { current_image_rel_id = Some(String::from_utf8_lossy(&attr.value).to_string()); } } } _ => {} ``` -------------------------------- ### Handle Shape Start Event in Rust Source: https://docs.rs/anytomd/1.2.1/src/anytomd/converter/pptx.rs.html Parses the start of a shape element (), identifying placeholder types and text body context. Requires mutable references to state variables. ```rust fn handle_shape_start( local_str: &str, e: &quick_xml::events::BytesStart, placeholder_type: &mut Option, in_text_body: &mut bool, in_paragraph: &mut bool, in_run: &mut bool, in_text: &mut bool, current_paragraph: &mut String, ) { match local_str { "ph" => { // or etc. let mut ph_type = PlaceholderType::Other; for attr in e.attributes().flatten() { let local_name = attr.key.local_name(); let key = std::str::from_utf8(local_name.as_ref()).unwrap_or(""); if key == "type" { let val = String::from_utf8_lossy(&attr.value); ph_type = match val.as_ref() { "title" => PlaceholderType::Title, "ctrTitle" => PlaceholderType::CenterTitle, "subTitle" => PlaceholderType::SubTitle, "body" => PlaceholderType::Body, _ => PlaceholderType::Other, }; } } *placeholder_type = Some(ph_type); } "txBody" => { *in_text_body = true; } "p" if *in_text_body => { *in_paragraph = true; current_paragraph.clear(); } "r" if *in_paragraph => { *in_run = true; } "t" if *in_run => { *in_text = true; } _ => {} } } ``` -------------------------------- ### Create GeminiDescriber from Environment (Missing Key) Source: https://docs.rs/anytomd/1.2.1/src/anytomd/converter/gemini.rs.html Attempts to create a GeminiDescriber from the environment when the API key is not set. Expects an error. ```rust with_env_key(|| { remove_env_key(); let result = GeminiDescriber::from_env(); assert!(result.is_err()); let err = result.unwrap_err(); assert!( format!("{err}").contains("GEMINI_API_KEY"), "error was: {err}" ); }); ``` -------------------------------- ### Handle Graphic Frame Start Event in Rust Source: https://docs.rs/anytomd/1.2.1/src/anytomd/converter/pptx.rs.html Manages the start of a graphic frame, specifically for table elements. It initializes table parsing state, including tracking if inside a table, row, or cell. ```rust fn handle_graphic_frame_start( local_str: &str, in_table: &mut bool, in_table_row: &mut bool, in_table_cell: &mut bool, in_cell_paragraph: &mut bool, in_cell_run: &mut bool, in_cell_text: &mut bool, current_cell: &mut String, current_row: &mut Vec, table_rows: &mut Vec>, ) { match local_str { "tbl" => { *in_table = true; table_rows.clear(); } "tr" if *in_table => { *in_table_row = true; current_row.clear(); } "tc" if *in_table_row => { *in_table_cell = true; current_cell.clear(); } "p" if *in_table_cell => { // Add space separator between paragraphs in the same cell if !current_cell.is_empty() { current_cell.push(' '); } *in_cell_paragraph = true; } "r" if *in_cell_paragraph => { *in_cell_run = true; } "t" if *in_cell_run => { *in_cell_text = true; } _ => {} } } ``` -------------------------------- ### Initialize AsyncGeminiDescriber from Environment Variable Source: https://docs.rs/anytomd/1.2.1/src/anytomd/converter/gemini.rs.html Create a new AsyncGeminiDescriber instance by reading the GEMINI_API_KEY environment variable. Requires the 'async-gemini' feature flag. Returns an error if the environment variable is not set. ```rust use anytomd::gemini::AsyncGeminiDescriber; let describer = AsyncGeminiDescriber::from_env().unwrap(); ``` -------------------------------- ### Extract markdown heading Source: https://docs.rs/anytomd/1.2.1/src/anytomd/converter/ipynb.rs.html Parses markdown content to find the first top-level heading starting with '# '. ```rust fn extract_heading_title(markdown: &str) -> Option { for line in markdown.lines() { let trimmed = line.trim(); if let Some(heading) = trimmed.strip_prefix("# ") { let heading = heading.trim(); if !heading.is_empty() { return Some(heading.to_string()); } } } None } ``` -------------------------------- ### Initialize AsyncGeminiDescriber with API Key Source: https://docs.rs/anytomd/1.2.1/src/anytomd/converter/gemini.rs.html Create a new AsyncGeminiDescriber instance using a provided API key. Ensure the 'async-gemini' feature flag is enabled. ```rust use anytomd::gemini::AsyncGeminiDescriber; let describer = AsyncGeminiDescriber::new("your-api-key".to_string()); ``` -------------------------------- ### anytomd - Convert File Source: https://docs.rs/anytomd Demonstrates how to convert a file from a given path to Markdown using the `convert_file` function. The library attempts to detect the file format automatically. ```APIDOC ## POST /convert/file ### Description Converts a file from the specified path to Markdown. The library attempts to detect the file format automatically. ### Method POST ### Endpoint /convert/file ### Parameters #### Request Body - **filePath** (string) - Required - The path to the file to be converted. - **options** (object) - Optional - Conversion options. - **skip_code_blocks** (boolean) - Optional - Whether to skip code blocks. - **skip_images** (boolean) - Optional - Whether to skip images. - **image_description_prompt** (string) - Optional - Prompt for image description. ### Request Example ```json { "filePath": "/path/to/your/document.docx", "options": { "skip_images": false } } ``` ### Response #### Success Response (200) - **markdown** (string) - The converted Markdown content. - **warnings** (array) - A list of warnings encountered during conversion. - **code** (string) - The warning code. - **message** (string) - The warning message. #### Response Example ```json { "markdown": "# Converted Document\nThis is the converted content.", "warnings": [] } ``` ``` -------------------------------- ### Initialize AsyncGeminiDescriber Source: https://docs.rs/anytomd/1.2.1/src/anytomd/converter/gemini.rs.html Creates a new AsyncGeminiDescriber with a specified API key and default model. Use this for direct initialization. ```rust let describer = AsyncGeminiDescriber::new("test-key".to_string()); assert_eq!(describer.api_key, "test-key"); assert_eq!(describer.model, "gemini-3-flash-preview"); ``` -------------------------------- ### Handle picture start events Source: https://docs.rs/anytomd/1.2.1/src/anytomd/converter/pptx.rs.html Extracts image relationship IDs and alternative text descriptions from picture XML elements. ```rust fn handle_picture_start( local_str: &str, e: &quick_xml::events::BytesStart, current_blip_rel_id: &mut Option, current_image_alt: &mut Option, ) { match local_str { "blip" => { for attr in e.attributes().flatten() { let key = std::str::from_utf8(attr.key.as_ref()).unwrap_or(""); if key == "r:embed" || key.ends_with(":embed") { let val = String::from_utf8_lossy(&attr.value).to_string(); *current_blip_rel_id = Some(val); } } } "cNvPr" => { for attr in e.attributes().flatten() { let local_name = attr.key.local_name(); let key = std::str::from_utf8(local_name.as_ref()).unwrap_or(""); if key == "descr" { let val = String::from_utf8_lossy(&attr.value).to_string(); if !val.is_empty() { *current_image_alt = Some(val); } } } } _ => {} } } ``` -------------------------------- ### Create GeminiDescriber from Environment (With Key) Source: https://docs.rs/anytomd/1.2.1/src/anytomd/converter/gemini.rs.html Creates a GeminiDescriber by reading the API key from the environment variable `GEMINI_API_KEY`. ```rust with_env_key(|| { set_env_key("test-env-key"); let result = GeminiDescriber::from_env(); assert!(result.is_ok()); let describer = result.unwrap(); assert_eq!(describer.api_key, "test-env-key"); }); ``` -------------------------------- ### Get Type ID - Generic Implementation Source: https://docs.rs/anytomd/1.2.1/anytomd/converter/code/struct.CodeConverter.html Retrieves the `TypeId` of the implementing type. This is a standard method for types that implement the `Any` trait. ```rust fn type_id(&self) -> TypeId ``` -------------------------------- ### Create AsyncGeminiDescriber from Environment (With Key) Source: https://docs.rs/anytomd/1.2.1/src/anytomd/converter/gemini.rs.html Creates an AsyncGeminiDescriber using the GEMINI_API_KEY environment variable. This test confirms successful initialization when the key is present. ```rust #[cfg(not(target_arch = "wasm32"))] super::with_env_key(|| { super::set_env_key("test-async-env-key"); let result = AsyncGeminiDescriber::from_env(); assert!(result.is_ok()); let describer = result.unwrap(); assert_eq!(describer.api_key, "test-async-env-key"); }); ``` -------------------------------- ### Example: Using `set` to Modify Pinned Value Source: https://docs.rs/anytomd/1.2.1/anytomd/converter/type.AsyncDescribeFuture.html Illustrates how to use the `set` method to change the value of a pinned mutable reference. ```rust use std::pin::Pin; let mut val: u8 = 5; let mut pinned: Pin<&mut u8> = Pin::new(&mut val); println!("{}", pinned); // 5 pinned.set(10); println!("{}", pinned); // 10 ``` -------------------------------- ### pub fn as_ref(&self) -> Pin<&::Target> Source: https://docs.rs/anytomd/1.2.1/anytomd/converter/type.AsyncDescribeFuture.html Gets a shared reference to the pinned value this Pin points to. ```APIDOC ## GET as_ref ### Description Gets a shared reference to the pinned value this Pin points to. This is a safe operation because the contract of Pin::new_unchecked ensures the pointee cannot move. ### Method GET ### Parameters #### Path Parameters - **self** (Pin) - Required - The Pin instance to reference. ``` -------------------------------- ### Instantiate AsyncGeminiDescriber Source: https://docs.rs/anytomd/1.2.1/anytomd/converter/gemini/struct.AsyncGeminiDescriber.html Create a new AsyncGeminiDescriber using an API key or from the GEMINI_API_KEY environment variable. Ensure the `async-gemini` feature is enabled. ```rust use anytomd::gemini::AsyncGeminiDescriber; let describer = AsyncGeminiDescriber::new("your-api-key".to_string()); // or from the GEMINI_API_KEY environment variable: let describer = AsyncGeminiDescriber::from_env().unwrap(); ``` -------------------------------- ### Supported Extensions - CodeConverter Source: https://docs.rs/anytomd/1.2.1/anytomd/converter/code/struct.CodeConverter.html Returns a slice of string slices representing the file extensions that this converter can handle. For example, `["docx"]`. ```rust fn supported_extensions(&self) -> &[&str] ``` -------------------------------- ### Package Presentation Components Source: https://docs.rs/anytomd/1.2.1/src/anytomd/converter/pptx.rs.html Handles the ZIP packaging of slide relationships, notes, and presentation metadata into the final PPTX structure. ```rust r#""# )); } for (img_idx, _) in slide.images.iter().enumerate() { let img_rid = format!("rIdImg{}", img_idx + 1); slide_rels.push_str(&format!( r#""#, img_idx + 1 )); } slide_rels.push_str(""); zip.start_file(format!("ppt/slides/_rels/slide{slide_num}.xml.rels"), opts) .unwrap(); zip.write_all(slide_rels.as_bytes()).unwrap(); } // Build notes slide if present if let Some(notes_text) = slide.notes { let notes_xml = build_notes_xml(notes_text); zip.start_file(format!("ppt/notesSlides/notesSlide{slide_num}.xml"), opts) .unwrap(); zip.write_all(notes_xml.as_bytes()).unwrap(); } } pres_xml.push_str(""); pres_rels_xml.push_str(""); zip.start_file("ppt/presentation.xml", opts).unwrap(); zip.write_all(pres_xml.as_bytes()).unwrap(); zip.start_file("ppt/_rels/presentation.xml.rels", opts) .unwrap(); zip.write_all(pres_rels_xml.as_bytes()).unwrap(); let cursor = zip.finish().unwrap(); cursor.into_inner() } ``` -------------------------------- ### Parsing 'body' Element in Rust Source: https://docs.rs/anytomd/1.2.1/src/anytomd/converter/docx.rs.html Sets the 'in_body' flag to true when the 'body' element is encountered, indicating the start of the main document content. ```Rust "body" => { in_body = true; } ``` -------------------------------- ### Test extension handling Source: https://docs.rs/anytomd/1.2.1/src/anytomd/lib.rs.html Verifies that file extensions are handled correctly regardless of case or leading dots. ```rust #[test] fn test_convert_bytes_extension_case_insensitive() { let result = convert_bytes(b"hello world", " TXT ", &ConversionOptions::default()).unwrap(); assert!(result.markdown.contains("hello world")); } ``` ```rust #[test] fn test_convert_bytes_extension_with_leading_dot() { let result = convert_bytes(b"hello world", ".txt", &ConversionOptions::default()).unwrap(); assert!(result.markdown.contains("hello world")); } ``` -------------------------------- ### IpynbConverter Struct Definition Source: https://docs.rs/anytomd/1.2.1/anytomd/converter/ipynb/struct.IpynbConverter.html Defines the IpynbConverter struct, which is responsible for converting Jupyter Notebook files to Markdown. No specific setup or imports are shown here. ```rust pub struct IpynbConverter; ``` -------------------------------- ### AsyncGeminiDescriber Initialization Source: https://docs.rs/anytomd/1.2.1/anytomd/converter/gemini/struct.AsyncGeminiDescriber.html Methods to initialize the AsyncGeminiDescriber instance. ```APIDOC ## Initialization ### Description Methods to create a new instance of the AsyncGeminiDescriber. ### Methods - **new(api_key: String)**: Create a new instance with a provided API key. - **from_env()**: Create an instance by reading the GEMINI_API_KEY environment variable. - **with_model(model: String)**: Set a custom model name (default: gemini-3-flash-preview). ``` -------------------------------- ### Test XML output starts with code fence Source: https://docs.rs/anytomd/1.2.1/src/anytomd/converter/xml.rs.html Verifies that the markdown output for valid XML always begins with a ```xml code fence. ```rust let result = convert(b"").unwrap(); assert!(result.markdown.starts_with("```xml\n")); ``` -------------------------------- ### Test IPYNB Source as String Source: https://docs.rs/anytomd/1.2.1/src/anytomd/converter/ipynb.rs.html Demonstrates that the converter handles notebook cells where the 'source' is a single string instead of an array. ```rust #[test] fn test_ipynb_source_as_string() { // nbformat allows source as a single string (not array) let nb = serde_json::json!({ "nbformat": 4, "nbformat_minor": 2, "metadata": { "kernelspec": { "language": "python" } }, "cells": [{ "cell_type": "code", "metadata": {}, "source": "x = 42" }] }); let data = serde_json::to_vec(&nb).unwrap(); let result = IpynbConverter .convert(&data, &ConversionOptions::default()) .unwrap(); assert!(result.markdown.contains("x = 42")); } ``` -------------------------------- ### pub fn as_mut(&mut self) -> Pin<&mut ::Target> Source: https://docs.rs/anytomd/1.2.1/anytomd/converter/type.AsyncDescribeFuture.html Gets a mutable reference to the pinned value this Pin points to. ```APIDOC ## GET as_mut ### Description Gets a mutable reference to the pinned value this Pin points to. This is useful when performing multiple calls to functions that consume the pinning pointer. ### Method GET ### Parameters #### Path Parameters - **self** (Pin) - Required - The Pin instance to reborrow. ``` -------------------------------- ### Test JSON detection with UTF-8 BOM Source: https://docs.rs/anytomd/1.2.1/src/anytomd/lib.rs.html Verifies that the converter correctly handles files starting with a UTF-8 Byte Order Mark (BOM) when parsing JSON. ```rust #[cfg(not(target_arch = "wasm32"))] #[test] fn test_convert_file_unknown_ext_json_with_utf8_bom() { let dir = std::env::temp_dir().join("anytomd_test_json_bom_detect"); std::fs::create_dir_all(&dir).unwrap(); let file_path = dir.join("payload.dat"); let mut data = vec![0xEF, 0xBB, 0xBF]; data.extend_from_slice(br#"{"k":1}"#); std::fs::write(&file_path, data).unwrap(); let result = convert_file(&file_path, &ConversionOptions::default()).unwrap(); assert!( result.markdown.contains("\"k\""), "expected JSON conversion, markdown was: {}", result.markdown ); let _ = std::fs::remove_dir_all(&dir); } ``` -------------------------------- ### Build Test DOCX with Image Source: https://docs.rs/anytomd/1.2.1/src/anytomd/converter/docx.rs.html Helper function to create a ZIP-compressed DOCX structure containing a document, relationships, and an image file for testing purposes. ```rust fn build_test_docx_with_image( document_xml: &str, rels_xml: &str, image_path: &str, image_data: &[u8], ) -> Vec { use std::io::Write; use zip::ZipWriter; use zip::write::SimpleFileOptions; let buf = Vec::new(); let mut zip = ZipWriter::new(Cursor::new(buf)); let opts = SimpleFileOptions::default(); // [Content_Types].xml let ct = r#""#; zip.start_file("[Content_Types].xml", opts).unwrap(); zip.write_all(ct.as_bytes()).unwrap(); // _rels/.rels zip.start_file("_rels/.rels", opts).unwrap(); zip.write_all( br#""#, ) .unwrap(); // word/document.xml zip.start_file("word/document.xml", opts).unwrap(); zip.write_all(document_xml.as_bytes()).unwrap(); // word/_rels/document.xml.rels zip.start_file("word/_rels/document.xml.rels", opts) .unwrap(); zip.write_all(rels_xml.as_bytes()).unwrap(); // Image file zip.start_file(image_path, opts).unwrap(); zip.write_all(image_data).unwrap(); let cursor = zip.finish().unwrap(); cursor.into_inner() } ``` -------------------------------- ### Converter Trait Definition Source: https://docs.rs/anytomd/1.2.1/src/anytomd/converter/mod.rs.html Trait implemented by format-specific converters, defining methods to get supported extensions, check conversion capability, and perform the conversion to Markdown. ```rust pub trait Converter { /// Returns the file extensions this converter supports (e.g., `["docx"]`). fn supported_extensions(&self) -> &[&str]; /// Check if this converter can handle the given extension. fn can_convert(&self, extension: &str, _header_bytes: &[u8]) -> bool { self.supported_extensions().contains(&extension) } /// Convert file bytes to Markdown. fn convert( &self, data: &[u8], options: &ConversionOptions, ) -> Result; } ``` -------------------------------- ### Create AsyncGeminiDescriber from Environment (Missing Key) Source: https://docs.rs/anytomd/1.2.1/src/anytomd/converter/gemini.rs.html Attempts to create an AsyncGeminiDescriber using the GEMINI_API_KEY environment variable. This test verifies that an error is returned if the key is not set. ```rust #[cfg(not(target_arch = "wasm32"))] super::with_env_key(|| { super::remove_env_key(); let result = AsyncGeminiDescriber::from_env(); assert!(result.is_err()); let err = result.unwrap_err(); assert!( format!("{err}").contains("GEMINI_API_KEY"), "error was: {err}" ); }); ``` -------------------------------- ### AsyncConversionOptions Struct Source: https://docs.rs/anytomd/1.2.1/src/anytomd/converter/mod.rs.html Wraps base conversion options with an optional async image describer for concurrent LLM-based alt text generation. Requires the `async` feature. ```rust #[cfg(feature = "async")] #[derive(Default)] pub struct AsyncConversionOptions { /// Base conversion options (resource limits, extract_images, strict mode). pub base: ConversionOptions, /// Optional async image describer for concurrent LLM-based alt text generation. pub async_image_describer: Option>, } ``` -------------------------------- ### Test: Decode Text UTF-16 LE BOM Source: https://docs.rs/anytomd/1.2.1/src/anytomd/converter/mod.rs.html Unit test setup for decoding text with a UTF-16 Little Endian BOM, followed by the characters 'A' and 'B'. ```rust #[test] fn test_decode_text_utf16_le_bom() { // UTF-16 LE BOM + "AB" let input: Vec = vec![0xFF, 0xFE, b'A', 0x00, b'B', 0x00]; ``` -------------------------------- ### Implement CloneToUninit for T (Nightly) Source: https://docs.rs/anytomd/1.2.1/anytomd/converter/struct.ConversionResult.html Nightly-only experimental API for copying data to uninitialized memory. ```rust unsafe fn clone_to_uninit(&self, dest: *mut u8) ``` -------------------------------- ### Get Shared Reference to Pinned Value Source: https://docs.rs/anytomd/1.2.1/anytomd/converter/type.AsyncDescribeFuture.html Retrieves a shared reference (`Pin<&T>`) to the value pointed to by a `Pin`. This is safe due to the pinning contract ensuring the value does not move. ```rust pub fn as_ref(&self) -> Pin<&::Target> where Ptr: Deref, ``` -------------------------------- ### Create GeminiDescriber with API Key Source: https://docs.rs/anytomd/1.2.1/src/anytomd/converter/gemini.rs.html Instantiate `GeminiDescriber` with a provided API key. This is for native targets only. ```rust use anytomd::gemini::GeminiDescriber; let describer = GeminiDescriber::new("your-api-key".to_string()); ``` -------------------------------- ### Example: Calling Method Twice on Pinned Self Source: https://docs.rs/anytomd/1.2.1/anytomd/converter/type.AsyncDescribeFuture.html Demonstrates how to call a method that consumes `self` multiple times on a pinned mutable reference by repeatedly using `as_mut` to reborrow. ```rust use std::pin::Pin; impl Type { fn method(self: Pin<&mut Self>) { // do something } fn call_method_twice(mut self: Pin<&mut Self>) { // `method` consumes `self`, so reborrow the `Pin<&mut Self>` via `as_mut`. self.as_mut().method(); self.as_mut().method(); } } ``` -------------------------------- ### Build Test DOCX with Multiple Images Source: https://docs.rs/anytomd/1.2.1/src/anytomd/converter/docx.rs.html Helper function to create a ZIP-based DOCX structure containing multiple image files for testing purposes. ```rust fn build_test_docx_with_images( document_xml: &str, rels_xml: &str, images: &[(&str, &[u8])], // (zip_path, data) ) -> Vec { use std::io::Write; use zip::ZipWriter; use zip::write::SimpleFileOptions; let buf = Vec::new(); let mut zip = ZipWriter::new(Cursor::new(buf)); let opts = SimpleFileOptions::default(); ``` -------------------------------- ### Test Python Code Conversion Source: https://docs.rs/anytomd/1.2.1/src/anytomd/converter/code.rs.html Tests the `convert_with_extension` function for Python files. It asserts that the generated Markdown starts with the correct language tag and contains the expected code content. ```rust #[test] fn test_code_python_fenced_block() { let converter = CodeConverter; let input = b"def hello():\n print('Hello, world!')\n"; let result = converter .convert_with_extension(input, "py", &ConversionOptions::default()) .unwrap(); assert!(result.markdown.starts_with("```python\n")); assert!(result.markdown.ends_with("\n```\n")); assert!(result.markdown.contains("def hello():")); } ``` -------------------------------- ### Build XML for Notes Slide Source: https://docs.rs/anytomd/1.2.1/src/anytomd/converter/pptx.rs.html Constructs the XML structure for a presentation notes slide by splitting input text into paragraphs. ```rust fn build_notes_xml(text: &str) -> String { // Split text by newlines to create separate paragraphs let paragraphs: Vec<&str> = text.lines().collect(); let mut para_xml = String::new(); for p in ¶graphs { para_xml.push_str(&format!(r#"{p}"#)); } format!( r#"1{para_xml}"# ) } ``` -------------------------------- ### Get Nested Mutable Reference from Pin Source: https://docs.rs/anytomd/1.2.1/anytomd/converter/type.AsyncDescribeFuture.html Obtains a `Pin<&mut T>` from a nested `Pin<&mut Pin>`. This is safe because the outer `Pin` guarantees the inner pointee cannot move. ```rust pub fn as_deref_mut( self: Pin<&mut Pin>, ) -> Pin<&mut ::Target> where Ptr: DerefMut, ``` -------------------------------- ### Unwrap Pin to Get Underlying Pointer Source: https://docs.rs/anytomd/1.2.1/anytomd/converter/type.AsyncDescribeFuture.html Safely unwraps a `Pin` to return the original `Ptr`, consuming the `Pin`. This is an unsafe operation that should only be used when the pinning contract is fully upheld. ```rust pub const unsafe fn into_inner_unchecked(pin: Pin) -> Ptr ``` -------------------------------- ### Verify Plain Text Content and Syntax Source: https://docs.rs/anytomd/1.2.1/src/anytomd/converter/xlsx.rs.html Use these assertions to ensure the plain text output contains expected descriptions and lacks markdown image syntax. ```rust // plain_text should contain the description assert!( result.plain_text.contains("A bar chart"), "plain_text should contain the image description, was: {}", result.plain_text ); // plain_text should NOT contain image markdown syntax assert!( !result.plain_text.contains("!["), "plain_text should not contain ![ image syntax, was: {}", result.plain_text ); } } ``` -------------------------------- ### Converter Supported Extensions Method Source: https://docs.rs/anytomd/1.2.1/anytomd/converter/trait.Converter.html Returns the file extensions this converter supports (e.g., ["docx"]). Implement this to declare which file types your converter can handle. ```rust fn supported_extensions(&self) -> &[&str]; ``` -------------------------------- ### XML Event Processing Loop Source: https://docs.rs/anytomd/1.2.1/src/anytomd/converter/pptx.rs.html The loop processes XML events using a reader, maintaining state for shapes, graphic frames, and pictures to correctly route start, empty, and text events. ```rust let mut in_cell_text = false; // Image state let mut current_blip_rel_id: Option = None; let mut current_image_alt: Option = None; // Track depth for nested elements let mut shape_depth: u32 = 0; let mut graphic_frame_depth: u32 = 0; let mut picture_depth: u32 = 0; // Group shape depth: is a transparent container — child shapes // (sp, graphicFrame, pic) are processed normally. The counter tracks nesting // for proper End-tag matching but does not gate any logic. let mut group_depth: u32 = 0; loop { match reader.read_event() { Ok(Event::Start(ref e)) => { let local = e.local_name(); let local_str = std::str::from_utf8(local.as_ref()).unwrap_or(""); match local_str { "grpSp" if !in_shape && !in_graphic_frame && !in_picture => { group_depth += 1; } "sp" if !in_shape && !in_graphic_frame && !in_picture => { in_shape = true; shape_depth = 1; placeholder_type = None; shape_paragraphs.clear(); } "graphicFrame" if !in_shape && !in_graphic_frame && !in_picture => { in_graphic_frame = true; graphic_frame_depth = 1; } "pic" if !in_shape && !in_graphic_frame && !in_picture => { in_picture = true; picture_depth = 1; current_blip_rel_id = None; current_image_alt = None; } _ if in_shape => { shape_depth += 1; handle_shape_start( local_str, e, &mut placeholder_type, &mut in_text_body, &mut in_paragraph, &mut in_run, &mut in_text, &mut current_paragraph, ); } _ if in_graphic_frame => { graphic_frame_depth += 1; handle_graphic_frame_start( local_str, &mut in_table, &mut in_table_row, &mut in_table_cell, &mut in_cell_paragraph, &mut in_cell_run, &mut in_cell_text, &mut current_cell, &mut current_row, &mut table_rows, ); } _ if in_picture => { picture_depth += 1; handle_picture_start( local_str, e, &mut current_blip_rel_id, &mut current_image_alt, ); } _ => {} } } Ok(Event::Empty(ref e)) => { let local = e.local_name(); let local_str = std::str::from_utf8(local.as_ref()).unwrap_or(""); if in_shape { handle_shape_empty( local_str, e, &mut placeholder_type, in_run, &mut current_paragraph, ); } else if in_graphic_frame { handle_graphic_frame_empty(local_str, in_cell_run, &mut current_cell); } else if in_picture { handle_picture_start( local_str, e, &mut current_blip_rel_id, &mut current_image_alt, ); } } Ok(Event::Text(ref e)) => { if in_shape && in_text && in_run { let text = e.unescape().unwrap_or_default().to_string(); current_paragraph.push_str(&text); } else if in_graphic_frame && in_cell_text && in_cell_run { let text = e.unescape().unwrap_or_default().to_string(); current_cell.push_str(&text); ``` -------------------------------- ### Implement Debug and Default for ConversionOptions Source: https://docs.rs/anytomd/1.2.1/src/anytomd/converter/mod.rs.html Provides custom Debug formatting and default configuration values for conversion options. ```rust impl std::fmt::Debug for ConversionOptions { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("ConversionOptions") .field("extract_images", &self.extract_images) .field("max_total_image_bytes", &self.max_total_image_bytes) .field("strict", &self.strict) .field("max_input_bytes", &self.max_input_bytes) .field( "max_uncompressed_zip_bytes", &self.max_uncompressed_zip_bytes, ) .field( "image_describer", &self.image_describer.as_ref().map(|_| ".."), ) .finish() } } impl Default for ConversionOptions { fn default() -> Self { Self { extract_images: false, max_total_image_bytes: 50 * 1024 * 1024, // 50 MB strict: false, max_input_bytes: 100 * 1024 * 1024, // 100 MB max_uncompressed_zip_bytes: 500 * 1024 * 1024, // 500 MB image_describer: None, } } } ``` -------------------------------- ### Derive OOXML .rels Path Source: https://docs.rs/anytomd/1.2.1/src/anytomd/converter/ooxml_utils.rs.html Derives the corresponding .rels file path for a given file path within an OOXML package. For example, 'ppt/slides/slide1.xml' becomes 'ppt/slides/_rels/slide1.xml.rels'. This function is essential for locating relationship metadata. ```rust pub(crate) fn derive_rels_path(file_path: &str) -> String { if let Some(pos) = file_path.rfind('/') { let dir = &file_path[..pos]; let filename = &file_path[pos + 1..]; format!("{dir}/_rels/{filename}.rels") } else { format!( ``` -------------------------------- ### Build DOCX ZIP with Numbering for Testing Source: https://docs.rs/anytomd/1.2.1/src/anytomd/converter/docx.rs.html Extended helper function to build a DOCX ZIP archive in memory, including optional numbering XML alongside document, styles, and relationships XML. ```rust fn build_test_docx_with_numbering( document_xml: &str, styles_xml: Option<&str>, rels_xml: Option<&str>, numbering_xml: Option<&str>, ) -> Vec { use std::io::Write; use zip::ZipWriter; use zip::write::SimpleFileOptions; let buf = Vec::new(); let mut zip = ZipWriter::new(Cursor::new(buf)); let opts = SimpleFileOptions::default(); // [Content_Types].xml let mut ct = String::from(r#""#); ct.push_str( r#""#, ); ct.push_str( r#""#, ); ct.push_str(r#""#); ct.push_str( ``` -------------------------------- ### Get Mutable Reference to Pinned Value Source: https://docs.rs/anytomd/1.2.1/anytomd/converter/type.AsyncDescribeFuture.html Retrieves a mutable reference (`Pin<&mut T>`) to the value pointed to by a `Pin`. This is useful when calling multiple functions that consume the pinning pointer, reborrowing via `as_mut` each time. ```rust pub fn as_mut(&mut self) -> Pin<&mut ::Target> where Ptr: DerefMut, ```