Reading and plotting drawings#

papermodels.paper.pdf#

Read and write PDF annotations with pikepdf.

papermodels.paper.pdf.load_pdf_annotations(pdf_path, show_skipped=False, show_unimplemented=False)[source]#

Returns a lists of pdf annotations keyed by page index.

Parameters:
  • pdf_path (Path | str) – Path-like object representing the path to the PDF file to open.

  • show_skipped (bool)

  • show_unimplemented (bool)

Return type:

list[Annotation]

papermodels.paper.pdf.update_pdf_annotations(pdf_path, parsed_annotations, export_path, append_text=False)[source]#

Returns None. Creates a copy of the file at ‘pdf_path’ with all of annotations representing structural elements (collector and transfer elements, not loads or legend entries) having their text field updated with the assigned tag of the structural element.

Parameters:
Return type:

None

papermodels.paper.pdf.update_annotation_text_field(pike_annot, text, replace=False)[source]#

Returns a copy of the ‘pike_annot’ with the text field updated with ‘text’. If ‘replace’ is True then the text field is completely replaced with ‘text’. If not, then ‘text’ is appended on a new line to the existing ‘text’.

Parameters:
  • pike_annot (Annotation)

  • text (str)

  • replace (bool)

papermodels.paper.pdf.compare_annotations(pm_annot, pike_annot, page_num, rotate)[source]#

Returns True if the pm_annot is nominally equal to the ‘pike_annot’ by converting the ‘pike_annot’ to the ‘pm_annot’ and seeing if they are equal. Will return True for duplicate annotations.

Parameters:
Return type:

bool

papermodels.paper.pdf.pike_annotation_to_pm_annotation(annot, annot_idx, page_idx, rotate=None, display_unimplemented_annots=False)[source]#

Returns either an Annotation object or None. None is returned if:

  • The annotation has a “parent” key

  • Has the word “ignore” in the text field

  • If that annotation is a type that cannot be read yet

  • The annotation does not have a Subj key

Note: Numerical data within pike annotation objects are of type Decimal

Parameters:
  • annot_idx (int)

  • page_idx (int)

  • rotate (int | None)

  • display_unimplemented_annots (bool)

Return type:

Annotation | None

papermodels.paper.pdf.get_dash_pattern(annot, content_stream=None)[source]#

Returns the dash array of ‘annot’ as a tuple of floats (e.g. (4.0, 4.0)) or None if the annotation is drawn with a solid line.

The border style dictionary (/BS) is the authoritative source. If the annotation does not have one, the dash operator (‘[…] phase d’) in the appearance ‘content_stream’ is used instead. PDF editors encode solid lines in several ways (no /BS, /S /S, an empty dash array, ‘[] 0 d’); all of these are normalized to None so that solid lines compare equal regardless of the software used to draw them.

Parameters:

content_stream (str | None)

Return type:

tuple | None

papermodels.paper.pdf.parse_content_stream(stream)[source]#

Parses data in the content stream into an ordered dictionary. Keys are operators. Values are operands.

Currently only parses non-path data reliably. Path data is not parsed separately so, in the output dict, repetitive operators are overwritten in the dict (e.g. multiple ‘l’ operators will only have one entry).

Parameters:

stream (str)

Return type:

dict[str, list]

papermodels.paper.pdf.parse_html_text_content(xml_code)[source]#

Returns ‘Python-formatted’ text for html text in the annotation popup

Parameters:

xml_code (str)

Return type:

str

papermodels.paper.dxf#

Read DXF drawings into Annotations, treating each DXF layer as an annotation’s text.

papermodels.paper.dxf.load_dxf_directory(directory_path, directory_page_idx=None)[source]#

Returns a list of Annotations representing the annotations in all of the .dxf files within the ‘directory_path’.

If ‘directory_page_idx’ is not None, then its value will be applied to the .page_idx attribute for all annotations in all files within the directory. Otherwise, a page idx will be generated based on the “glob order” of the files in the directory and applied to the annotations originating from that file.

Parameters:
  • directory_path (Path | str)

  • directory_page_idx (int | None)

Return type:

list[Annotation]

papermodels.paper.dxf.load_dxf_annotations(dxf_path, page_idx=0, polygonize_layers=None)[source]#

