├── tests ├── __init__.py ├── data │ └── sample.pdf ├── test_run.py ├── test_renderer.py └── test_response.py ├── drf_pdf ├── __init__.py ├── exceptions.py ├── renderer.py └── response.py ├── examples ├── drf_pdf_examples │ ├── simple │ │ ├── __init__.py │ │ ├── migrations │ │ │ └── __init__.py │ │ └── views.py │ ├── drf_pdf_examples │ │ ├── __init__.py │ │ ├── urls.py │ │ ├── wsgi.py │ │ └── settings.py │ └── manage.py └── requirements.txt ├── .coveragerc ├── .bumpversion.cfg ├── .landscape.yaml ├── MANIFEST.in ├── requirements-dev.txt ├── .editorconfig ├── mkdocs.yml ├── docs ├── api-guide │ ├── renderers.md │ └── responses.md └── index.md ├── Makefile ├── tox.ini ├── .gitignore ├── LICENSE ├── .travis.yml ├── conftest.py ├── setup.py └── README.md /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /drf_pdf/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/drf_pdf_examples/simple/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/drf_pdf_examples/drf_pdf_examples/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/drf_pdf_examples/simple/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | omit= 3 | */tests/* 4 | *test* 5 | -------------------------------------------------------------------------------- /tests/data/sample.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drgarcia1986/drf-pdf/HEAD/tests/data/sample.pdf -------------------------------------------------------------------------------- /tests/test_run.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | class TestRun(object): 5 | 6 | def test_should_run_this(self): 7 | assert True 8 | -------------------------------------------------------------------------------- /.bumpversion.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 0.2.0 3 | commit = True 4 | tag = True 5 | tag_name = {new_version} 6 | 7 | [bumpversion:file:setup.py] 8 | 9 | -------------------------------------------------------------------------------- /.landscape.yaml: -------------------------------------------------------------------------------- 1 | doc-warnings: yes 2 | test-warnings: no 3 | strictness: veryhigh 4 | max-line-length: 120 5 | autodetect: yes 6 | ignore-paths: 7 | - examples 8 | -------------------------------------------------------------------------------- /examples/requirements.txt: -------------------------------------------------------------------------------- 1 | Django==1.8 2 | Pillow==2.7.0 3 | djangorestframework==3.0.5 4 | drf-pdf==0.1.0 5 | py==1.4.26 6 | reportlab==3.1.44 7 | wsgiref==0.1.2 8 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include README.md 3 | include tox.ini 4 | include .coveragerc 5 | recursive-include tests requirements-dev.txt *.py 6 | recursive-exclude * __pycache__ 7 | global-exclude *pyc 8 | -------------------------------------------------------------------------------- /drf_pdf/exceptions.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | from rest_framework import status 3 | from rest_framework.exceptions import APIException 4 | 5 | 6 | class PDFFileNotFound(APIException): 7 | status_code = status.HTTP_404_NOT_FOUND 8 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | bumpversion==0.5.1 2 | tox==1.9.0 3 | pytest==2.6.4 4 | pytest-cov==1.8.1 5 | ipdb==0.8 6 | git+https://github.com/mverteuil/pytest-ipdb.git 7 | djangorestframework==3.0.5 8 | Django==1.7.5 9 | mkdocs==0.11.1 10 | -------------------------------------------------------------------------------- /examples/drf_pdf_examples/drf_pdf_examples/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, url 2 | from simple.views import SimpleExample 3 | 4 | 5 | urlpatterns = patterns( 6 | '', 7 | url(r'^simple/$', SimpleExample.as_view()), 8 | ) 9 | -------------------------------------------------------------------------------- /examples/drf_pdf_examples/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "drf_pdf_examples.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | indent_style = space 11 | indent_size = 4 12 | charset = utf-8 13 | 14 | [Makefile] 15 | indent_style = tab 16 | 17 | [*.{yml,yaml}] 18 | indent_size = 2 19 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: DRF PDF 2 | site_description: Simple PDF utils for Django Rest Framework 3 | repo_url: https://github.com/drgarcia1986/drf-pdf 4 | site_url: http://drf-pdf.readthedocs.org/en/latest/ 5 | theme: readthedocs 6 | 7 | pages: 8 | - ['index.md', 'Home'] 9 | - ['api-guide/renderers.md', 'API Guide', 'Renderers'] 10 | - ['api-guide/responses.md', 'API Guide', 'Responses'] 11 | -------------------------------------------------------------------------------- /tests/test_renderer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from drf_pdf import renderer 3 | 4 | 5 | class TestRenderer(object): 6 | """Test for DRF-PDF Renderer""" 7 | 8 | def test_render(self, pdf_file_sample): 9 | 10 | with open(pdf_file_sample, 'r') as f: 11 | file_data = f 12 | ret = renderer.PDFRenderer().render(file_data) 13 | 14 | assert ret == file_data 15 | -------------------------------------------------------------------------------- /docs/api-guide/renderers.md: -------------------------------------------------------------------------------- 1 | Renderers 2 | ========= 3 | 4 | In order to support a new `media_type` in _REST Framework_, it is needed to add a [renderer](http://www.django-rest-framework.org/api-guide/renderers/) to `renderer_classes`. So DRF PDF includes `PDFRenderer`. 5 | 6 | API Reference 7 | ------------- 8 | 9 | ### PDFRenderer 10 | 11 | Render PDF binary content. 12 | 13 | **.media_type**: `application/pdf` 14 | 15 | **.format**: `pdf` 16 | 17 | **.render_style**: `bynary` 18 | -------------------------------------------------------------------------------- /drf_pdf/renderer.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from rest_framework.renderers import BaseRenderer 3 | 4 | 5 | class PDFRenderer(BaseRenderer): 6 | 7 | """ Renderer for PDF binary content. """ 8 | 9 | media_type = 'application/pdf' 10 | format = 'pdf' 11 | charset = None 12 | render_style = 'binary' 13 | 14 | def render(self, data, media_type=None, renderer_context=None): 15 | """ 16 | Return the PDF data as it is 17 | """ 18 | return data 19 | -------------------------------------------------------------------------------- /examples/drf_pdf_examples/drf_pdf_examples/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for drf_pdf_examples project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "drf_pdf_examples.settings") 12 | 13 | from django.core.wsgi import get_wsgi_application 14 | application = get_wsgi_application() 15 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | clean: 2 | @find . -name "*.pyc" | xargs rm -rf 3 | @find . -name "*.pyo" | xargs rm -rf 4 | @find . -name "__pycache__" -type d | xargs rm -rf 5 | 6 | test: clean 7 | py.test -x --cov-config .coveragerc --cov-report html --cov-report xml --cov-report term --cov drf_pdf/ tests/ 8 | 9 | test-debug: clean 10 | py.test -x --ipdb drf_pdf/ tests/ 11 | 12 | requirements: clean 13 | pip install -r requirements-dev.txt 14 | 15 | release-patch: 16 | bumpversion patch 17 | 18 | release-minor: 19 | bumpversion minor 20 | 21 | release-major: 22 | bumpversion major 23 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = 3 | {py27,py34}-django{15,16,17,18}-drf{2,3,31} 4 | 5 | [testenv] 6 | setenv = 7 | PYTHONDONTWRITEBYTECODE=1 8 | deps = 9 | pytest 10 | pytest-cov 11 | django15: Django>=1.5,<1.6 12 | django16: Django>=1.6,<1.7 13 | django17: Django>=1.7,<1.8 14 | django18: Django>=1.8 15 | drf2: djangorestframework>=2.4 16 | drf3: djangorestframework>=3.0 17 | drf31: djangorestframework>=3.1 18 | commands = 19 | py.test -x --cov-config .coveragerc --cov-report html --cov-report xml --cov-report term --cov drf_pdf/ tests/ 20 | -------------------------------------------------------------------------------- /examples/drf_pdf_examples/simple/views.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from cStringIO import StringIO 3 | 4 | from rest_framework import status 5 | from rest_framework.views import APIView 6 | 7 | from drf_pdf.renderer import PDFRenderer 8 | from drf_pdf.response import PDFResponse 9 | 10 | from reportlab.pdfgen import canvas 11 | 12 | 13 | class SimpleExample(APIView): 14 | 15 | renderer_classes = (PDFRenderer, ) 16 | 17 | def get(self, request): 18 | pdf = StringIO() 19 | 20 | gen_pdf = canvas.Canvas(pdf) 21 | gen_pdf.drawString(100, 100, 'Simple example') 22 | gen_pdf.showPage() 23 | gen_pdf.save() 24 | 25 | return PDFResponse( 26 | pdf.getvalue(), 27 | file_name='example', 28 | status=status.HTTP_200_OK 29 | ) 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | 25 | # PyInstaller 26 | # Usually these files are written by a python script from a template 27 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 28 | *.manifest 29 | *.spec 30 | 31 | # Installer logs 32 | pip-log.txt 33 | pip-delete-this-directory.txt 34 | 35 | # Unit test / coverage reports 36 | htmlcov/ 37 | .tox/ 38 | .coverage 39 | .cache 40 | nosetests.xml 41 | coverage.xml 42 | 43 | # Translations 44 | *.mo 45 | *.pot 46 | 47 | # Django stuff: 48 | *.log 49 | 50 | # Sphinx documentation 51 | docs/_build/ 52 | 53 | # PyBuilder 54 | target/ 55 | 56 | # sqlite 57 | *.db 58 | *.sqlite3 59 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Diego Garcia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | sudo: false 4 | 5 | cache: 6 | directories: 7 | - $HOME/.cache/pip 8 | 9 | env: 10 | - TOX_ENV=py27-django15-drf2 11 | - TOX_ENV=py27-django15-drf3 12 | - TOX_ENV=py27-django15-drf31 13 | - TOX_ENV=py27-django16-drf2 14 | - TOX_ENV=py27-django16-drf3 15 | - TOX_ENV=py27-django16-drf31 16 | - TOX_ENV=py27-django17-drf2 17 | - TOX_ENV=py27-django17-drf3 18 | - TOX_ENV=py27-django17-drf31 19 | - TOX_ENV=py27-django18-drf2 20 | - TOX_ENV=py27-django18-drf3 21 | - TOX_ENV=py27-django18-drf31 22 | - TOX_ENV=py34-django15-drf2 23 | - TOX_ENV=py34-django15-drf3 24 | - TOX_ENV=py34-django15-drf31 25 | - TOX_ENV=py34-django16-drf2 26 | - TOX_ENV=py34-django16-drf3 27 | - TOX_ENV=py34-django16-drf31 28 | - TOX_ENV=py34-django17-drf2 29 | - TOX_ENV=py34-django17-drf3 30 | - TOX_ENV=py34-django17-drf31 31 | - TOX_ENV=py34-django18-drf2 32 | - TOX_ENV=py34-django18-drf3 33 | - TOX_ENV=py34-django18-drf31 34 | 35 | install: 36 | - make requirements 37 | 38 | before_script: 39 | - pip install python-coveralls 40 | 41 | script: 42 | - tox -e $TOX_ENV 43 | 44 | after_success: 45 | - coveralls 46 | -------------------------------------------------------------------------------- /tests/test_response.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import codecs 3 | import os 4 | import pytest 5 | from drf_pdf import response, exceptions 6 | 7 | 8 | class TestResponse(object): 9 | """Test for DRF-PDF Response""" 10 | 11 | def test_response(self, pdf_file_sample): 12 | with codecs.open(pdf_file_sample, 'rb', 'ISO-8859-2') as f: 13 | file_data = f.read() 14 | ret = response.PDFResponse( 15 | file_data, 16 | 'sample' 17 | ) 18 | 19 | assert ret.content_type.lower() == 'application/pdf' 20 | assert ret['Content-Length'] == str(len(file_data)) 21 | assert ret['Content-Disposition'].lower() == 'filename="sample.pdf"' 22 | 23 | 24 | class TestFileResponse(object): 25 | """Test for DRF-PDF File Response""" 26 | 27 | def test_response_by_file_name(self, pdf_file_sample): 28 | ret = response.PDFFileResponse(file_path=pdf_file_sample) 29 | 30 | assert ret.content_type.lower() == 'application/pdf' 31 | assert ret['Content-Disposition'].lower() == \ 32 | 'filename="{}"'.format(os.path.basename(pdf_file_sample)) 33 | 34 | def test_response_file_not_found(self, pdf_file_sample): 35 | with pytest.raises(exceptions.PDFFileNotFound): 36 | response.PDFFileResponse(file_path='foo.bar') 37 | -------------------------------------------------------------------------------- /docs/api-guide/responses.md: -------------------------------------------------------------------------------- 1 | Responses 2 | ========= 3 | 4 | You can use a [Regular REST Framework's Response](http://www.django-rest-framework.org/api-guide/responses/) to create a PDF file response, but _DRF PDF_ includes `PDFResponse` as a nice way to do it and it also includes some PDF file metadata. 5 | 6 | Example: 7 | ```python 8 | from rest_framework import status 9 | from drf_pdf.response import PDFResponse 10 | 11 | response = PDFResponse( 12 | pdf=pdf, 13 | file_name=pdf_id, 14 | status=status.HTTP_200_OK 15 | ) 16 | ``` 17 | 18 | Arguments: 19 | 20 | * `pdf`: A byte array with the PDF file content. 21 | 22 | * `file_name`: The default downloaded file name 23 | 24 | Another option is _DRF PDF_ `PDFFileResponse`. 25 | Different from `PDFResponse`, `PDFFileResponse` accepts the path of a pdf file and loads the pdf content from file system. 26 | 27 | Example: 28 | ```python 29 | from rest_framework import status 30 | from drf_pdf.response import PDFFileResponse 31 | 32 | response = PDFFileResponse( 33 | file_path='/path/to/file.pdf', 34 | status=status.HTTP_200_OK 35 | ) 36 | ``` 37 | 38 | Arguments: 39 | 40 | * `file_path`: The PDF file path (to load file from file system). 41 | 42 | You can also include _REST Framework's_ [Response arguments](http://www.django-rest-framework.org/api-guide/responses/#response) on both responses. 43 | -------------------------------------------------------------------------------- /conftest.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import pytest 4 | 5 | 6 | def pytest_configure(): 7 | """ pytest setup. """ 8 | 9 | import django 10 | from django.conf import settings 11 | 12 | settings.configure( 13 | DEBUG=False, 14 | DEBUG_PROPAGATE_EXCEPTIONS=True, 15 | DATABASES={'default': {'ENGINE': 'django.db.backends.sqlite3', 16 | 'NAME': ':memory:'}}, 17 | SECRET_KEY='not very secret in tests', 18 | USE_I18N=True, 19 | USE_L10N=True, 20 | STATIC_URL='/static/', 21 | ROOT_URLCONF='tests.urls', 22 | MIDDLEWARE_CLASSES=( 23 | 'django.middleware.common.CommonMiddleware', 24 | 'django.middleware.csrf.CsrfViewMiddleware', 25 | 'django.contrib.sessions.middleware.SessionMiddleware', 26 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 27 | ), 28 | INSTALLED_APPS=( 29 | 'django.contrib.auth', 30 | 'django.contrib.contenttypes', 31 | ), 32 | ) 33 | 34 | if django.get_version() >= '1.7': 35 | django.setup() 36 | 37 | 38 | @pytest.fixture 39 | def pdf_file_sample(): 40 | """ pytest fixture to get sample pdf file and run tests. """ 41 | 42 | return os.path.join( 43 | os.path.dirname(os.path.realpath(__file__)), 44 | 'tests', 45 | 'data', 46 | 'sample.pdf' 47 | ) 48 | -------------------------------------------------------------------------------- /drf_pdf/response.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | import codecs 3 | import os 4 | from rest_framework.response import Response 5 | from .exceptions import PDFFileNotFound 6 | 7 | 8 | class PDFResponse(Response): 9 | 10 | """ 11 | DRF Response to render data as a PDF File. 12 | 13 | kwargs: 14 | - pdf (byte array). The PDF file content. 15 | - file_name (string). The default downloaded file name. 16 | """ 17 | 18 | def __init__(self, pdf, file_name, *args, **kwargs): 19 | headers = { 20 | 'Content-Disposition': 'filename="{}.pdf"'.format(file_name), 21 | 'Content-Length': len(pdf), 22 | } 23 | 24 | super(PDFResponse, self).__init__( 25 | pdf, 26 | content_type='application/pdf', 27 | headers=headers, 28 | *args, 29 | **kwargs 30 | ) 31 | 32 | 33 | class PDFFileResponse(PDFResponse): 34 | 35 | """ 36 | DRF Response to render data as a PDF File 37 | from a PDF file from file system 38 | 39 | kwargs: 40 | - file_path (string). The PDF file path (to load file from file system) 41 | """ 42 | 43 | def __init__(self, file_path, *args, **kwargs): 44 | pdf = self.__load_pdf(file_path) 45 | file_name = os.path.splitext( 46 | os.path.basename(file_path) 47 | )[0] 48 | super(PDFFileResponse, self).__init__( 49 | pdf=pdf, 50 | file_name=file_name, 51 | *args, 52 | **kwargs 53 | ) 54 | 55 | def __load_pdf(self, file_path): 56 | if not os.path.isfile(file_path): 57 | raise PDFFileNotFound() 58 | with codecs.open(file_path, 'r', 'ISO-8859-2') as f: 59 | file_data = f.read() 60 | return file_data 61 | -------------------------------------------------------------------------------- /examples/drf_pdf_examples/drf_pdf_examples/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | BASE_DIR = os.path.dirname(os.path.dirname(__file__)) 3 | 4 | SECRET_KEY = 'c6u93$f-3@5@!6aw^3qias_=q&*5(91b8gyn*_img7lkx-4z11' 5 | 6 | DEBUG = True 7 | # TEMPLATE_DEBUG = True 8 | ALLOWED_HOSTS = [] 9 | 10 | 11 | # Application definition 12 | 13 | INSTALLED_APPS = ( 14 | # 'django.contrib.admin', 15 | # 'django.contrib.auth', 16 | # 'django.contrib.contenttypes', 17 | # 'django.contrib.sessions', 18 | # 'django.contrib.messages', 19 | # 'django.contrib.staticfiles', 20 | 21 | 'rest_framework', 22 | 'drf_pdf' 23 | ) 24 | 25 | MIDDLEWARE_CLASSES = ( 26 | # 'django.contrib.sessions.middleware.SessionMiddleware', 27 | # 'django.middleware.common.CommonMiddleware', 28 | # 'django.middleware.csrf.CsrfViewMiddleware', 29 | # 'django.contrib.auth.middleware.AuthenticationMiddleware', 30 | # 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 31 | # 'django.contrib.messages.middleware.MessageMiddleware', 32 | # 'django.middleware.clickjacking.XFrameOptionsMiddleware', 33 | ) 34 | 35 | ROOT_URLCONF = 'drf_pdf_examples.urls' 36 | 37 | WSGI_APPLICATION = 'drf_pdf_examples.wsgi.application' 38 | 39 | 40 | # Database 41 | # https://docs.djangoproject.com/en/1.7/ref/settings/#databases 42 | 43 | DATABASES = { 44 | # 'default': { 45 | # 'ENGINE': 'django.db.backends.sqlite3', 46 | # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 47 | # } 48 | } 49 | 50 | # Internationalization 51 | # https://docs.djangoproject.com/en/1.7/topics/i18n/ 52 | 53 | LANGUAGE_CODE = 'en-us' 54 | 55 | TIME_ZONE = 'UTC' 56 | 57 | USE_I18N = True 58 | 59 | USE_L10N = True 60 | 61 | USE_TZ = True 62 | 63 | 64 | # Static files (CSS, JavaScript, Images) 65 | # https://docs.djangoproject.com/en/1.7/howto/static-files/ 66 | 67 | STATIC_URL = '/static/' 68 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | from setuptools import setup 4 | 5 | 6 | def read(fname): 7 | """ Return file content. """ 8 | 9 | return open( 10 | os.path.join( 11 | os.path.dirname(__file__), fname) 12 | ).read() 13 | 14 | 15 | def get_packages(package): 16 | """ Return root package and all sub-packages. """ 17 | 18 | return [dirpath 19 | for dirpath, dirnames, filenames in os.walk(package) 20 | if os.path.exists(os.path.join(dirpath, '__init__.py'))] 21 | 22 | 23 | def get_package_data(package): 24 | """ 25 | Return all files under the root package, that are not in a package themselves. 26 | """ 27 | 28 | walk = [(dirpath.replace(package + os.sep, '', 1), filenames) 29 | for dirpath, dirnames, filenames in os.walk(package) 30 | if not os.path.exists(os.path.join(dirpath, '__init__.py'))] 31 | 32 | filepaths = [] 33 | for base, filenames in walk: 34 | filepaths.extend([os.path.join(base, filename) 35 | for filename in filenames]) 36 | return {package: filepaths} 37 | 38 | setup( 39 | name='drf-pdf', 40 | version='0.2.0', 41 | install_requires=['djangorestframework>=2.4'], 42 | url='https://github.com/drgarcia1986/drf-pdf', 43 | author='Diego Garcia', 44 | author_email='drgarcia1986@gmail.com', 45 | keywords='django djangorestframework render response pdf', 46 | description='A simple PDF renderer for Django Rest Framework', 47 | long_description=read('README.md'), 48 | packages=get_packages('drf_pdf'), 49 | packages_data=get_package_data('drf_pdf'), 50 | classifiers=[ 51 | 'Framework :: Django', 52 | 'Intended Audience :: Developers', 53 | 'License :: OSI Approved :: MIT License', 54 | 'Operating System :: OS Independent', 55 | 'Programming Language :: Python :: 2', 56 | 'Programming Language :: Python :: 3', 57 | ] 58 | ) 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DRF-PDF 2 | [![Documentation Status](https://readthedocs.org/projects/drf-pdf/badge/?version=latest)](https://readthedocs.org/projects/drf-pdf/?badge=latest) 3 | [![Build Status](https://travis-ci.org/drgarcia1986/drf-pdf.svg?branch=master)](https://travis-ci.org/drgarcia1986/drf-pdf) 4 | [![Coverage Status](https://coveralls.io/repos/drgarcia1986/drf-pdf/badge.svg)](https://coveralls.io/r/drgarcia1986/drf-pdf) 5 | [![Code Health](https://landscape.io/github/drgarcia1986/drf-pdf/master/landscape.svg?style=plastic)](https://landscape.io/github/drgarcia1986/drf-pdf/master) 6 | 7 | A simple PDF utils for Django Rest Framework 8 | 9 | ## Install 10 | 11 | ``` 12 | pip install drf-pdf 13 | ``` 14 | 15 | ## Example 16 | 17 | ```python 18 | # coding: utf - 8 19 | from rest_framework import status 20 | from rest_framework.response import Response 21 | from rest_framework.views import APIView 22 | 23 | from drf_pdf.renderer import PDFRenderer 24 | 25 | from my_pdf_package import PDFGenerator 26 | 27 | 28 | class PDFHandler(APIView): 29 | 30 | renderer_classes = (PDFRenderer, ) 31 | 32 | def get(self, request): 33 | pdf = PDFGenerator('foo') 34 | headers = { 35 | 'Content-Disposition': 'filename="foo.pdf"', 36 | 'Content-Length': len(pdf), 37 | } 38 | 39 | return Response( 40 | pdf, 41 | headers=headers, 42 | status=status.HTTP_200_OK 43 | ) 44 | ``` 45 | 46 | ### With two or more renderer_classes 47 | 48 | 49 | ```python 50 | # coding: utf - 8 51 | from rest_framework import status 52 | from rest_framework.response import Response 53 | from rest_framework.renderers import JSONRenderer 54 | from rest_framework.views import APIView 55 | 56 | from drf_pdf.response import PDFResponse 57 | from drf_pdf.renderer import PDFRenderer 58 | 59 | from my_pdf_package import get_pdf 60 | 61 | 62 | class PDFHandler(APIView): 63 | 64 | renderer_classes = (PDFRenderer, JSONRenderer) 65 | 66 | def get(self, request, pdf_id): 67 | pdf = get_pdf(pdf_id) 68 | if not pdf: 69 | return Response( 70 | {'error': 'Not found'}, 71 | status=status.HTTP_404_NOT_FOUND 72 | ) 73 | 74 | return PDFResponse( 75 | pdf=pdf, 76 | file_name=pdf_id, 77 | status=status.HTTP_200_OK 78 | ) 79 | ``` 80 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | Welcome to DRF PDF 2 | ================== 3 | 4 | DRF PDF is a set of simple PDF utils for [Django Rest Framework](http://www.django-rest-framework.org/) 5 | 6 | 7 | Requirements 8 | ------------ 9 | 10 | * Python (2.7, 3.4) 11 | * Django (1.5, 1.6, 1.7, 1.8) 12 | 13 | 14 | Installation 15 | ------------ 16 | 17 | Install using `pip` 18 | 19 | ``` 20 | pip install drf-pdf 21 | ``` 22 | 23 | Add `drf_pdf` to your `INSTALLED_APPS` setting. 24 | 25 | ```python 26 | INSTALLED_APPS = ( 27 | ... 28 | 'drf_pdf', 29 | ) 30 | ``` 31 | 32 | Example 33 | ------- 34 | 35 | ```python 36 | # coding: utf - 8 37 | from rest_framework import status 38 | from rest_framework.response import Response 39 | from rest_framework.views import APIView 40 | 41 | from drf_pdf.renderer import PDFRenderer 42 | 43 | from my_pdf_package import PDFGenerator 44 | 45 | 46 | class PDFHandler(APIView): 47 | 48 | renderer_classes = (PDFRenderer, ) 49 | 50 | def get(self, request): 51 | pdf = PDFGenerator('foo') 52 | headers = { 53 | 'Content-Disposition': 'filename="foo.pdf"', 54 | 'Content-Length': len(pdf), 55 | } 56 | 57 | return Response( 58 | pdf, 59 | headers=headers, 60 | status=status.HTTP_200_OK 61 | ) 62 | ``` 63 | 64 | With two or more `renderer_classes` 65 | 66 | ```python 67 | # coding: utf - 8 68 | from rest_framework import status 69 | from rest_framework.response import Response 70 | from rest_framework.renderers import JSONRenderer 71 | from rest_framework.views import APIView 72 | 73 | from drf_pdf.response import PDFResponse 74 | from drf_pdf.renderer import PDFRenderer 75 | 76 | from my_pdf_package import get_pdf 77 | 78 | 79 | class PDFHandler(APIView): 80 | 81 | renderer_classes = (PDFRenderer, JSONRenderer) 82 | 83 | def get(self, request, pdf_id): 84 | pdf = get_pdf(pdf_id) 85 | if not pdf: 86 | return Response( 87 | {'error': 'Not found'}, 88 | status=status.HTTP_404_NOT_FOUND 89 | ) 90 | 91 | return PDFResponse( 92 | pdf=pdf, 93 | file_name=pdf_id, 94 | status=status.HTTP_200_OK 95 | ) 96 | ``` 97 | 98 | API Guide 99 | --------- 100 | 101 | * [Renderers](api-guide/renderers.md) 102 | * [Response](api-guide/responses.md) 103 | --------------------------------------------------------------------------------