# Copyright (c) 2016-2021, Manfred Moitzi
# License: MIT License
# Purpose: fast & simple but restricted DXF R12 writer, with no in-memory
# drawing, and without dependencies to other ezdxf modules.
# The created DXF file contains no HEADER, TABLES or BLOCKS section only the
# ENTITIES section is present.
from typing import (
    TextIO,
    BinaryIO,
    Union,
    Sequence,
    Iterable,
    Tuple,
    cast,
    Iterator,
    Optional,
)
from contextlib import contextmanager
from functools import partial
from io import StringIO
from pathlib import Path
from ezdxf.lldxf.tagwriter import BinaryTagWriter

Vertex = Sequence[float]
rnd = partial(round, ndigits=6)

TEXT_ALIGN_FLAGS = {
    "LEFT": (0, 0),
    "CENTER": (1, 0),
    "RIGHT": (2, 0),
    "BOTTOM_LEFT": (0, 1),
    "BOTTOM_CENTER": (1, 1),
    "BOTTOM_RIGHT": (2, 1),
    "MIDDLE_LEFT": (0, 2),
    "MIDDLE_CENTER": (1, 2),
    "MIDDLE_RIGHT": (2, 2),
    "TOP_LEFT": (0, 3),
    "TOP_CENTER": (1, 3),
    "TOP_RIGHT": (2, 3),
}

VERTEX_GROUP_CODES = {"x": 10, "y": 20, "s": 40, "e": 41, "b": 42}


class BinaryDXFWriter:
    def __init__(self, stream: BinaryIO):
        self._stream = stream
        self._tagwriter = BinaryTagWriter(
            self._stream,
            dxfversion="AC1009",
            write_handles=False,
            encoding="cp1252",
        )
        self._tagwriter.write_signature()

    def write(self, s: str) -> None:
        self._tagwriter.write_str(s)


@contextmanager
def r12writer(
    stream: Union[TextIO, BinaryIO, str, Path],
    fixed_tables: bool = False,
    fmt: str = "asc",
) -> Iterator["R12FastStreamWriter"]:
    """Context manager for writing DXF entities to a stream/file. `stream` can
    be any file like object with a :func:`write` method or just a string for
    writing DXF entities to the file system. If `fixed_tables` is ``True``, a
    standard TABLES section is written in front of the ENTITIES
    section and some predefined text styles and line types can be used.

    Set argument `fmt` to "asc" to write ASCII DXF file (default) or "bin" to
    write Binary DXF files. ASCII DXF require a :class:`TextIO` stream and
    Binary DXF require a :class:`BinaryIO` stream.

    """
    _stream: Union[TextIO, BinaryIO, None] = None

    if fmt.startswith("asc"):
        if isinstance(stream, (str, Path)):
            _stream = open(stream, "wt", encoding="cp1252")
            stream = _stream
    elif fmt.startswith("bin"):
        if isinstance(stream, (str, Path)):
            _stream = open(stream, "wb")
            stream = cast(TextIO, BinaryDXFWriter(_stream))
        else:
            stream = cast(TextIO, BinaryDXFWriter(cast(BinaryIO, stream)))

    else:
        raise ValueError(f"Unknown format '{fmt}'.")
    writer = R12FastStreamWriter(cast(TextIO, stream), fixed_tables)
    try:
        yield writer
    finally:
        writer.close()
        if _stream:
            _stream.close()


