지난 포스팅은...
[3/4] 정관문서 서식잡기(장이름 중앙정렬, 위아래 빈라인 삽입)
3번 질문============== 정관 제 1 장 총칙 제 1조 제 2조.. 제 3조... 제 2 장 주식과 주권 제 4조.... 제 5조.. 제 3 장 임원 제 6조 등과 같을 때. 장의 위치를 페이지 가운데로 위치하고 싶고, 또한 각 장
www.martinii.fun
중앙정렬과 각 조항 위아래 빈 줄을 추가하는 것을 끝으로 정관이나 법령 서식 교정하기 콘텐츠는 마무리를 지으려 한다. 이번 포스팅에서는 추가 정보를 제공하는 것보다는, 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 |
댓글