Application: Autodesk Platform Services (formerly Forge)
Category: File Conversion
Difficulty: Intermediate
Language: Python
You need to convert AutoCAD DWG files to PDF format programmatically, either as part of a web application or batch processing workflow, without requiring AutoCAD to be installed.
Use the Autodesk Platform Services Model Derivative API to convert DWG files to PDF. This cloud-based solution handles the conversion on Autodesk's servers, supporting all AutoCAD versions and producing high-quality PDFs.
pip install requests
import requests
import base64
import time
import json
class APSConverter:
def __init__(self, client_id, client_secret):
self.client_id = client_id
self.client_secret = client_secret
self.access_token = None
self.base_url = "https://developer.api.autodesk.com"
def authenticate(self):
"""Get 2-legged OAuth token"""
url = "https://developer.api.autodesk.com/authentication/v2/token"
headers = {
"Content-Type": "application/x-www-form-urlencoded"
}
data = {
"client_id": self.client_id,
"client_secret": self.client_secret,
"grant_type": "client_credentials",
"scope": "data:read data:write data:create bucket:create bucket:read"
}
response = requests.post(url, headers=headers, data=data)
response.raise_for_status()
self.access_token = response.json()["access_token"]
print("✓ Authentication successful")
def upload_file(self, file_path, bucket_key="my-bucket"):
"""Upload DWG file to APS"""
# Create bucket if it doesn't exist
url = f"{self.base_url}/oss/v2/buckets"
headers = {
"Authorization": f"Bearer {self.access_token}",
"Content-Type": "application/json"
}
bucket_data = {
"bucketKey": bucket_key,
"policyKey": "transient" # Files deleted after 24 hours
}
requests.post(url, headers=headers, json=bucket_data)
# Upload file
object_name = file_path.split("/")[-1]
url = f"{self.base_url}/oss/v2/buckets/{bucket_key}/objects/{object_name}"
headers = {
"Authorization": f"Bearer {self.access_token}",
"Content-Type": "application/octet-stream"
}
with open(file_path, "rb") as f:
response = requests.put(url, headers=headers, data=f)
response.raise_for_status()
object_id = response.json()["objectId"]
print(f"✓ File uploaded: {object_name}")
return object_id
def convert_to_pdf(self, object_id):
"""Convert DWG to PDF using Model Derivative API"""
# Encode object ID to Base64 (URL-safe)
urn = base64.urlsafe_b64encode(object_id.encode()).decode()
url = f"{self.base_url}/modelderivative/v2/designdata/job"
headers = {
"Authorization": f"Bearer {self.access_token}",
"Content-Type": "application/json"
}
job_payload = {
"input": {
"urn": urn
},
"output": {
"formats": [
{
"type": "pdf",
"advanced": {
"paperSize": "ISO A4" # or "ANSI A", "ISO A3", etc.
}
}
]
}
}
response = requests.post(url, headers=headers, json=job_payload)
response.raise_for_status()
print("✓ Conversion job started")
return urn
def check_status(self, urn):
"""Check conversion status"""
url = f"{self.base_url}/modelderivative/v2/designdata/{urn}/manifest"
headers = {
"Authorization": f"Bearer {self.access_token}"
}
while True:
response = requests.get(url, headers=headers)
response.raise_for_status()
manifest = response.json()
status = manifest["status"]
if status == "success":
print("✓ Conversion completed successfully")
return manifest
elif status == "failed":
print("✗ Conversion failed")
print(json.dumps(manifest, indent=2))
return None
else:
print(f" Status: {status} - waiting...")
time.sleep(5)
def download_pdf(self, urn, output_path):
"""Download converted PDF"""
# Get manifest to find PDF derivative
manifest_url = f"{self.base_url}/modelderivative/v2/designdata/{urn}/manifest"
headers = {"Authorization": f"Bearer {self.access_token}"}
response = requests.get(manifest_url, headers=headers)
manifest = response.json()
# Find PDF derivative
pdf_urn = None
for derivative in manifest["derivatives"]:
if derivative["outputType"] == "pdf":
pdf_urn = derivative["children"][0]["urn"]
break
if not pdf_urn:
print("✗ PDF derivative not found")
return False
# Download PDF
download_url = f"{self.base_url}/modelderivative/v2/designdata/{urn}/manifest/{pdf_urn}"
response = requests.get(download_url, headers=headers)
response.raise_for_status()
with open(output_path, "wb") as f:
f.write(response.content)
print(f"✓ PDF downloaded: {output_path}")
return True
# Usage example
def convert_dwg_to_pdf(dwg_path, pdf_path):
# Initialize converter
converter = APSConverter(
client_id="YOUR_CLIENT_ID",
client_secret="YOUR_CLIENT_SECRET"
)
# Authenticate
converter.authenticate()
# Upload DWG
object_id = converter.upload_file(dwg_path)
# Start conversion
urn = converter.convert_to_pdf(object_id)
# Wait for completion
manifest = converter.check_status(urn)
if manifest:
# Download PDF
converter.download_pdf(urn, pdf_path)
return True
return False
# Run conversion
if __name__ == "__main__":
success = convert_dwg_to_pdf(
dwg_path="/path/to/drawing.dwg",
pdf_path="/path/to/output.pdf"
)
if success:
print("\n✓ Conversion completed successfully!")
else:
print("\n✗ Conversion failed")
import os
from concurrent.futures import ThreadPoolExecutor
def batch_convert_dwg_to_pdf(input_folder, output_folder, max_workers=5):
"""Convert multiple DWG files to PDF in parallel"""
converter = APSConverter(
client_id="YOUR_CLIENT_ID",
client_secret="YOUR_CLIENT_SECRET"
)
converter.authenticate()
# Get all DWG files
dwg_files = [f for f in os.listdir(input_folder) if f.lower().endswith('.dwg')]
print(f"Found {len(dwg_files)} DWG files to convert\n")
def convert_single(dwg_file):
try:
dwg_path = os.path.join(input_folder, dwg_file)
pdf_path = os.path.join(output_folder, dwg_file.replace('.dwg', '.pdf'))
print(f"Converting: {dwg_file}")
object_id = converter.upload_file(dwg_path)
urn = converter.convert_to_pdf(object_id)
manifest = converter.check_status(urn)
if manifest:
converter.download_pdf(urn, pdf_path)
return (dwg_file, True)
else:
return (dwg_file, False)
except Exception as e:
print(f"Error converting {dwg_file}: {str(e)}")
return (dwg_file, False)
# Process files in parallel
with ThreadPoolExecutor(max_workers=max_workers) as executor:
results = list(executor.map(convert_single, dwg_files))
# Summary
successful = sum(1 for _, success in results if success)
print(f"\n{'='*50}")
print(f"Conversion complete: {successful}/{len(dwg_files)} successful")
print(f"{'='*50}")
Mistake: Not encoding URN to Base64
# WRONG - URN must be Base64 encoded
urn = object_id
# CORRECT
urn = base64.urlsafe_b64encode(object_id.encode()).decode()
Mistake: Not waiting for conversion to complete
# WRONG - trying to download immediately
converter.convert_to_pdf(object_id)
converter.download_pdf(urn, output_path) # Will fail!
# CORRECT - wait for completion
converter.convert_to_pdf(object_id)
manifest = converter.check_status(urn) # Waits for completion
if manifest:
converter.download_pdf(urn, output_path)
import requests
import base64
import time
import json
class APSConverter:
def __init__(self, client_id, client_secret):
self.client_id = client_id
self.client_secret = client_secret
self.access_token = None
self.base_url = "https://developer.api.autodesk.com"
def authenticate(self):
"""Get 2-legged OAuth token"""
url = "https://developer.api.autodesk.com/authentication/v2/token"
headers = {
"Content-Type": "application/x-www-form-urlencoded"
}
data = {
"client_id": self.client_id,
"client_secret": self.client_secret,
"grant_type": "client_credentials",
"scope": "data:read data:write data:create bucket:create bucket:read"
}
response = requests.post(url, headers=headers, data=data)
response.raise_for_status()
self.access_token = response.json()["access_token"]
print("✓ Authentication successful")
def upload_file(self, file_path, bucket_key="my-bucket"):
"""Upload DWG file to APS"""
# Create bucket if it doesn't exist
url = f"{self.base_url}/oss/v2/buckets"
headers = {
"Authorization": f"Bearer {self.access_token}",
"Content-Type": "application/json"
}
bucket_data = {
"bucketKey": bucket_key,
"policyKey": "transient" # Files deleted after 24 hours
}
requests.post(url, headers=headers, json=bucket_data)
# Upload file
object_name = file_path.split("/")[-1]
url = f"{self.base_url}/oss/v2/buckets/{bucket_key}/objects/{object_name}"
headers = {
"Authorization": f"Bearer {self.access_token}",
"Content-Type": "application/octet-stream"
}
with open(file_path, "rb") as f:
response = requests.put(url, headers=headers, data=f)
response.raise_for_status()
object_id = response.json()["objectId"]
print(f"✓ File uploaded: {object_name}")
return object_id
def convert_to_pdf(self, object_id):
"""Convert DWG to PDF using Model Derivative API"""
# Encode object ID to Base64 (URL-safe)
urn = base64.urlsafe_b64encode(object_id.encode()).decode()
url = f"{self.base_url}/modelderivative/v2/designdata/job"
headers = {
"Authorization": f"Bearer {self.access_token}",
"Content-Type": "application/json"
}
job_payload = {
"input": {
"urn": urn
},
"output": {
"formats": [
{
"type": "pdf",
"advanced": {
"paperSize": "ISO A4" # or "ANSI A", "ISO A3", etc.
}
}
]
}
}
response = requests.post(url, headers=headers, json=job_payload)
response.raise_for_status()
print("✓ Conversion job started")
return urn
def check_status(self, urn):
"""Check conversion status"""
url = f"{self.base_url}/modelderivative/v2/designdata/{urn}/manifest"
headers = {
"Authorization": f"Bearer {self.access_token}"
}
while True:
response = requests.get(url, headers=headers)
response.raise_for_status()
manifest = response.json()
status = manifest["status"]
if status == "success":
print("✓ Conversion completed successfully")
return manifest
elif status == "failed":
print("✗ Conversion failed")
print(json.dumps(manifest, indent=2))
return None
else:
print(f" Status: {status} - waiting...")
time.sleep(5)
def download_pdf(self, urn, output_path):
"""Download converted PDF"""
# Get manifest to find PDF derivative
manifest_url = f"{self.base_url}/modelderivative/v2/designdata/{urn}/manifest"
headers = {"Authorization": f"Bearer {self.access_token}"}
response = requests.get(manifest_url, headers=headers)
manifest = response.json()
# Find PDF derivative
pdf_urn = None
for derivative in manifest["derivatives"]:
if derivative["outputType"] == "pdf":
pdf_urn = derivative["children"][0]["urn"]
break
if not pdf_urn:
print("✗ PDF derivative not found")
return False
# Download PDF
download_url = f"{self.base_url}/modelderivative/v2/designd