Introduction
Many companies still rely on PDF documents to store important information about products, contracts, reports, regulatory processes, suppliers, and internal operations.
These documents are easy to share and read, but they are not always easy to process automatically. A PDF may contain selectable text, tables, scanned images, signatures, low-quality pages, fields split across sections, and information spread across multiple pages.
When a person reads this type of document, they can understand the context, find the relevant information, and fill out a spreadsheet or internal system. For software, this process is more difficult. The system needs to convert the visual and textual content of the document into structured data, with clear, consistent fields that are ready to use.
In this article, we will explore a practical approach to structured data extraction from PDFs using Python, Ollama, and open-source models. The example uses public documents from the FDA 510(k), a United States regulatory process related to medical devices.
The goal is to show how a company can combine text extraction, OCR, context retrieval, language models, and JSON validation to turn regulatory documents into organized data.
We will also discuss why local models can be an interesting alternative for reducing costs compared with relying exclusively on commercial APIs from providers such as OpenAI, Anthropic, or Google Gemini.
This article is written mainly for non-technical readers, but some sections include commands and scripts to show how the solution was implemented in an example repository.
What are the challenges in extracting data from PDF documents?
PDFs were designed to preserve the appearance of a document. They keep layouts, fonts, images, tables, headers, footers, and other visual elements. This is useful for reading, printing, and distributing official documents.
For automation, however, this same characteristic can create problems.
A PDF may contain native text. In this case, the text is stored inside the file and can be selected, copied, or extracted using programming libraries. This is the simplest scenario.
However, many documents arrive in other formats: scanned pages, images embedded inside the PDF, old copies, compressed files, low-resolution documents, or reports generated by systems that do not preserve a clean text structure.
In these cases, the system may see an entire page only as an image. The content is visible to a person, but it is not available as text to the software. To recover this information, it is usually necessary to use OCR, which stands for Optical Character Recognition.
OCR is the technique used to identify letters, numbers, and words inside images. It can work well with clear documents, but it tends to fail when the image is skewed, blurred, compressed, affected by shadows, low resolution, or complex tables.
In addition to visual quality, there are other common challenges:
- important information may appear in different places depending on the document;
- fields may be spread across multiple pages;
- tables can be difficult to interpret;
- headers and footers can add noise;
- field names may vary across documents;
- long documents may exceed the context limit of language models;
- models may return incomplete or invalid responses;
- critical data needs evidence and human review.
In business processes, these problems have a direct operational impact. The larger the document volume, the more time teams spend reading, checking, typing, reworking, and validating information manually.
In healthcare, this challenge frequently appears in regulatory documents, technical materials, reports, product records, manuals, supplier documents, and compliance materials. Even when these documents follow some level of standardization, automated extraction still needs to handle variations in format and quality.
From unstructured documents to structured data
Many documents used by companies contain unstructured or semi-structured data.
Unstructured data appears in free-form content, such as paragraphs, technical descriptions, notes, justifications, or narrative sections. Semi-structured data has some visual organization, such as sections, tables, fields, and bullet points, but it is still not ready to be used directly by a system.
An internal system usually needs structured data. Instead of receiving an entire PDF, it needs specific fields.
For example, in a regulatory document for a medical device, it may be useful to extract:
- submission number;
- device name;
- responsible company;
- decision date;
- product code;
- regulation number;
- regulatory class;
- indications for use;
- technical description;
- predicate devices;
- evidence found in the document.
These fields can be stored in a database, displayed in a dashboard, sent to an approval workflow, used in an internal search system, or integrated with other applications.
A simplified example of structured output in JSON, which stands for JavaScript Object Notation, would be:
{
"document_type": "FDA 510(k) summary",
"submission_number": "K220646",
"device_name": "Play & Joy InvisiLube Lubricant Capsule",
"applicant": "InnoveMed Bio-tech Co., Ltd.",
"decision_date": "2023-07-10",
"product_code": "NUC",
"regulation_number": "21 CFR 884.5300",
"regulatory_class": "Class II"
}
JSON is a data format widely used by APIs, web applications, and business systems. It organizes data into key-value pairs, making it easier to exchange information between systems.
Structured PDF extraction usually follows a logical sequence:
- retrieve the document;
- identify whether it contains native text;
- apply OCR when necessary;
- split the content into smaller parts;
- locate relevant excerpts;
- send those excerpts to a language model;
- request structured output;
- validate the result;
- record evidence and diagnostics.
This approach avoids treating the artificial intelligence model as a standalone solution. The model is part of a larger workflow that includes document preparation, context retrieval, output control, and error handling.
For readers who are new to the topic, some concepts such as artificial intelligence, language models, tokens, and prompts were explained in more detail in the article Artificial Intelligence: Understanding the Main Concepts.
Example: extracting structured data from the FDA 510(k) dataset
To make the example closer to a real-world scenario, this article uses public documents from the FDA 510(k).
The FDA, or Food and Drug Administration, is the United States regulatory agency responsible for regulating, among other areas, medical devices. A medical device is an instrument, apparatus, software, implant, or similar article, including its accessories, that does not achieve its primary intended purpose through chemical action in the body in the same way a drug does.
The 510(k) is a premarket submission used by manufacturers to demonstrate that a medical device is substantially equivalent to another legally marketed device. This reference device is called a predicate device.
In practical terms, the company presents information about the device, its intended use, technological characteristics, manufacturer, regulatory classification, and comparison with similar devices. The FDA evaluates this information to determine whether the device can be marketed in the United States under that regulatory pathway.
This dataset is interesting for a proof of concept for three reasons.
First, it uses real and public healthcare documents, without relying on medical records, private clinical data, or sensitive patient information.
Second, the documents contain recurring fields, which makes it possible to define an extraction schema. A schema is the expected structure of the final output, such as a list of required and optional fields.
Third, the PDFs still represent common document automation challenges. Even in a regulatory domain, documents may contain narrative sections, tables, technical fields, and layout variations.
The repository used as the basis for this article is available at: https://github.com/evgomes/pdf-data-extraction-ollama
It implements a Python pipeline to download FDA 510(k) documents, prepare the files, extract text, apply OCR when necessary, retrieve relevant excerpts, and generate structured JSON using Ollama and an open-source model.
The initial example uses the K220646 record, related to the Play & Joy InvisiLube Lubricant Capsule device. The pipeline attempts to extract fields such as submission number, device name, applicant company, decision date, product code, regulation number, regulatory class, indications for use, technical description, and predicate devices.
Why use Ollama and open-source models?
Ollama is a tool that makes it easier to run language models locally. Instead of relying only on an external API, a company can download compatible models and run them on a local machine, internal server, or controlled environment.
A language model is an artificial intelligence system trained to interpret and generate text. In the context of this article, it receives relevant excerpts from the PDF and returns organized data in JSON format.
The key point is that inference can happen locally.
Inference is the moment when the model receives an input and generates a response. When a company uses a commercial API, it usually pays based on usage, such as tokens, requests, or processing volume. In projects with many documents, this variable cost can grow quickly.
With a local model, the cost profile changes. There are still hardware, electricity, setup, maintenance, and technical team costs, but the incremental cost per document can be lower when the volume is high or recurring.
In addition to cost, there are other potential benefits:
- more control over where data is processed;
- less dependence on external providers;
- the ability to keep fixed versions of the model and pipeline;
- adaptation of the workflow to specific document types;
- use of internal infrastructure in scenarios with security restrictions.
This does not mean open-source models are always better. Advanced commercial models from OpenAI, Anthropic, and Google may provide better results in complex tasks, difficult documents, longer reasoning, and advanced multimodal interpretation.
The most realistic decision is usually both technical and economic. For repetitive, well-defined, high-volume tasks, a local model may offer a good balance between quality and cost. For more difficult documents or exception cases, commercial models can be used as a fallback or as a premium processing layer.
This hybrid design often makes sense in companies: local models process most of the volume, while more expensive models are reserved for cases where the additional quality justifies the cost.
For the example code, I chose to use the qwen3.5:4b model.
Why choose qwen3.5:4b in this example?
The repository uses the qwen3.5:4b model as a baseline.
Baseline means starting point. It does not mean that the model is necessarily the best option for every scenario. It serves as an initial reference to test the pipeline, measure quality, identify limitations, and compare alternatives.
The choice makes sense for a proof of concept because qwen3.5:4b is a relatively lightweight model within the Qwen 3.5 family. On the Ollama page, it appears with approximately 4.66 billion parameters, Q4_K_M quantization, and a size of about 3.4 GB.
Some terms are worth explaining.
Parameters are internal values learned by the model during training. In general, larger models tend to have more capacity, but they also require more memory, processing power, and infrastructure.
Quantization is a technique used to reduce the size and memory consumption of a model. It makes it possible to run models on more accessible hardware, usually with some trade-off between efficiency and quality.
Q4_K_M is a common quantization format used in local models. For a non-technical reader, the main point is simple: the model becomes smaller and more practical to run locally.
A 4B model, or a model with around four billion parameters, should not be treated as equivalent to frontier commercial models. It can fail with complex documents, poor-quality images, difficult tables, or ambiguous instructions. Even so, it can be useful in well-defined tasks when it receives selected context, objective instructions, and validation around it.
This is the idea behind the project: instead of expecting a small model to solve everything by itself, the pipeline prepares the document, selects the relevant excerpts, and asks for objective JSON output.
With better hardware, other alternatives can be evaluated. Larger models from the Qwen family, larger versions of Llama, Gemma, Mistral, or other models compatible with Ollama may improve quality, robustness, and interpretation capacity. The ideal choice depends on tests with real documents, measuring accuracy, response time, memory usage, operational cost, and failure rate.
In summary, the idea here is to use a model that does not require powerful hardware, but is efficient enough to process the documents we need without creating additional costs beyond electricity and the hardware itself.
Repository structure
The project was organized in a simple way to make it easier to understand and adapt.
The main structure is:
.
+-- data/
| +-- sources.json
+-- schemas/
| +-- fda_510k.schema.json
+-- scripts/
| +-- download_dataset.py
| +-- find_fda_510k_samples.py
| +-- prepare_pdf.py
| +-- extract_510k.py
+-- src/
| +-- pdf_data_extraction_demo/
| +-- json_tools.py
| +-- ollama_client.py
| +-- pdf_processing.py
| +-- rag.py
+-- requirements.txt
+-- README.md
The data/sources.json file works as a dataset manifest. It tells the system which documents should be downloaded, where they come from, and which local file name should be used.
The download_dataset.py script downloads the PDFs defined in this manifest.
The prepare_pdf.py script prepares the PDF for processing. It extracts native text, renders pages as images, and, when requested, creates degraded versions to simulate documents with poor visual quality.
The extract_510k.py script runs the main extraction process. It builds the RAG workflow, calls the model through Ollama, attempts to generate structured JSON, and records diagnostics.
The ollama_client.py module centralizes communication with Ollama.
The pdf_processing.py module contains functions related to PDF processing, such as text extraction and page rendering.
The rag.py module implements text splitting into chunks and retrieval of relevant excerpts using BM25.
The json_tools.py module helps extract, repair, and validate JSON responses returned by the model.
This separation is important because it makes the project easier to evolve. In a real application, the same architecture could be adapted to other healthcare documents, such as technical materials, supplier records, compliance documents, internal reports, or device manuals.
How to prepare the environment
The project uses Python and Ollama.
Python is a programming language widely used in automation, data, and artificial intelligence projects.
First, clone the repository to a directory on your computer:
git clone https://github.com/evgomes/pdf-data-extraction-ollama.git
cd pdf-data-extraction-ollama
Then, create a virtual environment:
python -m venv .venv
A virtual environment is an isolated folder with the libraries used by the project. This avoids conflicts with other applications installed on the same computer.
On Windows PowerShell, activate the environment with:
.\.venv\Scripts\Activate.ps1
On Windows Command Prompt:
.\.venv\Scripts\activate.bat
On macOS or Linux:
source .venv/bin/activate
Then install the dependencies:
pip install -r requirements.txt
With Ollama installed and running, download the model used in the example:
ollama pull qwen3.5:4b
This step downloads the model to the local machine. From that point on, the pipeline can call it through Ollama’s local API.
Downloading the example dataset
After the environment is ready, the first step is to download the public PDF used in the example:
python scripts/download_dataset.py
The expected output is similar to:
downloading: https://www.accessdata.fda.gov/cdrh_docs/pdf22/K220646.pdf
saved: .../data/raw/fda_k220646.pdf
If the file already exists, the script informs you that it has already been downloaded:
exists: .../data/raw/fda_k220646.pdf
This step may seem simple, but it is relevant in a real project. Document ingestion needs to be reproducible. The system should know where each file came from, which document was downloaded, where it was saved, and how it will be identified in the following steps.
In a company, the same logic could be adapted to fetch documents from an internal portal, document management system, cloud storage bucket, email, supplier API, or upload workflow.
Preparing files for processing
After downloading the PDF, it needs to be prepared:
python scripts/prepare_pdf.py data/raw/fda_k220646.pdf --degrade
The expected output is:
pages: 7
native text chars: 13425
output: .../data/processed/fda_k220646
This step generates artifacts such as:
data/processed/fda_k220646/pages.jsonl
data/processed/fda_k220646/pages/page_001.png
data/processed/fda_k220646/degraded_pages/page_001.png
The pages.jsonl file stores the text extracted by page. JSONL, or JSON Lines, is a variation of JSON where each line represents an independent record. This format is useful for processing large volumes of documents incrementally.
The images in pages/ represent the rendered PDF pages. They are useful for visual tests, OCR, and comparison between native text and image-based content.
The images in degraded_pages/ simulate lower-quality documents. This helps test situations that are closer to real life, such as old scans, compressed PDFs, files sent by third parties, or pages with low sharpness.
This step is important because the pipeline needs to handle two scenarios: documents with native text and documents that depend on visual interpretation.
When native text is available, the process is usually faster, cheaper, and more reliable. When the document is scanned or degraded, OCR may be necessary, but it tends to require more processing and validation.
This point also connects to the topic of computer vision, an area of artificial intelligence focused on analyzing images and videos. In this article, however, the visual focus is on document pages, not scenes from the physical world.
Running the extraction
The simplest path is to run extraction in text mode:
python scripts/extract_510k.py data/raw/fda_k220646.pdf --model qwen3.5:4b --mode text --output outputs/fda_k220646_text.json
The expected output is:
wrote: outputs/fda_k220646_text.json
In this mode, the pipeline uses the native text extracted from the PDF. This is usually the best scenario when the document contains selectable text.
The result is a JSON file with the extracted data and diagnostic information. A simplified example of the main fields would be:
{
"document_type": "FDA 510(k) summary",
"submission_number": "K220646",
"device_name": "Play & Joy InvisiLube Lubricant Capsule",
"applicant": "InnoveMed Bio-tech Co., Ltd.",
"decision_date": "2023-07-10",
"product_code": "NUC",
"regulation_number": "21 CFR 884.5300",
"regulatory_class": "Class II"
}
In addition to the extracted data, the output file may include information such as:
selected_chunks: excerpts selected and sent to the model;diagnostics: diagnostic messages;parse_error: an error interpreting the response, if any;raw_model_response: the original response returned by the model;data: the final object with structured data.
This diagnostic information is important for auditing and continuous improvement. In document automation, it is not enough to know that a JSON file was generated. It is also necessary to know which excerpts the model relied on, which fields were uncertain, whether there was a parsing error, and which documents require human review.
How RAG fits into the process
RAG stands for Retrieval-Augmented Generation.
The idea is simple: before asking the model for an answer, the system searches for the most relevant excerpts in the document. This gives the model a smaller, more focused, and more useful context.
In the project, the PDF text is split into chunks. Then, BM25 is used to retrieve the excerpts that are closest to the extraction queries.
BM25 is a classic text search algorithm. It helps find parts of the document that are more related to important terms.
The queries used in the project look like this:
EXTRACTION_QUERIES = [
"510(k) Number Device Name Applicant Decision Date Product Code Regulation Number Regulatory Class",
"Indications for Use Describe prescription over the counter use",
"Predicate Device Information device name 510(k) number manufacturer",
"Device Description technological characteristics specifications table performance testing",
]
These queries search for information such as:
- 510(k) number, device name, applicant, decision date, product code, regulation number, and regulatory class;
- indications for use, usage description, prescription use, or over-the-counter use;
- predicate device information, including device name, 510(k) number, and manufacturer;
- device description, technological characteristics, specifications, and performance testing.
This workflow reduces noise. Instead of sending the entire PDF to the model, the system sends the excerpts most likely to contain the desired fields.
It also improves cost-effectiveness. The smaller and more relevant the context sent to the model, the lower the computational cost tends to be, and the more predictable the response tends to become.
Testing PDFs with low image quality
The project also allows testing a hybrid path, combining native text and visual OCR:
python scripts/extract_510k.py data/raw/fda_k220646.pdf --model qwen3.5:4b --mode hybrid --degrade-images --vision-ocr --max-ocr-pages 4 --max-image-pages 0 --ollama-timeout 30 --output outputs/fda_k220646_bad_image.json
The expected output is similar to:
vision OCR page 1: page_001.png
vision OCR page 2: page_002.png
vision OCR page 3: page_003.png
vision OCR page 4: page_004.png
wrote: outputs/fda_k220646_bad_image.json
In this mode, the system attempts to transcribe the visible text in the page images. The OCR prompt is direct:
Transcribe all visible text from this PDF page. Preserve section headings, table rows, numbers, units, and field labels. If text is unreadable, mark it as [unreadable]. Return plain text only.
This step has a specific goal: transcribe the visible content. It should not summarize, interpret, or fill in gaps. Interpretation happens later, during the structured extraction step.
Separating these responsibilities helps reduce errors. When the same prompt tries to transcribe, interpret, summarize, and structure at the same time, the model is more likely to omit details or invent information.
The visual path is more demanding than the text path. It may take more time, require more memory, and fail on modest hardware. For this reason, the pipeline records diagnostics and can continue using native text when available.
In a real solution, this type of fallback is essential. Business documents rarely arrive with the same quality standard.
The final extraction prompt
In the main step, the model receives relevant excerpts from the document and an instruction to return valid JSON.
The system instruction used in the project is short:
You extract regulated-document data. Return valid JSON only. Do not invent missing values.
This instruction defines three important rules.
The first is the model’s role: extracting data from regulated documents.
The second is the output format: valid JSON.
The third is a rule against hallucination: the model should not invent values when the information is not present in the context.
The main prompt also defines the expected fields, such as:
submission_number: submission number;device_name: device name;applicant: applicant or responsible company;decision_date: decision date;product_code: product code;regulation_number: regulation number;regulatory_class: regulatory class;indications_for_use: indications for use;predicate_devices: predicate devices;device_description: device description;performance_or_specifications: performance or specifications;evidence: evidence used to justify the extraction;warnings: warnings or points of attention.
The evidence field deserves attention. In a business process, it is important to know where each piece of information came from. If the model extracts a date, code, or regulatory class, the system should preserve evidence to support human review and auditing.
This is even more important in healthcare, where decisions may involve regulatory risk, safety, compliance, and technical responsibility.
What happens when the model fails
Language models can fail.
They may take too long, return an empty response, generate invalid JSON, ignore an instruction, confuse fields, or struggle with low-quality documents.
For that reason, the pipeline does not assume that the model response will always be correct. It accounts for situations such as:
- timeout during the model call;
- empty response;
- malformed JSON;
- context that is too large;
- visual OCR failure;
- response outside the expected format;
- need for JSON repair;
- missing or uncertain fields.
This brings the example closer to a real architecture. In a real solution, we need to make the pipeline resilient, avoiding interruptions when a single document fails or when an intermittent issue occurs. Artificial intelligence models are not deterministic, so solutions need to be prepared for failure scenarios and retries when necessary.
Applications in healthcare
In healthcare, many processes depend on long, technical, and regulatory documents. This includes medical device documents, technical materials, supplier records, protocols, reports, manuals, certificates, submissions, and compliance documents.
A pipeline like the one demonstrated in this article can help with tasks such as:
- organizing metadata from regulatory documents;
- extracting technical fields from product documents;
- creating searchable databases from PDFs;
- supporting supplier review;
- comparing similar documents;
- identifying missing fields;
- feeding internal compliance systems;
- accelerating document triage before human review;
- preserving evidence for auditing.
In the FDA 510(k) example, the goal is to extract data from public documents about medical devices. In a healthcare company, the same logic could be adapted to other types of documents, as long as security, privacy, and governance are properly addressed.
This point is important. Healthcare documents may contain sensitive data. In Brazil, the LGPD, or General Data Protection Law, requires special attention when processing personal data and health-related data. In other countries, there are specific rules, such as HIPAA, the Health Insurance Portability and Accountability Act, in the United States for certain contexts.
Running models locally can help control where data is processed, but it does not remove the need for security policies. It is necessary to define access permissions, record logs, protect files, classify documents, limit the use of sensitive data, and keep human review in critical decisions.
The application can also be adapted to other business areas that depend on PDFs, such as legal, finance, insurance, manufacturing, and procurement. The operational pattern is similar: static documents are converted into structured data that can feed systems, indicators, internal searches, and workflows.
Operational and financial impact
Structured PDF extraction can generate practical gains for companies that handle large volumes of documents.
The first gain is reduced manual work. When a system can pre-fill fields, locate relevant information, and point to evidence, the team can review instead of typing everything from scratch.
The second gain is speed. Documents that previously had to be read one by one can go through an initial automated triage.
The third gain is standardization. Fields extracted into JSON can follow a common structure, making it easier to compare different documents.
The fourth gain is traceability. By preserving evidence and diagnostics, the process becomes easier to audit.
The fifth gain is cost. With commercial APIs, cost grows with processing volume. With local models, cost is more closely tied to infrastructure. For recurring volumes, this can make processing more predictable.
Even so, the decision between local models and commercial models should be based on metrics. Important criteria include:
- extraction quality;
- cost per document;
- response time;
- memory usage;
- failure rate;
- maintenance effort;
- document complexity;
- data sensitivity;
- audit requirements;
- availability of technical staff.
In many cases, the best strategy may be hybrid. Local models process simpler and recurring documents. More advanced commercial models are used for difficult documents, exception cases, or higher-value validations.
Limitations and precautions
Despite good results in a controlled example, there are important limitations.
Scanned documents may require more robust OCR. Complex tables may need specific handling. Long PDFs may require better chunking and retrieval strategies. Critical fields may need deterministic validation. Sensitive data requires governance, security, and access control.
It is also important to test the pipeline with real documents from the target domain. A model that works well with FDA 510(k) documents may not perform the same way with reports, tender documents, contracts, technical manuals, or internal reports.
In a real solution, it would be recommended to add:
- prompt versioning;
- automated tests with known documents;
- model comparison;
- field-level validation;
- audit trail;
- human review for sensitive fields;
- accuracy metrics;
- document sensitivity classification;
- permission control;
- monitoring for failures and costs;
- fallback to stronger models when necessary.
If you work in a scenario where you need to automate data extraction from PDF documents and would like to evaluate options similar to this one, you can contact us. We can help.
Conclusion
PDFs remain an important barrier to business process automation. They contain useful information, but often in formats that are difficult to integrate with systems.
The example with public FDA 510(k) documents, Python, Ollama, and qwen3.5:4b shows a practical approach to transforming PDFs into structured data. The pipeline combines text extraction, page rendering, optional OCR, RAG, BM25, direct prompts, structured JSON, diagnostics, and failure handling.
This combination is more robust than simply sending an entire document to a model and expecting a perfect response. The value is in the full workflow: preparing the document, selecting the right excerpts, extracting relevant fields, validating the output, recording evidence, and enabling review.
For healthcare companies, this type of solution can support regulatory document analysis, supplier review, document database organization, and compliance processes. For other areas, the same pattern can be adapted whenever there is a relevant volume of PDFs and a need to turn manual reading into operational data.
Open-source models running locally do not replace commercial models in every scenario, but they can be a viable alternative for well-defined tasks, especially when cost, volume, privacy, and operational control are important factors.
Sources
- FDA. Premarket Notification 510(k). Available at: https://www.fda.gov/medical-devices/premarket-submissions-selecting-and-preparing-correct-submission/premarket-notification-510k. Accessed on: June 15, 2026.
- FDA. The 510(k) Program: Evaluating Substantial Equivalence in Premarket Notifications [510(k)]. Available at: https://www.fda.gov/regulatory-information/search-fda-guidance-documents/510k-program-evaluating-substantial-equivalence-premarket-notifications-510k. Accessed on: June 15, 2026.
- FDA AccessData. 510(k) Premarket Notification Database. Available at: https://www.accessdata.fda.gov/scripts/cdrh/cfdocs/cfpmn/pmn.cfm. Accessed on: June 15, 2026.
- FDA AccessData. 510(k) record K220646. Available at: https://www.accessdata.fda.gov/scripts/cdrh/cfdocs/cfpmn/pmn.cfm?ID=K220646. Accessed on: June 15, 2026.
- FDA AccessData. Public PDF for record K220646. Available at: https://www.accessdata.fda.gov/cdrh_docs/pdf22/K220646.pdf. Accessed on: June 15, 2026.
- Python Software Foundation. Python. Available at: https://www.python.org/. Accessed on: June 15, 2026.
- Ollama. Ollama. Available at: https://ollama.com/. Accessed on: June 15, 2026.
- Ollama Docs. API Introduction. Available at: https://docs.ollama.com/api/introduction. Accessed on: June 15, 2026.
- Ollama. qwen3.5:4b. Available at: https://ollama.com/library/qwen3.5:4b. Accessed on: June 15, 2026.
- Ollama. Qwen 3.5. Available at: https://ollama.com/library/qwen3.5. Accessed on: June 15, 2026.
- GitHub. evgomes/pdf-data-extraction-ollama. Available at: https://github.com/evgomes/pdf-data-extraction-ollama. Accessed on: June 15, 2026.