Returns a list of Annotations read from the DXF file at ‘dxf_path’.

Parameters:
  • dxf_path (Path | str) – Path-like object representing the path to the DXF file to open.

  • page_idx (int) – The page index to assign to the resulting Annotations.

  • polygonize_layers (list[str] | None) – Names of layers whose linework should be converted into polygons.

Return type:

list[Annotation]

papermodels.paper.dxf.dxf_file_to_dxf_entities(dxf_path, page_idx=0, polygonize_layers=None)[source]#

Returns a lists of pdf annotations keyed by page index.

Parameters:
  • dxf_path (Path | str) – Path-like object representing the path to the PDF file to open.

  • dxf_dir – If provided, a list of paths which to find DXF files comprising a single model. The order of the paths is important and will be used to create the order of spatial planes in the model, in ascending order (the first path will be the lowest plane in the model).

  • annotations_layer – if None, all entities will be treated as annotation entities

  • pages_layer – if None, will treat all entities in the modelspace as belonging to the same spatial plane. If provided, then entities existing within each page rectangle will be treated as belonging to the same plane. Each set of annotation entities within the page must also include an “origin” annotation. The origin must be in the same place for each “page”.

  • pages_layer_order – Optional[str], one of {“ltr”, “rtl”, “ttb”, “btt”} left-to-right, right-to-left, top-to-bottom, bottom-to-top Which will be used to order the pages in ascending order.

  • polygonize_layers (list[str] | None) – A list of layers if passed will polygonize the individual linestrings on these layers.

  • page_idx (int)

Return type:

dict

papermodels.paper.dxf.dxf_entity_to_shapely(entity, page_idx, local_idx, polygonize_lines=False)[source]#

Converts the entity into an Annotation

Parameters:
  • entity (DXFGraphic)

  • page_idx (int)

  • local_idx (int)

  • polygonize_lines (bool)

Return type:

Annotation

papermodels.paper.dxf.dxf_entities_to_annotations(dxf_entities)[source]#

Returns a list of annotation for the dxf-entities

Parameters:

dxf_entities (dict[str, list | str])

Return type:

list[Annotation]

papermodels.paper.dxf.parse_block_coordinates(entity)[source]#

Extract sequenced coordinates from the block.

papermodels.paper.dxf.parse_line_coords(entity)[source]#

Returns a DXF LINE entity as a LineString snapped to a 1e-3 grid.

Parameters:

entity (DXFGraphic)

papermodels.paper.dxf.parse_polyline_coords(entity)[source]#

Returns a DXF polyline entity as a Polygon snapped to a 1e-3 grid.

Parameters:

entity (DXFGraphic)

papermodels.paper.dxf.parse_arc_coords(arc, num_segments=12)[source]#

Approximate an ARC entity as a series of linear segments

Parameters:

arc (DXFGraphic)

papermodels.paper.dxf.coords_to_vertices_list(coords)[source]#

Formats the ordered pairs of coordinates in ‘coords’ as a flattened list of coordinates in this order [x0, y0, x1, y1, x2, y2, …, xn, yn]

Parameters:

coords (list[tuple[float, float]])

Return type:

list

papermodels.paper.annotations#

Parse, tag, filter, scale and align Annotations.

These functions turn raw Annotation records into the attribute dictionaries consumed by from_parsed_annotations().

papermodels.paper.annotations.annotations_to_shapely(annots, as_geometry_collection=False)[source]#

Returns a shapely geometry representing the geometry in ‘annot’

Parameters:
  • annots (list[Annotation]) – a list of Annotation objects

  • as_geometry_collection – If True, the geometries in ‘annots’ will all be grouped together in a shapely.geometry.GeometryCollection

Return type:

list[Any]

papermodels.paper.annotations.annotation_to_shapely(annot)[source]#

Returns an shapely geometry created from the annotation type and vertices in ‘annot’.

Parameters:

annot (Annotation)

Return type:

Any

papermodels.paper.annotations.get_annotation_geometry_pairs(annots)[source]#

Returns a dict of annotation, shapely geometry pairs

Parameters:

annots (list[Annotation])

Return type:

dict[Annotation, Geometry]

