# 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.
# Created: 14.04.2016
# Copyright (c) 2016-2020, Manfred Moitzi
# License: MIT License
from typing import TextIO, BinaryIO, Union, Sequence, Iterable, Tuple
from contextlib import contextmanager
from io import StringIO
from ezdxf.lldxf.tagwriter import BinaryTagWriter


# types
Vertex = Sequence[float]


def rnd(x: float) -> float:  # adjust output precision of floats by changing 'ndigits'
    return round(x, 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], fixed_tables: bool = False,
              fmt: str = 'asc') -> '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 = None
    if fmt.startswith('asc'):
        if not hasattr(stream, 'write'):
            _stream = open(stream, 'wt', encoding='cp1252')
            stream = _stream
    elif fmt.startswith('bin'):
        if hasattr(stream, 'write'):
            stream = BinaryDXFWriter(stream)
        else:
            _stream = open(stream, 'wb')
            stream = BinaryDXFWriter(_stream)
    else:
        raise ValueError(f"Unknown format '{fmt}'.")

    writer = R12FastStreamWriter(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: [TextIO, BinaryDXFWriter], 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: int,
                           linetype: 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.,
                 width: float = 1.,
                 align: str = "LEFT",
                 rotation: float = 0.,
                 oblique: float = 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.:
            dxf.append(dxf_tag(41, str(rnd(width))))
        if rotation != 0.:
            dxf.append(dxf_tag(50, str(rnd(rotation))))
        if oblique != 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
"""
