diff --git a/processMDSpec.py b/processMDSpec.py
index f065f95d5a71f7d428886d4e8272095b02dce65e..9b3d93c85f5635c80917d8834bf6b0f15682d8ef 100644
--- a/processMDSpec.py
+++ b/processMDSpec.py
@@ -10,10 +10,13 @@
 """
 
 from __future__ import annotations
+
+_print = print	# save the original print function
+
 from typing import Tuple, Generator
 import argparse
-from rich import print, markdown
-import re, sys, yaml
+from rich import markdown, print
+import re, sys, yaml, os
 from contextlib import contextmanager
 
 
@@ -37,13 +40,79 @@ def includeStack(filename:str) -> Generator [None, None, None]:
 			Generator: A generator that yields nothing.
 	"""
 	if filename in _includeStack:
-		print(f'[red]Circular include detected: {filename}')
-		raise Exception('Circular include detected')
+		raise Exception(f'Circular include detected: {" -> ".join(_includeStack)} -> {filename}')
 	_includeStack.append(filename)
 	yield
 	_includeStack.pop()
 
 
+def expandPaths(lines:list[str], currentPath:str, childPath:str) -> list[str]:
+	"""	Expand the paths in the markdown file. This means that all paths in links,
+		images, and include statements are extended so that they would be valid paths
+		from the root document.
+
+		Args:
+			lines: The lines of the markdown file.
+			currentPath: The current path of the file being processed.
+			childPath: The path of the child file being processed.
+
+		Returns:
+			list[str]: The lines of the markdown file with expanded paths.
+	"""
+
+	# Replace all relative paths in the markdown with the new path
+	# add a path to the current path
+	if currentPath[-1] != '/':
+		currentPath += '/'
+	newPath = currentPath + childPath
+	# Remove the leading './' from the path
+	while newPath.startswith('./'):
+		newPath = newPath[2:]
+
+	inCodeFence = False
+	for index, line in enumerate(lines):
+
+		# Ignore stuff in code fences
+		if re.match(r'^\s*```.*', line):
+			inCodeFence = not inCodeFence
+			continue
+		if inCodeFence:
+			continue
+
+		# handle the links in a line (there could be multiple links in a line)
+		links = re.findall(r'\[([^\]]+)\]\(([^\)]+)\)', line)
+		for linkText, linkPath in links:
+			# Skip URLs and absolute paths
+			if linkPath.startswith(('http://', 'https://', '/')):
+				continue
+			
+			# Construct the new path by adding addedPath to the original path
+			newLinkPath = linkPath[2:] if linkPath.startswith('./') else linkPath
+			
+			# Create the updated path
+			updatedPath = f"{newPath}{linkPath}" if newPath.endswith('/') else f"{newPath}/{newLinkPath}"
+			
+			# Replace the original link with the updated one in the markdown
+			line = line.replace(f'[{linkText}]({linkPath})', f'[{linkText}]({updatedPath})')
+			
+		# handle the include statements (there should only be one per line)
+		includes = re.findall(r'^\s*::include{file=([^\}]+)}', line)
+		for includePath in includes:
+
+			# Construct the new path by adding addedPath to the original path
+			includePath = includePath[2:] if includePath.startswith('./') else includePath
+			
+			# Create the updated path
+			updatedPath = f'{newPath}{includePath}' if newPath.endswith('/') else f'{newPath}/{includePath}'
+			
+						# Replace the original include with the updated one in the markdown
+			line = line.replace(f'::include{{file={includePath}}}', f'::include{{file={updatedPath}}}')
+		
+		lines[index] = line
+
+	return lines
+
+
 def processFrontMatter(lines:list[str], args:argparse.Namespace) -> Tuple[dict, list[str]]:
 	"""	Process the front matter of a markdown file. This includes extracting
 		the front matter information and returning it as a dictionary.
@@ -97,7 +166,7 @@ def processFile(args:argparse.Namespace) -> str:
 			The processed markdown content as a string.
 	"""
 
-	def handleIncludesForFile(filename:str) -> str:
+	def handleIncludesForFile(filename:str, currentPath:str) -> str:
 		"""	Read a single markdown file and return its content.
 
 			Args:
@@ -109,6 +178,14 @@ def processFile(args:argparse.Namespace) -> str:
 			Returns:
 				The content of the file.
 		"""
+		# Get the directory path from the filename
+		dirname = os.path.dirname(filename)
+		if dirname and not dirname.endswith('/'):
+			dirname = dirname + '/'
+
+		dirname = dirname if dirname else '.'
+		currentPath = currentPath if currentPath else '.'
+		filename = os.path.normpath(filename)
 
 		with includeStack(filename):
 			try:
@@ -117,8 +194,11 @@ def processFile(args:argparse.Namespace) -> str:
 			except FileNotFoundError:
 				print(f'[red]File not found: {filename}')
 				raise
+
+			# Expand the paths in the markdown file
 		
 			# extract front matter information
+			lines = expandPaths(lines, currentPath, dirname)
 			fm, lines = processFrontMatter(lines, args)
 			if fm:
 				_frontMatter[filename] = fm
@@ -129,7 +209,7 @@ def processFile(args:argparse.Namespace) -> str:
 			inCodeFence = False
 			for line in lines:
 
-				# Ignore code fences
+				# Ignore stuff code fences
 				if re.match(r'^\s*```.*', line):
 					inCodeFence = not inCodeFence
 					continue
@@ -139,17 +219,15 @@ def processFile(args:argparse.Namespace) -> str:
 				# Check for ::include{file=...} pattern using regex at the beginning of a line
 				match = re.search(r'^::include\{\s*file=(.*?)\s*\}', line.strip())
 				if match:
-					include_filename = match.group(1)
+					includeFilename = match.group(1)
 					# Read the included file and replace the include statement with its content
-					include_content = handleIncludesForFile(include_filename)
-					lines[lines.index(line)] = include_content
-			
+					lines[lines.index(line)] = handleIncludesForFile(includeFilename, os.path.dirname(filename))
+
 			return ''.join(lines)
 
-	return handleIncludesForFile(args.document)
+	return handleIncludesForFile(args.document, os.path.dirname(args.document))
 	
 
-
 if __name__ == '__main__':
 
 	parser = argparse.ArgumentParser(description='Process markdown specification files.')
@@ -158,7 +236,7 @@ if __name__ == '__main__':
 	parser.add_argument('--process-frontmatter', '-fm', dest='outputFrontMatter', action='store_true',  help='output front matter only')
 	parser.add_argument('--frontmatter-only', '-fmo', dest='onlyFrontMatter', action='store_true',  help='output only front matter')
 	parser.add_argument('--verbose', '-v', action='store_true', help='print debug information to stderr.')
-	parser.add_argument('document', type = str, help = 'a markdown specification document to process')
+	parser.add_argument('document', type=str, help='a markdown specification document to process')
 	args = parser.parse_args()
 
 	if args.verbose:
@@ -170,7 +248,7 @@ if __name__ == '__main__':
 	try: 
 		lines = processFile(args)
 	except Exception as e:
-		print(f'[red]Error processing file: {e}', file=sys.stderr)
+		print(f'[red]Error while processing {args.document}\n{e}', file=sys.stderr)
 		quit(1)
 	
 	if args.outputFrontMatter or args.onlyFrontMatter:
@@ -192,7 +270,7 @@ if __name__ == '__main__':
 			print(markdown.Markdown(lines))
 		else:
 			# Print the raw markdown content
-			print(lines)
+			_print(lines)