☁️ APS (Forge)intermediateFile Conversion

Convert DWG to PDF Using Autodesk Platform Services

Use APS (formerly Forge) Model Derivative API to convert AutoCAD DWG files to PDF format in the cloud.

0 views12/14/2025

Convert DWG to PDF Using Autodesk Platform Services (APS)

Application: Autodesk Platform Services (formerly Forge)
Category: File Conversion
Difficulty: Intermediate
Language: Python

Problem

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.

Solution

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.

Prerequisites

  1. Create an APS app at https://aps.autodesk.com/myapps
  2. Note your Client ID and Client Secret
  3. Install required Python packages:
pip install requests

Code Example

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")

Advanced: Batch Conversion

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}")

Key Points

  • APS uses 2-legged OAuth for server-to-server authentication
  • Files are uploaded to OSS (Object Storage Service) before conversion
  • Conversion is asynchronous - you must poll for status
  • Transient buckets automatically delete files after 24 hours
  • Supports various paper sizes: ISO A0-A4, ANSI A-E, etc.

Common Pitfalls

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)

Related Topics

  • APS authentication and tokens
  • Model Derivative API capabilities
  • Viewer integration
  • Webhook notifications for conversion events
  • Custom conversion settings

Code Example

Python
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