Update Pier_v1 project
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
1 parent
f25e36c050
commit
ad73ddec2b
2476 files changed
+3423
-4902
No files matched your search
@@ -0,0 +1,9 @@
|
||||
"""Public, GUI-free bridge drawing extraction API."""
|
||||
from .api import ExtractionResult, WorkbookResult, extract_folder, check_environment
|
||||
from .core import ExtractionError
|
||||
|
||||
__version__ = '0.1.9'
|
||||
__all__ = ['extract_folder', 'check_environment', 'ExtractionResult', 'WorkbookResult', 'ExtractionError']
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
from .cli import main
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,86 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable
|
||||
import platform
|
||||
from importlib.metadata import version
|
||||
|
||||
from .automatic import run_folder
|
||||
from .core import ExtractionError, converter_path as find_converter
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class WorkbookResult:
|
||||
name: str
|
||||
xlsx_path: Path
|
||||
json_path: Path
|
||||
review_csv_path: Path
|
||||
data: dict[str, Any]
|
||||
|
||||
@property
|
||||
def review_count(self) -> int:
|
||||
return sum(r['status'] not in ['추출','식별자','등록값'] for r in self.data['records'])
|
||||
|
||||
def to_dict(self, *, include_data: bool = True) -> dict[str, Any]:
|
||||
result = dict(name=self.name, xlsx_path=str(self.xlsx_path), json_path=str(self.json_path),
|
||||
review_csv_path=str(self.review_csv_path), review_count=self.review_count)
|
||||
if include_data:
|
||||
result['data'] = self.data
|
||||
return result
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ExtractionResult:
|
||||
output_dir: Path
|
||||
workbooks: tuple[WorkbookResult, ...]
|
||||
files: tuple[dict[str, Any], ...]
|
||||
schema_version: str = '1.0'
|
||||
|
||||
@property
|
||||
def review_count(self) -> int:
|
||||
return sum(book.review_count for book in self.workbooks)
|
||||
|
||||
@property
|
||||
def failed_file_count(self) -> int:
|
||||
return sum(f['status'] == '읽기 실패' for f in self.files)
|
||||
|
||||
@property
|
||||
def status(self) -> str:
|
||||
return 'needs_review' if self.review_count or self.failed_file_count else 'completed'
|
||||
|
||||
def to_dict(self, *, include_data: bool = True) -> dict[str, Any]:
|
||||
return dict(schema_version=self.schema_version, status=self.status,
|
||||
output_dir=str(self.output_dir), review_count=self.review_count,
|
||||
failed_file_count=self.failed_file_count,
|
||||
workbooks=[b.to_dict(include_data=include_data) for b in self.workbooks], files=list(self.files))
|
||||
|
||||
def extract_folder(
|
||||
folder: str | Path,
|
||||
*,
|
||||
output_dir: str | Path | None = None,
|
||||
converter_path: str | Path | None = None,
|
||||
on_progress: Callable[[str], None] | None = None,
|
||||
) -> ExtractionResult:
|
||||
"""Read a DWG folder synchronously and return paths, cell data and review statuses.
|
||||
|
||||
No GUI or network calls. Provide LibreDWG dwgread externally. On failure,
|
||||
raises ExtractionError. Partial file failures remain visible in result.files.
|
||||
Use a worker thread/process when calling from a GUI or async server.
|
||||
"""
|
||||
try:
|
||||
directory, outputs, files = run_folder(folder, output_dir, converter_path, on_progress or (lambda _: None))
|
||||
except ExtractionError:
|
||||
raise
|
||||
except (OSError, ValueError) as exc:
|
||||
raise ExtractionError(str(exc)) from exc
|
||||
books=tuple(WorkbookResult(data['name'],path.resolve(),path.with_suffix('.json').resolve(),
|
||||
path.with_suffix('.csv').resolve(),data) for path,data in outputs)
|
||||
return ExtractionResult(directory.resolve(),books,tuple(files))
|
||||
|
||||
def check_environment(*, converter_path: str | Path | None = None) -> dict[str, Any]:
|
||||
"""Check dependencies without converting any files or starting a GUI."""
|
||||
result = dict(python=platform.python_version(), openpyxl=version('openpyxl'), converter_available=False)
|
||||
try:
|
||||
result.update(converter_available=True, converter_path=str(find_converter(converter_path)))
|
||||
except ExtractionError as exc:
|
||||
result['converter_error']=str(exc)
|
||||
return result
|
||||
@@ -0,0 +1,314 @@
|
||||
"""Folder-only workflow: inspect CAD contents, infer table structure, relocate landmarks."""
|
||||
import copy
|
||||
import csv
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
import re
|
||||
import math
|
||||
from itertools import combinations
|
||||
from .core import APP,HEADERS,ROLES,ExtractionError,convert,converter_path,read_json,write_json,extract
|
||||
from .semantic import Index,norm,classify,bearings,pier,pier_tables,calculation_elevations,drawing_direction,pile_minimum_m
|
||||
from .workbook import export_workbook
|
||||
from .inference import coordinate_points,associate_ips,profile_fields,integrated_bearings
|
||||
from .geometry import LineIndex
|
||||
|
||||
EXCLUDE={'backup','temp','outputs','code','rag','node_modules','__pycache__','_도면추출결과'}
|
||||
PRIMARY_ROLES={'01','02','03'}
|
||||
SOURCE_POLICY={
|
||||
'Pier':{'C':'종평면 형식 표기, 미확인 시 T형','D':'03:교좌면EL 최솟값','E':'01','F':'01','G':'01','H':'01:말뚝길이 최솟값 mm','I':'02:전후열 중심거리+2000mm, 500mm 올림','J':'02:IP1-IP3 거리 초과 500mm','K':'T형 1개 / π형 2개'},
|
||||
'IP':{'D':'02','E':'02'},
|
||||
'Bearing':{'D':'02','E':'02','F':'03:교량받침EL(몰탈상단)','G':'02','H':'02:받침 표제의 전열/후열'},
|
||||
}
|
||||
|
||||
def discover(folder):
|
||||
root=Path(folder).resolve()
|
||||
if not root.is_dir():raise ExtractionError('도면 폴더가 없습니다.')
|
||||
files=[]
|
||||
for p in root.rglob('*'):
|
||||
rel=p.relative_to(root)
|
||||
if any(part.casefold() in EXCLUDE or part.startswith('.') for part in rel.parts[:-1]):continue
|
||||
if p.is_file() and p.suffix.lower()=='.dwg':files.append(p)
|
||||
if not files:raise ExtractionError('폴더와 하위 폴더에 DWG 파일이 없습니다.')
|
||||
return sorted(files)
|
||||
|
||||
def choose_layout(documents):
|
||||
scored=[]
|
||||
for path in (APP/'layouts').glob('*.json'):
|
||||
p=read_json(path);scores=[]
|
||||
for d,s in p['sources'].items():
|
||||
if d not in documents:continue
|
||||
if documents[d]['sha256']==s['sha256']:scores.append(1);continue
|
||||
sig=set(s.get('signature',[]));ts={norm(e['t']) for e in documents[d]['texts']}
|
||||
scores.append(len(sig&ts)/len(sig) if sig else 0)
|
||||
score=sum(scores)/3
|
||||
if score>=.55:scored.append((score,p))
|
||||
scored.sort(key=lambda x:-x[0])
|
||||
if not scored or len(scored)>1 and abs(scored[0][0]-scored[1][0])<.05:return None
|
||||
return scored[0][1]
|
||||
|
||||
def adaptive_result(profile,documents):
|
||||
"""Move numerical locators via text landmarks; never move manual geometry defaults."""
|
||||
p=copy.deepcopy(profile);indices={d:Index(v['texts']) for d,v in documents.items()}
|
||||
# The general drawing is intentionally unavailable under the current policy.
|
||||
p['rules']=[r for r in p['rules'] if not (r['sheet']=='Pier' and r['cell'][0] in 'CDIJK') and not (r['sheet']=='Bearing' and r['cell'][0]=='F')]
|
||||
p['notes']=[]
|
||||
for rule in p['rules']:
|
||||
if rule['sheet']=='Pier' and rule['cell'].startswith('H'):rule['pile_minimum']=True
|
||||
for ref in rule.get('refs',[])+rule.get('guards',[]):
|
||||
d=ref['doc']
|
||||
if d not in documents:
|
||||
rule['op']='missing';rule['note']='해당 역할의 도면을 찾지 못했습니다.';break
|
||||
if documents[d]['sha256']==p['sources'][d]['sha256']:continue
|
||||
entity=indices[d].locate(ref)
|
||||
if entity:
|
||||
ref.update(handle=entity['h'],x=entity['x'],y=entity['y'])
|
||||
else:
|
||||
# A failed semantic relocation must not accidentally fall back to
|
||||
# an unrelated numeric value remaining at the former coordinate.
|
||||
rule['op']='missing';rule['note']='제목·주변 문자로 추출 위치를 유일하게 대응하지 못했습니다.';break
|
||||
return extract(p,documents)
|
||||
|
||||
def blank_result(name):
|
||||
return dict(name=name,profile='automatic',cells={s:{} for s in HEADERS},records=[],changed_sources=[],notes=[])
|
||||
|
||||
def setcell(result,sheet,cell,value,status='추출',messages=None,evidence=None):
|
||||
result['cells'][sheet][cell]=value
|
||||
result['records']=[r for r in result['records'] if not(r['sheet']==sheet and r['cell']==cell)]
|
||||
result['records'].append(dict(sheet=sheet,cell=cell,value=value,status=status,messages=messages or [],note='도면 내용 및 상대 위치 자동 판독',evidence=evidence or []))
|
||||
|
||||
def combine_values(entries,field):
|
||||
values={e[field] for e in entries}
|
||||
if len(values)==1:return entries[0][field],'추출',[]
|
||||
return None,'미확정',['복수 도면/표의 값 상충: '+' / '.join(map(str,sorted(values,key=str)))]
|
||||
|
||||
def next_500(distance_m):
|
||||
# Remove sub-micrometre coordinate arithmetic noise at exact half-metres.
|
||||
return (math.floor(round(distance_m*1000,6)/500)+1)*500
|
||||
|
||||
def column_count(pier_type):
|
||||
return {'T형':1,'T':1,'Π형':2,'Π':2,'PI형':2}.get(norm(str(pier_type)))
|
||||
|
||||
def bearing_row_width(cells,rows):
|
||||
groups={'전열':[],'후열':[]}
|
||||
for row in rows:
|
||||
kind=cells.get(f'H{row}');xy=[cells.get(f'{c}{row}') for c in 'DE']
|
||||
if kind not in groups or not all(isinstance(v,(int,float)) and math.isfinite(v) for v in xy):return None,None
|
||||
groups[kind].append(xy)
|
||||
if not all(groups.values()):return None,None
|
||||
centers=[tuple(sum(p[k] for p in points)/len(points) for k in [0,1]) for points in groups.values()]
|
||||
distance_mm=math.dist(*centers)*1000
|
||||
if distance_mm<=1e-6:return None,None
|
||||
return math.ceil(round(distance_mm+2000,6)/500)*500,distance_mm
|
||||
|
||||
def apply_input_conventions(result):
|
||||
for cell,p in list(result['cells']['Pier'].items()):
|
||||
if not cell.startswith('B'):continue
|
||||
row=cell[1:];kind=result['cells']['Pier'].get('C'+row)
|
||||
count=column_count(kind)
|
||||
setcell(result,'Pier','K'+row,count,'기본값' if count else '미확인',[f'형식별 지정값: T형 1개, π형 2개. 현재 형식 {kind}'])
|
||||
record=next((r for r in result['records'] if r['sheet']=='Pier' and r['cell']=='H'+row),None)
|
||||
value=result['cells']['Pier'].get('H'+row);length=pile_minimum_m(value)
|
||||
setcell(result,'Pier','H'+row,round(length*1000,6) if length is not None else None,
|
||||
'산정값' if length is not None else '미확인',
|
||||
[f'말뚝길이 최솟값 선택 후 m→mm 변환. 변환 전 값: {value}']+((record or {}).get('messages',[])),
|
||||
(record or {}).get('evidence',[]))
|
||||
|
||||
def apply_derived_piers(result,zrows):
|
||||
cells=result['cells']
|
||||
def rows_for(sheet,p):
|
||||
return sorted(int(c[1:]) for c,v in cells[sheet].items() if c.startswith('B') and v==p)
|
||||
def point(sheet,row):
|
||||
xy=[cells[sheet].get(f'{c}{row}') for c in 'DE']
|
||||
return tuple(xy) if all(isinstance(v,(int,float)) and math.isfinite(v) for v in xy) else None
|
||||
def evidence(sheet,rows):
|
||||
return [e for r in result['records'] if r['sheet']==sheet and r['cell'][0] in 'DE' and int(r['cell'][1:]) in rows for e in r['evidence']]
|
||||
for cell,p in list(cells['Pier'].items()):
|
||||
if not cell.startswith('B'):continue
|
||||
row=int(cell[1:])
|
||||
if not cells['Pier'].get(f'C{row}'):setcell(result,'Pier',f'C{row}','T형','기본값',['사용자 지정 기본 교각형식. 형상 판독값 아님.'])
|
||||
entries=[e for e in zrows if e['p']==p and 'seat' in e]
|
||||
grouped={no:[e for e in entries if e['no']==no] for no in {e['no'] for e in entries}}
|
||||
seats=[combine_values(es,'seat')[0] for es in grouped.values()]
|
||||
expected={cells['Bearing'].get(f'C{r}') for r in rows_for('Bearing',p)}
|
||||
if seats and None not in seats and expected.issubset(grouped):
|
||||
setcell(result,'Pier',f'D{row}',min(seats),'산정값',['교각별 교좌면 EL 최솟값 (m)'],[e for r in entries for e in r['evidence']])
|
||||
else:setcell(result,'Pier',f'D{row}',None,'미확정',['교좌면 EL 누락 또는 동일 받침의 복수 값 상충'],[e for r in entries for e in r['evidence']])
|
||||
br=rows_for('Bearing',p);width,dist=bearing_row_width(cells['Bearing'],br)
|
||||
if width is not None:
|
||||
setcell(result,'Pier',f'I{row}',width,'산정값',[f'전열/후열 받침 좌표 평균점 사이 거리 {dist:.6f} mm + 2000mm, 500mm 단위 올림'],evidence('Bearing',br)+[e for r in result['records'] if r['sheet']=='Bearing' and r['cell'].startswith('H') and int(r['cell'][1:]) in br for e in r['evidence']])
|
||||
else:setcell(result,'Pier',f'I{row}',None,'미확인',['폭 산정에 필요한 전열·후열 구분 또는 좌표 부족/상충'])
|
||||
ip=rows_for('IP',p);selected=[]
|
||||
for number in [1,3]:
|
||||
matches=[r for r in ip if norm(str(cells['IP'].get(f'C{r}'))).replace('.','') in [str(number),f'IP{number}']]
|
||||
if len(matches)==1:selected.append(matches[0])
|
||||
points=[point('IP',r) for r in selected]
|
||||
if len(points)==2 and all(v is not None for v in points) and math.dist(*points)>0:
|
||||
dist=math.dist(*points)
|
||||
setcell(result,'Pier',f'J{row}',next_500(dist),'산정값',[f'IP1-IP3 평면거리 {dist*1000:.6f} mm를 초과하는 다음 500mm'],evidence('IP',selected))
|
||||
else:setcell(result,'Pier',f'J{row}',None,'미확인',['길이 산정에 필요한 유일한 IP1·IP3 좌표 부족/상충'])
|
||||
|
||||
def assemble_group(name,documents_by_role):
|
||||
# Multiple revisions are all read. Do not silently choose a "latest" filename.
|
||||
documents_by_role={d:v for d,v in documents_by_role.items() if d!='04'}
|
||||
simple={d:items[0][1] for d,items in documents_by_role.items() if len(items)==1}
|
||||
layout=choose_layout(simple)
|
||||
if layout:result=adaptive_result(layout,{d:simple[d] for d in ['01','02'] if d in simple})
|
||||
else:result=blank_result(name)
|
||||
result['name']=name;result['profile']='automatic'
|
||||
sources={};brows=[];zrows=[];prows=[];observed=set();ip_points=[]
|
||||
result['source_policy']=SOURCE_POLICY
|
||||
result['supplementary_drawings']=[]
|
||||
directions={drawing_direction(e['t']) for _,data in documents_by_role.get('02',[]) for e in data['texts']}-{None}
|
||||
for doc,items in documents_by_role.items():
|
||||
for index,(path,data) in enumerate(items):
|
||||
lines=LineIndex(data.get('segments')) if data.get('segments') else None
|
||||
key=doc if len(items)==1 else f'{doc}_{index+1}'
|
||||
sources[key]=dict(path=str(path),sha256=data['sha256'])
|
||||
if doc=='03':
|
||||
result['supplementary_drawings'].append(dict(doc=key,role=ROLES[doc],
|
||||
status='표고 추출 원문',texts=data['texts'],
|
||||
note='받침 Z=몰탈상단, 두부보 EL=교좌면 EL 최솟값'))
|
||||
rows,issues=calculation_elevations(data['texts'],lines);result['notes'].extend(issues)
|
||||
for b in integrated_bearings(data['texts'],rows):
|
||||
b['evidence']=[dict(doc=key,**e) for e in b['evidence']];brows.append(b);observed.add(b['p'])
|
||||
for row in rows:
|
||||
if len(directions)==1 and row['direction'] is not None and row['direction'] not in directions:continue
|
||||
row['evidence']=[dict(doc=key,**e) for e in row['evidence']];zrows.append(row);observed.add(row['p'])
|
||||
if doc=='01':
|
||||
for row in pier_tables(data['texts'],doc)+profile_fields(data['texts'],lines):
|
||||
row['evidence']=[dict(doc=key,**e) for e in row['evidence']];prows.append(row);observed.add(row['p'])
|
||||
if doc=='02':
|
||||
for point in coordinate_points(data['texts']):
|
||||
point['evidence']=[dict(doc=key,**e) for e in point['evidence']];ip_points.append(point)
|
||||
rows,issues=bearings(data['texts'],lines);result['notes'].extend(issues)
|
||||
for row in rows:
|
||||
row['evidence']=[dict(doc=key,**e) for e in row['evidence']];brows.append(row);observed.add(row['p'])
|
||||
primary_piers={r['p'] for r in brows+zrows}
|
||||
if primary_piers:
|
||||
observed=primary_piers
|
||||
prows=[r for r in prows if r['p'] in primary_piers]
|
||||
result['sources']=sources
|
||||
if len(directions)>1:result['notes'].append('복수 방향 도면: 교각/받침 번호가 겹치면 상충값으로 보존하며 임의 방향을 선택하지 않음.')
|
||||
if brows or zrows:
|
||||
result['cells']['Bearing']={};result['records']=[r for r in result['records'] if r['sheet']!='Bearing']
|
||||
keys=sorted({(r['p'],r['no']) for r in brows+zrows},key=lambda x:(int(x[0][1:]),x[1]))
|
||||
for row,(p,no) in enumerate(keys,3):
|
||||
setcell(result,'Bearing',f'B{row}',p,'식별자');setcell(result,'Bearing',f'C{row}',no,'식별자')
|
||||
for col,field,entries in [('D','x',brows),('E','y',brows),('F','z',zrows),('G','angle',brows),('H','row_type',brows)]:
|
||||
es=[e for e in entries if e['p']==p and e['no']==no and field in e]
|
||||
if es:
|
||||
value,status,messages=combine_values(es,field)
|
||||
setcell(result,'Bearing',f'{col}{row}',value,status,messages,[e for r in es for e in r['evidence']])
|
||||
else:setcell(result,'Bearing',f'{col}{row}',None,'미확인',[f'{p} 받침 {no}: 대응 표를 찾지 못했습니다.'])
|
||||
# Newly discovered piers are never silently omitted because an old layout had fewer rows.
|
||||
existing={v for c,v in result['cells']['Pier'].items() if c.startswith('B')}
|
||||
for p in sorted(observed-existing,key=lambda p:int(p[1:])):
|
||||
row=max([int(c[1:]) for c in result['cells']['Pier']]+[2])+1
|
||||
setcell(result,'Pier',f'B{row}',p,'식별자')
|
||||
for col in 'CDEFGHIJK':setcell(result,'Pier',f'{col}{row}',None,'미확인',['형상·치수·종단 값의 교각 대응 근거 미확인'])
|
||||
for p in observed:
|
||||
rows=[int(c[1:]) for c,v in result['cells']['Pier'].items() if c.startswith('B') and v==p]
|
||||
if len(rows)!=1:continue
|
||||
for col in 'CDEFGHIJK':
|
||||
entries=[e for e in prows if e['p']==p and e['col']==col]
|
||||
if entries:
|
||||
explicit=[e for e in entries if not e.get('inferred')]
|
||||
if explicit:entries=explicit
|
||||
elif layout:continue # Preserve audited locators, including unresolved conflicts.
|
||||
value,status,messages=combine_values(entries,'value')
|
||||
if col=='H':
|
||||
lengths=[pile_minimum_m(e['value']) for e in entries]
|
||||
value=min(lengths) if None not in lengths else None
|
||||
status='산정값' if value is not None else '미확인';messages=['말뚝길이 표기의 최솟값 선택']
|
||||
if any(e.get('inferred') for e in entries):
|
||||
messages+=['교각 표기와 종평면 인접 문자의 상대 위치로 대응: 지시선 연결 검토 필요']
|
||||
if value is not None:status='검토필요'
|
||||
setcell(result,'Pier',f'{col}{rows[0]}',value,status,messages,[e for r in entries for e in r['evidence']])
|
||||
# World-coordinate proximity, ambiguity rejection, then CAD top-to-bottom ordering.
|
||||
linked=associate_ips(ip_points,brows)
|
||||
if not result['cells']['IP']:
|
||||
row=3
|
||||
for p in sorted(observed,key=lambda p:int(p[1:])):
|
||||
points=linked.get(p,[])
|
||||
for i in range(4):
|
||||
point=points[i] if i<3 and i<len(points) else None
|
||||
setcell(result,'IP',f'B{row}',p,'식별자')
|
||||
setcell(result,'IP',f'C{row}',f'IP{i+1}','식별자')
|
||||
for col,field in [('D','x'),('E','y')]:
|
||||
setcell(result,'IP',f'{col}{row}',point[field] if point else None,'산정값' if point else '미확인',
|
||||
['받침 좌표 범위와 지시점 거리로 교각 대응, CAD 위→아래 순서' if point else '교각에 유일하게 연결되는 IP 3점 미확인'],point['evidence'] if point else [])
|
||||
if i==3:
|
||||
for col in 'DEF':setcell(result,'IP',f'{col}{row}',None,'기본값',['IP4는 빈칸으로 유지'])
|
||||
else:setcell(result,'IP',f'F{row}',None,'대상외',['꺾임각은 요청 항목 외'])
|
||||
row+=1
|
||||
max_ip_row=max([int(a[1:]) for a in result['cells']['IP']]+[2])
|
||||
for p in observed:
|
||||
rows=[r for r in range(3,max_ip_row+1) if result['cells']['IP'].get(f'B{r}')==p]
|
||||
fourth=[r for r in rows if norm(str(result['cells']['IP'].get(f'C{r}'))).replace('.','') in ['4','IP4']]
|
||||
if fourth:
|
||||
for r in fourth:
|
||||
for col in 'DEF':setcell(result,'IP',f'{col}{r}',None,'기본값',['IP4는 빈칸으로 유지'])
|
||||
continue
|
||||
row=max_ip_row+1;max_ip_row=row
|
||||
for col,value in [('B',p),('C','IP4'),('D',None),('E',None),('F',None)]:
|
||||
setcell(result,'IP',f'{col}{row}',value,'식별자' if col in 'BC' else '기본값',['IP4는 빈칸으로 유지'] if col not in 'BC' else [])
|
||||
result['coordinate_callouts']=ip_points
|
||||
apply_derived_piers(result,zrows)
|
||||
apply_input_conventions(result)
|
||||
result['notes']+=['종평면 형식 표기 우선, 미확인 시 기본 T형. 두부보 치수는 좌표 기반 산정값.',
|
||||
'폭=전후열 좌표 평균점 거리+2000mm를 500mm 올림. 길이=IP1-IP3 거리를 초과하는 다음 500mm.',
|
||||
'받침 Z=ELEVATION 몰탈상단, 두부보 EL=교각별 교좌면 EL 최솟값.',
|
||||
'폴더 자동 판독: 파일명 대신 도면 문자 내용으로 역할을 분류.',
|
||||
'좌표표 열은 헤더로 인식. 기타 값은 제목·주변 문자의 상대 배치를 대조.',
|
||||
'일반 형상 인식/OCR은 지원하지 않음. 근거가 모호한 값은 공란·검토 항목으로 보존.']
|
||||
for d in PRIMARY_ROLES:
|
||||
if d not in documents_by_role:result['notes'].append(f'{ROLES[d]} 역할 도면 미발견')
|
||||
return result
|
||||
|
||||
def run_folder(folder,output=None,converter=None,log=lambda s:None):
|
||||
root=Path(folder).resolve();files=discover(root)
|
||||
output=Path(output).resolve() if output else root/'_도면추출결과'
|
||||
output.mkdir(parents=True,exist_ok=True);exe=converter_path(converter)
|
||||
groups={};inventory=[]
|
||||
for path in files:
|
||||
if output==path.parent or output in path.parents:continue
|
||||
try:
|
||||
data=convert(path,output/'.cache',exe,log);roles=classify(data['texts'])
|
||||
inventory.append(dict(file=str(path),roles=roles,status='인식' if roles else '역할 미확정',text_count=len(data['texts'])))
|
||||
log(f'{path.name}: '+(', '.join(ROLES[d] for d in roles) or '문자 읽기 완료, 역할 미확정'))
|
||||
# Each folder is an independent drawing set: identical P1 IDs belonging
|
||||
# to different bridges must never be combined accidentally.
|
||||
for d in roles:
|
||||
if d=='04':continue # User policy: general pier drawings are not inputs.
|
||||
bucket=groups.setdefault(path.parent,{}).setdefault(d,[])
|
||||
if not any(old['sha256']==data['sha256'] for _,old in bucket):bucket.append((path,data))
|
||||
except Exception as e:
|
||||
inventory.append(dict(file=str(path),roles=[],status='읽기 실패',reason=str(e)));log(f'{path.name}: 읽기 실패 — {e}')
|
||||
if not groups:
|
||||
write_json(output/'파일판독내역.json',inventory)
|
||||
raise ExtractionError('모든 DWG를 읽었으나 대상 역할을 식별하지 못했습니다. 파일판독내역.json을 확인하세요.')
|
||||
# A single unambiguous set may be distributed over category subfolders.
|
||||
if len(groups)>1 and all(sum(len(g.get(d,[])) for g in groups.values())<=1 for d in ROLES):
|
||||
merged={}
|
||||
for g in groups.values():
|
||||
for d,items in g.items():merged.setdefault(d,[]).extend(items)
|
||||
groups={root:merged}
|
||||
destdir=output/datetime.now().strftime('%Y%m%d_%H%M%S_%f');destdir.mkdir()
|
||||
write_json(destdir/'파일판독내역.json',inventory);outputs=[]
|
||||
for group,docs in groups.items():
|
||||
simple={d:a[0][1] for d,a in docs.items() if len(a)==1};layout=choose_layout(simple)
|
||||
unchanged=layout and all(d in simple and simple[d]['sha256']==s['sha256'] for d,s in layout['sources'].items())
|
||||
name=layout['name'] if unchanged else group.name
|
||||
log(f'{name}: 표·교각 번호 및 상대 배치 분석 중')
|
||||
result=assemble_group(name,docs)
|
||||
if not any(result['cells'][s] for s in HEADERS):continue
|
||||
safe=re.sub(r'[^\w가-힣().-]+','_',name)[:45]
|
||||
target=destdir/(safe+f'_{len(outputs)+1}_교각입력데이터.xlsx')
|
||||
export_workbook(result,target);write_json(target.with_suffix('.json'),result)
|
||||
with target.with_suffix('.csv').open('w',encoding='utf-8-sig',newline='') as f:
|
||||
w=csv.writer(f);w.writerow(['시트','셀','상태','값','사유'])
|
||||
for r in result['records']:
|
||||
if r['status'] not in ['식별자','추출','등록값']:w.writerow([r['sheet'],r['cell'],r['status'],r['value'],' / '.join(r['messages'])])
|
||||
outputs.append((target,result));log(f'엑셀 저장: {target.name}')
|
||||
if not outputs:raise ExtractionError('역할은 식별했지만 교각/좌표 데이터를 추출하지 못했습니다. 파일판독내역을 확인하세요.')
|
||||
return destdir,outputs,inventory
|
||||
@@ -0,0 +1,31 @@
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from .api import extract_folder,check_environment
|
||||
from .core import ExtractionError
|
||||
|
||||
def main(argv=None):
|
||||
parser=argparse.ArgumentParser(description='Offline DWG bridge extractor (JSON stdout, progress stderr)')
|
||||
parser.add_argument('folder',nargs='?')
|
||||
parser.add_argument('--output',help='Result directory (default: input/_도면추출결과)')
|
||||
parser.add_argument('--converter',help='LibreDWG dwgread executable path')
|
||||
parser.add_argument('--check',action='store_true',help='Check runtime dependencies only')
|
||||
parser.add_argument('--include-data',action='store_true',help='Include all cell data in JSON stdout')
|
||||
args=parser.parse_args(argv)
|
||||
if hasattr(sys.stdout,'reconfigure'):sys.stdout.reconfigure(encoding='utf-8')
|
||||
if hasattr(sys.stderr,'reconfigure'):sys.stderr.reconfigure(encoding='utf-8')
|
||||
if args.check:
|
||||
result=check_environment(converter_path=args.converter)
|
||||
print(json.dumps(result,ensure_ascii=False))
|
||||
return 0 if result['converter_available'] else 2
|
||||
if not args.folder:parser.error('folder is required unless --check is used')
|
||||
try:
|
||||
result=extract_folder(args.folder,output_dir=args.output,converter_path=args.converter,
|
||||
on_progress=lambda msg:print(msg,file=sys.stderr,flush=True))
|
||||
print(json.dumps(result.to_dict(include_data=args.include_data),ensure_ascii=False))
|
||||
return 0
|
||||
except ExtractionError as exc:
|
||||
print(json.dumps(dict(schema_version='1.0',status='error',error=str(exc)),ensure_ascii=False))
|
||||
return 1
|
||||
|
||||
if __name__=='__main__':raise SystemExit(main())
|
||||
@@ -0,0 +1,284 @@
|
||||
"""Offline DWG extraction. No AI/network calls and no use of previous output values."""
|
||||
from __future__ import annotations
|
||||
import csv
|
||||
import hashlib
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
import tempfile
|
||||
from .geometry import scene
|
||||
from datetime import datetime
|
||||
|
||||
APP = Path(__file__).resolve().parent
|
||||
ROLES = {'01': '종평면도', '02': '받침배치도', '04': '교각일반도'}
|
||||
ROLES['03'] = 'ELEVATION 산출근거도'
|
||||
HEADERS = {
|
||||
'Pier': ['교각명', '교각형식', '두부보\nEL_m', '기초 하단\nEL_m', '지반고\nEL_m', '기초 형식', '말뚝\n길이_mm', '두부보\n폭_mm', '두부보\n길이_mm', '기둥갯수'],
|
||||
'IP': ['교각명', 'IP점', 'x_m', 'y_m', '코핑 꺾임각'],
|
||||
'Bearing': ['교각명', '받침번호', 'x_m', 'y_m', 'z_m', '방위각', '전열/후열'],
|
||||
}
|
||||
|
||||
class ExtractionError(ValueError):
|
||||
pass
|
||||
|
||||
def read_json(path):
|
||||
return json.loads(Path(path).read_text(encoding='utf-8-sig'))
|
||||
|
||||
def write_json(path, data):
|
||||
Path(path).write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding='utf-8')
|
||||
|
||||
def sha256(path):
|
||||
h = hashlib.sha256()
|
||||
with Path(path).open('rb') as f:
|
||||
for chunk in iter(lambda: f.read(1024 * 1024), b''):
|
||||
h.update(chunk)
|
||||
return h.hexdigest()
|
||||
|
||||
def converter_path(explicit=None):
|
||||
# Explicit configuration is authoritative: a typo must not select another binary.
|
||||
configured = explicit or os.environ.get('BRIDGE_DWGREAD')
|
||||
if configured:
|
||||
candidate = Path(configured).expanduser()
|
||||
if candidate.is_file():
|
||||
return candidate.resolve()
|
||||
raise ExtractionError(f'Configured DWG converter does not exist: {configured}')
|
||||
found = shutil.which('dwgread') or shutil.which('dwgread.exe')
|
||||
if found:
|
||||
return Path(found).resolve()
|
||||
raise ExtractionError('DWG converter not found. Pass converter_path or set BRIDGE_DWGREAD / PATH.')
|
||||
|
||||
|
||||
def compact(raw):
|
||||
if not isinstance(raw.get('OBJECTS'), list):
|
||||
raise ExtractionError('변환 결과에 DWG 객체 목록이 없습니다.')
|
||||
texts = []
|
||||
for e in raw['OBJECTS']:
|
||||
if e.get('entity') not in ('TEXT','MTEXT','ATTRIB','ATTDEF','DIMENSION_LINEAR','DIMENSION_ALIGNED'):
|
||||
continue
|
||||
t = e.get('text_value', e.get('text', ''))
|
||||
p = e.get('ins_pt', e.get('text_midpt', [0,0]))
|
||||
if not isinstance(t, str) or not t.strip():
|
||||
continue
|
||||
texts.append(dict(h=e['handle'][-1], x=p[0], y=p[1], t=t))
|
||||
if not texts:
|
||||
raise ExtractionError('추출 가능한 CAD 문자가 없습니다. 스캔 이미지 도면은 지원하지 않습니다.')
|
||||
return texts
|
||||
|
||||
def read_cad_json(path):
|
||||
"""Recover independently valid text entities if LibreDWG emits invalid geometry JSON."""
|
||||
raw=Path(path).read_text(encoding='utf-8',errors='replace')
|
||||
warnings=[]
|
||||
if '\ufffd' in raw:warnings.append('변환기의 잘못된 UTF-8 문자를 대체함; 해당 문자는 검토 필요')
|
||||
try:return json.loads(raw),warnings
|
||||
except json.JSONDecodeError:
|
||||
decoder=json.JSONDecoder();objects=[];failed=0
|
||||
for m in re.finditer(r'(?m)^ \{\s*\n\s*"(?:entity|object)":\s*"[^\"]+"',raw):
|
||||
try:
|
||||
obj,_=decoder.raw_decode(raw,m.start()+4)
|
||||
if '\ufffd' in obj.get('text_value',obj.get('text','')):failed+=1;continue
|
||||
objects.append(obj)
|
||||
except (json.JSONDecodeError,TypeError):failed+=1
|
||||
if not objects:raise ExtractionError('변환 JSON 오류 및 복구 가능한 CAD 문자 없음')
|
||||
warnings.append(f'변환 JSON 오류: 독립 CAD 객체 {len(objects)}개 복구, 손상 객체 {failed}개 제외')
|
||||
return {'OBJECTS':objects},warnings
|
||||
|
||||
def convert(source, cache, exe, log=lambda s: None):
|
||||
source, cache = Path(source), Path(cache)
|
||||
if source.suffix.lower() != '.dwg' or not source.is_file():
|
||||
raise ExtractionError(f'DWG 파일을 확인하세요: {source}')
|
||||
digest = sha256(source)
|
||||
# Both input content and converter version participate in the cache identity.
|
||||
key = digest + '-' + sha256(exe)[:12]
|
||||
cache.mkdir(parents=True, exist_ok=True)
|
||||
dest = cache/(key+'.json')
|
||||
if dest.exists():
|
||||
try:
|
||||
data = read_json(dest)
|
||||
if data.get('scene_version')==3 and data['sha256']==digest and isinstance(data['texts'],list) and data['texts']:
|
||||
log(f'{source.name}: 변경 없음, 캐시 사용')
|
||||
return data
|
||||
except (ValueError, KeyError):
|
||||
pass
|
||||
log(f'{source.name}: DWG 문자 읽는 중')
|
||||
with tempfile.TemporaryDirectory(prefix='dwg_', dir=cache) as tmp:
|
||||
raw = Path(tmp)/'raw.json'
|
||||
try:
|
||||
p = subprocess.run([str(exe), '-O', 'JSON', '-o', str(raw), str(source.resolve())],
|
||||
capture_output=True, timeout=180, creationflags=getattr(subprocess,'CREATE_NO_WINDOW',0))
|
||||
except subprocess.TimeoutExpired as e:
|
||||
raise ExtractionError(f'DWG 변환 시간 초과: {source.name}') from e
|
||||
if p.returncode != 0 or not raw.exists():
|
||||
message = p.stderr.decode('utf8', errors='replace')[-1200:]
|
||||
raise ExtractionError(f'DWG 변환 실패: {source.name}\n{message}')
|
||||
decoded,warnings=read_cad_json(raw)
|
||||
texts,segments,scene_warnings=scene(decoded)
|
||||
if not texts:raise ExtractionError('표시 공간에 추출 가능한 CAD 문자가 없습니다.')
|
||||
data = dict(sha256=digest,texts=texts,segments=segments,scene_version=3,conversion_warnings=warnings+scene_warnings)
|
||||
if sha256(source)!=digest:
|
||||
raise ExtractionError('읽는 동안 원본 도면이 변경되었습니다. 저장 후 다시 실행하세요.')
|
||||
staging = Path(tmp)/'compact.json'
|
||||
write_json(staging, data)
|
||||
os.replace(staging, dest)
|
||||
return data
|
||||
|
||||
def load_profile(path):
|
||||
p = read_json(path)
|
||||
if p.get('version') != 1 or not p.get('rules') or set(p.get('sources',{})) != set(ROLES):
|
||||
raise ExtractionError('지원하지 않는 추출 규칙 파일입니다.')
|
||||
seen = set()
|
||||
for r in p['rules']:
|
||||
sheet, cell = r.get('sheet'), r.get('cell','')
|
||||
if sheet not in HEADERS or not re.fullmatch(r'[B-K][1-9]\d{0,4}',cell):
|
||||
raise ExtractionError(f'대상 셀 오류: {sheet}!{cell}')
|
||||
if int(re.search(r'\d+',cell)[0])<3 or ord(cell[0])-66>=len(HEADERS[sheet]):
|
||||
raise ExtractionError(f'입력 표 밖 셀: {sheet}!{cell}')
|
||||
if (sheet,cell) in seen:
|
||||
raise ExtractionError(f'중복 규칙: {sheet}!{cell}')
|
||||
seen.add((sheet,cell))
|
||||
if r.get('op') not in ['label','manual','missing','number','min','agree','range','text']:
|
||||
raise ExtractionError(f'지원하지 않는 연산: {r.get("op")}')
|
||||
if r['op'] not in ['label','manual','missing'] and not r.get('refs'):
|
||||
raise ExtractionError(f'추출 위치 없음: {sheet}!{cell}')
|
||||
if r['op']=='manual' and r.get('doc') not in ROLES:
|
||||
raise ExtractionError('등록값의 근거 도면이 필요합니다.')
|
||||
for ref in r.get('refs',[])+r.get('guards',[]):
|
||||
if ref.get('doc') not in ROLES:
|
||||
raise ExtractionError('알 수 없는 근거 도면 역할입니다.')
|
||||
rx=re.compile(ref['pattern'])
|
||||
if not 0<=int(ref.get('group',1))<=rx.groups:
|
||||
raise ExtractionError('정규식 그룹 번호가 맞지 않습니다.')
|
||||
return p
|
||||
|
||||
def source_files(profile, folder, overrides=None):
|
||||
folder=Path(folder); overrides=overrides or {}; found={}
|
||||
for doc,s in profile['sources'].items():
|
||||
if overrides.get(doc):p=Path(overrides[doc])
|
||||
else:
|
||||
# No recursive guessing; explicitly selected folder only.
|
||||
matches=[f for f in folder.iterdir() if f.is_file() and f.name.casefold()==s['filename'].casefold()]
|
||||
if len(matches)!=1:
|
||||
raise ExtractionError(f'{ROLES[doc]} 파일을 선택하세요: {s["filename"]}')
|
||||
p=matches[0]
|
||||
if not p.is_file() or p.suffix.lower()!='.dwg':
|
||||
raise ExtractionError(f'{ROLES[doc]} DWG가 없습니다: {p}')
|
||||
found[doc]=p.resolve()
|
||||
if len(set(found.values()))!=3:
|
||||
raise ExtractionError('서로 다른 종평면도·받침배치도·교각일반도 3개를 선택하세요.')
|
||||
return found
|
||||
|
||||
def resolve(ref, documents):
|
||||
texts=documents[ref['doc']]['texts']
|
||||
# Handle is validated against geometry and text grammar, never trusted alone.
|
||||
spatial=[e for e in texts if abs(e['x']-ref['x'])<0.01 and abs(e['y']-ref['y'])<0.01]
|
||||
matches=[e for e in spatial if re.fullmatch(ref['pattern'],e['t'])]
|
||||
exact=[e for e in matches if e['h']==ref['handle']]
|
||||
if len(exact)==1:
|
||||
entity=exact[0]
|
||||
elif len(matches)==1:
|
||||
entity=matches[0]
|
||||
else:
|
||||
raise ExtractionError(f"문자 위치 식별 실패({len(matches)}개): {ROLES[ref['doc']]} handle {ref['handle']:X}")
|
||||
value=re.fullmatch(ref['pattern'],entity['t']).group(ref.get('group',1))
|
||||
return value,dict(doc=ref['doc'],**entity)
|
||||
|
||||
def evaluate(rule, documents, changed):
|
||||
op=rule['op']; evidence=[]; status='추출'; messages=[]
|
||||
if op=='label':return rule.get('value'),'식별자',[],[]
|
||||
if op=='missing':return None,'미확인',[rule.get('note','추출 규칙 미등록')],[]
|
||||
if op=='manual':
|
||||
if rule['doc'] in changed:
|
||||
return None,'재검토',['도면 변경: 형상 판독 등록값을 자동 적용하지 않았습니다.'],[]
|
||||
return rule.get('value'),'등록값',[rule.get('note','등록 시 형상 판독값')],[]
|
||||
for ref in rule.get('guards',[]):
|
||||
_,e=resolve(ref,documents);evidence.append(e)
|
||||
values=[]
|
||||
for ref in rule['refs']:
|
||||
value,e=resolve(ref,documents);values.append(value);evidence.append(e)
|
||||
if rule.get('pile_minimum'):
|
||||
from .semantic import pile_minimum_m
|
||||
lengths=[pile_minimum_m(v) for v in values]
|
||||
value=min(lengths) if lengths and None not in lengths else None
|
||||
status='산정값' if value is not None else '미확인'
|
||||
messages.append('말뚝길이 표기 최솟값 선택: '+' / '.join(values))
|
||||
elif op=='text':value=values[0].strip()
|
||||
elif op=='range':
|
||||
value=values[0].replace(' ','')
|
||||
if '~' in value:status='검토필요';messages.append('도면 범위 원문. 단일 모델 입력 길이는 미확정.')
|
||||
else:value=float(value)
|
||||
else:
|
||||
nums=[float(v) for v in values]
|
||||
if not all(math.isfinite(v) for v in nums):raise ExtractionError('유한 숫자가 아닙니다.')
|
||||
if op=='agree' and max(nums)-min(nums)>1e-8:
|
||||
value=None;status='미확정';messages.append('도면 표기 상충: '+' / '.join(map(str,nums)))
|
||||
else:
|
||||
value=(min(nums) if op=='min' else nums[0])*rule.get('scale',1)
|
||||
if rule.get('scale')==1000:value=round(value,6)
|
||||
if rule.get('review'):messages.append(rule['review']);status='검토필요'
|
||||
if any(e['doc'] in changed for e in evidence):
|
||||
messages.append('등록 후 도면 변경됨. 동일 위치에서 재추출한 값의 의미를 확인하세요.')
|
||||
if status=='추출':status='재검토'
|
||||
return value,status,messages,evidence
|
||||
|
||||
def extract(profile, documents):
|
||||
changed={d for d in documents if documents[d]['sha256']!=profile['sources'][d]['sha256']}
|
||||
cells={s:{} for s in HEADERS};records=[]
|
||||
for rule in profile['rules']:
|
||||
try:value,status,messages,evidence=evaluate(rule,documents,changed)
|
||||
except (ExtractionError,ValueError,IndexError) as e:
|
||||
value,status,messages,evidence=None,'추출실패',[str(e)],[]
|
||||
cells[rule['sheet']][rule['cell']]=value
|
||||
records.append(dict(sheet=rule['sheet'],cell=rule['cell'],value=value,status=status,
|
||||
messages=messages,note=rule.get('note',''),evidence=evidence))
|
||||
# Every labeled record must have all non-optional fields registered.
|
||||
for sheet,values in cells.items():
|
||||
rows={int(c[1:]) for c in values if c.startswith('B') and values[c] is not None}
|
||||
for row in sorted(rows):
|
||||
for col in range(2,2+len(HEADERS[sheet])):
|
||||
cell=f'{chr(64+col)}{row}'
|
||||
if cell not in values:
|
||||
values[cell]=None
|
||||
records.append(dict(sheet=sheet,cell=cell,value=None,status='미확인',messages=['필드 추출 규칙 미등록'],note='',evidence=[]))
|
||||
return dict(name=profile['name'],profile=profile['id'],cells=cells,records=records,
|
||||
changed_sources=sorted(changed),notes=profile.get('notes',[]))
|
||||
|
||||
def run(profile_path, folder, output, converter=None, overrides=None, log=lambda s:None):
|
||||
from .workbook import export_workbook
|
||||
profile=load_profile(profile_path)
|
||||
paths=source_files(profile,folder,overrides)
|
||||
exe=converter_path(converter)
|
||||
output=Path(output);output.mkdir(parents=True,exist_ok=True)
|
||||
docs={d:convert(p,output/'.cache',exe,log) for d,p in paths.items()}
|
||||
result=extract(profile,docs)
|
||||
result['sources']={d:dict(path=str(paths[d]),sha256=docs[d]['sha256']) for d in paths}
|
||||
if all(r['status']=='추출실패' for r in result['records'] if r['status'] not in ['식별자','등록값','미확인']):
|
||||
raise ExtractionError('이 도면은 선택한 추출 규칙과 맞지 않습니다. 새 규칙을 등록하세요.')
|
||||
safe=re.sub(r'[^\w가-힣().-]+','_',profile['name'])[:45]
|
||||
run_dir=output/(safe+'_'+datetime.now().strftime('%Y%m%d_%H%M%S_%f'))
|
||||
run_dir.mkdir(exist_ok=False)
|
||||
dest=run_dir/(safe+'_교각입력데이터.xlsx')
|
||||
export_workbook(result,dest)
|
||||
write_json(run_dir/'추출근거.json',result)
|
||||
with (run_dir/'검토항목.csv').open('w',encoding='utf-8-sig',newline='') as f:
|
||||
w=csv.writer(f);w.writerow(['시트','셀','상태','값','사유'])
|
||||
for r in result['records']:
|
||||
if r['status'] not in ['추출','식별자','등록값']:
|
||||
w.writerow([r['sheet'],r['cell'],r['status'],r['value'], ' / '.join(r['messages'])])
|
||||
log(f'완료: {dest}')
|
||||
return dest,result
|
||||
|
||||
def index_files(paths,output,converter=None,log=lambda s:None):
|
||||
"""Export searchable CAD text for offline, code-free CSV mapping registration."""
|
||||
output=Path(output);output.mkdir(parents=True,exist_ok=True);exe=converter_path(converter)
|
||||
manifest={}
|
||||
for doc,p in paths.items():
|
||||
data=convert(p,output/'.cache',exe,log)
|
||||
manifest[doc]=dict(filename=Path(p).name,sha256=data['sha256'],texts=data['texts'])
|
||||
with (output/f'{doc}_문자목록.tsv').open('w',encoding='utf-8-sig',newline='') as f:
|
||||
w=csv.writer(f,delimiter='\t');w.writerow(['handle(10진수)','도면X','도면Y','문자'])
|
||||
for e in data['texts']:w.writerow([e['h'],e['x'],e['y'],e['t']])
|
||||
write_json(output/'문자색인.json',manifest)
|
||||
return output/'문자색인.json'
|
||||
@@ -0,0 +1,122 @@
|
||||
"""Offline 2-D block expansion and bounded line connectivity for CAD annotations."""
|
||||
import math
|
||||
import bisect
|
||||
|
||||
class LineIndex:
|
||||
def __init__(self,segments):
|
||||
self.segments=segments or []
|
||||
self.horizontal=sorted((s['a'][1],i) for i,s in enumerate(self.segments) if abs(s['a'][1]-s['b'][1])<1e-7)
|
||||
self.ys=[v[0] for v in self.horizontal]
|
||||
lengths=sorted(math.dist(s['a'],s['b']) for s in self.segments)
|
||||
self.size=max(lengths[len(lengths)//2]*10,1e-6) if lengths else 1
|
||||
self.grid={};self.long=[]
|
||||
for i,s in enumerate(self.segments):
|
||||
x1,x2=sorted(math.floor(p[0]/self.size) for p in [s['a'],s['b']]);y1,y2=sorted(math.floor(p[1]/self.size) for p in [s['a'],s['b']])
|
||||
if (x2-x1+1)*(y2-y1+1)>100:self.long.append(i);continue
|
||||
for x in range(x1,x2+1):
|
||||
for y in range(y1,y2+1):self.grid.setdefault((x,y),[]).append(i)
|
||||
def near(self,x,y,r):
|
||||
x1,x2=math.floor((x-r)/self.size),math.floor((x+r)/self.size);y1,y2=math.floor((y-r)/self.size),math.floor((y+r)/self.size)
|
||||
if (x2-x1+1)*(y2-y1+1)>10000:return self.segments
|
||||
ids=set(self.long)
|
||||
for ix in range(x1,x2+1):
|
||||
for iy in range(y1,y2+1):ids.update(self.grid.get((ix,iy),[]))
|
||||
return [self.segments[i] for i in ids]
|
||||
def horizontal_near(self,y,r):
|
||||
return [self.segments[i] for _,i in self.horizontal[bisect.bisect_left(self.ys,y-r):bisect.bisect_right(self.ys,y+r)]]
|
||||
|
||||
IDENTITY=(1,0,0,1,0,0)
|
||||
def transform(m,p):
|
||||
a,b,c,d,x,y=m
|
||||
return [a*p[0]+c*p[1]+x,b*p[0]+d*p[1]+y]
|
||||
def compose(a,b):
|
||||
p=transform(a,[b[4],b[5]])
|
||||
return (a[0]*b[0]+a[2]*b[1],a[1]*b[0]+a[3]*b[1],a[0]*b[2]+a[2]*b[3],a[1]*b[2]+a[3]*b[3],*p)
|
||||
def handle(obj):return obj.get('handle',[0])[-1]
|
||||
def reference(value):return value[-1] if isinstance(value,list) and value else None
|
||||
|
||||
def scene(raw):
|
||||
objects=raw.get('OBJECTS',[]);lookup={handle(e):e for e in objects}
|
||||
headers={handle(e):e for e in objects if e.get('object')=='BLOCK_HEADER'}
|
||||
child_handles={reference(ref) for h in headers.values() if not h.get('name','').upper().startswith(('*MODEL_SPACE','*PAPER_SPACE')) for ref in h.get('entities',[])}
|
||||
roots=[h for h in headers.values() if h.get('name','').upper().startswith(('*MODEL_SPACE','*PAPER_SPACE'))]
|
||||
texts=[];segments=[];warnings=[]
|
||||
def visit(e,m,chain):
|
||||
kind=e.get('entity');h=handle(e)
|
||||
if e.get('invisible'):return
|
||||
if kind=='INSERT':
|
||||
bh=reference(e.get('block_header'));block=headers.get(bh)
|
||||
if not block or bh in chain or len(chain)>=16:
|
||||
warnings.append(f'블록 전개 불가: {h:X}');return
|
||||
if e.get('extrusion',[0,0,1])!=[0,0,1]:
|
||||
warnings.append(f'비평면 블록 미지원: {h:X}');return
|
||||
angle=e.get('rotation',0);scale=e.get('scale',[1,1,1]);p=e.get('ins_pt',[0,0]);base=block.get('base_pt',[0,0])
|
||||
c,s=math.cos(angle),math.sin(angle)
|
||||
local=(c*scale[0],s*scale[0],-s*scale[1],c*scale[1],0,0)
|
||||
offset=transform(local,base);local=(*local[:4],p[0]-offset[0],p[1]-offset[1])
|
||||
for ref in block.get('entities',[]):
|
||||
child=lookup.get(reference(ref))
|
||||
if child:visit(child,compose(m,local),(*chain,bh))
|
||||
return
|
||||
if kind in ('TEXT','MTEXT','ATTRIB','ATTDEF','DIMENSION_LINEAR','DIMENSION_ALIGNED'):
|
||||
if kind=='ATTDEF':return
|
||||
t=e.get('text_value',e.get('text',''));p=e.get('ins_pt',e.get('text_midpt'))
|
||||
if isinstance(t,str) and t.strip() and p:
|
||||
p=transform(m,p)
|
||||
if all(math.isfinite(v) for v in p):texts.append(dict(h=h,x=p[0],y=p[1],t=t,height=abs(e.get('height',e.get('text_height',0)))*math.hypot(m[0],m[1]),block_path=list(chain)))
|
||||
pairs=[]
|
||||
if kind=='LINE':pairs=[(e.get('start'),e.get('end'))]
|
||||
elif kind in ('LWPOLYLINE','LEADER'):
|
||||
points=e.get('points',[]);pairs=list(zip(points,points[1:]))
|
||||
if kind=='LWPOLYLINE' and e.get('flag',0)&1 and len(points)>2:pairs.append((points[-1],points[0]))
|
||||
for a,b in pairs:
|
||||
if a is None or b is None:continue
|
||||
a,b=transform(m,a),transform(m,b)
|
||||
if all(math.isfinite(v) for v in a+b) and a!=b:segments.append(dict(h=h,a=a,b=b,layer=reference(e.get('layer')),block_path=list(chain)))
|
||||
if roots and all('entities' in h for h in roots):
|
||||
ids={reference(ref) for root in roots for ref in root['entities']}
|
||||
root_handles={handle(root) for root in roots}
|
||||
# Older DWG versions encode space membership via entmode/owner, not entity arrays.
|
||||
ids.update(handle(e) for e in objects if e.get('entity') and (e.get('entmode') in (1,2) or reference(e.get('ownerhandle')) in root_handles) and handle(e) not in child_handles)
|
||||
# ATTRIB coordinates are stored in drawing space by LibreDWG.
|
||||
ids.update(handle(e) for e in objects if e.get('entity')=='ATTRIB' and handle(e) not in child_handles)
|
||||
for h in ids:
|
||||
if h in lookup:visit(lookup[h],IDENTITY,())
|
||||
else:
|
||||
for e in objects:
|
||||
if handle(e) not in child_handles:visit(e,IDENTITY,())
|
||||
return texts,segments,list(dict.fromkeys(warnings))
|
||||
|
||||
def leader_endpoint(text,segments):
|
||||
"""Follow a short horizontal landing plus connected leader, never a dense grid."""
|
||||
height=text.get('height',0)
|
||||
if height<=0:return None
|
||||
x,y=text['x'],text['y'];tol=height*.15
|
||||
landing=[]
|
||||
for seg in segments:
|
||||
a,b=seg['a'],seg['b']
|
||||
if abs(a[1]-b[1])<tol and min(a[0],b[0])-height<x<max(a[0],b[0])+height and abs(a[1]-y)<height*1.8 and height<math.dist(a,b)<height*35:landing.append(seg)
|
||||
if len(landing)!=1:return None
|
||||
start=landing[0];tips=[]
|
||||
for endpoint in [start['a'],start['b']]:
|
||||
current=endpoint;used={id(start)};path=[start]
|
||||
for _ in range(4):
|
||||
nexts=[s for s in segments if id(s) not in used and min(math.dist(current,s['a']),math.dist(current,s['b']))<tol and math.dist(s['a'],s['b'])<height*60]
|
||||
if not nexts:
|
||||
if len(path)>1:tips.append((current,path))
|
||||
break
|
||||
if len(nexts)!=1:break
|
||||
seg=nexts[0];used.add(id(seg));path=path+[seg]
|
||||
current=seg['b'] if math.dist(current,seg['a'])<tol else seg['a']
|
||||
return tips[0] if len(tips)==1 else None
|
||||
|
||||
def table_row_band(point,segments,span):
|
||||
"""Use real horizontal cell borders; reject oversized/ambiguous enclosures."""
|
||||
ys=[]
|
||||
for s in (segments.horizontal_near(point['y'],span) if isinstance(segments,LineIndex) else segments or []):
|
||||
a,b=s['a'],s['b']
|
||||
if abs(a[1]-b[1])<span*1e-5 and min(a[0],b[0])<point['x']<max(a[0],b[0]) and abs(a[1]-point['y'])<span:ys.append(a[1])
|
||||
lower=[y for y in ys if y<point['y']];upper=[y for y in ys if y>point['y']]
|
||||
if not lower or not upper:return None
|
||||
lo,hi=max(lower),min(upper)
|
||||
return (lo,hi) if span*.02<hi-lo<span else None
|
||||
@@ -0,0 +1,165 @@
|
||||
"""Conservative drawing-only associations; no workbook or sample-name dependencies."""
|
||||
import math
|
||||
import re
|
||||
from .semantic import norm, distance, pile_minimum_m, angle_text
|
||||
from .geometry import leader_endpoint,LineIndex
|
||||
|
||||
def coordinate_points(texts):
|
||||
points=[]
|
||||
for e in texts:
|
||||
x=re.search(r'X\s*[=:]\s*([-+]?\d+(?:\.\d+)?)',e['t'],re.I)
|
||||
if not x:continue
|
||||
y=re.search(r'Y\s*[=:]\s*([-+]?\d+(?:\.\d+)?)',e['t'],re.I);ye=e
|
||||
if not y:
|
||||
candidates=[a for a in texts if a['y']<e['y'] and abs(a['x']-e['x'])<max(.01,(e['y']-a['y'])*.05) and re.search(r'Y\s*[=:]',a['t'],re.I)]
|
||||
if candidates:
|
||||
ye=min(candidates,key=lambda a:distance(a,e));y=re.search(r'Y\s*[=:]\s*([-+]?\d+(?:\.\d+)?)',ye['t'],re.I)
|
||||
if y:points.append(dict(x=float(x[1]),y=float(y[1]),cad=e,evidence=[e,ye]))
|
||||
return points
|
||||
|
||||
def associate_ips(points,bearing_rows):
|
||||
"""Require exactly three unique points near an unambiguous bearing footprint."""
|
||||
footprints={}
|
||||
for p in {r['p'] for r in bearing_rows}:
|
||||
rows=[r for r in bearing_rows if r['p']==p]
|
||||
xy={(r['x'],r['y']) for r in rows}
|
||||
# Conflicting coordinates for one bearing indicate overlapping carriageways.
|
||||
if any(len({(r['x'],r['y']) for r in rows if r['no']==no})>1 for no in {r['no'] for r in rows}):continue
|
||||
if len(xy)<2:continue
|
||||
center=tuple(sum(v[k] for v in xy)/len(xy) for k in [0,1])
|
||||
radius=max(math.dist(center,v) for v in xy)
|
||||
# IP callouts often sit outside the outermost bearing, near cap edges.
|
||||
# Competing-pier and exact-three checks below still reject ambiguous groups.
|
||||
footprints[p]=(center,max(radius*2.5,2.0))
|
||||
grouped={p:{} for p in footprints}
|
||||
for point in points:
|
||||
xy=(point['x'],point['y'])
|
||||
candidates=sorted((math.dist(xy,c),p,limit) for p,(c,limit) in footprints.items())
|
||||
if not candidates:continue
|
||||
d,p,limit=candidates[0]
|
||||
if d>limit or len(candidates)>1 and candidates[1][0]<max(d*1.5,d+1):continue
|
||||
grouped[p].setdefault(xy,point)
|
||||
return {p:sorted(ps.values(),key=lambda a:(-a['cad']['y'],a['cad']['x'])) for p,ps in grouped.items() if len(ps)==3}
|
||||
|
||||
def profile_fields(texts,segments=None):
|
||||
"""Local explicit EL/GL and pile labels only. Unlabelled terrain tables stay unresolved."""
|
||||
labels=[e for e in texts if re.fullmatch(r'P\d+',norm(e['t']))]
|
||||
result=[];connections={};line_index=segments if isinstance(segments,LineIndex) else LineIndex(segments)
|
||||
for label in labels:
|
||||
peers=[e for e in labels if norm(e['t'])!=norm(label['t']) and abs(e['y']-label['y'])<abs(e['x']-label['x'])*.25]
|
||||
if not peers:continue
|
||||
span=min(abs(e['x']-label['x']) for e in peers)
|
||||
if span<=0:continue
|
||||
if any(norm(e['t'])!=norm(label['t']) and abs(e['x']-label['x'])<span*.03 and 0<abs(e['y']-label['y'])<span*.25 for e in labels):continue
|
||||
local=[]
|
||||
for e in texts:
|
||||
pos=(e['x'],e['y']);connection=None
|
||||
if segments and re.match(r'(?:E\.?L\.?|G\.?L\.?)',norm(e['t'])):
|
||||
key=(e['h'],e['x'],e['y'])
|
||||
if key not in connections:
|
||||
reach=max(e.get('height',0)*100,span)
|
||||
nearby=line_index.near(e['x'],e['y'],reach)
|
||||
connections[key]=leader_endpoint(e,nearby)
|
||||
connection=connections[key]
|
||||
if connection:pos=connection[0]
|
||||
if abs(pos[0]-label['x'])<span*.42 and abs(pos[1]-label['y'])<span*1.2:
|
||||
if connection:e=dict(e,leader_tip=pos,leader_handles=[s['h'] for s in connection[1]])
|
||||
local.append(e)
|
||||
def record(col,value,ev):result.append(dict(p=norm(label['t']),col=col,value=value,inferred=True,evidence=[label,*ev]))
|
||||
els=[]
|
||||
for e in local:
|
||||
m=re.fullmatch(r'(?:E\.?L\.?)[=:]*([-+]?\d+(?:\.\d+)?)(?:\([좌우]\))?',norm(e['t']))
|
||||
if m and abs(e['y']-label['y'])>span*.05:els.append((float(m[1]),e))
|
||||
gl=re.fullmatch(r'(?:G\.?L\.?|지반고(?:EL)?)[=:]*([-+]?\d+(?:\.\d+)?)',norm(e['t']))
|
||||
if gl:record('F',float(gl[1]),[e])
|
||||
ground_heads=[e for e in texts if norm(e['t']) in ['지반고','지반고EL','GROUNDLEVEL'] and e['y']<label['y'] and 0<label['y']-e['y']<span*3 and e['x']<label['x']]
|
||||
if ground_heads:
|
||||
head=min(ground_heads,key=lambda e:abs(e['y']-label['y']))
|
||||
band=max(head.get('height',0)*2,span*.06)
|
||||
row_values=[e for e in texts if abs(e['y']-head['y'])<band and head['x']<e['x'] and re.fullmatch(r'[-+]?\d+\.\d+',e['t'].strip())]
|
||||
# Regular station samples are not the pier station. Prefer inserted pier ordinates.
|
||||
xs=sorted(set(e['x'] for e in row_values));pitches={}
|
||||
for a,b in zip(xs,xs[1:]):
|
||||
step=b-a
|
||||
if span*.15<step<span:
|
||||
key=round(step/(span*.002));pitches[key]=pitches.get(key,0)+1
|
||||
if pitches:
|
||||
pitch=max(pitches,key=pitches.get)*span*.002
|
||||
phases=[(sum(abs((x-a)/pitch-round((x-a)/pitch))<.025 for x in xs),a) for a in xs]
|
||||
count,origin=max(phases,default=(0,0))
|
||||
if count>=4:row_values=[e for e in row_values if abs((e['x']-origin)/pitch-round((e['x']-origin)/pitch))>=.025]
|
||||
ground=[e for e in row_values if abs(e['x']-label['x'])<span*.15]
|
||||
if ground:
|
||||
nearest=min(ground,key=lambda e:abs(e['x']-label['x']))
|
||||
equally_close=[e for e in ground if abs(abs(e['x']-label['x'])-abs(nearest['x']-label['x']))<span*.001]
|
||||
for e in equally_close:record('F',float(e['t']),[head,e])
|
||||
# Multiple EL leader levels cannot safely be identified as foundation bottom.
|
||||
# The lowest EL leader group in a pier's profile column belongs to its base.
|
||||
# Retain paired left/right labels as separate candidates rather than choosing a value.
|
||||
if els:
|
||||
bottom=min(e.get('leader_tip',[e['x'],e['y']])[1] for _,e in els)
|
||||
els=[(v,e) for v,e in els if e.get('leader_tip',[e['x'],e['y']])[1]-bottom<span*.10]
|
||||
local_foundation=bool(els)
|
||||
if local_foundation:
|
||||
for value,e in els:record('E',value,[e])
|
||||
# A plan-view P label must not steal an abutment or another section's pile label.
|
||||
foundation_texts=[e for e in local if local_foundation and min(distance(e,a) for _,a in els)<span*.65]
|
||||
piles=[e for e in foundation_texts if '말뚝' in e['t'] and len(e['t'])<60]
|
||||
direct=[e for e in foundation_texts if any(t in norm(e['t']) for t in ['직접기초','확대기초'])]
|
||||
if piles and not direct:
|
||||
record('G','현장타설말뚝' if any('현장타설' in e['t'] for e in piles) else '말뚝기초',piles)
|
||||
lengths=[]
|
||||
for e in local:
|
||||
m=re.search(r'(?<![A-Z.])L\s*=\s*(\d+(?:\.\d+)?(?:\s*[~~]\s*\d+(?:\.\d+)?)?)\s*(MM|M)\b',e['t'].replace('\\P',' '),re.I)
|
||||
if m and min(distance(e,a) for a in piles)<span*.35:
|
||||
lengths.append((pile_minimum_m(m[1]+m[2]),e))
|
||||
if lengths:record('H',min(v for v,e in lengths),[e for v,e in lengths])
|
||||
elif direct and not piles:
|
||||
record('G','직접기초',direct);record('H',0,direct)
|
||||
types=[]
|
||||
for e in local:
|
||||
t=norm(e['t'])
|
||||
if re.search(r'(?:기둥(?:개수|수)?[:=]?2|2주식|Π형)',t):types.append(('π형',e))
|
||||
elif re.search(r'(?:기둥(?:개수|수)?[:=]?1|1주식|T형)',t):types.append(('T형',e))
|
||||
for value,e in types:record('C',value,[e])
|
||||
return result
|
||||
|
||||
|
||||
def scalar(text):
|
||||
t=re.sub(r'\s+','',text)
|
||||
m=re.fullmatch(r'(?:E\.?L\.?[=:]*)?([-+]?\d+(?:\.\d+)?)(?:\([좌우]\))?',t,re.I)
|
||||
return float(m[1]) if m else None
|
||||
|
||||
def integrated_bearings(ts,erows):
|
||||
out=[]
|
||||
for row in erows:
|
||||
no=next(e for e in row['evidence'] if re.fullmatch(r'\d+',e['t']) and int(e['t'])==row['no'])
|
||||
xheads=[e for e in ts if norm(e['t'])=='X좌표' and e['y']>no['y'] and e['x']>no['x']]
|
||||
if not xheads:continue
|
||||
xh=min(xheads,key=lambda e:distance(e,no))
|
||||
yheads=[e for e in ts if norm(e['t'])=='Y좌표' and abs(e['y']-xh['y'])<max(abs(e['x']-xh['x'])*.001,1e-6)]
|
||||
if not yheads:continue
|
||||
yh=min(yheads,key=lambda e:distance(e,xh));span=abs(yh['x']-xh['x'])
|
||||
if not span or abs(no['x']-xh['x'])>span*4 or xh['y']-no['y']>span*12:continue
|
||||
same=[e for e in ts if abs(e['y']-no['y'])<span*.001]
|
||||
vals={};ev=list(row['evidence'])
|
||||
for field,title in [('x','X좌표'),('y','Y좌표'),('angle','일반각도계')]:
|
||||
heads=[e for e in ts if norm(e['t'])==title and abs(e['y']-xh['y'])<span*.001]
|
||||
if not heads:continue
|
||||
h=min(heads,key=lambda e:distance(e,xh))
|
||||
cand=[e for e in same if abs(e['x']-h['x'])<span*.4 and (scalar(e['t']) is not None if field!='angle' else '°' in angle_text(e['t']))]
|
||||
if cand:
|
||||
e=min(cand,key=lambda e:abs(e['x']-h['x']));vals[field]=scalar(e['t']) if field!='angle' else angle_text(e['t']);ev.extend([h,e])
|
||||
sta=[e for e in same if 'STA.' in e['t'] and 0<e['x']-xh['x']<span*4]
|
||||
station=None
|
||||
if sta:
|
||||
sm=re.search(r'(\d+)K\+([\d.]+)',sta[0]['t']);station=int(sm[1])*1000+float(sm[2]) if sm else None;ev+=sta
|
||||
if 'x' in vals and 'y' in vals:out.append(dict(p=row['p'],no=row['no'],row_type=None,station=station,evidence=ev,**vals))
|
||||
for p in {r['p'] for r in out}:
|
||||
rs=[r for r in out if r['p']==p and r['station'] is not None]
|
||||
ss=sorted(set(r['station'] for r in rs))
|
||||
if len(ss)>1:
|
||||
gaps=[(b-a,(a+b)/2) for a,b in zip(ss,ss[1:])];gap,mid=max(gaps)
|
||||
if gap>.5:
|
||||
for r in rs:r['row_type']='전열' if r['station']<mid else '후열'
|
||||
return out
|
||||
+47508
File diff suppressed because it is too large.
Load diff
+65610
File diff suppressed because it is too large.
Load diff
Whitespace-only changes.
@@ -0,0 +1,285 @@
|
||||
"""Content/relative-layout readers. No fixed handles, file names or absolute positions."""
|
||||
import collections
|
||||
import math
|
||||
import re
|
||||
|
||||
NUMBER=r'[-+]?\d+(?:\.\d+)?'
|
||||
|
||||
def pile_minimum_m(value):
|
||||
"""Parse a length or range; unqualified drawing lengths are metres."""
|
||||
if isinstance(value,(int,float)):
|
||||
return float(value) if math.isfinite(value) and value>=0 else None
|
||||
t=re.sub(r'\s+','',str(value)).upper()
|
||||
t=re.sub(r'^L[=:]?','',t)
|
||||
unit=.001 if t.endswith('MM') else 1
|
||||
t=re.sub(r'MM$|M$','',t)
|
||||
if not re.fullmatch(r'\d+(?:\.\d+)?(?:[~~〜–/,-]\d+(?:\.\d+)?)*',t):return None
|
||||
return min(map(float,re.findall(r'\d+(?:\.\d+)?',t)))*unit
|
||||
def norm(t):return re.sub(r'\s+','',t).upper()
|
||||
def distance(a,b):return math.hypot(a['x']-b['x'],a['y']-b['y'])
|
||||
|
||||
def unique_entities(items,tolerance):
|
||||
out=[]
|
||||
for e in items:
|
||||
if not any(norm(a['t'])==norm(e['t']) and distance(a,e)<tolerance for a in out):out.append(e)
|
||||
return out
|
||||
|
||||
def angle_text(t):return re.sub(r'%%[dD]','°',t).strip()
|
||||
def pier(text):
|
||||
m=re.search(r'(?<![A-Z])P\s*\.?\s*(\d+)',text.upper())
|
||||
return 'P'+str(int(m[1])) if m else None
|
||||
|
||||
def classify(texts):
|
||||
ts=[norm(e['t']) for e in texts];roles=[]
|
||||
# File paths are deliberately absent. Titles are hints; tables also identify roles.
|
||||
if (any(axis_header(t,'X') for t in ts) and any(axis_header(t,'Y') for t in ts)) or any(drawing_title(t,'bearing') for t in ts):roles.append('02')
|
||||
if any(drawing_title(t,'pier') for t in ts):roles.append('04')
|
||||
if any(t=='지반고' for t in ts) and any(t in ['계획고','측점'] for t in ts):roles.append('01')
|
||||
# Geological/structural sheets also contain local "종단면도 (A-A)" views.
|
||||
# Such section titles alone do not identify the bridge longitudinal schedule.
|
||||
if any(drawing_title(t,'profile') and ('종평면도' in t or any(v in ts for v in ['측점','계획고','STATION'])) for t in ts) and '01' not in roles:roles.append('01')
|
||||
if any(t in ['교각명','교각번호'] for t in ts):
|
||||
if any('두부보' in t or '코핑' in t for t in ts) and '04' not in roles:roles.append('04')
|
||||
if any('기초하단' in t or '말뚝길이' in t for t in ts) and '01' not in roles:roles.append('01')
|
||||
if any(drawing_title(t,'elevation') for t in ts) or ('ELEVATION' in ts and 'STATION' in ts) or ('교좌면EL' in ts and any('몰탈상단' in t for t in ts)):roles.append('03')
|
||||
# An EL-X table with a pier identifier is stronger evidence than an exact title.
|
||||
# Calculation sheets remain ancillary and cannot supply authoritative bearing Z.
|
||||
if '04' not in roles and '03' not in roles and any(elx_header(t) for t in ts) and any(pier(t) for t in ts) and any(t in ['NO.','NO','번호'] for t in ts):roles.append('04')
|
||||
return roles
|
||||
|
||||
def drawing_title(text,kind):
|
||||
t=norm(text)
|
||||
if len(t)>90 or any(w in t for w in ['참조','참고','따른다','SEE']):return False
|
||||
patterns={
|
||||
'pier':r'교각(?:일반도|구조일반도|구조도|도)(?:\([^)]*\)|[-_\d])*$',
|
||||
'bearing':r'(?:교량)?받침(?:배치도|배치계획도)(?:\([^)]*\)|[-_\d])*$',
|
||||
'profile':r'(?:종평면도|종단면도|종단도)(?:\([^)]*\)|[-_\d])*$',
|
||||
'elevation':r'(?:ELEVATION|표고)(?:산출|계산)(?:근거)?(?:도|표)(?:\([^)]*\)|[-_\d])*$',
|
||||
}
|
||||
return bool(re.search(patterns[kind],t))
|
||||
|
||||
def axis_header(text,axis):
|
||||
t=norm(text).replace('_','')
|
||||
return t in {axis+'좌표',axis+'(M)',axis+'COORDINATE',axis+'좌표(M)',axis+'좌표M'}
|
||||
|
||||
def elx_header(text):
|
||||
return re.sub(r'''[.′’‘'"“”]''','',norm(text))=='ELX'
|
||||
|
||||
ALIASES={
|
||||
'C':{'교각형식','교각형태'},'D':{'두부보EL','두부보상단EL','코핑EL'},
|
||||
'E':{'기초하단EL','기초저면EL'},'F':{'지반고','지반고EL'},
|
||||
'G':{'기초형식'},'H':{'말뚝길이'},'I':{'두부보폭','코핑폭'},
|
||||
'J':{'두부보길이','코핑길이'},'K':{'기둥갯수','기둥개수','기둥수'},
|
||||
}
|
||||
|
||||
def header_field(t):
|
||||
t=re.sub(r'[_\s]|\((?:MM|M)\)','',norm(t))
|
||||
t=re.sub(r'(?:MM|M)$','',t)
|
||||
return next((col for col,labels in ALIASES.items() if t in labels),None)
|
||||
|
||||
def pier_tables(texts,role):
|
||||
"""Read explicitly labeled pier schedules of previously unseen layouts."""
|
||||
result=[];allowed='CDIJK' if role=='04' else 'EFGH'
|
||||
for ph in [e for e in texts if norm(e['t']) in ['교각명','교각번호']]:
|
||||
candidates=[e for e in texts if header_field(e['t']) in set(allowed) and e['x']!=ph['x'] and abs(e['y']-ph['y'])<abs(e['x']-ph['x'])*.001]
|
||||
if not candidates:continue
|
||||
span=min(abs(e['x']-ph['x']) for e in candidates)
|
||||
ids=sorted([e for e in texts if re.fullmatch(r'P\d+',norm(e['t'])) and abs(e['x']-ph['x'])<span*.4 and e['y']<ph['y']],key=lambda e:-e['y'])
|
||||
if not ids:continue
|
||||
pitch=ph['y']-ids[0]['y']
|
||||
if pitch>span*2:continue
|
||||
for i,label in enumerate(ids):
|
||||
if i and ids[i-1]['y']-label['y']>pitch*1.8:break
|
||||
for h in candidates:
|
||||
es=[e for e in texts if abs(e['y']-label['y'])<pitch*.03 and abs(e['x']-h['x'])<span*.4]
|
||||
if len(es)!=1:continue
|
||||
e=es[0];col=header_field(h['t']);value=e['t'].strip()
|
||||
if col not in 'CG':
|
||||
if col=='H' and pile_minimum_m(value) is not None:
|
||||
value=pile_minimum_m(value)
|
||||
if 'MM' in norm(h['t']) and not re.search(r'M\s*$',e['t'],re.I):value/=1000
|
||||
elif re.fullmatch(r'(?:EL[.= ]*)?'+NUMBER,value,re.I):value=float(re.search(NUMBER+r'$',value)[0])
|
||||
else:continue
|
||||
# Dimension headers with explicit m must be converted into mm.
|
||||
if col in 'IJ' and isinstance(value,float) and re.search(r'(?:_M|\(M\))$',norm(h['t'])):value*=1000
|
||||
result.append(dict(p=pier(label['t']),col=col,value=value,evidence=[ph,label,h,e]))
|
||||
return result
|
||||
|
||||
class Index:
|
||||
def __init__(self,texts):
|
||||
self.texts=texts;self.by_text=collections.defaultdict(list);self.cache={}
|
||||
for e in texts:self.by_text[norm(e['t'])].append(e)
|
||||
|
||||
def locate(self,ref):
|
||||
"""Fit scale+translation from local text landmarks, then validate other landmarks."""
|
||||
key=(ref['x'],ref['y'],ref['pattern'])
|
||||
if key in self.cache:return self.cache[key]
|
||||
anchors=[a for a in ref.get('anchors',[]) if self.by_text.get(a['t'])]
|
||||
matches={}
|
||||
pattern=re.compile(ref['pattern'])
|
||||
if len(anchors)<3:return None
|
||||
# Distinctive pier title is the first anchor when available. Do not substitute
|
||||
# another pier's equally shaped table if the identifying title vanished.
|
||||
original=ref.get('anchors',[])
|
||||
if original and pier(original[0]['t']) and original[0] not in anchors:return None
|
||||
first=anchors[0]
|
||||
for second in anchors[1:5]:
|
||||
dx,dy=second['x']-first['x'],second['y']-first['y'];den=dx*dx+dy*dy
|
||||
if den<1e-12:continue
|
||||
span=math.sqrt(den)
|
||||
for a in self.by_text[first['t']]:
|
||||
for b in self.by_text[second['t']]:
|
||||
ux,uy=b['x']-a['x'],b['y']-a['y'];scale=(ux*dx+uy*dy)/den
|
||||
if not .001<=scale<=1000:continue
|
||||
tol=max(span*scale*.00002,1e-5)
|
||||
if math.hypot(ux-scale*dx,uy-scale*dy)>tol:continue
|
||||
tx,ty=a['x']-scale*first['x'],a['y']-scale*first['y']
|
||||
confirmed=0
|
||||
for anchor in anchors:
|
||||
x,y=tx+scale*anchor['x'],ty+scale*anchor['y']
|
||||
if any(math.hypot(e['x']-x,e['y']-y)<tol for e in self.by_text[anchor['t']]):confirmed+=1
|
||||
if confirmed<3:continue
|
||||
x,y=tx+scale*ref['x'],ty+scale*ref['y']
|
||||
for e in self.texts:
|
||||
if abs(e['x']-x)<tol and abs(e['y']-y)<tol and pattern.fullmatch(e['t']):
|
||||
matches[(e['h'],e['x'],e['y'])]=(e,confirmed)
|
||||
# Prefer the transformation supported by the most independent headings.
|
||||
if matches:
|
||||
top=max(score for _,score in matches.values())
|
||||
best=[e for e,score in matches.values() if score==top]
|
||||
value=best[0] if len(best)==1 else None
|
||||
else:value=None
|
||||
self.cache[key]=value
|
||||
return value
|
||||
|
||||
def table_rows(texts,header,xspan,left=None):
|
||||
"""Numeric column with row pitch estimated from its own first two values."""
|
||||
xs=[e for e in texts if abs(e['x']-header['x'])<xspan*.45 and e['y']<header['y'] and re.fullmatch(NUMBER,e['t'])]
|
||||
xs.sort(key=lambda e:-e['y'])
|
||||
xs=unique_entities(xs,xspan*.001)
|
||||
if not xs:return []
|
||||
first=header['y']-xs[0]['y']
|
||||
if first<=0 or first>xspan:return []
|
||||
pitch=xs[0]['y']-xs[1]['y'] if len(xs)>1 else first
|
||||
if pitch<=0 or not .4*first<pitch<2.5*first:pitch=first
|
||||
result=[]
|
||||
for e in xs:
|
||||
if len(result)>=100:break
|
||||
if result and abs((result[-1]['y']-e['y'])-pitch)>pitch*.12:break
|
||||
# A new header or table title between rows terminates the current table.
|
||||
if any(norm(t['t'])==norm(header['t']) and abs(t['x']-header['x'])<xspan*.3 and e['y']<=t['y']<header['y'] for t in texts):break
|
||||
result.append(e)
|
||||
return result
|
||||
|
||||
def bearings(texts,segments=None):
|
||||
"""Read columns from headers even if their order/positions/row pitch change."""
|
||||
out=[];issues=[]
|
||||
titles=[e for e in texts if re.fullmatch(r'P\d+\([전후](?:열)?(?:,?RAMP-A교)?\)',norm(e['t']))]
|
||||
for xh in [e for e in texts if axis_header(e['t'],'X')]:
|
||||
# Y header nearest on the same row; position can be left or right of X.
|
||||
ys=[e for e in texts if axis_header(e['t'],'Y') and abs(e['y']-xh['y'])<max(abs(e['x']-xh['x'])*.0001,1e-5)]
|
||||
if not ys:continue
|
||||
yh=min(ys,key=lambda e:abs(e['x']-xh['x']));span=abs(yh['x']-xh['x'])
|
||||
if not span:continue
|
||||
ts=[t for t in titles if 0<t['y']-xh['y']<span and abs(t['x']-xh['x'])<span*2]
|
||||
if not ts:continue
|
||||
title=min(ts,key=lambda t:distance(t,xh));p=pier(title['t'])
|
||||
headings=[e for e in texts if abs(e['y']-xh['y'])<span*.001 and abs(e['x']-xh['x'])<span*3.5]
|
||||
ah=[e for e in headings if norm(e['t']) in ['일반각도계','방위각','AZIMUTH']]
|
||||
nh=[e for e in headings if norm(e['t']) in ['구분','NO.','NO','번호']]
|
||||
if not nh:continue
|
||||
noh=min(nh,key=lambda e:abs(e['x']-xh['x']))
|
||||
next_numbers=[e['x'] for e in texts if norm(e['t']) in ['구분','NO.','NO','번호'] and abs(e['y']-xh['y'])<span*.001 and e['x']>noh['x']+span*.1]
|
||||
right=min(next_numbers) if next_numbers else float('inf')
|
||||
ah=[e for e in ah if noh['x']<e['x']<right]
|
||||
angleh=min(ah,key=lambda e:abs(e['x']-xh['x'])) if ah else None
|
||||
for x in table_rows(texts,xh,span):
|
||||
from .geometry import table_row_band
|
||||
band=table_row_band(x,segments,span)
|
||||
same=[e for e in texts if (band[0]<e['y']<band[1] if band else abs(e['y']-x['y'])<span*.001)]
|
||||
yy=[e for e in same if abs(e['x']-yh['x'])<span*.45 and re.fullmatch(NUMBER,e['t'])]
|
||||
aa=[e for e in same if angleh and abs(e['x']-angleh['x'])<span*.45 and '°' in angle_text(e['t'])]
|
||||
nn=[e for e in same if abs(e['x']-noh['x'])<span*.25 and re.fullmatch(r'\d+',e['t'])]
|
||||
yy=unique_entities(yy,span*.001);aa=unique_entities(aa,span*.001);nn=unique_entities(nn,span*.001)
|
||||
if len(yy)==len(nn)==1 and len(aa)<=1:
|
||||
row_type='전열' if '(전' in norm(title['t']) else '후열'
|
||||
out.append(dict(p=p,no=int(nn[0]['t']),x=float(x['t']),y=float(yy[0]['t']),angle=angle_text(aa[0]['t']) if aa else None,row_type=row_type,evidence=[title,xh,yh,nn[0],x,yy[0]]+([angleh,*aa] if angleh else [])))
|
||||
else:issues.append(f'{p}: 좌표표 행 대응 모호함')
|
||||
return out,issues
|
||||
|
||||
def elevations(texts):
|
||||
out=[]
|
||||
titles=[e for e in texts if pier(e['t']) and (any(t in norm(e['t']) for t in ['교각일반도','교각구조도','교각도','교각구조일반도']) or re.fullmatch(r'\(.*방향P\d+\)',norm(e['t'])) or re.fullmatch(r'P\d+(?:\([^)]*\))?',norm(e['t'])))]
|
||||
for h in [e for e in texts if elx_header(e['t'])]:
|
||||
numbers=[e for e in texts if norm(e['t']) in ['NO.','NO','번호'] and e['x']<h['x'] and abs(e['y']-h['y'])<abs(e['x']-h['x'])*.001]
|
||||
if not numbers or not titles:continue
|
||||
nh=min(numbers,key=lambda e:abs(e['x']-h['x']));span=h['x']-nh['x']
|
||||
nearest=sorted([t for t in titles if t['y']>h['y']],key=lambda e:distance(e,h))
|
||||
if not nearest:continue
|
||||
title=nearest[0];p=pier(title['t'])
|
||||
# Do not match a tiny dimension-callout EL.X to a remote table.
|
||||
if distance(title,h)>span*15:continue
|
||||
for z in table_rows(texts,h,span*.45):
|
||||
nn=[e for e in texts if abs(e['y']-z['y'])<span*.001 and abs(e['x']-nh['x'])<span*.06 and re.fullmatch(r'\d+',e['t'])]
|
||||
if len(nn)==1:out.append(dict(p=p,no=int(nn[0]['t']),z=float(z['t']),evidence=[title,h,nh,nn[0],z]))
|
||||
return out
|
||||
|
||||
def drawing_direction(text):
|
||||
m=re.match(r'^\(?([가-힣]+)방향(?:[,)]|BR|교량받침|$)',norm(text))
|
||||
return m[1] if m and m[1] not in ['교축','교축직각','횡','종','양','직각'] else None
|
||||
|
||||
def calculation_elevations(texts,segments=None):
|
||||
"""Read mortar-top and seat columns, grouping merged pier cells by NO resets.
|
||||
|
||||
All matching tables/revisions are retained; callers resolve conflicts explicitly.
|
||||
No baseline-line elevation or general drawing EL-X is used.
|
||||
"""
|
||||
out=[];issues=[]
|
||||
for zh in [e for e in texts if norm(e['t']) in ['교량받침EL','교량받침EL(몰탈상단)']]:
|
||||
seats=[e for e in texts if norm(e['t'])=='교좌면EL' and e['x']!=zh['x'] and abs(e['y']-zh['y'])<abs(e['x']-zh['x'])*.15]
|
||||
if not seats:continue
|
||||
sh=min(seats,key=lambda e:distance(e,zh));span=abs(zh['x']-sh['x'])
|
||||
mortar=[e for e in texts if norm(e['t'])=='(몰탈상단)' and abs(e['x']-zh['x'])<span*.5 and abs(e['y']-zh['y'])<span*.5]
|
||||
if not mortar and '몰탈상단' not in zh['t']:continue
|
||||
heads=[e for e in texts if abs(e['y']-zh['y'])<span*.15 and e['x']<min(zh['x'],sh['x'])]
|
||||
nhs=[e for e in heads if norm(e['t']) in ['교량받침','받침번호','NO.','NO']]
|
||||
nhs=[h for h in nhs if any(re.fullmatch(r'\d+',e['t']) and abs(e['x']-h['x'])<span*.45 and 0<h['y']-e['y']<span*2 for e in texts)]
|
||||
if not nhs:continue
|
||||
nh=max(nhs,key=lambda e:e['x'])
|
||||
phs=[e for e in heads if norm(e['t']) in ['구분','교각명','교각번호'] and e['x']<nh['x']]
|
||||
if not phs:continue
|
||||
ph=max(phs,key=lambda e:e['x'])
|
||||
center=dict(x=(ph['x']+zh['x'])/2,y=zh['y'])
|
||||
directions=[e for e in texts if drawing_direction(e['t']) and e['y']>zh['y'] and abs(e['x']-center['x'])<zh['x']-ph['x'] and e['y']-zh['y']<(zh['x']-ph['x'])]
|
||||
direction_e=min(directions,key=lambda e:distance(e,center)) if directions else None
|
||||
direction=drawing_direction(direction_e['t']) if direction_e else None
|
||||
bottom=max([e['y'] for e in texts if norm(e['t'])==norm(zh['t']) and abs(e['x']-zh['x'])<span*.5 and e['y']<zh['y']]+[-float('inf')])
|
||||
nums=sorted([e for e in texts if bottom<e['y']<zh['y'] and abs(e['x']-nh['x'])<span*.45 and re.fullmatch(r'\d+',e['t'])],key=lambda e:-e['y'])
|
||||
if not nums:continue
|
||||
pitch=nums[0]['y']-nums[1]['y'] if len(nums)>1 else zh['y']-nums[0]['y']
|
||||
if pitch<=0 or pitch>span*2:continue
|
||||
contiguous=[]
|
||||
for e in nums:
|
||||
if contiguous and abs(contiguous[-1]['y']-e['y']-pitch)>pitch*.15:break
|
||||
contiguous.append(e)
|
||||
groups=[]
|
||||
for e in contiguous:
|
||||
if not groups or int(e['t'])==1:groups.append([])
|
||||
groups[-1].append(e)
|
||||
for group in groups:
|
||||
labels=[e for e in texts if re.fullmatch(r'[PA]\d+',norm(e['t'])) and abs(e['x']-ph['x'])<span*.45 and group[-1]['y']-pitch*.49<=e['y']<=group[0]['y']+pitch*.49]
|
||||
if len(labels)!=1:
|
||||
issues.append('ELEVATION 표: 받침 번호 묶음과 교각명 대응 미확정');continue
|
||||
label=labels[0];p=norm(label['t'])
|
||||
if p.startswith('A'):continue
|
||||
for no in group:
|
||||
values={};evidence=[label,nh,no,zh,sh]+mortar+([direction_e] if direction_e else [])
|
||||
from .geometry import table_row_band
|
||||
band=table_row_band(no,segments,span)
|
||||
for field,h in [('z',zh),('seat',sh)]:
|
||||
matches=[e for e in texts if (band[0]<e['y']<band[1] if band else abs(e['y']-no['y'])<pitch*.03) and abs(e['x']-h['x'])<span*.45 and re.fullmatch(r'(?:EL[. =]*)?'+NUMBER,e['t'],re.I)]
|
||||
if len(matches)==1:
|
||||
values[field]=float(re.search(NUMBER+r'$',matches[0]['t'])[0]);evidence.append(matches[0])
|
||||
if values:out.append(dict(p=p,no=int(no['t']),direction=direction,evidence=evidence,**values))
|
||||
else:issues.append(f'{p} 받침 {no["t"]}: 몰탈상단/교좌면 값 대응 미확정')
|
||||
return out,issues
|
||||
@@ -0,0 +1,64 @@
|
||||
"""Standalone Python XLSX writer, explicitly requested by the user."""
|
||||
from openpyxl import Workbook, load_workbook
|
||||
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
|
||||
from openpyxl.comments import Comment
|
||||
from openpyxl.utils import get_column_letter
|
||||
from .core import HEADERS
|
||||
|
||||
def put(ws,cell,value):
|
||||
c=ws[cell];c.value=value
|
||||
if isinstance(value,str):c.data_type='s' # CAD text must never execute as an Excel formula.
|
||||
return c
|
||||
|
||||
def export_workbook(result,path):
|
||||
wb=Workbook();wb.remove(wb.active)
|
||||
font='맑은 고딕'
|
||||
for name,headers in HEADERS.items():
|
||||
s=wb.create_sheet(name);s.sheet_view.showGridLines=False;s.freeze_panes='D3'
|
||||
put(s,'B1',result['name']+' '+name+' 입력데이터').font=Font(name=font,size=14,bold=True)
|
||||
s.row_dimensions[1].height=30;s.row_dimensions[2].height=38
|
||||
for j,title in enumerate(headers,2):
|
||||
c=s.cell(2,j,title);c.font=Font(name=font,size=10,bold=True,color='FFFFFF');c.fill=PatternFill('solid',fgColor='203864');c.alignment=Alignment(horizontal='center',vertical='center',wrap_text=True)
|
||||
s.column_dimensions[get_column_letter(j)].width=16
|
||||
s.column_dimensions['B'].width=10;s.column_dimensions['C'].width=21
|
||||
if name!='Pier':
|
||||
s.column_dimensions['D'].width=21;s.column_dimensions['E'].width=21
|
||||
if name=='Bearing':s.column_dimensions['G'].width=23
|
||||
for cell,value in result['cells'][name].items():
|
||||
c=put(s,cell,value);c.font=Font(name=font,size=10,color='C00000');c.alignment=Alignment(horizontal='center',vertical='center')
|
||||
c.border=Border(*( [Side(style='thin',color='B7B7B7')]*4 ))
|
||||
s.row_dimensions[c.row].height=24
|
||||
if name=='Pier' and c.column in [4,5,6]:c.number_format='0.0000' if c.column==4 else '0.000'
|
||||
elif name!='Pier' and c.column in [4,5]:c.number_format='0.000000' if name=='IP' else '0.0000'
|
||||
elif name=='Bearing' and c.column==6:c.number_format='0.0000'
|
||||
note_col=len(headers)+3
|
||||
s.cell(2,note_col,'상태 / 추출 및 검토 근거').font=Font(name=font,bold=True,size=10)
|
||||
s.column_dimensions[get_column_letter(note_col)].width=100
|
||||
notes={}
|
||||
for r in result['records']:
|
||||
if r['sheet']!=name:continue
|
||||
c=s[r['cell']]
|
||||
if r['status'] not in ['추출','식별자','등록값']:
|
||||
c.fill=PatternFill('solid',fgColor='FFF2CC')
|
||||
body=[f"상태: {r['status']}",r['note'],*r['messages']]
|
||||
for e in r['evidence']:
|
||||
body.append(f"{result['sources'][e['doc']]['path']}\nhandle {e['h']:X}, ({e['x']:.4f}, {e['y']:.4f})\n{e['t']}")
|
||||
if r['status']!='식별자':c.comment=Comment('\n'.join(filter(None,body)),'도면추출 프로그램')
|
||||
if r['status'] not in ['추출','식별자']:
|
||||
notes.setdefault(c.row,[]).append(f"{r['cell']} {r['status']}: "+' / '.join(r['messages']))
|
||||
for row,messages in notes.items():
|
||||
c=s.cell(row,note_col,'; '.join(messages));c.font=Font(name=font,size=10);c.alignment=Alignment(wrap_text=True,vertical='top')
|
||||
s.row_dimensions[row].height=max(24,min(100,20*(len(c.value)//85+1)))
|
||||
last=max([int(c[1:]) for c in result['cells'][name]]+[2])+3
|
||||
for text in result.get('notes',[]):
|
||||
put(s,f'B{last}',text).font=Font(name=font,size=10);last+=1
|
||||
for d,src in result['sources'].items():
|
||||
put(s,f'B{last}',f"출처 {d}: {src['path']}").font=Font(name=font,size=10);last+=1
|
||||
wb.save(path)
|
||||
# Check the actual serialized file, not only the in-memory workbook.
|
||||
saved=load_workbook(path)
|
||||
for sheet,cells in result['cells'].items():
|
||||
for address,value in cells.items():
|
||||
if saved[sheet][address].value!=value:
|
||||
raise ValueError(f'엑셀 저장 대조 실패: {sheet}!{address}')
|
||||
saved.close()
|
||||
@@ -0,0 +1,80 @@
|
||||
"""Folder-only default interface."""
|
||||
import os
|
||||
from pathlib import Path
|
||||
import queue
|
||||
import threading
|
||||
import tkinter as tk
|
||||
from tkinter import ttk,filedialog,messagebox
|
||||
import sys
|
||||
PACKAGE = Path(__file__).resolve().parents[1]
|
||||
WORKSPACE = PACKAGE.parents[1]
|
||||
sys.path.insert(0, str(PACKAGE / 'src'))
|
||||
from bridge_drawing_extractor import extract_folder
|
||||
|
||||
def run_folder(folder, log):
|
||||
result = extract_folder(folder,
|
||||
converter_path=WORKSPACE / 'Code/giyang_extract/libredwg/dwgread.exe',
|
||||
on_progress=log)
|
||||
return result.output_dir, [(b.xlsx_path, b.data) for b in result.workbooks], result.files
|
||||
|
||||
|
||||
class AutoApp:
|
||||
def __init__(self,root):
|
||||
self.root=root;self.busy=False;self.events=queue.Queue();self.last_output=None
|
||||
root.title('도면 폴더 → 교각 입력 엑셀');root.geometry('960x640');root.minsize(760,520)
|
||||
self.folder=tk.StringVar();self.status=tk.StringVar(value='도면이 들어 있는 폴더를 선택하세요.')
|
||||
frame=ttk.Frame(root,padding=16);frame.pack(fill='both',expand=True);frame.columnconfigure(0,weight=1);frame.rowconfigure(5,weight=1)
|
||||
ttk.Label(frame,text='도면 폴더에서 자동 추출',font=('맑은 고딕',16,'bold')).grid(row=0,column=0,columnspan=2,sticky='w',pady=(0,12))
|
||||
ttk.Label(frame,text='종평면·받침배치·ELEVATION에서 추출합니다. 기존 엑셀 없이 IP 연결·500mm 단위 치수 산정.').grid(row=1,column=0,columnspan=2,sticky='w',pady=(0,12))
|
||||
ttk.Entry(frame,textvariable=self.folder).grid(row=2,column=0,sticky='ew',padx=(0,8))
|
||||
ttk.Button(frame,text='도면 폴더 선택',command=self.choose).grid(row=2,column=1)
|
||||
buttons=ttk.Frame(frame);buttons.grid(row=3,column=0,columnspan=2,sticky='w',pady=12)
|
||||
self.start_button=ttk.Button(buttons,text='자동 추출 시작',command=self.start);self.start_button.pack(side='left',padx=(0,8))
|
||||
ttk.Button(buttons,text='결과 폴더 열기',command=self.open_output).pack(side='left')
|
||||
self.progress=ttk.Progressbar(frame,mode='indeterminate');self.progress.grid(row=4,column=0,columnspan=2,sticky='ew',pady=(0,8))
|
||||
self.text=tk.Text(frame,state='disabled',wrap='word',font=('맑은 고딕',10));self.text.grid(row=5,column=0,columnspan=2,sticky='nsew')
|
||||
scroll=ttk.Scrollbar(frame,command=self.text.yview);scroll.grid(row=5,column=2,sticky='ns');self.text.configure(yscrollcommand=scroll.set)
|
||||
ttk.Label(frame,textvariable=self.status,wraplength=870).grid(row=6,column=0,columnspan=2,sticky='w',pady=10)
|
||||
ttk.Label(frame,text='결과: 선택 폴더의 _도면추출결과. 판독이 모호한 값은 공란·검토 사유로 표시합니다.',wraplength=870).grid(row=7,column=0,columnspan=2,sticky='w')
|
||||
root.after(100,self.poll)
|
||||
|
||||
def choose(self):
|
||||
folder=filedialog.askdirectory()
|
||||
if folder:self.folder.set(folder)
|
||||
|
||||
def append(self,text):
|
||||
self.text.configure(state='normal');self.text.insert('end',text+'\n');self.text.see('end');self.text.configure(state='disabled')
|
||||
|
||||
def start(self):
|
||||
if self.busy:return
|
||||
folder=self.folder.get()
|
||||
if not folder or not Path(folder).is_dir():messagebox.showerror('폴더 선택','유효한 도면 폴더를 선택하세요.');return
|
||||
self.busy=True;self.start_button.state(['disabled']);self.progress.start();self.status.set('도면을 읽고 있습니다…')
|
||||
def worker():
|
||||
try:self.events.put(('done',run_folder(folder,log=lambda s:self.events.put(('log',s)))))
|
||||
except Exception as e:self.events.put(('error',str(e)))
|
||||
threading.Thread(target=worker,daemon=True).start()
|
||||
|
||||
def poll(self):
|
||||
try:
|
||||
while True:
|
||||
kind,value=self.events.get_nowait()
|
||||
if kind=='log':self.append(value);continue
|
||||
self.busy=False;self.start_button.state(['!disabled']);self.progress.stop()
|
||||
if kind=='error':self.status.set('처리 실패 — 판독 메시지를 확인하세요.');self.append(value)
|
||||
else:
|
||||
folder,outputs,inventory=value;self.last_output=folder
|
||||
issues=sum(r['value'] is None and r['status']!='대상외' for _,result in outputs for r in result['records'])
|
||||
failed=sum(x['status']=='읽기 실패' for x in inventory)
|
||||
self.status.set(f'엑셀 {len(outputs)}개 저장 · 검토 {issues}셀 · 파일 읽기 실패 {failed}개')
|
||||
self.append('저장 위치: '+str(folder));self.append('CSV의 검토 사유와 파일판독내역.json을 확인하세요.')
|
||||
except queue.Empty:pass
|
||||
self.root.after(100,self.poll)
|
||||
|
||||
def open_output(self):
|
||||
if self.last_output and self.last_output.exists():os.startfile(str(self.last_output))
|
||||
|
||||
if __name__ == '__main__':
|
||||
root = tk.Tk()
|
||||
app = AutoApp(root)
|
||||
root.mainloop()
|
||||
@@ -0,0 +1,244 @@
|
||||
LibreDWG - free implementation of the DWG file format
|
||||
|
||||
LibreDWG is a free C library to read and write DWG files. This program is
|
||||
part of the GNU project, released under the aegis of GNU. It is licensed
|
||||
under the terms of the GNU General Public License version 3 (or at you option
|
||||
any later version).
|
||||
|
||||
DWG is a file format created in the 70's for the emerging CAD applications.
|
||||
Currently it is the native file format of AutoCAD, a proprietary CAD program
|
||||
developed by AutoDesk.
|
||||
|
||||
LibreDWG is a fork from LibDWG due to its usage of Esperanto, which we
|
||||
think is not the best strategy for a free software project which aims
|
||||
to get lots of contributors. LibreDWG is written in English. At the
|
||||
moment our decoder (i.e. reader) is done, it can read all DWG
|
||||
versions, just some very advanced R2010+ objects fail to read and are
|
||||
skipped over. The writer is good enough for everything but R2007.
|
||||
R2010-R2018 writing leads to CRC errors still. Among the
|
||||
example applications we wrote using LibreDWG is a reader, a writer, a
|
||||
rewriter (i.e. saveas), an initial SVG and Postscript conversion, dxf
|
||||
and json converters, dwggrep to search for text, and dwglayer to print
|
||||
the list of layers. More are in the pipeline. Please contact us if you
|
||||
want to help.
|
||||
|
||||
The strings API is UTF-8 (WTF8 really, Windows UCS-2 has no surrogate
|
||||
pairs), and encodes to the internal 8-bit or UCS-2 strings, depending
|
||||
on the DWG version. Older DWG's are encoded in ~30 codepages, which we
|
||||
convert to unicode/UTF-8.
|
||||
|
||||
We also maintain a diff document for our internal fixed ODA-5.4.1 spec
|
||||
at doc/ODA-5.4.2-libredwg-diff.pdf
|
||||
|
||||
For security contact file a ticket or email the package maintainers:
|
||||
|
||||
Reini Urban <rurban AT cpan.org>
|
||||
|
||||
The now inactive founders and previous maintainers were:
|
||||
|
||||
Felipe Corrêa da Silva Sanches <juca AT members.fsf.org>
|
||||
Rodrigo Rodrigues da Silva <pitanga AT members.fsf.org>
|
||||
Hardeep Singh Rai <hardeep.rai AT gmail DOT com>
|
||||
|
||||
If you are looking for general information about the project, check our website:
|
||||
http://www.gnu.org/software/libredwg/
|
||||
|
||||
== Building and installing the library ==
|
||||
|
||||
You need the following dependencies:
|
||||
- Basic development tools (a C99 compiler like gcc/clang, make, autoconf, automake
|
||||
and libtool)
|
||||
|
||||
Optionally:
|
||||
|
||||
- libiconv: optional codepage conversion. without it's just a bit slower.
|
||||
- libmimalloc: faster memory allocation, but usually not worth the hassle.
|
||||
- pslib for dwg2ps: http://pslib.sourceforge.net/doc/pslib.html
|
||||
- pcre2 with 8bit and 16bit libraries for dwggrep regexp support.
|
||||
- gperf to update static hashes for several API's
|
||||
Only needed when you added objects or changed the dwg.spec significantly
|
||||
- cmake, ninja
|
||||
- ODAFileConverter from https://www.opendesign.com/guestfiles/oda_file_converter
|
||||
for doing cross-checks.
|
||||
- xvfb-run from xvfb/xorg-x11-server-Xvfb for silencing ODAFileConverter popups.
|
||||
- TeXinfo for building the docs
|
||||
- SWIG 1.7+
|
||||
- Python 2.7 or 3.x development headers (debian: python-dev; yum: python-devel)
|
||||
and the libxml2 python bindings.
|
||||
- Perl 5 for the perl bindings
|
||||
- doxygen to generate the reference manual
|
||||
- jq to check json validity, and as dwgfilter backend
|
||||
- shellcheck to check shell scripts
|
||||
- rpmlint to check the spec validity
|
||||
- jinq with some svg11 relaxng to check SVG validity.
|
||||
LaTeXML used to have a broken svg11-basic.rng. If so
|
||||
the zip from http://yupotan.sppd.ne.jp/relax-ng/svg11/ is correct.
|
||||
Needs to be installed into /usr/local/share/relaxng/svg11/
|
||||
sudo unzip ~/Downloads/svg11-relaxng-20060604.zip -d /usr/local/share/relaxng/
|
||||
On Ubuntu I needed:
|
||||
apt install jing libjaxp1.3-java libbatik-java libavalon-framework-java
|
||||
- mapbox/geojsonhint as geojson linter
|
||||
npm install -g @mapbox/geojsonhint
|
||||
- geojson-validation as 2nd geojson fallback linter
|
||||
npm install -g geojson-validation
|
||||
- shfmt to format shell scripts
|
||||
- clang-format to format the C source code
|
||||
- clang-tidy to check source code
|
||||
- valgrind to find leaks and memory bugs
|
||||
- timeout to help tests with large or hanging DWG's
|
||||
- GNU parallel to speed up special tests (not the moreutils parallel!)
|
||||
- picat to find unknown fields, a better prolog. http://picat-lang.org/
|
||||
|
||||
See INSTALL for generic instructions. Basically, you do:
|
||||
|
||||
$ sh ./autogen.sh (if you checked out the source code from git)
|
||||
$ ./configure [--disable-bindings] [--disable-docs] [--disable-shared]
|
||||
$ make
|
||||
$ make check
|
||||
$ sudo make install
|
||||
|
||||
This builds and installs various files in the "installation dirs":
|
||||
$libdir ($exec_prefix/lib) -- library files (libredwg.*)
|
||||
$includedir ($prefix/include) -- dwg.h dwg_api.h
|
||||
$libdir/pkgconfig -- libredwg.pc
|
||||
$infodir ($datarootdir/info) -- LibreDWG.info
|
||||
|
||||
Use "./configure --help" to see a full list of options you can use to
|
||||
customize the installation dirs. Other options recognized by configure are:
|
||||
|
||||
--enable-release
|
||||
|
||||
Recommended for packagers and fuzzers. Disables all features
|
||||
which will not be enabled in official tarball releases. Like skipping
|
||||
unstable DWG features or objects, unknown DWG versions and objects.
|
||||
|
||||
--enable-trace
|
||||
|
||||
Enable runtime tracing via LIBREDWG_TRACE (default: no). When
|
||||
enabled, the environment variable LIBREDWG_TRACE is consulted on
|
||||
the first decode/encode attempt. Its value is an integer: 0 (no
|
||||
output) through 9 (full verbosity). Most tools do support a
|
||||
--verbosity|-v flag instead.
|
||||
|
||||
--with-dxf-precision=rfc
|
||||
|
||||
How many after-comma places in DXF doubles. 6 is the recommended
|
||||
value (e.g. use rfc), max and the default is 16.
|
||||
|
||||
--with-geojson-precision=rfc
|
||||
|
||||
How many after-comma places in GeoJSON doubles. 6 is the
|
||||
recommended value by RFC 7946, max and the default is 16.
|
||||
|
||||
--disable-write
|
||||
|
||||
Disable DWG write support (default: no).
|
||||
Write support only works for earlier versions until r2004.
|
||||
Rewriting most DWG's <= r2004 usually works fine.
|
||||
|
||||
--disable-bindings
|
||||
|
||||
Disable SWIG bindings for python and perl5.
|
||||
|
||||
--disable-dxf
|
||||
|
||||
Disables all in and out modules: DXF, DXFB, JSON, GeoJSON.
|
||||
|
||||
--disable-json
|
||||
|
||||
Disables the json in and out modules: JSON, GeoJSON.
|
||||
|
||||
--disable-python
|
||||
|
||||
Disable python bindings and tests.
|
||||
Related: --enable-python=python3.6
|
||||
to enforce a specific version.
|
||||
|
||||
--enable-debug
|
||||
|
||||
Activates support for unstable classes. For testing only.
|
||||
|
||||
--enable-gcov
|
||||
|
||||
Activates support for GNU gcov, test coverage reporting.
|
||||
|
||||
--with-perl-install=vendor
|
||||
|
||||
Where to install the perl bindings: vendor,arch,yes,no,site.
|
||||
Default is perl-install=site
|
||||
|
||||
== Windows ==
|
||||
|
||||
See the following scripts how we build on Windows:
|
||||
|
||||
* .github/workflows/main.yml: via msys/autotools, or cmake. gcc, clang-cl or VS2019.
|
||||
* build-aux/smoke.sh: cross-compilation recipes
|
||||
* build-aux/msys2.bat: msys2 deps
|
||||
* .appveyor.yml: windows smoker
|
||||
|
||||
We do support mingw, clang-cl and Visual Studio >= 2019 via cmake.
|
||||
stdint.h and inttypes.h must be available.
|
||||
Pre-compiled Windows binaries are here: https://github.com/LibreDWG/libredwg/releases
|
||||
|
||||
== Example usage of the library ==
|
||||
|
||||
The programs and examples directories contains some application code
|
||||
that uses our lib:
|
||||
|
||||
* dwgread -- A DWG reader supporting various output formats, such as
|
||||
JSON, DXF, DXFB, GeoJSON.
|
||||
|
||||
* dwgwrite -- A DWG writer supporting various input formats, such as DXF, DXFB, JSON.
|
||||
|
||||
* dxfwrite -- A DXF writer supporting various input formats, such as DWG, DXF, DXFB, JSON.
|
||||
|
||||
* dwg2dxf -- Converts a DWG to a DXF (ascii or binary, minimal or full), optionally
|
||||
under a different version. About 90% coverage.
|
||||
|
||||
* dxf2dwg -- Converts a ascii or binary DXF to a DWG (r2000 only so far).
|
||||
About 80% coverage.
|
||||
|
||||
* dwglayers -- Prints the list of layers in a DWG. Optionally with --extnames
|
||||
|
||||
* dwggrep -- Searches DWG files for a text string, via regular expressions.
|
||||
|
||||
* dwg2ps -- Opens a DWG file and outputs an PostScript file. This code
|
||||
depends on the PSlib library. On Debian-based systems this is usually
|
||||
available as a package named pslib-dev. This is very limited so far.
|
||||
|
||||
* dwgfilter -- Applies JSON filters via JQ. Either as structured grep,
|
||||
or to mass change certain values.
|
||||
|
||||
* dwg2SVG -- Opens a DWG file and outputs an SVG (Scalable Vector Graphics)
|
||||
file. SVG is a W3C standard for 2d vector graphics. You can open these
|
||||
files on several free software tools. We recommend Inkscape, a free software
|
||||
vector graphics editor. The dwg2SVG program will not handle 3d content
|
||||
from DWG since SVG only supports 2-dimensional images. Handles only some
|
||||
entities.
|
||||
|
||||
* dwgbmp -- Extracts the bmp thumbnail of a dwg file when it is available.
|
||||
|
||||
* dwgadd -- the easiest way to create DWG's (or DXF, JSON) from scratch or
|
||||
add entities to an existing DWG. It accepts a very simple file with commands
|
||||
to create entities or objects and set its properties.
|
||||
|
||||
* dwgrewrite -- Reads a DWG, writes it back under a different name and optionally
|
||||
under a different version (default r2000), and re-reads it back for confirmation.
|
||||
|
||||
* load_dwg -- This is a skeleton code, reading the DWG and adding an entity.
|
||||
Look there if you want to have a quickly glance on how to use the library.
|
||||
|
||||
* dwg2svg2 -- Another example how to use the DWG API, bypassing direct access to
|
||||
the DWG struct.
|
||||
|
||||
* xmlsuite -- an extensive example library to use XML to compare against prepared
|
||||
features is in the test/xmlsuite directory. It also features code to output entities
|
||||
in XML, but does not use the python bindings.
|
||||
|
||||
Copyright (C) 2009, 2010, 2015, 2018-2026 Free Software Foundation, Inc.
|
||||
|
||||
This library is free software, licensed under the terms of the GNU
|
||||
General Public License as published by the Free Software Foundation,
|
||||
either version 3 of the License, or (at your option) any later version.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,84 @@
|
||||
#!/bin/sh
|
||||
# options: --help, --version, --debug, -i
|
||||
version() {
|
||||
echo "dwgfilter 0.14"
|
||||
exit
|
||||
}
|
||||
help() {
|
||||
echo "dwgfilter [OPTIONS...] dwgfile"
|
||||
echo ""
|
||||
echo "Allow custom jq queries on a temporary JSON dump."
|
||||
echo ""
|
||||
echo "OPTIONS: --help,--version"
|
||||
echo " --debug keep the tmp json"
|
||||
echo " -i write back in-place, with an updating JQ query"
|
||||
echo " ... all other options are passed to jq. See 'man jq'"
|
||||
exit
|
||||
}
|
||||
|
||||
opts=
|
||||
# get last arg
|
||||
for dwg; do true; done
|
||||
for arg in "$@"
|
||||
do
|
||||
case $arg in
|
||||
--help) help ;;
|
||||
--version) version ;;
|
||||
--debug) debug=1 ;;
|
||||
"$dwg") if [ ! -f "$dwg" ]; then echo DWG "$dwg" not found; exit 1; fi ;;
|
||||
-i) writemode=1 ;;
|
||||
*) opts="$opts $arg" ;;
|
||||
esac
|
||||
done
|
||||
if [ ! -f "$dwg" ]
|
||||
then
|
||||
echo Wrong input DWG "$dwg"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$opts" ]
|
||||
then
|
||||
echo Input JQ query arguments missing
|
||||
exit 1
|
||||
fi
|
||||
|
||||
prefix=""
|
||||
exec_prefix="${prefix}"
|
||||
jq=""
|
||||
jq="${jq:-jq}"
|
||||
json="$(mktemp "${TMPDIR:-/tmp}/dwgfilter.XXXXXX.json")" || exit 1
|
||||
if [ -z "$debug" ]; then
|
||||
trap 'rm -f "$json"' EXIT
|
||||
trap 'exit 2' INT TERM
|
||||
fi
|
||||
selfpath="$(realpath "$0")"
|
||||
if [ "$selfpath" = "${exec_prefix}/bin/dwgfilter" ]; then
|
||||
dwgread="${exec_prefix}/bin/dwgread"
|
||||
dwgwrite="${exec_prefix}/bin/dwgwrite"
|
||||
else
|
||||
dwgread="$(dirname "$selfpath")/dwgread"
|
||||
dwgwrite="$(dirname "$selfpath")/dwgwrite"
|
||||
fi
|
||||
|
||||
if [ -n "$debug" ]; then
|
||||
dwgread="$dwgread -v3"
|
||||
dwgwrite="$dwgwrite -v3"
|
||||
fi
|
||||
echo "$dwgread -O json -o $json $dwg"
|
||||
$dwgread -O json -o "$json" "$dwg"
|
||||
echo "$jq $opts $json"
|
||||
$jq "$opts" "$json"
|
||||
fail=$?
|
||||
if [ $fail = 0 ] && [ -n "$writemode" ]; then
|
||||
mv "$dwg" "$dwg.bak"
|
||||
echo "$dwgwrite -o $dwg $json"
|
||||
if $dwgwrite -o "$dwg" "$json"; then
|
||||
:
|
||||
else
|
||||
mv "$dwg.bak" "$dwg"
|
||||
fail=1
|
||||
fi
|
||||
fi
|
||||
if [ -n "$debug" ]; then
|
||||
echo "Keeping JSON dump: $json"
|
||||
fi
|
||||
exit $fail
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,152 @@
|
||||
/*****************************************************************************/
|
||||
/* LibreDWG - free implementation of the DWG file format */
|
||||
/* */
|
||||
/* Copyright (C) 2018-2019 Free Software Foundation, Inc. */
|
||||
/* */
|
||||
/* This library is free software, licensed under the terms of the GNU */
|
||||
/* General Public License as published by the Free Software Foundation, */
|
||||
/* either version 3 of the License, or (at your option) any later version. */
|
||||
/* You should have received a copy of the GNU General Public License */
|
||||
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
/*****************************************************************************/
|
||||
|
||||
/*
|
||||
* bd.c: print double of bits
|
||||
* written by Reini Urban
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../src/config.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "dwg.h"
|
||||
#include "../src/bits.h"
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
char *input;
|
||||
int hex = 0;
|
||||
int size, bits;
|
||||
unsigned long pos;
|
||||
Bit_Chain dat = EMPTY_CHAIN (0);
|
||||
|
||||
if (argc < 2)
|
||||
{
|
||||
printf ("usage: examples/bd "
|
||||
"001100000000000000000000000000011000000000000000100000001010010"
|
||||
"00\n");
|
||||
printf ("or examples/bd -x '8055 40f9 3284 d222 3e40 7436 e0d9 23fd'\n");
|
||||
return 1;
|
||||
}
|
||||
if (argc > 2 && !strcmp (argv[1], "-x"))
|
||||
{
|
||||
hex = 1;
|
||||
input = argv[2];
|
||||
size = strlen (input);
|
||||
}
|
||||
else
|
||||
{
|
||||
input = argv[1];
|
||||
bits = strlen (input);
|
||||
size = (bits / 8);
|
||||
}
|
||||
|
||||
dat.chain = malloc (size + 1);
|
||||
dat.size = size;
|
||||
dat.version = R_2004;
|
||||
if (hex)
|
||||
{
|
||||
dat.size = bit_write_hexbits (&dat, input);
|
||||
bits = dat.size / 8;
|
||||
}
|
||||
else
|
||||
bit_write_bits1 (&dat, input);
|
||||
pos = bit_position (&dat);
|
||||
bit_set_position (&dat, 0);
|
||||
// accept more types, like CMC, BS, BL, HANDLE and print all possible
|
||||
// variants
|
||||
if (pos == 64)
|
||||
{
|
||||
double d = bit_read_RD (&dat);
|
||||
printf ("%.15f RD @%lu\n", d, bit_position (&dat));
|
||||
}
|
||||
else if (pos == 2 || pos > 64)
|
||||
{
|
||||
double d = bit_read_BD (&dat);
|
||||
printf ("%.15f BD @%lu (%ld)\n", d, bit_position (&dat), pos);
|
||||
if (pos == 2)
|
||||
{
|
||||
int i;
|
||||
bit_set_position (&dat, 0);
|
||||
i = bit_read_BB (&dat);
|
||||
printf ("%d BB @%lu\n", i, bit_position (&dat));
|
||||
if (i == 2)
|
||||
printf ("%d BS/BL @%lu\n", 0, bit_position (&dat));
|
||||
else if (i == 3)
|
||||
printf ("%d BS @%lu\n", 256, bit_position (&dat));
|
||||
}
|
||||
}
|
||||
else if (pos == 34)
|
||||
{
|
||||
long l = bit_read_BL (&dat);
|
||||
printf ("%ld BL @%lu\n", l, bit_position (&dat));
|
||||
}
|
||||
else if (pos == 32)
|
||||
{
|
||||
long l = bit_read_RL (&dat);
|
||||
printf ("%ld RL @%lu\n", l, bit_position (&dat));
|
||||
}
|
||||
else if (pos == 16)
|
||||
{
|
||||
int l = (int)bit_read_RS (&dat);
|
||||
printf ("%d RS @%lu\n", l, bit_position (&dat));
|
||||
}
|
||||
else if (pos == 10)
|
||||
{
|
||||
long l = bit_read_BS (&dat);
|
||||
Dwg_Handle h;
|
||||
printf ("%ld BS @%lu\n", l, bit_position (&dat));
|
||||
bit_set_position (&dat, 0);
|
||||
if (!bit_read_H (&dat, &h) && h.size == 1)
|
||||
printf (FORMAT_H " H @%lu (%ld)\n", ARGS_H (h), bit_position (&dat),
|
||||
pos);
|
||||
}
|
||||
else if (pos == 8)
|
||||
{
|
||||
int l = (int)bit_read_RC (&dat);
|
||||
Dwg_Handle h;
|
||||
printf ("%d RC @%lu\n", l, bit_position (&dat));
|
||||
bit_set_position (&dat, 0);
|
||||
if (!bit_read_H (&dat, &h) && h.size == 1)
|
||||
printf (FORMAT_H " H @%lu (%ld)\n", ARGS_H (h), bit_position (&dat),
|
||||
pos);
|
||||
}
|
||||
else
|
||||
{
|
||||
Dwg_Handle h;
|
||||
Dwg_Color c;
|
||||
double d = bit_read_BD (&dat);
|
||||
if (pos >= bit_position (&dat))
|
||||
{
|
||||
printf ("%.15f BD? @%lu (%ld)\n", d, bit_position (&dat), pos);
|
||||
}
|
||||
else
|
||||
{ // divide into chunks of 34,32,18,16,10,8,2
|
||||
printf ("? (%ld)\n", pos);
|
||||
}
|
||||
bit_set_position (&dat, 0);
|
||||
if (!bit_read_H (&dat, &h))
|
||||
printf (FORMAT_H " H? @%lu (%ld)\n", ARGS_H (h), bit_position (&dat),
|
||||
pos);
|
||||
bit_set_position (&dat, 0);
|
||||
bit_read_CMC (&dat, &dat, &c);
|
||||
if (c.index < 257)
|
||||
printf ("%d 0x%06X 0x%x CMC? @%lu (%ld)\n", c.index, c.rgb, c.flag,
|
||||
bit_position (&dat), pos);
|
||||
}
|
||||
free (dat.chain);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
/*****************************************************************************/
|
||||
/* LibreDWG - free implementation of the DWG file format */
|
||||
/* */
|
||||
/* Copyright (C) 2018-2019 Free Software Foundation, Inc. */
|
||||
/* */
|
||||
/* This library is free software, licensed under the terms of the GNU */
|
||||
/* General Public License as published by the Free Software Foundation, */
|
||||
/* either version 3 of the License, or (at your option) any later version. */
|
||||
/* You should have received a copy of the GNU General Public License */
|
||||
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
/*****************************************************************************/
|
||||
|
||||
/*
|
||||
* bits.c: print all possible type/values of given bits
|
||||
* written by Reini Urban
|
||||
*/
|
||||
|
||||
#include "../src/config.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "dwg.h"
|
||||
#include "../src/bits.h"
|
||||
static int maxoff = 0;
|
||||
|
||||
static int
|
||||
decode (Bit_Chain *dat, int off, const int size)
|
||||
{
|
||||
int pos = 0;
|
||||
BITCODE_BS bs = 0;
|
||||
printf ("decode offset:%d, size:%d\n", off, size);
|
||||
if (off >= size)
|
||||
return 0;
|
||||
|
||||
bit_set_position (dat, off);
|
||||
if (size - off >= 66)
|
||||
{
|
||||
double d = bit_read_BD (dat);
|
||||
int p = (int)bit_position (dat);
|
||||
if (d != bit_nan ())
|
||||
{
|
||||
printf ("%.15f BD @%d @%u.%u (%d)\n", d, p, p / 8, p % 8, size);
|
||||
pos = p;
|
||||
}
|
||||
bit_set_position (dat, off);
|
||||
}
|
||||
if (size - off >= 64)
|
||||
{
|
||||
double d = bit_read_RD (dat);
|
||||
int p = (int)bit_position (dat);
|
||||
if (d != bit_nan ())
|
||||
{
|
||||
printf ("%.15f RD @%d @%u.%u (%d)\n", d, p, p / 8, p % 8, size);
|
||||
pos = p;
|
||||
}
|
||||
bit_set_position (dat, off);
|
||||
}
|
||||
if (size - off >= 34)
|
||||
{
|
||||
BITCODE_BL l = (long)bit_read_BL (dat);
|
||||
int p = (int)bit_position (dat);
|
||||
if (p <= size - off)
|
||||
{
|
||||
printf ("%u BL @%d @%u.%u (%d)\n", l, p, p / 8, p % 8, size);
|
||||
pos = p;
|
||||
}
|
||||
bit_set_position (dat, off);
|
||||
}
|
||||
if (size - off >= 32)
|
||||
{
|
||||
BITCODE_BL l = (long)bit_read_RL (dat);
|
||||
pos = (int)bit_position (dat);
|
||||
printf ("%u RL @%d @%u.%u (%d)\n", l, pos, pos / 8, pos % 8, size);
|
||||
bit_set_position (dat, off);
|
||||
}
|
||||
if (size - off >= 16)
|
||||
{
|
||||
BITCODE_RS l = (int)bit_read_RS (dat);
|
||||
pos = (int)bit_position (dat);
|
||||
printf ("%d RS @%d (%d)\n", l, pos, size);
|
||||
bit_set_position (dat, off);
|
||||
}
|
||||
if (size - off >= 10)
|
||||
{
|
||||
int p;
|
||||
bs = bit_read_BS (dat);
|
||||
p = (int)bit_position (dat);
|
||||
if (p <= size - off)
|
||||
{
|
||||
printf ("%d BS @%d (%d)\n", bs, p, size);
|
||||
pos = p;
|
||||
}
|
||||
bit_set_position (dat, off);
|
||||
}
|
||||
if (size - off >= 8)
|
||||
{
|
||||
int l = bit_read_RC (dat);
|
||||
pos = (int)bit_position (dat);
|
||||
printf ("%d RC @%d (%d)\n", l, pos, size);
|
||||
bit_set_position (dat, off);
|
||||
}
|
||||
if (size - off >= 8)
|
||||
{
|
||||
Dwg_Handle h;
|
||||
int err, p;
|
||||
err = bit_read_H (dat, &h);
|
||||
p = (int)bit_position (dat);
|
||||
if (!err && h.size == 1 && p <= size - off)
|
||||
{
|
||||
printf ("%x.%d.%lX H @%d (%d)\n", h.code, h.size, h.value, p, size);
|
||||
pos = p;
|
||||
}
|
||||
bit_set_position (dat, off);
|
||||
}
|
||||
if (size - off >= 4)
|
||||
{
|
||||
Dwg_Color c;
|
||||
int p;
|
||||
bit_read_CMC (dat, dat, &c);
|
||||
p = (int)bit_position (dat);
|
||||
if (c.index < 257 && p <= size - off)
|
||||
{
|
||||
printf ("%d 0x%06X 0x%x CMC @%d (%d)\n", c.index, c.rgb, c.flag, p,
|
||||
size);
|
||||
pos = p;
|
||||
}
|
||||
free (c.name);
|
||||
free (c.book_name);
|
||||
bit_set_position (dat, off);
|
||||
}
|
||||
if (size - off >= 2)
|
||||
{
|
||||
int i = bit_read_BB (dat);
|
||||
pos = (int)bit_position (dat);
|
||||
printf ("%d BB @%d (%d)\n", i, pos, size);
|
||||
if (i == 2)
|
||||
printf ("%d BS/BL @%d (%d)\n", 0, pos, size);
|
||||
else if (i == 3)
|
||||
printf ("%d BS @%d (%d)\n", 256, pos, size);
|
||||
bit_set_position (dat, off);
|
||||
}
|
||||
// if BS is a valid length try TV also
|
||||
if (bs > 0 && bs < size - off - pos)
|
||||
{
|
||||
int i;
|
||||
char *s = bit_read_TV (dat);
|
||||
int p = (int)bit_position (dat);
|
||||
if (s)
|
||||
{
|
||||
for (i = 0; i < bs; i++)
|
||||
if (!isprint (s[i]))
|
||||
break;
|
||||
if (i == bs)
|
||||
{
|
||||
printf ("%s TV @%d (%d)\n", s, pos, size);
|
||||
pos = p;
|
||||
}
|
||||
free (s);
|
||||
}
|
||||
}
|
||||
|
||||
while (pos < size)
|
||||
{
|
||||
if (pos <= off)
|
||||
pos++;
|
||||
if (maxoff < pos)
|
||||
maxoff = pos;
|
||||
// printf ("offset %d\n", pos);
|
||||
return decode (dat, off + 1, size);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
int hex = 0;
|
||||
int print_bits = 0;
|
||||
int i;
|
||||
int pos;
|
||||
Bit_Chain dat = EMPTY_CHAIN (0);
|
||||
|
||||
if (argc < 2)
|
||||
{
|
||||
printf ("usage: examples/bits "
|
||||
"001100000000000000000000000000011000000000000000100000001010010"
|
||||
"00\n");
|
||||
printf (
|
||||
"or examples/bits -x '8055 40f9 3284 d222 3e40 7436 e0d9 23fd'\n");
|
||||
return 1;
|
||||
}
|
||||
i = 1;
|
||||
if (argc > 2 && !strcmp (argv[i], "-x"))
|
||||
{
|
||||
hex = 1;
|
||||
i++;
|
||||
}
|
||||
if (argc > 2 && !strcmp (argv[i], "-b"))
|
||||
{
|
||||
print_bits = 1;
|
||||
i++;
|
||||
}
|
||||
|
||||
// dat.chain = malloc (size + 1);
|
||||
// dat.size = size;
|
||||
dat.version = R_2004;
|
||||
do
|
||||
{
|
||||
char *input = argv[i];
|
||||
int bits = strlen (input);
|
||||
if (hex)
|
||||
dat.size += bits;
|
||||
else
|
||||
dat.size += bits / 8;
|
||||
dat.chain = realloc (dat.chain, dat.size + 1);
|
||||
if (hex)
|
||||
{
|
||||
int size = bit_write_hexbits (&dat, input);
|
||||
dat.size -= dat.size - size;
|
||||
}
|
||||
else
|
||||
bit_write_bits1 (&dat, input);
|
||||
i++;
|
||||
}
|
||||
while (i < argc);
|
||||
|
||||
pos = (int)bit_position (&dat);
|
||||
if (print_bits)
|
||||
bit_print_bits (&dat.chain[0], pos);
|
||||
// accept all types, like CMC, BS, BL, HANDLE and print all possible variants
|
||||
pos = decode (&dat, 0, (int)pos);
|
||||
|
||||
free (dat.chain);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,531 @@
|
||||
/*****************************************************************************/
|
||||
/* LibreDWG - free implementation of the DWG file format */
|
||||
/* */
|
||||
/* Copyright (C) 2013-2020,2023 Free Software Foundation, Inc. */
|
||||
/* */
|
||||
/* This library is free software, licensed under the terms of the GNU */
|
||||
/* General Public License as published by the Free Software Foundation, */
|
||||
/* either version 3 of the License, or (at your option) any later version. */
|
||||
/* You should have received a copy of the GNU General Public License */
|
||||
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
/*****************************************************************************/
|
||||
|
||||
/*
|
||||
* dwg2svg2.c: convert a DWG to SVG via the API.
|
||||
if there are paperspace entities, only output them. else all modelspace
|
||||
entities.
|
||||
* written by Gaganjyot Singh
|
||||
* modified by Reini Urban
|
||||
*/
|
||||
|
||||
#include "../src/config.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <math.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#ifdef ENABLE_MIMALLOC
|
||||
# include <mimalloc-override.h>
|
||||
#endif
|
||||
|
||||
#include <dwg.h>
|
||||
#include <dwg_api.h>
|
||||
#include "geom.h"
|
||||
|
||||
static int opts = 0;
|
||||
static dwg_data g_dwg;
|
||||
static double model_xmin, model_ymin;
|
||||
static double page_width, page_height, scale;
|
||||
|
||||
static int
|
||||
usage (void)
|
||||
{
|
||||
printf ("\nUsage: dwg2svg2 [-v[0-9]] DWGFILE\n");
|
||||
return 1;
|
||||
}
|
||||
static int
|
||||
opt_version (void)
|
||||
{
|
||||
printf ("dwg2svg2 %s\n", PACKAGE_VERSION);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
help (void)
|
||||
{
|
||||
printf ("\nUsage: dwg2svg2 [OPTION]... DWGFILE >file.svg\n");
|
||||
printf ("Example to use the DWG api\n"
|
||||
"\n");
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
printf (" -v[0-9], --verbose [0-9] verbosity\n");
|
||||
printf (" --help display this help and exit\n");
|
||||
printf (" --version output version information and exit\n"
|
||||
"\n");
|
||||
#else
|
||||
printf (" -v[0-9] verbosity\n");
|
||||
printf (" -h display this help and exit\n");
|
||||
printf (" -i output version information and exit\n"
|
||||
"\n");
|
||||
#endif
|
||||
printf ("GNU LibreDWG online manual: "
|
||||
"<https://www.gnu.org/software/libredwg/>\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define log_if_error(msg) \
|
||||
if (error) \
|
||||
{ \
|
||||
fprintf (stderr, "ERROR: %s\n", msg); \
|
||||
exit (1); \
|
||||
}
|
||||
#define log_error(msg) \
|
||||
{ \
|
||||
fprintf (stderr, "ERROR: %s\n", msg); \
|
||||
exit (1); \
|
||||
}
|
||||
#define dynget(obj, name, field, var) \
|
||||
if (!dwg_dynapi_entity_value (obj, "" name, "" field, var, NULL)) \
|
||||
{ \
|
||||
fprintf (stderr, "ERROR: %s.%s\n", name, field); \
|
||||
exit (1); \
|
||||
}
|
||||
#define dynget_utf8(obj, name, field, var) \
|
||||
if (!dwg_dynapi_entity_utf8text (obj, "" name, "" field, var, &isnew, \
|
||||
NULL)) \
|
||||
{ \
|
||||
fprintf (stderr, "ERROR: %s.%s\n", name, field); \
|
||||
exit (1); \
|
||||
}
|
||||
|
||||
static double
|
||||
transform_X (double x)
|
||||
{
|
||||
return x - model_xmin;
|
||||
}
|
||||
|
||||
static double
|
||||
transform_Y (double y)
|
||||
{
|
||||
return page_height - (y - model_ymin);
|
||||
}
|
||||
|
||||
static void output_SVG (dwg_data *dwg);
|
||||
|
||||
static int
|
||||
test_SVG (char *filename)
|
||||
{
|
||||
int error;
|
||||
|
||||
memset (&g_dwg, 0, sizeof (dwg_data));
|
||||
g_dwg.opts = opts;
|
||||
error = dwg_read_file (filename, &g_dwg);
|
||||
if (error < DWG_ERR_CRITICAL)
|
||||
output_SVG (&g_dwg);
|
||||
|
||||
dwg_free (&g_dwg);
|
||||
/* This value is the return value for `main',
|
||||
so clamp it to either 0 or 1. */
|
||||
return error < DWG_ERR_CRITICAL ? 0 : 1;
|
||||
}
|
||||
|
||||
static void
|
||||
output_TEXT (dwg_object *obj)
|
||||
{
|
||||
int error, index;
|
||||
dwg_point_2d ins_pt;
|
||||
Dwg_Entity_TEXT *text;
|
||||
char *text_value;
|
||||
double fontsize;
|
||||
const Dwg_Version_Type dwg_version = obj->parent->header.version;
|
||||
int isnew = 0;
|
||||
|
||||
index = dwg_object_get_index (obj, &error);
|
||||
log_if_error ("object_get_index");
|
||||
text = dwg_object_to_TEXT (obj);
|
||||
if (!text)
|
||||
log_error ("dwg_object_to_TEXT");
|
||||
dynget_utf8 (text, "TEXT", "text_value", &text_value);
|
||||
dynget (text, "TEXT", "ins_pt", &ins_pt);
|
||||
dynget (text, "TEXT", "height", &fontsize);
|
||||
|
||||
printf ("\t<text id=\"dwg-object-%d\" x=\"%f\" y=\"%f\" "
|
||||
"font-family=\"Verdana\" font-size=\"%f\" fill=\"blue\">%s</text>\n",
|
||||
index, transform_X (ins_pt.x), transform_Y (ins_pt.y), fontsize,
|
||||
text_value);
|
||||
|
||||
if (text_value && isnew)
|
||||
free (text_value);
|
||||
}
|
||||
|
||||
static void
|
||||
output_LINE (dwg_object *obj)
|
||||
{
|
||||
int error, index;
|
||||
Dwg_Entity_LINE *line;
|
||||
dwg_point_3d start, end;
|
||||
|
||||
index = dwg_object_get_index (obj, &error);
|
||||
log_if_error ("object_get_index");
|
||||
line = dwg_object_to_LINE (obj);
|
||||
if (!line)
|
||||
log_error ("dwg_object_to_LINE");
|
||||
if (!dwg_get_LINE (line, "start", &start))
|
||||
log_error ("LINE.start");
|
||||
if (!dwg_get_LINE (line, "end", &end))
|
||||
log_error ("LINE.end");
|
||||
|
||||
printf ("\t<path id=\"dwg-object-%d\" d=\"M %f,%f %f,%f\" "
|
||||
"style=\"fill:none;stroke:blue;stroke-width:0.1px\" />\n",
|
||||
index, transform_X (start.x), transform_Y (start.y),
|
||||
transform_X (end.x), transform_Y (end.y));
|
||||
}
|
||||
|
||||
static void
|
||||
output_CIRCLE (dwg_object *obj)
|
||||
{
|
||||
Dwg_Entity_CIRCLE *circle;
|
||||
int error, index;
|
||||
double radius;
|
||||
dwg_point_3d center;
|
||||
|
||||
index = dwg_object_get_index (obj, &error);
|
||||
log_if_error ("object_get_index");
|
||||
circle = dwg_object_to_CIRCLE (obj);
|
||||
if (!circle)
|
||||
log_error ("dwg_object_to_CIRCLE");
|
||||
if (!dwg_get_CIRCLE (circle, "center", ¢er))
|
||||
log_error ("CIRCLE.center");
|
||||
if (!dwg_get_CIRCLE (circle, "radius", &radius))
|
||||
log_error ("CIRCLE.radius");
|
||||
|
||||
printf ("\t<circle id=\"dwg-object-%d\" cx=\"%f\" cy=\"%f\" r=\"%f\" "
|
||||
"fill=\"none\" stroke=\"blue\" stroke-width=\"0.1px\" />\n",
|
||||
index, transform_X (center.x), transform_Y (center.y), radius);
|
||||
}
|
||||
|
||||
static void
|
||||
output_ARC (dwg_object *obj)
|
||||
{
|
||||
Dwg_Entity_ARC *arc;
|
||||
int error, index;
|
||||
double radius, start_angle, end_angle;
|
||||
dwg_point_3d center;
|
||||
double x_start, y_start, x_end, y_end;
|
||||
int large_arc;
|
||||
|
||||
index = dwg_object_get_index (obj, &error);
|
||||
log_if_error ("object_get_index");
|
||||
arc = dwg_object_to_ARC (obj);
|
||||
if (!arc)
|
||||
log_error ("dwg_object_to_ARC");
|
||||
dynget (arc, "ARC", "radius", &radius);
|
||||
dynget (arc, "ARC", "center", ¢er);
|
||||
dynget (arc, "ARC", "start_angle", &start_angle);
|
||||
dynget (arc, "ARC", "end_angle", &end_angle);
|
||||
|
||||
x_start = center.x + radius * cos (start_angle);
|
||||
y_start = center.y + radius * sin (start_angle);
|
||||
x_end = center.x + radius * cos (end_angle);
|
||||
y_end = center.y + radius * sin (end_angle);
|
||||
// Assuming clockwise arcs.
|
||||
large_arc = (end_angle - start_angle < M_PI) ? 0 : 1;
|
||||
|
||||
printf ("\t<path id=\"dwg-object-%d\" d=\"M %f,%f A %f,%f 0 %d 0 %f,%f\" "
|
||||
"fill=\"none\" stroke=\"blue\" stroke-width=\"%f\" />\n",
|
||||
index, transform_X (x_start), transform_Y (y_start), radius, radius,
|
||||
large_arc, transform_X (x_end), transform_Y (y_end), 0.1);
|
||||
}
|
||||
|
||||
static void
|
||||
output_INSERT (dwg_object *obj)
|
||||
{
|
||||
int index, error;
|
||||
BITCODE_RL abs_ref;
|
||||
double rotation;
|
||||
dwg_ent_insert *insert;
|
||||
dwg_point_3d ins_pt, _scale;
|
||||
dwg_handle *obj_handle, *ins_handle;
|
||||
Dwg_Data *dwg;
|
||||
|
||||
insert = dwg_object_to_INSERT (obj);
|
||||
if (!insert)
|
||||
log_error ("dwg_object_to_INSERT");
|
||||
dwg = obj->parent;
|
||||
index = dwg_object_get_index (obj, &error);
|
||||
log_if_error ("object_get_index");
|
||||
dynget (insert, "INSERT", "rotation", &rotation);
|
||||
dynget (insert, "INSERT", "ins_pt", &ins_pt);
|
||||
dynget (insert, "INSERT", "scale", &_scale);
|
||||
obj_handle = dwg_object_get_handle (obj, &error);
|
||||
log_if_error ("get_handle");
|
||||
if (!insert->block_header)
|
||||
log_error ("insert->block_header");
|
||||
abs_ref = insert->block_header->absolute_ref;
|
||||
|
||||
if (insert->block_header->handleref.code == 5 || dwg->header.version < R_13)
|
||||
{
|
||||
printf ("\t<use id=\"dwg-object-%d\" transform=\"translate(%f %f) "
|
||||
"rotate(%f) scale(%f %f)\" xlink:href=\"#symbol-%X\" /><!-- "
|
||||
"block_header->handleref: " FORMAT_H " -->\n",
|
||||
index, transform_X (ins_pt.x), transform_Y (ins_pt.y),
|
||||
(180.0 / M_PI) * rotation, _scale.x, _scale.y, abs_ref,
|
||||
ARGS_H (*obj_handle));
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ("\n\n<!-- WRONG INSERT(" FORMAT_H ") -->\n",
|
||||
ARGS_H (*obj_handle));
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
output_object (dwg_object *obj)
|
||||
{
|
||||
int i = 0;
|
||||
if (!obj)
|
||||
{
|
||||
fprintf (stderr, "object is NULL\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dwg_object_get_fixedtype (obj) == DWG_TYPE_INSERT)
|
||||
{
|
||||
i++;
|
||||
output_INSERT (obj);
|
||||
}
|
||||
else if (dwg_object_get_fixedtype (obj) == DWG_TYPE_LINE)
|
||||
{
|
||||
i++;
|
||||
output_LINE (obj);
|
||||
}
|
||||
else if (dwg_object_get_fixedtype (obj) == DWG_TYPE_CIRCLE)
|
||||
{
|
||||
i++;
|
||||
output_CIRCLE (obj);
|
||||
}
|
||||
else if (dwg_object_get_fixedtype (obj) == DWG_TYPE_TEXT)
|
||||
{
|
||||
i++;
|
||||
output_TEXT (obj);
|
||||
}
|
||||
else if (dwg_object_get_fixedtype (obj) == DWG_TYPE_ARC)
|
||||
{
|
||||
i++;
|
||||
output_ARC (obj);
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
static int
|
||||
output_BLOCK_HEADER (dwg_object_ref *ref)
|
||||
{
|
||||
dwg_object *hdr, *obj;
|
||||
dwg_obj_block_header *_hdr;
|
||||
int error;
|
||||
BITCODE_RL abs_ref;
|
||||
char *name;
|
||||
int i = 0;
|
||||
|
||||
if (!ref)
|
||||
{
|
||||
fprintf (stderr,
|
||||
"Empty BLOCK."
|
||||
" Could not output an SVG symbol for this BLOCK_HEADER\n");
|
||||
return 0;
|
||||
}
|
||||
hdr = dwg_ref_get_object (ref, &error);
|
||||
if (!hdr || error)
|
||||
{
|
||||
abs_ref = dwg_ref_get_absref (ref, &error);
|
||||
fprintf (stderr, "Failed to resolve BLOCK handle %X.\n",
|
||||
(unsigned)abs_ref);
|
||||
return 0;
|
||||
}
|
||||
abs_ref = dwg_ref_get_absref (ref, &error);
|
||||
|
||||
_hdr = dwg_object_to_BLOCK_HEADER (hdr);
|
||||
if (_hdr)
|
||||
{
|
||||
i++;
|
||||
dynget (_hdr, "BLOCK_HEADER", "name", &name);
|
||||
// name = dwg_obj_block_header_get_name (_hdr, &error);
|
||||
printf ("\t<g id=\"symbol-%X\" >\n\t\t<!-- %s -->\n",
|
||||
abs_ref ? abs_ref : 0, name ? name : "");
|
||||
if (name != NULL && name != _hdr->name
|
||||
&& hdr->parent->header.version >= R_2007)
|
||||
free (name);
|
||||
}
|
||||
else
|
||||
printf ("\t<g id=\"symbol-%X\" >\n\t\t<!-- ? -->\n",
|
||||
abs_ref ? abs_ref : 0);
|
||||
|
||||
obj = get_first_owned_entity (hdr);
|
||||
while (obj)
|
||||
{
|
||||
i += output_object (obj);
|
||||
obj = get_next_owned_entity (hdr, obj);
|
||||
}
|
||||
printf ("\t</g>\n");
|
||||
return i;
|
||||
}
|
||||
|
||||
static void
|
||||
output_SVG (dwg_data *dwg)
|
||||
{
|
||||
unsigned int i, num_hdr_objs;
|
||||
int error;
|
||||
dwg_obj_block_control *_ctrl;
|
||||
dwg_object_ref *hdr;
|
||||
dwg_object_ref **hdr_refs;
|
||||
dwg_object_ref *ms = dwg_model_space_ref (dwg);
|
||||
dwg_object_ref *ps = dwg_paper_space_ref (dwg);
|
||||
|
||||
double dx = dwg_model_x_max (dwg) - dwg_model_x_min (dwg);
|
||||
double dy = dwg_model_y_max (dwg) - dwg_model_y_min (dwg);
|
||||
double pdx = dwg->header_vars.PLIMMAX.x - dwg->header_vars.PLIMMIN.x;
|
||||
double pdy = dwg->header_vars.PLIMMAX.y - dwg->header_vars.PLIMMIN.y;
|
||||
double scale_x = dx / (pdx == 0.0 ? 1.0 : pdx);
|
||||
double scale_y = dy / (pdy == 0.0 ? 1.0 : pdy);
|
||||
scale = 25.4 / 72.0; // pt:mm TODO
|
||||
|
||||
model_xmin = dwg_model_x_min (dwg);
|
||||
model_ymin = dwg_model_y_min (dwg);
|
||||
page_width = dx;
|
||||
page_height = dy;
|
||||
scale *= (scale_x > scale_y ? scale_x : scale_y);
|
||||
|
||||
_ctrl = dwg_block_control (dwg);
|
||||
hdr_refs = dwg_obj_block_control_get_block_headers (_ctrl, &error);
|
||||
log_if_error ("block_control_get_block_headers");
|
||||
num_hdr_objs = dwg_obj_block_control_get_num_entries (_ctrl, &error);
|
||||
log_if_error ("block_control_get_num_entries");
|
||||
|
||||
printf ("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
|
||||
"<svg\n"
|
||||
" xmlns:svg=\"http://www.w3.org/2000/svg\"\n"
|
||||
" xmlns=\"http://www.w3.org/2000/svg\"\n"
|
||||
" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
|
||||
" version=\"1.1\"\n"
|
||||
" width=\"%f\"\n"
|
||||
" height=\"%f\"\n"
|
||||
">\n",
|
||||
page_width, page_height);
|
||||
|
||||
printf ("\t<defs>\n");
|
||||
for (i = 0; i < num_hdr_objs; i++)
|
||||
{
|
||||
hdr = hdr_refs[i];
|
||||
if (hdr == ms || hdr == ps)
|
||||
continue;
|
||||
output_BLOCK_HEADER (hdr_refs[i]);
|
||||
}
|
||||
printf ("\t</defs>\n");
|
||||
|
||||
if (ps)
|
||||
i = output_BLOCK_HEADER (ps);
|
||||
if (!ps || !i)
|
||||
output_BLOCK_HEADER (ms);
|
||||
free (hdr_refs);
|
||||
|
||||
printf ("</svg>\n");
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
int i = 1;
|
||||
int c;
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
int option_index = 0;
|
||||
static struct option long_options[]
|
||||
= { { "verbose", 1, &opts, 1 }, // optional
|
||||
{ "help", 0, 0, 0 },
|
||||
{ "version", 0, 0, 0 },
|
||||
{ NULL, 0, NULL, 0 } };
|
||||
#endif
|
||||
|
||||
if (argc < 2)
|
||||
return usage ();
|
||||
|
||||
while
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
((c = getopt_long (argc, argv, ":v::h", long_options, &option_index))
|
||||
!= -1)
|
||||
#else
|
||||
((c = getopt (argc, argv, ":v::hi")) != -1)
|
||||
#endif
|
||||
{
|
||||
if (c == -1)
|
||||
break;
|
||||
switch (c)
|
||||
{
|
||||
case ':': // missing arg
|
||||
if (optarg && !strcmp (optarg, "v"))
|
||||
{
|
||||
opts = 1;
|
||||
break;
|
||||
}
|
||||
fprintf (stderr, "%s: option '-%c' requires an argument\n", argv[0],
|
||||
optopt);
|
||||
break;
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
case 0:
|
||||
/* This option sets a flag */
|
||||
if (!strcmp (long_options[option_index].name, "verbose"))
|
||||
{
|
||||
if (opts < 0 || opts > 9)
|
||||
return usage ();
|
||||
# if defined(USE_TRACING) && defined(HAVE_SETENV)
|
||||
{
|
||||
char v[2];
|
||||
*v = opts + '0';
|
||||
*(v + 1) = 0;
|
||||
setenv ("LIBREDWG_TRACE", v, 1);
|
||||
}
|
||||
# endif
|
||||
break;
|
||||
}
|
||||
if (!strcmp (long_options[option_index].name, "version"))
|
||||
return opt_version ();
|
||||
if (!strcmp (long_options[option_index].name, "help"))
|
||||
return help ();
|
||||
break;
|
||||
#else
|
||||
case 'i':
|
||||
return opt_version ();
|
||||
#endif
|
||||
case 'v': // support -v3 and -v
|
||||
i = (optind > 0 && optind < argc) ? optind - 1 : 1;
|
||||
if (!memcmp (argv[i], "-v", 2))
|
||||
{
|
||||
opts = argv[i][2] ? argv[i][2] - '0' : 1;
|
||||
}
|
||||
if (opts < 0 || opts > 9)
|
||||
return usage ();
|
||||
#if defined(USE_TRACING) && defined(HAVE_SETENV)
|
||||
{
|
||||
char v[2];
|
||||
*v = opts + '0';
|
||||
*(v + 1) = 0;
|
||||
setenv ("LIBREDWG_TRACE", v, 1);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case 'h':
|
||||
return help ();
|
||||
case '?':
|
||||
fprintf (stderr, "%s: invalid option '-%c' ignored\n", argv[0],
|
||||
optopt);
|
||||
break;
|
||||
default:
|
||||
return usage ();
|
||||
}
|
||||
}
|
||||
i = optind;
|
||||
if (i >= argc)
|
||||
return usage ();
|
||||
|
||||
return test_SVG (argv[i]);
|
||||
}
|
||||
Binary file not shown.
File diff suppressed because it is too large.
Load diff
Binary file not shown.
@@ -0,0 +1,834 @@
|
||||
/*****************************************************************************/
|
||||
/* LibreDWG - free implementation of the DWG file format */
|
||||
/* */
|
||||
/* Copyright (C) 2020 Free Software Foundation, Inc. */
|
||||
/* */
|
||||
/* This library is free software, licensed under the terms of the GNU */
|
||||
/* General Public License as published by the Free Software Foundation, */
|
||||
/* either version 3 of the License, or (at your option) any later version. */
|
||||
/* You should have received a copy of the GNU General Public License */
|
||||
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
/*****************************************************************************/
|
||||
|
||||
/*
|
||||
* dwgfuzz.c: afl++/honggfuzz fuzzing for all in- and exporters. Just not the
|
||||
separate ones.
|
||||
*
|
||||
* Also usable like:
|
||||
../configure --disable-shared --disable-bindings CC=hfuzz-clang CFLAGS='-O2
|
||||
-g -fsanitize=address,undefined -fno-omit-frame-pointer -I/usr/local/include'
|
||||
&& make -C src && make -C examples dwgfuzz honggfuzz -i ../.fuzz-in-dxf --
|
||||
examples/dwgfuzz -indxf ___FILE___
|
||||
|
||||
* Also useful for debugging the fuzzers.
|
||||
AFL_DONT_OPTIMIZE=1 AFL_LLVM_INSTRIM=1 AFL_USE_ASAN=1 make -C examples
|
||||
dwgfuzz V=1 AFL_DEBUG=15 AFL_DEBUG_CHILD_OUTPUT=1 gdb --args afl-fuzz -m none
|
||||
-i ../.fuzz-in-dxf/ -o .fuzz-out/ examples/dwgfuzz (gdb) set follow-fork-mode
|
||||
child
|
||||
* written by Reini Urban
|
||||
*/
|
||||
|
||||
#include "../src/config.h"
|
||||
#ifdef HAVE_SSCANF_S
|
||||
# define __STDC_WANT_LIB_EXT1__ 1
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include <dwg.h>
|
||||
#include <dwg_api.h>
|
||||
#include "common.h"
|
||||
#include "decode.h"
|
||||
#include "encode.h"
|
||||
#include "bits.h"
|
||||
#ifndef DISABLE_DXF
|
||||
# include "out_dxf.h"
|
||||
# ifndef DISABLE_JSON
|
||||
# include "in_json.h"
|
||||
# include "out_json.h"
|
||||
# endif
|
||||
# include "in_dxf.h"
|
||||
#endif
|
||||
|
||||
#define FUZZ_INMEM 0
|
||||
#define FUZZ_STDIN 1
|
||||
#define FUZZ_FILE 2
|
||||
|
||||
// defined by ./make-afl-clang-fast.sh for INMEM. defaults to FILE for
|
||||
// debugging
|
||||
#ifndef FUZZ_MODE
|
||||
# define FUZZ_MODE FUZZ_INMEM
|
||||
// #define FUZZ_MODE FUZZ_STDIN
|
||||
// #define FUZZ_MODE FUZZ_FILE
|
||||
#endif
|
||||
|
||||
int dwg_fuzz_dat (Dwg_Data **restrict dwgp, Bit_Chain *restrict dat);
|
||||
|
||||
static int
|
||||
version (void)
|
||||
{
|
||||
#if FUZZ_MODE == FUZZ_INMEM
|
||||
printf ("dwgfuzz %s INMEM\n", PACKAGE_VERSION);
|
||||
#elif FUZZ_MODE == FUZZ_FILE
|
||||
printf ("dwgfuzz %s FILE\n", PACKAGE_VERSION);
|
||||
#elif FUZZ_MODE == FUZZ_STDIN
|
||||
printf ("dwgfuzz %s STDIN\n", PACKAGE_VERSION);
|
||||
#else
|
||||
error unknown FUZZ_MODE
|
||||
#endif
|
||||
#ifndef __AFL_COMPILER
|
||||
printf ("not instrumented\n");
|
||||
#else
|
||||
# ifdef __AFL_FUZZ_TESTCASE_BUF
|
||||
printf ("shared-memory instrumented\n");
|
||||
# else
|
||||
printf ("instrumented\n");
|
||||
# endif
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef __AFL_COMPILER
|
||||
# define __AFL_FUZZ_INIT()
|
||||
# if FUZZ_MODE == FUZZ_INMEM
|
||||
unsigned char *__AFL_FUZZ_TESTCASE_BUF;
|
||||
unsigned long __AFL_FUZZ_TESTCASE_LEN;
|
||||
# define __AFL_INIT() \
|
||||
fp = fopen (argv[2], "rb"); \
|
||||
if (!fp) \
|
||||
return 0; \
|
||||
__AFL_FUZZ_TESTCASE_LEN = dat.size; \
|
||||
dat_read_file (&dat, fp, argv[2]); \
|
||||
__AFL_FUZZ_TESTCASE_BUF = dat.chain;
|
||||
# else
|
||||
# define __AFL_INIT()
|
||||
# endif
|
||||
# define __AFL_FUZZ_TESTCASE_LEN dat.size
|
||||
# define __AFL_LOOP(i) 1
|
||||
#endif
|
||||
|
||||
__AFL_FUZZ_INIT ();
|
||||
|
||||
static int
|
||||
help (void)
|
||||
{
|
||||
#if FUZZ_MODE == FUZZ_FILE
|
||||
printf ("\nUsage: dwgfuzz MODE @@\n");
|
||||
#elif !defined __AFL_COMPILER
|
||||
printf ("\nUsage: dwgfuzz MODE ../.fuzz-in/INFILE\n");
|
||||
#else
|
||||
printf ("\nUsage: dwgfuzz MODE\n");
|
||||
#endif
|
||||
printf ("afl++ clang-fast shared-memory backend, using many importers and "
|
||||
"exporters.\n"
|
||||
"\n");
|
||||
printf ("MODE:\n");
|
||||
#ifdef USE_WRITE
|
||||
# ifndef DISABLE_DXF
|
||||
printf (" -indxf: import from DXF, export as DWG\n");
|
||||
# endif
|
||||
# ifndef DISABLE_JSON
|
||||
printf (" -injson: import from JSON, export as DWG\n");
|
||||
# endif
|
||||
printf (" -rw: import from DWG, export as DWG, re-import from "
|
||||
"this DWG (rewrite)\n");
|
||||
printf (" -add: import from special add file, export as DWG and DXF, "
|
||||
"re-import from this\n");
|
||||
#endif
|
||||
printf (" -dwg: import from DWG only\n");
|
||||
#ifndef DISABLE_DXF
|
||||
printf (" -dxf: import from DWG, export as DXF\n");
|
||||
printf (" -dxfb: import from DWG, export as binary DXF\n");
|
||||
#endif
|
||||
#ifndef DISABLE_JSON
|
||||
printf (" -json: import from DWG, export as JSON\n");
|
||||
printf (" -geojson: import from DWG, export as GeoJSON\n");
|
||||
#endif
|
||||
printf ("\n"
|
||||
" --version display the version and exit\n");
|
||||
printf (" --help display this help and exit\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// for LLVMFuzzerTestOneInput see llvmfuzz.c
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
int i = 0;
|
||||
Dwg_Data dwg;
|
||||
Bit_Chain dat = { 0 };
|
||||
Bit_Chain out_dat = { 0 };
|
||||
FILE *fp;
|
||||
struct stat attrib;
|
||||
enum
|
||||
{
|
||||
INVALID,
|
||||
DWG,
|
||||
#if defined(USE_WRITE) && !defined(DISABLE_DXF)
|
||||
INDXF,
|
||||
#endif
|
||||
#if defined(USE_WRITE) && !defined(DISABLE_JSON)
|
||||
INJSON,
|
||||
#endif
|
||||
#if defined(USE_WRITE)
|
||||
RW,
|
||||
ADD,
|
||||
#endif
|
||||
#if !defined(DISABLE_DXF)
|
||||
DXF,
|
||||
DXFB,
|
||||
#endif
|
||||
#if !defined(DISABLE_JSON)
|
||||
JSON,
|
||||
GEOJSON,
|
||||
#endif
|
||||
} mode
|
||||
= INVALID;
|
||||
if (argc <= 1 || !*argv[1])
|
||||
return 1;
|
||||
if (strEQc (argv[1], "--version"))
|
||||
return version ();
|
||||
if (strEQc (argv[1], "--help"))
|
||||
return help ();
|
||||
|
||||
__AFL_INIT ();
|
||||
if (strEQc (argv[1], "-dwg"))
|
||||
mode = DWG;
|
||||
#ifdef USE_WRITE
|
||||
# ifndef DISABLE_DXF
|
||||
else if (strEQc (argv[1], "-indxf"))
|
||||
mode = INDXF;
|
||||
# endif
|
||||
# ifndef DISABLE_JSON
|
||||
else if (strEQc (argv[1], "-injson"))
|
||||
mode = INJSON;
|
||||
# endif
|
||||
else if (strEQc (argv[1], "-rw"))
|
||||
mode = RW;
|
||||
else if (strEQc (argv[1], "-add"))
|
||||
mode = ADD;
|
||||
#endif /* USE_WRITE */
|
||||
#ifndef DISABLE_DXF
|
||||
else if (strEQc (argv[1], "-dxf"))
|
||||
mode = DXF;
|
||||
else if (strEQc (argv[1], "-dxfb"))
|
||||
mode = DXFB;
|
||||
#endif
|
||||
#ifndef DISABLE_JSON
|
||||
else if (strEQc (argv[1], "-json"))
|
||||
mode = JSON;
|
||||
else if (strEQc (argv[1], "-geojson"))
|
||||
mode = GEOJSON;
|
||||
#endif
|
||||
else
|
||||
return 1;
|
||||
if (mode == INVALID)
|
||||
return 1;
|
||||
#if FUZZ_MODE == FUZZ_FILE
|
||||
if (argc <= 2 || !*argv[2])
|
||||
return 1;
|
||||
#endif
|
||||
#if FUZZ_MODE == FUZZ_INMEM && !defined(__AFL_COMPILER)
|
||||
if (argc <= 2 || !*argv[2])
|
||||
return 1;
|
||||
#endif
|
||||
|
||||
memset (&dwg, 0, sizeof (dwg));
|
||||
dat.chain = NULL;
|
||||
// dat.opts = 3;;
|
||||
#if FUZZ_MODE == FUZZ_INMEM
|
||||
dat.chain = __AFL_FUZZ_TESTCASE_BUF;
|
||||
#endif
|
||||
|
||||
while (__AFL_LOOP (10000))
|
||||
{ // llvm_mode persistent, non-forking mode
|
||||
i++;
|
||||
#ifndef __AFL_COMPILER
|
||||
# ifdef USE_WRITE
|
||||
if (mode == ADD && i > 1)
|
||||
break;
|
||||
# endif
|
||||
if (i > 3)
|
||||
break;
|
||||
#endif
|
||||
#if FUZZ_MODE == FUZZ_INMEM
|
||||
// fastest mode via shared mem
|
||||
if (!dat.chain)
|
||||
dat.chain = __AFL_FUZZ_TESTCASE_BUF;
|
||||
dat.size = __AFL_FUZZ_TESTCASE_LEN;
|
||||
printf ("Fuzzing from shared memory (%" PRIuSIZE ")\n", dat.size);
|
||||
#elif FUZZ_MODE == FUZZ_STDIN
|
||||
// still 10x faster than the old file-forking fuzzer.
|
||||
dat.size = 0;
|
||||
// dat.chain = NULL;
|
||||
dat_read_stream (&dat, stdin);
|
||||
printf ("Fuzzing from stdin (%" PRIuSIZE ")\n", dat.size);
|
||||
#elif FUZZ_MODE == FUZZ_FILE
|
||||
dat.size = 0;
|
||||
fp = fopen (argv[2], "rb");
|
||||
if (!fp)
|
||||
return 0;
|
||||
dat_read_file (&dat, fp, argv[2]);
|
||||
fclose (fp);
|
||||
printf ("Fuzzing from file (%" PRIuSIZE ")\n", dat.size);
|
||||
#else
|
||||
# error Missing FUZZ_MODE
|
||||
#endif
|
||||
if (dat.size == 0)
|
||||
exit (1);
|
||||
if (dat.size < 100)
|
||||
continue; // useful minimum input length
|
||||
memset (&out_dat, 0, sizeof (out_dat));
|
||||
bit_chain_set_version (&out_dat, &dat);
|
||||
#ifdef _WIN32
|
||||
out_dat.fh = fopen ("NUL", "w");
|
||||
#else
|
||||
out_dat.fh = fopen ("/dev/null", "w");
|
||||
#endif
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case DWG:
|
||||
if (dwg_decode (&dat, &dwg) >= DWG_ERR_CRITICAL)
|
||||
exit (0);
|
||||
break;
|
||||
#if defined(USE_WRITE) && !defined(DISABLE_DXF)
|
||||
case INDXF:
|
||||
if (dwg_read_dxf (&dat, &dwg) < DWG_ERR_CRITICAL)
|
||||
{
|
||||
if (dat.version == R_2007)
|
||||
out_dat.version = R_2010;
|
||||
if (dwg_encode (&dwg, &out_dat) >= DWG_ERR_CRITICAL)
|
||||
exit (0);
|
||||
free (out_dat.chain);
|
||||
out_dat.chain = NULL;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#if defined(USE_WRITE)
|
||||
case RW:
|
||||
if (dwg_decode (&dat, &dwg) < DWG_ERR_CRITICAL)
|
||||
{
|
||||
if (dat.version == R_2007)
|
||||
out_dat.version = R_2010;
|
||||
if (dwg_encode (&dwg, &out_dat) >= DWG_ERR_CRITICAL)
|
||||
exit (0);
|
||||
if (dwg_decode (&out_dat, &dwg) >= DWG_ERR_CRITICAL)
|
||||
exit (0);
|
||||
free (out_dat.chain);
|
||||
out_dat.chain = NULL;
|
||||
}
|
||||
break;
|
||||
case ADD:
|
||||
{
|
||||
Dwg_Data *dwgp = &dwg;
|
||||
if (dwg_fuzz_dat (&dwgp, &dat) == 0)
|
||||
{
|
||||
int error;
|
||||
out_dat.byte = 0;
|
||||
out_dat.bit = 0;
|
||||
out_dat.from_version = dwg.header.from_version;
|
||||
out_dat.version = dwg.header.version;
|
||||
out_dat.opts = dwg.opts;
|
||||
error = dwg_encode (&dwg, &out_dat); // dwg -> out_dat
|
||||
if (error >= DWG_ERR_CRITICAL)
|
||||
exit (0);
|
||||
|
||||
out_dat.byte = 0;
|
||||
out_dat.bit = 0;
|
||||
memset (&dwg, 0, sizeof (Dwg_Data));
|
||||
error = dwg_decode (&out_dat, &dwg); // out_dat -> dwg
|
||||
if (error >= DWG_ERR_CRITICAL)
|
||||
exit (0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#if defined(USE_WRITE) && !defined(DISABLE_JSON)
|
||||
case INJSON:
|
||||
if (dwg_read_json (&dat, &dwg) < DWG_ERR_CRITICAL)
|
||||
{
|
||||
if (dat.version == R_2007)
|
||||
out_dat.version = R_2010;
|
||||
if (dwg_encode (&dwg, &out_dat) >= DWG_ERR_CRITICAL)
|
||||
exit (0);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#ifndef DISABLE_DXF
|
||||
case DXF:
|
||||
if (dwg_decode (&dat, &dwg) < DWG_ERR_CRITICAL)
|
||||
{
|
||||
if (dwg_write_dxf (&out_dat, &dwg) >= DWG_ERR_CRITICAL)
|
||||
exit (0);
|
||||
}
|
||||
break;
|
||||
case DXFB:
|
||||
if (dwg_decode (&dat, &dwg) < DWG_ERR_CRITICAL)
|
||||
{
|
||||
if (dwg_write_dxfb (&out_dat, &dwg) >= DWG_ERR_CRITICAL)
|
||||
exit (0);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#ifndef DISABLE_JSON
|
||||
case JSON:
|
||||
if (dwg_decode (&dat, &dwg) < DWG_ERR_CRITICAL)
|
||||
{
|
||||
if (dwg_write_json (&out_dat, &dwg) >= DWG_ERR_CRITICAL)
|
||||
exit (0);
|
||||
}
|
||||
break;
|
||||
case GEOJSON:
|
||||
if (dwg_decode (&dat, &dwg) < DWG_ERR_CRITICAL)
|
||||
{
|
||||
if (dwg_write_geojson (&out_dat, &dwg) >= DWG_ERR_CRITICAL)
|
||||
exit (0);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case INVALID:
|
||||
default:
|
||||
exit (1);
|
||||
}
|
||||
free (out_dat.chain);
|
||||
fclose (out_dat.fh);
|
||||
}
|
||||
dwg_free (&dwg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(USE_WRITE)
|
||||
|
||||
static dwg_point_2d *
|
||||
scan_pts2d (unsigned num_pts, char **pp)
|
||||
{
|
||||
char *p = *pp;
|
||||
dwg_point_2d *pts;
|
||||
|
||||
p = strchr (p, '(');
|
||||
if (!p)
|
||||
return NULL;
|
||||
p++;
|
||||
if (num_pts > 5000)
|
||||
exit (0);
|
||||
pts = calloc (num_pts, 16);
|
||||
if (!pts)
|
||||
exit (0);
|
||||
for (unsigned i = 0; i < num_pts; i++)
|
||||
{
|
||||
if (sscanf (p, "(%lf %lf)", &pts[i].x, &pts[i].y))
|
||||
{
|
||||
p = strchr (p, ')');
|
||||
if (!p)
|
||||
break;
|
||||
p++;
|
||||
}
|
||||
else
|
||||
{
|
||||
*pp = p;
|
||||
p = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (p)
|
||||
{
|
||||
*pp = p;
|
||||
return pts;
|
||||
}
|
||||
else
|
||||
{
|
||||
free (pts);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static dwg_point_3d *
|
||||
scan_pts3d (unsigned num_pts, char **pp)
|
||||
{
|
||||
char *p = *pp;
|
||||
dwg_point_3d *pts;
|
||||
|
||||
p = strchr (p, '(');
|
||||
if (!p)
|
||||
return NULL;
|
||||
p++;
|
||||
if (num_pts > 5000)
|
||||
exit (0);
|
||||
pts = calloc (num_pts, 24);
|
||||
if (!pts)
|
||||
exit (0);
|
||||
for (unsigned i = 0; i < num_pts; i++)
|
||||
{
|
||||
if (sscanf (p, "(%lf %lf %lf)", &pts[i].x, &pts[i].y, &pts[i].z))
|
||||
{
|
||||
p = strchr (p, ')');
|
||||
if (!p)
|
||||
break;
|
||||
p++;
|
||||
}
|
||||
else
|
||||
{
|
||||
*pp = p;
|
||||
p = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (p && num_pts)
|
||||
{
|
||||
*pp = p;
|
||||
return pts;
|
||||
}
|
||||
else
|
||||
{
|
||||
free (pts);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static char *
|
||||
next_line (char *restrict p, const char *restrict end)
|
||||
{
|
||||
while (p < end && *p != '\n')
|
||||
p++;
|
||||
if (p < end)
|
||||
p++;
|
||||
return p;
|
||||
}
|
||||
|
||||
int
|
||||
dwg_fuzz_dat (Dwg_Data **restrict dwgp, Bit_Chain *restrict dat)
|
||||
{
|
||||
Dwg_Data *dwg;
|
||||
Dwg_Object *mspace;
|
||||
Dwg_Object_Ref *mspace_ref;
|
||||
Dwg_Object_BLOCK_HEADER *hdr;
|
||||
const char *end;
|
||||
char *p;
|
||||
Dwg_Version_Type version = R_INVALID;
|
||||
int i = 0;
|
||||
int imperial = 0;
|
||||
BITCODE_BL orig_num;
|
||||
|
||||
if (!dat->chain)
|
||||
abort ();
|
||||
end = (char *)&dat->chain[dat->size - 1];
|
||||
if ((p = strstr ((char *)dat->chain, "\nimperial\n")))
|
||||
{
|
||||
imperial = 1;
|
||||
p += strlen ("\nimperial\n");
|
||||
}
|
||||
if ((p = strstr ((char *)dat->chain, "version")))
|
||||
{
|
||||
int i_ver;
|
||||
char s_ver[16];
|
||||
i = sscanf (p, "version %d", &i_ver);
|
||||
if (i)
|
||||
{
|
||||
snprintf (s_ver, 16, "r%d", i_ver);
|
||||
s_ver[15] = '\0';
|
||||
version = dwg_version_as (s_ver);
|
||||
p += strlen ("version ");
|
||||
}
|
||||
else
|
||||
p += strlen ("version ");
|
||||
p = next_line (p, end);
|
||||
}
|
||||
if (!i || version >= R_AFTER)
|
||||
version = R_2000;
|
||||
|
||||
dwg = dwg_new_Document (version, imperial, 0);
|
||||
*dwgp = dwg;
|
||||
mspace = dwg_model_space_object (dwg);
|
||||
mspace_ref = dwg_model_space_ref (dwg);
|
||||
hdr = mspace->tio.object->tio.BLOCK_HEADER;
|
||||
orig_num = dwg->num_objects;
|
||||
|
||||
// read dat line by line and call the matching add API
|
||||
while (p && p < end)
|
||||
{
|
||||
char text[120], s1[120];
|
||||
dwg_point_2d p2, p3, p4;
|
||||
dwg_point_3d pt1, pt2, pt3, pt4, scale;
|
||||
double height, rot, len, f1, f2;
|
||||
int i1, i2;
|
||||
unsigned u;
|
||||
Dwg_Entity_VIEWPORT *viewport = NULL;
|
||||
Dwg_Entity_MTEXT *mtext = NULL;
|
||||
Dwg_Object_DICTIONARY *dictionary = NULL;
|
||||
Dwg_Object_XRECORD *xrecord = NULL;
|
||||
Dwg_Object_MLINESTYLE *mlinestyle = NULL;
|
||||
Dwg_Object_DIMSTYLE *dimstyle = NULL;
|
||||
Dwg_Object_UCS *ucs = NULL;
|
||||
Dwg_Object_LAYOUT *layout = NULL;
|
||||
|
||||
// accepts only ASCII strings, for fuzzing only
|
||||
# ifdef HAVE_SSCANF_S
|
||||
# define SSCANF_S sscanf_s
|
||||
# define SZ , 119
|
||||
# define FMT_NAME "%[a-zA-Z0-9_]"
|
||||
# define FMT_TBL "\"%[a-zA-Z0-9._ -]\""
|
||||
# define FMT_PATH "\"%[a-zA-Z0-9_. \\-]\""
|
||||
# define FMT_ANY "\"%s\""
|
||||
# else
|
||||
# define SSCANF_S sscanf
|
||||
# define SZ
|
||||
# define FMT_NAME "%119[a-zA-Z0-9_]"
|
||||
# define FMT_TBL "\"%119[a-zA-Z0-9._ -]\""
|
||||
# define FMT_PATH "\"%119[a-zA-Z0-9_. \\-]\""
|
||||
# define FMT_ANY "\"%119s\""
|
||||
# endif
|
||||
|
||||
# define SET_ENT(var, name) \
|
||||
if (!var) \
|
||||
; \
|
||||
else if (SSCANF_S (p, #var "." FMT_NAME " = %d", &s1[0] SZ, &i1)) \
|
||||
dwg_dynapi_entity_set_value (var, #name, s1, &i1, 0); \
|
||||
else if (SSCANF_S (p, #var "." FMT_NAME " = %lf", &s1[0] SZ, &f1)) \
|
||||
dwg_dynapi_entity_set_value (var, #name, s1, &f1, 0); \
|
||||
else if (SSCANF_S (p, #var "." FMT_NAME " = " FMT_ANY, &s1[0] SZ, \
|
||||
&text[0] SZ)) \
|
||||
dwg_dynapi_entity_set_value (var, #name, s1, text, 1)
|
||||
|
||||
if (SSCANF_S (p, "line (%lf %lf %lf) (%lf %lf %lf)", &pt1.x, &pt1.y,
|
||||
&pt1.z, &pt2.x, &pt2.y, &pt2.z))
|
||||
dwg_add_LINE (hdr, &pt1, &pt2);
|
||||
else if (SSCANF_S (p, "ray (%lf %lf %lf) (%lf %lf %lf)", &pt1.x, &pt1.y,
|
||||
&pt1.z, &pt2.x, &pt2.y, &pt2.z))
|
||||
dwg_add_RAY (hdr, &pt1, &pt2);
|
||||
else if (SSCANF_S (p, "xline (%lf %lf %lf) (%lf %lf %lf)", &pt1.x,
|
||||
&pt1.y, &pt1.z, &pt2.x, &pt2.y, &pt2.z))
|
||||
dwg_add_XLINE (hdr, &pt1, &pt2);
|
||||
else if (SSCANF_S (p, "text " FMT_ANY " (%lf %lf %lf) %lf", &text[0] SZ,
|
||||
&pt1.x, &pt1.y, &pt1.z, &height))
|
||||
dwg_add_TEXT (hdr, text, &pt1, height);
|
||||
else if (SSCANF_S (p, "mtext (%lf %lf %lf) %lf " FMT_ANY, &pt1.x, &pt1.y,
|
||||
&pt1.z, &height, &text[0] SZ))
|
||||
mtext = dwg_add_MTEXT (hdr, &pt1, height, text);
|
||||
else
|
||||
SET_ENT (mtext, MTEXT);
|
||||
else if (SSCANF_S (p, "block " FMT_TBL, &text[0] SZ))
|
||||
dwg_add_BLOCK (hdr, text);
|
||||
else if (memBEGINc (p, "endblk\n")) dwg_add_ENDBLK (hdr);
|
||||
else if (SSCANF_S (p, "insert (%lf %lf %lf) " FMT_TBL " %lf %lf %lf %lf",
|
||||
&pt1.x, &pt1.y, &pt1.z, &text[0] SZ, &scale.x,
|
||||
&scale.y, &scale.z, &rot))
|
||||
dwg_add_INSERT (hdr, &pt1, text, scale.x, scale.y, scale.z,
|
||||
deg2rad (rot));
|
||||
else if (SSCANF_S (p,
|
||||
"minsert (%lf %lf %lf) " FMT_TBL
|
||||
" %lf %lf %lf %lf %d %d "
|
||||
"%lf %lf",
|
||||
&pt1.x, &pt1.y, &pt1.z, &text[0] SZ, &scale.x,
|
||||
&scale.y, &scale.z, &rot, &i1, &i2, &f1, &f2))
|
||||
dwg_add_MINSERT (hdr, &pt1, text, scale.x, scale.y, scale.z,
|
||||
deg2rad (rot), i1, i2, f1, f2);
|
||||
else if (SSCANF_S (p, "point (%lf %lf %lf)", &pt1.x, &pt1.y, &pt1.z))
|
||||
dwg_add_POINT (hdr, &pt1);
|
||||
else if (SSCANF_S (p, "circle (%lf %lf %lf) %lf", &pt1.x, &pt1.y, &pt1.z,
|
||||
&f1)) dwg_add_CIRCLE (hdr, &pt1, f1);
|
||||
else if (SSCANF_S (p, "arc (%lf %lf %lf) %lf %lf %lf", &pt1.x, &pt1.y,
|
||||
&pt1.z, &f1, &f2, &height))
|
||||
dwg_add_ARC (hdr, &pt1, f1, f2, height);
|
||||
else if (SSCANF_S (p,
|
||||
"dimension_aligned (%lf %lf %lf) (%lf %lf %lf) (%lf "
|
||||
"%lf %lf)",
|
||||
&pt1.x, &pt1.y, &pt1.z, &pt2.x, &pt2.y, &pt2.z,
|
||||
&pt3.x, &pt3.y, &pt3.z))
|
||||
dwg_add_DIMENSION_ALIGNED (hdr, &pt1, &pt2, &pt3);
|
||||
else if (SSCANF_S (
|
||||
p,
|
||||
"dimension_linear (%lf %lf %lf) (%lf %lf %lf) (%lf %lf "
|
||||
"%lf) %lf",
|
||||
&pt1.x, &pt1.y, &pt1.z, &pt2.x, &pt2.y, &pt2.z, &pt3.x,
|
||||
&pt3.y, &pt3.z, &rot))
|
||||
dwg_add_DIMENSION_LINEAR (hdr, &pt1, &pt2, &pt3, deg2rad (rot));
|
||||
else if (SSCANF_S (
|
||||
p,
|
||||
"dimension_ang2ln (%lf %lf %lf) (%lf %lf %lf) (%lf %lf "
|
||||
"%lf) (%lf %lf %lf)",
|
||||
&pt1.x, &pt1.y, &pt1.z, &pt2.x, &pt2.y, &pt2.z, &pt3.x,
|
||||
&pt3.y, &pt3.z, &pt4.x, &pt4.y, &pt4.z))
|
||||
dwg_add_DIMENSION_ANG2LN (hdr, &pt1, &pt2, &pt3, &pt4);
|
||||
else if (SSCANF_S (
|
||||
p,
|
||||
"dimension_ang3pt (%lf %lf %lf) (%lf %lf %lf) (%lf %lf "
|
||||
"%lf) (%lf %lf %lf)",
|
||||
&pt1.x, &pt1.y, &pt1.z, &pt2.x, &pt2.y, &pt2.z, &pt3.x,
|
||||
&pt3.y, &pt3.z, &pt4.x, &pt4.y, &pt4.z))
|
||||
dwg_add_DIMENSION_ANG3PT (hdr, &pt1, &pt2, &pt3, &pt4);
|
||||
else if (SSCANF_S (p,
|
||||
"dimension_diameter (%lf %lf %lf) (%lf %lf %lf) %lf",
|
||||
&pt1.x, &pt1.y, &pt1.z, &pt2.x, &pt2.y, &pt2.z, &len))
|
||||
dwg_add_DIMENSION_DIAMETER (hdr, &pt1, &pt2, len);
|
||||
else if (SSCANF_S (p,
|
||||
"dimension_ordinate (%lf %lf %lf) (%lf %lf %lf) %d",
|
||||
&pt1.x, &pt1.y, &pt1.z, &pt2.x, &pt2.y, &pt2.z, &i1))
|
||||
dwg_add_DIMENSION_ORDINATE (hdr, &pt1, &pt2, i1 ? true : false);
|
||||
else if (SSCANF_S (p, "dimension_radius (%lf %lf %lf) (%lf %lf %lf) %lf",
|
||||
&pt1.x, &pt1.y, &pt1.z, &pt2.x, &pt2.y, &pt2.z, &len))
|
||||
dwg_add_DIMENSION_RADIUS (hdr, &pt1, &pt2, len);
|
||||
else if (SSCANF_S (p,
|
||||
"3dface (%lf %lf %lf) (%lf %lf %lf) (%lf %lf "
|
||||
"%lf) (%lf %lf %lf)",
|
||||
&pt1.x, &pt1.y, &pt1.z, &pt2.x, &pt2.y, &pt2.z,
|
||||
&pt3.x, &pt3.y, &pt3.z, &pt4.x, &pt4.y, &pt4.z))
|
||||
dwg_add_3DFACE (hdr, &pt1, &pt2, &pt3, &pt4);
|
||||
else if (SSCANF_S (p,
|
||||
"3dface (%lf %lf %lf) (%lf %lf %lf) (%lf %lf "
|
||||
"%lf)",
|
||||
&pt1.x, &pt1.y, &pt1.z, &pt2.x, &pt2.y, &pt2.z,
|
||||
&pt3.x, &pt3.y, &pt3.z))
|
||||
dwg_add_3DFACE (hdr, &pt1, &pt2, &pt3, NULL);
|
||||
else if (SSCANF_S (
|
||||
p, "solid (%lf %lf %lf) (%lf %lf) (%lf %lf) (%lf %lf)",
|
||||
&pt1.x, &pt1.y, &pt1.z, &p2.x, &p2.y, &p3.x, &p3.y, &p4.x,
|
||||
&p4.y)) dwg_add_SOLID (hdr, &pt1, &p2, &p3, &p4);
|
||||
else if (SSCANF_S (
|
||||
p, "trace (%lf %lf %lf) (%lf %lf) (%lf %lf) (%lf %lf)",
|
||||
&pt1.x, &pt1.y, &pt1.z, &p2.x, &p2.y, &p3.x, &p3.y, &p4.x,
|
||||
&p4.y)) dwg_add_TRACE (hdr, &pt1, &p2, &p3, &p4);
|
||||
else if (SSCANF_S (p, "polyline_2d %d ((%lf %lf)", &i1, &pt1.x, &pt1.y))
|
||||
{
|
||||
dwg_point_2d *pts = scan_pts2d (i1, &p);
|
||||
if (i1 && pts)
|
||||
{
|
||||
dwg_add_POLYLINE_2D (hdr, i1, pts);
|
||||
free (pts);
|
||||
}
|
||||
}
|
||||
else if (SSCANF_S (p, "polyline_3d %d ((%lf %lf %lf)", &i1, &pt1.x,
|
||||
&pt1.y, &pt1.z))
|
||||
{
|
||||
dwg_point_3d *pts = scan_pts3d (i1, &p);
|
||||
if (i1 && pts)
|
||||
{
|
||||
dwg_add_POLYLINE_3D (hdr, i1, pts);
|
||||
free (pts);
|
||||
}
|
||||
}
|
||||
else if (SSCANF_S (p, "polyline_mesh %d %d ((%lf %lf %lf)", &i1, &i2,
|
||||
&pt1.x, &pt1.y, &pt1.z))
|
||||
{
|
||||
dwg_point_3d *pts = scan_pts3d (i1 * i2, &p);
|
||||
if (i1 && i2 && pts)
|
||||
{
|
||||
dwg_add_POLYLINE_MESH (hdr, i1, i2, pts);
|
||||
free (pts);
|
||||
}
|
||||
}
|
||||
else if (SSCANF_S (p, "dictionary " FMT_TBL " " FMT_TBL " %u",
|
||||
&text[0] SZ, &s1[0] SZ, &u)) dictionary
|
||||
= dwg_add_DICTIONARY (dwg, text, s1, (unsigned long)u);
|
||||
else if (dictionary
|
||||
&& SSCANF_S (p, "xrecord dictionary " FMT_TBL, &text[0] SZ))
|
||||
xrecord
|
||||
= dwg_add_XRECORD (dictionary, text);
|
||||
else if (SSCANF_S (p, "shape " FMT_PATH " (%lf %lf %lf) %lf %lf",
|
||||
&text[0] SZ, &pt1.x, &pt1.y, &pt1.z, &scale.x, &rot))
|
||||
dwg_add_SHAPE (hdr, text, &pt1, scale.x, deg2rad (rot));
|
||||
else if (SSCANF_S (p, "viewport " FMT_TBL, &text[0] SZ)) viewport
|
||||
= dwg_add_VIEWPORT (hdr, text);
|
||||
else SET_ENT (viewport, VIEWPORT);
|
||||
else if (SSCANF_S (p, "ellipse (%lf %lf %lf) %lf %lf", &pt1.x, &pt1.y,
|
||||
&pt1.z, &f1, &f2))
|
||||
dwg_add_ELLIPSE (hdr, &pt1, f1, f2);
|
||||
else if (SSCANF_S (p, "spline %d ((%lf %lf %lf)", &i1, &pt1.x, &pt1.y,
|
||||
&pt1.z))
|
||||
{
|
||||
dwg_point_3d *fitpts = scan_pts3d (i1, &p);
|
||||
if (i1 && fitpts
|
||||
&& sscanf (p, ") (%lf %lf %lf) (%lf %lf %lf)", &pt2.x, &pt2.y,
|
||||
&pt2.z, &pt3.x, &pt3.y, &pt3.z))
|
||||
{
|
||||
dwg_add_SPLINE (hdr, i1, fitpts, &pt2, &pt3);
|
||||
}
|
||||
free (fitpts);
|
||||
}
|
||||
else if (mtext
|
||||
&& sscanf (p, "leader %d ((%lf %lf %lf)", &i1, &pt1.x, &pt1.y,
|
||||
&pt1.z))
|
||||
{
|
||||
dwg_point_3d *pts = scan_pts3d (i1, &p);
|
||||
if (i1 && pts && sscanf (p, ") mtext %d", &i2))
|
||||
{
|
||||
dwg_add_LEADER (hdr, i1, pts, mtext, i2);
|
||||
}
|
||||
free (pts);
|
||||
}
|
||||
else if (SSCANF_S (p,
|
||||
"tolerance " FMT_TBL " (%lf %lf %lf) (%lf %lf %lf)",
|
||||
&text[0] SZ, &pt1.x, &pt1.y, &pt1.z, &pt2.x, &pt2.y,
|
||||
&pt2.z)) dwg_add_TOLERANCE (hdr, text, &pt1, &pt2);
|
||||
else if (SSCANF_S (p, "mlinestyle " FMT_TBL, &text[0] SZ)) mlinestyle
|
||||
= dwg_add_MLINESTYLE (dwg, text);
|
||||
else if (SSCANF_S (p, "dimstyle " FMT_TBL, &text[0] SZ)) dimstyle
|
||||
= dwg_add_DIMSTYLE (dwg, text);
|
||||
else SET_ENT (mlinestyle, MLINESTYLE);
|
||||
else SET_ENT (dimstyle, DIMSTYLE);
|
||||
else if (SSCANF_S (
|
||||
p, "ucs (%lf %lf %lf) (%lf %lf %lf) (%lf %lf %lf) " FMT_TBL,
|
||||
&pt1.x, &pt1.y, &pt1.z, &pt2.x, &pt2.y, &pt2.z, &pt3.x,
|
||||
&pt3.y, &pt3.z, &text[0] SZ)) ucs
|
||||
= dwg_add_UCS (dwg, &pt1, &pt2, &pt3, text);
|
||||
else SET_ENT (ucs, UCS);
|
||||
else if (viewport
|
||||
&& SSCANF_S (p, "layout viewport " FMT_TBL " " FMT_ANY,
|
||||
&text[0] SZ, &s1[0] SZ))
|
||||
{
|
||||
int error;
|
||||
Dwg_Object *obj = dwg_ent_generic_to_object (viewport, &error);
|
||||
if (!error)
|
||||
layout = dwg_add_LAYOUT (obj, text, s1);
|
||||
}
|
||||
else if (SSCANF_S (p, "torus (%lf %lf %lf) (%lf %lf %lf) %lf %lf",
|
||||
&pt1.x, &pt1.y, &pt1.z, &pt2.x, &pt2.y, &pt2.z, &f1,
|
||||
&f2)) dwg_add_TORUS (hdr, &pt1, &pt2, f1, f2);
|
||||
else if (SSCANF_S (p, "sphere (%lf %lf %lf) (%lf %lf %lf) %lf", &pt1.x,
|
||||
&pt1.y, &pt1.z, &pt2.x, &pt2.y, &pt2.z, &f1))
|
||||
dwg_add_SPHERE (hdr, &pt1, &pt2, f1);
|
||||
else if (SSCANF_S (
|
||||
p, "cylinder (%lf %lf %lf) (%lf %lf %lf) %lf %lf %lf %lf",
|
||||
&pt1.x, &pt1.y, &pt1.z, &pt2.x, &pt2.y, &pt2.z, &height,
|
||||
&f1, &f2, &len))
|
||||
dwg_add_CYLINDER (hdr, &pt1, &pt2, height, f1, f2, len);
|
||||
else if (SSCANF_S (p, "cone (%lf %lf %lf) (%lf %lf %lf) %lf %lf %lf %lf",
|
||||
&pt1.x, &pt1.y, &pt1.z, &pt2.x, &pt2.y, &pt2.z,
|
||||
&height, &f1, &f2, &len))
|
||||
dwg_add_CONE (hdr, &pt1, &pt2, height, f1, f2, len);
|
||||
else if (SSCANF_S (p, "wedge (%lf %lf %lf) (%lf %lf %lf) %lf %lf %lf",
|
||||
&pt1.x, &pt1.y, &pt1.z, &pt2.x, &pt2.y, &pt2.z, &len,
|
||||
&f1, &height))
|
||||
dwg_add_WEDGE (hdr, &pt1, &pt2, len, f1, height);
|
||||
else if (SSCANF_S (p, "box (%lf %lf %lf) (%lf %lf %lf) %lf %lf %lf",
|
||||
&pt1.x, &pt1.y, &pt1.z, &pt2.x, &pt2.y, &pt2.z, &len,
|
||||
&f1, &height))
|
||||
dwg_add_BOX (hdr, &pt1, &pt2, len, f1, height);
|
||||
else if (SSCANF_S (p,
|
||||
"pyramid (%lf %lf %lf) (%lf %lf %lf) %lf %d %lf %lf",
|
||||
&pt1.x, &pt1.y, &pt1.z, &pt2.x, &pt2.y, &pt2.z,
|
||||
&height, &i1, &f1, &f2))
|
||||
dwg_add_PYRAMID (hdr, &pt1, &pt2, height, i1, f1, f2);
|
||||
else if (SSCANF_S (p, "HEADER." FMT_NAME " = %d", &s1[0] SZ, &i1))
|
||||
dwg_dynapi_header_set_value (dwg, s1, &i1, 0);
|
||||
else if (SSCANF_S (p, "HEADER." FMT_NAME " = %lf", &s1[0] SZ, &f1))
|
||||
dwg_dynapi_header_set_value (dwg, s1, &f1, 0);
|
||||
else if (SSCANF_S (p, "HEADER." FMT_NAME " = " FMT_ANY, &s1[0] SZ,
|
||||
&text[0] SZ))
|
||||
dwg_dynapi_header_set_value (dwg, s1, text, 1);
|
||||
|
||||
p = next_line (p, end);
|
||||
}
|
||||
// dwg_resolve_objectrefs_silent (orig_dwg);
|
||||
// start fuzzing if at least 2 entities were added.
|
||||
return (dwg->num_objects - orig_num > 2 ? 0 : 1);
|
||||
}
|
||||
|
||||
#endif // USE_WRITE
|
||||
Binary file not shown.
@@ -0,0 +1,356 @@
|
||||
/*****************************************************************************/
|
||||
/* LibreDWG - free implementation of the DWG file format */
|
||||
/* */
|
||||
/* Copyright (C) 2021, 2023 Free Software Foundation, Inc. */
|
||||
/* */
|
||||
/* This library is free software, licensed under the terms of the GNU */
|
||||
/* General Public License as published by the Free Software Foundation, */
|
||||
/* either version 3 of the License, or (at your option) any later version. */
|
||||
/* You should have received a copy of the GNU General Public License */
|
||||
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
/*****************************************************************************/
|
||||
|
||||
/*
|
||||
* llvmfuzz.c: libfuzzer testing, esp. for oss-fuzz. with libfuzzer or
|
||||
* standalone written by Reini Urban
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
// #include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "common.h"
|
||||
#include <dwg.h>
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
# include <sys/time.h>
|
||||
#endif
|
||||
#include "decode.h"
|
||||
#include "encode.h"
|
||||
#include "bits.h"
|
||||
#ifndef DISABLE_DXF
|
||||
# include "out_dxf.h"
|
||||
# ifndef DISABLE_JSON
|
||||
# include "in_json.h"
|
||||
# include "out_json.h"
|
||||
# endif
|
||||
# include "in_dxf.h"
|
||||
#endif
|
||||
|
||||
// Number of output converters selectable via out: 0 encode, 1 dxf, 2 dxfb, 3
|
||||
// json, 4 geojson
|
||||
#ifdef DISABLE_DXF
|
||||
# define LLVMFUZZ_NUM_OUTPUTS 1
|
||||
#else
|
||||
# ifdef DISABLE_JSON
|
||||
# define LLVMFUZZ_NUM_OUTPUTS 3
|
||||
# else
|
||||
# define LLVMFUZZ_NUM_OUTPUTS 5
|
||||
# endif
|
||||
#endif
|
||||
|
||||
int out;
|
||||
int ver;
|
||||
|
||||
extern int LLVMFuzzerTestOneInput (const unsigned char *data, size_t size);
|
||||
|
||||
// libfuzzer limitation:
|
||||
// Enforce NULL-termination of the input buffer, to avoid bogus reports. copy
|
||||
// it. Problematic is mostly strtol(3) which also works with \n termination.
|
||||
static int
|
||||
enforce_null_termination (Bit_Chain *dat, bool enforce)
|
||||
{
|
||||
unsigned char *copy;
|
||||
unsigned char c;
|
||||
if (!dat->size)
|
||||
return 0;
|
||||
c = dat->chain[dat->size - 1];
|
||||
// Allow \n termination without \0 in DXF? No, still crashes
|
||||
if (!enforce && ((c == '\n' && c + 1 == '\0') || c == '\0'))
|
||||
return 0;
|
||||
#ifdef STANDALONE
|
||||
fprintf (stderr,
|
||||
"llvmfuzz_standalone: enforce libfuzzer buffer NULL termination\n");
|
||||
#endif
|
||||
copy = malloc (dat->size + 1);
|
||||
memcpy (copy, dat->chain, dat->size);
|
||||
copy[dat->size] = '\0';
|
||||
dat->chain = copy;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
LLVMFuzzerTestOneInput (const unsigned char *data, size_t size)
|
||||
{
|
||||
Dwg_Data dwg;
|
||||
Bit_Chain dat = { NULL, 0, 0, 0, 0, 0, 0, NULL, 0 };
|
||||
Bit_Chain out_dat = { NULL, 0, 0, 0, 0, 0, 0, NULL, 0 };
|
||||
int copied = 0;
|
||||
struct ly_ctx *ctx = NULL;
|
||||
|
||||
static char tmp_file[256];
|
||||
|
||||
#ifndef STANDALONE
|
||||
/* The libfuzzer path otherwise leaves out/ver at 0, so only dwg_encode runs.
|
||||
Derive them from the input (without consuming it, so the existing corpus
|
||||
keeps decoding) to also drive the out_dxf/out_dxfb/out_json encoders. */
|
||||
out = 0;
|
||||
ver = 0;
|
||||
if (size)
|
||||
{
|
||||
unsigned int h = 2166136261u;
|
||||
const size_t n = size > 4096 ? 4096 : size;
|
||||
for (size_t i = 0; i < n; i++)
|
||||
h = (h ^ data[i]) * 16777619u;
|
||||
out = (int)(h % LLVMFUZZ_NUM_OUTPUTS);
|
||||
ver = (int)((h >> 8) % 20);
|
||||
}
|
||||
#endif
|
||||
|
||||
dat.chain = (unsigned char *)data;
|
||||
dat.size = size;
|
||||
memset (&dwg, 0, sizeof (dwg));
|
||||
|
||||
// Detect the input format: DWG, DXF or JSON
|
||||
if (dat.size > 2 && dat.chain[0] == 'A' && dat.chain[1] == 'C')
|
||||
{
|
||||
if (dwg_decode (&dat, &dwg) >= DWG_ERR_CRITICAL)
|
||||
{
|
||||
dwg_free (&dwg);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#ifndef DISABLE_JSON
|
||||
else if (dat.size > 1 && dat.chain[0] == '{')
|
||||
{
|
||||
copied = enforce_null_termination (&dat, true);
|
||||
if (dwg_read_json (&dat, &dwg) >= DWG_ERR_CRITICAL)
|
||||
{
|
||||
if (copied)
|
||||
bit_chain_free (&dat);
|
||||
dwg_free (&dwg);
|
||||
return 0;
|
||||
}
|
||||
dat.opts |= DWG_OPTS_INJSON;
|
||||
dwg.opts |= DWG_OPTS_INJSON;
|
||||
}
|
||||
#endif
|
||||
#ifndef DISABLE_DXF
|
||||
else
|
||||
{
|
||||
copied = enforce_null_termination (&dat, false);
|
||||
if (dwg_read_dxf (&dat, &dwg) >= DWG_ERR_CRITICAL)
|
||||
{
|
||||
if (copied)
|
||||
bit_chain_free (&dat);
|
||||
dwg_free (&dwg);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#else
|
||||
else
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
memset (&out_dat, 0, sizeof (out_dat));
|
||||
bit_chain_set_version (&out_dat, &dat);
|
||||
if (copied)
|
||||
bit_chain_free (&dat);
|
||||
|
||||
#if 0
|
||||
snprintf (tmp_file, 255, "/tmp/llvmfuzzer%d.out", getpid());
|
||||
tmp_file[255] = '\0';
|
||||
#elif defined _WIN32
|
||||
strcpy (tmp_file, "NUL");
|
||||
#else
|
||||
strcpy (tmp_file, "/dev/null");
|
||||
#endif
|
||||
out_dat.fh = fopen (tmp_file, "w");
|
||||
|
||||
switch (out)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
switch (ver)
|
||||
{
|
||||
// TODO support preR13, many downconverters still missing
|
||||
case 0:
|
||||
out_dat.version = dwg.header.version = R_1_4;
|
||||
break;
|
||||
case 1:
|
||||
out_dat.version = dwg.header.version = R_2_0;
|
||||
break;
|
||||
case 2:
|
||||
out_dat.version = dwg.header.version = R_2_10;
|
||||
break;
|
||||
case 3:
|
||||
out_dat.version = dwg.header.version = R_2_21;
|
||||
break;
|
||||
case 4:
|
||||
out_dat.version = dwg.header.version = R_2_4;
|
||||
break;
|
||||
case 5:
|
||||
out_dat.version = dwg.header.version = R_2_6;
|
||||
break;
|
||||
case 6:
|
||||
out_dat.version = dwg.header.version = R_9;
|
||||
break;
|
||||
case 7:
|
||||
out_dat.version = dwg.header.version = R_10;
|
||||
break;
|
||||
case 8:
|
||||
out_dat.version = dwg.header.version = R_11;
|
||||
break;
|
||||
case 9:
|
||||
out_dat.version = dwg.header.version = R_12;
|
||||
break;
|
||||
case 10:
|
||||
out_dat.version = dwg.header.version = R_13;
|
||||
break;
|
||||
case 11:
|
||||
out_dat.version = dwg.header.version = R_13c3;
|
||||
break;
|
||||
case 12:
|
||||
out_dat.version = dwg.header.version = R_14;
|
||||
break;
|
||||
case 13:
|
||||
out_dat.version = dwg.header.version = R_2004;
|
||||
break;
|
||||
default: // favor this one
|
||||
out_dat.version = dwg.header.version = R_2000;
|
||||
break;
|
||||
}
|
||||
dwg_encode (&dwg, &out_dat);
|
||||
break;
|
||||
}
|
||||
#ifndef DISABLE_DXF
|
||||
case 1:
|
||||
dwg_write_dxf (&out_dat, &dwg);
|
||||
break;
|
||||
case 2: // experimental
|
||||
dwg_write_dxfb (&out_dat, &dwg);
|
||||
break;
|
||||
# ifndef DISABLE_JSON
|
||||
case 3:
|
||||
dwg_write_json (&out_dat, &dwg);
|
||||
break;
|
||||
case 4:
|
||||
dwg_write_geojson (&out_dat, &dwg);
|
||||
break;
|
||||
# endif
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
dwg_free (&dwg);
|
||||
free (out_dat.chain);
|
||||
fclose (out_dat.fh);
|
||||
// unlink (tmp_file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef STANDALONE
|
||||
/*
|
||||
# ifdef __GNUC__
|
||||
__attribute__((weak))
|
||||
# endif
|
||||
extern int LLVMFuzzerInitialize(int *argc, char ***argv);
|
||||
*/
|
||||
|
||||
static int
|
||||
usage (void)
|
||||
{
|
||||
printf ("\nUsage: OUT=0 VER=3 llvmfuzz_standalone INPUT...");
|
||||
return 1;
|
||||
}
|
||||
// llvmfuzz_standalone reproducer, see OUT and VER env vars
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
unsigned seed;
|
||||
const unsigned int possible_outputformats = LLVMFUZZ_NUM_OUTPUTS;
|
||||
|
||||
if (argc <= 1 || !*argv[1])
|
||||
return usage ();
|
||||
if (getenv ("SEED"))
|
||||
seed = (unsigned)strtol (getenv ("SEED"), NULL, 10) % 9999;
|
||||
else
|
||||
{
|
||||
# ifdef HAVE_GETTIMEOFDAY
|
||||
struct timeval tval;
|
||||
gettimeofday (&tval, NULL);
|
||||
seed = (unsigned)(tval.tv_sec * 1000 + tval.tv_usec) % 9999;
|
||||
# else
|
||||
seed = (unsigned)time (NULL) % 9999;
|
||||
# endif
|
||||
}
|
||||
srand (seed);
|
||||
/* works only on linux
|
||||
if (LLVMFuzzerInitialize)
|
||||
LLVMFuzzerInitialize (&argc, &argv);
|
||||
*/
|
||||
for (int i = 1; i < argc; i++)
|
||||
{
|
||||
unsigned char *buf;
|
||||
FILE *f = fopen (argv[i], "rb");
|
||||
struct stat attrib;
|
||||
long len;
|
||||
size_t n_read;
|
||||
int fd;
|
||||
if (!f)
|
||||
{
|
||||
fprintf (stderr, "Illegal file argument %s\n", argv[i]);
|
||||
continue;
|
||||
}
|
||||
fd = fileno (f);
|
||||
if (fd < 0 || fstat (fd, &attrib)
|
||||
|| !(S_ISREG (attrib.st_mode)
|
||||
# ifndef _WIN32
|
||||
|| S_ISLNK (attrib.st_mode)
|
||||
# endif
|
||||
))
|
||||
{
|
||||
fprintf (stderr, "Illegal input file \"%s\"\n", argv[i]);
|
||||
continue;
|
||||
}
|
||||
// libFuzzer design bug, not zero-terminating its text buffer
|
||||
fseek (f, 0, SEEK_END);
|
||||
len = ftell (f);
|
||||
fseek (f, 0, SEEK_SET);
|
||||
if (len <= 0)
|
||||
continue;
|
||||
buf = (unsigned char *)malloc (len);
|
||||
n_read = fread (buf, 1, len, f);
|
||||
fclose (f);
|
||||
assert ((long)n_read == len);
|
||||
|
||||
out = rand () % possible_outputformats;
|
||||
# ifdef STANDALONE
|
||||
if (getenv ("OUT"))
|
||||
out = strtol (getenv ("OUT"), NULL, 10);
|
||||
// print SEED onlyu when needed (no env vars given)
|
||||
if (!(out || getenv ("VER")))
|
||||
fprintf (stderr, "SEED=%04u ", seed);
|
||||
fprintf (stderr, "OUT=%d ", out);
|
||||
# endif
|
||||
if (out == 0)
|
||||
{
|
||||
ver = rand () % 20;
|
||||
# ifdef STANDALONE
|
||||
if (getenv ("VER"))
|
||||
ver = strtol (getenv ("VER"), NULL, 10);
|
||||
fprintf (stderr, "VER=%d ", ver);
|
||||
# endif
|
||||
}
|
||||
fprintf (stderr, "examples/llvmfuzz_standalone %s [%" PRIuSIZE "]\n",
|
||||
argv[i], len);
|
||||
LLVMFuzzerTestOneInput (buf, len);
|
||||
free (buf);
|
||||
// Bit_Chain dat = { 0 };
|
||||
// dat_read_file (&dat, fp, argv[i]);
|
||||
// LLVMFuzzerTestOneInput (dat.chain, dat.size);
|
||||
// bit_free_chain (&dat);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,230 @@
|
||||
/*****************************************************************************/
|
||||
/* LibreDWG - free implementation of the DWG file format */
|
||||
/* */
|
||||
/* Copyright (C) 2009, 2023 Free Software Foundation, Inc. */
|
||||
/* Copyright (C) 2010 Thien-Thi Nguyen */
|
||||
/* */
|
||||
/* This library is free software, licensed under the terms of the GNU */
|
||||
/* General Public License as published by the Free Software Foundation, */
|
||||
/* either version 3 of the License, or (at your option) any later version. */
|
||||
/* You should have received a copy of the GNU General Public License */
|
||||
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
/*****************************************************************************/
|
||||
|
||||
/*
|
||||
* load_dwg.c: load a DWG, get lines, text and circles
|
||||
* written by Felipe Castro
|
||||
* modified by Felipe Corrêa da Silva Sances
|
||||
* modified by Thien-Thi Nguyen
|
||||
* modified by Reini Urban
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#ifdef __STDC_ALLOC_LIB__ /* for strdup */
|
||||
# define __STDC_WANT_LIB_EXT2__ 1 /* for strdup */
|
||||
#else
|
||||
# define _USE_BSD 1
|
||||
#endif
|
||||
#ifndef _XOPEN_SOURCE /* for strdup, snprintf */
|
||||
# define _XOPEN_SOURCE 700
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#ifndef DISABLE_WRITE
|
||||
# include <sys/stat.h>
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#include "dwg.h"
|
||||
#include "dwg_api.h"
|
||||
#include "../src/bits.h"
|
||||
|
||||
#include "../programs/suffix.inc"
|
||||
static int help (void);
|
||||
int verbosity (int argc, char **argv, int i, unsigned int *opts);
|
||||
#include "../programs/common.inc"
|
||||
|
||||
static int
|
||||
usage (void)
|
||||
{
|
||||
printf ("\nUsage: load_dwg [-v[0-9]] DWGFILE\n");
|
||||
return 1;
|
||||
}
|
||||
static int
|
||||
opt_version (void)
|
||||
{
|
||||
printf ("load_dwg %s\n", PACKAGE_VERSION);
|
||||
return 0;
|
||||
}
|
||||
static int
|
||||
help (void)
|
||||
{
|
||||
printf ("\nUsage: load_dwg [OPTION]... DWGFILE\n");
|
||||
printf ("Example to add fingerprint elements to a DWG.\n"
|
||||
"\n");
|
||||
printf (" -v[0-9], --verbose [0-9] verbosity\n");
|
||||
printf (" --help display this help and exit\n");
|
||||
printf (" --version output version information and exit\n"
|
||||
"\n");
|
||||
printf ("GNU LibreDWG online manual: "
|
||||
"<https://www.gnu.org/software/libredwg/>\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
add_fingerprint (Dwg_Data *dwg, dwg_point_3d *pt)
|
||||
{
|
||||
int error = 0;
|
||||
char text[128];
|
||||
double height = dwg->header_vars.TEXTSIZE;
|
||||
time_t t = time (NULL);
|
||||
Dwg_Object_BLOCK_HEADER *hdr = dwg_get_block_header (dwg, &error);
|
||||
Dwg_Entity_TEXT *ent;
|
||||
if (error)
|
||||
return 1;
|
||||
|
||||
#ifdef HAVE_WFORMAT_Y2K
|
||||
GCC46_DIAG_IGNORE (-Wformat-y2k)
|
||||
#endif
|
||||
strftime (text, sizeof (text), "Last updated: %c", localtime (&t));
|
||||
#ifdef HAVE_WFORMAT_Y2K
|
||||
GCC46_DIAG_RESTORE
|
||||
#endif
|
||||
|
||||
#ifdef USE_WRITE
|
||||
if ((ent = dwg_add_TEXT (hdr, text, pt, height)))
|
||||
{
|
||||
ent->horiz_alignment = HORIZ_ALIGNMENT_RIGHT;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
change_fingerprint (Dwg_Data *dwg, Dwg_Entity_TEXT *_obj)
|
||||
{
|
||||
char text[128];
|
||||
double height = dwg->header_vars.TEXTSIZE;
|
||||
time_t t = time (NULL);
|
||||
|
||||
#ifdef HAVE_WFORMAT_Y2K
|
||||
GCC46_DIAG_IGNORE (-Wformat-y2k)
|
||||
#endif
|
||||
strftime (text, sizeof (text), "Last updated: %c", localtime (&t));
|
||||
#ifdef HAVE_WFORMAT_Y2K
|
||||
GCC46_DIAG_RESTORE
|
||||
#endif
|
||||
|
||||
if (dwg->header.version < R_2007)
|
||||
{
|
||||
if (strlen (text) < strlen (_obj->text_value))
|
||||
strcpy (_obj->text_value, text);
|
||||
else
|
||||
{
|
||||
free (_obj->text_value);
|
||||
_obj->text_value = strdup (text);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
free (_obj->text_value);
|
||||
_obj->text_value = (BITCODE_TV)bit_utf8_to_TU (text, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
load_dwg (char *filename, unsigned int opts)
|
||||
{
|
||||
BITCODE_BL i;
|
||||
int success, found = 0;
|
||||
Dwg_Data dwg;
|
||||
dwg_point_3d pt;
|
||||
|
||||
#ifdef USE_WRITE
|
||||
char *new_filename = (char *)malloc (strlen (filename) + 4);
|
||||
char *fn = strdup (filename);
|
||||
char *base = basename (fn);
|
||||
char *p;
|
||||
struct stat st;
|
||||
|
||||
if ((p = strrchr (base, '.')))
|
||||
*p = '\0';
|
||||
sprintf (new_filename, "%s_new.dwg", base);
|
||||
free (fn);
|
||||
#endif
|
||||
|
||||
memset (&dwg, 0, sizeof (Dwg_Data));
|
||||
dwg.opts = opts;
|
||||
success = dwg_read_file (filename, &dwg);
|
||||
// get the insertion point for our fingerprint
|
||||
pt.x = dwg.header_vars.LIMMAX.x;
|
||||
pt.y = dwg.header_vars.LIMMAX.y;
|
||||
pt.z = 0.0;
|
||||
|
||||
// check if a fingerprint already exists there.
|
||||
// if so update it. if not add it
|
||||
for (i = 0; i < dwg.num_objects; i++)
|
||||
{
|
||||
if (dwg.object[i].fixedtype == DWG_TYPE_TEXT)
|
||||
{
|
||||
Dwg_Entity_TEXT *_obj = dwg.object[i].tio.entity->tio.TEXT;
|
||||
if (pt.x == _obj->ins_pt.x && pt.y == _obj->ins_pt.y
|
||||
&& _obj->horiz_alignment == HORIZ_ALIGNMENT_RIGHT)
|
||||
{
|
||||
found++;
|
||||
change_fingerprint (&dwg, _obj);
|
||||
fprintf (stderr, "fingerprint updated at (%f, %f)\n", pt.x,
|
||||
pt.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
add_fingerprint (&dwg, &pt);
|
||||
fprintf (stderr, "fingerprint added at (%f, %f)\n", pt.x, pt.y);
|
||||
}
|
||||
|
||||
#ifdef USE_WRITE
|
||||
if (0 == stat (new_filename, &st))
|
||||
unlink (new_filename);
|
||||
if (dwg.header.version > R_2000)
|
||||
dwg.header.version = R_2000;
|
||||
success = dwg_write_file (new_filename, &dwg);
|
||||
free (new_filename);
|
||||
#endif
|
||||
|
||||
dwg_free (&dwg);
|
||||
return success;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
int i = 1;
|
||||
unsigned int opts = 0;
|
||||
|
||||
if (argc < 2)
|
||||
return usage ();
|
||||
#if defined(USE_TRACING) && defined(HAVE_SETENV)
|
||||
setenv ("LIBREDWG_TRACE", "1", 0);
|
||||
#endif
|
||||
if (argc > 2
|
||||
&& (!strcmp (argv[i], "--verbose") || !strncmp (argv[i], "-v", 2)))
|
||||
{
|
||||
int num_args = verbosity (argc, argv, i, &opts);
|
||||
argc -= num_args;
|
||||
i += num_args;
|
||||
}
|
||||
if (argc > 1 && !strcmp (argv[i], "--help"))
|
||||
return help ();
|
||||
if (argc > 1 && !strcmp (argv[i], "--version"))
|
||||
return opt_version ();
|
||||
|
||||
REQUIRE_INPUT_FILE_ARG (argc);
|
||||
load_dwg (argv[i], opts);
|
||||
return 0;
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,76 @@
|
||||
/*****************************************************************************/
|
||||
/* LibreDWG - free implementation of the DWG file format */
|
||||
/* */
|
||||
/* Copyright (C) 2023 Free Software Foundation, Inc. */
|
||||
/* */
|
||||
/* This library is free software, licensed under the terms of the GNU */
|
||||
/* General Public License as published by the Free Software Foundation, */
|
||||
/* either version 3 of the License, or (at your option) any later version. */
|
||||
/* You should have received a copy of the GNU General Public License */
|
||||
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
/*****************************************************************************/
|
||||
|
||||
/*
|
||||
* odaversion.c: prints the ODA compatible DWG version
|
||||
* modified by Reini Urban
|
||||
oda understands those versions:
|
||||
"ACAD9","ACAD10","ACAD12",
|
||||
"ACAD13","ACAD14",
|
||||
"ACAD2000","ACAD2004",
|
||||
"ACAD2007","ACAD2010",
|
||||
"ACAD2013","ACAD2018"
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
FILE *f;
|
||||
char buf[7];
|
||||
size_t size;
|
||||
|
||||
if (argc != 2)
|
||||
return 1;
|
||||
f = fopen (argv[1], "rb");
|
||||
if (!f)
|
||||
return 1;
|
||||
size = fread (buf, 1, 6, f);
|
||||
if (size != 6)
|
||||
return 1;
|
||||
buf[6] = '\0';
|
||||
if (buf[0] == 'A' && buf[1] == 'C')
|
||||
{
|
||||
int v;
|
||||
if (1 != sscanf (buf, "AC%4d", &v))
|
||||
return 1;
|
||||
if (v < 1004) // <r9
|
||||
printf ("\n");
|
||||
else if (v <= 1005)
|
||||
printf ("9\n");
|
||||
else if (v <= 1006)
|
||||
printf ("10\n");
|
||||
else if (v <= 1009)
|
||||
printf ("11\n");
|
||||
else if (v <= 1012)
|
||||
printf ("13\n");
|
||||
else if (v <= 1014)
|
||||
printf ("14\n");
|
||||
else if (v <= 1016)
|
||||
printf ("2000\n");
|
||||
else if (v <= 1018)
|
||||
printf ("2004\n");
|
||||
else if (v <= 1021)
|
||||
printf ("2007\n");
|
||||
else if (v <= 1024)
|
||||
printf ("2010\n");
|
||||
else if (v < 1028)
|
||||
printf ("2013\n");
|
||||
else if (v < 1033)
|
||||
printf ("2018\n");
|
||||
else
|
||||
printf ("\n");
|
||||
}
|
||||
fclose (f);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
unsigned char block[255];
|
||||
|
||||
main ()
|
||||
{
|
||||
int len, err;
|
||||
while (!feof (stdin))
|
||||
{
|
||||
len = fread (block, 1, 255, stdin);
|
||||
err = rs_decode_block (block, 1);
|
||||
fwrite (block, 1, 239, stdout);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
unsigned char data[239];
|
||||
unsigned char parity[16];
|
||||
|
||||
main ()
|
||||
{
|
||||
int len, i;
|
||||
while (!feof (stdin))
|
||||
{
|
||||
len = fread (data, 1, 239, stdin);
|
||||
rs_encode_block (parity, data, len);
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
fputc ('-', stderr);
|
||||
}
|
||||
|
||||
fwrite (data, 1, len, stdout);
|
||||
|
||||
while (len++ < 239)
|
||||
fputc (0, stdout); /* Pad data with 0s */
|
||||
|
||||
fwrite (parity, 16, 1, stdout);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
Binary file not shown.
@@ -0,0 +1,41 @@
|
||||
# libredwg.la - a libtool library file
|
||||
# Generated by libtool (GNU libtool) 2.5.4
|
||||
#
|
||||
# Please DO NOT delete this file!
|
||||
# It is necessary for linking the library.
|
||||
|
||||
# The name that we can dlopen(3).
|
||||
dlname='../bin/libredwg-0.dll'
|
||||
|
||||
# Names of this library.
|
||||
library_names='libredwg.dll.a'
|
||||
|
||||
# The name of the static archive.
|
||||
old_library=''
|
||||
|
||||
# Linker flags that cannot go in dependency_libs.
|
||||
inherited_linker_flags=''
|
||||
|
||||
# Libraries that this one depends upon.
|
||||
dependency_libs=''
|
||||
|
||||
# Names of additional weak libraries provided by this library
|
||||
weak_library_names=''
|
||||
|
||||
# Version information for libredwg.
|
||||
current=0
|
||||
age=0
|
||||
revision=14
|
||||
|
||||
# Is this an already installed library?
|
||||
installed=yes
|
||||
|
||||
# Should we warn about portability when linking against -modules?
|
||||
shouldnotlink=no
|
||||
|
||||
# Files to dlopen/dlpreopen
|
||||
dlopen=''
|
||||
dlpreopen=''
|
||||
|
||||
# Directory that this library needs to be installed in:
|
||||
libdir='/lib'
|
||||
@@ -0,0 +1,12 @@
|
||||
# libredwg.pc. Generated from libredwg.pc.in by configure.
|
||||
prefix=
|
||||
exec_prefix=${prefix}
|
||||
libdir=${exec_prefix}/lib
|
||||
includedir=${prefix}/include
|
||||
|
||||
Name: libredwg
|
||||
Description: Free implementation of the DWG file format
|
||||
Version: 0.14
|
||||
URL: https://savannah.gnu.org/projects/libredwg/
|
||||
Libs: -L${libdir} -lredwg -lm
|
||||
Cflags: -I${includedir}
|
||||
File diff suppressed because it is too large.
Load diff
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,41 @@
|
||||
# _LibreDWG.la - a libtool library file
|
||||
# Generated by libtool (GNU libtool) 2.5.4
|
||||
#
|
||||
# Please DO NOT delete this file!
|
||||
# It is necessary for linking the library.
|
||||
|
||||
# The name that we can dlopen(3).
|
||||
dlname='_LibreDWG.dll'
|
||||
|
||||
# Names of this library.
|
||||
library_names='_LibreDWG.dll.a'
|
||||
|
||||
# The name of the static archive.
|
||||
old_library=''
|
||||
|
||||
# Linker flags that cannot go in dependency_libs.
|
||||
inherited_linker_flags=''
|
||||
|
||||
# Libraries that this one depends upon.
|
||||
dependency_libs=' /lib/libredwg.la -L=C:/msys64/MINGW64/lib -lpython3.12 -lversion -lshlwapi -lpathcch -lbcrypt'
|
||||
|
||||
# Names of additional weak libraries provided by this library
|
||||
weak_library_names=''
|
||||
|
||||
# Version information for _LibreDWG.
|
||||
current=0
|
||||
age=0
|
||||
revision=0
|
||||
|
||||
# Is this an already installed library?
|
||||
installed=yes
|
||||
|
||||
# Should we warn about portability when linking against -modules?
|
||||
shouldnotlink=yes
|
||||
|
||||
# Files to dlopen/dlpreopen
|
||||
dlopen=''
|
||||
dlpreopen=''
|
||||
|
||||
# Directory that this library needs to be installed in:
|
||||
libdir='/lib/python3.12/site-packages'
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,561 @@
|
||||
This is LibreDWG.info, produced by makeinfo version 7.2 from
|
||||
LibreDWG.texi.
|
||||
|
||||
This manual is for GNU LibreDWG (version 0.14, 27 June 2026).
|
||||
|
||||
Copyright © 2010-2026 Free Software Foundation, Inc.
|
||||
|
||||
Permission is granted to copy, distribute and/or modify this
|
||||
document under the terms of the GNU Free Documentation License,
|
||||
Version 1.3 or any later version published by the Free Software
|
||||
Foundation; with no Invariant Sections, with no Front-Cover Texts,
|
||||
and with no Back-Cover Texts. A copy of the license is included in
|
||||
the section entitled "GNU Free Documentation License".
|
||||
|
||||
All other trademarks, trade names or company names referenced herein
|
||||
are used for identification purposes only and are the property of their
|
||||
respective owners.
|
||||
|
||||
DWG is the native and proprietary file format for AutoCAD® and a
|
||||
trademark of Autodesk, Inc. LibreDWG is neither associated with
|
||||
Autodesk, nor with the Open Design Alliance.
|
||||
INFO-DIR-SECTION Libraries
|
||||
START-INFO-DIR-ENTRY
|
||||
* LibreDWG: (LibreDWG). Free implementation of the DWG file format.
|
||||
END-INFO-DIR-ENTRY
|
||||
|
||||
|
||||
Indirect:
|
||||
LibreDWG.info-1: 1095
|
||||
LibreDWG.info-2: 303901
|
||||
|
||||
Tag Table:
|
||||
(Indirect)
|
||||
Node: Top1095
|
||||
Node: Overview2721
|
||||
Node: Usage10452
|
||||
Node: Types11183
|
||||
Node: Objects13909
|
||||
Node: HEADER14200
|
||||
Node: ENTITIES30247
|
||||
Ref: 3DFACE30440
|
||||
Ref: 3DLINE30786
|
||||
Ref: 3DSOLID31018
|
||||
Ref: ALIGNMENTPARAMETERENTITY32363
|
||||
Ref: ARC32429
|
||||
Ref: ARCALIGNEDTEXT32756
|
||||
Ref: ARC_DIMENSION34036
|
||||
Ref: ATTDEF35421
|
||||
Ref: ATTRIB36733
|
||||
Ref: BASEPOINTPARAMETERENTITY38032
|
||||
Ref: BLOCK38100
|
||||
Ref: BODY38286
|
||||
Ref: CAMERA38321
|
||||
Ref: CIRCLE38418
|
||||
Ref: DGNUNDERLAY38656
|
||||
Ref: UNDERLAY38656
|
||||
Ref: DIMENSION_ALIGNED39243
|
||||
Ref: DIMENSION_ANG2LN40375
|
||||
Ref: DIMENSION_ANG3PT41563
|
||||
Ref: DIMENSION_DIAMETER42732
|
||||
Ref: DIMENSION_LINEAR43821
|
||||
Ref: DIMENSION_ORDINATE45000
|
||||
Ref: DIMENSION_RADIUS46129
|
||||
Ref: DWFUNDERLAY47213
|
||||
Ref: ELLIPSE47250
|
||||
Ref: ENDBLK47572
|
||||
Ref: ENDREP47641
|
||||
Ref: EXTRUDEDSURFACE47885
|
||||
Ref: FLIPGRIPENTITY50489
|
||||
Ref: FLIPPARAMETERENTITY50571
|
||||
Ref: GEOPOSITIONMARKER50651
|
||||
Ref: HATCH51401
|
||||
Ref: HELIX52654
|
||||
Ref: IMAGE53951
|
||||
Ref: INSERT54708
|
||||
Ref: JUMP55444
|
||||
Ref: LARGE_RADIAL_DIMENSION55668
|
||||
Ref: LAYOUTPRINTCONFIG56837
|
||||
Ref: LEADER56981
|
||||
Ref: LIGHT58131
|
||||
Ref: LINE60261
|
||||
Ref: LINEARGRIPENTITY60572
|
||||
Ref: LINEARPARAMETERENTITY60656
|
||||
Ref: LOAD60723
|
||||
Ref: LOFTEDSURFACE60840
|
||||
Ref: LWPOLYLINE63218
|
||||
Ref: MESH63818
|
||||
Ref: MINSERT64479
|
||||
Ref: MLINE65174
|
||||
Ref: MPOLYGON65626
|
||||
Ref: MTEXT66784
|
||||
Ref: MULTILEADER68234
|
||||
Ref: NAVISWORKSMODEL70082
|
||||
Ref: NURBSURFACE70325
|
||||
Ref: OLE2FRAME71987
|
||||
Ref: OLEFRAME72414
|
||||
Ref: PDFUNDERLAY72634
|
||||
Ref: PLANESURFACE72676
|
||||
Ref: POINT74181
|
||||
Ref: POINTCLOUD74479
|
||||
Ref: POINTCLOUDEX75499
|
||||
Ref: POINTPARAMETERENTITY77032
|
||||
Ref: POLARGRIPENTITY77110
|
||||
Ref: POLYLINE_2D77184
|
||||
Ref: POLYLINE_3D77917
|
||||
Ref: POLYLINE_MESH78410
|
||||
Ref: POLYLINE_PFACE78947
|
||||
Ref: PROXY_ENTITY79349
|
||||
Ref: RAY79942
|
||||
Ref: REGION80090
|
||||
Ref: REPEAT80125
|
||||
Ref: REVOLVEDSURFACE80203
|
||||
Ref: ROTATIONGRIPENTITY82280
|
||||
Ref: ROTATIONPARAMETERENTITY82366
|
||||
Ref: RTEXT82434
|
||||
Ref: SECTIONOBJECT82784
|
||||
Ref: SEQEND83409
|
||||
Ref: SHAPE83516
|
||||
Ref: SOLID83955
|
||||
Ref: SPLINE84315
|
||||
Ref: SWEPTSURFACE85173
|
||||
Ref: TABLE87891
|
||||
Ref: TEXT94167
|
||||
Ref: TOLERANCE94880
|
||||
Ref: TRACE95247
|
||||
Ref: UNKNOWN_ENT95612
|
||||
Ref: VERTEX_2D95684
|
||||
Ref: VERTEX_3D96035
|
||||
Ref: VERTEX_MESH96185
|
||||
Ref: VERTEX_PFACE96228
|
||||
Ref: VERTEX_PFACE_FACE96276
|
||||
Ref: VIEWPORT96425
|
||||
Ref: VISIBILITYGRIPENTITY98425
|
||||
Ref: VISIBILITYPARAMETERENTITY98513
|
||||
Ref: WIPEOUT98583
|
||||
Ref: XLINE99339
|
||||
Ref: XYGRIPENTITY99376
|
||||
Ref: XYPARAMETERENTITY99456
|
||||
Node: OBJECTS99514
|
||||
Ref: ACMECOMMANDHISTORY99706
|
||||
Ref: ACMESCOPE99816
|
||||
Ref: ACMESTATEMGR99929
|
||||
Ref: ACSH_BOOLEAN_CLASS100048
|
||||
Ref: ACSH_BOX_CLASS100425
|
||||
Ref: ACSH_BREP_CLASS100793
|
||||
Ref: ACSH_CHAMFER_CLASS102306
|
||||
Ref: ACSH_CONE_CLASS102798
|
||||
Ref: ACSH_CYLINDER_CLASS103224
|
||||
Ref: ACSH_EXTRUSION_CLASS103651
|
||||
Ref: ACSH_FILLET_CLASS104981
|
||||
Ref: ACSH_HISTORY_CLASS105631
|
||||
Ref: ACSH_LOFT_CLASS105956
|
||||
Ref: ACSH_PYRAMID_CLASS106367
|
||||
Ref: ACSH_REVOLVE_CLASS106780
|
||||
Ref: ACSH_SPHERE_CLASS107492
|
||||
Ref: ACSH_SWEEP_CLASS107784
|
||||
Ref: ACSH_TORUS_CLASS109113
|
||||
Ref: ACSH_WEDGE_CLASS109456
|
||||
Ref: ALDIMOBJECTCONTEXTDATA109831
|
||||
Ref: ANGDIMOBJECTCONTEXTDATA110137
|
||||
Ref: ANNOTSCALEOBJECTCONTEXTDATA110443
|
||||
Ref: APPID110638
|
||||
Ref: APPID_CONTROL111010
|
||||
Ref: ASSOC2DCONSTRAINTGROUP111250
|
||||
Ref: ASSOC3POINTANGULARDIMACTIONBODY112193
|
||||
Ref: ASSOCACTION112661
|
||||
Ref: ASSOCACTIONPARAM113266
|
||||
Ref: ASSOCALIGNEDDIMACTIONBODY113474
|
||||
Ref: ASSOCARRAYACTIONBODY113910
|
||||
Ref: ASSOCARRAYMODIFYACTIONBODY114229
|
||||
Ref: ASSOCASMBODYACTIONPARAM114675
|
||||
Ref: ASSOCBLENDSURFACEACTIONBODY116237
|
||||
Ref: ASSOCCOMPOUNDACTIONPARAM116823
|
||||
Ref: ASSOCDEPENDENCY117469
|
||||
Ref: ASSOCDIMDEPENDENCYBODY118160
|
||||
Ref: ASSOCEDGEACTIONPARAM118416
|
||||
Ref: ASSOCEDGECHAMFERACTIONBODY118914
|
||||
Ref: ASSOCEDGEFILLETACTIONBODY119198
|
||||
Ref: ASSOCEXTENDSURFACEACTIONBODY119485
|
||||
Ref: ASSOCEXTRUDEDSURFACEACTIONBODY119860
|
||||
Ref: ASSOCFACEACTIONPARAM120185
|
||||
Ref: ASSOCFILLETSURFACEACTIONBODY120568
|
||||
Ref: ASSOCGEOMDEPENDENCY121005
|
||||
Ref: ASSOCLOFTEDSURFACEACTIONBODY121340
|
||||
Ref: ASSOCMLEADERACTIONBODY121667
|
||||
Ref: ASSOCNETWORK122118
|
||||
Ref: ASSOCNETWORKSURFACEACTIONBODY123029
|
||||
Ref: ASSOCOBJECTACTIONPARAM123356
|
||||
Ref: ASSOCOFFSETSURFACEACTIONBODY123701
|
||||
Ref: ASSOCORDINATEDIMACTIONBODY124067
|
||||
Ref: ASSOCOSNAPPOINTREFACTIONPARAM124512
|
||||
Ref: ASSOCPATCHSURFACEACTIONBODY125290
|
||||
Ref: ASSOCPATHACTIONPARAM125615
|
||||
Ref: ASSOCPERSSUBENTMANAGER126308
|
||||
Ref: ASSOCPLANESURFACEACTIONBODY128253
|
||||
Ref: ASSOCPOINTREFACTIONPARAM128582
|
||||
Ref: ASSOCRESTOREENTITYSTATEACTIONBODY129246
|
||||
Ref: ASSOCREVOLVEDSURFACEACTIONBODY129468
|
||||
Ref: ASSOCROTATEDDIMACTIONBODY129798
|
||||
Ref: ASSOCSWEPTSURFACEACTIONBODY130241
|
||||
Ref: ASSOCTRIMSURFACEACTIONBODY130572
|
||||
Ref: ASSOCVALUEDEPENDENCY131008
|
||||
Ref: ASSOCVARIABLE131141
|
||||
Ref: ASSOCVERTEXACTIONPARAM132099
|
||||
Ref: BLKREFOBJECTCONTEXTDATA132475
|
||||
Ref: BLOCKALIGNEDCONSTRAINTPARAMETER132825
|
||||
Ref: BLOCKALIGNMENTGRIP133978
|
||||
Ref: BLOCKALIGNMENTPARAMETER134551
|
||||
Ref: BLOCKANGULARCONSTRAINTPARAMETER135542
|
||||
Ref: BLOCKARRAYACTION136838
|
||||
Ref: BLOCKBASEPOINTPARAMETER137495
|
||||
Ref: BLOCKDIAMETRICCONSTRAINTPARAMETER138168
|
||||
Ref: BLOCKFLIPACTION139370
|
||||
Ref: BLOCKFLIPGRIP140043
|
||||
Ref: BLOCKFLIPPARAMETER140722
|
||||
Ref: BLOCKGRIPLOCATIONCOMPONENT141973
|
||||
Ref: BLOCKHORIZONTALCONSTRAINTPARAMETER142197
|
||||
Ref: BLOCKLINEARCONSTRAINTPARAMETER143362
|
||||
Ref: BLOCKLINEARGRIP144512
|
||||
Ref: BLOCKLINEARPARAMETER145082
|
||||
Ref: BLOCKLOOKUPACTION146195
|
||||
Ref: BLOCKLOOKUPGRIP146935
|
||||
Ref: BLOCKLOOKUPPARAMETER147457
|
||||
Ref: BLOCKMOVEACTION148190
|
||||
Ref: BLOCKPARAMDEPENDENCYBODY148892
|
||||
Ref: BLOCKPOINTPARAMETER149147
|
||||
Ref: BLOCKPOLARGRIP149860
|
||||
Ref: BLOCKPOLARPARAMETER150381
|
||||
Ref: BLOCKPOLARSTRETCHACTION151653
|
||||
Ref: BLOCKPROPERTIESTABLE152494
|
||||
Ref: BLOCKPROPERTIESTABLEGRIP152581
|
||||
Ref: BLOCKRADIALCONSTRAINTPARAMETER153113
|
||||
Ref: BLOCKREPRESENTATION154270
|
||||
Ref: BLOCKROTATEACTION154425
|
||||
Ref: BLOCKROTATIONGRIP155112
|
||||
Ref: BLOCKROTATIONPARAMETER155636
|
||||
Ref: BLOCKSCALEACTION156798
|
||||
Ref: BLOCKSTRETCHACTION157486
|
||||
Ref: BLOCKUSERPARAMETER158460
|
||||
Ref: BLOCKVERTICALCONSTRAINTPARAMETER159252
|
||||
Ref: BLOCKVISIBILITYGRIP160406
|
||||
Ref: BLOCKVISIBILITYPARAMETER160932
|
||||
Ref: BLOCKXYGRIP161877
|
||||
Ref: BLOCKXYPARAMETER162395
|
||||
Ref: BLOCK_CONTROL163680
|
||||
Ref: BLOCK_HEADER163980
|
||||
Ref: BREAKDATA165294
|
||||
Ref: BREAKPOINTREF165498
|
||||
Ref: CELLSTYLEMAP165573
|
||||
Ref: CONTEXTDATAMANAGER165749
|
||||
Ref: CSACDOCUMENTOPTIONS165957
|
||||
Ref: CURVEPATH166067
|
||||
Ref: DATALINK166223
|
||||
Ref: DATATABLE167177
|
||||
Ref: DBCOLOR167456
|
||||
Ref: DETAILVIEWSTYLE167573
|
||||
Ref: DICTIONARY169292
|
||||
Ref: DICTIONARYVAR169570
|
||||
Ref: DICTIONARYWDFLT169727
|
||||
Ref: DIMASSOC170042
|
||||
Ref: DIMSTYLE170378
|
||||
Ref: DIMSTYLE_CONTROL174070
|
||||
Ref: DMDIMOBJECTCONTEXTDATA174406
|
||||
Ref: DUMMY174736
|
||||
Ref: DYNAMICBLOCKPROXYNODE174820
|
||||
Ref: DYNAMICBLOCKPURGEPREVENTER174952
|
||||
Ref: EVALUATION_GRAPH175097
|
||||
Ref: FCFOBJECTCONTEXTDATA175522
|
||||
Ref: FIELD175802
|
||||
Ref: FIELDLIST176682
|
||||
Ref: GEODATA176866
|
||||
Ref: GEOMAPIMAGE178721
|
||||
Ref: GRADIENT_BACKGROUND179768
|
||||
Ref: GROUND_PLANE_BACKGROUND180158
|
||||
Ref: GROUP180578
|
||||
Ref: IBL_BACKGROUND180850
|
||||
Ref: IDBUFFER181181
|
||||
Ref: IMAGEDEF181361
|
||||
Ref: IMAGEDEF_REACTOR181698
|
||||
Ref: IMAGE_BACKGROUND181823
|
||||
Ref: INDEX182206
|
||||
Ref: LAYER182324
|
||||
Ref: LAYERFILTER183090
|
||||
Ref: LAYER_CONTROL183238
|
||||
Ref: LAYER_INDEX183467
|
||||
Ref: LAYOUT183668
|
||||
Ref: LEADEROBJECTCONTEXTDATA184636
|
||||
Ref: LIGHTLIST185106
|
||||
Ref: LONG_TRANSACTION185323
|
||||
Ref: LTYPE185391
|
||||
Ref: LTYPE_CONTROL186093
|
||||
Ref: MATERIAL186381
|
||||
Ref: MENTALRAYRENDERSETTINGS188206
|
||||
Ref: MLEADEROBJECTCONTEXTDATA191009
|
||||
Ref: MLEADERSTYLE191211
|
||||
Ref: MLINESTYLE193258
|
||||
Ref: MOTIONPATH193665
|
||||
Ref: MTEXTATTRIBUTEOBJECTCONTEXTDATA194062
|
||||
Ref: MTEXTOBJECTCONTEXTDATA194542
|
||||
Ref: NAVISWORKSMODELDEF195372
|
||||
Ref: OBJECT_PTR195701
|
||||
Ref: ORDDIMOBJECTCONTEXTDATA195787
|
||||
Ref: PARTIAL_VIEWING_INDEX196146
|
||||
Ref: PERSUBENTMGR196354
|
||||
Ref: PLACEHOLDER196818
|
||||
Ref: PLOTSETTINGS196893
|
||||
Ref: POINTCLOUDCOLORMAP198272
|
||||
Ref: POINTCLOUDDEF198813
|
||||
Ref: POINTCLOUDDEFEX199159
|
||||
Ref: POINTCLOUDDEF_REACTOR199511
|
||||
Ref: POINTCLOUDDEF_REACTOR_EX199644
|
||||
Ref: POINTPATH199762
|
||||
Ref: PROXY_OBJECT199922
|
||||
Ref: RADIMLGOBJECTCONTEXTDATA200428
|
||||
Ref: RADIMOBJECTCONTEXTDATA200776
|
||||
Ref: RAPIDRTRENDERSETTINGS201082
|
||||
Ref: RASTERVARIABLES201997
|
||||
Ref: RENDERENTRY202245
|
||||
Ref: RENDERENVIRONMENT203123
|
||||
Ref: RENDERGLOBAL203781
|
||||
Ref: RENDERSETTINGS204268
|
||||
Ref: SCALE204808
|
||||
Ref: SECTIONVIEWSTYLE205099
|
||||
Ref: SECTION_MANAGER207330
|
||||
Ref: SECTION_SETTINGS207535
|
||||
Ref: SKYLIGHT_BACKGROUND207754
|
||||
Ref: SOLID_BACKGROUND207917
|
||||
Ref: SORTENTSTABLE208078
|
||||
Ref: SPATIAL_FILTER208302
|
||||
Ref: SPATIAL_INDEX208879
|
||||
Ref: STYLE209199
|
||||
Ref: STYLE_CONTROL209901
|
||||
Ref: SUN210122
|
||||
Ref: SUNSTUDY210661
|
||||
Ref: TABLECONTENT211932
|
||||
Ref: TABLEGEOMETRY212190
|
||||
Ref: TABLESTYLE212436
|
||||
Ref: TEXTOBJECTCONTEXTDATA213298
|
||||
Ref: TVDEVICEPROPERTIES213681
|
||||
Ref: UCS214082
|
||||
Ref: UCS_CONTROL214783
|
||||
Ref: UNKNOWN_OBJ215010
|
||||
Ref: VBA_PROJECT215084
|
||||
Ref: VIEW215231
|
||||
Ref: VIEW_CONTROL217000
|
||||
Ref: VISUALSTYLE217228
|
||||
Ref: VPORT223233
|
||||
Ref: VPORT_CONTROL225472
|
||||
Ref: VX_CONTROL225700
|
||||
Ref: VX_TABLE_RECORD225930
|
||||
Ref: WIPEOUTVARIABLES226532
|
||||
Ref: XRECORD226648
|
||||
Ref: PDFDEFINITION226962
|
||||
Ref: UNDERLAYDEFINITION226962
|
||||
Ref: DGNDEFINITION227112
|
||||
Ref: DWFDEFINITION227165
|
||||
Ref: ASSOCARRAYMODIFYPARAMETERS227231
|
||||
Ref: ASSOCARRAYPARAMETERS227231
|
||||
Ref: ASSOCARRAYPATHPARAMETERS227568
|
||||
Ref: ASSOCARRAYPOLARPARAMETERS227635
|
||||
Ref: ASSOCARRAYRECTANGULARPARAMETERS227708
|
||||
Ref: Dwg_3DSOLID_material227770
|
||||
Ref: Dwg_3DSOLID_silhouette227966
|
||||
Ref: Dwg_3DSOLID_wire228340
|
||||
Ref: Dwg_ACSH_HistoryNode228941
|
||||
Ref: Dwg_ACSH_SubentColor229163
|
||||
Ref: Dwg_ACSH_SubentMaterial229360
|
||||
Ref: Dwg_ACTIONBODY229515
|
||||
Ref: Dwg_ARRAYITEMLOCATOR229703
|
||||
Ref: Dwg_ASSOCACTIONBODY_action229844
|
||||
Ref: Dwg_ASSOCACTION_Deps230009
|
||||
Ref: Dwg_ASSOCARRAYITEM230154
|
||||
Ref: Dwg_ASSOCPARAMBASEDACTIONBODY230615
|
||||
Ref: Dwg_ASSOCSURFACEACTIONBODY231001
|
||||
Ref: Dwg_AcDbMTextObjectEmbedded231290
|
||||
Ref: Dwg_AcDs233055
|
||||
Ref: Dwg_AcDs_Data234002
|
||||
Ref: Dwg_AcDs_DataBlob234136
|
||||
Ref: Dwg_AcDs_DataBlob01234418
|
||||
Ref: Dwg_AcDs_DataBlobRef234685
|
||||
Ref: Dwg_AcDs_DataBlobRef_Page234985
|
||||
Ref: Dwg_AcDs_DataIndex235071
|
||||
Ref: Dwg_AcDs_DataIndex_Entry235229
|
||||
Ref: Dwg_AcDs_Data_Record235350
|
||||
Ref: Dwg_AcDs_Data_RecordHdr235445
|
||||
Ref: Dwg_AcDs_Schema235598
|
||||
Ref: Dwg_AcDs_SchemaData235774
|
||||
Ref: Dwg_AcDs_SchemaData_UProp236052
|
||||
Ref: Dwg_AcDs_SchemaIndex236139
|
||||
Ref: Dwg_AcDs_SchemaIndex_Prop236468
|
||||
Ref: Dwg_AcDs_Schema_Prop236588
|
||||
Ref: Dwg_AcDs_Search236870
|
||||
Ref: Dwg_AcDs_Search_Data236983
|
||||
Ref: Dwg_AcDs_Search_IdIdx237244
|
||||
Ref: Dwg_AcDs_Search_IdIdxs237372
|
||||
Ref: Dwg_AcDs_Segment237480
|
||||
Ref: Dwg_AcDs_SegmentIndex237924
|
||||
Ref: Dwg_BLOCKACTION_connectionpts238022
|
||||
Ref: Dwg_BLOCKLOOKUPACTION_lut238113
|
||||
Ref: Dwg_BLOCKPARAMETER_PropInfo238348
|
||||
Ref: Dwg_BLOCKPARAMETER_connection238489
|
||||
Ref: Dwg_BLOCKPARAMVALUESET238576
|
||||
Ref: Dwg_BLOCKSTRETCHACTION_codes238842
|
||||
Ref: Dwg_BLOCKSTRETCHACTION_handles239069
|
||||
Ref: Dwg_BLOCKVISIBILITYPARAMETER_state239299
|
||||
Ref: Dwg_COMPOUNDOBJECTID239603
|
||||
Ref: Dwg_CONSTRAINTGROUPNODE239781
|
||||
Ref: Dwg_CONTEXTDATA_dict240019
|
||||
Ref: Dwg_CONTEXTDATA_submgr240187
|
||||
Ref: Dwg_CellContentGeometry240410
|
||||
Ref: Dwg_CellStyle240854
|
||||
Ref: Dwg_ColorRamp241760
|
||||
Ref: Dwg_ContentFormat241977
|
||||
Ref: Dwg_DATALINK_customdata242518
|
||||
Ref: Dwg_DATATABLE_column242670
|
||||
Ref: Dwg_DATATABLE_row242870
|
||||
Ref: Dwg_DIMASSOC_Ref242995
|
||||
Ref: Dwg_DIMENSION_common243829
|
||||
Ref: Dwg_EVAL_Edge244826
|
||||
Ref: Dwg_EVAL_Node245129
|
||||
Ref: Dwg_EvalExpr245440
|
||||
Ref: Dwg_EvalVariant245881
|
||||
Ref: Dwg_FIELD_ChildValue246117
|
||||
Ref: Dwg_FileDepList_Files246278
|
||||
Ref: Dwg_FormattedTableData246625
|
||||
Ref: Dwg_FormattedTableMerged246872
|
||||
Ref: Dwg_GEODATA_meshface247126
|
||||
Ref: Dwg_GEODATA_meshpt247242
|
||||
Ref: Dwg_GridFormat247332
|
||||
Ref: Dwg_HATCH_Color247750
|
||||
Ref: Dwg_HATCH_ControlPoint247918
|
||||
Ref: Dwg_HATCH_DefLine248076
|
||||
Ref: Dwg_HATCH_Path248350
|
||||
Ref: Dwg_HATCH_PathSeg248808
|
||||
Ref: Dwg_HATCH_PolylinePath249802
|
||||
Ref: Dwg_LAYER_entry249954
|
||||
Ref: Dwg_LEADER_ArrowHead250158
|
||||
Ref: Dwg_LEADER_BlockLabel250331
|
||||
Ref: Dwg_LEADER_Break250578
|
||||
Ref: Dwg_LEADER_Line250730
|
||||
Ref: Dwg_LEADER_Node251284
|
||||
Ref: Dwg_LIGHTLIST_light251864
|
||||
Ref: Dwg_LTYPE_dash252016
|
||||
Ref: Dwg_LWPOLYLINE_width252464
|
||||
Ref: Dwg_LinkedData252560
|
||||
Ref: Dwg_LinkedTableData252666
|
||||
Ref: Dwg_MATERIAL_color252935
|
||||
Ref: Dwg_MATERIAL_gentexture253109
|
||||
Ref: Dwg_MATERIAL_mapper253287
|
||||
Ref: Dwg_MESH_edge253733
|
||||
Ref: Dwg_MLEADER_AnnotContext253896
|
||||
Ref: Dwg_MLEADER_Content_Block254862
|
||||
Ref: Dwg_MLEADER_Content_MText255155
|
||||
Ref: Dwg_MLINESTYLE_line256154
|
||||
Ref: Dwg_MLINE_line256393
|
||||
Ref: Dwg_MLINE_vertex256639
|
||||
Ref: Dwg_OCD_Dimension256934
|
||||
Ref: Dwg_PARTIAL_VIEWING_INDEX_Entry257557
|
||||
Ref: Dwg_POINTCLOUDCOLORMAP_Ramp257766
|
||||
Ref: Dwg_POINTCLOUDEX_Croppings257997
|
||||
Ref: Dwg_POINTCLOUD_Clippings258418
|
||||
Ref: Dwg_POINTCLOUD_IntensityStyle258710
|
||||
Ref: Dwg_PROXY_LWPOLYLINE258968
|
||||
Ref: Dwg_R2004_Header259577
|
||||
Ref: Dwg_SECTION_geometrysettings260693
|
||||
Ref: Dwg_SECTION_typesettings261465
|
||||
Ref: Dwg_SPLINE_control_point261905
|
||||
Ref: Dwg_SUNSTUDY_Dates262098
|
||||
Ref: Dwg_SummaryInfo_Property262211
|
||||
Ref: Dwg_TABLEGEOMETRY_Cell262301
|
||||
Ref: Dwg_TABLESTYLE_CellStyle262677
|
||||
Ref: Dwg_TABLESTYLE_border263878
|
||||
Ref: Dwg_TABLESTYLE_rowstyles264004
|
||||
Ref: Dwg_TABLE_AttrDef264548
|
||||
Ref: Dwg_TABLE_BreakHeight264744
|
||||
Ref: Dwg_TABLE_BreakRow264918
|
||||
Ref: Dwg_TABLE_Cell265086
|
||||
Ref: Dwg_TABLE_CustomDataItem266797
|
||||
Ref: Dwg_TABLE_value267010
|
||||
Ref: Dwg_TableCell267603
|
||||
Ref: Dwg_TableCellContent268562
|
||||
Ref: Dwg_TableCellContent_Attr268968
|
||||
Ref: Dwg_TableDataColumn269169
|
||||
Ref: Dwg_TableRow269439
|
||||
Ref: Dwg_UCS_orthopts269850
|
||||
Ref: Dwg_VALUEPARAM269997
|
||||
Ref: Dwg_VALUEPARAM_vars270300
|
||||
Ref: Dwg_MLEADER_Content270400
|
||||
Ref: Common Entity fields270530
|
||||
Ref: Common Object fields272376
|
||||
Node: Sections272806
|
||||
Node: HEADER_275138
|
||||
Node: OBJECTS_275272
|
||||
Node: CLASSES275603
|
||||
Node: HANDLES277004
|
||||
Node: R2004_Header277389
|
||||
Node: UNKNOWN277681
|
||||
Node: SummaryInfo277903
|
||||
Node: Preview278707
|
||||
Node: VBAProject279072
|
||||
Node: AppInfo279192
|
||||
Node: AppInfoHistory279368
|
||||
Node: FileDepList279506
|
||||
Node: AcDS279709
|
||||
Node: RevHistory279910
|
||||
Node: Security280048
|
||||
Node: ObjFreeSpace280187
|
||||
Node: Template280341
|
||||
Node: AuxHeader280507
|
||||
Node: Signature280669
|
||||
Node: INFO280787
|
||||
Node: SYSTEM_MAP280928
|
||||
Node: Tables281099
|
||||
Node: EXTRAS282602
|
||||
Node: Structures285278
|
||||
Node: EED285475
|
||||
Node: XDATA287871
|
||||
Node: Functions288217
|
||||
Node: Decoding289677
|
||||
Node: Encoding292452
|
||||
Node: add api295016
|
||||
Node: dynapi299155
|
||||
Node: strings303901
|
||||
Node: Other Formats305975
|
||||
Node: DXF306315
|
||||
Node: DXFB306997
|
||||
Node: JSON307299
|
||||
Node: GeoJSON308336
|
||||
Node: Errors309362
|
||||
Node: Programs310702
|
||||
Ref: dwgread310889
|
||||
Ref: dwgwrite311461
|
||||
Ref: dxfwrite311679
|
||||
Ref: dwg2dxf311908
|
||||
Ref: dxf2dwg312409
|
||||
Ref: dwgrewrite312836
|
||||
Ref: dwglayers313009
|
||||
Ref: dwggrep313642
|
||||
Ref: dwgfilter314143
|
||||
Ref: dwg2SVG314431
|
||||
Ref: dwg2ps314976
|
||||
Ref: dwgplot315456
|
||||
Ref: load_dwg315719
|
||||
Ref: dwg2svg2315776
|
||||
Ref: unknown316094
|
||||
Ref: dwgfuzz316357
|
||||
Ref: dwgadd317154
|
||||
Node: Bindings317420
|
||||
Node: Reference API318052
|
||||
Node: Reporting bugs318583
|
||||
Node: GNU Free Documentation License320363
|
||||
Node: Index345517
|
||||
Node: General Index345753
|
||||
Node: Object and Field Index377436
|
||||
|
||||
End Tag Table
|
||||
|
||||
|
||||
Local Variables:
|
||||
coding: utf-8
|
||||
End:
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,18 @@
|
||||
This is the file .../info/dir, which contains the
|
||||
topmost node of the Info hierarchy, called (dir)Top.
|
||||
The first time you invoke Info you start off looking at this node.
|
||||
|
||||
File: dir, Node: Top This is the top of the INFO tree
|
||||
|
||||
This (the Directory node) gives a menu of major topics.
|
||||
Typing "q" exits, "H" lists all Info commands, "d" returns here,
|
||||
"h" gives a primer for first-timers,
|
||||
"mEmacs<Return>" visits the Emacs manual, etc.
|
||||
|
||||
In Emacs, you can click mouse button 2 on a menu item or cross reference
|
||||
to select it.
|
||||
|
||||
* Menu:
|
||||
|
||||
Libraries
|
||||
* LibreDWG: (LibreDWG). Free implementation of the DWG file format.
|
||||
@@ -0,0 +1,65 @@
|
||||
# example dwgadd file. strings are utf8 and must not exceed length 255
|
||||
#readdwg "example_2000.dwg"
|
||||
#readdxf "example_2000.dxf"
|
||||
#readjson "example_2000.json"
|
||||
#version 2000
|
||||
HEADER.VIEWSIZE = 1.2345
|
||||
HEADER.ANGBASE = 90
|
||||
HEADER.LUPREC = 3
|
||||
HEADER.MENU = "other"
|
||||
HEADER.INSBASE = (1 0 0)
|
||||
HEADER.LIMMIN = (12 9)
|
||||
point (2 0 0)
|
||||
text "test" (0.0 1.0 0.0) 8
|
||||
block "bloko"
|
||||
attdef 8 0 "prompt" (0.0 1.0 0.0) TAG "default_text"
|
||||
line (0 1 0) (1 1 0)
|
||||
endblk
|
||||
insert (0 1 0) "bloko" 1 1 1 0
|
||||
attrib 8 0 (0.0 2.0 0.0) TAG "text1"
|
||||
minsert (0 1 0) "bloko" 1 1 1 0 1 2 1 1
|
||||
minsert.rotation = 90
|
||||
polyline_2d 2 ((0 1) (1 1))
|
||||
polyline_3d 2 ((0 1 0.5) (1 1 -0.5))
|
||||
lwpolyline 2 ((0 1) (1 1))
|
||||
arc (0.0 1.0 0.0) 0.5 0.0 3.0
|
||||
circle (0.0 1.0 0.0) 0.5
|
||||
polyline_pface 5 3 ((0 0 1) (2 0 1) (2 2 0) (1 2 0.5) (1 1 -0.5)) ((0 1 2 3) (1 2 -3 4) (2 -3 4))
|
||||
polyline_mesh 3 2 ((0 0 1) (2 0 2) (2 2 1) (1 2 0.5) (1 1 -1) (0 1 0))
|
||||
dimension_aligned (0 0 0) (2 0 0) (2 2 0)
|
||||
dimension_ang2ln (0 0 0) (2 0 0) (2 2 0) (2 2 0)
|
||||
dimension_ang3pt (0 0 0) (2 0 0) (2 1 0) (2 2 0)
|
||||
dimension_diameter (0 0 0) (2 0 0) 2
|
||||
dimension_ordinate (0 0 0) (2 0 0) 1
|
||||
dimension_radius (0 0 0) (2 0 0) 1
|
||||
dimension_linear (2 0 0) (2 1 0) (0 0 0) 90
|
||||
3dface (0 0 0) (2 0 0) (2 1 0) (0 2 0)
|
||||
solid (0 0 0) (2 0) (2 1) (0 2)
|
||||
trace (0 0 0) (2 0) (2 1) (0 2)
|
||||
shape "roman.shx" (0 0 0) 2 45
|
||||
ellipse (0 0 0) 2.0 0.75
|
||||
spline 3 ((0 0 0) (2 0 0) (2 1 0)) (0 2 0) (0 3 0)
|
||||
ray (0 0 0) (2 0 0)
|
||||
xline (0 0 0) (2 0 0)
|
||||
dictionary "ACAD_MATERIAL" "Global" 0
|
||||
xrecord dictionary "REFRACTIONTILE"
|
||||
mtext (0 1 0) 10 "test\ntext"
|
||||
leader 2 ((0 0 0) (0 1 0)) mtext 15
|
||||
tolerance "testtekst" (0 0 0) (0 0 1)
|
||||
mlinestyle "Double"
|
||||
mlinestyle.start_angle = 45
|
||||
mline 4 ((3.9 1.5 0) (3.2 1.8 0) (4.6 1.0 0) (3.8 1.7 0))
|
||||
dimstyle "Dim1"
|
||||
dimstyle.DIMSCALE = 2.0
|
||||
dimstyle.DIMUPT = 1
|
||||
ucs (0 1 0) (1 0 0) (0 0 1) "Ucs1"
|
||||
ucs.ucs_elevation = 1.0
|
||||
viewport "Viewport1"
|
||||
layout viewport "Model" "ANSI_A_(8.50_x_11.00_Inches)"
|
||||
torus (10 5 0) (0 0 1) 19 2.78
|
||||
sphere (10 5 0) (0 0 1) 15
|
||||
cylinder (10 5 0) (0 0 1) 15 5 5 5
|
||||
cone (10 5 0) (0 0 1) 15 5 5 0
|
||||
wedge (10 5 0) (0 0 1) 3.3 2.4 4.8
|
||||
box (10 5 0) (0 0 1) 3.3 2.0 2.5
|
||||
pyramid (10 5 0) (0 0 1) 4.5 4 2.0 2.5
|
||||
@@ -0,0 +1,32 @@
|
||||
# example dwgadd file for r10. strings are utf8 and must not exceed length 255
|
||||
HEADER.VIEWSIZE = 1.2345
|
||||
HEADER.ANGBASE = 90
|
||||
HEADER.LUPREC = 3
|
||||
HEADER.MENU = "other"
|
||||
HEADER.INSBASE = (1 0 0)
|
||||
HEADER.LIMMIN = (12 9)
|
||||
point (2 0 0)
|
||||
text "test" (0.0 1.0 0.0) 8
|
||||
block "BLOKO"
|
||||
attdef 8 0 "prompt" (0.0 1.0 0.0) TAG "default_text"
|
||||
line (0 1 0) (1 1 0)
|
||||
endblk
|
||||
insert (0 1 0) "BLOKO" 1 1 1 0
|
||||
attrib 8 0 (0.0 2.0 0.0) TAG "text1"
|
||||
polyline_2d 2 ((0 1) (1 1))
|
||||
polyline_3d 2 ((0 1 0) (1 1 0))
|
||||
arc (0.0 1.0 0.0) 0.5 0.0 3.0
|
||||
circle (0.0 1.0 0.0) 0.5
|
||||
polyline_pface 5 3 ((0 0 0) (2 0 0) (2 2 0) (1 2 0) (1 1 0)) ((0 1 2 3) (1 2 3 4) (2 3 4 5))
|
||||
polyline_mesh 3 2 ((0 0 0) (2 0 0) (2 2 0) (1 2 0) (1 1 0) (0 1 0))
|
||||
dimension_aligned (0 0 0) (2 0 0) (2 2 0)
|
||||
dimension_ang2ln (0 0 0) (2 0 0) (2 2 0) (2 2 0)
|
||||
dimension_ang3pt (0 0 0) (2 0 0) (2 1 0) (2 2 0)
|
||||
dimension_diameter (0 0 0) (2 0 0) 2
|
||||
dimension_ordinate (0 0 0) (2 0 0) 1
|
||||
dimension_radius (0 0 0) (2 0 0) 1
|
||||
dimension_linear (2 0 0) (2 1 0) (0 0 0) 90
|
||||
3dface (0 0 0) (2 0 0) (2 1 0) (0 2 0)
|
||||
solid (0 0 0) (2 0) (2 1) (0 2)
|
||||
trace (0 0 0) (2 0) (2 1) (0 2)
|
||||
shape "ROMAN.SHX" (0 0 0) 2 45
|
||||
@@ -0,0 +1,40 @@
|
||||
# example dwgadd file for r11. strings are utf8 and must not exceed length 255
|
||||
HEADER.VIEWSIZE = 1.2345
|
||||
HEADER.ANGBASE = 90
|
||||
HEADER.LUPREC = 3
|
||||
HEADER.MENU = "other"
|
||||
HEADER.INSBASE = (1 0 0)
|
||||
HEADER.LIMMIN = (12 9)
|
||||
point (2 0 0)
|
||||
text "test" (0.0 1.0 0.0) 8
|
||||
block "BLOKO"
|
||||
attdef 8 0 "prompt" (0.0 1.0 0.0) TAG "default_text"
|
||||
line (0 1 0) (1 1 0)
|
||||
endblk
|
||||
insert (0 1 0) "BLOKO" 1 1 1 0
|
||||
attrib 8 0 (0.0 2.0 0.0) TAG "text1"
|
||||
polyline_2d 2 ((0 1) (1 1))
|
||||
polyline_3d 2 ((0 1 0.5) (1 1 -0.5))
|
||||
arc (0.0 1.0 0.0) 0.5 0.0 3.0
|
||||
circle (0.0 1.0 0.0) 0.5
|
||||
polyline_pface 5 3 ((0 0 1) (2 0 1) (2 2 0) (1 2 0.5) (1 1 -0.5)) ((0 1 2 3) (1 2 -3 4) (2 -3 4))
|
||||
polyline_mesh 3 2 ((0 0 1) (2 0 2) (2 2 1) (1 2 0.5) (1 1 -1) (0 1 0))
|
||||
dimension_aligned (0 0 0) (2 0 0) (2 2 0)
|
||||
dimension_ang2ln (0 0 0) (2 0 0) (2 2 0) (2 2 0)
|
||||
dimension_ang3pt (0 0 0) (2 0 0) (2 1 0) (2 2 0)
|
||||
dimension_diameter (0 0 0) (2 0 0) 2
|
||||
dimension_ordinate (0 0 0) (2 0 0) 1
|
||||
dimension_radius (0 0 0) (2 0 0) 1
|
||||
dimension_linear (2 0 0) (2 1 0) (0 0 0) 90
|
||||
3dface (0 0 0) (2 0 0) (2 1 0) (0 2 0)
|
||||
solid (0 0 0) (2 0) (2 1) (0 2)
|
||||
trace (0 0 0) (2 0) (2 1) (0 2)
|
||||
shape "ROMAN.SHX" (0 0 0) 2 45
|
||||
dimstyle "DIM1"
|
||||
dimstyle.DIMSCALE = 2.0
|
||||
dimstyle.DIMUPT = 1
|
||||
ucs (0 1 0) (1 0 0) (0 0 1) "UCS1"
|
||||
ucs.ucs_elevation = 1.0
|
||||
layer "FOO"
|
||||
line (0 0 0) (100 0 100)
|
||||
line.layer = "FOO"
|
||||
@@ -0,0 +1,16 @@
|
||||
# example dwgadd file for r1.4. strings are utf8 and must not exceed length 255
|
||||
HEADER.TEXTSIZE = 1.2345
|
||||
HEADER.LUPREC = 3
|
||||
HEADER.INSBASE = (1 0)
|
||||
HEADER.LIMMIN = (12 9)
|
||||
point (2 0 0)
|
||||
text "test" (0.0 1.0 0.0) 8
|
||||
block "BLOKO"
|
||||
line (0 1 0) (1 1 0)
|
||||
endblk
|
||||
insert (0 1 0) "BLOKO" 1 1 1 0
|
||||
arc (0.0 1.0 0.0) 0.5 0.0 3.0
|
||||
circle (0.0 1.0 0.0) 0.5
|
||||
solid (0 0 0) (2 0) (2 1) (0 2)
|
||||
trace (0 0 0) (2 0) (2 1) (0 2)
|
||||
shape "ROMAN.SHX" (0 0 0) 2 45
|
||||
@@ -0,0 +1,23 @@
|
||||
# example dwgadd file for r2.10. strings are utf8 and must not exceed length 255
|
||||
HEADER.VIEWSIZE = 1.2345
|
||||
HEADER.LUPREC = 3
|
||||
HEADER.MENU = "other"
|
||||
HEADER.INSBASE = (1 0 0)
|
||||
HEADER.LIMMIN = (12 9)
|
||||
point (2 0 0)
|
||||
text "test" (0.0 1.0 0.0) 8
|
||||
block "BLOKO"
|
||||
attdef 8 0 "prompt" (0.0 1.0 0.0) TAG "default_text"
|
||||
line (0 1 0) (1 1 0)
|
||||
endblk
|
||||
insert (0 1 0) "BLOKO" 1 1 1 0
|
||||
attrib 8 0 (0.0 2.0 0.0) TAG "text1"
|
||||
polyline_2d 2 ((0 1) (2 3))
|
||||
polyline_3d 2 ((0 1 2) (3 4 5))
|
||||
arc (0.0 1.0 0.0) 0.5 0.0 3.0
|
||||
circle (0.0 1.0 0.0) 0.5
|
||||
polyline_pface 5 3 ((0 0 0) (2 0 0) (2 2 0) (1 2 0) (1 1 0)) ((0 1 2 3) (1 2 3 4) (2 3 4 5))
|
||||
polyline_mesh 3 2 ((0 0 0) (2 0 0) (2 2 0) (1 2 0) (1 1 0) (0 1 0))
|
||||
solid (0 0 0) (2 0) (2 1) (0 2)
|
||||
trace (0 0 0) (2 0) (2 1) (0 2)
|
||||
shape "ROMAN.SHX" (0 0 0) 2 45
|
||||
@@ -0,0 +1,36 @@
|
||||
#! /usr/bin/env python3
|
||||
|
||||
from libredwg import (
|
||||
Dwg_Data,
|
||||
dwg_read_file,
|
||||
new_Dwg_Object_Array,
|
||||
Dwg_Object_Array_getitem,
|
||||
)
|
||||
|
||||
import sys
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
print("Usage: load_dwg.py <filename>")
|
||||
exit()
|
||||
|
||||
filename = sys.argv[1]
|
||||
a = Dwg_Data()
|
||||
a.object = new_Dwg_Object_Array(1000)
|
||||
error = dwg_read_file(filename, a)
|
||||
|
||||
if error > 0: # critical errors
|
||||
print("Error: ", error)
|
||||
if error > 127:
|
||||
exit()
|
||||
|
||||
print(".dwg version: %s" % a.header.version)
|
||||
print("Num objects: %d " % a.num_objects)
|
||||
|
||||
# XXX TODO Error: Dwg_Object_LAYER_CONTROL object has no attribute 'tio'
|
||||
# print "Num layers: %d" % a.layer_control.tio.object.tio.LAYER_CONTROL.num_entries
|
||||
|
||||
# XXX ugly, but works
|
||||
for i in range(0, a.num_objects):
|
||||
obj = Dwg_Object_Array_getitem(a.object, i)
|
||||
print(" Supertype: ", obj.supertype)
|
||||
print(" Type: ", obj.type)
|
||||
@@ -0,0 +1,38 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||
.TH DWG2SVG "1" "June 2026" "dwg2SVG 0.14" "User Commands"
|
||||
.SH NAME
|
||||
dwg2SVG \- manual page for dwg2SVG 0.14
|
||||
.SH SYNOPSIS
|
||||
.B dwg2SVG
|
||||
[\fI\,OPTION\/\fR]... \fI\,DWGFILE >SVGFILE\/\fR
|
||||
.SH DESCRIPTION
|
||||
Converts some 2D elements of the DWG to a SVG.
|
||||
.TP
|
||||
\fB\-v[0\-9]\fR, \fB\-\-verbose\fR [0\-9]
|
||||
verbosity
|
||||
.TP
|
||||
\fB\-\-mspace\fR
|
||||
only model\-space, no paper\-space
|
||||
.TP
|
||||
\fB\-\-force\-free\fR
|
||||
force free
|
||||
.TP
|
||||
\fB\-\-help\fR
|
||||
display this help and exit
|
||||
.TP
|
||||
\fB\-\-version\fR
|
||||
output version information and exit
|
||||
.PP
|
||||
GNU LibreDWG online manual: <https://www.gnu.org/software/libredwg/>
|
||||
.SH "SEE ALSO"
|
||||
The full documentation for
|
||||
.B dwg2SVG
|
||||
is maintained as a Texinfo manual. If the
|
||||
.B info
|
||||
and
|
||||
.B dwg2SVG
|
||||
programs are properly installed at your site, the command
|
||||
.IP
|
||||
.B info LibreDWG
|
||||
.PP
|
||||
should give you access to the complete manual.
|
||||
@@ -0,0 +1,57 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||
.TH DWG2DXF "1" "June 2026" "dwg2dxf 0.14" "User Commands"
|
||||
.SH NAME
|
||||
dwg2dxf \- manual page for dwg2dxf 0.14
|
||||
.SH SYNOPSIS
|
||||
.B dwg2dxf
|
||||
[\fI\,OPTION\/\fR]... \fI\,DWGFILES\/\fR...
|
||||
.SH DESCRIPTION
|
||||
Converts DWG files to DXF.
|
||||
Default DXFFILE: DWGFILE with .dxf extension (.dxb with \fB\-b\fR) in the current directory.
|
||||
Existing files are not overwritten, unless \fB\-y\fR is given.
|
||||
.TP
|
||||
\fB\-v[0\-9]\fR, \fB\-\-verbose\fR [0\-9]
|
||||
verbosity
|
||||
.TP
|
||||
\fB\-\-as\fR rNNNN
|
||||
save as version
|
||||
.IP
|
||||
Valid versions:
|
||||
.IP
|
||||
r12, r14, r2000, r2004, r2007, r2010, r2013, r2018
|
||||
.IP
|
||||
Experimental versions:
|
||||
.IP
|
||||
r9, r10, r11
|
||||
.TP
|
||||
\fB\-m\fR, \fB\-\-minimal\fR
|
||||
only $ACADVER, HANDSEED and ENTITIES
|
||||
.TP
|
||||
\fB\-b\fR, \fB\-\-binary\fR
|
||||
save as binary DXB
|
||||
.TP
|
||||
\fB\-y\fR, \fB\-\-overwrite\fR
|
||||
overwrite existing files
|
||||
.TP
|
||||
\fB\-o\fR outfile, \fB\-\-file\fR
|
||||
optional, only valid with one single DWGFILE
|
||||
.TP
|
||||
\fB\-\-help\fR
|
||||
display this help and exit
|
||||
.TP
|
||||
\fB\-\-version\fR
|
||||
output version information and exit
|
||||
.PP
|
||||
GNU LibreDWG online manual: <https://www.gnu.org/software/libredwg/>
|
||||
.SH "SEE ALSO"
|
||||
The full documentation for
|
||||
.B dwg2dxf
|
||||
is maintained as a Texinfo manual. If the
|
||||
.B info
|
||||
and
|
||||
.B dwg2dxf
|
||||
programs are properly installed at your site, the command
|
||||
.IP
|
||||
.B info LibreDWG
|
||||
.PP
|
||||
should give you access to the complete manual.
|
||||
@@ -0,0 +1,52 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||
.TH DWGADD "1" "June 2026" "dwgadd 0.14" "User Commands"
|
||||
.SH NAME
|
||||
dwgadd \- manual page for dwgadd 0.14
|
||||
.SH SYNOPSIS
|
||||
.B dwgadd
|
||||
[\fI\,OPTIONS\/\fR] \fI\,-o outfile addfile\/\fR
|
||||
.SH DESCRIPTION
|
||||
Create a DWG (or DXF, JSON) by adding entities with instructions from a special dwgadd file.
|
||||
See `man 5 dwgadd`
|
||||
.TP
|
||||
\fB\-\-dxf\fR:
|
||||
write DXF, not DWG
|
||||
.HP
|
||||
\fB\-\-binary\fR: write binary DXF, not DWG
|
||||
.TP
|
||||
\fB\-\-json\fR:
|
||||
write JSON, not DWG
|
||||
.TP
|
||||
\fB\-v[0\-9]\fR, \fB\-\-verbose\fR [0\-9]
|
||||
verbosity
|
||||
.TP
|
||||
\fB\-\-as\fR rNNNN
|
||||
save as version
|
||||
.IP
|
||||
Valid versions:
|
||||
.IP
|
||||
r1.1, r1.2, r1.4, r2.6, r2.10, r9, r10, r11, r13, r14, r2000 (default), r2004
|
||||
.IP
|
||||
Planned versions:
|
||||
.IP
|
||||
r2007, r2010, r2013, r2018
|
||||
.HP
|
||||
\fB\-o\fR outfile, \fB\-\-file\fR outfile (default: stdout)
|
||||
.TP
|
||||
\fB\-\-version\fR
|
||||
display the version and exit
|
||||
.TP
|
||||
\fB\-\-help\fR
|
||||
display this help and exit
|
||||
.SH "SEE ALSO"
|
||||
The full documentation for
|
||||
.B dwgadd
|
||||
is maintained as a Texinfo manual. If the
|
||||
.B info
|
||||
and
|
||||
.B dwgadd
|
||||
programs are properly installed at your site, the command
|
||||
.IP
|
||||
.B info LibreDWG
|
||||
.PP
|
||||
should give you access to the complete manual.
|
||||
@@ -0,0 +1,33 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||
.TH DWGBMP "1" "June 2026" "dwgbmp 0.14" "User Commands"
|
||||
.SH NAME
|
||||
dwgbmp \- manual page for dwgbmp 0.14
|
||||
.SH SYNOPSIS
|
||||
.B dwgbmp
|
||||
[\fI\,OPTION\/\fR]... \fI\,DWGFILE \/\fR[\fI\,thumbnailfile\/\fR]
|
||||
.SH DESCRIPTION
|
||||
Extract the DWG thumbnail image as BMP, WMF or PNG.
|
||||
Default thumbnailfile: DWGFILE with the proper extension.
|
||||
.TP
|
||||
\fB\-v[0\-9]\fR, \fB\-\-verbose\fR [0\-9]
|
||||
verbosity
|
||||
.TP
|
||||
\fB\-\-help\fR
|
||||
display this help and exit
|
||||
.TP
|
||||
\fB\-\-version\fR
|
||||
output version information and exit
|
||||
.PP
|
||||
GNU LibreDWG online manual: <https://www.gnu.org/software/libredwg/>
|
||||
.SH "SEE ALSO"
|
||||
The full documentation for
|
||||
.B dwgbmp
|
||||
is maintained as a Texinfo manual. If the
|
||||
.B info
|
||||
and
|
||||
.B dwgbmp
|
||||
programs are properly installed at your site, the command
|
||||
.IP
|
||||
.B info LibreDWG
|
||||
.PP
|
||||
should give you access to the complete manual.
|
||||
@@ -0,0 +1,31 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||
.TH DWGFILTER "1" "June 2026" "dwgfilter 0.14" "User Commands"
|
||||
.SH NAME
|
||||
dwgfilter \- manual page for dwgfilter 0.14
|
||||
.SH DESCRIPTION
|
||||
dwgfilter [OPTIONS...] dwgfile
|
||||
.PP
|
||||
Allow custom jq queries on a temporary JSON dump.
|
||||
.PP
|
||||
OPTIONS: \fB\-\-help\fR,\-\-version
|
||||
.TP
|
||||
\fB\-\-debug\fR
|
||||
keep the tmp json
|
||||
.TP
|
||||
\fB\-i\fR
|
||||
write back in\-place, with an updating JQ query
|
||||
.TP
|
||||
\&...
|
||||
all other options are passed to jq. See 'man jq'
|
||||
.SH "SEE ALSO"
|
||||
The full documentation for
|
||||
.B dwgfilter
|
||||
is maintained as a Texinfo manual. If the
|
||||
.B info
|
||||
and
|
||||
.B dwgfilter
|
||||
programs are properly installed at your site, the command
|
||||
.IP
|
||||
.B info LibreDWG
|
||||
.PP
|
||||
should give you access to the complete manual.
|
||||
@@ -0,0 +1,56 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||
.TH DWGGREP "1" "June 2026" "dwggrep 0.14" "User Commands"
|
||||
.SH NAME
|
||||
dwggrep \- manual page for dwggrep 0.14
|
||||
.SH SYNOPSIS
|
||||
.B dwggrep
|
||||
[\fI\,OPTIONS\/\fR]... \fI\,pattern files\/\fR
|
||||
.SH DESCRIPTION
|
||||
Search regex pattern in a list of DWGs.
|
||||
.TP
|
||||
\fB\-i\fR
|
||||
Case\-insensitive pattern
|
||||
.TP
|
||||
\fB\-x\fR
|
||||
Extended regex pattern
|
||||
.TP
|
||||
\fB\-c\fR, \fB\-\-count\fR
|
||||
Print only the count of matched elements.
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-no\-filename\fR
|
||||
Print no filename.
|
||||
.TP
|
||||
\fB\-y\fR, \fB\-\-type\fR NAME
|
||||
Search only NAME entities or objects.
|
||||
.TP
|
||||
\fB\-d\fR, \fB\-\-dxf\fR NUM
|
||||
Search only DXF group NUM fields.
|
||||
.TP
|
||||
\fB\-t\fR, \fB\-\-text\fR
|
||||
Search only in TEXT\-like entities.
|
||||
.TP
|
||||
\fB\-b\fR, \fB\-\-blocks\fR
|
||||
Search also in all block definitions.
|
||||
.TP
|
||||
\fB\-n\fR, \fB\-\-tables\fR
|
||||
Search only in table names.
|
||||
.TP
|
||||
\fB\-\-help\fR
|
||||
Display this help and exit
|
||||
.TP
|
||||
\fB\-\-version\fR
|
||||
Output version information and exit
|
||||
.PP
|
||||
GNU LibreDWG online manual: <https://www.gnu.org/software/libredwg/>
|
||||
.SH "SEE ALSO"
|
||||
The full documentation for
|
||||
.B dwggrep
|
||||
is maintained as a Texinfo manual. If the
|
||||
.B info
|
||||
and
|
||||
.B dwggrep
|
||||
programs are properly installed at your site, the command
|
||||
.IP
|
||||
.B info LibreDWG
|
||||
.PP
|
||||
should give you access to the complete manual.
|
||||
@@ -0,0 +1,42 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||
.TH DWGLAYERS "1" "June 2026" "dwglayers 0.14" "User Commands"
|
||||
.SH NAME
|
||||
dwglayers \- manual page for dwglayers 0.14
|
||||
.SH SYNOPSIS
|
||||
.B dwglayers
|
||||
[\fI\,OPTION\/\fR]... \fI\,DWGFILE\/\fR
|
||||
.SH DESCRIPTION
|
||||
Print list of layers.
|
||||
.TP
|
||||
\fB\-x\fR, \fB\-\-extnames\fR
|
||||
prints EXTNAMES (r13\-r14 only)
|
||||
.IP
|
||||
i.e. space instead of _
|
||||
.TP
|
||||
\fB\-f\fR, \fB\-\-flags\fR
|
||||
prints also flags:
|
||||
.IP
|
||||
3 chars for: f for frozen, + or \- for ON or OFF, l for locked
|
||||
.TP
|
||||
\fB\-o\fR, \fB\-\-on\fR
|
||||
prints only ON layers
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
display this help and exit
|
||||
.TP
|
||||
\fB\-\-version\fR
|
||||
output version information and exit
|
||||
.PP
|
||||
GNU LibreDWG online manual: <https://www.gnu.org/software/libredwg/>
|
||||
.SH "SEE ALSO"
|
||||
The full documentation for
|
||||
.B dwglayers
|
||||
is maintained as a Texinfo manual. If the
|
||||
.B info
|
||||
and
|
||||
.B dwglayers
|
||||
programs are properly installed at your site, the command
|
||||
.IP
|
||||
.B info LibreDWG
|
||||
.PP
|
||||
should give you access to the complete manual.
|
||||
@@ -0,0 +1,39 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||
.TH DWGREAD "1" "June 2026" "dwgread 0.14" "User Commands"
|
||||
.SH NAME
|
||||
dwgread \- manual page for dwgread 0.14
|
||||
.SH SYNOPSIS
|
||||
.B dwgread
|
||||
[\fI\,OPTION\/\fR]... \fI\,DWGFILE\/\fR
|
||||
.SH DESCRIPTION
|
||||
Reads the DWG into some optional output format to stdout or some file,
|
||||
and prints error, success or verbose internal progress to stderr.
|
||||
.TP
|
||||
\fB\-v[0\-9]\fR, \fB\-\-verbose\fR [0\-9]
|
||||
verbosity
|
||||
.TP
|
||||
\fB\-O\fR fmt, \fB\-\-format\fR fmt
|
||||
fmt: DXF, DXFB, JSON, minJSON, GeoJSON
|
||||
.TP
|
||||
\fB\-o\fR outfile
|
||||
also defines the output fmt. Default: stdout
|
||||
.TP
|
||||
\fB\-\-help\fR
|
||||
display this help and exit
|
||||
.TP
|
||||
\fB\-\-version\fR
|
||||
output version information and exit
|
||||
.PP
|
||||
GNU LibreDWG online manual: <https://www.gnu.org/software/libredwg/>
|
||||
.SH "SEE ALSO"
|
||||
The full documentation for
|
||||
.B dwgread
|
||||
is maintained as a Texinfo manual. If the
|
||||
.B info
|
||||
and
|
||||
.B dwgread
|
||||
programs are properly installed at your site, the command
|
||||
.IP
|
||||
.B info LibreDWG
|
||||
.PP
|
||||
should give you access to the complete manual.
|
||||
@@ -0,0 +1,46 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||
.TH DWGREWRITE "1" "June 2026" "dwgrewrite 0.14" "User Commands"
|
||||
.SH NAME
|
||||
dwgrewrite \- manual page for dwgrewrite 0.14
|
||||
.SH SYNOPSIS
|
||||
.B dwgrewrite
|
||||
[\fI\,OPTION\/\fR]... \fI\,INFILE \/\fR[\fI\,OUTFILE\/\fR]
|
||||
.SH DESCRIPTION
|
||||
Rewrites the DWG as another DWG.
|
||||
Default OUTFILE: INFILE with <\-rewrite.dwg> appended.
|
||||
.TP
|
||||
\fB\-v[0\-9]\fR, \fB\-\-verbose\fR [0\-9]
|
||||
verbosity
|
||||
.TP
|
||||
\fB\-\-as\fR rNNNN
|
||||
save as version
|
||||
.IP
|
||||
Valid versions:
|
||||
.IP
|
||||
r1.4, r2.6, r2.10, r9, r10, r11, r13, r14, r2000 (default), r2004
|
||||
.IP
|
||||
Planned versions:
|
||||
.IP
|
||||
r2007, r2010, r2013, r2018
|
||||
.HP
|
||||
\fB\-o\fR dwgfile, \fB\-\-file\fR
|
||||
.TP
|
||||
\fB\-\-help\fR
|
||||
display this help and exit
|
||||
.TP
|
||||
\fB\-\-version\fR
|
||||
output version information and exit
|
||||
.PP
|
||||
GNU LibreDWG online manual: <https://www.gnu.org/software/libredwg/>
|
||||
.SH "SEE ALSO"
|
||||
The full documentation for
|
||||
.B dwgrewrite
|
||||
is maintained as a Texinfo manual. If the
|
||||
.B info
|
||||
and
|
||||
.B dwgrewrite
|
||||
programs are properly installed at your site, the command
|
||||
.IP
|
||||
.B info LibreDWG
|
||||
.PP
|
||||
should give you access to the complete manual.
|
||||
@@ -0,0 +1,55 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||
.TH DWGWRITE "1" "June 2026" "dwgwrite 0.14" "User Commands"
|
||||
.SH NAME
|
||||
dwgwrite \- manual page for dwgwrite 0.14
|
||||
.SH SYNOPSIS
|
||||
.B dwgwrite
|
||||
[\fI\,OPTION\/\fR]... [\fI\,-o DWGFILE\/\fR] \fI\,INFILE\/\fR
|
||||
.SH DESCRIPTION
|
||||
Writes a DWG file from various input formats. Only r2000 for now.
|
||||
.TP
|
||||
\fB\-v[0\-9]\fR, \fB\-\-verbose\fR [0\-9]
|
||||
verbosity
|
||||
.TP
|
||||
\fB\-\-as\fR rNNNN
|
||||
save as version
|
||||
.IP
|
||||
Valid versions:
|
||||
.IP
|
||||
r1.1, r1.2, r1.3, r1.4, r2.0, r2.10, r2.21, r2.22, r2.4,
|
||||
r2.5, r2.6, r9, r10, r11, r13, r14, r2000, r2004,
|
||||
r2010, r2013, r2018
|
||||
.IP
|
||||
Planned versions:
|
||||
.IP
|
||||
r2007
|
||||
.TP
|
||||
\fB\-I\fR fmt, \fB\-\-format\fR fmt
|
||||
DXF, DXFB, JSON
|
||||
.IP
|
||||
Planned input formats: GeoJSON, YAML, XML/OGR, GPX
|
||||
.HP
|
||||
\fB\-o\fR dwgfile, \fB\-\-file\fR
|
||||
.TP
|
||||
\fB\-y\fR, \fB\-\-overwrite\fR
|
||||
overwrite existing files
|
||||
.TP
|
||||
\fB\-\-help\fR
|
||||
display this help and exit
|
||||
.TP
|
||||
\fB\-\-version\fR
|
||||
output version information and exit
|
||||
.PP
|
||||
GNU LibreDWG online manual: <https://www.gnu.org/software/libredwg/>
|
||||
.SH "SEE ALSO"
|
||||
The full documentation for
|
||||
.B dwgwrite
|
||||
is maintained as a Texinfo manual. If the
|
||||
.B info
|
||||
and
|
||||
.B dwgwrite
|
||||
programs are properly installed at your site, the command
|
||||
.IP
|
||||
.B info LibreDWG
|
||||
.PP
|
||||
should give you access to the complete manual.
|
||||
@@ -0,0 +1,49 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||
.TH DXF2DWG "1" "June 2026" "dxf2dwg 0.14" "User Commands"
|
||||
.SH NAME
|
||||
dxf2dwg \- manual page for dxf2dwg 0.14
|
||||
.SH SYNOPSIS
|
||||
.B dxf2dwg
|
||||
[\fI\,OPTION\/\fR]... \fI\,DXFFILES \/\fR...
|
||||
.SH DESCRIPTION
|
||||
Converts the DXF to a DWG. Accepts ascii and binary DXF.
|
||||
Default DWGFILE: DXFFILE with .dwg extension in the current directory.
|
||||
Existing files are not overwritten, unless \fB\-y\fR is given.
|
||||
Encoding currently does not work for r2007.
|
||||
.TP
|
||||
\fB\-v[0\-9]\fR, \fB\-\-verbose\fR [0\-9]
|
||||
verbosity
|
||||
.TP
|
||||
\fB\-\-as\fR rNNNN
|
||||
save as version
|
||||
.IP
|
||||
Valid versions:
|
||||
.IP
|
||||
r12, r14, r2000 (default), r2004
|
||||
.IP
|
||||
Planned versions:
|
||||
.IP
|
||||
r9, r10, r11, r2007, r2010, r2013, r2018
|
||||
.TP
|
||||
\fB\-o\fR outfile, \fB\-\-file\fR
|
||||
optional, only valid with one single DXFFILE
|
||||
.TP
|
||||
\fB\-\-help\fR
|
||||
display this help and exit
|
||||
.TP
|
||||
\fB\-\-version\fR
|
||||
output version information and exit
|
||||
.PP
|
||||
GNU LibreDWG online manual: <https://www.gnu.org/software/libredwg/>
|
||||
.SH "SEE ALSO"
|
||||
The full documentation for
|
||||
.B dxf2dwg
|
||||
is maintained as a Texinfo manual. If the
|
||||
.B info
|
||||
and
|
||||
.B dxf2dwg
|
||||
programs are properly installed at your site, the command
|
||||
.IP
|
||||
.B info LibreDWG
|
||||
.PP
|
||||
should give you access to the complete manual.
|
||||
@@ -0,0 +1,54 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||
.TH DXFWRITE "1" "June 2026" "dxfwrite 0.14" "User Commands"
|
||||
.SH NAME
|
||||
dxfwrite \- manual page for dxfwrite 0.14
|
||||
.SH SYNOPSIS
|
||||
.B dxfwrite
|
||||
[\fI\,OPTION\/\fR]... [\fI\,-o DXFFILE\/\fR] \fI\,INFILE\/\fR
|
||||
.SH DESCRIPTION
|
||||
Writes a DXF file from various input formats.
|
||||
.TP
|
||||
\fB\-v[0\-9]\fR, \fB\-\-verbose\fR [0\-9]
|
||||
verbosity
|
||||
.TP
|
||||
\fB\-\-as\fR rNNNN
|
||||
save as version
|
||||
.IP
|
||||
Valid versions:
|
||||
.TP
|
||||
r9, r10, r11, r12, r13, r14, r2000, r2004, r2007,
|
||||
r2010, r2013, r2018
|
||||
.TP
|
||||
\fB\-I\fR fmt, \fB\-\-format\fR fmt
|
||||
DWG, DXF, DXFB, JSON
|
||||
.HP
|
||||
\fB\-o\fR dxffile, \fB\-\-file\fR
|
||||
.TP
|
||||
\fB\-m\fR, \fB\-\-minimal\fR
|
||||
only $ACADVER, HANDSEED and ENTITIES
|
||||
.TP
|
||||
\fB\-b\fR, \fB\-\-binary\fR
|
||||
create a binary DXF
|
||||
.TP
|
||||
\fB\-y\fR, \fB\-\-overwrite\fR
|
||||
overwrite existing files
|
||||
.TP
|
||||
\fB\-\-help\fR
|
||||
display this help and exit
|
||||
.TP
|
||||
\fB\-\-version\fR
|
||||
output version information and exit
|
||||
.PP
|
||||
GNU LibreDWG online manual: <https://www.gnu.org/software/libredwg/>
|
||||
.SH "SEE ALSO"
|
||||
The full documentation for
|
||||
.B dxfwrite
|
||||
is maintained as a Texinfo manual. If the
|
||||
.B info
|
||||
and
|
||||
.B dxfwrite
|
||||
programs are properly installed at your site, the command
|
||||
.IP
|
||||
.B info LibreDWG
|
||||
.PP
|
||||
should give you access to the complete manual.
|
||||
@@ -0,0 +1,264 @@
|
||||
.\" -*- mode: troff; coding: utf-8 -*-
|
||||
.\" Automatically generated by Pod::Man 5.01 (Pod::Simple 3.43)
|
||||
.\"
|
||||
.\" Standard preamble:
|
||||
.\" ========================================================================
|
||||
.de Sp \" Vertical space (when we can't use .PP)
|
||||
.if t .sp .5v
|
||||
.if n .sp
|
||||
..
|
||||
.de Vb \" Begin verbatim text
|
||||
.ft CW
|
||||
.nf
|
||||
.ne \\$1
|
||||
..
|
||||
.de Ve \" End verbatim text
|
||||
.ft R
|
||||
.fi
|
||||
..
|
||||
.\" \*(C` and \*(C' are quotes in nroff, nothing in troff, for use with C<>.
|
||||
.ie n \{\
|
||||
. ds C` ""
|
||||
. ds C' ""
|
||||
'br\}
|
||||
.el\{\
|
||||
. ds C`
|
||||
. ds C'
|
||||
'br\}
|
||||
.\"
|
||||
.\" Escape single quotes in literal strings from groff's Unicode transform.
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.\"
|
||||
.\" If the F register is >0, we'll generate index entries on stderr for
|
||||
.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
|
||||
.\" entries marked with X<> in POD. Of course, you'll have to process the
|
||||
.\" output yourself in some meaningful fashion.
|
||||
.\"
|
||||
.\" Avoid warning from groff about undefined register 'F'.
|
||||
.de IX
|
||||
..
|
||||
.nr rF 0
|
||||
.if \n(.g .if rF .nr rF 1
|
||||
.if (\n(rF:(\n(.g==0)) \{\
|
||||
. if \nF \{\
|
||||
. de IX
|
||||
. tm Index:\\$1\t\\n%\t"\\$2"
|
||||
..
|
||||
. if !\nF==2 \{\
|
||||
. nr % 0
|
||||
. nr F 2
|
||||
. \}
|
||||
. \}
|
||||
.\}
|
||||
.rr rF
|
||||
.\" ========================================================================
|
||||
.\"
|
||||
.IX Title "DWGADD 5"
|
||||
.TH DWGADD 5 2025-03-25 0.14 "User Commands"
|
||||
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
|
||||
.\" way too many mistakes in technical documents.
|
||||
.if n .ad l
|
||||
.nh
|
||||
.SH NAME
|
||||
dwgadd.5 \- Format of the LibreDWG dwgadd example input\-file
|
||||
.SH DESCRIPTION
|
||||
.IX Header "DESCRIPTION"
|
||||
The dwgadd program accepts this input format to add entities and objects
|
||||
to a DWG file.
|
||||
.SH COMMANDS
|
||||
.IX Header "COMMANDS"
|
||||
commands start at each new line and must begin with a keyword and has optional arguments.
|
||||
.PP
|
||||
comments start with # and are ignored.
|
||||
.PP
|
||||
entities are created with the arguments as in the VBA object model AddEntity or AddObject methods.
|
||||
.PP
|
||||
entities are added to the model space by default, objects to the DWG or to parents objects, as e.g.
|
||||
with \f(CW\*(C`xrecord dictionary\*(C'\fR.
|
||||
.IP "\fBreaddwg\fR ""file.dwg""" 4
|
||||
.IX Item "readdwg ""file.dwg"""
|
||||
This must be the very first command. Otherwise a new, empty DWG Document is created.
|
||||
.IP "\fBreaddxf\fR ""file.dxf""" 4
|
||||
.IX Item "readdxf ""file.dxf"""
|
||||
This must be the very first command. Otherwise a new, empty DWG Document is created.
|
||||
.IP "\fBreadjson\fR ""file.json""" 4
|
||||
.IX Item "readjson ""file.json"""
|
||||
This must be the very first command. Otherwise a new, empty DWG Document is created.
|
||||
.IP \fBimperial\fR 4
|
||||
.IX Item "imperial"
|
||||
By default a new DWG is created as metric.
|
||||
There must be maximal one imperial line.
|
||||
.IP "\fBversion\fR number" 4
|
||||
.IX Item "version number"
|
||||
Writes the output file in this version. It accepts numbers only \f(CW\*(C`[0\-9.]+\*(C'\fR,
|
||||
prefixes it with an "r", and converts this string via \f(CWdwg_version_as("r"version)\fR
|
||||
to a \f(CW\*(C`Dwg_Version_Type\*(C'\fR. It accepts only valid DWG versions.
|
||||
There must be maximal one version line.
|
||||
.IP \fBpspace\fR 4
|
||||
.IX Item "pspace"
|
||||
Add further entities to the paper space.
|
||||
.IP \fBmspace\fR 4
|
||||
.IX Item "mspace"
|
||||
Add further entities to the model space again. This is only needed
|
||||
when a previous \f(CW\*(C`pspace\*(C'\fR command was used. mspace is the default.
|
||||
.IP "\fBHEADER\fR.\fIfield\fR = value" 4
|
||||
.IX Item "HEADER.field = value"
|
||||
Assign "VALUES" to a HEADER field. See the full documentation of valid HEADER fields.
|
||||
.IP "\fBpoint\fR (3dpoint)" 4
|
||||
.IX Item "point (3dpoint)"
|
||||
.PD 0
|
||||
.IP "\fBline\fR (3dpoint) (3dpoint)" 4
|
||||
.IX Item "line (3dpoint) (3dpoint)"
|
||||
.PD
|
||||
Create a line variable and entity. You may set all other fields via the
|
||||
\&\f(CW\*(C`line.field = value\*(C'\fR syntax. E.g. \f(CW\*(C`line.layer = "0"\*(C'\fR
|
||||
.IP "\fBray\fR (3dpoint) (3dpoint)" 4
|
||||
.IX Item "ray (3dpoint) (3dpoint)"
|
||||
.PD 0
|
||||
.IP "\fBxline\fR (3dpoint) (3dpoint)" 4
|
||||
.IX Item "xline (3dpoint) (3dpoint)"
|
||||
.IP "\fBtext\fR ""text_value"" (ins_pt) height" 4
|
||||
.IX Item "text ""text_value"" (ins_pt) height"
|
||||
.IP "\fBmtext\fR (ins_pt) height ""text_value""" 4
|
||||
.IX Item "mtext (ins_pt) height ""text_value"""
|
||||
.IP "\fBleader\fR num_points ((points)...) mtext type" 4
|
||||
.IX Item "leader num_points ((points)...) mtext type"
|
||||
.PD
|
||||
mtext must be the immediate previous command.
|
||||
.IP "\fBattribute\fR 8 0 ""prompt"" (0.0 1.0 0.0) ""tag"" ""text""" 4
|
||||
.IX Item "attribute 8 0 ""prompt"" (0.0 1.0 0.0) ""tag"" ""text"""
|
||||
.PD 0
|
||||
.IP "\fBattdef\fR 8 0 ""prompt"" (0.0 1.0 0.0) ""tag"" ""default_text""" 4
|
||||
.IX Item "attdef 8 0 ""prompt"" (0.0 1.0 0.0) ""tag"" ""default_text"""
|
||||
.IP "\fBblock\fR ""name""" 4
|
||||
.IX Item "block ""name"""
|
||||
.PD
|
||||
Starts a new block.
|
||||
.IP \fBendblk\fR 4
|
||||
.IX Item "endblk"
|
||||
Ends the current block.
|
||||
.IP "\fBinsert\fR (ins_pt) ""name"" xscale yscale zscale rotation" 4
|
||||
.IX Item "insert (ins_pt) ""name"" xscale yscale zscale rotation"
|
||||
.PD 0
|
||||
.IP "\fBminsert\fR (ins_pt) ""name"" xscale yscale zscale rotation numrows numcols rowspacing colspacing" 4
|
||||
.IX Item "minsert (ins_pt) ""name"" xscale yscale zscale rotation numrows numcols rowspacing colspacing"
|
||||
.IP "\fBpolyline\fR_2d numpts ((2dpoints) ...)" 4
|
||||
.IX Item "polyline_2d numpts ((2dpoints) ...)"
|
||||
.IP "\fBlwpolyline\fR numpts ((2dpoints) ...)" 4
|
||||
.IX Item "lwpolyline numpts ((2dpoints) ...)"
|
||||
.IP "\fBpolyline\fR_3d numpts ((3dpoints) ...)" 4
|
||||
.IX Item "polyline_3d numpts ((3dpoints) ...)"
|
||||
.IP "\fBarc\fR (center) radius startangle endangle" 4
|
||||
.IX Item "arc (center) radius startangle endangle"
|
||||
.IP "\fBcircle\fR (center) radius" 4
|
||||
.IX Item "circle (center) radius"
|
||||
.IP "\fBpolyline\fR_pface 5 3 ((3dpoints)...) ((faces_4ints)...)" 4
|
||||
.IX Item "polyline_pface 5 3 ((3dpoints)...) ((faces_4ints)...)"
|
||||
.IP "\fBpolyline\fR_mesh num_m_verts num_n_verts ((3dpoints)...)" 4
|
||||
.IX Item "polyline_mesh num_m_verts num_n_verts ((3dpoints)...)"
|
||||
.IP "\fBdimension\fR_aligned (xline1_pt) (xline2_pt) (text_midpt)" 4
|
||||
.IX Item "dimension_aligned (xline1_pt) (xline2_pt) (text_midpt)"
|
||||
.IP "\fBdimension\fR_ang2ln (center_pt) (xline1end_pt) (xline2end_pt) (text_midpt)" 4
|
||||
.IX Item "dimension_ang2ln (center_pt) (xline1end_pt) (xline2end_pt) (text_midpt)"
|
||||
.IP "\fBdimension\fR_ang3pt (center_pt) (xline1_pt) (xline2_pt) (text_midpt)" 4
|
||||
.IX Item "dimension_ang3pt (center_pt) (xline1_pt) (xline2_pt) (text_midpt)"
|
||||
.IP "\fBdimension\fR_diameter (chord_pt) (far_chord_pt) leader_len" 4
|
||||
.IX Item "dimension_diameter (chord_pt) (far_chord_pt) leader_len"
|
||||
.IP "\fBdimension\fR_ordinate (feature_location_pt) (leader_endpt) number" 4
|
||||
.IX Item "dimension_ordinate (feature_location_pt) (leader_endpt) number"
|
||||
.IP "\fBdimension\fR_radius (center_pt) (chord_pt) leader_len" 4
|
||||
.IX Item "dimension_radius (center_pt) (chord_pt) leader_len"
|
||||
.IP "\fBdimension\fR_linear (xline1_pt) (xline2_pt) (def_pt) angle" 4
|
||||
.IX Item "dimension_linear (xline1_pt) (xline2_pt) (def_pt) angle"
|
||||
.IP "\fB3dface\fR (pt1) (pt2) (pt3) [(pt4)]" 4
|
||||
.IX Item "3dface (pt1) (pt2) (pt3) [(pt4)]"
|
||||
.IP "\fBsolid\fR (pt1) (pt2\-2d) (pt3\-2d) (pt4\-2d)" 4
|
||||
.IX Item "solid (pt1) (pt2-2d) (pt3-2d) (pt4-2d)"
|
||||
.IP "\fBtrace\fR (pt1) (pt2\-2d) (pt3\-2d) (pt4\-2d)" 4
|
||||
.IX Item "trace (pt1) (pt2-2d) (pt3-2d) (pt4-2d)"
|
||||
.IP "\fBshape\fR ""name"" (3dpoint) scale angle" 4
|
||||
.IX Item "shape ""name"" (3dpoint) scale angle"
|
||||
.IP "\fBviewport\fR ""name""" 4
|
||||
.IX Item "viewport ""name"""
|
||||
.IP "\fBellipse\fR (center_pt) major_axis axis_ratio" 4
|
||||
.IX Item "ellipse (center_pt) major_axis axis_ratio"
|
||||
.IP "\fBspline\fR num_fitpts ((fitpts)...) (begin_tan_vector) (end_tan_vector)" 4
|
||||
.IX Item "spline num_fitpts ((fitpts)...) (begin_tan_vector) (end_tan_vector)"
|
||||
.IP "\fBdictionary\fR ""name"" ""key"" handle_ref" 4
|
||||
.IX Item "dictionary ""name"" ""key"" handle_ref"
|
||||
.IP "\fBxrecord\fR dictionary ""name""" 4
|
||||
.IX Item "xrecord dictionary ""name"""
|
||||
.IP "\fBtolerance\fR ""testtekst"" (0 0 0) (0 0 1)" 4
|
||||
.IX Item "tolerance ""testtekst"" (0 0 0) (0 0 1)"
|
||||
.IP "\fBmlinestyle\fR ""name""" 4
|
||||
.IX Item "mlinestyle ""name"""
|
||||
.IP "\fBmline\fR num_points ((points)...)" 4
|
||||
.IX Item "mline num_points ((points)...)"
|
||||
.IP "\fBlayout\fR viewport ""name"" ""canonical_media_name""" 4
|
||||
.IX Item "layout viewport ""name"" ""canonical_media_name"""
|
||||
.IP "\fBtorus\fR (center_pt) (normal) major_radius minor_radius" 4
|
||||
.IX Item "torus (center_pt) (normal) major_radius minor_radius"
|
||||
.IP "\fBsphere\fR (center_pt) (normal) radius" 4
|
||||
.IX Item "sphere (center_pt) (normal) radius"
|
||||
.IP "\fBcylinder\fR (center_pt) (normal) height major_radius minor_radius x_radius" 4
|
||||
.IX Item "cylinder (center_pt) (normal) height major_radius minor_radius x_radius"
|
||||
.IP "\fBcone\fR (center_pt) (normal) height major_radius minor_radius x_radius" 4
|
||||
.IX Item "cone (center_pt) (normal) height major_radius minor_radius x_radius"
|
||||
.IP "\fBwedge\fR (center_pt) (normal) length width height" 4
|
||||
.IX Item "wedge (center_pt) (normal) length width height"
|
||||
.IP "\fBbox\fR (center_pt) (normal) length width height" 4
|
||||
.IX Item "box (center_pt) (normal) length width height"
|
||||
.IP "\fBpyramid\fR (center_pt) (normal) height sides radius topradius" 4
|
||||
.IX Item "pyramid (center_pt) (normal) height sides radius topradius"
|
||||
.IP "\fBlayer\fR ""name""" 4
|
||||
.IX Item "layer ""name"""
|
||||
.IP "\fBstyle\fR ""name""" 4
|
||||
.IX Item "style ""name"""
|
||||
.IP "\fBltype\fR ""name""" 4
|
||||
.IX Item "ltype ""name"""
|
||||
.IP "\fBvport\fR ""name""" 4
|
||||
.IX Item "vport ""name"""
|
||||
.IP "\fBview\fR ""name""" 4
|
||||
.IX Item "view ""name"""
|
||||
.IP "\fBvport\fR ""name""" 4
|
||||
.IX Item "vport ""name"""
|
||||
.IP "\fBlayout\fR ""name""" 4
|
||||
.IX Item "layout ""name"""
|
||||
.IP "\fBgroup\fR ""name""" 4
|
||||
.IX Item "group ""name"""
|
||||
.IP "\fIentity\fR.\fIfield\fR = value" 4
|
||||
.IX Item "entity.field = value"
|
||||
.PD
|
||||
Every field of the just created entity or object can be set. See "VALUES"
|
||||
and the full documentation of the available entity fields.
|
||||
.SH VALUES
|
||||
.IX Header "VALUES"
|
||||
Acceptable values must be formatted as:
|
||||
.IP \fBinteger\fR 4
|
||||
.IX Item "integer"
|
||||
negative integers are allowed.
|
||||
\&\f(CW\*(C`0xXXX\*(C'\fR hex numbers are not accepted.
|
||||
.IP \fBfloat\fR 4
|
||||
.IX Item "float"
|
||||
floats may omit the dot.
|
||||
.IP "\fBangle\fR as float in degrees, not radians." 4
|
||||
.IX Item "angle as float in degrees, not radians."
|
||||
.PD 0
|
||||
.IP "\fBstring\fR in double-quotes" 4
|
||||
.IX Item "string in double-quotes"
|
||||
.PD
|
||||
E.g "string" or "" for the empty string.
|
||||
.IP "\fBpoint\fR as (float, float, [float])" 4
|
||||
.IX Item "point as (float, float, [float])"
|
||||
esp. (float float) for 2dpoint
|
||||
.Sp
|
||||
(float float float) for 3dpoint. (0 0 0) is a valid 3dpoint.
|
||||
.ie n .IP "\fBhandle\fR as ""D.D.XXX""" 4
|
||||
.el .IP "\fBhandle\fR as \f(CWD.D.XXX\fR" 4
|
||||
.IX Item "handle as D.D.XXX"
|
||||
<C[0\-12]> as type "." decimal digits as size "." hexadecimal absolute ref.
|
||||
For relative handle codes, like 6,8,10,12 do not take the 3rd handle part asis,
|
||||
but use the absolute ref.
|
||||
.Sp
|
||||
E.g. \f(CW5.1.15\fR for HEADER.LTYPE_BYLAYER.
|
||||
Reference in new issue
Block a user