지난 포스팅은...
중앙정렬과 각 조항 위아래 빈 줄을 추가하는 것을 끝으로 정관이나 법령 서식 교정하기 콘텐츠는 마무리를 지으려 한다. 이번 포스팅에서는 추가 정보를 제공하는 것보다는, 1. 입맛대로 개별의 코드를 하나의 파일로 모아서, 한 번에 실행할 수 있다는 걸 보여주고 싶고, 2. 아래에서 보여줄 예제처럼, 모든 함수를 한 파일에 우겨넣으면 파일이 굉장히 비대해지고, 어느 부분에서 잘못되어 가는지 짚어내기도 쉽지 않다. 특정 함수들을 묶어서 모듈화하면 코드가 짧아진다는 것도 추후에 보여드리고 싶다.
이야기한 바와 같이 이번 포스팅은, 0~3번까지의 포스팅을 종합한 코드와 영상을 보여드릴 예정. 영상은 아래와 같다.
코드와 예제파일은 여기 붙여놓았다. 파이썬과 pywin32모듈, 그리고 한/글 프로그램이 설치되어 있다면 아래의 예제를 실행해볼 수 있다.
마지막으로 아래 자료는 법령집의 예시자료가 아닌, 실제 주식회사 정관 샘플과 교정용 코드다.
공부에 참고하시길 바람. 녹화 영상 먼저.
from tkinter import Tk
from tkinter.filedialog import askopenfilename
import re
import win32com.client as win32
import pyperclip as cb
# 우선 한글을 불러오겠습니다. GUI를 열어서 해당 HWP파일을 선택합니다.
root = Tk()
filename = askopenfilename()
root.destroy()
# 이제 아래아한글을 엽니다.
hwp = win32.gencache.EnsureDispatch("HWPFrame.HwpObject")
hwp.RegisterModule("FilePathCheckDLL", "FilePathCheckerModule")
hwp.Open(filename)
hwp.XHwpWindows.Item(0).Visible = True
hwp.HAction.Run("FrameFullScreen")
# %% 코드반복이 많아질 것을 대비해서, 여기 몇 개만 미리 정의해놓겠습니다.
def 찾아바꾸기(find_string, replace_string):
"""찾아바꾸기 단축함수"""
hwp.Run("MoveSelNextWord")
hwp.HAction.GetDefault("ExecReplace", hwp.HParameterSet.HFindReplace.HSet) # 한/글 특성상 부득이하게 두두번번 실실행행
hwp.HParameterSet.HFindReplace.Direction = hwp.FindDir("Forward")
hwp.HParameterSet.HFindReplace.FindString = find_string
hwp.HParameterSet.HFindReplace.ReplaceString = replace_string
hwp.HParameterSet.HFindReplace.ReplaceMode = 1
hwp.HParameterSet.HFindReplace.IgnoreMessage = 1
hwp.HAction.Execute("ExecReplace", hwp.HParameterSet.HFindReplace.HSet)
hwp.HAction.GetDefault("ExecReplace", hwp.HParameterSet.HFindReplace.HSet)
hwp.HParameterSet.HFindReplace.Direction = hwp.FindDir("Forward")
hwp.HParameterSet.HFindReplace.FindString = find_string
hwp.HParameterSet.HFindReplace.ReplaceString = replace_string
hwp.HParameterSet.HFindReplace.ReplaceMode = 1
hwp.HParameterSet.HFindReplace.IgnoreMessage = 1
hwp.HAction.Execute("ExecReplace", hwp.HParameterSet.HFindReplace.HSet)
return
# 문자입력 함수
def 입력(문자열):
"""문자입력 함수"""
hwp.HAction.GetDefault("InsertText", hwp.HParameterSet.HInsertText.HSet)
hwp.HParameterSet.HInsertText.Text = 문자열
hwp.HAction.Execute("InsertText", hwp.HParameterSet.HInsertText.HSet)
return
def 조항번호_테스트():
"""조항번호 순서 및 갯수 맞는지 테스트
이 부분은 조금 복잡할 수 있는데, "리스트 컴프리헨션"이라는 파이썬 문법입니다.
문서 모든 텍스트를 파이썬으로 불러와서[hwp.GetTextFile]
전체문자열을 엔터[\r\n]로 자른 리스트 중에 "제"로 시작하면서 "("를 가지고 있는 원소만 가져다가
좌우 스페이스 있으면 떼고[strip], "("로 자른 리스트 왼쪽에 있는 값만 리스트로 리턴합니다.
최종적으로는 ["제1조", "제2조", .. "제46조"] 식의 리스트가 됩니다.
len(각조항시작)을 실행해보시면 조항이 50개인 걸 확인하실 수 있습니다. 마지막 조항번호는 (현재) 46이니까, 문제가 있는 게 확실하네요."""
각조항시작 = [int(i[1:].strip().split("조")[0]) for i in hwp.GetTextFile("TEXT", "").split("\r\n") if
i.startswith("제") and "(" in i]
print("검사완료:합격" if 각조항시작 == sorted(각조항시작) and max(각조항시작) == len(
각조항시작) else "검사완료:불합격") # True를 리턴하면 조항 중복이 없고 갯수가 맞다는 뜻입니다.
################################################ 1. 조항번호 정렬작업
# %%
# 이제 번호를 정렬하는 작업을 해보겠습니다. 저는 어떻게 하고 싶냐면,
# hwp.GetText()를 사용하되, 기존의 방법대로 말고 변경할 부분의 위치(튜플)만 사전으로 뽑아다 따로 만들어놓고,
# 다 찾은 다음에 해당 리스트를 순회하면서 숫자를 바꾸겠습니다. 위의 사전은 "위치"를 키, (기존숫자, 바꿀숫자) 튜플을 값으로 만들어 둡니다.
#
# 아래 코드부터 가만히 읽어보시기 바랍니다.
hwp.InitScan()
조항번호 = 1
while True:
문단 = hwp.GetText()
if 문단[0] == 1:
break
else:
if re.match(r"^제[ ]?\d+[ ]?조[ ]?\(?", 문단[1]): # 무식한 방법임. "제?조"로 시작하는 문단으로 가서
hwp.MovePos(201) # moveScanPos : GetText() 실행 후 위치로 이동한다.
hwp.MovePos(27) # moveCurrentCaret : 현재 캐럿이 위치한 곳으로 이동한다.
hwp.Run("MoveSelLineEnd") # 한 줄 선택해서
hwp.Run("Copy") # 복사해놓고
바꿀문자열 = re.sub(pattern="제[ ]?\d+[ ]?조[ ]?\(", repl=f"제{조항번호}조(", string=cb.paste()) # 숫자를 옳게 바꾼다. (옳아도 실행)
입력(바꿀문자열)
조항번호 += 1
hwp.ReleaseScan()
hwp.MovePos(2)
조항번호_테스트()
# 조항번호 정렬작업 끝
################################################ 2-1. 조항제목 괄호 뒤에 빈칸 없으면 추가하기
#%%
def hwp_조항제목_괄호뒤에_공백_없으면_넣기(hwp):
hwp.InitScan()
while True:
text = hwp.GetText()
if text[0] == 1: # 끝에 닿은 경우
break
elif re.match("제[ ]*\d+조[ ]*\(", text[1].replace(" ", "")) and text[1].split(")", maxsplit=1)[1][0] != " ":
hwp.MovePos(201) # moveScanPos : GetText() 실행 후 위치로 이동한다.
hwp.Run("MoveSelLineEnd")
hwp.Run("Copy")
입력(cb.paste().replace(")", ") "))
hwp.InitScan()
continue
else:
pass
hwp.ReleaseScan()
hwp.MovePos(2)
# 위 함수는 그대로 사용합니다.
hwp_조항제목_괄호뒤에_공백_없으면_넣기(hwp)
################################################ 2-2. 조항번호 뒤에 공백 붙이기
def hwp_번호앞에_공백넣기(hwp): # 주의사항 : 번호정렬이 되어 있어야 한다.
hwp.InitScan()
조항번호 = 1
while True:
text = hwp.GetText()
if text[0] == 1: # 끝에 닿은 경우
break
elif text[0] == 0: # 탐색 중 문자열이 수정된 경우
hwp.ReleaseScan()
hwp.InitScan()
continue
elif text[1].replace(" ", "").startswith(f"제{조항번호}조(") and not text[1][1:].startswith(" "):
hwp.MovePos(201) # moveScanPos : GetText() 실행 후 위치로 이동한다.
hwp.HAction.Run("MoveLineBegin")
target_text = re.match(r"^제?\d+조\(?", text[1]).group(0) # '제1조('를 찾음
찾아바꾸기(target_text,
f"제{조항번호: 3}조(") # ": 3"의 의미는, "문자열을 포함하여 세자리가 되어야 하고 남은 칸은 공백(3 앞의 스페이스)으로. 003으로 변경하려면 :03으로 바꾸면 됨."
조항번호 += 1
continue
else:
pass
hwp.ReleaseScan()
hwp.MovePos(2)
# 위 함수는 그대로 사용합니다.
hwp_번호앞에_공백넣기(hwp)
################################################ 3. 장제목, 조항제목 굵게
#%% 결국 정규식으로 다 가게 되네요. 오타도 많고, 예외도 많아서 코드로 처리하면 너무 길어집니다. 정규식으로는 간단해요.
def hwp_진하게(hwp, type):
if type == "장":
hwp.HAction.Run("MoveSelLineEnd")
if hwp.CharShape.Item("Bold") != 1:
hwp.HAction.Run("CharShapeBold")
elif type == "조":
hwp.HAction.GetDefault("RepeatFind", hwp.HParameterSet.HFindReplace.HSet)
hwp.HParameterSet.HFindReplace.FindString = ")"
hwp.HParameterSet.HFindReplace.Direction = hwp.FindDir("Forward")
hwp.HParameterSet.HFindReplace.IgnoreMessage = 1
hwp.HParameterSet.HFindReplace.FindType = 1
hwp.HAction.Execute("RepeatFind", hwp.HParameterSet.HFindReplace.HSet)
hwp.HAction.Run("MoveRight")
hwp.HAction.Run("MoveLeft")
hwp.HAction.Run("MoveSelLineBegin")
if hwp.CharShape.Item("Bold") != 1:
hwp.HAction.Run("CharShapeBold")
hwp.HAction.Run("MoveLineBegin")
def hwp_장과조_찾아가서_진하게(hwp):
# max_number = hwp_get_max_number(hwp)
hwp.InitScan()
장번호 = 1
조항번호 = 1
while True:
text = hwp.GetText()
if text[0] == 1:
break
else:
if re.match(rf"^제{조항번호}조\(?", text[1].replace(" ", "")):
hwp.MovePos(201)
hwp.MovePos(27)
hwp_진하게(hwp, "조")
조항번호 += 1
hwp.InitScan()
if re.match(rf"^제{장번호}장", text[1].replace(" ", "")):
hwp.MovePos(201)
hwp.MovePos(27)
hwp_진하게(hwp, "장")
장번호 += 1
hwp.InitScan()
else:
pass
hwp.ReleaseScan()
hwp.MovePos(2)
hwp_장과조_찾아가서_진하게(hwp)
################################################ 4. 장은 센터정렬, 모든 장조 위아래 빈 줄 하나씩
#%%
def hwp_center_align_and_insert_blank_line(hwp, dir, target):
if target == "장":
hwp.HAction.Run("ParagraphShapeAlignCenter")
else:
pass
if dir == "above":
hwp.HAction.Run("MoveLineBegin")
hwp.HAction.Run("BreakPara")
elif dir == "below":
hwp.HAction.Run("MoveLineEnd")
hwp.HAction.Run("BreakPara")
else:
raise ValueError
def hwp_check_if_blank_exists_above(hwp):
current_position = hwp.GetPos() # 현위치 저장(간혹 다음 검색위치로 튀는문제 조치)
hwp.HAction.Run("MoveLineBegin")
hwp.HAction.Run("MoveSelLeft")
hwp.HAction.Run("MoveSelLeft")
hwp.HAction.Run("Copy")
hwp.SetPos(*current_position) # 방금위치 복원
if cb.paste() == "\r\n\r\n":
return True
else:
return False
def hwp_check_if_blank_exists_below(hwp):
current_position = hwp.GetPos() # 현위치 저장(간혹 다음 검색위치로 튀는문제 조치)
hwp.HAction.Run("MoveLineEnd")
hwp.HAction.Run("MoveSelRight")
hwp.HAction.Run("MoveSelRight")
hwp.HAction.Run("Copy")
hwp.SetPos(*current_position) # 방금위치 복원
if cb.paste() == "\r\n\r\n":
return True
else:
return False
def hwp_find_and_go(hwp):
hwp.InitScan()
장번호 = 1
조번호 = 1
while True:
text = hwp.GetText()
if text[0] == 1:
break
else:
if re.match(rf"^제{장번호}장.+", text[1].strip().replace(" ", "")):
장번호 += 1
hwp.MovePos(201) # moveScanPos : GetText() 실행 후 위치로 이동한다.
hwp.MovePos(27)
if not hwp_check_if_blank_exists_above(hwp):
hwp_center_align_and_insert_blank_line(hwp, "above", "장")
if not hwp_check_if_blank_exists_below(hwp):
hwp_center_align_and_insert_blank_line(hwp, "below", "장")
hwp.InitScan()
if re.match(rf"^제{조번호}조.+", text[1].strip().replace(" ", "")):
조번호 += 1
hwp.MovePos(201) # moveScanPos : GetText() 실행 후 위치로 이동한다.
hwp.MovePos(27)
if not hwp_check_if_blank_exists_above(hwp):
hwp_center_align_and_insert_blank_line(hwp, "above", "조")
hwp.InitScan()
else:
pass
hwp.ReleaseScan()
hwp.MovePos(2)
hwp_find_and_go(hwp)
개인적으로 가장 피곤한 자동화작업을 고르라면, 이런 종류의 서식교정 작업이다.
한 달에도 몇 번씩 반복하는 작업이 아니라면, 이 정도 시간을 자동화 구축에 들이는 것이 손해라는 느낌이 든다.
오늘 포스팅은 여기에서 마친다.
행복한 하루 되시길!
'아래아한글 자동화 > python+hwp 중급' 카테고리의 다른 글
[1/5, HwpEqn 서론] LaTeX 수식을 한/글 수식편집기에 넣을 수 있다? (7) | 2020.12.16 |
---|---|
[한/글자동화 예제] 글자크기를 임의로 바꾸는 간단한 방법 (0) | 2020.12.15 |
[한/글자동화 예제]현재 선택영역에 글자속성 적용하기 (0) | 2020.12.14 |
댓글