|
|
|
|
|
"""Deploying 3.2
|
|
|
|
|
|
Automatically generated by Colab.
|
|
|
|
|
|
Original file is located at
|
|
|
https://colab.research.google.com/drive/1HEw0DdXhDcxtJN1pjs7bCnlhr-wXX3-m
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def normalize_vertices(vertices):
|
|
|
"""Sort vertices to ensure consistent order."""
|
|
|
return tuple(sorted(tuple(v) for v in vertices))
|
|
|
|
|
|
def areas_are_similar(area1, area2, tolerance=0.2):
|
|
|
"""Check if two areas are within a given tolerance."""
|
|
|
return abs(area1 - area2) <= tolerance
|
|
|
|
|
|
|
|
|
from ctypes import sizeof
|
|
|
|
|
|
"""Version to be deployed of 3.2 Calculating area/perimeter
|
|
|
|
|
|
Automatically generated by Colab.
|
|
|
|
|
|
Original file is located at
|
|
|
https://colab.research.google.com/drive/1XPeCoTBgWSNBYZ3aMKBteP4YG3w4bORs
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
"""## Imports"""
|
|
|
import xml.etree.ElementTree as ET
|
|
|
from PyPDF2 import PdfReader, PdfWriter
|
|
|
from PyPDF2.generic import TextStringObject, NameObject, ArrayObject, FloatObject
|
|
|
from PyPDF2.generic import NameObject, TextStringObject, DictionaryObject, FloatObject, ArrayObject
|
|
|
|
|
|
from typing import NewType
|
|
|
from ctypes import sizeof
|
|
|
|
|
|
from PyPDF2 import PdfReader, PdfWriter
|
|
|
from PyPDF2.generic import NameObject, TextStringObject, FloatObject, DictionaryObject
|
|
|
|
|
|
import numpy as np
|
|
|
import cv2
|
|
|
from matplotlib import pyplot as plt
|
|
|
import math
|
|
|
from PIL import Image , ImageDraw, ImageFont , ImageColor
|
|
|
import fitz
|
|
|
import ezdxf as ez
|
|
|
import sys
|
|
|
from ezdxf import units
|
|
|
|
|
|
from ezdxf.math import OCS, Matrix44, Vec3
|
|
|
import ezdxf
|
|
|
import matplotlib.pyplot as plt
|
|
|
from matplotlib.patches import Polygon
|
|
|
from shapely.geometry import Point, Polygon as ShapelyPolygon
|
|
|
from ezdxf.math import Vec2
|
|
|
import random
|
|
|
import pandas as pd
|
|
|
|
|
|
import tsadropboxretrieval
|
|
|
from ezdxf import bbox
|
|
|
from math import sin, cos, radians
|
|
|
import google_sheet_Legend
|
|
|
from PyPDF2 import PdfReader
|
|
|
from io import BytesIO
|
|
|
|
|
|
from ezdxf.colors import aci2rgb
|
|
|
from collections import Counter
|
|
|
|
|
|
from math import isclose
|
|
|
|
|
|
from typing import List, Tuple, Any, Set
|
|
|
from shapely.geometry import Polygon, MultiPolygon, GeometryCollection
|
|
|
from shapely.ops import unary_union
|
|
|
|
|
|
import random
|
|
|
import string
|
|
|
import zlib
|
|
|
import base64
|
|
|
import datetime
|
|
|
import uuid
|
|
|
from xml.etree.ElementTree import Element, SubElement, tostring, ElementTree
|
|
|
from xml.dom.minidom import parseString
|
|
|
|
|
|
import colorsys
|
|
|
from xml.etree.ElementTree import Element, SubElement, tostring
|
|
|
|
|
|
import re
|
|
|
|
|
|
import ezdxf
|
|
|
from ezdxf.bbox import extents
|
|
|
|
|
|
def detect_scale_from_page(dxf_path, page_pixel_width, m_per_pixel_from_pdf):
|
|
|
"""
|
|
|
Detects mm/px scale factor using the bounding box of the entire DXF content.
|
|
|
"""
|
|
|
doc = ezdxf.readfile(dxf_path)
|
|
|
|
|
|
|
|
|
msp = doc.modelspace()
|
|
|
bbox_msp = extents(msp, fast=True)
|
|
|
|
|
|
if bbox_msp.has_data:
|
|
|
min_x, min_y, max_x, max_y = bbox_msp.extmin.x, bbox_msp.extmin.y, bbox_msp.extmax.x, bbox_msp.extmax.y
|
|
|
else:
|
|
|
|
|
|
psp = doc.layout("Layout1")
|
|
|
bbox_psp = extents(psp, fast=True)
|
|
|
if not bbox_psp.has_data:
|
|
|
raise ValueError("No bounding box data found in modelspace or paperspace.")
|
|
|
min_x, min_y, max_x, max_y = bbox_psp.extmin.x, bbox_psp.extmin.y, bbox_psp.extmax.x, bbox_psp.extmax.y
|
|
|
|
|
|
|
|
|
dxf_width = max_x - min_x
|
|
|
|
|
|
|
|
|
pdf_metric_width = page_pixel_width * m_per_pixel_from_pdf
|
|
|
|
|
|
|
|
|
correction_factor = dxf_width / pdf_metric_width
|
|
|
|
|
|
|
|
|
return correction_factor
|
|
|
|
|
|
|
|
|
"""## Notes"""
|
|
|
|
|
|
|
|
|
'''
|
|
|
This portion is used to convert vertices read from dxf to pixels in order to accurately locate shapes in the image and pdf
|
|
|
ratio :
|
|
|
MeasuredMetric* PixelValue/ DxfMetric = MeasuredPixel
|
|
|
PixelValue: get from pixel conversion code , second number in the bracker represents the perimeter
|
|
|
DxfMetric: measured perimeter from foxit
|
|
|
|
|
|
divide pixelvalue by dxfmetric, will give u a ratio , this is ur dxfratio
|
|
|
|
|
|
|
|
|
'''
|
|
|
AllhatchesCodes= {
|
|
|
'Brick':'<</Length 172/Type/Pattern/PatternType 1/PaintType 1/TilingType 1/Resources<<>>/Matrix[1 0 0 1 0 0]/BBox[0 0 18 18]/XStep 18/YStep 18>>\nstream\n{fillcolor} rg 0 0 18 18 re f {strokecolor} RG 1 w -1 18 m 19.00001 18 l 9 18 m 9 9 l -1 9 m 19.00001 9 l 0 9 m 0 0 l -1 0 m 19.00001 0 l 18 0 m 18 9 l S \nendstream'
|
|
|
,
|
|
|
'DiagonalBrick': '''<</Length 138
|
|
|
/Type/Pattern/PatternType 1/PaintType 1/TilingType 1
|
|
|
/Resources <<>>
|
|
|
/Matrix [1 0 0 1 0 0]
|
|
|
/BBox [0 0 18 18]
|
|
|
/XStep 18/YStep 18>>stream
|
|
|
{fillcolor} rg 0 0 18 18 re f {strokecolor} RG 1 w -1 -1 m 19.00001 19.00001 l 9 9 m 0 18 l -1 17 m 1 19.00001 l 17 -1 m 19.00001 1 l S
|
|
|
endstream'''
|
|
|
,
|
|
|
'Horizontal':'''<</Length 113
|
|
|
/Type/Pattern/PatternType 1/PaintType 1/TilingType 1
|
|
|
/Resources<<>>
|
|
|
/Matrix[1 0 0 1 0 0]
|
|
|
/BBox[0 0 18 18]
|
|
|
/XStep 18/YStep 18>>\nstream\n
|
|
|
{fillcolor} rg 0 0 18 18 re f {strokecolor} RG 1 w -1 13.5 m 19.00001 13.5 l -1 4.5 m 19.00001 4.5 l S
|
|
|
endstream'''
|
|
|
,
|
|
|
'Vertical':'''<</Length 97
|
|
|
/Type/Pattern/PatternType 1/PaintType 1/TilingType 1
|
|
|
/Resources<<>>
|
|
|
/Matrix[1 0 0 1 0 0]
|
|
|
/BBox[0 0 18 18]
|
|
|
/XStep 18/YStep 18>>\nstream\n
|
|
|
{fillcolor} rg 0 0 18 18 re f {strokecolor} RG 1 w 4.5 19.00001 m 4.5 -1 l 13.5 19.00001 m 13.5 -1 l S
|
|
|
endstream'''
|
|
|
,
|
|
|
'DiagonalDown':'''<</Length 133
|
|
|
/Type/Pattern/PatternType 1/PaintType 1/TilingType 1
|
|
|
/Resources<<>>/Matrix[1 0 0 1 0 0]
|
|
|
/BBox[0 0 18 18]/XStep 18/YStep 18>>\nstream\n
|
|
|
{fillcolor} rg 0 0 18 18 re f {strokecolor} RG 1 w -1 19.00001 m 19.00001 -1 l -1 1 m 1 -1 l 17 19.00001 m 19.00001 17 l S
|
|
|
endstream'''
|
|
|
,
|
|
|
'DiagonalUp':'''<</Length 125
|
|
|
/Type/Pattern/PatternType 1/PaintType 1/TilingType 1
|
|
|
/Resources<<>>/Matrix[1 0 0 1 0 0]
|
|
|
/BBox[0 0 18 18]/XStep 18/YStep 18>>\nstream\n
|
|
|
{fillcolor} rg 0 0 18 18 re f {strokecolor} RG 1 w -1 17 m 1 19.00001 l -1 -1 m 19.00001 19.00001 l 17 -1 m 19.00001 1 l S
|
|
|
endstream'''
|
|
|
,
|
|
|
'Grid':'''<</Length 163
|
|
|
/Type/Pattern/PatternType 1/PaintType 1/TilingType 1
|
|
|
/Resources<<>>/Matrix[1 0 0 1 0 0]
|
|
|
/BBox[0 0 18 18]/XStep 18/YStep 18>>\nstream\n
|
|
|
{fillcolor} rg 0 0 18 18 re f {strokecolor} RG 1 w 4.5 19.00001 m 4.5 -1 l 13.5 19.00001 m 13.5 -1 l -1 13.5 m 19.00001 13.5 l -1 4.5 m 19.00001 4.5 l S
|
|
|
endstream'''
|
|
|
,
|
|
|
'Weave':'''<</Length 260
|
|
|
/Type/Pattern/PatternType 1/PaintType 1/TilingType 1
|
|
|
/Resources<<>>/Matrix[1 0 0 1 0 0]
|
|
|
/BBox[0 0 18 18]/XStep 18/YStep 18>>\nstream\n
|
|
|
{fillcolor} rg 0 0 18 18 re f {strokecolor} RG 1 w -1 19.00001 m 4.5 13.5 l -1 7.999999 m 10 19.00001 l 7.999999 19.00001 m 19.00001 7.999999 l 17 19.00001 m 19.00001 17 l -1 -1 m 13.5 13.5 l 4.5 4.5 m 10 -1 l 9 9 m 19.00001 -1 l 18 9 m 13.5 4.5 l S
|
|
|
endstream'''
|
|
|
,
|
|
|
'10Dots':'''<</Length 6765/Type/Pattern/PatternType 1/PaintType 1/TilingType 1/Resources<<>>
|
|
|
/Matrix[1 0 0 1 0 0]/BBox[0 0 18 18]/XStep 18/YStep 18>>\nstream\n
|
|
|
{fillcolor} rg 0 0 18 18 re f {strokecolor} RG 1 w 0 -0.5 m 0.2761424 -0.5 0.5 -0.2761424 0.5 0 c 0.5 0.2761424 0.2761424 0.5 0 0.5 c -0.2761424 0.5 -0.5 0.2761424 -0.5 0 c -0.5 -0.2761424 -0.2761424 -0.5 0 -0.5 c h 0 4 m 0.2761424 4 0.5 4.223857 0.5 4.5 c 0.5 4.776143 0.2761424 5 0 5 c -0.2761424 5 -0.5 4.776143 -0.5 4.5 c -0.5 4.223857 -0.2761424 4 0 4 c h 0 8.5 m 0.2761424 8.5 0.5 8.723858 0.5 9 c 0.5 9.276142 0.2761424 9.5 0 9.5 c -0.2761424 9.5 -0.5 9.276142 -0.5 9 c -0.5 8.723858 -0.2761424 8.5 0 8.5 c h 0 13 m 0.2761424 13 0.5 13.22386 0.5 13.5 c 0.5 13.77614 0.2761424 14 0 14 c -0.2761424 14 -0.5 13.77614 -0.5 13.5 c -0.5 13.22386 -0.2761424 13 0 13 c h 0 17.5 m 0.2761424 17.5 0.5 17.72386 0.5 18 c 0.5 18.27614 0.2761424 18.5 0 18.5 c -0.2761424 18.5 -0.5 18.27614 -0.5 18 c -0.5 17.72386 -0.2761424 17.5 0 17.5 c h 4.5 -0.5 m 4.776143 -0.5 5 -0.2761424 5 0 c 5 0.2761424 4.776143 0.5 4.5 0.5 c 4.223857 0.5 4 0.2761424 4 0 c 4 -0.2761424 4.223857 -0.5 4.5 -0.5 c h 4.5 4 m 4.776143 4 5 4.223857 5 4.5 c 5 4.776143 4.776143 5 4.5 5 c 4.223857 5 4 4.776143 4 4.5 c 4 4.223857 4.223857 4 4.5 4 c h 4.5 8.5 m 4.776143 8.5 5 8.723858 5 9 c 5 9.276142 4.776143 9.5 4.5 9.5 c 4.223857 9.5 4 9.276142 4 9 c 4 8.723858 4.223857 8.5 4.5 8.5 c h 4.5 13 m 4.776143 13 5 13.22386 5 13.5 c 5 13.77614 4.776143 14 4.5 14 c 4.223857 14 4 13.77614 4 13.5 c 4 13.22386 4.223857 13 4.5 13 c h 4.5 17.5 m 4.776143 17.5 5 17.72386 5 18 c 5 18.27614 4.776143 18.5 4.5 18.5 c 4.223857 18.5 4 18.27614 4 18 c 4 17.72386 4.223857 17.5 4.5 17.5 c h 9 -0.5 m 9.276142 -0.5 9.5 -0.2761424 9.5 0 c 9.5 0.2761424 9.276142 0.5 9 0.5 c 8.723858 0.5 8.5 0.2761424 8.5 0 c 8.5 -0.2761424 8.723858 -0.5 9 -0.5 c h 9 4 m 9.276142 4 9.5 4.223857 9.5 4.5 c 9.5 4.776143 9.276142 5 9 5 c 8.723858 5 8.5 4.776143 8.5 4.5 c 8.5 4.223857 8.723858 4 9 4 c h 9 8.5 m 9.276142 8.5 9.5 8.723858 9.5 9 c 9.5 9.276142 9.276142 9.5 9 9.5 c 8.723858 9.5 8.5 9.276142 8.5 9 c 8.5 8.723858 8.723858 8.5 9 8.5 c h 9 13 m 9.276142 13 9.5 13.22386 9.5 13.5 c 9.5 13.77614 9.276142 14 9 14 c 8.723858 14 8.5 13.77614 8.5 13.5 c 8.5 13.22386 8.723858 13 9 13 c h 9 17.5 m 9.276142 17.5 9.5 17.72386 9.5 18 c 9.5 18.27614 9.276142 18.5 9 18.5 c 8.723858 18.5 8.5 18.27614 8.5 18 c 8.5 17.72386 8.723858 17.5 9 17.5 c h 13.5 -0.5 m 13.77614 -0.5 14 -0.2761424 14 0 c 14 0.2761424 13.77614 0.5 13.5 0.5 c 13.22386 0.5 13 0.2761424 13 0 c 13 -0.2761424 13.22386 -0.5 13.5 -0.5 c h 13.5 4 m 13.77614 4 14 4.223857 14 4.5 c 14 4.776143 13.77614 5 13.5 5 c 13.22386 5 13 4.776143 13 4.5 c 13 4.223857 13.22386 4 13.5 4 c h 13.5 8.5 m 13.77614 8.5 14 8.723858 14 9 c 14 9.276142 13.77614 9.5 13.5 9.5 c 13.22386 9.5 13 9.276142 13 9 c 13 8.723858 13.22386 8.5 13.5 8.5 c h 13.5 13 m 13.77614 13 14 13.22386 14 13.5 c 14 13.77614 13.77614 14 13.5 14 c 13.22386 14 13 13.77614 13 13.5 c 13 13.22386 13.22386 13 13.5 13 c h 13.5 17.5 m 13.77614 17.5 14 17.72386 14 18 c 14 18.27614 13.77614 18.5 13.5 18.5 c 13.22386 18.5 13 18.27614 13 18 c 13 17.72386 13.22386 17.5 13.5 17.5 c h 18 -0.5 m 18.27614 -0.5 18.5 -0.2761424 18.5 0 c 18.5 0.2761424 18.27614 0.5 18 0.5 c 17.72386 0.5 17.5 0.2761424 17.5 0 c 17.5 -0.2761424 17.72386 -0.5 18 -0.5 c h 18 4 m 18.27614 4 18.5 4.223857 18.5 4.5 c 18.5 4.776143 18.27614 5 18 5 c 17.72386 5 17.5 4.776143 17.5 4.5 c 17.5 4.223857 17.72386 4 18 4 c h 18 8.5 m 18.27614 8.5 18.5 8.723858 18.5 9 c 18.5 9.276142 18.27614 9.5 18 9.5 c 17.72386 9.5 17.5 9.276142 17.5 9 c 17.5 8.723858 17.72386 8.5 18 8.5 c h 18 13 m 18.27614 13 18.5 13.22386 18.5 13.5 c 18.5 13.77614 18.27614 14 18 14 c 17.72386 14 17.5 13.77614 17.5 13.5 c 17.5 13.22386 17.72386 13 18 13 c h 18 17.5 m 18.27614 17.5 18.5 17.72386 18.5 18 c 18.5 18.27614 18.27614 18.5 18 18.5 c 17.72386 18.5 17.5 18.27614 17.5 18 c 17.5 17.72386 17.72386 17.5 18 17.5 c h 2.25 1.75 m 2.526142 1.75 2.75 1.973858 2.75 2.25 c 2.75 2.526142 2.526142 2.75 2.25 2.75 c 1.973858 2.75 1.75 2.526142 1.75 2.25 c 1.75 1.973858 1.973858 1.75 2.25 1.75 c h 2.25 6.25 m 2.526142 6.25 2.75 6.473857 2.75 6.75 c 2.75 7.026143 2.526142 7.25 2.25 7.25 c 1.973858 7.25 1.75 7.026143 1.75 6.75 c 1.75 6.473857 1.973858 6.25 2.25 6.25 c h 2.25 10.75 m 2.526142 10.75 2.75 10.97386 2.75 11.25 c 2.75 11.52614 2.526142 11.75 2.25 11.75 c 1.973858 11.75 1.75 11.52614 1.75 11.25 c 1.75 10.97386 1.973858 10.75 2.25 10.75 c h 2.25 15.25 m 2.526142 15.25 2.75 15.47386 2.75 15.75 c 2.75 16.02614 2.526142 16.25 2.25 16.25 c 1.973858 16.25 1.75 16.02614 1.75 15.75 c 1.75 15.47386 1.973858 15.25 2.25 15.25 c h 6.75 1.75 m 7.026143 1.75 7.25 1.973858 7.25 2.25 c 7.25 2.526142 7.026143 2.75 6.75 2.75 c 6.473857 2.75 6.25 2.526142 6.25 2.25 c 6.25 1.973858 6.473857 1.75 6.75 1.75 c h 6.75 6.25 m 7.026143 6.25 7.25 6.473857 7.25 6.75 c 7.25 7.026143 7.026143 7.25 6.75 7.25 c 6.473857 7.25 6.25 7.026143 6.25 6.75 c 6.25 6.473857 6.473857 6.25 6.75 6.25 c h 6.75 10.75 m 7.026143 10.75 7.25 10.97386 7.25 11.25 c 7.25 11.52614 7.026143 11.75 6.75 11.75 c 6.473857 11.75 6.25 11.52614 6.25 11.25 c 6.25 10.97386 6.473857 10.75 6.75 10.75 c h 6.75 15.25 m 7.026143 15.25 7.25 15.47386 7.25 15.75 c 7.25 16.02614 7.026143 16.25 6.75 16.25 c 6.473857 16.25 6.25 16.02614 6.25 15.75 c 6.25 15.47386 6.473857 15.25 6.75 15.25 c h 11.25 1.75 m 11.52614 1.75 11.75 1.973858 11.75 2.25 c 11.75 2.526142 11.52614 2.75 11.25 2.75 c 10.97386 2.75 10.75 2.526142 10.75 2.25 c 10.75 1.973858 10.97386 1.75 11.25 1.75 c h 11.25 6.25 m 11.52614 6.25 11.75 6.473857 11.75 6.75 c 11.75 7.026143 11.52614 7.25 11.25 7.25 c 10.97386 7.25 10.75 7.026143 10.75 6.75 c 10.75 6.473857 10.97386 6.25 11.25 6.25 c h 11.25 10.75 m 11.52614 10.75 11.75 10.97386 11.75 11.25 c 11.75 11.52614 11.52614 11.75 11.25 11.75 c 10.97386 11.75 10.75 11.52614 10.75 11.25 c 10.75 10.97386 10.97386 10.75 11.25 10.75 c h 11.25 15.25 m 11.52614 15.25 11.75 15.47386 11.75 15.75 c 11.75 16.02614 11.52614 16.25 11.25 16.25 c 10.97386 16.25 10.75 16.02614 10.75 15.75 c 10.75 15.47386 10.97386 15.25 11.25 15.25 c h 15.75 1.75 m 16.02614 1.75 16.25 1.973858 16.25 2.25 c 16.25 2.526142 16.02614 2.75 15.75 2.75 c 15.47386 2.75 15.25 2.526142 15.25 2.25 c 15.25 1.973858 15.47386 1.75 15.75 1.75 c h 15.75 6.25 m 16.02614 6.25 16.25 6.473857 16.25 6.75 c 16.25 7.026143 16.02614 7.25 15.75 7.25 c 15.47386 7.25 15.25 7.026143 15.25 6.75 c 15.25 6.473857 15.47386 6.25 15.75 6.25 c h 15.75 10.75 m 16.02614 10.75 16.25 10.97386 16.25 11.25 c 16.25 11.52614 16.02614 11.75 15.75 11.75 c 15.47386 11.75 15.25 11.52614 15.25 11.25 c 15.25 10.97386 15.47386 10.75 15.75 10.75 c h 15.75 15.25 m 16.02614 15.25 16.25 15.47386 16.25 15.75 c 16.25 16.02614 16.02614 16.25 15.75 16.25 c 15.47386 16.25 15.25 16.02614 15.25 15.75 c 15.25 15.47386 15.47386 15.25 15.75 15.25 c h
|
|
|
{strokecolor} rg f
|
|
|
endstream'''
|
|
|
,
|
|
|
'20Dots':'''<</Length 13441/Type/Pattern/PatternType 1/PaintType 1/TilingType 1/Resources<<>>
|
|
|
/Matrix[1 0 0 1 0 0]/BBox[0 0 18 18]/XStep 18/YStep 18>>\nstream\n
|
|
|
{fillcolor} rg 0 0 18 18 re f {strokecolor} RG 1 w 0 -0.5 m 0.2761424 -0.5 0.5 -0.2761424 0.5 0 c 0.5 0.2761424 0.2761424 0.5 0 0.5 c -0.2761424 0.5 -0.5 0.2761424 -0.5 0 c -0.5 -0.2761424 -0.2761424 -0.5 0 -0.5 c h 0 2.500001 m 0.2761424 2.500001 0.5 2.723858 0.5 3.000001 c 0.5 3.276143 0.2761424 3.500001 0 3.500001 c -0.2761424 3.500001 -0.5 3.276143 -0.5 3.000001 c -0.5 2.723858 -0.2761424 2.500001 0 2.500001 c h 0 5.5 m 0.2761424 5.5 0.5 5.723857 0.5 6 c 0.5 6.276142 0.2761424 6.5 0 6.5 c -0.2761424 6.5 -0.5 6.276142 -0.5 6 c -0.5 5.723857 -0.2761424 5.5 0 5.5 c h 0 8.5 m 0.2761424 8.5 0.5 8.723858 0.5 9 c 0.5 9.276142 0.2761424 9.5 0 9.5 c -0.2761424 9.5 -0.5 9.276142 -0.5 9 c -0.5 8.723858 -0.2761424 8.5 0 8.5 c h 0 11.5 m 0.2761424 11.5 0.5 11.72386 0.5 12 c 0.5 12.27614 0.2761424 12.5 0 12.5 c -0.2761424 12.5 -0.5 12.27614 -0.5 12 c -0.5 11.72386 -0.2761424 11.5 0 11.5 c h 0 14.5 m 0.2761424 14.5 0.5 14.72386 0.5 15 c 0.5 15.27614 0.2761424 15.5 0 15.5 c -0.2761424 15.5 -0.5 15.27614 -0.5 15 c -0.5 14.72386 -0.2761424 14.5 0 14.5 c h 0 17.5 m 0.2761424 17.5 0.5 17.72386 0.5 18 c 0.5 18.27614 0.2761424 18.5 0 18.5 c -0.2761424 18.5 -0.5 18.27614 -0.5 18 c -0.5 17.72386 -0.2761424 17.5 0 17.5 c h 3.000001 -0.5 m 3.276143 -0.5 3.500001 -0.2761424 3.500001 0 c 3.500001 0.2761424 3.276143 0.5 3.000001 0.5 c 2.723858 0.5 2.500001 0.2761424 2.500001 0 c 2.500001 -0.2761424 2.723858 -0.5 3.000001 -0.5 c h 3.000001 2.500001 m 3.276143 2.500001 3.500001 2.723858 3.500001 3.000001 c 3.500001 3.276143 3.276143 3.500001 3.000001 3.500001 c 2.723858 3.500001 2.500001 3.276143 2.500001 3.000001 c 2.500001 2.723858 2.723858 2.500001 3.000001 2.500001 c h 3.000001 5.5 m 3.276143 5.5 3.500001 5.723857 3.500001 6 c 3.500001 6.276142 3.276143 6.5 3.000001 6.5 c 2.723858 6.5 2.500001 6.276142 2.500001 6 c 2.500001 5.723857 2.723858 5.5 3.000001 5.5 c h 3.000001 8.5 m 3.276143 8.5 3.500001 8.723858 3.500001 9 c 3.500001 9.276142 3.276143 9.5 3.000001 9.5 c 2.723858 9.5 2.500001 9.276142 2.500001 9 c 2.500001 8.723858 2.723858 8.5 3.000001 8.5 c h 3.000001 11.5 m 3.276143 11.5 3.500001 11.72386 3.500001 12 c 3.500001 12.27614 3.276143 12.5 3.000001 12.5 c 2.723858 12.5 2.500001 12.27614 2.500001 12 c 2.500001 11.72386 2.723858 11.5 3.000001 11.5 c h 3.000001 14.5 m 3.276143 14.5 3.500001 14.72386 3.500001 15 c 3.500001 15.27614 3.276143 15.5 3.000001 15.5 c 2.723858 15.5 2.500001 15.27614 2.500001 15 c 2.500001 14.72386 2.723858 14.5 3.000001 14.5 c h 3.000001 17.5 m 3.276143 17.5 3.500001 17.72386 3.500001 18 c 3.500001 18.27614 3.276143 18.5 3.000001 18.5 c 2.723858 18.5 2.500001 18.27614 2.500001 18 c 2.500001 17.72386 2.723858 17.5 3.000001 17.5 c h 6 -0.5 m 6.276142 -0.5 6.5 -0.2761424 6.5 0 c 6.5 0.2761424 6.276142 0.5 6 0.5 c 5.723857 0.5 5.5 0.2761424 5.5 0 c 5.5 -0.2761424 5.723857 -0.5 6 -0.5 c h 6 2.500001 m 6.276142 2.500001 6.5 2.723858 6.5 3.000001 c 6.5 3.276143 6.276142 3.500001 6 3.500001 c 5.723857 3.500001 5.5 3.276143 5.5 3.000001 c 5.5 2.723858 5.723857 2.500001 6 2.500001 c h 6 5.5 m 6.276142 5.5 6.5 5.723857 6.5 6 c 6.5 6.276142 6.276142 6.5 6 6.5 c 5.723857 6.5 5.5 6.276142 5.5 6 c 5.5 5.723857 5.723857 5.5 6 5.5 c h 6 8.5 m 6.276142 8.5 6.5 8.723858 6.5 9 c 6.5 9.276142 6.276142 9.5 6 9.5 c 5.723857 9.5 5.5 9.276142 5.5 9 c 5.5 8.723858 5.723857 8.5 6 8.5 c h 6 11.5 m 6.276142 11.5 6.5 11.72386 6.5 12 c 6.5 12.27614 6.276142 12.5 6 12.5 c 5.723857 12.5 5.5 12.27614 5.5 12 c 5.5 11.72386 5.723857 11.5 6 11.5 c h 6 14.5 m 6.276142 14.5 6.5 14.72386 6.5 15 c 6.5 15.27614 6.276142 15.5 6 15.5 c 5.723857 15.5 5.5 15.27614 5.5 15 c 5.5 14.72386 5.723857 14.5 6 14.5 c h 6 17.5 m 6.276142 17.5 6.5 17.72386 6.5 18 c 6.5 18.27614 6.276142 18.5 6 18.5 c 5.723857 18.5 5.5 18.27614 5.5 18 c 5.5 17.72386 5.723857 17.5 6 17.5 c h 9 -0.5 m 9.276142 -0.5 9.5 -0.2761424 9.5 0 c 9.5 0.2761424 9.276142 0.5 9 0.5 c 8.723858 0.5 8.5 0.2761424 8.5 0 c 8.5 -0.2761424 8.723858 -0.5 9 -0.5 c h 9 2.500001 m 9.276142 2.500001 9.5 2.723858 9.5 3.000001 c 9.5 3.276143 9.276142 3.500001 9 3.500001 c 8.723858 3.500001 8.5 3.276143 8.5 3.000001 c 8.5 2.723858 8.723858 2.500001 9 2.500001 c h 9 5.5 m 9.276142 5.5 9.5 5.723857 9.5 6 c 9.5 6.276142 9.276142 6.5 9 6.5 c 8.723858 6.5 8.5 6.276142 8.5 6 c 8.5 5.723857 8.723858 5.5 9 5.5 c h 9 8.5 m 9.276142 8.5 9.5 8.723858 9.5 9 c 9.5 9.276142 9.276142 9.5 9 9.5 c 8.723858 9.5 8.5 9.276142 8.5 9 c 8.5 8.723858 8.723858 8.5 9 8.5 c h 9 11.5 m 9.276142 11.5 9.5 11.72386 9.5 12 c 9.5 12.27614 9.276142 12.5 9 12.5 c 8.723858 12.5 8.5 12.27614 8.5 12 c 8.5 11.72386 8.723858 11.5 9 11.5 c h 9 14.5 m 9.276142 14.5 9.5 14.72386 9.5 15 c 9.5 15.27614 9.276142 15.5 9 15.5 c 8.723858 15.5 8.5 15.27614 8.5 15 c 8.5 14.72386 8.723858 14.5 9 14.5 c h 9 17.5 m 9.276142 17.5 9.5 17.72386 9.5 18 c 9.5 18.27614 9.276142 18.5 9 18.5 c 8.723858 18.5 8.5 18.27614 8.5 18 c 8.5 17.72386 8.723858 17.5 9 17.5 c h 12 -0.5 m 12.27614 -0.5 12.5 -0.2761424 12.5 0 c 12.5 0.2761424 12.27614 0.5 12 0.5 c 11.72386 0.5 11.5 0.2761424 11.5 0 c 11.5 -0.2761424 11.72386 -0.5 12 -0.5 c h 12 2.500001 m 12.27614 2.500001 12.5 2.723858 12.5 3.000001 c 12.5 3.276143 12.27614 3.500001 12 3.500001 c 11.72386 3.500001 11.5 3.276143 11.5 3.000001 c 11.5 2.723858 11.72386 2.500001 12 2.500001 c h 12 5.5 m 12.27614 5.5 12.5 5.723857 12.5 6 c 12.5 6.276142 12.27614 6.5 12 6.5 c 11.72386 6.5 11.5 6.276142 11.5 6 c 11.5 5.723857 11.72386 5.5 12 5.5 c h 12 8.5 m 12.27614 8.5 12.5 8.723858 12.5 9 c 12.5 9.276142 12.27614 9.5 12 9.5 c 11.72386 9.5 11.5 9.276142 11.5 9 c 11.5 8.723858 11.72386 8.5 12 8.5 c h 12 11.5 m 12.27614 11.5 12.5 11.72386 12.5 12 c 12.5 12.27614 12.27614 12.5 12 12.5 c 11.72386 12.5 11.5 12.27614 11.5 12 c 11.5 11.72386 11.72386 11.5 12 11.5 c h 12 14.5 m 12.27614 14.5 12.5 14.72386 12.5 15 c 12.5 15.27614 12.27614 15.5 12 15.5 c 11.72386 15.5 11.5 15.27614 11.5 15 c 11.5 14.72386 11.72386 14.5 12 14.5 c h 12 17.5 m 12.27614 17.5 12.5 17.72386 12.5 18 c 12.5 18.27614 12.27614 18.5 12 18.5 c 11.72386 18.5 11.5 18.27614 11.5 18 c 11.5 17.72386 11.72386 17.5 12 17.5 c h 15 -0.5 m 15.27614 -0.5 15.5 -0.2761424 15.5 0 c 15.5 0.2761424 15.27614 0.5 15 0.5 c 14.72386 0.5 14.5 0.2761424 14.5 0 c 14.5 -0.2761424 14.72386 -0.5 15 -0.5 c h 15 2.500001 m 15.27614 2.500001 15.5 2.723858 15.5 3.000001 c 15.5 3.276143 15.27614 3.500001 15 3.500001 c 14.72386 3.500001 14.5 3.276143 14.5 3.000001 c 14.5 2.723858 14.72386 2.500001 15 2.500001 c h 15 5.5 m 15.27614 5.5 15.5 5.723857 15.5 6 c 15.5 6.276142 15.27614 6.5 15 6.5 c 14.72386 6.5 14.5 6.276142 14.5 6 c 14.5 5.723857 14.72386 5.5 15 5.5 c h 15 8.5 m 15.27614 8.5 15.5 8.723858 15.5 9 c 15.5 9.276142 15.27614 9.5 15 9.5 c 14.72386 9.5 14.5 9.276142 14.5 9 c 14.5 8.723858 14.72386 8.5 15 8.5 c h 15 11.5 m 15.27614 11.5 15.5 11.72386 15.5 12 c 15.5 12.27614 15.27614 12.5 15 12.5 c 14.72386 12.5 14.5 12.27614 14.5 12 c 14.5 11.72386 14.72386 11.5 15 11.5 c h 15 14.5 m 15.27614 14.5 15.5 14.72386 15.5 15 c 15.5 15.27614 15.27614 15.5 15 15.5 c 14.72386 15.5 14.5 15.27614 14.5 15 c 14.5 14.72386 14.72386 14.5 15 14.5 c h 15 17.5 m 15.27614 17.5 15.5 17.72386 15.5 18 c 15.5 18.27614 15.27614 18.5 15 18.5 c 14.72386 18.5 14.5 18.27614 14.5 18 c 14.5 17.72386 14.72386 17.5 15 17.5 c h 18 -0.5 m 18.27614 -0.5 18.5 -0.2761424 18.5 0 c 18.5 0.2761424 18.27614 0.5 18 0.5 c 17.72386 0.5 17.5 0.2761424 17.5 0 c 17.5 -0.2761424 17.72386 -0.5 18 -0.5 c h 18 2.500001 m 18.27614 2.500001 18.5 2.723858 18.5 3.000001 c 18.5 3.276143 18.27614 3.500001 18 3.500001 c 17.72386 3.500001 17.5 3.276143 17.5 3.000001 c 17.5 2.723858 17.72386 2.500001 18 2.500001 c h 18 5.5 m 18.27614 5.5 18.5 5.723857 18.5 6 c 18.5 6.276142 18.27614 6.5 18 6.5 c 17.72386 6.5 17.5 6.276142 17.5 6 c 17.5 5.723857 17.72386 5.5 18 5.5 c h 18 8.5 m 18.27614 8.5 18.5 8.723858 18.5 9 c 18.5 9.276142 18.27614 9.5 18 9.5 c 17.72386 9.5 17.5 9.276142 17.5 9 c 17.5 8.723858 17.72386 8.5 18 8.5 c h 18 11.5 m 18.27614 11.5 18.5 11.72386 18.5 12 c 18.5 12.27614 18.27614 12.5 18 12.5 c 17.72386 12.5 17.5 12.27614 17.5 12 c 17.5 11.72386 17.72386 11.5 18 11.5 c h 18 14.5 m 18.27614 14.5 18.5 14.72386 18.5 15 c 18.5 15.27614 18.27614 15.5 18 15.5 c 17.72386 15.5 17.5 15.27614 17.5 15 c 17.5 14.72386 17.72386 14.5 18 14.5 c h 18 17.5 m 18.27614 17.5 18.5 17.72386 18.5 18 c 18.5 18.27614 18.27614 18.5 18 18.5 c 17.72386 18.5 17.5 18.27614 17.5 18 c 17.5 17.72386 17.72386 17.5 18 17.5 c h 1.5 1 m 1.776143 1 2 1.223858 2 1.5 c 2 1.776143 1.776143 2 1.5 2 c 1.223858 2 1 1.776143 1 1.5 c 1 1.223858 1.223858 1 1.5 1 c h 1.5 4 m 1.776143 4 2 4.223857 2 4.5 c 2 4.776143 1.776143 5 1.5 5 c 1.223858 5 1 4.776143 1 4.5 c 1 4.223857 1.223858 4 1.5 4 c h 1.5 7 m 1.776143 7 2 7.223858 2 7.5 c 2 7.776143 1.776143 8 1.5 8 c 1.223858 8 1 7.776143 1 7.5 c 1 7.223858 1.223858 7 1.5 7 c h 1.5 10 m 1.776143 10 2 10.22386 2 10.5 c 2 10.77614 1.776143 11 1.5 11 c 1.223858 11 1 10.77614 1 10.5 c 1 10.22386 1.223858 10 1.5 10 c h 1.5 13 m 1.776143 13 2 13.22386 2 13.5 c 2 13.77614 1.776143 14 1.5 14 c 1.223858 14 1 13.77614 1 13.5 c 1 13.22386 1.223858 13 1.5 13 c h 1.5 16 m 1.776143 16 2 16.22386 2 16.5 c 2 16.77614 1.776143 17 1.5 17 c 1.223858 17 1 16.77614 1 16.5 c 1 16.22386 1.223858 16 1.5 16 c h 4.5 1 m 4.776143 1 5 1.223858 5 1.5 c 5 1.776143 4.776143 2 4.5 2 c 4.223857 2 4 1.776143 4 1.5 c 4 1.223858 4.223857 1 4.5 1 c h 4.5 4 m 4.776143 4 5 4.223857 5 4.5 c 5 4.776143 4.776143 5 4.5 5 c 4.223857 5 4 4.776143 4 4.5 c 4 4.223857 4.223857 4 4.5 4 c h 4.5 7 m 4.776143 7 5 7.223858 5 7.5 c 5 7.776143 4.776143 8 4.5 8 c 4.223857 8 4 7.776143 4 7.5 c 4 7.223858 4.223857 7 4.5 7 c h 4.5 10 m 4.776143 10 5 10.22386 5 10.5 c 5 10.77614 4.776143 11 4.5 11 c 4.223857 11 4 10.77614 4 10.5 c 4 10.22386 4.223857 10 4.5 10 c h 4.5 13 m 4.776143 13 5 13.22386 5 13.5 c 5 13.77614 4.776143 14 4.5 14 c 4.223857 14 4 13.77614 4 13.5 c 4 13.22386 4.223857 13 4.5 13 c h 4.5 16 m 4.776143 16 5 16.22386 5 16.5 c 5 16.77614 4.776143 17 4.5 17 c 4.223857 17 4 16.77614 4 16.5 c 4 16.22386 4.223857 16 4.5 16 c h 7.5 1 m 7.776143 1 8 1.223858 8 1.5 c 8 1.776143 7.776143 2 7.5 2 c 7.223858 2 7 1.776143 7 1.5 c 7 1.223858 7.223858 1 7.5 1 c h 7.5 4 m 7.776143 4 8 4.223857 8 4.5 c 8 4.776143 7.776143 5 7.5 5 c 7.223858 5 7 4.776143 7 4.5 c 7 4.223857 7.223858 4 7.5 4 c h 7.5 7 m 7.776143 7 8 7.223858 8 7.5 c 8 7.776143 7.776143 8 7.5 8 c 7.223858 8 7 7.776143 7 7.5 c 7 7.223858 7.223858 7 7.5 7 c h 7.5 10 m 7.776143 10 8 10.22386 8 10.5 c 8 10.77614 7.776143 11 7.5 11 c 7.223858 11 7 10.77614 7 10.5 c 7 10.22386 7.223858 10 7.5 10 c h 7.5 13 m 7.776143 13 8 13.22386 8 13.5 c 8 13.77614 7.776143 14 7.5 14 c 7.223858 14 7 13.77614 7 13.5 c 7 13.22386 7.223858 13 7.5 13 c h 7.5 16 m 7.776143 16 8 16.22386 8 16.5 c 8 16.77614 7.776143 17 7.5 17 c 7.223858 17 7 16.77614 7 16.5 c 7 16.22386 7.223858 16 7.5 16 c h 10.5 1 m 10.77614 1 11 1.223858 11 1.5 c 11 1.776143 10.77614 2 10.5 2 c 10.22386 2 10 1.776143 10 1.5 c 10 1.223858 10.22386 1 10.5 1 c h 10.5 4 m 10.77614 4 11 4.223857 11 4.5 c 11 4.776143 10.77614 5 10.5 5 c 10.22386 5 10 4.776143 10 4.5 c 10 4.223857 10.22386 4 10.5 4 c h 10.5 7 m 10.77614 7 11 7.223858 11 7.5 c 11 7.776143 10.77614 8 10.5 8 c 10.22386 8 10 7.776143 10 7.5 c 10 7.223858 10.22386 7 10.5 7 c h 10.5 10 m 10.77614 10 11 10.22386 11 10.5 c 11 10.77614 10.77614 11 10.5 11 c 10.22386 11 10 10.77614 10 10.5 c 10 10.22386 10.22386 10 10.5 10 c h 10.5 13 m 10.77614 13 11 13.22386 11 13.5 c 11 13.77614 10.77614 14 10.5 14 c 10.22386 14 10 13.77614 10 13.5 c 10 13.22386 10.22386 13 10.5 13 c h 10.5 16 m 10.77614 16 11 16.22386 11 16.5 c 11 16.77614 10.77614 17 10.5 17 c 10.22386 17 10 16.77614 10 16.5 c 10 16.22386 10.22386 16 10.5 16 c h 13.5 1 m 13.77614 1 14 1.223858 14 1.5 c 14 1.776143 13.77614 2 13.5 2 c 13.22386 2 13 1.776143 13 1.5 c 13 1.223858 13.22386 1 13.5 1 c h 13.5 4 m 13.77614 4 14 4.223857 14 4.5 c 14 4.776143 13.77614 5 13.5 5 c 13.22386 5 13 4.776143 13 4.5 c 13 4.223857 13.22386 4 13.5 4 c h 13.5 7 m 13.77614 7 14 7.223858 14 7.5 c 14 7.776143 13.77614 8 13.5 8 c 13.22386 8 13 7.776143 13 7.5 c 13 7.223858 13.22386 7 13.5 7 c h 13.5 10 m 13.77614 10 14 10.22386 14 10.5 c 14 10.77614 13.77614 11 13.5 11 c 13.22386 11 13 10.77614 13 10.5 c 13 10.22386 13.22386 10 13.5 10 c h 13.5 13 m 13.77614 13 14 13.22386 14 13.5 c 14 13.77614 13.77614 14 13.5 14 c 13.22386 14 13 13.77614 13 13.5 c 13 13.22386 13.22386 13 13.5 13 c h 13.5 16 m 13.77614 16 14 16.22386 14 16.5 c 14 16.77614 13.77614 17 13.5 17 c 13.22386 17 13 16.77614 13 16.5 c 13 16.22386 13.22386 16 13.5 16 c h 16.5 1 m 16.77614 1 17 1.223858 17 1.5 c 17 1.776143 16.77614 2 16.5 2 c 16.22386 2 16 1.776143 16 1.5 c 16 1.223858 16.22386 1 16.5 1 c h 16.5 4 m 16.77614 4 17 4.223857 17 4.5 c 17 4.776143 16.77614 5 16.5 5 c 16.22386 5 16 4.776143 16 4.5 c 16 4.223857 16.22386 4 16.5 4 c h 16.5 7 m 16.77614 7 17 7.223858 17 7.5 c 17 7.776143 16.77614 8 16.5 8 c 16.22386 8 16 7.776143 16 7.5 c 16 7.223858 16.22386 7 16.5 7 c h 16.5 10 m 16.77614 10 17 10.22386 17 10.5 c 17 10.77614 16.77614 11 16.5 11 c 16.22386 11 16 10.77614 16 10.5 c 16 10.22386 16.22386 10 16.5 10 c h 16.5 13 m 16.77614 13 17 13.22386 17 13.5 c 17 13.77614 16.77614 14 16.5 14 c 16.22386 14 16 13.77614 16 13.5 c 16 13.22386 16.22386 13 16.5 13 c h 16.5 16 m 16.77614 16 17 16.22386 17 16.5 c 17 16.77614 16.77614 17 16.5 17 c 16.22386 17 16 16.77614 16 16.5 c 16 16.22386 16.22386 16 16.5 16 c h
|
|
|
{strokecolor} rg f
|
|
|
endstream'''
|
|
|
,
|
|
|
'30Dots':'''<</Length 25547/Type/Pattern/PatternType 1/PaintType 1/TilingType 1/Resources<<>>
|
|
|
/Matrix[1 0 0 1 0 0]/BBox[0 0 18 18]/XStep 18/YStep 18>>\nstream\n
|
|
|
{fillcolor} rg 0 0 18 18 re f {strokecolor} RG
|
|
|
1 w 0 -0.5 m 0.2761424 -0.5 0.5 -0.2761424 0.5 0 c 0.5 0.2761424 0.2761424 0.5 0 0.5 c -0.2761424 0.5 -0.5 0.2761424 -0.5 0 c -0.5 -0.2761424 -0.2761424 -0.5 0 -0.5 c h 0 2.071428 m 0.2761424 2.071428 0.5 2.295285 0.5 2.571428 c 0.5 2.84757 0.2761424 3.071428 0 3.071428 c -0.2761424 3.071428 -0.5 2.84757 -0.5 2.571428 c -0.5 2.295285 -0.2761424 2.071428 0 2.071428 c h 0 4.642858 m 0.2761424 4.642858 0.5 4.866715 0.5 5.142858 c 0.5 5.419 0.2761424 5.642858 0 5.642858 c -0.2761424 5.642858 -0.5 5.419 -0.5 5.142858 c -0.5 4.866715 -0.2761424 4.642858 0 4.642858 c h 0 7.214287 m 0.2761424 7.214287 0.5 7.438144 0.5 7.714287 c 0.5 7.990429 0.2761424 8.214287 0 8.214287 c -0.2761424 8.214287 -0.5 7.990429 -0.5 7.714287 c -0.5 7.438144 -0.2761424 7.214287 0 7.214287 c h 0 9.785715 m 0.2761424 9.785715 0.5 10.00957 0.5 10.28572 c 0.5 10.56186 0.2761424 10.78572 0 10.78572 c -0.2761424 10.78572 -0.5 10.56186 -0.5 10.28572 c -0.5 10.00957 -0.2761424 9.785715 0 9.785715 c h 0 12.35714 m 0.2761424 12.35714 0.5 12.581 0.5 12.85714 c 0.5 13.13328 0.2761424 13.35714 0 13.35714 c -0.2761424 13.35714 -0.5 13.13328 -0.5 12.85714 c -0.5 12.581 -0.2761424 12.35714 0 12.35714 c h 0 14.92857 m 0.2761424 14.92857 0.5 15.15243 0.5 15.42857 c 0.5 15.70471 0.2761424 15.92857 0 15.92857 c -0.2761424 15.92857 -0.5 15.70471 -0.5 15.42857 c -0.5 15.15243 -0.2761424 14.92857 0 14.92857 c h 0 17.5 m 0.2761424 17.5 0.5 17.72386 0.5 18 c 0.5 18.27614 0.2761424 18.5 0 18.5 c -0.2761424 18.5 -0.5 18.27614 -0.5 18 c -0.5 17.72386 -0.2761424 17.5 0 17.5 c h 2.571428 -0.5 m 2.84757 -0.5 3.071428 -0.2761424 3.071428 0 c 3.071428 0.2761424 2.84757 0.5 2.571428 0.5 c 2.295285 0.5 2.071428 0.2761424 2.071428 0 c 2.071428 -0.2761424 2.295285 -0.5 2.571428 -0.5 c h 2.571428 2.071428 m 2.84757 2.071428 3.071428 2.295285 3.071428 2.571428 c 3.071428 2.84757 2.84757 3.071428 2.571428 3.071428 c 2.295285 3.071428 2.071428 2.84757 2.071428 2.571428 c 2.071428 2.295285 2.295285 2.071428 2.571428 2.071428 c h 2.571428 4.642858 m 2.84757 4.642858 3.071428 4.866715 3.071428 5.142858 c 3.071428 5.419 2.84757 5.642858 2.571428 5.642858 c 2.295285 5.642858 2.071428 5.419 2.071428 5.142858 c 2.071428 4.866715 2.295285 4.642858 2.571428 4.642858 c h 2.571428 7.214287 m 2.84757 7.214287 3.071428 7.438144 3.071428 7.714287 c 3.071428 7.990429 2.84757 8.214287 2.571428 8.214287 c 2.295285 8.214287 2.071428 7.990429 2.071428 7.714287 c 2.071428 7.438144 2.295285 7.214287 2.571428 7.214287 c h 2.571428 9.785715 m 2.84757 9.785715 3.071428 10.00957 3.071428 10.28572 c 3.071428 10.56186 2.84757 10.78572 2.571428 10.78572 c 2.295285 10.78572 2.071428 10.56186 2.071428 10.28572 c 2.071428 10.00957 2.295285 9.785715 2.571428 9.785715 c h 2.571428 12.35714 m 2.84757 12.35714 3.071428 12.581 3.071428 12.85714 c 3.071428 13.13328 2.84757 13.35714 2.571428 13.35714 c 2.295285 13.35714 2.071428 13.13328 2.071428 12.85714 c 2.071428 12.581 2.295285 12.35714 2.571428 12.35714 c h 2.571428 14.92857 m 2.84757 14.92857 3.071428 15.15243 3.071428 15.42857 c 3.071428 15.70471 2.84757 15.92857 2.571428 15.92857 c 2.295285 15.92857 2.071428 15.70471 2.071428 15.42857 c 2.071428 15.15243 2.295285 14.92857 2.571428 14.92857 c h 2.571428 17.5 m 2.84757 17.5 3.071428 17.72386 3.071428 18 c 3.071428 18.27614 2.84757 18.5 2.571428 18.5 c 2.295285 18.5 2.071428 18.27614 2.071428 18 c 2.071428 17.72386 2.295285 17.5 2.571428 17.5 c h 5.142858 -0.5 m 5.419 -0.5 5.642858 -0.2761424 5.642858 0 c 5.642858 0.2761424 5.419 0.5 5.142858 0.5 c 4.866715 0.5 4.642858 0.2761424 4.642858 0 c 4.642858 -0.2761424 4.866715 -0.5 5.142858 -0.5 c h 5.142858 2.071428 m 5.419 2.071428 5.642858 2.295285 5.642858 2.571428 c 5.642858 2.84757 5.419 3.071428 5.142858 3.071428 c 4.866715 3.071428 4.642858 2.84757 4.642858 2.571428 c 4.642858 2.295285 4.866715 2.071428 5.142858 2.071428 c h 5.142858 4.642858 m 5.419 4.642858 5.642858 4.866715 5.642858 5.142858 c 5.642858 5.419 5.419 5.642858 5.142858 5.642858 c 4.866715 5.642858 4.642858 5.419 4.642858 5.142858 c 4.642858 4.866715 4.866715 4.642858 5.142858 4.642858 c h 5.142858 7.214287 m 5.419 7.214287 5.642858 7.438144 5.642858 7.714287 c 5.642858 7.990429 5.419 8.214287 5.142858 8.214287 c 4.866715 8.214287 4.642858 7.990429 4.642858 7.714287 c 4.642858 7.438144 4.866715 7.214287 5.142858 7.214287 c h 5.142858 9.785715 m 5.419 9.785715 5.642858 10.00957 5.642858 10.28572 c 5.642858 10.56186 5.419 10.78572 5.142858 10.78572 c 4.866715 10.78572 4.642858 10.56186 4.642858 10.28572 c 4.642858 10.00957 4.866715 9.785715 5.142858 9.785715 c h 5.142858 12.35714 m 5.419 12.35714 5.642858 12.581 5.642858 12.85714 c 5.642858 13.13328 5.419 13.35714 5.142858 13.35714 c 4.866715 13.35714 4.642858 13.13328 4.642858 12.85714 c 4.642858 12.581 4.866715 12.35714 5.142858 12.35714 c h 5.142858 14.92857 m 5.419 14.92857 5.642858 15.15243 5.642858 15.42857 c 5.642858 15.70471 5.419 15.92857 5.142858 15.92857 c 4.866715 15.92857 4.642858 15.70471 4.642858 15.42857 c 4.642858 15.15243 4.866715 14.92857 5.142858 14.92857 c h 5.142858 17.5 m 5.419 17.5 5.642858 17.72386 5.642858 18 c 5.642858 18.27614 5.419 18.5 5.142858 18.5 c 4.866715 18.5 4.642858 18.27614 4.642858 18 c 4.642858 17.72386 4.866715 17.5 5.142858 17.5 c h 7.714287 -0.5 m 7.990429 -0.5 8.214287 -0.2761424 8.214287 0 c 8.214287 0.2761424 7.990429 0.5 7.714287 0.5 c 7.438144 0.5 7.214287 0.2761424 7.214287 0 c 7.214287 -0.2761424 7.438144 -0.5 7.714287 -0.5 c h 7.714287 2.071428 m 7.990429 2.071428 8.214287 2.295285 8.214287 2.571428 c 8.214287 2.84757 7.990429 3.071428 7.714287 3.071428 c 7.438144 3.071428 7.214287 2.84757 7.214287 2.571428 c 7.214287 2.295285 7.438144 2.071428 7.714287 2.071428 c h 7.714287 4.642858 m 7.990429 4.642858 8.214287 4.866715 8.214287 5.142858 c 8.214287 5.419 7.990429 5.642858 7.714287 5.642858 c 7.438144 5.642858 7.214287 5.419 7.214287 5.142858 c 7.214287 4.866715 7.438144 4.642858 7.714287 4.642858 c h 7.714287 7.214287 m 7.990429 7.214287 8.214287 7.438144 8.214287 7.714287 c 8.214287 7.990429 7.990429 8.214287 7.714287 8.214287 c 7.438144 8.214287 7.214287 7.990429 7.214287 7.714287 c 7.214287 7.438144 7.438144 7.214287 7.714287 7.214287 c h 7.714287 9.785715 m 7.990429 9.785715 8.214287 10.00957 8.214287 10.28572 c 8.214287 10.56186 7.990429 10.78572 7.714287 10.78572 c 7.438144 10.78572 7.214287 10.56186 7.214287 10.28572 c 7.214287 10.00957 7.438144 9.785715 7.714287 9.785715 c h 7.714287 12.35714 m 7.990429 12.35714 8.214287 12.581 8.214287 12.85714 c 8.214287 13.13328 7.990429 13.35714 7.714287 13.35714 c 7.438144 13.35714 7.214287 13.13328 7.214287 12.85714 c 7.214287 12.581 7.438144 12.35714 7.714287 12.35714 c h 7.714287 14.92857 m 7.990429 14.92857 8.214287 15.15243 8.214287 15.42857 c 8.214287 15.70471 7.990429 15.92857 7.714287 15.92857 c 7.438144 15.92857 7.214287 15.70471 7.214287 15.42857 c 7.214287 15.15243 7.438144 14.92857 7.714287 14.92857 c h 7.714287 17.5 m 7.990429 17.5 8.214287 17.72386 8.214287 18 c 8.214287 18.27614 7.990429 18.5 7.714287 18.5 c 7.438144 18.5 7.214287 18.27614 7.214287 18 c 7.214287 17.72386 7.438144 17.5 7.714287 17.5 c h 10.28572 -0.5 m 10.56186 -0.5 10.78572 -0.2761424 10.78572 0 c 10.78572 0.2761424 10.56186 0.5 10.28572 0.5 c 10.00957 0.5 9.785715 0.2761424 9.785715 0 c 9.785715 -0.2761424 10.00957 -0.5 10.28572 -0.5 c h 10.28572 2.071428 m 10.56186 2.071428 10.78572 2.295285 10.78572 2.571428 c 10.78572 2.84757 10.56186 3.071428 10.28572 3.071428 c 10.00957 3.071428 9.785715 2.84757 9.785715 2.571428 c 9.785715 2.295285 10.00957 2.071428 10.28572 2.071428 c h 10.28572 4.642858 m 10.56186 4.642858 10.78572 4.866715 10.78572 5.142858 c 10.78572 5.419 10.56186 5.642858 10.28572 5.642858 c 10.00957 5.642858 9.785715 5.419 9.785715 5.142858 c 9.785715 4.866715 10.00957 4.642858 10.28572 4.642858 c h 10.28572 7.214287 m 10.56186 7.214287 10.78572 7.438144 10.78572 7.714287 c 10.78572 7.990429 10.56186 8.214287 10.28572 8.214287 c 10.00957 8.214287 9.785715 7.990429 9.785715 7.714287 c 9.785715 7.438144 10.00957 7.214287 10.28572 7.214287 c h 10.28572 9.785715 m 10.56186 9.785715 10.78572 10.00957 10.78572 10.28572 c 10.78572 10.56186 10.56186 10.78572 10.28572 10.78572 c 10.00957 10.78572 9.785715 10.56186 9.785715 10.28572 c 9.785715 10.00957 10.00957 9.785715 10.28572 9.785715 c h 10.28572 12.35714 m 10.56186 12.35714 10.78572 12.581 10.78572 12.85714 c 10.78572 13.13328 10.56186 13.35714 10.28572 13.35714 c 10.00957 13.35714 9.785715 13.13328 9.785715 12.85714 c 9.785715 12.581 10.00957 12.35714 10.28572 12.35714 c h 10.28572 14.92857 m 10.56186 14.92857 10.78572 15.15243 10.78572 15.42857 c 10.78572 15.70471 10.56186 15.92857 10.28572 15.92857 c 10.00957 15.92857 9.785715 15.70471 9.785715 15.42857 c 9.785715 15.15243 10.00957 14.92857 10.28572 14.92857 c h 10.28572 17.5 m 10.56186 17.5 10.78572 17.72386 10.78572 18 c 10.78572 18.27614 10.56186 18.5 10.28572 18.5 c 10.00957 18.5 9.785715 18.27614 9.785715 18 c 9.785715 17.72386 10.00957 17.5 10.28572 17.5 c h 12.85714 -0.5 m 13.13328 -0.5 13.35714 -0.2761424 13.35714 0 c 13.35714 0.2761424 13.13328 0.5 12.85714 0.5 c 12.581 0.5 12.35714 0.2761424 12.35714 0 c 12.35714 -0.2761424 12.581 -0.5 12.85714 -0.5 c h 12.85714 2.071428 m 13.13328 2.071428 13.35714 2.295285 13.35714 2.571428 c 13.35714 2.84757 13.13328 3.071428 12.85714 3.071428 c 12.581 3.071428 12.35714 2.84757 12.35714 2.571428 c 12.35714 2.295285 12.581 2.071428 12.85714 2.071428 c h 12.85714 4.642858 m 13.13328 4.642858 13.35714 4.866715 13.35714 5.142858 c 13.35714 5.419 13.13328 5.642858 12.85714 5.642858 c 12.581 5.642858 12.35714 5.419 12.35714 5.142858 c 12.35714 4.866715 12.581 4.642858 12.85714 4.642858 c h 12.85714 7.214287 m 13.13328 7.214287 13.35714 7.438144 13.35714 7.714287 c 13.35714 7.990429 13.13328 8.214287 12.85714 8.214287 c 12.581 8.214287 12.35714 7.990429 12.35714 7.714287 c 12.35714 7.438144 12.581 7.214287 12.85714 7.214287 c h 12.85714 9.785715 m 13.13328 9.785715 13.35714 10.00957 13.35714 10.28572 c 13.35714 10.56186 13.13328 10.78572 12.85714 10.78572 c 12.581 10.78572 12.35714 10.56186 12.35714 10.28572 c 12.35714 10.00957 12.581 9.785715 12.85714 9.785715 c h 12.85714 12.35714 m 13.13328 12.35714 13.35714 12.581 13.35714 12.85714 c 13.35714 13.13328 13.13328 13.35714 12.85714 13.35714 c 12.581 13.35714 12.35714 13.13328 12.35714 12.85714 c 12.35714 12.581 12.581 12.35714 12.85714 12.35714 c h 12.85714 14.92857 m 13.13328 14.92857 13.35714 15.15243 13.35714 15.42857 c 13.35714 15.70471 13.13328 15.92857 12.85714 15.92857 c 12.581 15.92857 12.35714 15.70471 12.35714 15.42857 c 12.35714 15.15243 12.581 14.92857 12.85714 14.92857 c h 12.85714 17.5 m 13.13328 17.5 13.35714 17.72386 13.35714 18 c 13.35714 18.27614 13.13328 18.5 12.85714 18.5 c 12.581 18.5 12.35714 18.27614 12.35714 18 c 12.35714 17.72386 12.581 17.5 12.85714 17.5 c h 15.42857 -0.5 m 15.70471 -0.5 15.92857 -0.2761424 15.92857 0 c 15.92857 0.2761424 15.70471 0.5 15.42857 0.5 c 15.15243 0.5 14.92857 0.2761424 14.92857 0 c 14.92857 -0.2761424 15.15243 -0.5 15.42857 -0.5 c h 15.42857 2.071428 m 15.70471 2.071428 15.92857 2.295285 15.92857 2.571428 c 15.92857 2.84757 15.70471 3.071428 15.42857 3.071428 c 15.15243 3.071428 14.92857 2.84757 14.92857 2.571428 c 14.92857 2.295285 15.15243 2.071428 15.42857 2.071428 c h 15.42857 4.642858 m 15.70471 4.642858 15.92857 4.866715 15.92857 5.142858 c 15.92857 5.419 15.70471 5.642858 15.42857 5.642858 c 15.15243 5.642858 14.92857 5.419 14.92857 5.142858 c 14.92857 4.866715 15.15243 4.642858 15.42857 4.642858 c h 15.42857 7.214287 m 15.70471 7.214287 15.92857 7.438144 15.92857 7.714287 c 15.92857 7.990429 15.70471 8.214287 15.42857 8.214287 c 15.15243 8.214287 14.92857 7.990429 14.92857 7.714287 c 14.92857 7.438144 15.15243 7.214287 15.42857 7.214287 c h 15.42857 9.785715 m 15.70471 9.785715 15.92857 10.00957 15.92857 10.28572 c 15.92857 10.56186 15.70471 10.78572 15.42857 10.78572 c 15.15243 10.78572 14.92857 10.56186 14.92857 10.28572 c 14.92857 10.00957 15.15243 9.785715 15.42857 9.785715 c h 15.42857 12.35714 m 15.70471 12.35714 15.92857 12.581 15.92857 12.85714 c 15.92857 13.13328 15.70471 13.35714 15.42857 13.35714 c 15.15243 13.35714 14.92857 13.13328 14.92857 12.85714 c 14.92857 12.581 15.15243 12.35714 15.42857 12.35714 c h 15.42857 14.92857 m 15.70471 14.92857 15.92857 15.15243 15.92857 15.42857 c 15.92857 15.70471 15.70471 15.92857 15.42857 15.92857 c 15.15243 15.92857 14.92857 15.70471 14.92857 15.42857 c 14.92857 15.15243 15.15243 14.92857 15.42857 14.92857 c h 15.42857 17.5 m 15.70471 17.5 15.92857 17.72386 15.92857 18 c 15.92857 18.27614 15.70471 18.5 15.42857 18.5 c 15.15243 18.5 14.92857 18.27614 14.92857 18 c 14.92857 17.72386 15.15243 17.5 15.42857 17.5 c h 18 -0.5 m 18.27614 -0.5 18.5 -0.2761424 18.5 0 c 18.5 0.2761424 18.27614 0.5 18 0.5 c 17.72386 0.5 17.5 0.2761424 17.5 0 c 17.5 -0.2761424 17.72386 -0.5 18 -0.5 c h 18 2.071428 m 18.27614 2.071428 18.5 2.295285 18.5 2.571428 c 18.5 2.84757 18.27614 3.071428 18 3.071428 c 17.72386 3.071428 17.5 2.84757 17.5 2.571428 c 17.5 2.295285 17.72386 2.071428 18 2.071428 c h 18 4.642858 m 18.27614 4.642858 18.5 4.866715 18.5 5.142858 c 18.5 5.419 18.27614 5.642858 18 5.642858 c 17.72386 5.642858 17.5 5.419 17.5 5.142858 c 17.5 4.866715 17.72386 4.642858 18 4.642858 c h 18 7.214287 m 18.27614 7.214287 18.5 7.438144 18.5 7.714287 c 18.5 7.990429 18.27614 8.214287 18 8.214287 c 17.72386 8.214287 17.5 7.990429 17.5 7.714287 c 17.5 7.438144 17.72386 7.214287 18 7.214287 c h 18 9.785715 m 18.27614 9.785715 18.5 10.00957 18.5 10.28572 c 18.5 10.56186 18.27614 10.78572 18 10.78572 c 17.72386 10.78572 17.5 10.56186 17.5 10.28572 c 17.5 10.00957 17.72386 9.785715 18 9.785715 c h 18 12.35714 m 18.27614 12.35714 18.5 12.581 18.5 12.85714 c 18.5 13.13328 18.27614 13.35714 18 13.35714 c 17.72386 13.35714 17.5 13.13328 17.5 12.85714 c 17.5 12.581 17.72386 12.35714 18 12.35714 c h 18 14.92857 m 18.27614 14.92857 18.5 15.15243 18.5 15.42857 c 18.5 15.70471 18.27614 15.92857 18 15.92857 c 17.72386 15.92857 17.5 15.70471 17.5 15.42857 c 17.5 15.15243 17.72386 14.92857 18 14.92857 c h 18 17.5 m 18.27614 17.5 18.5 17.72386 18.5 18 c 18.5 18.27614 18.27614 18.5 18 18.5 c 17.72386 18.5 17.5 18.27614 17.5 18 c 17.5 17.72386 17.72386 17.5 18 17.5 c h 1.285714 0.7857141 m 1.561857 0.7857141 1.785714 1.009572 1.785714 1.285714 c 1.785714 1.561857 1.561857 1.785714 1.285714 1.785714 c 1.009572 1.785714 0.7857141 1.561857 0.7857141 1.285714 c 0.7857141 1.009572 1.009572 0.7857141 1.285714 0.7857141 c h 1.285714 3.357143 m 1.561857 3.357143 1.785714 3.581 1.785714 3.857143 c 1.785714 4.133285 1.561857 4.357142 1.285714 4.357142 c 1.009572 4.357142 0.7857141 4.133285 0.7857141 3.857143 c 0.7857141 3.581 1.009572 3.357143 1.285714 3.357143 c h 1.285714 5.928572 m 1.561857 5.928572 1.785714 6.15243 1.785714 6.428572 c 1.785714 6.704715 1.561857 6.928572 1.285714 6.928572 c 1.009572 6.928572 0.7857141 6.704715 0.7857141 6.428572 c 0.7857141 6.15243 1.009572 5.928572 1.285714 5.928572 c h 1.285714 8.5 m 1.561857 8.5 1.785714 8.723858 1.785714 9 c 1.785714 9.276142 1.561857 9.5 1.285714 9.5 c 1.009572 9.5 0.7857141 9.276142 0.7857141 9 c 0.7857141 8.723858 1.009572 8.5 1.285714 8.5 c h 1.285714 11.07143 m 1.561857 11.07143 1.785714 11.29529 1.785714 11.57143 c 1.785714 11.84757 1.561857 12.07143 1.285714 12.07143 c 1.009572 12.07143 0.7857141 11.84757 0.7857141 11.57143 c 0.7857141 11.29529 1.009572 11.07143 1.285714 11.07143 c h 1.285714 13.64286 m 1.561857 13.64286 1.785714 13.86672 1.785714 14.14286 c 1.785714 14.419 1.561857 14.64286 1.285714 14.64286 c 1.009572 14.64286 0.7857141 14.419 0.7857141 14.14286 c 0.7857141 13.86672 1.009572 13.64286 1.285714 13.64286 c h 1.285714 16.21429 m 1.561857 16.21429 1.785714 16.43814 1.785714 16.71429 c 1.785714 16.99043 1.561857 17.21429 1.285714 17.21429 c 1.009572 17.21429 0.7857141 16.99043 0.7857141 16.71429 c 0.7857141 16.43814 1.009572 16.21429 1.285714 16.21429 c h 3.857143 0.7857141 m 4.133285 0.7857141 4.357142 1.009572 4.357142 1.285714 c 4.357142 1.561857 4.133285 1.785714 3.857143 1.785714 c 3.581 1.785714 3.357143 1.561857 3.357143 1.285714 c 3.357143 1.009572 3.581 0.7857141 3.857143 0.7857141 c h 3.857143 3.357143 m 4.133285 3.357143 4.357142 3.581 4.357142 3.857143 c 4.357142 4.133285 4.133285 4.357142 3.857143 4.357142 c 3.581 4.357142 3.357143 4.133285 3.357143 3.857143 c 3.357143 3.581 3.581 3.357143 3.857143 3.357143 c h 3.857143 5.928572 m 4.133285 5.928572 4.357142 6.15243 4.357142 6.428572 c 4.357142 6.704715 4.133285 6.928572 3.857143 6.928572 c 3.581 6.928572 3.357143 6.704715 3.357143 6.428572 c 3.357143 6.15243 3.581 5.928572 3.857143 5.928572 c h 3.857143 8.5 m 4.133285 8.5 4.357142 8.723858 4.357142 9 c 4.357142 9.276142 4.133285 9.5 3.857143 9.5 c 3.581 9.5 3.357143 9.276142 3.357143 9 c 3.357143 8.723858 3.581 8.5 3.857143 8.5 c h 3.857143 11.07143 m 4.133285 11.07143 4.357142 11.29529 4.357142 11.57143 c 4.357142 11.84757 4.133285 12.07143 3.857143 12.07143 c 3.581 12.07143 3.357143 11.84757 3.357143 11.57143 c 3.357143 11.29529 3.581 11.07143 3.857143 11.07143 c h 3.857143 13.64286 m 4.133285 13.64286 4.357142 13.86672 4.357142 14.14286 c 4.357142 14.419 4.133285 14.64286 3.857143 14.64286 c 3.581 14.64286 3.357143 14.419 3.357143 14.14286 c 3.357143 13.86672 3.581 13.64286 3.857143 13.64286 c h 3.857143 16.21429 m 4.133285 16.21429 4.357142 16.43814 4.357142 16.71429 c 4.357142 16.99043 4.133285 17.21429 3.857143 17.21429 c 3.581 17.21429 3.357143 16.99043 3.357143 16.71429 c 3.357143 16.43814 3.581 16.21429 3.857143 16.21429 c h 6.428572 0.7857141 m 6.704715 0.7857141 6.928572 1.009572 6.928572 1.285714 c 6.928572 1.561857 6.704715 1.785714 6.428572 1.785714 c 6.15243 1.785714 5.928572 1.561857 5.928572 1.285714 c 5.928572 1.009572 6.15243 0.7857141 6.428572 0.7857141 c h 6.428572 3.357143 m 6.704715 3.357143 6.928572 3.581 6.928572 3.857143 c 6.928572 4.133285 6.704715 4.357142 6.428572 4.357142 c 6.15243 4.357142 5.928572 4.133285 5.928572 3.857143 c 5.928572 3.581 6.15243 3.357143 6.428572 3.357143 c h 6.428572 5.928572 m 6.704715 5.928572 6.928572 6.15243 6.928572 6.428572 c 6.928572 6.704715 6.704715 6.928572 6.428572 6.928572 c 6.15243 6.928572 5.928572 6.704715 5.928572 6.428572 c 5.928572 6.15243 6.15243 5.928572 6.428572 5.928572 c h 6.428572 8.5 m 6.704715 8.5 6.928572 8.723858 6.928572 9 c 6.928572 9.276142 6.704715 9.5 6.428572 9.5 c 6.15243 9.5 5.928572 9.276142 5.928572 9 c 5.928572 8.723858 6.15243 8.5 6.428572 8.5 c h 6.428572 11.07143 m 6.704715 11.07143 6.928572 11.29529 6.928572 11.57143 c 6.928572 11.84757 6.704715 12.07143 6.428572 12.07143 c 6.15243 12.07143 5.928572 11.84757 5.928572 11.57143 c 5.928572 11.29529 6.15243 11.07143 6.428572 11.07143 c h 6.428572 13.64286 m 6.704715 13.64286 6.928572 13.86672 6.928572 14.14286 c 6.928572 14.419 6.704715 14.64286 6.428572 14.64286 c 6.15243 14.64286 5.928572 14.419 5.928572 14.14286 c 5.928572 13.86672 6.15243 13.64286 6.428572 13.64286 c h 6.428572 16.21429 m 6.704715 16.21429 6.928572 16.43814 6.928572 16.71429 c 6.928572 16.99043 6.704715 17.21429 6.428572 17.21429 c 6.15243 17.21429 5.928572 16.99043 5.928572 16.71429 c 5.928572 16.43814 6.15243 16.21429 6.428572 16.21429 c h 9 0.7857141 m 9.276142 0.7857141 9.5 1.009572 9.5 1.285714 c 9.5 1.561857 9.276142 1.785714 9 1.785714 c 8.723858 1.785714 8.5 1.561857 8.5 1.285714 c 8.5 1.009572 8.723858 0.7857141 9 0.7857141 c h 9 3.357143 m 9.276142 3.357143 9.5 3.581 9.5 3.857143 c 9.5 4.133285 9.276142 4.357142 9 4.357142 c 8.723858 4.357142 8.5 4.133285 8.5 3.857143 c 8.5 3.581 8.723858 3.357143 9 3.357143 c h 9 5.928572 m 9.276142 5.928572 9.5 6.15243 9.5 6.428572 c 9.5 6.704715 9.276142 6.928572 9 6.928572 c 8.723858 6.928572 8.5 6.704715 8.5 6.428572 c 8.5 6.15243 8.723858 5.928572 9 5.928572 c h 9 8.5 m 9.276142 8.5 9.5 8.723858 9.5 9 c 9.5 9.276142 9.276142 9.5 9 9.5 c 8.723858 9.5 8.5 9.276142 8.5 9 c 8.5 8.723858 8.723858 8.5 9 8.5 c h 9 11.07143 m 9.276142 11.07143 9.5
|
|
|
11.29529 9.5 11.57143 c 9.5 11.84757 9.276142 12.07143 9 12.07143 c 8.723858 12.07143 8.5 11.84757 8.5 11.57143 c 8.5 11.29529 8.723858 11.07143 9 11.07143 c h 9 13.64286 m 9.276142 13.64286 9.5 13.86672 9.5 14.14286 c 9.5 14.419 9.276142 14.64286 9 14.64286 c 8.723858 14.64286 8.5 14.419 8.5 14.14286 c 8.5 13.86672 8.723858 13.64286 9 13.64286 c h 9 16.21429 m 9.276142 16.21429 9.5 16.43814 9.5 16.71429 c 9.5 16.99043 9.276142 17.21429 9 17.21429 c 8.723858 17.21429 8.5 16.99043 8.5 16.71429 c 8.5 16.43814 8.723858 16.21429 9 16.21429 c h 11.57143 0.7857141 m 11.84757 0.7857141 12.07143 1.009572 12.07143 1.285714 c 12.07143 1.561857 11.84757 1.785714 11.57143 1.785714 c 11.29529 1.785714 11.07143 1.561857 11.07143 1.285714 c 11.07143 1.009572 11.29529 0.7857141 11.57143 0.7857141 c h 11.57143 3.357143 m 11.84757 3.357143 12.07143 3.581 12.07143 3.857143 c 12.07143 4.133285 11.84757 4.357142 11.57143 4.357142 c 11.29529 4.357142 11.07143 4.133285 11.07143 3.857143 c 11.07143 3.581 11.29529 3.357143 11.57143 3.357143 c h 11.57143 5.928572 m 11.84757 5.928572 12.07143 6.15243 12.07143 6.428572 c 12.07143 6.704715 11.84757 6.928572 11.57143 6.928572 c 11.29529 6.928572 11.07143 6.704715 11.07143 6.428572 c 11.07143 6.15243 11.29529 5.928572 11.57143 5.928572 c h 11.57143 8.5 m 11.84757 8.5 12.07143 8.723858 12.07143 9 c 12.07143 9.276142 11.84757 9.5 11.57143 9.5 c 11.29529 9.5 11.07143 9.276142 11.07143 9 c 11.07143 8.723858 11.29529 8.5 11.57143 8.5 c h 11.57143 11.07143 m 11.84757 11.07143 12.07143 11.29529 12.07143 11.57143 c 12.07143 11.84757 11.84757 12.07143 11.57143 12.07143 c 11.29529 12.07143 11.07143 11.84757 11.07143 11.57143 c 11.07143 11.29529 11.29529 11.07143 11.57143 11.07143 c h 11.57143 13.64286 m 11.84757 13.64286 12.07143 13.86672 12.07143 14.14286 c 12.07143 14.419 11.84757 14.64286 11.57143 14.64286 c 11.29529 14.64286 11.07143 14.419 11.07143 14.14286 c 11.07143 13.86672 11.29529 13.64286 11.57143 13.64286 c h 11.57143 16.21429 m 11.84757 16.21429 12.07143 16.43814 12.07143 16.71429 c 12.07143 16.99043 11.84757 17.21429 11.57143 17.21429 c 11.29529 17.21429 11.07143 16.99043 11.07143 16.71429 c 11.07143 16.43814 11.29529 16.21429 11.57143 16.21429 c h 14.14286 0.7857141 m 14.419 0.7857141 14.64286 1.009572 14.64286 1.285714 c 14.64286 1.561857 14.419 1.785714 14.14286 1.785714 c 13.86672 1.785714 13.64286 1.561857 13.64286 1.285714 c 13.64286 1.009572 13.86672 0.7857141 14.14286 0.7857141 c h 14.14286 3.357143 m 14.419 3.357143 14.64286 3.581 14.64286 3.857143 c 14.64286 4.133285 14.419 4.357142 14.14286 4.357142 c 13.86672 4.357142 13.64286 4.133285 13.64286 3.857143 c 13.64286 3.581 13.86672 3.357143 14.14286 3.357143 c h 14.14286 5.928572 m 14.419 5.928572 14.64286 6.15243 14.64286 6.428572 c 14.64286 6.704715 14.419 6.928572 14.14286 6.928572 c 13.86672 6.928572 13.64286 6.704715 13.64286 6.428572 c 13.64286 6.15243 13.86672 5.928572 14.14286 5.928572 c h 14.14286 8.5 m 14.419 8.5 14.64286 8.723858 14.64286 9 c 14.64286 9.276142 14.419 9.5 14.14286 9.5 c 13.86672 9.5 13.64286 9.276142 13.64286 9 c 13.64286 8.723858 13.86672 8.5 14.14286 8.5 c h 14.14286 11.07143 m 14.419 11.07143 14.64286 11.29529 14.64286 11.57143 c 14.64286 11.84757 14.419 12.07143 14.14286 12.07143 c 13.86672 12.07143 13.64286 11.84757 13.64286 11.57143 c 13.64286 11.29529 13.86672 11.07143 14.14286 11.07143 c h 14.14286 13.64286 m 14.419 13.64286 14.64286 13.86672 14.64286 14.14286 c 14.64286 14.419 14.419 14.64286 14.14286 14.64286 c 13.86672 14.64286 13.64286 14.419 13.64286 14.14286 c 13.64286 13.86672 13.86672 13.64286 14.14286 13.64286 c h 14.14286 16.21429 m 14.419 16.21429 14.64286 16.43814 14.64286 16.71429 c 14.64286 16.99043 14.419 17.21429 14.14286 17.21429 c 13.86672 17.21429 13.64286 16.99043 13.64286 16.71429 c 13.64286 16.43814 13.86672 16.21429 14.14286 16.21429 c h 16.71429 0.7857141 m 16.99043 0.7857141 17.21429 1.009572 17.21429 1.285714 c 17.21429 1.561857 16.99043 1.785714 16.71429 1.785714 c 16.43814 1.785714 16.21429 1.561857 16.21429 1.285714 c 16.21429 1.009572 16.43814 0.7857141 16.71429 0.7857141 c h 16.71429 3.357143 m 16.99043 3.357143 17.21429 3.581 17.21429 3.857143 c 17.21429 4.133285 16.99043 4.357142 16.71429 4.357142 c 16.43814 4.357142 16.21429 4.133285 16.21429 3.857143 c 16.21429 3.581 16.43814 3.357143 16.71429 3.357143 c h 16.71429 5.928572 m 16.99043 5.928572 17.21429 6.15243 17.21429 6.428572 c 17.21429 6.704715 16.99043 6.928572 16.71429 6.928572 c 16.43814 6.928572 16.21429 6.704715 16.21429 6.428572 c 16.21429 6.15243 16.43814 5.928572 16.71429 5.928572 c h 16.71429 8.5 m 16.99043 8.5 17.21429 8.723858 17.21429 9 c 17.21429 9.276142 16.99043 9.5 16.71429 9.5 c 16.43814 9.5 16.21429 9.276142 16.21429 9 c 16.21429 8.723858 16.43814 8.5 16.71429 8.5 c h 16.71429 11.07143 m 16.99043 11.07143 17.21429 11.29529 17.21429 11.57143 c 17.21429 11.84757 16.99043 12.07143 16.71429 12.07143 c 16.43814 12.07143 16.21429 11.84757 16.21429 11.57143 c 16.21429 11.29529 16.43814 11.07143 16.71429 11.07143 c h 16.71429 13.64286 m 16.99043 13.64286 17.21429 13.86672 17.21429 14.14286 c 17.21429 14.419 16.99043 14.64286 16.71429 14.64286 c 16.43814 14.64286 16.21429 14.419 16.21429 14.14286 c 16.21429 13.86672 16.43814 13.64286 16.71429 13.64286 c h 16.71429 16.21429 m 16.99043 16.21429 17.21429 16.43814 17.21429 16.71429 c 17.21429 16.99043 16.99043 17.21429 16.71429 17.21429 c 16.43814 17.21429 16.21429 16.99043 16.21429 16.71429 c 16.21429 16.43814 16.43814 16.21429 16.71429 16.21429 c h
|
|
|
{strokecolor} rg f
|
|
|
endstream'''
|
|
|
}
|
|
|
|
|
|
HatchStyleTemplates={
|
|
|
'Brick' :'/PatternName(Brick)',
|
|
|
'DiagonalBrick':'/PatternName(Diagonal Brick)',
|
|
|
'Horizontal':'/PatternName(Horizontal)',
|
|
|
'Vertical':'/PatternName(Vertical)',
|
|
|
'DiagonalDown':'/PatternName(Diagonal Down)',
|
|
|
'DiagonalUp':'/PatternName(Diagonal Up)',
|
|
|
'Grid':'/PatternName(Grid)',
|
|
|
'Weave':'/PatternName(Weave)',
|
|
|
'10Dots':'/PatternName(10% Dots)',
|
|
|
'20Dots':'/PatternName(20% Dots)',
|
|
|
'30Dots':'/PatternName(30% Dots)'
|
|
|
}
|
|
|
|
|
|
def calculate_bounding_rect(vertices):
|
|
|
xs = [pt[0] for pt in vertices]
|
|
|
ys = [pt[1] for pt in vertices]
|
|
|
min_x = min(xs)
|
|
|
max_x = max(xs)
|
|
|
min_y = min(ys)
|
|
|
max_y = max(ys)
|
|
|
return [min_x, min_y, max_x, max_y]
|
|
|
def generate_annotation_xml_block(vertices, area_text, author, custom_data: dict, column_order: list, index: int,
|
|
|
type_internal: str = 'Bluebeam.PDF.Annotations.AnnotationMeasureArea',
|
|
|
subject: str = 'Area Measurement',
|
|
|
label: str = '',opacity:str='',
|
|
|
color:str='', linestyle:str='',
|
|
|
hatchstyle:str='',hatchLinescolor:str='',
|
|
|
bb_objptrMeas:str=''):
|
|
|
now = datetime.datetime.utcnow()
|
|
|
mod_date = now.strftime("D:%Y%m%d%H%M%S+00'00'")
|
|
|
creation_date = now.isoformat() + 'Z'
|
|
|
id_str = "fitz-" + uuid.uuid4().hex[:4].upper()
|
|
|
|
|
|
vert_str = ' '.join([f'{x:.4f}' for point in vertices for x in point])
|
|
|
ordered_column_values = [f'({custom_data.get(col, "")})' for col in column_order]
|
|
|
bsi_column_data = ''.join(ordered_column_values)
|
|
|
meastype=''
|
|
|
if subject.startswith('Area'):
|
|
|
meastype='129'
|
|
|
polygonpolylineDimension='/PolygonDimension'
|
|
|
polygonpolyline='/Polygon'
|
|
|
elif subject.startswith('Perimeter'):
|
|
|
meastype='130'
|
|
|
polygonpolylineDimension='/PolyLineDimension'
|
|
|
polygonpolyline='/PolyLine'
|
|
|
rectvertices=calculate_bounding_rect(vertices)
|
|
|
|
|
|
raw_text = f'''<<
|
|
|
/DS(font: Helvetica 12pt; text-align:center; line-height:13.8pt; color:#FF0000)
|
|
|
/Cap false
|
|
|
/AlignOnSegment true
|
|
|
/MeasurementTypes {meastype}
|
|
|
/SlopeType 1
|
|
|
/PitchRun 12
|
|
|
/IT
|
|
|
{polygonpolylineDimension}
|
|
|
/Vertices[{vert_str}]
|
|
|
/IC[{color}]
|
|
|
/Pattern/{hatchstyle}/PatternColor[{hatchLinescolor}]
|
|
|
/FillOpacity {opacity}
|
|
|
/T({author})
|
|
|
/CA {opacity}
|
|
|
/RC(<?xml version="1.0"?><body xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/" xfa:contentType="text/html" xfa:APIVersion="BluebeamPDFRevu:2018" xfa:spec="2.2.0" style="font:Helvetica 12pt; text-align:center; line-height:13.8pt; color:#FF0000" xmlns="http://www.w3.org/1999/xhtml"><p>{area_text}</p></body>)
|
|
|
/Label({label})
|
|
|
/Subj({subject})
|
|
|
/Measure/BBObjPtr_{bb_objptrMeas}
|
|
|
/BSIColumnData[{bsi_column_data}]
|
|
|
/NM({id_str})
|
|
|
/Subtype/{polygonpolyline}
|
|
|
/Rect[{rectvertices[0]} {rectvertices[1]} {rectvertices[2]} {rectvertices[3]}]
|
|
|
/Contents({area_text})
|
|
|
/F 4
|
|
|
/C[{color}]
|
|
|
/BS{linestyle}
|
|
|
/M({mod_date})
|
|
|
>>'''.encode('utf-8')
|
|
|
|
|
|
compressed = zlib.compress(raw_text)
|
|
|
base64_raw = base64.b16encode(compressed).lower().decode()
|
|
|
|
|
|
annotation = Element('Annotation')
|
|
|
SubElement(annotation, 'Page').text = '1'
|
|
|
SubElement(annotation, 'Contents').text = area_text
|
|
|
SubElement(annotation, 'ModDate').text = creation_date
|
|
|
SubElement(annotation, 'Color').text = '#B7B7E8'
|
|
|
SubElement(annotation, 'Type').text = 'Polygon'
|
|
|
SubElement(annotation, 'ID').text = id_str
|
|
|
SubElement(annotation, 'TypeInternal').text = type_internal
|
|
|
SubElement(annotation, 'Raw').text = base64_raw
|
|
|
SubElement(annotation, 'Index').text = str(index)
|
|
|
|
|
|
custom = SubElement(annotation, 'Custom')
|
|
|
for key, value in custom_data.items():
|
|
|
SubElement(custom, key).text = value
|
|
|
|
|
|
SubElement(annotation, 'Subject').text = subject
|
|
|
SubElement(annotation, 'CreationDate').text = creation_date
|
|
|
SubElement(annotation, 'Author').text = author
|
|
|
SubElement(annotation, 'Label').text = label
|
|
|
|
|
|
return annotation
|
|
|
|
|
|
def generate_bb_objptr():
|
|
|
return ''.join(random.choices(string.ascii_uppercase, k=16))
|
|
|
|
|
|
def compresslikeBBRaw(textToCompress):
|
|
|
decompressedX = textToCompress.encode('utf-8')
|
|
|
print(decompressedX)
|
|
|
recompressedX = zlib.compress(decompressedX)
|
|
|
print(recompressedX.hex())
|
|
|
return recompressedX.hex()
|
|
|
|
|
|
def setBrickHatch(fillcolor,strokecolor):
|
|
|
|
|
|
randombb_objptr=generate_bb_objptr()
|
|
|
resourceid=compresslikeBBRaw(randombb_objptr)
|
|
|
compressedRaw=compresslikeBBRaw(AllhatchesCodes['Brick'].format(fillcolor=fillcolor, strokecolor=strokecolor))
|
|
|
return 'BBObjPtr_'+randombb_objptr+HatchStyleTemplates['Brick'],compressedRaw, resourceid
|
|
|
|
|
|
def setDiagonalBrickHatch(fillcolor,strokecolor):
|
|
|
|
|
|
randombb_objptr=generate_bb_objptr()
|
|
|
resourceid=compresslikeBBRaw(randombb_objptr)
|
|
|
compressedRaw=compresslikeBBRaw(AllhatchesCodes['DiagonalBrick'].format(fillcolor=fillcolor, strokecolor=strokecolor))
|
|
|
return 'BBObjPtr_'+randombb_objptr+HatchStyleTemplates['DiagonalBrick'],compressedRaw,resourceid
|
|
|
|
|
|
def setHorizontalHatch(fillcolor,strokecolor):
|
|
|
|
|
|
randombb_objptr=generate_bb_objptr()
|
|
|
resourceid=compresslikeBBRaw(randombb_objptr)
|
|
|
compressedRaw=compresslikeBBRaw(AllhatchesCodes['Horizontal'].format(fillcolor=fillcolor, strokecolor=strokecolor))
|
|
|
return 'BBObjPtr_'+randombb_objptr+HatchStyleTemplates['Horizontal'],compressedRaw,resourceid
|
|
|
|
|
|
def setVerticalHatch(fillcolor,strokecolor):
|
|
|
|
|
|
randombb_objptr=generate_bb_objptr()
|
|
|
resourceid=compresslikeBBRaw(randombb_objptr)
|
|
|
compressedRaw=compresslikeBBRaw(AllhatchesCodes['Vertical'].format(fillcolor=fillcolor, strokecolor=strokecolor))
|
|
|
return 'BBObjPtr_'+randombb_objptr+HatchStyleTemplates['Vertical'],compressedRaw,resourceid
|
|
|
|
|
|
def setDiagonalDownHatch(fillcolor,strokecolor):
|
|
|
|
|
|
randombb_objptr=generate_bb_objptr()
|
|
|
resourceid=compresslikeBBRaw(randombb_objptr)
|
|
|
compressedRaw=compresslikeBBRaw(AllhatchesCodes['DiagonalDown'].format(fillcolor=fillcolor, strokecolor=strokecolor))
|
|
|
return 'BBObjPtr_'+randombb_objptr+HatchStyleTemplates['DiagonalDown'],compressedRaw,resourceid
|
|
|
|
|
|
def setDiagonalUpHatch(fillcolor,strokecolor):
|
|
|
|
|
|
randombb_objptr=generate_bb_objptr()
|
|
|
resourceid=compresslikeBBRaw(randombb_objptr)
|
|
|
compressedRaw=compresslikeBBRaw(AllhatchesCodes['DiagonalUp'].format(fillcolor=fillcolor, strokecolor=strokecolor))
|
|
|
return 'BBObjPtr_'+randombb_objptr+HatchStyleTemplates['DiagonalUp'],compressedRaw,resourceid
|
|
|
|
|
|
def setGridHatch(fillcolor,strokecolor):
|
|
|
|
|
|
randombb_objptr=generate_bb_objptr()
|
|
|
resourceid=compresslikeBBRaw(randombb_objptr)
|
|
|
compressedRaw=compresslikeBBRaw(AllhatchesCodes['Grid'].format(fillcolor=fillcolor, strokecolor=strokecolor))
|
|
|
return 'BBObjPtr_'+randombb_objptr+HatchStyleTemplates['Grid'],compressedRaw,resourceid
|
|
|
|
|
|
def setWeaveHatch(fillcolor,strokecolor):
|
|
|
|
|
|
randombb_objptr=generate_bb_objptr()
|
|
|
resourceid=compresslikeBBRaw(randombb_objptr)
|
|
|
compressedRaw=compresslikeBBRaw(AllhatchesCodes['Weave'].format(fillcolor=fillcolor, strokecolor=strokecolor))
|
|
|
return 'BBObjPtr_'+randombb_objptr+HatchStyleTemplates['Weave'],compressedRaw,resourceid
|
|
|
|
|
|
def set10DotsHatch(fillcolor,strokecolor):
|
|
|
|
|
|
randombb_objptr=generate_bb_objptr()
|
|
|
resourceid=compresslikeBBRaw(randombb_objptr)
|
|
|
compressedRaw=compresslikeBBRaw(AllhatchesCodes['10Dots'].format(fillcolor=fillcolor, strokecolor=strokecolor))
|
|
|
return 'BBObjPtr_'+randombb_objptr+HatchStyleTemplates['10Dots'],compressedRaw,resourceid
|
|
|
|
|
|
def set20DotsHatch(fillcolor,strokecolor):
|
|
|
|
|
|
randombb_objptr=generate_bb_objptr()
|
|
|
resourceid=compresslikeBBRaw(randombb_objptr)
|
|
|
compressedRaw=compresslikeBBRaw(AllhatchesCodes['20Dots'].format(fillcolor=fillcolor, strokecolor=strokecolor))
|
|
|
return 'BBObjPtr_'+randombb_objptr+HatchStyleTemplates['20Dots'],compressedRaw,resourceid
|
|
|
|
|
|
def set30DotsHatch(fillcolor,strokecolor):
|
|
|
|
|
|
randombb_objptr=generate_bb_objptr()
|
|
|
resourceid=compresslikeBBRaw(randombb_objptr)
|
|
|
compressedRaw=compresslikeBBRaw(AllhatchesCodes['30Dots'].format(fillcolor=fillcolor, strokecolor=strokecolor))
|
|
|
return 'BBObjPtr_'+randombb_objptr+HatchStyleTemplates['30Dots'],compressedRaw,resourceid
|
|
|
|
|
|
def save_multiple_annotations_bax(annotations, output_path, column_order,pdfWidth,pdfHeight):
|
|
|
"""
|
|
|
annotations: list of dicts, each with:
|
|
|
- vertices: list of [x, y]
|
|
|
- text: str (label/tooltip)
|
|
|
- author: str
|
|
|
- custom_data: dict of custom field values
|
|
|
- type_internal: str (e.g., Bluebeam.PDF.Annotations.AnnotationMeasurePerimeter)
|
|
|
- subject: str (e.g., Perimeter Measurement)
|
|
|
"""
|
|
|
globalhatches=[]
|
|
|
scales=[]
|
|
|
doc = Element('Document', Version='1')
|
|
|
|
|
|
page = SubElement(doc, 'Page', Index='0')
|
|
|
SubElement(page, 'Label').text = '1'
|
|
|
SubElement(page, 'Width').text = str(pdfWidth)
|
|
|
SubElement(page, 'Height').text = str(pdfHeight)
|
|
|
|
|
|
for i, ann in enumerate(annotations):
|
|
|
|
|
|
bb_objptrMeas=generate_bb_objptr()
|
|
|
resourceidComp=compresslikeBBRaw(bb_objptrMeas)
|
|
|
scales.append(resourceidComp)
|
|
|
|
|
|
hatchstyle_key = ann.get('hatchstyle')
|
|
|
if hatchstyle_key not in globalhatches and hatchstyle_key:
|
|
|
globalhatches.append([hatchstyle_key[2],hatchstyle_key[1]])
|
|
|
hatchstyle=hatchstyle_key[0]
|
|
|
else:
|
|
|
hatchstyle='none'
|
|
|
|
|
|
annotation_xml = generate_annotation_xml_block(
|
|
|
vertices=ann['vertices'],
|
|
|
area_text=ann['text'],
|
|
|
author=ann['author'],
|
|
|
custom_data=ann['custom_data'],
|
|
|
column_order=column_order,
|
|
|
index=i,
|
|
|
bb_objptrMeas=bb_objptrMeas,
|
|
|
type_internal=ann.get('type_internal', 'Bluebeam.PDF.Annotations.AnnotationMeasureArea'),
|
|
|
subject=ann.get('subject', 'Area Measurement'),
|
|
|
label=ann.get('label', 'label1'),
|
|
|
opacity=ann.get('opacity', ''),
|
|
|
color=ann.get('color', ''),
|
|
|
linestyle=ann.get('linestyle', ''),
|
|
|
hatchstyle=hatchstyle,
|
|
|
hatchLinescolor=ann.get('hatchLinescolor',''),
|
|
|
)
|
|
|
page.append(annotation_xml)
|
|
|
|
|
|
GlobalResources = SubElement(doc, 'GlobalResources')
|
|
|
for hatch in globalhatches:
|
|
|
Resource = SubElement(GlobalResources, 'Resource')
|
|
|
SubElement(Resource, 'ID').text = hatch[0]
|
|
|
SubElement(Resource, 'Raw').text = hatch[1]
|
|
|
for scale in scales:
|
|
|
Resource = SubElement(GlobalResources, 'Resource')
|
|
|
SubElement(Resource, 'ID').text = scale
|
|
|
SubElement(Resource, 'Raw').text = '789c85d04f0b82401005f0af3247bd34a35176b085503c55847f22a80e2a4b7858ad7537e8dba7951ea2a5e330efc783e7fb983eae1c373c6fb5e498e842f577bcc6d872a014b00407848d87e310dd6a5170193552e40a33abfb0540139ace5ccff316188243844962d98c9d3134b297eba2f4255626d1dee06d3e200a4149cd47989ae0c99dd32fb8ebe0a8f7265dea3fb5b9bc7095d5950a9aface655b3575bf070d8b30f604438f6873'
|
|
|
bax_xml= tostring(doc, encoding="unicode", method="xml")
|
|
|
return bax_xml
|
|
|
"""PDF to image"""
|
|
|
|
|
|
def pdftoimg(datadoc,pdf_content=0):
|
|
|
if pdf_content:
|
|
|
doc = fitz.open(stream=pdf_content, filetype="pdf")
|
|
|
else:
|
|
|
doc = fitz.open('pdf',datadoc)
|
|
|
page=doc[0]
|
|
|
pix = page.get_pixmap()
|
|
|
pl=Image.frombytes('RGB', [pix.width,pix.height],pix.samples)
|
|
|
img=np.array(pl)
|
|
|
img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
|
|
|
return img
|
|
|
|
|
|
|
|
|
|
|
|
ISO_SIZES_INCHES = {
|
|
|
"A0": (33.11, 46.81),
|
|
|
"A1": (23.39, 33.11),
|
|
|
"A2": (16.54, 23.39),
|
|
|
"A3": (11.69, 16.54),
|
|
|
"A4": (8.27, 11.69),
|
|
|
"A5": (5.83, 8.27),
|
|
|
"A6": (4.13, 5.83),
|
|
|
"A7": (2.91, 4.13),
|
|
|
"A8": (2.05, 2.91),
|
|
|
"A9": (1.46, 2.05),
|
|
|
"A10": (1.02, 1.46)
|
|
|
}
|
|
|
|
|
|
def get_paper_size_in_inches(width, height):
|
|
|
"""Find the closest matching paper size in inches."""
|
|
|
for size, (w, h) in ISO_SIZES_INCHES.items():
|
|
|
if (abs(w - width) < 0.1 and abs(h - height) < 0.1) or (abs(w - height) < 0.1 and abs(h - width) < 0.1):
|
|
|
return size
|
|
|
return "Unknown Size"
|
|
|
|
|
|
def analyze_pdf(datadoc,pdf_content=0):
|
|
|
|
|
|
if pdf_content:
|
|
|
pdf_document = fitz.open(stream=pdf_content, filetype="pdf")
|
|
|
else:
|
|
|
pdf_document = fitz.open('pdf',datadoc)
|
|
|
|
|
|
|
|
|
for page_number in range(len(pdf_document)):
|
|
|
page = pdf_document[page_number]
|
|
|
rect = page.rect
|
|
|
width_points, height_points = rect.width, rect.height
|
|
|
|
|
|
|
|
|
width_inches, height_inches = width_points / 72, height_points / 72
|
|
|
|
|
|
paper_size = get_paper_size_in_inches(width_inches, height_inches)
|
|
|
|
|
|
print(f"Page {page_number + 1}: {width_inches:.2f} x {height_inches:.2f} inches ({paper_size})")
|
|
|
|
|
|
pdf_document.close()
|
|
|
return width_inches , height_inches , paper_size
|
|
|
|
|
|
|
|
|
def get_dxfSize(dxfpath):
|
|
|
|
|
|
doc = ezdxf.readfile(dxfpath)
|
|
|
msp = doc.modelspace()
|
|
|
|
|
|
|
|
|
cache = bbox.Cache()
|
|
|
overall_bbox = bbox.extents(msp, cache=cache)
|
|
|
print("Overall Bounding Box:", overall_bbox)
|
|
|
print(overall_bbox.extmin[0]+overall_bbox.extmax[0], overall_bbox.extmin[1]+overall_bbox.extmax[1])
|
|
|
|
|
|
return overall_bbox.extmin[0]+overall_bbox.extmax[0], overall_bbox.extmin[1]+overall_bbox.extmax[1]
|
|
|
|
|
|
|
|
|
|
|
|
def switch_case(argument):
|
|
|
switcher = {
|
|
|
"A0": 1.27,
|
|
|
"A1": 2.54,
|
|
|
"A2": 5.08,
|
|
|
"A3": 10.16,
|
|
|
"A4": 20.32,
|
|
|
"A5": 40.64,
|
|
|
"A6": 81.28,
|
|
|
"A7": 162.56,
|
|
|
"A8": 325.12,
|
|
|
"A9": 650.24,
|
|
|
"A10": 1300.48
|
|
|
}
|
|
|
|
|
|
print("Final Ratio=",switcher.get(argument, 1))
|
|
|
return switcher.get(argument, 1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def RetriveRatio(datadoc,dxfpath,pdf_content=0):
|
|
|
if pdf_content:
|
|
|
width,height,paper_size = analyze_pdf (datadoc,pdf_content)
|
|
|
else:
|
|
|
width,height,paper_size = analyze_pdf (datadoc)
|
|
|
|
|
|
|
|
|
if(width > height ):
|
|
|
bigger=width
|
|
|
else:
|
|
|
bigger=height
|
|
|
|
|
|
width_dxf,height_dxf = get_dxfSize(dxfpath)
|
|
|
|
|
|
if(width_dxf > height_dxf ):
|
|
|
bigger_dxf=width_dxf
|
|
|
else:
|
|
|
bigger_dxf=height_dxf
|
|
|
|
|
|
if(0.2 < bigger_dxf/bigger < 1.2):
|
|
|
print("bigger_dxf/bigger",bigger/bigger_dxf)
|
|
|
argument = paper_size
|
|
|
FinalRatio=switch_case(argument)
|
|
|
else:
|
|
|
FinalRatio=1
|
|
|
return FinalRatio,width_dxf
|
|
|
|
|
|
|
|
|
"""Flips image
|
|
|
DXF origin is at the bottom left while img origin is top left
|
|
|
"""
|
|
|
|
|
|
def flip(img):
|
|
|
height, width = img.shape[:2]
|
|
|
|
|
|
|
|
|
angle = 180
|
|
|
|
|
|
|
|
|
rotation_matrix = cv2.getRotationMatrix2D((width/2, height/2), angle, 1)
|
|
|
|
|
|
|
|
|
rotated_image = cv2.warpAffine(img, rotation_matrix, (width, height))
|
|
|
flipped_horizontal = cv2.flip(rotated_image, 1)
|
|
|
return flipped_horizontal
|
|
|
|
|
|
|
|
|
|
|
|
def aci_to_rgb(aci):
|
|
|
aci_rgb_map = {
|
|
|
0: (0, 0, 0),
|
|
|
1: (255, 0, 0),
|
|
|
2: (255, 255, 0),
|
|
|
3: (0, 255, 0),
|
|
|
4: (0, 255, 255),
|
|
|
5: (0, 0, 255),
|
|
|
6: (255, 0, 255),
|
|
|
7: (255, 255, 255),
|
|
|
8: (65, 65, 65),
|
|
|
9: (128, 128, 128),
|
|
|
10: (255, 0, 0),
|
|
|
11: (255, 170, 170),
|
|
|
12: (189, 0, 0),
|
|
|
13: (189, 126, 126),
|
|
|
14: (129, 0, 0),
|
|
|
15: (129, 86, 86),
|
|
|
16: (104, 0, 0),
|
|
|
17: (104, 69, 69),
|
|
|
18: (79, 0, 0),
|
|
|
19: (79, 53, 53),
|
|
|
20: (255, 63, 0),
|
|
|
21: (255, 191, 170),
|
|
|
22: (189, 46, 0),
|
|
|
23: (189, 141, 126),
|
|
|
24: (129, 31, 0),
|
|
|
25: (129, 96, 86),
|
|
|
26: (104, 25, 0),
|
|
|
27: (104, 78, 69),
|
|
|
28: (79, 19, 0),
|
|
|
29: (79, 59, 53),
|
|
|
30: (255, 127, 0),
|
|
|
31: (255, 212, 170),
|
|
|
32: (189, 94, 0),
|
|
|
33: (189, 157, 126),
|
|
|
34: (129, 64, 0),
|
|
|
35: (129, 107, 86),
|
|
|
36: (104, 52, 0),
|
|
|
37: (104, 86, 69),
|
|
|
38: (79, 39, 0),
|
|
|
39: (79, 66, 53),
|
|
|
40: (255, 191, 0),
|
|
|
41: (255, 234, 170),
|
|
|
42: (189, 141, 0),
|
|
|
43: (189, 173, 126),
|
|
|
44: (129, 96, 0),
|
|
|
45: (129, 118, 86),
|
|
|
46: (104, 78, 0),
|
|
|
47: (104, 95, 69),
|
|
|
48: (79, 59, 0),
|
|
|
49: (79, 73, 53),
|
|
|
50: (255, 255, 0),
|
|
|
51: (255, 255, 170),
|
|
|
52: (189, 189, 0),
|
|
|
53: (189, 189, 126),
|
|
|
54: (129, 129, 0),
|
|
|
55: (129, 129, 86),
|
|
|
56: (104, 104, 0),
|
|
|
57: (104, 104, 69),
|
|
|
58: (79, 79, 0),
|
|
|
59: (79, 79, 53),
|
|
|
60: (191, 255, 0),
|
|
|
61: (234, 255, 170),
|
|
|
62: (141, 189, 0),
|
|
|
63: (173, 189, 126),
|
|
|
64: (96, 129, 0),
|
|
|
65: (118, 129, 86),
|
|
|
66: (78, 104, 0),
|
|
|
67: (95, 104, 69),
|
|
|
68: (59, 79, 0),
|
|
|
69: (73, 79, 53),
|
|
|
70: (127, 255, 0),
|
|
|
71: (212, 255, 170),
|
|
|
72: (94, 189, 0),
|
|
|
73: (157, 189, 126),
|
|
|
74: (64, 129, 0),
|
|
|
75: (107, 129, 86),
|
|
|
76: (52, 104, 0),
|
|
|
77: (86, 104, 69),
|
|
|
78: (39, 79, 0),
|
|
|
79: (66, 79, 53),
|
|
|
80: (63, 255, 0),
|
|
|
81: (191, 255, 170),
|
|
|
82: (46, 189, 0),
|
|
|
83: (141, 189, 126),
|
|
|
84: (31, 129, 0),
|
|
|
85: (96, 129, 86),
|
|
|
86: (25, 104, 0),
|
|
|
87: (78, 104, 69),
|
|
|
88: (19, 79, 0),
|
|
|
89: (59, 79, 53),
|
|
|
90: (0, 255, 0),
|
|
|
91: (170, 255, 170),
|
|
|
92: (0, 189, 0),
|
|
|
93: (126, 189, 126),
|
|
|
94: (0, 129, 0),
|
|
|
95: (86, 129, 86),
|
|
|
96: (0, 104, 0),
|
|
|
97: (69, 104, 69),
|
|
|
98: (0, 79, 0),
|
|
|
99: (53, 79, 53),
|
|
|
100: (0, 255, 63),
|
|
|
101: (170, 255, 191),
|
|
|
102: (0, 189, 46),
|
|
|
103: (126, 189, 141),
|
|
|
104: (0, 129, 31),
|
|
|
105: (86, 129, 96),
|
|
|
106: (0, 104, 25),
|
|
|
107: (69, 104, 78),
|
|
|
108: (0, 79, 19),
|
|
|
109: (53, 79, 59),
|
|
|
110: (0, 255, 127),
|
|
|
111: (170, 255, 212),
|
|
|
112: (0, 189, 94),
|
|
|
113: (126, 189, 157),
|
|
|
114: (0, 129, 64),
|
|
|
115: (86, 129, 107),
|
|
|
116: (0, 104, 52),
|
|
|
117: (69, 104, 86),
|
|
|
118: (0, 79, 39),
|
|
|
119: (53, 79, 66),
|
|
|
120: (0, 255, 191),
|
|
|
121: (170, 255, 234),
|
|
|
122: (0, 189, 141),
|
|
|
123: (126, 189, 173),
|
|
|
124: (0, 129, 96),
|
|
|
125: (86, 129, 118),
|
|
|
126: (0, 104, 78),
|
|
|
127: (69, 104, 95),
|
|
|
128: (0, 79, 59),
|
|
|
129: (53, 79, 73),
|
|
|
130: (0, 255, 255),
|
|
|
131: (170, 255, 255),
|
|
|
132: (0, 189, 189),
|
|
|
133: (126, 189, 189),
|
|
|
134: (0, 129, 129),
|
|
|
135: (86, 129, 129),
|
|
|
136: (0, 104, 104),
|
|
|
137: (69, 104, 104),
|
|
|
138: (0, 79, 79),
|
|
|
139: (53, 79, 79),
|
|
|
140: (0, 191, 255),
|
|
|
141: (170, 234, 255),
|
|
|
142: (0, 141, 189),
|
|
|
143: (126, 173, 189),
|
|
|
144: (0, 96, 129),
|
|
|
145: (86, 118, 129),
|
|
|
146: (0, 78, 104),
|
|
|
147: (69, 95, 104),
|
|
|
148: (0, 59, 79),
|
|
|
149: (53, 73, 79),
|
|
|
150: (0, 127, 255),
|
|
|
151: (170, 212, 255),
|
|
|
152: (0, 94, 189),
|
|
|
153: (126, 157, 189),
|
|
|
154: (0, 64, 129),
|
|
|
155: (86, 107, 129),
|
|
|
156: (0, 52, 104),
|
|
|
157: (69, 86, 104),
|
|
|
158: (0, 39, 79),
|
|
|
159: (53, 66, 79),
|
|
|
160: (0, 63, 255),
|
|
|
161: (170, 191, 255),
|
|
|
162: (0, 46, 189),
|
|
|
163: (126, 141, 189),
|
|
|
164: (0, 31, 129),
|
|
|
165: (86, 96, 129),
|
|
|
166: (0, 25, 104),
|
|
|
167: (69, 78, 104),
|
|
|
168: (0, 19, 79),
|
|
|
169: (53, 59, 79),
|
|
|
170: (0, 0, 255),
|
|
|
171: (170, 170, 255),
|
|
|
172: (0, 0, 189),
|
|
|
173: (126, 126, 189),
|
|
|
174: (0, 0, 129),
|
|
|
175: (86, 86, 129),
|
|
|
176: (0, 0, 104),
|
|
|
177: (69, 69, 104),
|
|
|
178: (0, 0, 79),
|
|
|
179: (53, 53, 79),
|
|
|
180: (63, 0, 255),
|
|
|
181: (191, 170, 255),
|
|
|
182: (46, 0, 189),
|
|
|
183: (141, 126, 189),
|
|
|
184: (31, 0, 129),
|
|
|
185: (96, 86, 129),
|
|
|
186: (25, 0, 104),
|
|
|
187: (78, 69, 104),
|
|
|
188: (19, 0, 79),
|
|
|
189: (59, 53, 79),
|
|
|
190: (127, 0, 255),
|
|
|
191: (212, 170, 255),
|
|
|
192: (94, 0, 189),
|
|
|
193: (157, 126, 189),
|
|
|
194: (64, 0, 129),
|
|
|
195: (107, 86, 129),
|
|
|
196: (52, 0, 104),
|
|
|
197: (86, 69, 104),
|
|
|
198: (39, 0, 79),
|
|
|
199: (66, 53, 79),
|
|
|
200: (191, 0, 255),
|
|
|
201: (234, 170, 255),
|
|
|
202: (141, 0, 189),
|
|
|
203: (173, 126, 189),
|
|
|
204: (96, 0, 129),
|
|
|
205: (118, 86, 129),
|
|
|
206: (78, 0, 104),
|
|
|
207: (95, 69, 104),
|
|
|
208: (59, 0, 79),
|
|
|
209: (73, 53, 79),
|
|
|
210: (255, 0, 255),
|
|
|
211: (255, 170, 255),
|
|
|
212: (189, 0, 189),
|
|
|
213: (189, 126, 189),
|
|
|
214: (129, 0, 129),
|
|
|
215: (129, 86, 129),
|
|
|
216: (104, 0, 104),
|
|
|
217: (104, 69, 104),
|
|
|
218: (79, 0, 79),
|
|
|
219: (79, 53, 79),
|
|
|
220: (255, 0, 191),
|
|
|
221: (255, 170, 234),
|
|
|
222: (189, 0, 141),
|
|
|
223: (189, 126, 173),
|
|
|
224: (129, 0, 96),
|
|
|
225: (129, 86, 118),
|
|
|
226: (104, 0, 78),
|
|
|
227: (104, 69, 95),
|
|
|
228: (79, 0, 59),
|
|
|
229: (79, 53, 73),
|
|
|
230: (255, 0, 127),
|
|
|
231: (255, 170, 212),
|
|
|
232: (189, 0, 94),
|
|
|
233: (189, 126, 157),
|
|
|
234: (129, 0, 64),
|
|
|
235: (129, 86, 107),
|
|
|
236: (104, 0, 52),
|
|
|
237: (104, 69, 86),
|
|
|
238: (79, 0, 39),
|
|
|
239: (79, 53, 66),
|
|
|
240: (255, 0, 63),
|
|
|
241: (255, 170, 191),
|
|
|
242: (189, 0, 46),
|
|
|
243: (189, 126, 141),
|
|
|
244: (129, 0, 31),
|
|
|
245: (129, 86, 96),
|
|
|
246: (104, 0, 25),
|
|
|
247: (104, 69, 78),
|
|
|
248: (79, 0, 19),
|
|
|
249: (79, 53, 59),
|
|
|
250: (51, 51, 51),
|
|
|
251: (80, 80, 80),
|
|
|
252: (105, 105, 105),
|
|
|
253: (130, 130, 130),
|
|
|
254: (190, 190, 190),
|
|
|
255: (255, 255, 255)
|
|
|
}
|
|
|
|
|
|
|
|
|
return aci_rgb_map.get(aci, (255, 255, 255))
|
|
|
|
|
|
|
|
|
|
|
|
def int_to_rgb(color_int):
|
|
|
"""Convert an integer to an (R, G, B) tuple."""
|
|
|
r = (color_int >> 16) & 255
|
|
|
g = (color_int >> 8) & 255
|
|
|
b = color_int & 255
|
|
|
return (r, g, b)
|
|
|
|
|
|
|
|
|
|
|
|
def get_hatch_color(entity):
|
|
|
|
|
|
if entity.dxf.hasattr('true_color'):
|
|
|
true_color = entity.dxf.true_color
|
|
|
rgb_color = int_to_rgb(true_color)
|
|
|
print(f"True color detected (RGB): {rgb_color}")
|
|
|
return rgb_color
|
|
|
|
|
|
color_index = entity.dxf.color
|
|
|
print("color_index = ", color_index)
|
|
|
|
|
|
|
|
|
if color_index == 0:
|
|
|
print("Color is ByLayer, checking layer color...")
|
|
|
layer_name = entity.dxf.layer
|
|
|
layer = entity.doc.layers.get(layer_name)
|
|
|
|
|
|
if layer:
|
|
|
layer_color_index = layer.dxf.color
|
|
|
print(f"Layer '{layer_name}' Color Index = {layer_color_index}")
|
|
|
return aci_to_rgb(layer_color_index)
|
|
|
else:
|
|
|
print(f"Layer '{layer_name}' not found, defaulting to white.")
|
|
|
return (255, 255, 255)
|
|
|
|
|
|
elif color_index == 256:
|
|
|
print("Color is ByBlock, checking block color or defaulting to white.")
|
|
|
block_color = (255, 255, 255)
|
|
|
|
|
|
|
|
|
if hasattr(entity, 'block'):
|
|
|
block_ref = entity.block
|
|
|
if block_ref.dxf.hasattr('color'):
|
|
|
block_color = aci_to_rgb(block_ref.dxf.color)
|
|
|
print(f"Block reference color found: {block_color}")
|
|
|
else:
|
|
|
print("Block has no color attribute, using default (white).")
|
|
|
return block_color
|
|
|
|
|
|
|
|
|
print(f"Entity Color Index = {color_index}")
|
|
|
if 1 <= color_index <= 255:
|
|
|
rgb_color = aci_to_rgb(color_index)
|
|
|
print(f"Converted RGB = {rgb_color}")
|
|
|
return rgb_color
|
|
|
|
|
|
|
|
|
print("Invalid or unhandled color index, defaulting to white.")
|
|
|
return (255, 255, 255)
|
|
|
|
|
|
def calculate_distance(p1, p2):
|
|
|
return math.sqrt((p1[0] - p2[0])**2 + (p1[1] - p2[1])**2)
|
|
|
|
|
|
def normalize_color(color):
|
|
|
"""Convert PDF color (range 0-1) to RGB (range 0-255)."""
|
|
|
return tuple(min(max(round(c * 255), 0), 255) for c in color)
|
|
|
|
|
|
def color_close_enough(c1, c2, threshold=10):
|
|
|
return all(abs(a - b) <= threshold for a, b in zip(c1, c2))
|
|
|
|
|
|
|
|
|
"""### Hatched areas"""
|
|
|
def get_hatched_areas(datadoc,filename,FinalRatio,rotationangle,SearchArray):
|
|
|
|
|
|
doc = ezdxf.readfile(filename)
|
|
|
doc.header['$MEASUREMENT'] = 1
|
|
|
msp = doc.modelspace()
|
|
|
trial=0
|
|
|
hatched_areas = []
|
|
|
threshold=0.001
|
|
|
TextFound = 0
|
|
|
j=0
|
|
|
unique_shapes = []
|
|
|
|
|
|
text_with_positions = []
|
|
|
text_color_mapping = {}
|
|
|
color_palette = [
|
|
|
(255, 0, 0), (0, 0, 255), (0, 255, 255), (0, 64, 0), (255, 204, 0),
|
|
|
(255, 128, 64), (255, 0, 128), (255, 128, 192), (128, 128, 255),
|
|
|
(128, 64, 0), (0, 255, 0), (0, 200, 0), (255, 128, 255), (128, 0, 255),
|
|
|
(0, 128, 192), (128, 0, 128), (128, 0, 0), (0, 128, 255), (149, 1, 70),
|
|
|
(255, 182, 128), (222, 48, 71), (240, 0, 112), (255, 0, 255),
|
|
|
(192, 46, 65), (0, 0, 128), (0, 128, 64), (255, 255, 0), (128, 0, 80),
|
|
|
(255, 255, 128), (90, 255, 140), (255, 200, 20), (91, 16, 51),
|
|
|
(90, 105, 138), (114, 10, 138), (36, 82, 78), (225, 105, 190),
|
|
|
(108, 150, 170), (11, 35, 75), (42, 176, 170), (255, 176, 170),
|
|
|
(209, 151, 15), (81, 27, 85), (226, 106, 122), (67, 119, 149),
|
|
|
(159, 179, 140), (159, 179, 30), (255, 85, 198), (255, 27, 85),
|
|
|
(188, 158, 8), (140, 188, 120), (59, 61, 52), (65, 81, 21),
|
|
|
(212, 255, 174), (15, 164, 90), (41, 217, 245), (213, 23, 182),
|
|
|
(11, 85, 169), (78, 153, 239), (0, 66, 141), (64, 98, 232),
|
|
|
(140, 112, 255), (57, 33, 154), (194, 117, 252), (116, 92, 135),
|
|
|
(74, 43, 98), (188, 13, 123), (129, 58, 91), (255, 128, 100),
|
|
|
(171, 122, 145), (255, 98, 98), (222, 48, 77)
|
|
|
]
|
|
|
|
|
|
import re
|
|
|
|
|
|
Legendarray = []
|
|
|
|
|
|
if(SearchArray):
|
|
|
for i in range(len(SearchArray)):
|
|
|
|
|
|
if (SearchArray[i][0] and SearchArray[i][1] and SearchArray[i][2]):
|
|
|
for text_entity in doc.modelspace().query('TEXT MTEXT'):
|
|
|
text = text_entity.text.strip() if hasattr(text_entity, 'text') else ""
|
|
|
|
|
|
if(text.startswith(SearchArray[i][0]) and len(text)==int(SearchArray[i][2])):
|
|
|
position = text_entity.dxf.insert
|
|
|
x, y = position.x, position.y
|
|
|
|
|
|
for text_entity in doc.modelspace().query('TEXT MTEXT'):
|
|
|
NBS = text_entity.text.strip() if hasattr(text_entity, 'text') else ""
|
|
|
if (NBS.startswith(SearchArray[i][1])):
|
|
|
positionNBS = text_entity.dxf.insert
|
|
|
xNBS, yNBS = positionNBS.x, positionNBS.y
|
|
|
|
|
|
if(x == xNBS or y == yNBS):
|
|
|
textNBS=NBS
|
|
|
break
|
|
|
|
|
|
else:
|
|
|
textNBS = None
|
|
|
|
|
|
|
|
|
|
|
|
nearest_hatch = None
|
|
|
min_distance = float('inf')
|
|
|
detected_color = (255, 255, 255)
|
|
|
|
|
|
|
|
|
for hatch in doc.modelspace().query('HATCH'):
|
|
|
if hatch.paths:
|
|
|
for path in hatch.paths:
|
|
|
if path.type == 1:
|
|
|
vertices = [v[:2] for v in path.vertices]
|
|
|
|
|
|
centroid_x = sum(v[0] for v in vertices) / len(vertices)
|
|
|
centroid_y = sum(v[1] for v in vertices) / len(vertices)
|
|
|
centroid = (centroid_x, centroid_y)
|
|
|
|
|
|
|
|
|
distance = calculate_distance((x, y), centroid)
|
|
|
|
|
|
|
|
|
if distance < min_distance:
|
|
|
min_distance = distance
|
|
|
nearest_hatch = hatch
|
|
|
|
|
|
|
|
|
current_color = get_hatch_color(hatch)
|
|
|
if current_color != (255, 255, 255):
|
|
|
detected_color = current_color
|
|
|
break
|
|
|
|
|
|
|
|
|
|
|
|
Legendarray.append([text, textNBS, (x, y), detected_color])
|
|
|
print("text_with_positions=",text_with_positions)
|
|
|
|
|
|
elif (SearchArray[i][0] and SearchArray[i][2]):
|
|
|
for text_entity in doc.modelspace().query('TEXT MTEXT'):
|
|
|
text = text_entity.text.strip() if hasattr(text_entity, 'text') else ""
|
|
|
|
|
|
if(text.startswith(SearchArray[i][0]) and len(text)==int(SearchArray[i][2])):
|
|
|
position = text_entity.dxf.insert
|
|
|
x, y = position.x, position.y
|
|
|
textNBS = None
|
|
|
nearest_hatch = None
|
|
|
min_distance = float('inf')
|
|
|
detected_color = (255, 255, 255)
|
|
|
|
|
|
|
|
|
for hatch in doc.modelspace().query('HATCH'):
|
|
|
if hatch.paths:
|
|
|
for path in hatch.paths:
|
|
|
if path.type == 1:
|
|
|
vertices = [v[:2] for v in path.vertices]
|
|
|
|
|
|
centroid_x = sum(v[0] for v in vertices) / len(vertices)
|
|
|
centroid_y = sum(v[1] for v in vertices) / len(vertices)
|
|
|
centroid = (centroid_x, centroid_y)
|
|
|
|
|
|
|
|
|
distance = calculate_distance((x, y), centroid)
|
|
|
|
|
|
|
|
|
if distance < min_distance:
|
|
|
min_distance = distance
|
|
|
nearest_hatch = hatch
|
|
|
|
|
|
|
|
|
current_color = get_hatch_color(hatch)
|
|
|
if current_color != (255, 255, 255):
|
|
|
detected_color = current_color
|
|
|
break
|
|
|
|
|
|
|
|
|
|
|
|
Legendarray.append([text, textNBS, (x, y), detected_color])
|
|
|
print("text_with_positions=",text_with_positions)
|
|
|
|
|
|
elif(SearchArray[i][0]):
|
|
|
for text_entity in doc.modelspace().query('TEXT MTEXT'):
|
|
|
text = text_entity.text.strip() if hasattr(text_entity, 'text') else ""
|
|
|
|
|
|
if(text.startswith(SearchArray[i][0])):
|
|
|
position = text_entity.dxf.insert
|
|
|
x, y = position.x, position.y
|
|
|
textNBS = None
|
|
|
nearest_hatch = None
|
|
|
min_distance = float('inf')
|
|
|
detected_color = (255, 255, 255)
|
|
|
|
|
|
|
|
|
for hatch in doc.modelspace().query('HATCH'):
|
|
|
if hatch.paths:
|
|
|
for path in hatch.paths:
|
|
|
if path.type == 1:
|
|
|
vertices = [v[:2] for v in path.vertices]
|
|
|
|
|
|
centroid_x = sum(v[0] for v in vertices) / len(vertices)
|
|
|
centroid_y = sum(v[1] for v in vertices) / len(vertices)
|
|
|
centroid = (centroid_x, centroid_y)
|
|
|
|
|
|
|
|
|
distance = calculate_distance((x, y), centroid)
|
|
|
|
|
|
|
|
|
if distance < min_distance:
|
|
|
min_distance = distance
|
|
|
nearest_hatch = hatch
|
|
|
|
|
|
|
|
|
current_color = get_hatch_color(hatch)
|
|
|
if current_color != (255, 255, 255):
|
|
|
detected_color = current_color
|
|
|
break
|
|
|
|
|
|
|
|
|
|
|
|
Legendarray.append([text, textNBS, (x, y), detected_color])
|
|
|
print("text_with_positions=",Legendarray)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
grouped = {}
|
|
|
for entry in Legendarray:
|
|
|
key = entry[0]
|
|
|
grouped.setdefault(key, []).append(entry)
|
|
|
|
|
|
|
|
|
filtered_results = []
|
|
|
for key, entries in grouped.items():
|
|
|
|
|
|
complete = next((entry for entry in entries if entry[1] is not None), None)
|
|
|
if complete:
|
|
|
filtered_results.append(complete)
|
|
|
else:
|
|
|
|
|
|
filtered_results.append(entries[0])
|
|
|
|
|
|
Legendarray=filtered_results
|
|
|
|
|
|
|
|
|
|
|
|
for entity in doc.modelspace().query('TEXT MTEXT'):
|
|
|
if hasattr(entity, 'text'):
|
|
|
text = entity.text
|
|
|
if text.startswith('C') and (len(text) > 1 and (text[1].isdigit() or text[1].upper() == 'T' or text[1].upper() == 'L')):
|
|
|
parts = text.split(' ')
|
|
|
|
|
|
main_text = parts[0]
|
|
|
|
|
|
|
|
|
|
|
|
position = entity.dxf.insert
|
|
|
|
|
|
|
|
|
|
|
|
if main_text not in text_color_mapping:
|
|
|
|
|
|
color_index = len(text_color_mapping) % len(color_palette)
|
|
|
text_color_mapping[main_text] = color_palette[color_index]
|
|
|
|
|
|
|
|
|
color = text_color_mapping[main_text]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
text_with_positions.append([main_text, position, color])
|
|
|
|
|
|
for entity in msp:
|
|
|
if entity.dxftype() == 'HATCH':
|
|
|
|
|
|
for path in entity.paths:
|
|
|
vertices = []
|
|
|
|
|
|
if path.type == 1:
|
|
|
|
|
|
vertices = [(vertex[0] * FinalRatio, vertex[1] * FinalRatio) for vertex in path.vertices]
|
|
|
|
|
|
if len(vertices) > 3:
|
|
|
poly = ShapelyPolygon(vertices)
|
|
|
minx, miny, maxx, maxy = poly.bounds
|
|
|
width = maxx - minx
|
|
|
height = maxy - miny
|
|
|
|
|
|
if (poly.area > 0.9 and (height > 0.7 and width > 0.7)):
|
|
|
area1 = round(poly.area, 3)
|
|
|
perimeter = round(poly.length, 3)
|
|
|
normalized_vertices = normalize_vertices(vertices)
|
|
|
|
|
|
rgb_color = get_hatch_color(entity)
|
|
|
if(rgb_color == (255, 255, 255)):
|
|
|
if(len(text_with_positions)>0):
|
|
|
|
|
|
for text, position, color in text_with_positions:
|
|
|
text_position = Point(position[0], position[1])
|
|
|
|
|
|
if poly.contains(text_position):
|
|
|
rgb_color = color
|
|
|
break
|
|
|
|
|
|
duplicate_found = False
|
|
|
for existing_vertices, existing_area in unique_shapes:
|
|
|
if normalized_vertices == existing_vertices and areas_are_similar(area1, existing_area):
|
|
|
duplicate_found = True
|
|
|
break
|
|
|
|
|
|
if not duplicate_found:
|
|
|
|
|
|
unique_shapes.append((normalized_vertices, area1))
|
|
|
hatched_areas.append([vertices, area1, perimeter, rgb_color])
|
|
|
|
|
|
elif path.type == 2:
|
|
|
|
|
|
|
|
|
try:
|
|
|
path.spline_edges_to_line_edges(factor=8)
|
|
|
except Exception:
|
|
|
|
|
|
pass
|
|
|
|
|
|
vert = []
|
|
|
|
|
|
for edge in path.edges:
|
|
|
|
|
|
if hasattr(edge, "start") and hasattr(edge, "end"):
|
|
|
sx, sy = edge.start
|
|
|
ex, ey = edge.end
|
|
|
|
|
|
vert.append((sx * FinalRatio, sy * FinalRatio))
|
|
|
else:
|
|
|
|
|
|
if hasattr(edge, "control_points") and edge.control_points:
|
|
|
for cp in edge.control_points:
|
|
|
vert.append((cp[0] * FinalRatio, cp[1] * FinalRatio))
|
|
|
elif hasattr(edge, "fit_points") and edge.fit_points:
|
|
|
for fp in edge.fit_points:
|
|
|
vert.append((fp[0] * FinalRatio, fp[1] * FinalRatio))
|
|
|
else:
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
if path.edges and hasattr(path.edges[-1], "end"):
|
|
|
ex, ey = path.edges[-1].end
|
|
|
vert.append((ex * FinalRatio, ey * FinalRatio))
|
|
|
|
|
|
|
|
|
cleaned = []
|
|
|
for p in vert:
|
|
|
if not cleaned or (abs(cleaned[-1][0] - p[0]) > 1e-9 or abs(cleaned[-1][1] - p[1]) > 1e-9):
|
|
|
cleaned.append(p)
|
|
|
vert = cleaned
|
|
|
|
|
|
|
|
|
if len(vert) >= 3:
|
|
|
poly = ShapelyPolygon(vert)
|
|
|
minx, miny, maxx, maxy = poly.bounds
|
|
|
width = maxx - minx
|
|
|
height = maxy - miny
|
|
|
|
|
|
if (poly.area > 0.1 and (height > 0.1 and width > 0.1)):
|
|
|
area1 = round(poly.area, 3)
|
|
|
perimeter = round(poly.length, 3)
|
|
|
normalized_vertices = normalize_vertices(vert)
|
|
|
rgb_color = get_hatch_color(entity)
|
|
|
if rgb_color == (255, 255, 255) and len(text_with_positions) > 0:
|
|
|
for text, position, color in text_with_positions:
|
|
|
text_position = Point(position[0], position[1])
|
|
|
if poly.contains(text_position):
|
|
|
rgb_color = color
|
|
|
break
|
|
|
|
|
|
duplicate_found = False
|
|
|
for existing_vertices, existing_area in unique_shapes:
|
|
|
if normalized_vertices == existing_vertices and areas_are_similar(area1, existing_area):
|
|
|
duplicate_found = True
|
|
|
break
|
|
|
|
|
|
if not duplicate_found:
|
|
|
unique_shapes.append((normalized_vertices, area1))
|
|
|
hatched_areas.append([vert, area1, perimeter, rgb_color])
|
|
|
|
|
|
elif entity.dxftype() == 'SOLID':
|
|
|
vertices = [entity.dxf.vtx0 * (FinalRatio), entity.dxf.vtx1* (FinalRatio), entity.dxf.vtx2* (FinalRatio), entity.dxf.vtx3* (FinalRatio)]
|
|
|
poly = ShapelyPolygon(vertices)
|
|
|
minx, miny, maxx, maxy = poly.bounds
|
|
|
|
|
|
|
|
|
width = maxx - minx
|
|
|
height = maxy - miny
|
|
|
|
|
|
if (poly.area > 0.9 and (height > 0.7 and width > 0.7)):
|
|
|
area1 = round(poly.area, 3)
|
|
|
perimeter = round(poly.length, 3)
|
|
|
normalized_vertices = normalize_vertices(vertices)
|
|
|
|
|
|
duplicate_found = False
|
|
|
for existing_vertices, existing_area in unique_shapes:
|
|
|
if normalized_vertices == existing_vertices or areas_are_similar(area1, existing_area):
|
|
|
duplicate_found = True
|
|
|
break
|
|
|
|
|
|
if not duplicate_found:
|
|
|
rgb_color = get_hatch_color(entity)
|
|
|
unique_shapes.append((normalized_vertices, area1))
|
|
|
hatched_areas.append([vertices, area1, perimeter, rgb_color])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
elif entity.dxftype() == 'LWPOLYLINE':
|
|
|
vertices = []
|
|
|
lwpolyline = entity
|
|
|
points = lwpolyline.get_points()
|
|
|
flag = 0
|
|
|
|
|
|
|
|
|
for i in range(len(points)):
|
|
|
vertices.append([points[i][0] * FinalRatio, points[i][1] * FinalRatio])
|
|
|
|
|
|
|
|
|
if len(vertices) > 3:
|
|
|
|
|
|
if vertices[0][0] == vertices[-1][0] or vertices[0][1] == vertices[-1][1]:
|
|
|
poly = ShapelyPolygon(vertices)
|
|
|
minx, miny, maxx, maxy = poly.bounds
|
|
|
|
|
|
|
|
|
width = maxx - minx
|
|
|
height = maxy - miny
|
|
|
|
|
|
|
|
|
if (poly.area > 0.9 and (height > 0.7 and width > 0.7)):
|
|
|
area1 = round(poly.area, 3)
|
|
|
perimeter = round(poly.length, 3)
|
|
|
|
|
|
normalized_vertices = normalize_vertices(vertices)
|
|
|
|
|
|
duplicate_found = False
|
|
|
for existing_vertices, existing_area in unique_shapes:
|
|
|
if normalized_vertices == existing_vertices or areas_are_similar(area1, existing_area):
|
|
|
duplicate_found = True
|
|
|
break
|
|
|
|
|
|
if not duplicate_found:
|
|
|
rgb_color = get_hatch_color(entity)
|
|
|
unique_shapes.append((normalized_vertices, area1))
|
|
|
hatched_areas.append([vertices, area1, perimeter, rgb_color])
|
|
|
|
|
|
|
|
|
|
|
|
elif entity.dxftype() == 'POLYLINE':
|
|
|
|
|
|
|
|
|
|
|
|
flag=0
|
|
|
vertices = [(v.dxf.location.x * (FinalRatio), v.dxf.location.y * (FinalRatio)) for v in entity.vertices]
|
|
|
|
|
|
|
|
|
if(len(vertices)>3):
|
|
|
|
|
|
if(vertices[0][0] == vertices[len(vertices)-1][0] or vertices[0][1] == vertices[len(vertices)-1][1]):
|
|
|
|
|
|
poly=ShapelyPolygon(vertices)
|
|
|
minx, miny, maxx, maxy = poly.bounds
|
|
|
|
|
|
|
|
|
width = maxx - minx
|
|
|
height = maxy - miny
|
|
|
|
|
|
if (poly.area > 0.9 and (height > 0.7 and width > 0.7)):
|
|
|
area1 = round(poly.area,3)
|
|
|
perimeter = round (poly.length,3)
|
|
|
normalized_vertices = normalize_vertices(vertices)
|
|
|
|
|
|
duplicate_found = False
|
|
|
for existing_vertices, existing_area in unique_shapes:
|
|
|
if normalized_vertices == existing_vertices or areas_are_similar(area1, existing_area):
|
|
|
duplicate_found = True
|
|
|
break
|
|
|
|
|
|
if not duplicate_found:
|
|
|
rgb_color = get_hatch_color(entity)
|
|
|
unique_shapes.append((normalized_vertices, area1))
|
|
|
hatched_areas.append([vertices, area1, perimeter, rgb_color])
|
|
|
|
|
|
|
|
|
elif entity.dxftype() == 'SPLINE':
|
|
|
spline_entity = entity
|
|
|
vertices = []
|
|
|
control_points = spline_entity.control_points
|
|
|
if(len(control_points)>3):
|
|
|
for i in range(len(control_points)):
|
|
|
vertices.append([control_points[i][0]* (FinalRatio),control_points[i][1]* (FinalRatio)])
|
|
|
poly=ShapelyPolygon(vertices)
|
|
|
|
|
|
minx, miny, maxx, maxy = poly.bounds
|
|
|
|
|
|
|
|
|
width = maxx - minx
|
|
|
height = maxy - miny
|
|
|
|
|
|
|
|
|
if (poly.area > 0.9 and (height > 0.7 and width > 0.7)):
|
|
|
area1 = round(poly.area,3)
|
|
|
perimeter = round (poly.length,3)
|
|
|
normalized_vertices = normalize_vertices(vertices)
|
|
|
|
|
|
duplicate_found = False
|
|
|
for existing_vertices, existing_area in unique_shapes:
|
|
|
if normalized_vertices == existing_vertices or areas_are_similar(area1, existing_area):
|
|
|
duplicate_found = True
|
|
|
break
|
|
|
|
|
|
if not duplicate_found:
|
|
|
rgb_color = get_hatch_color(entity)
|
|
|
unique_shapes.append((normalized_vertices, area1))
|
|
|
hatched_areas.append([vertices, area1, perimeter, rgb_color])
|
|
|
|
|
|
sorted_data = sorted(hatched_areas, key=lambda x: x[1])
|
|
|
return sorted_data,Legendarray
|
|
|
|
|
|
"""### Rotate polygon"""
|
|
|
|
|
|
|
|
|
|
|
|
def rotate_point(point, angle,pdfrotation,width,height, center_point=(0, 0)):
|
|
|
"""Rotates a point around center_point(origin by default)
|
|
|
Angle is in degrees.
|
|
|
Rotation is counter-clockwise
|
|
|
"""
|
|
|
angle_rad = radians(angle % 360)
|
|
|
|
|
|
new_point = (point[0] - center_point[0], point[1] - center_point[1])
|
|
|
new_point = (new_point[0] * cos(angle_rad) - new_point[1] * sin(angle_rad),
|
|
|
new_point[0] * sin(angle_rad) + new_point[1] * cos(angle_rad))
|
|
|
|
|
|
if pdfrotation!=0:
|
|
|
|
|
|
new_point = (new_point[0]+width + center_point[0], new_point[1] + center_point[1])
|
|
|
else:
|
|
|
|
|
|
new_point = (new_point[0] + center_point[0], new_point[1]+ height + center_point[1])
|
|
|
|
|
|
return new_point
|
|
|
|
|
|
|
|
|
def rotate_polygon(polygon, angle, pdfrotation,width,height,center_point=(0, 0)):
|
|
|
"""Rotates the given polygon which consists of corners represented as (x,y)
|
|
|
around center_point (origin by default)
|
|
|
Rotation is counter-clockwise
|
|
|
Angle is in degrees
|
|
|
"""
|
|
|
rotated_polygon = []
|
|
|
for corner in polygon:
|
|
|
rotated_corner = rotate_point(corner, angle,pdfrotation,width,height, center_point)
|
|
|
rotated_polygon.append(rotated_corner)
|
|
|
return rotated_polygon
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def Create_DF(dxfpath,datadoc,hatched_areas,pdf_content=0):
|
|
|
|
|
|
if pdf_content:
|
|
|
FinalRatio,width_dxf= RetriveRatio(datadoc,dxfpath,pdf_content)
|
|
|
else:
|
|
|
FinalRatio,width_dxf= RetriveRatio(datadoc,dxfpath)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SimilarAreaDictionary= pd.DataFrame(columns=['Guess','Color','Occurences','Area','Total Area','Perimeter','Total Perimeter','Length','Total Length','Texts','Comments'])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TotalArea=0
|
|
|
TotalPerimeter=0
|
|
|
for shape in hatched_areas:
|
|
|
area = shape[1]
|
|
|
perimeter = shape[2]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TotalArea = area
|
|
|
TotalPerimeter = perimeter
|
|
|
tol=0
|
|
|
condition1 = (SimilarAreaDictionary['Area'] >= area - tol) & (SimilarAreaDictionary['Area'] <= area +tol)
|
|
|
condition2 = (SimilarAreaDictionary['Perimeter'] >= perimeter -tol) & (SimilarAreaDictionary['Perimeter'] <= perimeter +tol)
|
|
|
combined_condition = condition1 & condition2
|
|
|
|
|
|
if any(combined_condition):
|
|
|
index = np.where(combined_condition)[0][0]
|
|
|
SimilarAreaDictionary.at[index, 'Occurences'] += 1
|
|
|
SimilarAreaDictionary.at[index, 'Total Area'] = SimilarAreaDictionary.at[index, 'Total Area'] + area
|
|
|
SimilarAreaDictionary.at[index, 'Total Perimeter'] = SimilarAreaDictionary.at[index, 'Total Perimeter'] + perimeter
|
|
|
else:
|
|
|
TotalArea=area
|
|
|
TotalPerimeter=perimeter
|
|
|
new_data = {'Area': area, 'Total Area': TotalArea ,'Perimeter': perimeter, 'Total Perimeter': TotalPerimeter, 'Occurences': 1, 'Color':shape[3],'Comments':''}
|
|
|
SimilarAreaDictionary = pd.concat([SimilarAreaDictionary, pd.DataFrame([new_data])], ignore_index=True)
|
|
|
|
|
|
|
|
|
return SimilarAreaDictionary
|
|
|
"""### Draw on Image and PDF"""
|
|
|
def color_close_enough(c1, c2, threshold=10):
|
|
|
return all(abs(a - b) <= threshold for a, b in zip(c1, c2))
|
|
|
|
|
|
|
|
|
|
|
|
def group_vertices(raw):
|
|
|
"""Convert flat list [x1,y1,x2,y2,...] into [[x1,y1],[x2,y2],...]"""
|
|
|
if not raw or len(raw) < 2:
|
|
|
return []
|
|
|
return [[raw[i], raw[i+1]] for i in range(0, len(raw), 2)]
|
|
|
|
|
|
|
|
|
def extract_measurement(obj):
|
|
|
"""Extract numeric measurement from annotation's /Contents field."""
|
|
|
contents = obj.get("/Contents")
|
|
|
if not contents:
|
|
|
return None
|
|
|
|
|
|
text = str(contents)
|
|
|
m = re.search(r"([0-9]*\.?[0-9]+)", text)
|
|
|
return float(m.group(1)) if m else None
|
|
|
|
|
|
def adjustannotations(OutputPdfStage1,text_with_positions):
|
|
|
input_pdf_path = OutputPdfStage1
|
|
|
output_pdf_path = "Final-WallsAdjusted.pdf"
|
|
|
annotations_data = []
|
|
|
Trim=0
|
|
|
|
|
|
|
|
|
pdf_bytes_io = BytesIO(OutputPdfStage1)
|
|
|
|
|
|
reader = PdfReader(pdf_bytes_io)
|
|
|
writer = PdfWriter()
|
|
|
|
|
|
|
|
|
writer.append_pages_from_reader(reader)
|
|
|
|
|
|
|
|
|
metadata = reader.metadata
|
|
|
writer.add_metadata(metadata)
|
|
|
|
|
|
for page_index, page in enumerate(writer.pages):
|
|
|
if "/Annots" not in page:
|
|
|
continue
|
|
|
|
|
|
for annot in page["/Annots"]:
|
|
|
obj = annot.get_object()
|
|
|
subtype = obj.get("/Subtype")
|
|
|
|
|
|
|
|
|
if subtype == "/Line":
|
|
|
raw_vertices = obj.get("/L", [])
|
|
|
else:
|
|
|
raw_vertices = obj.get("/Vertices", [])
|
|
|
vertices = group_vertices(raw_vertices)
|
|
|
|
|
|
|
|
|
raw_color = obj.get("/C")
|
|
|
try:
|
|
|
annot_color = normalize_color(raw_color)
|
|
|
except:
|
|
|
annot_color = raw_color
|
|
|
|
|
|
|
|
|
measurement = extract_measurement(obj)
|
|
|
|
|
|
area = measurement if subtype == "/Polygon" else None
|
|
|
perimeter = measurement if subtype in ["/Line", "/PolyLine"] else None
|
|
|
|
|
|
|
|
|
matched_text = None
|
|
|
matched_nbs = None
|
|
|
if subtype in ["/Line", "/PolyLine", "/Polygon"] and raw_color:
|
|
|
matched_entry = next(
|
|
|
((t, n) for t, n, _, c in text_with_positions if color_close_enough(annot_color, c)),
|
|
|
(None, None)
|
|
|
)
|
|
|
matched_text, matched_nbs = matched_entry
|
|
|
combined = ""
|
|
|
if matched_text and matched_nbs:
|
|
|
combined = f"{matched_text} - {matched_nbs}"
|
|
|
elif matched_text:
|
|
|
combined = matched_text
|
|
|
elif matched_nbs:
|
|
|
combined = matched_nbs
|
|
|
if combined:
|
|
|
obj.update({NameObject("/T"): TextStringObject(combined)})
|
|
|
|
|
|
|
|
|
if subtype == "/PolyLine" and obj.get("/Subj", "") == "Perimeter Measurement":
|
|
|
|
|
|
title = obj.get("/T", "")
|
|
|
title_str = str(title).strip()
|
|
|
|
|
|
if title_str == "Skirting Measurement":
|
|
|
|
|
|
Trim=0
|
|
|
elif title_str == "Trim Measurement":
|
|
|
|
|
|
Trim=1
|
|
|
|
|
|
obj.update({
|
|
|
NameObject("/Measure"): DictionaryObject({
|
|
|
NameObject("/Type"): NameObject("/Measure"),
|
|
|
NameObject("/L"): DictionaryObject({
|
|
|
NameObject("/G"): FloatObject(1),
|
|
|
NameObject("/U"): TextStringObject("m"),
|
|
|
}),
|
|
|
}),
|
|
|
NameObject("/IT"): NameObject("/LineDimension"),
|
|
|
NameObject("/Subj"): TextStringObject("Length Measurement"),
|
|
|
})
|
|
|
if subtype == "/Polygon" and obj.get("/Subj", "") == "Area Measurement":
|
|
|
obj.update({
|
|
|
NameObject("/Measure"): DictionaryObject({
|
|
|
NameObject("/Type"): NameObject("/Measure"),
|
|
|
NameObject("/Area"): DictionaryObject({
|
|
|
NameObject("/G"): FloatObject(1),
|
|
|
NameObject("/U"): TextStringObject("sq m"),
|
|
|
}),
|
|
|
}),
|
|
|
NameObject("/IT"): NameObject("/Area_Annotation"),
|
|
|
NameObject("/Subj"): TextStringObject("Area Measurement"),
|
|
|
})
|
|
|
|
|
|
|
|
|
annotations_data.append([
|
|
|
vertices,
|
|
|
area,
|
|
|
perimeter,
|
|
|
annot_color,
|
|
|
matched_text,
|
|
|
matched_nbs,
|
|
|
Trim,
|
|
|
])
|
|
|
|
|
|
|
|
|
|
|
|
output_pdf_io = BytesIO()
|
|
|
writer.write(output_pdf_io)
|
|
|
output_pdf_io.seek(0)
|
|
|
|
|
|
print(f"Annotations updated and saved to {output_pdf_path}")
|
|
|
return output_pdf_io.read() , annotations_data
|
|
|
|
|
|
|
|
|
def _order_points_monotonic(points, eps=1e-9):
|
|
|
"""
|
|
|
Order a list of (x,y) points along their main axis (PCA),
|
|
|
then refine ordering by greedy nearest-neighbor but never stepping
|
|
|
backward along the principal-axis projection (monotonic).
|
|
|
Returns ordered list of (x,y).
|
|
|
"""
|
|
|
if not points:
|
|
|
return []
|
|
|
if len(points) <= 2:
|
|
|
return list(points)
|
|
|
|
|
|
pts = np.array(points, dtype=float)
|
|
|
mean = pts.mean(axis=0)
|
|
|
centered = pts - mean
|
|
|
|
|
|
|
|
|
try:
|
|
|
_, _, vt = np.linalg.svd(centered, full_matrices=False)
|
|
|
principal = vt[0]
|
|
|
except Exception:
|
|
|
|
|
|
return sorted(points, key=lambda p: (p[0], p[1]))
|
|
|
|
|
|
|
|
|
proj = centered.dot(principal)
|
|
|
|
|
|
|
|
|
idxs = list(np.argsort(proj))
|
|
|
proj_sorted = proj[idxs]
|
|
|
|
|
|
|
|
|
ordered = []
|
|
|
remaining = set(idxs)
|
|
|
curr_idx = idxs[0]
|
|
|
ordered.append(tuple(pts[curr_idx].tolist()))
|
|
|
remaining.remove(curr_idx)
|
|
|
|
|
|
while remaining:
|
|
|
|
|
|
cand = [i for i in remaining if proj[i] >= proj[curr_idx] - eps]
|
|
|
if not cand:
|
|
|
|
|
|
cand = list(remaining)
|
|
|
|
|
|
|
|
|
curr_pt = pts[curr_idx]
|
|
|
best = min(cand, key=lambda i: np.hypot(*(pts[i] - curr_pt)))
|
|
|
ordered.append(tuple(pts[best].tolist()))
|
|
|
remaining.remove(best)
|
|
|
curr_idx = best
|
|
|
|
|
|
return ordered
|
|
|
|
|
|
|
|
|
def find_nearby_vertices(shapes, distance_threshold=0.5):
|
|
|
"""
|
|
|
shapes: list structured as [ vertices_list, area, perimeter, color_tuple ]
|
|
|
where vertices_list is [(x,y), ...]
|
|
|
distance_threshold: maximum absolute difference in x and y to consider two vertices 'close'
|
|
|
|
|
|
Returns:
|
|
|
intersections: [ [ [(x,y), ...] ], ... ]
|
|
|
grouped_shapes: list of grouped shapes (each group is a merged list of shape indices)
|
|
|
"""
|
|
|
intersections = []
|
|
|
grouped_shapes = []
|
|
|
used = set()
|
|
|
|
|
|
n = len(shapes)
|
|
|
for i in range(n):
|
|
|
for j in range(i + 1, n):
|
|
|
shape1 = shapes[i]
|
|
|
shape2 = shapes[j]
|
|
|
|
|
|
|
|
|
if (
|
|
|
shape1[3] == shape2[3] or
|
|
|
shape1[3] in [(0,0,0), (255,255,255)] or
|
|
|
shape2[3] in [(0,0,0), (255,255,255)]
|
|
|
):
|
|
|
continue
|
|
|
|
|
|
verts1 = list(shape1[0])
|
|
|
verts2 = list(shape2[0])
|
|
|
if len(verts1) > 1 and verts1[0] == verts1[-1]:
|
|
|
verts1 = verts1[:-1]
|
|
|
if len(verts2) > 1 and verts2[0] == verts2[-1]:
|
|
|
verts2 = verts2[:-1]
|
|
|
|
|
|
close_points = []
|
|
|
for (x1, y1) in verts1:
|
|
|
for (x2, y2) in verts2:
|
|
|
if abs(x1 - x2) <= distance_threshold and abs(y1 - y2) <= distance_threshold:
|
|
|
p = (float(x2), float(y2))
|
|
|
if p not in close_points:
|
|
|
close_points.append(p)
|
|
|
|
|
|
if close_points:
|
|
|
|
|
|
ordered = _order_points_monotonic(close_points)
|
|
|
dedup = []
|
|
|
for pt in ordered:
|
|
|
if not dedup or (abs(pt[0]-dedup[-1][0])>1e-6 or abs(pt[1]-dedup[-1][1])>1e-6):
|
|
|
dedup.append(pt)
|
|
|
|
|
|
if len(dedup) >= 2:
|
|
|
intersections.append([dedup])
|
|
|
|
|
|
|
|
|
group = {i, j}
|
|
|
|
|
|
merged = False
|
|
|
for g in grouped_shapes:
|
|
|
if not group.isdisjoint(g):
|
|
|
g.update(group)
|
|
|
merged = True
|
|
|
break
|
|
|
if not merged:
|
|
|
grouped_shapes.append(group)
|
|
|
|
|
|
|
|
|
grouped_shapes_final = []
|
|
|
for g in grouped_shapes:
|
|
|
merged_verts = []
|
|
|
colors = set()
|
|
|
for idx in g:
|
|
|
merged_verts.extend(shapes[idx][0])
|
|
|
colors.add(shapes[idx][3])
|
|
|
grouped_shapes_final.append([merged_verts, None, None, list(colors)])
|
|
|
|
|
|
return intersections, grouped_shapes_final
|
|
|
|
|
|
def process_polygons(polygons, imgg, dxfratio, rotationangle, derotationMatrix, rotationOld, width, height):
|
|
|
processed_shapes = []
|
|
|
|
|
|
for polygon in polygons:
|
|
|
cntPoints = []
|
|
|
cntPoints1 = []
|
|
|
shapeePerimeter = []
|
|
|
shapeeArea = []
|
|
|
Text_Detected = 0
|
|
|
|
|
|
blackImgShapes = np.zeros(imgg.shape[:2], dtype="uint8")
|
|
|
blackImgShapes = cv2.cvtColor(blackImgShapes, cv2.COLOR_GRAY2BGR)
|
|
|
|
|
|
|
|
|
vertices = polygon
|
|
|
print("vertices = ", vertices)
|
|
|
|
|
|
for vertex in vertices:
|
|
|
for pt in vertex:
|
|
|
x = pt[0] * dxfratio
|
|
|
y = pt[1] * dxfratio
|
|
|
if rotationangle == 0 and y < 0:
|
|
|
y = -y
|
|
|
cntPoints.append([int(x), int(y)])
|
|
|
cntPoints1.append([x, y])
|
|
|
|
|
|
|
|
|
cv2.drawContours(blackImgShapes, [np.array(cntPoints)], -1, (255, 255, 255), thickness=-1)
|
|
|
|
|
|
|
|
|
for px, py in cntPoints1:
|
|
|
p = fitz.Point(px, py) * derotationMatrix
|
|
|
shapeePerimeter.append([p[0], p[1]])
|
|
|
|
|
|
|
|
|
shapeePerimeter = np.flip(np.array(shapeePerimeter), 1).tolist()
|
|
|
shapeePerimeter = rotate_polygon(shapeePerimeter, rotationangle, rotationOld, width, height)
|
|
|
|
|
|
|
|
|
processed_shapes.append(shapeePerimeter)
|
|
|
|
|
|
return processed_shapes
|
|
|
|
|
|
|
|
|
def process_grouped_shapes(grouped_shapes, imgg, dxfratio, rotationangle, derotationMatrix, rotationOld, width, height):
|
|
|
"""
|
|
|
Processes grouped shapes (merged polygons).
|
|
|
|
|
|
grouped_shapes: list of [vertices_list, None, None, colors_list]
|
|
|
Returns: list of [coords, area, perimeter, color]
|
|
|
"""
|
|
|
processed_shapes = []
|
|
|
|
|
|
for group in grouped_shapes:
|
|
|
merged_vertices = group[0]
|
|
|
colors = group[3]
|
|
|
|
|
|
cntPoints = []
|
|
|
cntPoints1 = []
|
|
|
shapeePerimeter = []
|
|
|
|
|
|
blackImgShapes = np.zeros(imgg.shape[:2], dtype="uint8")
|
|
|
blackImgShapes = cv2.cvtColor(blackImgShapes, cv2.COLOR_GRAY2BGR)
|
|
|
|
|
|
for pt in merged_vertices:
|
|
|
x = pt[0] * dxfratio
|
|
|
y = pt[1] * dxfratio
|
|
|
if rotationangle == 0 and y < 0:
|
|
|
y = -y
|
|
|
cntPoints.append([int(x), int(y)])
|
|
|
cntPoints1.append([x, y])
|
|
|
|
|
|
if not cntPoints:
|
|
|
continue
|
|
|
|
|
|
|
|
|
contour_np = np.array(cntPoints, dtype=np.int32)
|
|
|
cv2.drawContours(blackImgShapes, [contour_np], -1, (255, 255, 255), thickness=-1)
|
|
|
|
|
|
area = cv2.contourArea(contour_np)
|
|
|
perimeter = cv2.arcLength(contour_np, True)
|
|
|
|
|
|
|
|
|
for px, py in cntPoints1:
|
|
|
p = fitz.Point(px, py) * derotationMatrix
|
|
|
shapeePerimeter.append([p[0], p[1]])
|
|
|
|
|
|
|
|
|
shapeePerimeter = np.flip(np.array(shapeePerimeter), 1).tolist()
|
|
|
shapeePerimeter = rotate_polygon(shapeePerimeter, rotationangle, rotationOld, width, height)
|
|
|
|
|
|
|
|
|
processed_shapes.append([shapeePerimeter, area, perimeter, colors[0] if colors else (0,0,0)])
|
|
|
|
|
|
return processed_shapes
|
|
|
|
|
|
class DSU:
|
|
|
def __init__(self, n: int):
|
|
|
self.p = list(range(n))
|
|
|
self.r = [0]*n
|
|
|
def find(self, x: int) -> int:
|
|
|
while self.p[x] != x:
|
|
|
self.p[x] = self.p[self.p[x]]
|
|
|
x = self.p[x]
|
|
|
return x
|
|
|
def union(self, a: int, b: int):
|
|
|
ra, rb = self.find(a), self.find(b)
|
|
|
if ra == rb: return
|
|
|
if self.r[ra] < self.r[rb]:
|
|
|
self.p[ra] = rb
|
|
|
elif self.r[ra] > self.r[rb]:
|
|
|
self.p[rb] = ra
|
|
|
else:
|
|
|
self.p[rb] = ra
|
|
|
self.r[ra] += 1
|
|
|
|
|
|
|
|
|
def _ensure_vertices_list(verts_any) -> List[Tuple[float, float]]:
|
|
|
"""Accept [(x,y), ...] or [[(x,y), ...]] and return a flat list of (x,y) floats."""
|
|
|
if verts_any and isinstance(verts_any[0], (list, tuple)) and len(verts_any) == 1 \
|
|
|
and verts_any[0] and isinstance(verts_any[0][0], (list, tuple)):
|
|
|
verts_any = verts_any[0]
|
|
|
return [(float(x), float(y)) for (x, y) in verts_any]
|
|
|
|
|
|
def _bbox(verts):
|
|
|
xs = [p[0] for p in verts]; ys = [p[1] for p in verts]
|
|
|
return (min(xs), min(ys), max(xs), max(ys))
|
|
|
|
|
|
def _boxes_manhattan_gap(b1, b2):
|
|
|
minx1, miny1, maxx1, maxy1 = b1
|
|
|
minx2, miny2, maxx2, maxy2 = b2
|
|
|
dx = max(0.0, max(minx1 - maxx2, minx2 - maxx1))
|
|
|
dy = max(0.0, max(miny1 - maxy2, miny2 - maxy1))
|
|
|
return dx, dy
|
|
|
|
|
|
def _shapes_quick_close(verts1, verts2, axis_threshold):
|
|
|
"""Quick AABB gate then vertex-vertex proximity check (axis threshold)."""
|
|
|
b1 = _bbox(verts1); b2 = _bbox(verts2)
|
|
|
dx_box, dy_box = _boxes_manhattan_gap(b1, b2)
|
|
|
if dx_box > axis_threshold or dy_box > axis_threshold:
|
|
|
return False
|
|
|
for (x1,y1) in verts1:
|
|
|
for (x2,y2) in verts2:
|
|
|
if abs(x1 - x2) <= axis_threshold and abs(y1 - y2) <= axis_threshold:
|
|
|
return True
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def gather_and_union_groups(
|
|
|
hatched_areas: List[Any],
|
|
|
axis_threshold: float = 0.5,
|
|
|
require_diff_color: bool = True,
|
|
|
skip_colors: Set[Tuple[int,int,int]] = None,
|
|
|
) -> List[Tuple[List[Tuple[float,float]], float, float, Tuple[int,int,int]]]:
|
|
|
"""
|
|
|
Group shapes that are close (chain merging), then UNITE the original polygons of each group.
|
|
|
Returns list of (coords, area, perimeter, color) where coords is the outer boundary of each polygon
|
|
|
produced by the union (for MultiPolygon each part is appended as a separate result).
|
|
|
- hatched_areas items: [ vertices, area, perimeter, color ]
|
|
|
"""
|
|
|
if skip_colors is None:
|
|
|
skip_colors = set()
|
|
|
|
|
|
|
|
|
polys = []
|
|
|
colors = []
|
|
|
areas = []
|
|
|
perims = []
|
|
|
for item in hatched_areas:
|
|
|
if not item or len(item) < 4:
|
|
|
continue
|
|
|
verts_any, area, perim, color = item[0], item[1], item[2], item[3]
|
|
|
if color in skip_colors:
|
|
|
continue
|
|
|
verts = _ensure_vertices_list(verts_any)
|
|
|
if len(verts) < 3:
|
|
|
continue
|
|
|
try:
|
|
|
poly = Polygon(verts)
|
|
|
if not poly.is_valid or poly.is_empty:
|
|
|
|
|
|
poly = poly.buffer(0)
|
|
|
if poly.is_empty:
|
|
|
continue
|
|
|
polys.append(poly)
|
|
|
colors.append(tuple(color))
|
|
|
areas.append(float(area) if isinstance(area, (int,float)) else poly.area)
|
|
|
perims.append(float(perim) if isinstance(perim, (int,float)) else poly.length)
|
|
|
except Exception:
|
|
|
continue
|
|
|
|
|
|
m = len(polys)
|
|
|
if m == 0:
|
|
|
return []
|
|
|
|
|
|
|
|
|
dsu = DSU(m)
|
|
|
for i in range(m):
|
|
|
for j in range(i+1, m):
|
|
|
if require_diff_color and colors[i] == colors[j]:
|
|
|
continue
|
|
|
verts_i = list(polys[i].exterior.coords)
|
|
|
verts_j = list(polys[j].exterior.coords)
|
|
|
if _shapes_quick_close(verts_i, verts_j, axis_threshold):
|
|
|
dsu.union(i, j)
|
|
|
|
|
|
|
|
|
comp = {}
|
|
|
for i in range(m):
|
|
|
r = dsu.find(i)
|
|
|
comp.setdefault(r, []).append(i)
|
|
|
|
|
|
results = []
|
|
|
for root, members in comp.items():
|
|
|
group_polys = [polys[k] for k in members]
|
|
|
try:
|
|
|
unioned = unary_union(group_polys)
|
|
|
except Exception:
|
|
|
|
|
|
u = group_polys[0]
|
|
|
for p in group_polys[1:]:
|
|
|
u = u.union(p)
|
|
|
unioned = u
|
|
|
|
|
|
|
|
|
def _handle_polygon(polygon_geom, rep_color):
|
|
|
coords = [(float(x), float(y)) for x,y in polygon_geom.exterior.coords]
|
|
|
area = polygon_geom.area
|
|
|
perim = polygon_geom.length
|
|
|
results.append((coords, area, perim, rep_color))
|
|
|
|
|
|
|
|
|
rep_color = colors[members[0]] if members else (0,0,0)
|
|
|
|
|
|
if isinstance(unioned, Polygon):
|
|
|
_handle_polygon(unioned, rep_color)
|
|
|
elif isinstance(unioned, MultiPolygon) or isinstance(unioned, GeometryCollection):
|
|
|
|
|
|
for geom in getattr(unioned, "geoms", []):
|
|
|
if isinstance(geom, Polygon):
|
|
|
_handle_polygon(geom, rep_color)
|
|
|
else:
|
|
|
|
|
|
continue
|
|
|
|
|
|
return results
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def process_grouped_shapes(
|
|
|
grouped_polys: List[Tuple[List[Tuple[float,float]], float, float, Tuple[int,int,int]]],
|
|
|
imgg, dxfratio, rotationangle, derotationMatrix, rotationOld, width, height
|
|
|
) -> List[Tuple[List[Tuple[float,float]], float, float, Tuple[int,int,int]]]:
|
|
|
"""
|
|
|
For each grouped polygon (coords, area, perim, color), scale, derotate, rotate and return
|
|
|
processed polygon in same (coords, area, perim, color) format, coords as list of [x,y] floats.
|
|
|
"""
|
|
|
processed = []
|
|
|
for coords, area, perim, color in grouped_polys:
|
|
|
if not coords:
|
|
|
continue
|
|
|
|
|
|
cntPoints = []
|
|
|
cntPoints1 = []
|
|
|
|
|
|
for (x,y) in coords:
|
|
|
xs = x * dxfratio
|
|
|
ys = y * dxfratio
|
|
|
if rotationangle == 0 and ys < 0:
|
|
|
ys = -ys
|
|
|
cntPoints.append([int(xs), int(ys)])
|
|
|
cntPoints1.append([xs, ys])
|
|
|
|
|
|
if not cntPoints1:
|
|
|
continue
|
|
|
|
|
|
|
|
|
shapeePerimeter = []
|
|
|
for px, py in cntPoints1:
|
|
|
p = fitz.Point(px, py) * derotationMatrix
|
|
|
shapeePerimeter.append([p[0], p[1]])
|
|
|
|
|
|
|
|
|
shapeePerimeter = np.flip(np.array(shapeePerimeter), 1).tolist()
|
|
|
shapeePerimeter = rotate_polygon(shapeePerimeter, rotationangle, rotationOld, width, height)
|
|
|
|
|
|
|
|
|
final_coords = [(float(x), float(y)) for x,y in shapeePerimeter]
|
|
|
if len(final_coords) < 3:
|
|
|
continue
|
|
|
|
|
|
processed.append((final_coords, area, perim, color))
|
|
|
|
|
|
return processed
|
|
|
|
|
|
def ROI_boundingBoxCoor(img):
|
|
|
|
|
|
imgGray= cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
|
|
_, thresh = cv2.threshold(imgGray, 250, 255, cv2.THRESH_BINARY_INV)
|
|
|
|
|
|
|
|
|
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
|
|
|
walls = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
|
|
|
|
|
|
|
|
|
num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(walls, connectivity=8)
|
|
|
|
|
|
best_idx = None
|
|
|
best_score = -1
|
|
|
h, w = walls.shape
|
|
|
cx_img, cy_img = w//2, h//2
|
|
|
|
|
|
for i in range(1, num_labels):
|
|
|
x, y, bw, bh, area = stats[i]
|
|
|
cx, cy = centroids[i]
|
|
|
|
|
|
|
|
|
|
|
|
dist = np.hypot(cx - cx_img, cy - cy_img)
|
|
|
score = area - 0.5 * dist
|
|
|
|
|
|
if score > best_score:
|
|
|
best_score = score
|
|
|
best_idx = i
|
|
|
|
|
|
|
|
|
bbox = None
|
|
|
if best_idx is not None:
|
|
|
x, y, bw, bh, _ = stats[best_idx]
|
|
|
margin = 50
|
|
|
x = int(max(x - margin, 0))
|
|
|
y = int(max(y - margin, 0))
|
|
|
bw = int(min(bw + 2*margin, w-x))
|
|
|
bh = int(min(bh + 2*margin, h-y))
|
|
|
|
|
|
|
|
|
bbox = (x, y, x+bw, y+bh)
|
|
|
|
|
|
|
|
|
imgcopy = img.copy()
|
|
|
cv2.rectangle(imgcopy, (bbox[0], bbox[1]), (bbox[2], bbox[3]), (0,255,0), 2)
|
|
|
|
|
|
mask = np.zeros_like(img)
|
|
|
cv2.rectangle(mask, (bbox[0], bbox[1]), (bbox[2], bbox[3]), (255,255,255), -1)
|
|
|
main_zone = cv2.bitwise_and(img, mask)
|
|
|
|
|
|
return imgcopy, main_zone, bbox
|
|
|
|
|
|
return img, img, bbox
|
|
|
|
|
|
def draw_bb_onPDF(doc,bbox):
|
|
|
page = doc[0]
|
|
|
x1, y1 = bbox[0],bbox[1]
|
|
|
x2, y2 = bbox[2],bbox[3]
|
|
|
|
|
|
p1 = fitz.Point(x1,y1)
|
|
|
p2 = fitz.Point(x2,y2)
|
|
|
|
|
|
p1=p1*page.derotation_matrix
|
|
|
p2=p2*page.derotation_matrix
|
|
|
|
|
|
rect = fitz.Rect(p1, p2).normalize()
|
|
|
x0, y0, x1, y1 = rect.x0, rect.y0, rect.x1, rect.y1
|
|
|
pdf_bbox=[x0, y0, x1, y1]
|
|
|
|
|
|
page.draw_rect(rect)
|
|
|
doc.save('kk.pdf')
|
|
|
return pdf_bbox
|
|
|
|
|
|
def mainFunctionDrawImgPdf(datadoc,dxfpath, dxfratio,SearchArray,CorrectionRatio,points_Of_drawing_Canvas,pdfpath=0,pdfname=0,pdf_content=0):
|
|
|
OutputPdfStage1='BB Trial.pdf'
|
|
|
if pdf_content:
|
|
|
FinalRatio,width_dxf= RetriveRatio(datadoc,dxfpath,pdf_content)
|
|
|
else:
|
|
|
FinalRatio,width_dxf= RetriveRatio(datadoc,dxfpath)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if pdf_content:
|
|
|
img=pdftoimg(datadoc,pdf_content)
|
|
|
else:
|
|
|
img=pdftoimg(datadoc)
|
|
|
flipped_horizontal=flip(img)
|
|
|
allcnts = []
|
|
|
imgg = flipped_horizontal
|
|
|
|
|
|
if pdf_content:
|
|
|
doc = fitz.open(stream=pdf_content, filetype="pdf")
|
|
|
else:
|
|
|
doc = fitz.open('pdf',datadoc)
|
|
|
page2 = doc[0]
|
|
|
rotationOld=page2.rotation
|
|
|
derotationMatrix=page2.derotation_matrix
|
|
|
pix=page2.get_pixmap()
|
|
|
width=abs(page2.mediabox[2])+abs(page2.mediabox[0])
|
|
|
height=abs(page2.mediabox[3])+abs(page2.mediabox[1])
|
|
|
print('mediabox', width , height)
|
|
|
|
|
|
Correction = CorrectionRatio / width_dxf
|
|
|
print("Correction Factor = ",round(Correction/FinalRatio),1)
|
|
|
dxfratio = dxfratio * round((Correction/FinalRatio),1)
|
|
|
print("new omar dxfRatio = ",dxfratio)
|
|
|
|
|
|
imgcopy, main_zone, bbox = ROI_boundingBoxCoor(img)
|
|
|
pdf_bbox=draw_bb_onPDF(doc,bbox)
|
|
|
bxmin, bymin, bxmax, bymax = pdf_bbox
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if page2.rotation!=0:
|
|
|
|
|
|
rotationangle = page2.rotation
|
|
|
page2.set_rotation(0)
|
|
|
ratio = pix.width/ img.shape[0]
|
|
|
else:
|
|
|
ratio = pix.width/ img.shape[1]
|
|
|
rotationangle = 270
|
|
|
|
|
|
|
|
|
hatched_areas,Legendarray = get_hatched_areas(datadoc,dxfpath,FinalRatio,rotationangle,SearchArray)
|
|
|
|
|
|
nearby,grouped_shapes_final=find_nearby_vertices(hatched_areas)
|
|
|
processed_intersections = process_polygons(nearby, img, dxfratio, rotationangle, derotationMatrix,rotationOld,width,height)
|
|
|
|
|
|
groups = gather_and_union_groups(hatched_areas, axis_threshold=0.5,
|
|
|
require_diff_color=True,
|
|
|
skip_colors={(0,0,0),(255,255,255)})
|
|
|
|
|
|
processed_grouped_shapes = process_grouped_shapes(groups,
|
|
|
imgg, dxfratio, rotationangle, derotationMatrix, rotationOld, width, height)
|
|
|
|
|
|
allshapes=[]
|
|
|
|
|
|
NewColors = []
|
|
|
if pdf_content:
|
|
|
SimilarAreaDictionary=Create_DF(dxfpath,datadoc,hatched_areas,pdf_content)
|
|
|
else:
|
|
|
SimilarAreaDictionary=Create_DF(dxfpath,datadoc,hatched_areas)
|
|
|
i=0
|
|
|
flagcolor = 0
|
|
|
ColorCheck=[]
|
|
|
|
|
|
XMLArea = []
|
|
|
XMLPerimeter = []
|
|
|
|
|
|
|
|
|
for polygon in hatched_areas:
|
|
|
cntPoints = []
|
|
|
cntPoints1 = []
|
|
|
shapeePerimeter = []
|
|
|
shapeeArea = []
|
|
|
|
|
|
blackImgShapes = np.zeros(imgg.shape[:2], dtype="uint8")
|
|
|
blackImgShapes= cv2.cvtColor(blackImgShapes, cv2.COLOR_GRAY2BGR)
|
|
|
|
|
|
|
|
|
|
|
|
for vertex in polygon[0]:
|
|
|
x = (vertex[0]) *dxfratio
|
|
|
y = (vertex[1]) *dxfratio
|
|
|
if rotationangle==0:
|
|
|
if y<0:
|
|
|
y=y*-1
|
|
|
cntPoints.append([int(x), int(y)])
|
|
|
cntPoints1.append([x, y])
|
|
|
|
|
|
cv2.drawContours(blackImgShapes, [np.array(cntPoints)], -1, ([255,255,255]), thickness=-1)
|
|
|
x, y, w, h = cv2.boundingRect(np.array(cntPoints))
|
|
|
firstpoint = 0
|
|
|
for poi in np.array(cntPoints1):
|
|
|
if firstpoint == 0:
|
|
|
x2, y2 = poi
|
|
|
p2 = fitz.Point(x2,y2)
|
|
|
|
|
|
p2=p2*derotationMatrix
|
|
|
shapeePerimeter.append([p2[0],p2[1]])
|
|
|
firstpoint = 1
|
|
|
else:
|
|
|
x1, y1 = poi
|
|
|
p1 = fitz.Point(x1,y1)
|
|
|
|
|
|
p1=p1*derotationMatrix
|
|
|
print("P1 = ",p1)
|
|
|
shapeePerimeter.append([p1[0],p1[1]])
|
|
|
|
|
|
shapeePerimeter.append([p2[0],p2[1]])
|
|
|
shapeePerimeter=np.flip(shapeePerimeter,1)
|
|
|
shapeePerimeter=rotate_polygon(shapeePerimeter,rotationangle,rotationOld,width,height)
|
|
|
|
|
|
for poi in np.array(cntPoints1):
|
|
|
x1, y1 = poi
|
|
|
p1 = fitz.Point(x1,y1)
|
|
|
|
|
|
p1=p1*derotationMatrix
|
|
|
print("P1 = ",p1)
|
|
|
shapeeArea.append([p1[0],p1[1]])
|
|
|
|
|
|
shapeeArea.append([p2[0],p2[1]])
|
|
|
shapeeArea=np.flip(shapeeArea,1)
|
|
|
shapeeArea=rotate_polygon(shapeeArea,rotationangle,rotationOld,width,height)
|
|
|
|
|
|
|
|
|
tol=0
|
|
|
condition1 = (SimilarAreaDictionary['Area'] >= polygon[1] - tol) & (SimilarAreaDictionary['Area'] <= polygon[1] +tol)
|
|
|
condition2 = (SimilarAreaDictionary['Perimeter'] >= polygon[2] -tol) & (SimilarAreaDictionary['Perimeter'] <= polygon[2] +tol)
|
|
|
combined_condition = condition1 & condition2
|
|
|
|
|
|
if any(combined_condition):
|
|
|
flagcolor = 1
|
|
|
index = np.where(combined_condition)[0][0]
|
|
|
|
|
|
NewColors=SimilarAreaDictionary.at[index, 'Color']
|
|
|
else:
|
|
|
flagcolor = 2
|
|
|
NewColors=SimilarAreaDictionary.at[i, 'Color']
|
|
|
|
|
|
if(int(NewColors[0])==255 and int(NewColors[1])==255 and int(NewColors[2])==255):
|
|
|
WhiteImgFinal = cv2.bitwise_and(blackImgShapes,imgg)
|
|
|
flipped=flip(WhiteImgFinal)
|
|
|
|
|
|
|
|
|
imgslice = WhiteImgFinal[y:y+h, x:x+w]
|
|
|
if(imgslice.shape[0] != 0 and imgslice.shape[1] != 0):
|
|
|
flippedSlice=flip(imgslice)
|
|
|
|
|
|
|
|
|
|
|
|
flippedSlice_pil = Image.fromarray(flippedSlice)
|
|
|
|
|
|
|
|
|
patch_size = 100
|
|
|
patch_colors = []
|
|
|
|
|
|
|
|
|
for i in range(0, flippedSlice_pil.width, patch_size):
|
|
|
for j in range(0, flippedSlice_pil.height, patch_size):
|
|
|
|
|
|
patch = flippedSlice_pil.crop((i, j, i + patch_size, j + patch_size))
|
|
|
patch_colors += patch.getcolors(patch_size * patch_size)
|
|
|
|
|
|
|
|
|
max_count = 0
|
|
|
dominant_color = None
|
|
|
tolerance = 5
|
|
|
black_threshold = 30
|
|
|
white_threshold = 225
|
|
|
|
|
|
for count, color in patch_colors:
|
|
|
|
|
|
if not (all(c <= black_threshold for c in color) or all(c >= white_threshold for c in color)):
|
|
|
|
|
|
if count > max_count:
|
|
|
max_count = count
|
|
|
dominant_color = color
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if dominant_color is not None:
|
|
|
ColorCheck.append(dominant_color)
|
|
|
|
|
|
NewColors = None
|
|
|
|
|
|
for color in ColorCheck:
|
|
|
|
|
|
print("color = ",color)
|
|
|
print("dominant_color = ",dominant_color)
|
|
|
if (abs(color[0] - dominant_color[0]) < 20 and
|
|
|
abs(color[1] - dominant_color[1]) < 20 and
|
|
|
abs(color[2] - dominant_color[2]) < 20):
|
|
|
NewColors = (color[2], color[1], color[0])
|
|
|
break
|
|
|
else:
|
|
|
|
|
|
NewColors = (dominant_color[2], dominant_color[1], dominant_color[0])
|
|
|
|
|
|
if NewColors not in ColorCheck:
|
|
|
ColorCheck.append(NewColors)
|
|
|
|
|
|
|
|
|
if flagcolor == 1:
|
|
|
SimilarAreaDictionary.at[index, 'Color'] = NewColors
|
|
|
|
|
|
elif flagcolor == 2:
|
|
|
SimilarAreaDictionary.at[i, 'Color'] = NewColors
|
|
|
|
|
|
|
|
|
cv2.drawContours(imgg, [np.array(cntPoints)], -1, ([NewColors[2],NewColors[1],NewColors[0]]), thickness=-1)
|
|
|
|
|
|
vertices_area = [
|
|
|
[[float(x), float(y)] for x, y in shapeeArea],
|
|
|
str(polygon[1]),NewColors
|
|
|
]
|
|
|
XMLArea.append(vertices_area)
|
|
|
|
|
|
vertices_Perimeter = [
|
|
|
[[float(x), float(y)] for x, y in shapeePerimeter],
|
|
|
str(polygon[1]),NewColors
|
|
|
]
|
|
|
XMLPerimeter.append(vertices_Perimeter)
|
|
|
|
|
|
discard = False
|
|
|
if(points_Of_drawing_Canvas):
|
|
|
Boundingpolygon = np.array(
|
|
|
[(p.x, p.y) for p in points_Of_drawing_Canvas],
|
|
|
dtype=np.float32
|
|
|
)
|
|
|
|
|
|
for x, y in shapeeArea:
|
|
|
|
|
|
result = cv2.pointPolygonTest(Boundingpolygon, (x, y), False)
|
|
|
if result < 0:
|
|
|
discard = True
|
|
|
break
|
|
|
else:
|
|
|
for point in shapeeArea:
|
|
|
if not (bxmin <= point[0] <= bxmax and bymin <= point[1] <= bymax):
|
|
|
discard = True
|
|
|
break
|
|
|
|
|
|
if not discard:
|
|
|
|
|
|
annot11 = page2.add_polygon_annot( points=shapeeArea)
|
|
|
annot11.set_border(width=0.2)
|
|
|
annot11.set_colors(stroke=(int(NewColors[0])/255,int(NewColors[1])/255,int(NewColors[2])/255), fill= (int(NewColors[0])/255,int(NewColors[1])/255,int(NewColors[2])/255) )
|
|
|
annot11.set_info(content=str(polygon[1])+' sq m',subject='Area Measurement', title="ADR Team")
|
|
|
annot11.set_opacity(0.8)
|
|
|
|
|
|
annot11.update()
|
|
|
|
|
|
|
|
|
discard = False
|
|
|
if(points_Of_drawing_Canvas):
|
|
|
Boundingpolygon = np.array(
|
|
|
[(p.x, p.y) for p in points_Of_drawing_Canvas],
|
|
|
dtype=np.float32
|
|
|
)
|
|
|
|
|
|
for x, y in shapeePerimeter:
|
|
|
|
|
|
result = cv2.pointPolygonTest(Boundingpolygon, (x, y), False)
|
|
|
if result < 0:
|
|
|
discard = True
|
|
|
break
|
|
|
else:
|
|
|
for point in shapeePerimeter:
|
|
|
if not (bxmin <= point[0] <= bxmax and bymin <= point[1] <= bymax):
|
|
|
discard = True
|
|
|
break
|
|
|
|
|
|
if not discard:
|
|
|
annot12 = page2.add_polyline_annot( points=shapeePerimeter )
|
|
|
annot12.set_border(width=0.8)
|
|
|
annot12.set_colors(stroke=(int(NewColors[0])/255,int(NewColors[1])/255,int(NewColors[2])/255))
|
|
|
annot12.set_info(content=str(polygon[2])+' m',subject='Perimeter Measurement', title="ADR Team")
|
|
|
annot12.set_opacity(0.8)
|
|
|
|
|
|
annot12.update()
|
|
|
i += 1
|
|
|
alpha = 0.8
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
page2.set_rotation(rotationOld)
|
|
|
Correct_img=flip(imgg)
|
|
|
|
|
|
image_new1 = cv2.addWeighted(Correct_img, alpha, img, 1 - alpha, 0)
|
|
|
SimilarAreaDictionary = SimilarAreaDictionary.fillna(' ')
|
|
|
|
|
|
|
|
|
white_color = (255, 255, 255)
|
|
|
|
|
|
|
|
|
SimilarAreaDictionary = SimilarAreaDictionary[SimilarAreaDictionary['Color'] != white_color]
|
|
|
|
|
|
|
|
|
SimilarAreaDictionary.reset_index(drop=True, inplace=True)
|
|
|
|
|
|
grouped_df = SimilarAreaDictionary.groupby('Color').agg({
|
|
|
'Guess':'first',
|
|
|
'Occurences': 'sum',
|
|
|
'Area':'first',
|
|
|
'Total Area': 'sum',
|
|
|
'Perimeter':'first',
|
|
|
'Total Perimeter': 'sum',
|
|
|
'Length':'first',
|
|
|
'Total Length':'first',
|
|
|
'Texts':'first',
|
|
|
'Comments':'first'
|
|
|
|
|
|
}).reset_index()
|
|
|
|
|
|
SimilarAreaDictionary = grouped_df
|
|
|
|
|
|
modified_pdf_data = doc.tobytes()
|
|
|
|
|
|
OutputPdfStage2,annotations_data=adjustannotations(modified_pdf_data,Legendarray)
|
|
|
|
|
|
allvertices = annotations_data
|
|
|
hatchcolorR= '0'
|
|
|
hatchcolorG= '1'
|
|
|
hatchcolorB= '1'
|
|
|
LinestyleTemplates = {
|
|
|
'Solid': '<</W {w}/S/S/Type/Border>>',
|
|
|
'Dashed1':'<</W {w}/S/D/D[2 2]/Type/Border>>',
|
|
|
'Dashed2': '<</W {w}/S/D/D[3 3]/Type/Border>>',
|
|
|
'Dashed3': '<</W {w}/S/D/D[4 4]/Type/Border>>',
|
|
|
'Dashed4': '<</W {w}/S/D/D[4 3 2 3]/Type/Border>>',
|
|
|
'Dashed5': '<</W {w}/S/D/D[4 3 16 3]/Type/Border>>',
|
|
|
'Dashed6': '<</W {w}/S/D/D[8 4 4 4]/Type/Border>>'
|
|
|
}
|
|
|
|
|
|
|
|
|
HatchFunctions = {
|
|
|
'None':'',
|
|
|
'Brick': setBrickHatch,
|
|
|
'DiagonalBrick':setDiagonalBrickHatch,
|
|
|
'Horizontal':setHorizontalHatch,
|
|
|
'Vertical':setVerticalHatch,
|
|
|
'DiagonalDown':setDiagonalDownHatch,
|
|
|
'DiagonalUp':setDiagonalUpHatch,
|
|
|
'Grid': setGridHatch,
|
|
|
'Weave':setWeaveHatch,
|
|
|
'10Dots':set10DotsHatch,
|
|
|
'20Dots':set20DotsHatch,
|
|
|
'30Dots':set30DotsHatch
|
|
|
}
|
|
|
|
|
|
|
|
|
area = 1
|
|
|
perimeter = 1
|
|
|
|
|
|
import colorsys
|
|
|
|
|
|
annotations=[]
|
|
|
for shapeinvertices in allvertices:
|
|
|
|
|
|
|
|
|
if(shapeinvertices[3] == None):
|
|
|
rn=0
|
|
|
gn=0
|
|
|
bn=0
|
|
|
|
|
|
else:
|
|
|
rn=shapeinvertices[3][0]-150/255
|
|
|
gn=shapeinvertices[3][1]-150/255
|
|
|
bn=shapeinvertices[3][2]-150/255
|
|
|
|
|
|
h, s, v = colorsys.rgb_to_hsv(rn, gn, bn)
|
|
|
|
|
|
|
|
|
s2, v2 = 0.6, 0.9
|
|
|
|
|
|
|
|
|
r2, g2, b2 = colorsys.hsv_to_rgb(h, s2, v2)
|
|
|
|
|
|
R=str(r2)
|
|
|
G=str(g2)
|
|
|
B=str(b2)
|
|
|
|
|
|
r2, g2, b2 = colorsys.hsv_to_rgb(h, s2, v2)
|
|
|
color_str = f"{r2:.3f} {g2:.3f} {b2:.3f}"
|
|
|
hatch_color = f"{float(hatchcolorR):.0f} {float(hatchcolorG):.0f} {float(hatchcolorB):.0f}"
|
|
|
|
|
|
if(shapeinvertices[1] is not None):
|
|
|
|
|
|
annotations.append(
|
|
|
{
|
|
|
|
|
|
'vertices': shapeinvertices[0],
|
|
|
'text': str(shapeinvertices[1])+' sq m',
|
|
|
'author': 'ADR',
|
|
|
'custom_data': {'Legend': shapeinvertices[4],'NBS':shapeinvertices[5]},
|
|
|
'type_internal': 'Bluebeam.PDF.Annotations.AnnotationMeasureArea',
|
|
|
'subject': 'Area Measurement',
|
|
|
'label': 'label',
|
|
|
'opacity': '0.5',
|
|
|
'color': color_str,
|
|
|
'linestyle': LinestyleTemplates['Solid'].format(w=0),
|
|
|
'hatchstyle': HatchFunctions['DiagonalDown'](color_str, hatch_color),
|
|
|
|
|
|
'hatchLinescolor':hatch_color,
|
|
|
|
|
|
|
|
|
}
|
|
|
)
|
|
|
elif(shapeinvertices[2] is not None):
|
|
|
|
|
|
annotations.append(
|
|
|
{
|
|
|
|
|
|
'vertices': shapeinvertices[0],
|
|
|
'text': str(shapeinvertices[2])+' m',
|
|
|
'author': 'ADR',
|
|
|
'custom_data': {'Legend': shapeinvertices[4],'NBS':shapeinvertices[5]},
|
|
|
'type_internal': 'Bluebeam.PDF.Annotations.AnnotationMeasurePerimeter',
|
|
|
'subject': 'Perimeter Measurement',
|
|
|
'label':'label2',
|
|
|
'opacity': '0.7',
|
|
|
'color': R+ ' '+G + ' '+B,
|
|
|
'linestyle': LinestyleTemplates['Solid'].format(w=2),
|
|
|
'hatchstyle': '',
|
|
|
'hatchLinescolor':'',
|
|
|
|
|
|
}
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
column_order = ['Legend','NBS']
|
|
|
|
|
|
|
|
|
|
|
|
pdfWidth=1684
|
|
|
pdfHeight=2384
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bax_xml=save_multiple_annotations_bax(
|
|
|
annotations, 'FF-Ceiling.bax', column_order, pdfWidth, pdfHeight
|
|
|
)
|
|
|
|
|
|
def generate_bluebeam_columns_raw(column_names):
|
|
|
"""
|
|
|
Generate BluebeamUserDefinedColumns XML as raw string, without headers or extra fields.
|
|
|
"""
|
|
|
root = Element("BluebeamUserDefinedColumns")
|
|
|
|
|
|
for idx, name in enumerate(column_names):
|
|
|
item = SubElement(root, "BSIColumnItem", Index=str(idx), Subtype="Text")
|
|
|
SubElement(item, "Name").text = name
|
|
|
SubElement(item, "DisplayOrder").text = str(idx)
|
|
|
SubElement(item, "Deleted").text = "False"
|
|
|
SubElement(item, "Multiline").text = "False"
|
|
|
|
|
|
|
|
|
return tostring(root, encoding="unicode", method="xml")
|
|
|
|
|
|
|
|
|
column_xml = generate_bluebeam_columns_raw(column_order)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
doc2 =fitz.open('pdf',OutputPdfStage2)
|
|
|
if pdf_content:
|
|
|
gc,spreadsheet_service,spreadsheetId, spreadsheet_url , namepathArr=google_sheet_Legend.legendGoogleSheets(SimilarAreaDictionary , pdfname,pdfpath,pdf_content)
|
|
|
else:
|
|
|
gc,spreadsheet_service,spreadsheetId, spreadsheet_url , namepathArr=google_sheet_Legend.legendGoogleSheets(SimilarAreaDictionary , pdfname,pdfpath)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
list1=pd.DataFrame(columns=['content', 'id', 'subject','color'])
|
|
|
|
|
|
|
|
|
for page in doc2:
|
|
|
|
|
|
for annot in page.annots():
|
|
|
|
|
|
annot_color = annot.colors
|
|
|
if annot_color is not None:
|
|
|
|
|
|
stroke_color = annot_color.get('stroke')
|
|
|
fill_color = annot_color.get('fill')
|
|
|
if fill_color:
|
|
|
v='fill'
|
|
|
|
|
|
if stroke_color:
|
|
|
v='stroke'
|
|
|
x,y,z=int(annot_color.get(v)[0]*255),int(annot_color.get(v)[1]*255),int(annot_color.get(v)[2]*255)
|
|
|
list1.loc[len(list1)] =[annot.info['content'],annot.info['id'],annot.info['subject'],[x,y,z]]
|
|
|
print('LISTTT',list1)
|
|
|
return doc2,image_new1, SimilarAreaDictionary ,spreadsheetId, spreadsheet_url , namepathArr , list1,hatched_areas, bax_xml, column_xml
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|