Skip to content

Commit

Permalink
Merge pull request #17 from insightindustry/feature-container-support
Browse files Browse the repository at this point in the history
Feature container support [Closes #13] and [Closes #16]
  • Loading branch information
insightindustry authored Nov 26, 2017
2 parents 1a54153 + 08ebce5 commit 427d49c
Show file tree
Hide file tree
Showing 12 changed files with 728 additions and 50 deletions.
38 changes: 22 additions & 16 deletions docs/source/glossary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,18 @@ Glossary
.. glossary::

Bounding Box
The rectangular area that a given :term:`Design Target` or
The rectangular area that a given :term:`Container` or
:term:`Content Element` takes up on a given :term:`Page`. It has a position,
a width, a height.

Container
A rectangular area on a :term:`Page` that may contain one or more
:term:`Content Elements <Content Element>`.

Container Template
A set of repeatable instructions that can be applied to a
:term:`Container` which determine how the :term:`Container` is drawn.

Content

|br|
Expand All @@ -26,46 +34,44 @@ Glossary
be a paragraph of text, an image, etc.

Design Target
A rectangular area on a :term:`Page` that may contain one or more
:term:`Content Elements <Content Element>`.

Design Target Template
A set of repeatable instructions that can be applied to a
:term:`Design Target` which determine how the :term:`Design Target` is
drawn.
|br|

.. seealso::

:term:`Container`

Design Grid
A grid pattern that can be drawn on your PDF when generated to aid in
testing your design.

Flow
Flow occurs when a :term:`Content Element` will not fit in its
:term:`Design Target`, and therefore must be drawn in the next available
:term:`Design Target` in the related :term:`Story` or in the PDF.
:term:`Container`, and therefore must be drawn in the next available
:term:`Container` in the related :term:`Story` or in the PDF.

Flowable Content
A :term:`Content Element` that is allowed to :term:`flow <Flow>` across
:term:`Design Targets <Design Target>`.
:term:`Containers <Container>`.

Frame

|br|

.. seealso::

:term:`Design Target`.
:term:`Container`.

Page
An object that corresponds to a single physical page (sheet of paper)
within your PDF. What gets drawn on the page is a collection of
:term:`Design Targets <Design Target>`, which in turn contain
:term:`Containers <Container>`, which in turn contain
:term:`Content Elements <Content Element>`.

Page Map
A rendering of a page where each the :term:`Bounding Box` of each
:term:`Design Target` is drawn. May also include the content contained in
the :term:`Design Target`, but does not have to.
:term:`Container` is drawn. May also include the content contained in
the :term:`Container`, but does not have to.

Page Template
A set of repeatable instructions that can be applied to a :term:`Page` which
Expand All @@ -85,7 +91,7 @@ Glossary
methods.

Story
A collection of :term:`Design Targets <Design Target>` whose
A collection of :term:`Containers <Container>` whose
:term:`content <Content>` can :term:`flow <Flow>` across its members.

Template
Expand All @@ -94,7 +100,7 @@ Glossary

.. seealso::

:term:`Design Target Template`
:term:`Container Template`

:term:`Page Template`

Expand Down
2 changes: 1 addition & 1 deletion docs/source/index.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Why PDFDesigner?
generation, I tend to consider them "fiddly".

For one thing, the `PLATYPUS`_ layout engine works by iterating over lists
of :term:`Design Targets <Design Target>` (called ``Frames`` in their
of :term:`Containers <Container>` (called ``Frames`` in their
parlance) and within each frame over lists of :term:`Flowable Content`
(called ``Flowables`` in their parlance). If you have a complicated design,
that may lead to the creation of very long, hard-to-write, hard-to-read
Expand Down
8 changes: 4 additions & 4 deletions docs/source/tutorials/conceptual_overview.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ recommended ``import`` pattern:

import pdfdesigner
from pdfdesigner.defaults import DEFAULT_SETTINGS, DEFAULT_STYLES, DEFAULT_TEMPLATES, DEFAULT_COLORS
from pdfdesigner.templates import SectionTemplate, PageTemplate, DesignTargetTemplate
from pdfdesigner.design.layout import Section, Story, Page, DesignTarget
from pdfdesigner.templates import SectionTemplate, PageTemplate, ContainerTemplate
from pdfdesigner.design.layout import Section, Story, Page, Container
from pdfdesigner.design.content import Paragraph, Table, Image
from pdfdesigner.design.units import POINT, PICA, MM, CM, INCH

Expand All @@ -44,7 +44,7 @@ templates

* :term:`Section Templates <Section Template>`
* :term:`Page Templates <Page Template>`
* :term:`DesignTargetTemplate <Design Target Template>`
* :term:`ContainerTemplate <Container Template>`

which are used to simplify design.

Expand All @@ -59,7 +59,7 @@ design
* :term:`Sections <Section>`
* :term:`Pages <Page>`
* :term:`Stories <Story>`
* :term:`Design Targets <Design Target>`
* :term:`Containers <Container>`

content
Contains modules and classes that are used to instantiate the
Expand Down
22 changes: 16 additions & 6 deletions pdfdesigner/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,7 @@ def get_page_number(item_id,


def get_component(item_id):
"""Return the :term:`Content Element` or :term:`Design Target` identified
by ``item_id``.
"""Return the :term:`Content Element` or :term:`Container` identified by ``item_id``.
:param item_id: The ``id`` of the object who's page number should be returned.
Expand All @@ -121,17 +120,28 @@ def get_component(item_id):

def get_container(item_id,
first_only = False):
"""Return the :term:`Design Target` that contains the :class:`ContentElement`
"""Return the :class:`Container` that contains the :class:`ContentElement`
identified by ``item_id``.
:param item_id: The ``id`` of the :class:`ContentElement` who's container
should be returned.
:param first_only: If ``True``, returns the first :class:`DesignTarget` where
:param first_only: If ``True``, returns the first :class:`Container` where
the :class:`ContentElement` appears. If ``False``, returns a tuple with all
of them.
:returns: The :class:`DesignTarget` identified by ``item_id``.
:rtype: :class:`DesignTarget` / ``None`` / tuple
:returns: The :class:`Container` identified by ``item_id``.
:rtype: :class:`Container` / ``None`` / tuple
"""
raise NotImplementedError()


def get_page(page_number):
"""Return the :class:`Page` with the given ``page_number``.
:param page_number: The page number of the :class:`Page` to return.
:type page_number: int
:rtype: :class:`Page` or ``None``.
"""
raise NotImplementedError()
8 changes: 4 additions & 4 deletions pdfdesigner/design/content/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,18 @@
"""

from .content_element import ContentElement
from .paragraph import Paragraph
# from .image import Image
# from .table import Table
from .stylesheet import Stylesheet, Style
from .content_element import ContentElement
from .paragraph import Paragraph
from .fonts import FontDefinition, register_font_family

__all__ = [
'ContentElement',
'Paragraph',
'Image',
'Table',
#'Image',
#'Table',
'Stylesheet',
'Style',
'FontDefinition',
Expand Down
102 changes: 92 additions & 10 deletions pdfdesigner/design/content/content_element.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
Implements the base class for defining :term:`Content Elements <Content Element>.`
"""
import uuid
import random
import string
import pdfdesigner
from pdfdesigner.design.content import Style

Expand All @@ -16,9 +17,16 @@ class ContentElement(object):
"""Object representation of a :term:`Content Element`."""

def __init__(self,
name = None,
style = None):
"""Create the :class:`ContentElement` and populate basic attributes."""
self.id = uuid.uuid4()
"""Create the :class:`ContentElement` and populate basic attributes.
:param style: The :class:`Style` which should be applied to the
:class:`ContentElement`.
:type style: :class:`Style`
"""
self._name = name
self.id = self.set_id(name)
self._style = None
self._has_flowed = False

Expand All @@ -34,15 +42,28 @@ def __repr__(self):

return ''.join(return_tuple)

def set_id(self, value):
"""Set the object's :ref:`ContentElement.id` to the hash of ``value``.
:param value: The value whose hash should be used as the object's ID.
"""
hash_value = hash(value)
self.id = hash_value

@property
def name(self):
"""Alias the :ref:`ContentElement.id` attribute."""
return self.id
if self._name is None:
return self.id

return self._name

@name.setter
def name(self, value):
"""Alias the :ref:`ContentElement.id` attribute."""
self.id = value
if self._name is None:
self._name = value
self.set_id(value)

@property
def style(self):
Expand Down Expand Up @@ -86,23 +107,23 @@ def page_numbers(self):

@property
def container(self):
"""Return the first :class:`DesignTarget` that contains the :class:`ContentElement`.
"""Return the first :class:`Container` that contains the :class:`ContentElement`.
:rtype: :class:`DesignTarget` / ``None``
:rtype: :class:`Container` / ``None``
"""
return pdfdesigner.get_container(self.id, first_only = True)

@property
def containers(self):
"""Return all :class:`DesignTargets <DesignTarget>` that contain the :class:`ContentElement`.
"""Return all :class:`Containers <Container>` that contain the :class:`ContentElement`.
:rtype: tuple / ``None``
"""
return pdfdesigner.get_container(self.id, first_only = False)

@property
def is_flowable(self):
"""Return whether the :class:`ContentElement` can flow across :class:`DesignTarget`
"""Return whether the :class:`ContentElement` can flow across :class:`Container`
objects.
:rtype: boolean
Expand All @@ -115,8 +136,69 @@ def is_flowable(self):
@property
def has_flowed(self):
"""Return whether the :class:`ContentElement` has flowed across
:class:`DesignTargets <DesignTarget>`.
:class:`Containers <Container>`.
:rtype: boolean
"""
return self._has_flowed

def get_required_width(self):
"""Return the object's minimum required width.
:returns: ``None`` if there is no minimum required width, otherwise
the number of points required.
.. note::
This method should be implemented in classes that inherit from
:class:`ContentElement`.
"""
return None

def get_required_height(self, width = None):
"""Return the object's minimum required height to be drawn completely.
:param width: The width to assume when calculating the required height.
:type width: numeric
:returns: ``None`` if there is no minimum required height, or if the
object is :term:`Flowable Content`. Otherwise the height required.
.. note::
This method should be implemented in classes that inherit from
:class:`ContentElement`.
"""
if self.is_flowable:
return None

return None

def will_fit(self, dimensions):
"""Check whether the :class:`ContentElement` will fit within the dimensions.
:param dimensions: The ``(width, height)`` to check against.
:type dimensions: tuple
"""
will_fit = True

if not isinstance(dimensions, tuple):
raise TypeError('dimensions must be a tuple of form (width, height)')

width = dimensions[0]
height = dimensions[1]

if width is None:
raise ValueError('width cannot be None')
if height is None:
raise ValueError('height cannot be None')

required_width = self.get_required_width()
required_height = self.get_required_height(width = width)

if required_width is not None and width < required_width:
will_fit = False
if required_height is not None and height < required_height:
will_fit = False

return will_fit
Loading

0 comments on commit 427d49c

Please sign in to comment.