Recently, I dug into the mystery of merged cells in the PowerPoint table. I ended up figuring out my own methods to deal with merged cells.
Please refer to the following link: https://stackoverflow.com/a/74563860/6354194
There are a few useful functions for merged cells:
1. test if the cell is merged
2. test if the cell is the first(Top-Left) cell of the merged area
3. get the index no. of the cells in the merged area, top to bottom, left to right
4. get the width and height of merged area
5. test if the given cells are within a merged area
VBA에서 파워포인트 표안의 셀이 병합되었는지 확인하는 것이 실제로는 간단하지 않습니다.
엑셀에서도 병합된 셀의 경우 처리하는 것이 쉽지 않습니다. 그나마 엑셀은 MeredArea 등으로 지원을 하지만 파워포인트에서는 셀이 병합되었는지조차 쉽게 알 수 없습니다.
셀이 병합되었는지 알아내는 제일 좋은 방법은 셀의 넓이/높이와 열/행의 크기와 비교하는 방법입니다. 예를 들어 셀의 Width가 해당 Column의 Width보다 크다면 현재 셀은 가로로 병합된 셀에 속하는 것을 알 수 있습니다.
'simply check if the cell is merged
Function IsMerged(oTbl As Table, rr As Integer, cc As Integer) As Boolean
Dim c As Cell
'the current cell
Set c = oTbl.Cell(rr, cc)
'Check the width and height
If c.Shape.Width <> oTbl.Columns(cc).Width Then IsMerged = True
If c.Shape.Height <> oTbl.Rows(rr).Height Then IsMerged = True
End Function
여기에서부터 출발해서 파워포인트에서 지원하지 않는, 병합된 셀과 관련한 함수들이 절실히 필요합니다.
병합된 셀에 값을 넣을 때는 첫번째 셀에만 넣으면 됩니다. 그런데 첫번째 셀 즉, 병합된 셀영역 중 가장 첫번째행의 맨 좌측 셀인지 알아내는 것도 쉽지 않습니다. 조건을 비교해서 첫번째 셀인지 알아내야 합니다. 세로 병합된 경우 첫번째 행이라면 첫번째 셀입니다. 가로로 병합된 경우 현재 셀로 부터 왼쪽으로 몇개의 셀이 합쳐졌는지 확인해서 1개인 경우 세로로도 첫번째 행인 경우 병합된 셀들의 가장 첫번째셀임을 알아낼 수 있습니다.
그 외에도 현재 셀이 병합된 전체 셀에서 몇번째 셀인지, 병합된 셀의 가로, 세로 크기는 몇개인지, 특정 셀이 병합된 영역안에 있는지 판단하는 것도 필요합니다.
특히 병합된 셀영역에서 몇번째 셀인지 알아내는 것은 그리 쉽지 않습니다. 아래 GetMergedIndex함수처럼 병합된 영역에서 몇번째 셀인지 알아낼 수 있습니다. 먼저 몇번째 열의 위치인지 알아내고 현재 셀 위로 몇개의 행이 병합되었는지 파악해서 병합된 행수와 행마다의 열수를 곱해서 모두 합치면 병합된 셀의 순번(index)을 알아낼 수 있습니다.
'Returns the index no. of the cell in the merged area
Function getMergedIndex(oTbl As Table, rr As Integer, cc As Integer) As Integer
Dim c As Cell
Dim i As Integer, j As Integer, mc As Integer
Set c = oTbl.Cell(rr, cc)
'horizontally merged
If c.Shape.Width <> oTbl.Columns(cc).Width Then
'get the horizontal index
For i = 1 To cc - 1
If oTbl.Cell(rr, cc - i).Shape.Left <> c.Shape.Left Then Exit For
Next i
'get the merged row count above
For j = 1 To rr - 1
If oTbl.Cell(rr - j, cc).Shape.Top <> c.Shape.Top Then Exit For
Next j
'if merged both horizontally and vertically
If j > 1 Then
'get the column count of the merged cells above
mc = oTbl.Cell(rr - 1, cc).Shape.Width / oTbl.Columns(cc).Width
'For mc = 1 To oTbl.Columns.Count - cc
' If oTbl.Cell(rr - 1, cc + mc).Shape.Left <> c.Shape.Left Then Exit For
'Next mc
'mc = i + mc - 1
'add up to the merged cells above
getMergedIndex = (j - 1) * mc + i
Else
getMergedIndex = i
End If
'vertically merged
ElseIf c.Shape.Height <> oTbl.Rows(rr).Height Then
For i = 1 To rr - 1
If oTbl.Cell(rr - i, cc).Shape.Top <> c.Shape.Top Then Exit For
Next i
getMergedIndex = i
Else
'not merged
End If
End Function
[ ][ ][ ][ ][ ][ ][ ][ ][ ][01]
[ ][01][02][03][04][ ][ ][ ][ ][02]
[ ][05][06][07][08][ ][ ][ ][ ][ ]
[ ][ ][01][02][03][04][01][02][ ][ ]
[ ][ ][05][06][07][08][03][04][ ][ ]
[ ][ ][ ][ ][ ][ ][05][06][ ][ ]
[01][02][03][04][01][ ][07][08][01][02]
[05][06][07][08][02][ ][ ][ ][03][04]
[01][02][03][04][ ][ ][ ][ ][05][06]
[05][06][07][08][ ][ ][ ][ ][07][08]
이렇게 병합된 셀과 관련한 유용한 함수들을 만들어 모아 보았습니다.
'https://stackoverflow.com/a/74563860/6354194
'In VBA, we can compare the height of the cell and the row. If they are different, it means the cell is merged vertically. The following function determines whether the given cell is merged vertically or horizontally.
'simply check if the cell is merged
Function IsMerged(oTbl As Table, rr As Integer, cc As Integer) As Boolean
Dim c As Cell
'the current cell
Set c = oTbl.Cell(rr, cc)
'Check the width and height
'horizonatally merged
If c.Shape.Width <> oTbl.Columns(cc).Width Then IsMerged = True
'vertically merged
If c.Shape.Height <> oTbl.Rows(rr).Height Then IsMerged = True
End Function
'We can also identify if the cell is the Top-Left(first) cell of the merged area by following method:
Function isTopLeftCell(oTbl As Table, rr As Integer, cc As Integer) As Boolean
Dim i As Integer
With oTbl.Cell(rr, cc).Shape
'horizontally merged
If .Width <> oTbl.Columns(cc).Width Then
'count the left cells merged from the currnet cell
For i = 1 To cc - 1
If oTbl.Cell(rr, cc - i).Shape.Left <> .Left Then Exit For
Next i
'count the rows above
If i = 1 Then
For i = 1 To rr - 1
If oTbl.Cell(rr - i, cc).Shape.Top <> .Top Then Exit For
Next i
If i = 1 Then isTopLeftCell = True: Exit Function
End If
'vertically merged
ElseIf .Height <> oTbl.Rows(rr).Height Then
For i = 1 To rr - 1
If oTbl.Cell(rr - i, cc).Shape.Top <> .Top Then Exit For
Next i
If i = 1 Then isTopLeftCell = True: Exit Function
Else
'isTopLeftCell = False
End If
End With
End Function
'The following returns the width(ww) and height(hh) of the merged area
'Returns the width and height of the merged area
Function getMergedArea(oTbl As Table, rr As Integer, cc As Integer, ByRef ww As Integer, ByRef hh As Integer)
Dim c As Cell
Dim i As Integer, j As Integer
Set c = oTbl.Cell(rr, cc)
'ww = 1: hh = 1
ww = c.Shape.Width / oTbl.Columns(cc).Width
hh = c.Shape.Height / oTbl.Rows(rr).Height
End Function
'If you want get the index no. of the cell in the merged area: (which is the result of my hard working!)
'Returns the index no. of the cell : top to bottom, left to right
Function getMergedIndex(oTbl As Table, rr As Integer, cc As Integer) As Integer
Dim c As Cell
Dim i As Integer, j As Integer, mc As Integer
Set c = oTbl.Cell(rr, cc)
'horizontally merged
If c.Shape.Width <> oTbl.Columns(cc).Width Then
'get the horizontal index
For i = 1 To cc - 1
If oTbl.Cell(rr, cc - i).Shape.Left <> c.Shape.Left Then Exit For
Next i
'get the merged row count above
For j = 1 To rr - 1
If oTbl.Cell(rr - j, cc).Shape.Top <> c.Shape.Top Then Exit For
Next j
'if merged both horizontally and vertically
If j > 1 Then
'get the column count of the merged cells above
mc = oTbl.Cell(rr - 1, cc).Shape.Width / oTbl.Columns(cc).Width
'For mc = 1 To oTbl.Columns.Count - cc
' If oTbl.Cell(rr - 1, cc + mc).Shape.Left <> c.Shape.Left Then Exit For
'Next mc
'mc = i + mc - 1
'add up to the merged cells above
getMergedIndex = (j - 1) * mc + i
Else
getMergedIndex = i
End If
'vertically merged
ElseIf c.Shape.Height <> oTbl.Rows(rr).Height Then
For i = 1 To rr - 1
If oTbl.Cell(rr - i, cc).Shape.Top <> c.Shape.Top Then Exit For
Next i
getMergedIndex = i
Else
'not merged
End If
End Function
''For example:
''[ ][ ][ ][ ][ ][ ][ ][ ][ ][ ]
''[ ][01][02][03][04][ ][ ][ ][ ][ ]
''[ ][05][06][07][08][ ][ ][ ][ ][ ]
''[ ][ ][01][02][03][04][01][02][ ][ ]
''[ ][ ][05][06][07][08][03][04][ ][ ]
''[ ][ ][ ][ ][ ][ ][05][06][ ][ ]
''[01][02][03][04][01][ ][07][08][01][02]
''[05][06][07][08][02][ ][ ][ ][03][04]
''[01][02][03][04][ ][ ][ ][ ][05][06]
''[05][06][07][08][ ][ ][ ][ ][07][08]
'finally, check if two given cells are contained within a merged area
Function isMerged2(oTbl As Table, r1%, c1%, r2%, c2%) As Boolean
If oTbl.Cell(r1, c1).Shape.Left = oTbl.Cell(r2, c2).Shape.Left And _
oTbl.Cell(r1, c1).Shape.Top = oTbl.Cell(r2, c2).Shape.Top And _
oTbl.Cell(r1, c1).Shape.Width = oTbl.Cell(r2, c2).Shape.Width And _
oTbl.Cell(r1, c1).Shape.Height = oTbl.Cell(r2, c2).Shape.Height Then _
isMerged2 = True
End Function
이제 아래와 같은 코드로 실제 셀이 병합되었는지 등을 실행창에서 테스트해볼 수 있습니다.
마지막에 있는 Select 방식은 위와는 다른 방식으로 셀을 선택해서 다른 셀도 선택되었는지 확인해서 셀의 병합 여부를 판단하는 방식입니다.
'Visually print out the merged status of each cell in the table
Sub Test_IsMerged()
Dim r As Integer, c As Integer
Dim shp As Shape
Dim str As String
If ActiveWindow.Selection.Type = ppSelectionNone Then _
MsgBox "Select a table first": Exit Sub
Set shp = ActiveWindow.Selection.ShapeRange(1)
If shp.Type = msoTable Then
For r = 1 To shp.Table.Rows.Count
str = ""
For c = 1 To shp.Table.Columns.Count
'Debug.Print r, c, IsMerged(shp.Table, r, c)
str = str & IIf(IsMerged(shp.Table, r, c), "[M]", "[ ]")
Next c
Debug.Print str
Next r
End If
End Sub
'list the top-left cells of each merged area
Sub Test_isTopLeftCell()
Dim r As Integer, c As Integer, i As Integer
Dim shp As Shape
If ActiveWindow.Selection.Type = ppSelectionNone Then _
MsgBox "Select a table first": Exit Sub
Set shp = ActiveWindow.Selection.ShapeRange(1)
If shp.Type = msoTable Then
For r = 1 To shp.Table.Rows.Count
For c = 1 To shp.Table.Columns.Count
If isTopLeftCell(shp.Table, r, c) Then
i = i + 1
Debug.Print "First cell of the merged area #" & i & " starting at " & r; ", " & c
End If
Next c
Next r
End If
End Sub
'visually print out the merged index no. of each cell in the given table
'Index no. is horizontally first and vertically later
Sub Test_getMergedIndex()
Dim r As Integer, c As Integer
Dim shp As Shape
Dim str As String
Dim i As Integer
If ActiveWindow.Selection.Type = ppSelectionNone Then _
MsgBox "Select a table first": Exit Sub
Set shp = ActiveWindow.Selection.ShapeRange(1)
If shp.Type = msoTable Then
For r = 1 To shp.Table.Rows.Count
str = ""
For c = 1 To shp.Table.Columns.Count
'Debug.Print r, c, getMergedIndex(shp.Table, r, c)
i = getMergedIndex(shp.Table, r, c)
str = str & IIf(i > 0, "[" & Format(i, "00") & "]", "[ ]")
Next c
Debug.Print str
Next r
End If
End Sub
''Merged area #1 starting at 2, 2: width: 4, height: 2
''Merged area #2 starting at 4, 3: width: 4, height: 2
''Merged area #3 starting at 4, 7: width: 2, height: 4
''Merged area #4 starting at 7, 1: width: 4, height: 2
''Merged area #5 starting at 7, 5: width: 1, height: 2
''Merged area #6 starting at 7, 9: width: 2, height: 4
''Merged area #7 starting at 9, 1: width: 4, height: 2
'visually test the merged area
Sub Test_getMergedArea()
Dim r As Integer, c As Integer
Dim shp As Shape
Dim w As Integer, h As Integer, i As Integer
If ActiveWindow.Selection.Type = ppSelectionNone Then _
MsgBox "Select a table first": Exit Sub
Set shp = ActiveWindow.Selection.ShapeRange(1)
If shp.Type = msoTable Then
For r = 1 To shp.Table.Rows.Count
For c = 1 To shp.Table.Columns.Count
If isTopLeftCell(shp.Table, r, c) Then
'If getMergedIndex(shp.Table, r, c) = 1 Then
i = i + 1
Debug.Print "Merged area #" & i & " starting at " & r; ", " & c & ": ",
Call getMergedArea(shp.Table, r, c, w, h)
Debug.Print "width: " & w & ", height: " & h
End If
Next c
Next r
End If
End Sub
''get the width and height of the merged area if given a cell location
''Merged area at 8, 5: width: 1, height: 2
Sub Test2_getMergedArea()
Dim r As Integer, c As Integer
Dim shp As Shape
Dim w As Integer, h As Integer, i As Integer
If ActiveWindow.Selection.Type = ppSelectionNone Then _
MsgBox "Select a table first": Exit Sub
Set shp = ActiveWindow.Selection.ShapeRange(1)
r = 8: c = 5
Debug.Print "Merged area at " & r; ", " & c & ": ",
Call getMergedArea(shp.Table, r, c, w, h)
Debug.Print "width: " & w & ", height: " & h
End Sub
Sub Test_IsMerged2()
Dim r1%, c1%, r2%, c2%
Dim tTbl As Table
r1 = 10: c1 = 10: r2 = 8: c2 = 9
Set tTbl = ActiveWindow.Selection.ShapeRange(1).Table
If isMerged2(tTbl, r1, c1, r2, c2) Then Debug.Print "Merged" Else Debug.Print "Not merged"
End Sub
''check if two cells are merged
Function isMerged2(oTbl As Table, r1%, c1%, r2%, c2%) As Boolean
If oTbl.Cell(r1, c1).Shape.Left = oTbl.Cell(r2, c2).Shape.Left And _
oTbl.Cell(r1, c1).Shape.Top = oTbl.Cell(r2, c2).Shape.Top And _
oTbl.Cell(r1, c1).Shape.Width = oTbl.Cell(r2, c2).Shape.Width And _
oTbl.Cell(r1, c1).Shape.Height = oTbl.Cell(r2, c2).Shape.Height Then _
isMerged2 = True
End Function
Function IsMergedSelect(oTbl As Table, rr As Integer, cc As Integer) As Boolean
Dim i As Integer
Dim c(0 To 4) As Cell ' five cells including the current one
'Get ready to select
ActiveWindow.Selection.Unselect
'First, select the current cell
Set c(0) = oTbl.Cell(rr, cc) 'center(current cell)
c(0).Select
'Check if any other cell is also selected
'Especially, cehck for the four cells around the current one
If cc > 1 Then Set c(1) = oTbl.Cell(rr, cc - 1) 'one column left
If rr > 1 Then Set c(2) = oTbl.Cell(rr - 1, cc) 'one row up(top)
If cc < oTbl.Columns.Count Then Set c(3) = oTbl.Cell(rr, cc + 1) 'one column right
If rr < oTbl.Rows.Count Then Set c(4) = oTbl.Cell(rr + 1, cc) 'one row down(bottom)
'If one of the four cells is also selected, then it means they are merged!
For i = 1 To 4
If Not c(i) Is Nothing Then
If c(i).Selected Then IsMergedSelect = True: Exit For
End If
Next i
'Reset the selection
ActiveWindow.Selection.Unselect
End Function
Function getMergedIndexSel(oTbl As Table, rr As Integer, cc As Integer) As Integer
Dim i As Integer, j As Integer
Dim c(0 To 4) As Cell ' five cells including the current one
'Get ready to select
ActiveWindow.Selection.Unselect
'First, select the current cell
Set c(0) = oTbl.Cell(rr, cc) 'center(current cell)
c(0).Select
'Check if any other cell is also selected
'Especially, cehck for the four cells around the current one
If cc > 1 Then Set c(1) = oTbl.Cell(rr, cc - 1) 'one column left
If rr > 1 Then Set c(2) = oTbl.Cell(rr - 1, cc) 'one row up
If cc < oTbl.Columns.Count Then Set c(3) = oTbl.Cell(rr, cc + 1) 'one column right
If rr < oTbl.Rows.Count Then Set c(4) = oTbl.Cell(rr + 1, cc) 'one row down
'If one of the four cells is also selected, then it means they are merged!
For i = 1 To 4
If Not c(i) Is Nothing Then
If c(i).Selected Then
'to the left
If i = 1 Then
j = 0
Do
If cc - 1 - j = 1 Then Exit Do 'first column
j = j + 1
Loop While oTbl.Cell(rr, cc - 1 - j).Selected
getMergedIndexSel = j + 1
'to the top
ElseIf i = 2 Then
If rr - 2 >= 1 Then 'first row
j = 0
While oTbl.Cell(rr - 2 - j, cc).Selected
j = j + 1
Wend
End If
getMergedIndexSel = j + 2
'right or down
Else
getMergedIndexSel = 1
End If
Exit For
End If
End If
Next i
'Reset the selection
ActiveWindow.Selection.Unselect
End Function
샘플 테스트 파일 첨부합니다.
'PPT+VBA' 카테고리의 다른 글
모핑 슬라이드 사진앨범 생성 (0) | 2023.10.06 |
---|---|
프랙탈1 - Sierpinsky 삼각형 그리기 (0) | 2023.09.27 |
차트(Moon Chart) 자동으로 그리기 (0) | 2023.09.04 |
도형의 Node를 대칭되게 조절 (0) | 2023.08.23 |
엑셀 데이터로 파워포인트 차트 일괄 생성 (0) | 2023.06.28 |
스핀버튼을 눌러 총금액계산 (1) | 2023.05.21 |
SRT 자막을 책갈피 애니메이션효과로 자동 변환 (0) | 2023.05.16 |
오디오책갈피를 이용한 자막 애니메이션 자동 추가 (0) | 2023.05.03 |
최근댓글