class R12FastStreamWriter:
    """Fast stream writer to create simple DXF R12 drawings.

    Args:
        stream: a file like object with a :func:`write` method.
        fixed_tables: if `fixed_tables` is ``True``, a standard TABLES section
            is written in front of the ENTITIES section and some predefined text
            styles and line types can be used.

    """

    def __init__(self, stream: Union[TextIO], fixed_tables=False):
        self.stream = stream
        if fixed_tables:
            stream.write(PREFACE)
        stream.write("0\nSECTION\n2\nENTITIES\n")  # write header

    def close(self) -> None:
        """Writes the DXF tail. Call is not necessary when using the context
        manager :func:`r12writer`.
        """
        self.stream.write("0\nENDSEC\n0\nEOF\n")  # write tail

    def add_line(
        self,
        start: Vertex,
        end: Vertex,
        layer: str = "0",
        color: int = None,
        linetype: str = None,
    ) -> None:
        """Add a LINE entity from `start` to `end`.

        Args:
            start: start vertex as ``(x, y[, z])`` tuple
            end: end vertex as  as ``(x, y[, z])`` tuple
            layer: layer name as string, without a layer definition the assigned
                color = ``7`` (black/white) and line type is ``'Continuous'``.
            color: color as :ref:`ACI` in the range from ``0`` to ``256``,
                ``0`` is `ByBlock` and ``256`` is `ByLayer`, default is `ByLayer`
                which is always color = ``7`` (black/white) without a layer
                definition.
            linetype: line type as string, if FIXED-TABLES are written some
                predefined line types are available, else line type is always
                `ByLayer`, which is always ``'Continuous'`` without a LAYERS
                table.

        """
        dxf = ["0\nLINE\n"]
        dxf.append(dxf_attribs(layer, color, linetype))
        dxf.append(dxf_vertex(start, code=10))
        dxf.append(dxf_vertex(end, code=11))
        self.stream.write("".join(dxf))

    def add_circle(
        self,
        center: Vertex,
        radius: float,
        layer: str = "0",
        color: int = None,
        linetype: str = None,
    ) -> None:
        """Add a CIRCLE entity.

        Args:
            center: circle center point as ``(x, y)`` tuple
            radius: circle radius as float
            layer: layer name as string see :meth:`add_line`
            color: color as :ref:`ACI` see :meth:`add_line`
            linetype: line type as string see :meth:`add_line`

        """
        dxf = ["0\nCIRCLE\n"]
        dxf.append(dxf_attribs(layer, color, linetype))
        dxf.append(dxf_vertex(center))
        dxf.append(dxf_tag(40, str(rnd(radius))))
        self.stream.write("".join(dxf))

    def add_arc(
        self,
        center: Vertex,
        radius: float,
        start: float = 0,
        end: float = 360,
        layer: str = "0",
        color: int = None,
        linetype: str = None,
    ) -> None:
        """Add an ARC entity. The arc goes counter clockwise from `start` angle
        to `end` angle.

        Args:
            center: arc center point as ``(x, y)`` tuple
            radius: arc radius as float
            start: arc start angle in degrees as float
            end: arc end angle in degrees as float
            layer: layer name as string see :meth:`add_line`
            color: color as :ref:`ACI` see :meth:`add_line`
            linetype: line type as string see :meth:`add_line`

        """
        dxf = ["0\nARC\n"]
        dxf.append(dxf_attribs(layer, color, linetype))
        dxf.append(dxf_vertex(center))
        dxf.append(dxf_tag(40, str(rnd(radius))))
        dxf.append(dxf_tag(50, str(rnd(start))))
        dxf.append(dxf_tag(51, str(rnd(end))))
        self.stream.write("".join(dxf))

    def add_point(
        self,
        location: Vertex,
        layer: str = "0",
        color: int = None,
        linetype: str = None,
    ) -> None:
        """
        Add a POINT entity.

        Args:
            location: point location as ``(x, y [,z])`` tuple
            layer: layer name as string see :meth:`add_line`
            color: color as :ref:`ACI` see :meth:`add_line`
            linetype: line type as string see :meth:`add_line`

        """
        dxf = ["0\nPOINT\n"]
        dxf.append(dxf_attribs(layer, color, linetype))
        dxf.append(dxf_vertex(location))
        self.stream.write("".join(dxf))

    def add_3dface(
        self,
        vertices: Iterable[Vertex],
        invisible: int = 0,
        layer: str = "0",
        color: int = None,
        linetype: str = None,
    ) -> None:
        """Add a 3DFACE entity. 3DFACE is a spatial area with 3 or 4 vertices,
        all vertices have to be in the same plane.

        Args:
            vertices: iterable of 3 or 4 ``(x, y, z)`` vertices.
            invisible: bit coded flag to define the invisible edges,

                1. edge = 1
                2. edge = 2
                3. edge = 4
                4. edge = 8

                Add edge values to set multiple edges invisible,
                1. edge + 3. edge = 1 + 4 = 5, all edges = 15

            layer: layer name as string see :meth:`add_line`
            color: color as :ref:`ACI` see :meth:`add_line`
            linetype: line type as string see :meth:`add_line`

        """
        self._add_quadrilateral(
            "3DFACE", vertices, invisible, layer, color, linetype
        )

    def add_solid(
        self,
        vertices: Iterable[Vertex],
        layer: str = "0",
        color: int = None,
        linetype: str = None,
    ) -> None:
        """Add a SOLID entity. SOLID is a solid filled area with 3 or 4 edges
        and SOLID is a 2D entity.

        Args:
            vertices: iterable of 3 or 4 ``(x, y[, z])`` tuples, z-axis will be
                ignored.
            layer: layer name as string see :meth:`add_line`
            color: color as :ref:`ACI` see :meth:`add_line`
            linetype: line type as string see :meth:`add_line`

        """
        self._add_quadrilateral("SOLID", vertices, 0, layer, color, linetype)

    def _add_quadrilateral(
        self,
        dxftype: str,
        vertices: Iterable[Vertex],
        flags: int,
        layer: str,
        color: Optional[int],
        linetype: Optional[str],
    ) -> None:
        dxf = ["0\n%s\n" % dxftype]
        dxf.append(dxf_attribs(layer, color, linetype))
        vertices = list(vertices)
        if len(vertices) < 3:
            raise ValueError("%s needs 3 or 4 vertices." % dxftype)
        elif len(vertices) == 3:
            vertices.append(vertices[-1])  # double last vertex
        dxf.extend(
            dxf_vertex(vertex, code)
            for code, vertex in enumerate(vertices, start=10)
        )
        if flags:
            dxf.append(dxf_tag(70, str(flags)))
        self.stream.write("".join(dxf))

    def add_polyline(
        self,
        vertices: Iterable[Vertex],
        closed: bool = False,
        layer: str = "0",
        color: int = None,
        linetype: str = None,
    ) -> None:
        """Add a 3D POLYLINE entity.

        Args:
            vertices: iterable of ``(x, y[, z])`` tuples, z-axis is ``0`` by
                default
            closed: ``True`` creates a closed polyline
            layer: layer name as string see :meth:`add_line`
            color: color as :ref:`ACI` see :meth:`add_line`
            linetype: line type as string see :meth:`add_line`

        .. versionchanged:: 0.12
            Write only 3D POLYLINE entity, added `closed` argument.

        """
        dxf = ["0\nPOLYLINE\n"]
        dxf.append(dxf_attribs(layer, color, linetype))
        dxf.append(dxf_tag(66, 1))  # entities follow
        dxf.append(dxf_tag(70, 8 + int(closed)))  # bit 1 is the closed state
        self.stream.write("".join(dxf))

        vertex_template = "0\nVERTEX\n" + dxf_attribs(layer) + dxf_tag(70, 32)
        for vertex in vertices:
            self.stream.write(vertex_template)
            vertex = tuple(vertex)
            len_vertex = len(vertex)
            if len_vertex < 2:
                raise ValueError("Vertices require at least a x- and a y-axis.")
            elif len_vertex == 2:
                vertex = (vertex[0], vertex[1], 0)
            self.stream.write(dxf_vertex(vertex[:3]))
        self.stream.write("0\nSEQEND\n")

    def add_polyline_2d(
        self,
        points: Iterable[Sequence],
        format: str = "xy",
        closed: bool = False,
        start_width: float = 0,
        end_width: float = 0,
        layer: str = "0",
        color: int = None,
        linetype: str = None,
    ) -> None:
        """Add a 2D POLYLINE entity with start width, end width and bulge value
        support.

        Format codes:

        === =================================
        x   x-coordinate
        y   y-coordinate
        s   start width
        e   end width
        b   bulge value
        v   (x, y) tuple (z-axis is ignored)
        === =================================

        Args:
            points: iterable of (x, y, [start_width, [end_width, [bulge]]])
                tuple, value order according to the `format` string, unset
                values default to ``0``
            format: format: format string, default is ``'xy'``
            closed: ``True`` creates a closed polyline
            start_width: default start width, default is ``0``
            end_width: default end width, default is ``0``
            layer: layer name as string see :meth:`add_line`
            color: color as :ref:`ACI` see :meth:`add_line`
            linetype: line type as string see :meth:`add_line`

        """

        def vertex_attribs(data: Sequence) -> dict:
            attribs = dict()
            for code, value in zip(format, data):
                if code == "v":
                    location = tuple(value)
                    attribs["x"] = location[0]
                    attribs["y"] = location[1]
                else:
                    attribs[code] = value
            return attribs

        dxf = ["0\nPOLYLINE\n"]
        dxf.append(dxf_attribs(layer, color, linetype))
        dxf.append(dxf_tag(66, 1))  # entities follow
        dxf.append(dxf_tag(70, int(closed)))  # bit 1 is the closed state
        if start_width:  # default start width
            dxf.append(dxf_tag(40, start_width))
        if end_width:  # default end width
            dxf.append(dxf_tag(41, end_width))
        self.stream.write("".join(dxf))

        vertex_template = "0\nVERTEX\n" + dxf_attribs(layer) + dxf_tag(70, 0)
        for point in points:
            self.stream.write(vertex_template)
            attribs = vertex_attribs(point)
            for format_code in format:
                value = attribs.get(format_code, 0)
                if value == 0 and format_code in "seb":
                    continue  # do not write default values
                self.stream.write(
                    dxf_tag(VERTEX_GROUP_CODES[format_code], value)
                )
        self.stream.write("0\nSEQEND\n")

    def add_polyface(
        self,
        vertices: Iterable[Vertex],
        faces: Iterable[Sequence[int]],
        layer: str = "0",
        color: int = None,
        linetype: str = None,
    ) -> None:
        """Add a POLYFACE entity. The POLYFACE entity supports only faces of
        maximum 4 vertices, more indices will be ignored. A simple square would
        be::

            v0 = (0, 0, 0)
            v1 = (1, 0, 0)
            v2 = (1, 1, 0)
            v3 = (0, 1, 0)
            dxf.add_polyface(vertices=[v0, v1, v2, v3], faces=[(0, 1, 2, 3)])

        All 3D form functions of the :mod:`ezdxf.render.forms` module return
        :class:`~ezdxf.render.MeshBuilder` objects, which provide the required
        vertex and face lists.

        See sphere example: https://github.com/mozman/ezdxf/blob/master/examples/r12writer.py

        Args:
            vertices: iterable of ``(x, y, z)`` tuples
            faces: iterable of 3 or 4 vertex indices, indices have to be 0-based
            layer: layer name as string see :meth:`add_line`
            color: color as :ref:`ACI` see :meth:`add_line`
            linetype: line type as string see :meth:`add_line`

        """

        def write_polyline(flags: int = 64) -> None:
            dxf = ["0\nPOLYLINE\n"]
            dxf.append(dxf_attribs(layer, color, linetype))
            dxf.append(dxf_tag(66, 1))  # entities follow
            dxf.append(dxf_tag(70, flags))
            dxf.append(dxf_tag(71, vertex_count))
            dxf.append(dxf_tag(72, face_count))
            self.stream.write("".join(dxf))

        def write_vertices(flags: int = 64 + 128):
            buf = StringIO()
            count = 0
            s = "0\nVERTEX\n" + dxf_attribs(layer) + dxf_tag(70, flags)
            for vertex in vertices:
                count += 1
                buf.write(s)
                buf.write(dxf_vertex(vertex))
            s = buf.getvalue()
            buf.close()
            return count, s

        def write_faces(flags: int = 128):
            buf = StringIO()
            count = 0
            s = (
                "0\nVERTEX\n"
                + dxf_attribs(layer, color)
                + dxf_tag(70, flags)
                + dxf_vertex((0, 0, 0))
            )
            for face in faces:
                count += 1
                buf.write(s)
                for code, index in zip((71, 72, 73, 74), face):
                    buf.write(dxf_tag(code, index + 1))
            s = buf.getvalue()
            buf.close()
            return count, s

        vertex_count, vertex_str = write_vertices()
        face_count, face_str = write_faces()
        write_polyline()
        self.stream.write(vertex_str)
        self.stream.write(face_str)
        self.stream.write("0\nSEQEND\n")

    def add_polymesh(
        self,
        vertices: Iterable[Vertex],
        size: Tuple[int, int],
        closed=(False, False),
        layer: str = "0",
        color: int = None,
        linetype: str = None,
    ) -> None:
        """Add a POLYMESH entity. A POLYMESH is a mesh of m rows and n columns,
        each mesh vertex has its own x-, y- and z coordinates. The mesh can be
        closed in m- and/or n-direction. The vertices have to be in column
        order:  (m0, n0), (m0, n1), (m0, n2), (m1, n0), (m1, n1), (m1, n2), ...

        See example: https://github.com/mozman/ezdxf/blob/master/examples/r12writer.py

        Args:
            vertices: iterable of ``(x, y, z)`` tuples, in column order
            size: mesh dimension as (m, n)-tuple, requirement:
                ``len(vertices) == m*n``
            closed: (m_closed, n_closed) tuple, for closed mesh in m and/or n
                direction
            layer: layer name as string see :meth:`add_line`
            color: color as :ref:`ACI` see :meth:`add_line`
            linetype: line type as string see :meth:`add_line`

        """
        m, n = size
        m_closed, n_closed = closed

        def write_polyline(flags: int = 16) -> None:
            if m_closed:
                flags += 1
            if n_closed:
                flags += 32

            dxf = ["0\nPOLYLINE\n"]
            dxf.append(dxf_attribs(layer, color, linetype))
            dxf.append(dxf_tag(66, 1))  # entities follow
            dxf.append(dxf_tag(70, flags))
            dxf.append(dxf_tag(71, m))
            dxf.append(dxf_tag(72, n))
            self.stream.write("".join(dxf))

        def write_vertices(flags: int = 64) -> int:
            count = 0
            s = "0\nVERTEX\n" + dxf_attribs(layer) + dxf_tag(70, flags)
            for vertex in vertices:
                count += 1
                self.stream.write(s)
                self.stream.write(dxf_vertex(vertex))
            return count

        write_polyline()
        count = write_vertices()
        if m * n != count:
            raise ValueError("Invalid mesh dimensions.")

        self.stream.write("0\nSEQEND\n")

    def add_text(
        self,
        text: str,
        insert: Vertex = (0, 0),
        height: float = 1.0,
        width: float = 1.0,
        align: str = "LEFT",
        rotation: float = 0.0,
        oblique: float = 0.0,
        style: str = "STANDARD",
        layer: str = "0",
        color: int = None,
    ) -> None:
        """Add a one line TEXT entity.

        Args:
            text: the text as string
            insert: insert location as ``(x, y)`` tuple
            height: text height in drawing units
            width: text width as factor
            align: text alignment, see table below
            rotation: text rotation in degrees as float
            oblique: oblique in degrees as float, vertical = ``0`` (default)
            style: text style name as string, if FIXED-TABLES are written some
                predefined text styles are available, else text style is
                always ``'STANDARD'``.
            layer: layer name as string see :meth:`add_line`
            color: color as :ref:`ACI` see :meth:`add_line`

        ============   =============== ================= =====
        Vert/Horiz     Left            Center            Right
        ============   =============== ================= =====
        Top            ``TOP_LEFT``    ``TOP_CENTER``    ``TOP_RIGHT``
        Middle         ``MIDDLE_LEFT`` ``MIDDLE_CENTER`` ``MIDDLE_RIGHT``
        Bottom         ``BOTTOM_LEFT`` ``BOTTOM_CENTER`` ``BOTTOM_RIGHT``
        Baseline       ``LEFT``        ``CENTER``         ``RIGHT``
        ============   =============== ================= =====

        The special alignments ``ALIGNED`` and ``FIT`` are not available.

        """
        # text style is always STANDARD without a TABLES section
        dxf = ["0\nTEXT\n"]
        dxf.append(dxf_attribs(layer, color))
        dxf.append(dxf_vertex(insert, code=10))
        dxf.append(dxf_tag(1, str(text)))
        dxf.append(dxf_tag(40, str(rnd(height))))
        if width != 1.0:
            dxf.append(dxf_tag(41, str(rnd(width))))
        if rotation != 0.0:
            dxf.append(dxf_tag(50, str(rnd(rotation))))
        if oblique != 0.0:
            dxf.append(dxf_tag(51, str(rnd(oblique))))
        if style != "STANDARD":
            dxf.append(dxf_tag(7, str(style)))
        halign, valign = TEXT_ALIGN_FLAGS[align.upper()]
        dxf.append(dxf_tag(72, str(halign)))
        dxf.append(dxf_tag(73, str(valign)))
        dxf.append(dxf_vertex(insert, code=11))  # align point
        self.stream.write("".join(dxf))


