Update dxf__omar3_2.py
Browse files- dxf__omar3_2.py +112 -77
dxf__omar3_2.py
CHANGED
|
@@ -49,6 +49,9 @@ from PyPDF2.generic import NameObject, TextStringObject, DictionaryObject, Float
|
|
| 49 |
from typing import NewType
|
| 50 |
from ctypes import sizeof
|
| 51 |
|
|
|
|
|
|
|
|
|
|
| 52 |
import numpy as np
|
| 53 |
import cv2
|
| 54 |
from matplotlib import pyplot as plt
|
|
@@ -75,6 +78,12 @@ import google_sheet_Legend
|
|
| 75 |
from PyPDF2 import PdfReader
|
| 76 |
from io import BytesIO
|
| 77 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
"""## Notes"""
|
| 79 |
|
| 80 |
#new approach to get width and height of dxf plan
|
|
@@ -1130,10 +1139,32 @@ def Create_DF(dxfpath,datadoc,hatched_areas,pdf_content=0):
|
|
| 1130 |
# print(SimilarAreaDictionary)
|
| 1131 |
return SimilarAreaDictionary
|
| 1132 |
"""### Draw on Image and PDF"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1133 |
|
| 1134 |
def adjustannotations(OutputPdfStage1,text_with_positions):
|
| 1135 |
input_pdf_path = OutputPdfStage1
|
| 1136 |
output_pdf_path = "Final-WallsAdjusted.pdf"
|
|
|
|
| 1137 |
|
| 1138 |
# Load the input PDF
|
| 1139 |
pdf_bytes_io = BytesIO(OutputPdfStage1)
|
|
@@ -1149,83 +1180,87 @@ def adjustannotations(OutputPdfStage1,text_with_positions):
|
|
| 1149 |
writer.add_metadata(metadata)
|
| 1150 |
|
| 1151 |
for page_index, page in enumerate(writer.pages):
|
| 1152 |
-
if "/Annots" in page:
|
| 1153 |
-
|
| 1154 |
-
for annot_index, annot in enumerate(annotations):
|
| 1155 |
-
obj = annot.get_object()
|
| 1156 |
-
|
| 1157 |
-
# print("obj", obj)
|
| 1158 |
-
# print(obj.get("/IT"))
|
| 1159 |
-
|
| 1160 |
-
if obj.get("/Subtype") == "/Line":
|
| 1161 |
-
# print("AWL ANNOT IF")
|
| 1162 |
-
# Check the /IT value to differentiate annotations
|
| 1163 |
-
# if "/Contents" in obj and "m" in obj["/Contents"]:
|
| 1164 |
-
if "/Subj" in obj and "Perimeter Measurement" in obj["/Subj"]:
|
| 1165 |
-
# print("Tany IF")
|
| 1166 |
-
obj.update({
|
| 1167 |
-
NameObject("/Measure"): DictionaryObject({
|
| 1168 |
-
NameObject("/Type"): NameObject("/Measure"),
|
| 1169 |
-
NameObject("/L"): DictionaryObject({
|
| 1170 |
-
NameObject("/G"): FloatObject(1),
|
| 1171 |
-
NameObject("/U"): TextStringObject("m"), # Unit of measurement for area
|
| 1172 |
-
}),
|
| 1173 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1174 |
}),
|
| 1175 |
-
|
| 1176 |
-
|
| 1177 |
-
|
| 1178 |
-
|
| 1179 |
-
|
| 1180 |
-
|
| 1181 |
-
|
| 1182 |
-
|
| 1183 |
-
|
| 1184 |
-
|
| 1185 |
-
|
| 1186 |
-
|
| 1187 |
-
|
| 1188 |
-
|
| 1189 |
-
|
| 1190 |
-
|
| 1191 |
-
|
| 1192 |
-
|
| 1193 |
-
|
| 1194 |
-
|
| 1195 |
-
|
| 1196 |
-
|
| 1197 |
-
|
| 1198 |
-
|
| 1199 |
-
|
| 1200 |
-
|
| 1201 |
-
|
| 1202 |
-
elif (obj.get("/Subtype") == "/Polygon" and "/C" in obj):
|
| 1203 |
-
# Normalize and match the color
|
| 1204 |
-
annot_color = normalize_color(obj["/C"])
|
| 1205 |
-
# print("annot_color = ",annot_color)
|
| 1206 |
-
# print("LASTarray = ",text_with_positions)
|
| 1207 |
-
# matched_entry = next(
|
| 1208 |
-
# ((text, NBS) for text,NBS, _, color in text_with_positions if annot_color == color),
|
| 1209 |
-
# (None, None)
|
| 1210 |
-
# )
|
| 1211 |
-
matched_entry = next(
|
| 1212 |
-
((text, NBS) for text,NBS, _, color in text_with_positions if color_close_enough(annot_color, color)),
|
| 1213 |
-
(None, None)
|
| 1214 |
-
)
|
| 1215 |
-
# print("matched_entry = ",matched_entry)
|
| 1216 |
-
matched_text, matched_nbs = matched_entry
|
| 1217 |
-
|
| 1218 |
-
combined_text = ""
|
| 1219 |
-
if matched_text and matched_nbs:
|
| 1220 |
-
combined_text = f"{matched_text} - {matched_nbs}"
|
| 1221 |
-
elif matched_text:
|
| 1222 |
-
combined_text = matched_text
|
| 1223 |
-
elif matched_nbs:
|
| 1224 |
-
combined_text = matched_nbs
|
| 1225 |
-
|
| 1226 |
-
obj.update({
|
| 1227 |
-
NameObject("/T"): TextStringObject(combined_text), # Custom text for "Comment" column
|
| 1228 |
-
})
|
| 1229 |
|
| 1230 |
|
| 1231 |
|
|
@@ -1234,7 +1269,7 @@ def adjustannotations(OutputPdfStage1,text_with_positions):
|
|
| 1234 |
output_pdf_io.seek(0)
|
| 1235 |
|
| 1236 |
print(f"Annotations updated and saved to {output_pdf_path}")
|
| 1237 |
-
return output_pdf_io.read()
|
| 1238 |
|
| 1239 |
|
| 1240 |
|
|
@@ -1487,7 +1522,7 @@ def mainFunctionDrawImgPdf(datadoc,dxfpath, dxfratio,SearchArray,pdfpath=0,pdfna
|
|
| 1487 |
# doc.save(OutputPdfStage1)
|
| 1488 |
modified_pdf_data = doc.tobytes()
|
| 1489 |
# OutputPdfStage2=adjustannotations(modified_pdf_data)
|
| 1490 |
-
OutputPdfStage2=adjustannotations(modified_pdf_data,Legendarray)
|
| 1491 |
|
| 1492 |
|
| 1493 |
# with open("Adjusted_PDF.pdf", "wb") as f:
|
|
|
|
| 49 |
from typing import NewType
|
| 50 |
from ctypes import sizeof
|
| 51 |
|
| 52 |
+
from PyPDF2 import PdfReader, PdfWriter
|
| 53 |
+
from PyPDF2.generic import NameObject, TextStringObject, FloatObject, DictionaryObject
|
| 54 |
+
|
| 55 |
import numpy as np
|
| 56 |
import cv2
|
| 57 |
from matplotlib import pyplot as plt
|
|
|
|
| 78 |
from PyPDF2 import PdfReader
|
| 79 |
from io import BytesIO
|
| 80 |
|
| 81 |
+
from ezdxf.colors import aci2rgb
|
| 82 |
+
from collections import Counter
|
| 83 |
+
|
| 84 |
+
from math import isclose
|
| 85 |
+
|
| 86 |
+
|
| 87 |
"""## Notes"""
|
| 88 |
|
| 89 |
#new approach to get width and height of dxf plan
|
|
|
|
| 1139 |
# print(SimilarAreaDictionary)
|
| 1140 |
return SimilarAreaDictionary
|
| 1141 |
"""### Draw on Image and PDF"""
|
| 1142 |
+
def color_close_enough(c1, c2, threshold=10):
|
| 1143 |
+
return all(abs(a - b) <= threshold for a, b in zip(c1, c2))
|
| 1144 |
+
|
| 1145 |
+
|
| 1146 |
+
|
| 1147 |
+
def group_vertices(raw):
|
| 1148 |
+
"""Convert flat list [x1,y1,x2,y2,...] into [[x1,y1],[x2,y2],...]"""
|
| 1149 |
+
if not raw or len(raw) < 2:
|
| 1150 |
+
return []
|
| 1151 |
+
return [[raw[i], raw[i+1]] for i in range(0, len(raw), 2)]
|
| 1152 |
+
|
| 1153 |
+
|
| 1154 |
+
def extract_measurement(obj):
|
| 1155 |
+
"""Extract numeric measurement from annotation's /Contents field."""
|
| 1156 |
+
contents = obj.get("/Contents")
|
| 1157 |
+
if not contents:
|
| 1158 |
+
return None
|
| 1159 |
+
# Find first numeric occurrence
|
| 1160 |
+
text = str(contents)
|
| 1161 |
+
m = re.search(r"([0-9]*\.?[0-9]+)", text)
|
| 1162 |
+
return float(m.group(1)) if m else None
|
| 1163 |
|
| 1164 |
def adjustannotations(OutputPdfStage1,text_with_positions):
|
| 1165 |
input_pdf_path = OutputPdfStage1
|
| 1166 |
output_pdf_path = "Final-WallsAdjusted.pdf"
|
| 1167 |
+
annotations_data = []
|
| 1168 |
|
| 1169 |
# Load the input PDF
|
| 1170 |
pdf_bytes_io = BytesIO(OutputPdfStage1)
|
|
|
|
| 1180 |
writer.add_metadata(metadata)
|
| 1181 |
|
| 1182 |
for page_index, page in enumerate(writer.pages):
|
| 1183 |
+
if "/Annots" not in page:
|
| 1184 |
+
continue
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1185 |
|
| 1186 |
+
for annot in page["/Annots"]:
|
| 1187 |
+
obj = annot.get_object()
|
| 1188 |
+
subtype = obj.get("/Subtype")
|
| 1189 |
+
|
| 1190 |
+
# Group vertices for metadata
|
| 1191 |
+
if subtype == "/Line":
|
| 1192 |
+
raw_vertices = obj.get("/L", [])
|
| 1193 |
+
else:
|
| 1194 |
+
raw_vertices = obj.get("/Vertices", [])
|
| 1195 |
+
vertices = group_vertices(raw_vertices)
|
| 1196 |
+
|
| 1197 |
+
# Normalize color
|
| 1198 |
+
raw_color = obj.get("/C")
|
| 1199 |
+
try:
|
| 1200 |
+
annot_color = normalize_color(raw_color)
|
| 1201 |
+
except:
|
| 1202 |
+
annot_color = raw_color
|
| 1203 |
+
|
| 1204 |
+
# Extract measurement from annotation content
|
| 1205 |
+
measurement = extract_measurement(obj)
|
| 1206 |
+
# Assign to area or perimeter based on subtype
|
| 1207 |
+
area = measurement if subtype == "/Polygon" else None
|
| 1208 |
+
perimeter = measurement if subtype in ["/Line", "/PolyLine"] else None
|
| 1209 |
+
|
| 1210 |
+
# Match text and NBS
|
| 1211 |
+
matched_text = None
|
| 1212 |
+
matched_nbs = None
|
| 1213 |
+
if subtype in ["/Line", "/PolyLine", "/Polygon"] and raw_color:
|
| 1214 |
+
matched_entry = next(
|
| 1215 |
+
((t, n) for t, n, _, c in text_with_positions if color_close_enough(annot_color, c)),
|
| 1216 |
+
(None, None)
|
| 1217 |
+
)
|
| 1218 |
+
matched_text, matched_nbs = matched_entry
|
| 1219 |
+
combined = ""
|
| 1220 |
+
if matched_text and matched_nbs:
|
| 1221 |
+
combined = f"{matched_text} - {matched_nbs}"
|
| 1222 |
+
elif matched_text:
|
| 1223 |
+
combined = matched_text
|
| 1224 |
+
elif matched_nbs:
|
| 1225 |
+
combined = matched_nbs
|
| 1226 |
+
if combined:
|
| 1227 |
+
obj.update({NameObject("/T"): TextStringObject(combined)})
|
| 1228 |
+
|
| 1229 |
+
# Update annotation dictionaries for measurement type
|
| 1230 |
+
if subtype == "/Line" and obj.get("/Subj", "") == "Perimeter Measurement":
|
| 1231 |
+
obj.update({
|
| 1232 |
+
NameObject("/Measure"): DictionaryObject({
|
| 1233 |
+
NameObject("/Type"): NameObject("/Measure"),
|
| 1234 |
+
NameObject("/L"): DictionaryObject({
|
| 1235 |
+
NameObject("/G"): FloatObject(1),
|
| 1236 |
+
NameObject("/U"): TextStringObject("m"),
|
| 1237 |
}),
|
| 1238 |
+
}),
|
| 1239 |
+
NameObject("/IT"): NameObject("/LineDimension"),
|
| 1240 |
+
NameObject("/Subj"): TextStringObject("Length Measurement"),
|
| 1241 |
+
})
|
| 1242 |
+
if subtype == "/Polygon" and obj.get("/Subj", "") == "Area Measurement":
|
| 1243 |
+
obj.update({
|
| 1244 |
+
NameObject("/Measure"): DictionaryObject({
|
| 1245 |
+
NameObject("/Type"): NameObject("/Measure"),
|
| 1246 |
+
NameObject("/Area"): DictionaryObject({
|
| 1247 |
+
NameObject("/G"): FloatObject(1),
|
| 1248 |
+
NameObject("/U"): TextStringObject("sq m"),
|
| 1249 |
+
}),
|
| 1250 |
+
}),
|
| 1251 |
+
NameObject("/IT"): NameObject("/Area_Annotation"),
|
| 1252 |
+
NameObject("/Subj"): TextStringObject("Area Measurement"),
|
| 1253 |
+
})
|
| 1254 |
+
|
| 1255 |
+
# Append metadata
|
| 1256 |
+
annotations_data.append([
|
| 1257 |
+
vertices,
|
| 1258 |
+
area,
|
| 1259 |
+
perimeter,
|
| 1260 |
+
annot_color,
|
| 1261 |
+
matched_text,
|
| 1262 |
+
matched_nbs,
|
| 1263 |
+
])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1264 |
|
| 1265 |
|
| 1266 |
|
|
|
|
| 1269 |
output_pdf_io.seek(0)
|
| 1270 |
|
| 1271 |
print(f"Annotations updated and saved to {output_pdf_path}")
|
| 1272 |
+
return output_pdf_io.read() , annotations_data
|
| 1273 |
|
| 1274 |
|
| 1275 |
|
|
|
|
| 1522 |
# doc.save(OutputPdfStage1)
|
| 1523 |
modified_pdf_data = doc.tobytes()
|
| 1524 |
# OutputPdfStage2=adjustannotations(modified_pdf_data)
|
| 1525 |
+
OutputPdfStage2,annotations_data=adjustannotations(modified_pdf_data,Legendarray)
|
| 1526 |
|
| 1527 |
|
| 1528 |
# with open("Adjusted_PDF.pdf", "wb") as f:
|