papermodels.paper.annotations.parsed_annotations_to_loading_geometry(parsed_annots)[source]#

Convert annotations representing loading areas into a list of LoadingArea

Parameters:

parsed_annots (dict[Annotation, dict])

Return type:

list[LoadingGeometry]

papermodels.paper.annotations.parse_annotations(annots, legend, legend_identifier)[source]#

Returns a dictionary of annotations organized by their legend entry. If the annotation type is not in the ‘legend’, then it is excluded from the results.

Parameters:
Return type:

dict[Annotation, dict]

papermodels.paper.annotations.is_region_markup(annot_attrs)[source]#

True for markup that describes where a joist array spans rather than a structural element: extent lines and joist containers. These carry no reaction type.

Parameters:

annot_attrs (dict)

Return type:

bool

papermodels.paper.annotations.is_joist_container(annot_attrs)[source]#

Returns True if the parsed annotation attributes describe a joist container markup.

Parameters:

annot_attrs (dict)

Return type:

bool

papermodels.paper.annotations.tag_parsed_annotations(parsed_annots)[source]#

Adds an identifying tag to the annotation based on the page number of the annotation and its identified type. Prioritizes manually named tags according to thsi format: “{ABBR}{PAGE_ID}.{INDEX}”. If an annotation is not already tagged accordingly then it will be auto-assigned an integer index after all tagged annotations have been accounted for.

All tags are guaranteed to be unique.

Parameters:

parsed_annots (dict[Annotation, dict])

Return type:

dict[Annotation, dict]

papermodels.paper.annotations.parse_tag_components(tag)[source]#

Returns a tuple of the tag components: type abbrev., page_id, tag_idx

Parameters:

tag (str)

Return type:

tuple[str, int, int]

papermodels.paper.annotations.assign_page_id_to_annotations(annots, page_containers, left_to_right=True)[source]#

Returns a list of Annotation representing ‘annots’ after they have been assigned to their page container in ‘page_containers’.

Parameters:
Return type:

list[list[Annotation]]

papermodels.paper.annotations.enumerate_page_annotations(page_annots, left_to_right=True)[source]#

Returns the annotations in ‘annots’ that correspond to page-demarcation polygons organized in a seqential fashion.

Parameters:
Return type:

dict[Polygon, int]

papermodels.paper.annotations.sort_annotations_by_page_polygon(annots, page_geom_map)[source]#

Sorts each annotation into its own list[Annotation] coresponding to which page they are contained in.

Parameters:
Return type:

dict[Polygon, list[Annotation]]

papermodels.paper.annotations.align_annotations_to_pages(annotations_by_page)[source]#

Returns a copy of ‘annotations_by_page’ but with all annotations aligned within their respective pages by their origin points. The distance between the page origin points and the page corner is set by the distance of the first origin on the first page.

Parameters:

annotations_by_page (dict[Polygon, list[Annotation]])

Return type:

list[list[Annotation]]

papermodels.paper.annotations.reset_annotations_to_origin(annots_on_page, page_origin_xy, origin_centroid_xy, global_offset)[source]#

Translates all of the annotations in ‘annots_on_page’ so that the origin annotation (contained within ‘annots_on_page’) is located at ‘xy_offset’ from the bottom-left corner.

Parameters:
Return type:

list[Annotation]

papermodels.paper.annotations.get_origin_offset(origin_annot, page_poly)[source]#

Returns the xy offset of the ‘origin_annot’ to the bottom-left corner of the ‘page_annot’.

Parameters:
Return type:

Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | complex | bytes | str | _NestedSequence[complex | bytes | str]

papermodels.paper.annotations.get_origin_centroid(origin_annot)[source]#

Returns the centroid of the ‘origin_annot’ as an xy tuple.

Parameters:

origin_annot (Annotation)

Return type:

Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | complex | bytes | str | _NestedSequence[complex | bytes | str]

papermodels.paper.annotations.get_page_bottom_left_corner(page_poly)[source]#

Returns the coordinate fo the bottom left hand corner of the ‘page_annot’ (a rectangle)

Parameters:

page_poly (Polygon)

Return type:

Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | complex | bytes | str | _NestedSequence[complex | bytes | str]

papermodels.paper.annotations.get_page_geom_by_page_index(page_annots)[source]#

