מיזוג תאי טבלה
לפעמים שורות מסוימות בטבלה דורשות כותרת או גושי טקסט גדולים שתופסים את כל רוחב הטבלה. לעיצוב נכון של הטבלה, המשתמש יכול למזג מספר תאי טבלה לאחד. Aspose.Words תומך בתאים ממוזגים בעבודה עם כל פורמטי הקלט, כולל ייבוא HTML תוכן.
כיצד למזג תאי טבלה
ב Aspose.Words, תאים ממוזגים מיוצגים על ידי התכונות הבאות של הכיתה CellFormat:
- HorizontalMerge המתאר אם התא הוא חלק ממיזוג אופקי של תאים
- VerticalMerge המתאר אם התא הוא חלק ממיזוג אנכי של תאים
הערכים של מאפיינים אלה קובעים את התנהגות המיזוג של תאים:
- לתא הראשון ברצף של תאים ממוזגים יהיה CellMerge.First
- כל תאים ממוזגים לאחר מכן יהיו CellMerge.Previous
- לתא שאינו ממוזג יהיה CellMerge.None
בדוק אם התא ממוזג
כדי לבדוק אם תא הוא חלק מרצף של תאים ממוזגים, אנו פשוט בודקים את המאפיינים HorizontalMerge ו VerticalMerge.
דוגמת הקוד הבאה מראה כיצד להדפיס את סוג מיזוג התאים האופקי והאנכי:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-C.git. | |
auto doc = MakeObject<Document>(MyDir + u"Table with merged cells.docx"); | |
auto table = System::ExplicitCast<Table>(doc->GetChild(NodeType::Table, 0, true)); | |
for (const auto& row : System::IterateOver<Row>(table->get_Rows())) | |
{ | |
for (const auto& cell : System::IterateOver<Cell>(row->get_Cells())) | |
{ | |
std::cout << PrintCellMergeType(cell) << std::endl; | |
} | |
} |
תאים ממוזגים בטבלה
כדי למזג תאים בטבלה שנוצרה עם DocumentBuilder, עליך להגדיר את סוג המיזוג המתאים לכל תא שבו המיזוג צפוי - תחילה CellMerge.First ולאחר מכן CellMerge.Previous.
כמו כן, עליך לזכור לנקות את הגדרת המיזוג עבור אותם תאים שבהם אין צורך במיזוג – ניתן לעשות זאת על ידי הגדרת התא הראשון שאינו מיזוג ל-CellMerge.None. אם זה לא נעשה, כל התאים בטבלה ימוזגו.
דוגמת הקוד הבאה מראה כיצד ליצור טבלה עם שתי שורות שבהן התאים בשורה הראשונה ממוזגים אופקית:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-C.git. | |
auto doc = MakeObject<Document>(); | |
auto builder = MakeObject<DocumentBuilder>(doc); | |
builder->InsertCell(); | |
builder->get_CellFormat()->set_HorizontalMerge(CellMerge::First); | |
builder->Write(u"Text in merged cells."); | |
builder->InsertCell(); | |
// This cell is merged to the previous and should be empty. | |
builder->get_CellFormat()->set_HorizontalMerge(CellMerge::Previous); | |
builder->EndRow(); | |
builder->InsertCell(); | |
builder->get_CellFormat()->set_HorizontalMerge(CellMerge::None); | |
builder->Write(u"Text in one cell."); | |
builder->InsertCell(); | |
builder->Write(u"Text in another cell."); | |
builder->EndRow(); | |
builder->EndTable(); | |
doc->Save(ArtifactsDir + u"WorkingWithTables.HorizontalMerge.docx"); |
דוגמת הקוד הבאה מראה כיצד ליצור טבלה בת שתי עמודות שבה התאים בעמודה הראשונה ממוזגים אנכית:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-C.git. | |
auto doc = MakeObject<Document>(); | |
auto builder = MakeObject<DocumentBuilder>(doc); | |
builder->InsertCell(); | |
builder->get_CellFormat()->set_VerticalMerge(CellMerge::First); | |
builder->Write(u"Text in merged cells."); | |
builder->InsertCell(); | |
builder->get_CellFormat()->set_VerticalMerge(CellMerge::None); | |
builder->Write(u"Text in one cell"); | |
builder->EndRow(); | |
builder->InsertCell(); | |
// This cell is vertically merged to the cell above and should be empty. | |
builder->get_CellFormat()->set_VerticalMerge(CellMerge::Previous); | |
builder->InsertCell(); | |
builder->get_CellFormat()->set_VerticalMerge(CellMerge::None); | |
builder->Write(u"Text in another cell"); | |
builder->EndRow(); | |
builder->EndTable(); | |
doc->Save(ArtifactsDir + u"WorkingWithTables.VerticalMerge.docx"); |
מיזוג תאי טבלה במקרים אחרים
במצבים אחרים שבהם לא נעשה שימוש ב - DocumentBuilder, כגון בטבלה קיימת, מיזוג תאים בדרך הקודמת עשוי להיות לא קל. במקום זאת, אנו יכולים לעטוף את הפעולות הבסיסיות הכרוכות ביישום מאפייני מיזוג לתאים בשיטה המקלה על המשימה הרבה יותר. שיטה זו דומה לשיטת האוטומציה של מיזוג, הנקראת למזג טווח של תאים בטבלה.
הקוד שלהלן ימזג את תאי הטבלה בטווח שצוין, החל מהתא הנתון ומסתיים בתא הקצה. במקרה זה, הטווח יכול להקיף מספר שורות או עמודות:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-C.git. | |
void MergeCells(SharedPtr<Cell> startCell, SharedPtr<Cell> endCell) | |
{ | |
SharedPtr<Table> parentTable = startCell->get_ParentRow()->get_ParentTable(); | |
// Find the row and cell indices for the start and end cell. | |
System::Drawing::Point startCellPos(startCell->get_ParentRow()->IndexOf(startCell), parentTable->IndexOf(startCell->get_ParentRow())); | |
System::Drawing::Point endCellPos(endCell->get_ParentRow()->IndexOf(endCell), parentTable->IndexOf(endCell->get_ParentRow())); | |
// Create a range of cells to be merged based on these indices. | |
// Inverse each index if the end cell is before the start cell. | |
System::Drawing::Rectangle mergeRange( | |
System::Math::Min(startCellPos.get_X(), endCellPos.get_X()), System::Math::Min(startCellPos.get_Y(), endCellPos.get_Y()), | |
System::Math::Abs(endCellPos.get_X() - startCellPos.get_X()) + 1, System::Math::Abs(endCellPos.get_Y() - startCellPos.get_Y()) + 1); | |
for (const auto& row : System::IterateOver<Row>(parentTable->get_Rows())) | |
{ | |
for (const auto& cell : System::IterateOver<Cell>(row->get_Cells())) | |
{ | |
System::Drawing::Point currentPos(row->IndexOf(cell), parentTable->IndexOf(row)); | |
// Check if the current cell is inside our merge range, then merge it. | |
if (mergeRange.Contains(currentPos)) | |
{ | |
cell->get_CellFormat()->set_HorizontalMerge(currentPos.get_X() == mergeRange.get_X() ? CellMerge::First : CellMerge::Previous); | |
cell->get_CellFormat()->set_VerticalMerge(currentPos.get_Y() == mergeRange.get_Y() ? CellMerge::First : CellMerge::Previous); | |
} | |
} | |
} | |
} |
דוגמת הקוד הבאה מראה כיצד למזג טווח של תאים בין שני תאים מוגדרים:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-C.git. | |
auto doc = MakeObject<Document>(MyDir + u"Table with merged cells.docx"); | |
SharedPtr<Table> table = doc->get_FirstSection()->get_Body()->get_Tables()->idx_get(0); | |
// We want to merge the range of cells found inbetween these two cells. | |
SharedPtr<Cell> cellStartRange = table->get_Rows()->idx_get(0)->get_Cells()->idx_get(0); | |
SharedPtr<Cell> cellEndRange = table->get_Rows()->idx_get(1)->get_Cells()->idx_get(1); | |
// Merge all the cells between the two specified cells into one. | |
MergeCells(cellStartRange, cellEndRange); | |
doc->Save(ArtifactsDir + u"WorkingWithTables.MergeCellRange.docx"); |
בהתאם לגרסת המסגרת בה אתה משתמש, ייתכן שתרצה לחדד שיטה זו על ידי הפיכתה לשיטת הרחבה. במקרה זה, אתה יכול לקרוא לשיטה זו ישירות על תא כדי למזג טווח של תאים, כגון cell1.Merge(cell2)
.
תאים ממוזגים אנכיים ואופקיים בטבלה
כפי שאמרנו במאמרים קודמים, טבלה ב Microsoft Word היא קבוצה של שורות עצמאיות. לכל שורה יש קבוצה של תאים שאינם תלויים בתאים של שורות אחרות. לכן, בטבלה Microsoft Word אין אובייקט כזה כמו “עמודה”, ו" עמודה 1 “הוא משהו כמו"קבוצה של תאים 1 של כל שורה בטבלה”. זה מאפשר למשתמשים יש טבלה שבה, למשל, בשורה 1 מורכב משני תאים-2 סנטימטר ו 1 סנטימטר, ואת השורה 2 מורכב משני תאים שונים-1 סנטימטר ו 2 סנטימטר רחב. ו Aspose.Words תומך במושג זה של טבלאות.
לטבלה ב HTML יש מבנה שונה במהותו: לכל שורה יש אותו מספר תאים ו (חשוב למשימה) לכל תא יש את רוחב העמודה המתאימה, זהה לכל התאים בעמודה אחת. אז אם HorizontalMerge ו VerticalMerge מחזירים ערך שגוי, השתמש בדוגמה הבאה של הקוד:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-C.git. | |
auto doc = MakeObject<Document>(MyDir + u"Table with merged cells.docx"); | |
auto visitor = MakeObject<WorkingWithTables::SpanVisitor>(doc); | |
doc->Accept(visitor); |
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-C.git. | |
/// <summary> | |
/// Helper class that contains collection of rowinfo for each row. | |
/// </summary> | |
class TableInfo : public System::Object | |
{ | |
public: | |
SharedPtr<System::Collections::Generic::List<SharedPtr<WorkingWithTables::RowInfo>>> get_Rows() | |
{ | |
return mRows; | |
} | |
TableInfo() : mRows(MakeObject<System::Collections::Generic::List<SharedPtr<WorkingWithTables::RowInfo>>>()) | |
{ | |
} | |
private: | |
SharedPtr<System::Collections::Generic::List<SharedPtr<WorkingWithTables::RowInfo>>> mRows; | |
}; | |
/// <summary> | |
/// Helper class that contains collection of cellinfo for each cell. | |
/// </summary> | |
class RowInfo : public System::Object | |
{ | |
public: | |
SharedPtr<System::Collections::Generic::List<SharedPtr<WorkingWithTables::CellInfo>>> get_Cells() | |
{ | |
return mCells; | |
} | |
RowInfo() : mCells(MakeObject<System::Collections::Generic::List<SharedPtr<WorkingWithTables::CellInfo>>>()) | |
{ | |
} | |
private: | |
SharedPtr<System::Collections::Generic::List<SharedPtr<WorkingWithTables::CellInfo>>> mCells; | |
}; | |
/// <summary> | |
/// Helper class that contains info about cell. currently here is only colspan and rowspan. | |
/// </summary> | |
class CellInfo : public System::Object | |
{ | |
public: | |
int get_ColSpan() | |
{ | |
return pr_ColSpan; | |
} | |
int get_RowSpan() | |
{ | |
return pr_RowSpan; | |
} | |
CellInfo(int colSpan, int rowSpan) : pr_ColSpan(0), pr_RowSpan(0) | |
{ | |
pr_ColSpan = colSpan; | |
pr_RowSpan = rowSpan; | |
} | |
private: | |
int pr_ColSpan; | |
int pr_RowSpan; | |
}; | |
class SpanVisitor : public DocumentVisitor | |
{ | |
public: | |
/// <summary> | |
/// Creates new SpanVisitor instance. | |
/// </summary> | |
/// <param name="doc"> | |
/// Is document which we should parse. | |
/// </param> | |
SpanVisitor(SharedPtr<Document> doc) : mTables(MakeObject<System::Collections::Generic::List<SharedPtr<WorkingWithTables::TableInfo>>>()) | |
{ | |
mWordTables = doc->GetChildNodes(NodeType::Table, true); | |
// We will parse HTML to determine the rowspan and colspan of each cell. | |
auto htmlStream = MakeObject<System::IO::MemoryStream>(); | |
auto options = MakeObject<Aspose::Words::Saving::HtmlSaveOptions>(); | |
options->set_ImagesFolder(System::IO::Path::GetTempPath()); | |
doc->Save(htmlStream, options); | |
// Load HTML into the XML document. | |
auto xmlDoc = MakeObject<System::Xml::XmlDocument>(); | |
htmlStream->set_Position(0); | |
xmlDoc->Load(htmlStream); | |
// Get collection of tables in the HTML document. | |
SharedPtr<System::Xml::XmlNodeList> tables = xmlDoc->get_DocumentElement()->GetElementsByTagName(u"table"); | |
for (const auto& table : System::IterateOver(tables)) | |
{ | |
auto tableInf = MakeObject<WorkingWithTables::TableInfo>(); | |
// Get collection of rows in the table. | |
SharedPtr<System::Xml::XmlNodeList> rows = table->SelectNodes(u"tr"); | |
for (const auto& row : System::IterateOver(rows)) | |
{ | |
auto rowInf = MakeObject<WorkingWithTables::RowInfo>(); | |
// Get collection of cells. | |
SharedPtr<System::Xml::XmlNodeList> cells = row->SelectNodes(u"td"); | |
for (const auto& cell : System::IterateOver(cells)) | |
{ | |
// Determine row span and colspan of the current cell. | |
SharedPtr<System::Xml::XmlAttribute> colSpanAttr = cell->get_Attributes()->idx_get(u"colspan"); | |
SharedPtr<System::Xml::XmlAttribute> rowSpanAttr = cell->get_Attributes()->idx_get(u"rowspan"); | |
int colSpan = colSpanAttr == nullptr ? 0 : System::Int32::Parse(colSpanAttr->get_Value()); | |
int rowSpan = rowSpanAttr == nullptr ? 0 : System::Int32::Parse(rowSpanAttr->get_Value()); | |
auto cellInf = MakeObject<WorkingWithTables::CellInfo>(colSpan, rowSpan); | |
rowInf->get_Cells()->Add(cellInf); | |
} | |
tableInf->get_Rows()->Add(rowInf); | |
} | |
mTables->Add(tableInf); | |
} | |
} | |
VisitorAction VisitCellStart(SharedPtr<Cell> cell) override | |
{ | |
int tabIdx = mWordTables->IndexOf(cell->get_ParentRow()->get_ParentTable()); | |
int rowIdx = cell->get_ParentRow()->get_ParentTable()->IndexOf(cell->get_ParentRow()); | |
int cellIdx = cell->get_ParentRow()->IndexOf(cell); | |
int colSpan = 0; | |
int rowSpan = 0; | |
if (tabIdx < mTables->get_Count() && rowIdx < mTables->idx_get(tabIdx)->get_Rows()->get_Count() && | |
cellIdx < mTables->idx_get(tabIdx)->get_Rows()->idx_get(rowIdx)->get_Cells()->get_Count()) | |
{ | |
colSpan = mTables->idx_get(tabIdx)->get_Rows()->idx_get(rowIdx)->get_Cells()->idx_get(cellIdx)->get_ColSpan(); | |
rowSpan = mTables->idx_get(tabIdx)->get_Rows()->idx_get(rowIdx)->get_Cells()->idx_get(cellIdx)->get_RowSpan(); | |
} | |
std::cout << tabIdx << "." << rowIdx << "." << cellIdx << " colspan=" << colSpan << "\t rowspan=" << rowSpan << std::endl; | |
return VisitorAction::Continue; | |
} | |
private: | |
SharedPtr<System::Collections::Generic::List<SharedPtr<WorkingWithTables::TableInfo>>> mTables; | |
SharedPtr<NodeCollection> mWordTables; | |
}; |
המר לתאים ממוזגים אופקית
לפעמים לא ניתן לזהות אילו תאים מתמזגים כי כמה גרסאות חדשות יותר של Microsoft Word כבר לא משתמשות בדגלי המיזוג כאשר תאים מתמזגים באופן אופקי. אבל במצבים שבהם תאים מתמזגים לתא באופן אופקי על ידי רוחבם באמצעות דגלי מיזוג, Aspose.Words מספק את שיטת ConvertToHorizontallyMergedCells
להמרת תאים. שיטה זו פשוט הופכת את הטבלה ומוסיפה תאים חדשים לפי הצורך.
הדוגמה הבאה של הקוד מראה את השיטה לעיל בפעולה:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-C.git. | |
auto doc = MakeObject<Document>(MyDir + u"Table with merged cells.docx"); | |
SharedPtr<Table> table = doc->get_FirstSection()->get_Body()->get_Tables()->idx_get(0); | |
// Now merged cells have appropriate merge flags. | |
table->ConvertToHorizontallyMergedCells(); |