def dxf_attribs(layer: str, color: int = None, linetype: str = None) -> str:
    dxf = ["8\n%s\n" % layer]  # layer is required
    if linetype is not None:
        dxf.append("6\n%s\n" % linetype)
    if color is not None:
        if 0 <= int(color) < 257:
            dxf.append("62\n%d\n" % color)
        else:
            raise ValueError(
                "color has to be an integer in the range from 0 to 256."
            )
    return "".join(dxf)


def dxf_vertex(vertex: Vertex, code=10) -> str:
    dxf = []
    for c in vertex:
        dxf.append("%d\n%s\n" % (code, str(rnd(c))))
        code += 10
    return "".join(dxf)


def dxf_tag(code: int, value) -> str:
    return "%d\n%s\n" % (code, value)


FORMAT_CODES = frozenset("xysebv")

PREFACE = """  0
SECTION
  2
HEADER
  9
$ACADVER
  1
AC1009
  9
$DWGCODEPAGE
  3
ANSI_1252
  0
ENDSEC
  0
SECTION
  2
TABLES
  0
TABLE
  2
LTYPE
  5
431
 70
20
  0
LTYPE
  5
40F
  2
CONTINUOUS
 70
0
  3
Solid line
 72
65
 73
0
 40
0.0
  0
LTYPE
  5
410
  2
CENTER
 70
0
  3
Center ____ _ ____ _ ____ _ ____ _ ____ _ ____
 72
65
 73
4
 40
2.0
 49
1.25
 49
-0.25
 49
0.25
 49
-0.25
  0
LTYPE
  5
411
  2
DASHED
 70
0
  3
Dashed __ __ __ __ __ __ __ __ __ __ __ __ __ _
 72
65
 73
2
 40
0.75
 49
0.5
 49
-0.25
  0
LTYPE
  5
412
  2
PHANTOM
 70
0
  3
Phantom ______  __  __  ______  __  __  ______
 72
65
 73
6
 40
2.5
 49
1.25
 49
-0.25
 49
0.25
 49
-0.25
 49
0.25
 49
-0.25
  0
LTYPE
  5
413
  2
HIDDEN
 70
0
  3
Hidden __ __ __ __ __ __ __ __ __ __ __ __ __ __
 72
65
 73
2
 40
9.525
 49
6.345
 49
-3.175
  0
LTYPE
  5
43B
  2
CENTERX2
 70
0
  3
Center (2x) ________  __  ________  __  ________
 72
65
 73
4
 40
3.5
 49
2.5
 49
-0.25
 49
0.5
 49
-0.25
  0
LTYPE
  5
43C
  2
CENTER2
 70
0
  3
Center (.5x) ____ _ ____ _ ____ _ ____ _ ____
 72
65
 73
4
 40
1.0
 49
0.625
 49
-0.125
 49
0.125
 49
-0.125
  0
LTYPE
  5
43D
  2
DASHEDX2
 70
0
  3
Dashed (2x) ____  ____  ____  ____  ____  ____
 72
65
 73
2
 40
1.2
 49
1.0
 49
-0.2
  0
LTYPE
  5
43E
  2
DASHED2
 70
0
  3
Dashed (.5x) _ _ _ _ _ _ _ _ _ _ _ _ _ _
 72
65
 73
2
 40
0.3
 49
0.25
 49
-0.05
  0
LTYPE
  5
43F
  2
PHANTOMX2
 70
0
  3
Phantom (2x)____________    ____    ____    ____________
 72
65
 73
6
 40
4.25
 49
2.5
 49
-0.25
 49
0.5
 49
-0.25
 49
0.5
 49
-0.25
  0
LTYPE
  5
440
  2
PHANTOM2
 70
0
  3
Phantom (.5x) ___ _ _ ___ _ _ ___ _ _ ___ _ _ ___
 72
65
 73
6
 40
1.25
 49
0.625
 49
-0.125
 49
0.125
 49
-0.125
 49
0.125
 49
-0.125
  0
LTYPE
  5
441
  2
DASHDOT
 70
0
  3
Dash dot __ . __ . __ . __ . __ . __ . __ . __
 72
65
 73
4
 40
1.4
 49
1.0
 49
-0.2
 49
0.0
 49
-0.2
  0
LTYPE
  5
442
  2
DASHDOTX2
 70
0
  3
Dash dot (2x) ____  .  ____  .  ____  .  ____
 72
65
 73
4
 40
2.4
 49
2.0
 49
-0.2
 49
0.0
 49
-0.2
  0
LTYPE
  5
443
  2
DASHDOT2
 70
0
  3
Dash dot (.5x) _ . _ . _ . _ . _ . _ . _ . _
 72
65
 73
4
 40
0.7
 49
0.5
 49
-0.1
 49
0.0
 49
-0.1
  0
LTYPE
  5
444
  2
DOT
 70
0
  3
Dot .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
 72
65
 73
2
 40
0.2
 49
0.0
 49
-0.2
  0
LTYPE
  5
445
  2
DOTX2
 70
0
  3
Dot (2x) .    .    .    .    .    .    .    .
 72
65
 73
2
 40
0.4
 49
0.0
 49
-0.4
  0
LTYPE
  5
446
  2
DOT2
 70
0
  3
Dot (.5) . . . . . . . . . . . . . . . . . . .
 72
65
 73
2
 40
0.1
 49
0.0
 49
-0.1
  0
LTYPE
  5
447
  2
DIVIDE
 70
0
  3
Divide __ . . __ . . __ . . __ . . __ . . __
 72
65
 73
6
 40
1.6
 49
1.0
 49
-0.2
 49
0.0
 49
-0.2
 49
0.0
 49
-0.2
  0
LTYPE
  5
448
  2
DIVIDEX2
 70
0
  3
Divide (2x) ____  . .  ____  . .  ____  . .  ____
 72
65
 73
6
 40
2.6
 49
2.0
 49
-0.2
 49
0.0
 49
-0.2
 49
0.0
 49
-0.2
  0
LTYPE
  5
449
  2
DIVIDE2
 70
0
  3
Divide(.5x) _ . _ . _ . _ . _ . _ . _ . _
 72
65
 73
6
 40
0.8
 49
0.5
 49
-0.1
 49
0.0
 49
-0.1
 49
0.0
 49
-0.1
  0
ENDTAB
  0
TABLE
  2
STYLE
  5
433
 70
18
  0
STYLE
  5
417
  2
STANDARD
 70
0
 40
0.0
 41
1.0
 50
0.0
 71
0
 42
0.2
  3
txt
  4

  0
STYLE
  5
44A
  2
OpenSans
 70
0
 40
0.0
 41
1.0
 50
0.0
 71
0
 42
1.0
  3
OpenSans-Regular.ttf
  4

  0
STYLE
  5
44F
  2
OpenSansCondensed-Light
 70
0
 40
0.0
 41
1.0
 50
0.0
 71
0
 42
1.0
  3
OpenSansCondensed-Light.ttf
  4

  0
ENDTAB
  0
TABLE
  2
VIEW
  5
434
 70
0
  0
ENDTAB
  0
ENDSEC
"""