Returns a dictionary that organizes the ‘page_annots’ by their page id.

Parameters:

page_annots (list[Annotation])

Return type:

dict[int, list[Polygon]]

papermodels.paper.annotations.parse_annot_kwargs(text_contents)[source]#

Returns a dictionary representing the kwargs that may be encoded into the text field of the annotation

Parameters:

text_contents (str)

Return type:

dict[str, str]

papermodels.paper.annotations.remove_windows_crlf(text_contents)[source]#

Normalizes Windows (CRLF) and classic Mac (CR) line endings to LF.

Parameters:

text_contents (str)

Return type:

str

papermodels.paper.annotations.filter_annotations(annots, properties)[source]#

Returns a list of annotations from ‘annots’ that have properties that match the keywords in ‘properties’. Note: The filtering process currently requires that both the keys AND values in ‘properties’ be hashable. ‘properties’ is a dictionary of annotation properties and their values, e.g.

{‘line_weight’: 3.0, ‘line_color’: (1, 0, 0)} or {‘text’: “Slab Outline”}

The returned annotations will only be annotations that match ALL of the properties described.

Parameters:
Return type:

list[Annotation]

papermodels.paper.annotations.scale_annotations(annots, scale, paper_origin=None, round_precision=4)[source]#

Scale the annotations in ‘annots’. Each vertex in each annotation in ‘annots’ will be multiplied by ‘scale’. If ‘paper_origin’ is provided, then the annotation coordinates will have their origin reset to ‘paper_origin’. Note that ‘paper_origin’ is the unscaled coordinate space (i.e. in points) The purpose of setting ‘paper_origin’ if the annotation has a “datum” that is set somewhere in the file. Leave as None if there is no datum set.

Parameters:
Return type:

list[Annotation]

papermodels.paper.annotations.parse_legend(legend_text, legend_identifier)[source]#

Returns a dict of key/value pairs extracted from teh legened annotation text

Parameters:
  • legend_text (str)

  • legend_identifier (str)

Return type:

dict

papermodels.paper.annotations.strip_html_tags(s)[source]#

Removes but does not sanitize HTML tags from strings

Parameters:

s (str)

Return type:

str

papermodels.paper.annotations.str_to_int(s)[source]#

Returns ‘s’ as an int if it parses as one, otherwise returns ‘s’ unchanged.

Parameters:

s (str)

Return type:

int | str

papermodels.paper.plot#

Matplotlib plotting of annotations and elements.

papermodels.paper.plot.plot_elements(elements, figsize=(17, 11), dpi=100, plot_trib_areas=False, plot_extent_polygons=False, plot_subelement_tags=False, plot_elems_by_tag=None)[source]#

Plots the elements in matplotlib. Size and dpi can be adjusted to make the plot bigger/smaller. Size is in inches and dpi stands for “dots per inch”. For a biggish plot, values of size=12, dpi=200 gives good results.

Colors are based on the kind of element that is being plotted

Parameters:
Return type:

Figure

papermodels.paper.plot.get_element_plotting_attributes(element, is_subelement=False)[source]#

Returns a dict of plotting attributes for the element.

e.g. {

“is_poly”: True, “is_line”: False, “is_linear”: False, “is_subelement”: False, “tag”: str, “rank”: int, “xy”: np.array([[…], […]]), “anchor_point”: np.array([…, …]), “extent_polygon_xy”: np.array([[…], […]]) | None, “trib_area_xy”: np.array([[…], […]]) | None,

}

Parameters:
Return type:

dict[str, Any]

papermodels.paper.plot.plot_annotations(annots, figsize=(17, 11), dpi=100, plot_tags=False, plot_annots_by_tag=None)[source]#

Plots that annotations, ‘annots’ in matplotlib. Size and dpi can be adjusted to make the plot bigger/smaller. Size is in inches and dpi stands for “dots per inch”. For a biggish plot, values of size=12, dpi=200 gives good results.

Parameters:
Return type:

Figure

papermodels.paper.plot.xy_vertices(vertices, dpi, close=False)[source]#

Returns a list of lists of floats to emulate a 2d numpy array of x, y values

Parameters:
Return type:

list[list[float]]