75 lines
2.1 KiB
Python
75 lines
2.1 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""첨부 zip 압축해제 + 문서화.
|
|
|
|
- zip 내 CP949(EUC-KR) 파일명 mojibake를 복원해 `첨부/[zip]<이름>/` 에 해제
|
|
- 내부 HWP/HWPX → md 변환(hwpx_text.to_md / hwp5_to_md)
|
|
"""
|
|
|
|
import re, sys, zipfile
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
import hwpx_text
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
|
|
|
|
def fixname(n):
|
|
"""zip 엔트리명 CP437 mojibake → CP949 복원."""
|
|
try:
|
|
return n.encode("cp437").decode("cp949")
|
|
except Exception:
|
|
return n
|
|
|
|
|
|
def safe_part(s):
|
|
return re.sub(r'[:*?"<>|]', "_", s).strip()
|
|
|
|
|
|
def extract_one(zip_path):
|
|
zf = zipfile.ZipFile(zip_path)
|
|
dest = zip_path.parent / ("[압축] " + zip_path.stem)
|
|
dest.mkdir(exist_ok=True)
|
|
n = 0
|
|
for info in zf.infolist():
|
|
if info.is_dir():
|
|
continue
|
|
rel = "/".join(safe_part(p) for p in fixname(info.filename).split("/"))
|
|
out = dest / rel
|
|
out.parent.mkdir(parents=True, exist_ok=True)
|
|
with zf.open(info) as src:
|
|
out.write_bytes(src.read())
|
|
n += 1
|
|
return dest, n
|
|
|
|
|
|
def convert_dir(folder):
|
|
ok = fail = 0
|
|
for f in sorted(folder.rglob("*")):
|
|
if f.suffix.lower() not in (".hwp", ".hwpx"):
|
|
continue
|
|
md = f.with_suffix(".md")
|
|
try:
|
|
b = f.read_bytes()[:4]
|
|
if b[:2] == b"PK": # HWPX
|
|
text = hwpx_text.to_md(f)
|
|
elif b.hex() == "d0cf11e0": # 구형 HWP
|
|
text = hwpx_text.hwp5_to_md(f)
|
|
else:
|
|
fail += 1
|
|
continue
|
|
md.write_text(text, encoding="utf-8")
|
|
ok += 1
|
|
except Exception as e:
|
|
print(f" ! {f.name[:50]} :: {type(e).__name__}")
|
|
fail += 1
|
|
return ok, fail
|
|
|
|
|
|
if __name__ == "__main__":
|
|
zips = [Path(a) for a in sys.argv[1:]] or list(ROOT.rglob("첨부/*.zip"))
|
|
for z in zips:
|
|
dest, n = extract_one(z)
|
|
ok, fail = convert_dir(dest)
|
|
print(f"[{z.name}] 해제 {n} / md {ok} 실패 {fail} → {dest.name}")
|