/// <summary> /// This is where all the excitement happens. We update the specified group /// with the data from the spreadsheet row. /// </summary> /// <param name="row"></param> /// <param name="group"></param> private void PutRowInGroup(ContentRow row, XmlElement group) { var sheetLanguages = _sheet.Languages; foreach (var lang in sheetLanguages) { var colIndex = _sheet.GetRequiredColumnForLang(lang); var content = row.GetCell(colIndex).Content; var editable = HtmlDom.GetEditableChildInLang(group, lang); if (editable == null) { if (IsEmptyCell(content)) { continue; // Review: or make an empty one? } editable = TranslationGroupManager.MakeElementWithLanguageForOneGroup(group, lang); } if (IsEmptyCell(content)) { editable.ParentNode.RemoveChild(editable); } else { editable.InnerXml = content; } } if (RemoveOtherLanguages) { HtmlDom.RemoveOtherLanguages(@group, sheetLanguages); } }
/// <summary> /// This is where all the excitement happens. We update the specified group /// with the data from the spreadsheet row. /// </summary> /// <param name="row"></param> /// <param name="group"></param> private void PutRowInGroup(ContentRow row, XmlElement group) { var sheetLanguages = _sheet.Languages; foreach (var lang in sheetLanguages) { var colIndex = _sheet.ColumnForLang(lang); var content = row.GetCell(colIndex).Content; var editable = HtmlDom.GetEditableChildInLang(group, lang); if (editable == null) { if (string.IsNullOrEmpty(content)) { continue; // Review: or make an empty one? } var temp = HtmlDom.GetEditableChildInLang(group, "z"); // standard template element if (temp == null) { temp = HtmlDom.GetEditableChildInLang(group, null); // use any available template } if (temp == null) { // Enhance: Eventually we should be able to come up with some sort of default here. // Since this is a rather simple temporary expedient I haven't unit tested it. _warnings.Add( $"Could not import group {_groupOnPageIndex} ({content}) on page {HtmlDom.NumberOfPage(_currentPage)} because it has no bloom-editable children to use as templates."); return; } editable = temp.Clone() as XmlElement; editable.SetAttribute("lang", lang); group.AppendChild(editable); } if (HasMarkup(content)) { try { editable.InnerXml = content; } catch (XmlException) { // It wasn't XML after all? Just somehow had a wedge? Keep the whole lot as text. SetContentAsText(editable, content); } } else { SetContentAsText(editable, content); } } if (RemoveOtherLanguages) { HtmlDom.RemoveOtherLanguages(@group, sheetLanguages); } }
private bool RowHasText(ContentRow row) { var sheetLanguages = _sheet.Languages; foreach (var lang in sheetLanguages) { var colIndex = _sheet.GetRequiredColumnForLang(lang); var content = row.GetCell(colIndex).Content; if (!string.IsNullOrEmpty(content)) { return(true); } } return(false); }
public static void ReadSpreadsheet(InternalSpreadsheet spreadsheet, string path) { var info = new FileInfo(path); using (var package = new ExcelPackage(info)) { var worksheet = package.Workbook.Worksheets[0]; var rowCount = worksheet.Dimension.Rows; var colCount = worksheet.Dimension.Columns; // Enhance: eventually we should detect any rows that are not ContentRows, // and either drop them or make plain SpreadsheetRows. ReadRow(worksheet, 0, colCount, spreadsheet.Header); for (var r = 1; r < rowCount; r++) { var row = new ContentRow(); ReadRow(worksheet, r, colCount, row); spreadsheet.AddRow(row); } } }
private void AddTranslationGroupRow(XmlNode group, string pageNumber, int groupIndex) { var row = new ContentRow(); row.AddCell(InternalSpreadsheet.TextGroupLabel); row.AddCell(pageNumber); row.AddCell(groupIndex.ToString(CultureInfo.InvariantCulture)); foreach (var editable in group.SafeSelectNodes("./*[contains(@class, 'bloom-editable')]").Cast <XmlElement>()) { var lang = editable.Attributes["lang"]?.Value ?? ""; if (lang == "z" || lang == "") { continue; } var index = _spreadsheet.ColumnForLang(lang); var content = Params.RetainMarkup ? editable.InnerXml : GetContent(editable); row.SetCell(index, content); } _spreadsheet.AddRow(row); }
private void PutRowInImage(ContentRow currentRow) { var spreadsheetImgPath = currentRow.GetCell(InternalSpreadsheet.ImageSourceColumnLabel).Content; if (spreadsheetImgPath == InternalSpreadsheet.BlankContentIndicator) { spreadsheetImgPath = "placeHolder.png"; } var destFileName = Path.GetFileName(spreadsheetImgPath); var imgElement = GetImgFromContainer(_currentImageContainer); // Enhance: warn if null? imgElement?.SetAttribute("src", UrlPathString.CreateFromUnencodedString(destFileName).UrlEncoded); // Earlier versions of Bloom often had explicit height and width settings on images. // In case anything of the sort remains, it probably won't be correct for the new image, // so best to get rid of it. imgElement?.RemoveAttribute("height"); imgElement?.RemoveAttribute("width"); // image containers often have a generated title attribute that gives the file name and // notes about its resolution, etc. We think it will be regenerated as needed, but certainly // the one from a previous image is no use. _currentImageContainer.RemoveAttribute("title"); if (_pathToSpreadsheetFolder != null) //currently will only be null in tests { // To my surprise, if spreadsheetImgPath is rooted (a full path), this will just use it, // ignoring _pathToSpreadsheetFolder, which is what we want. var fullSpreadsheetPath = Path.Combine(_pathToSpreadsheetFolder, spreadsheetImgPath); if (spreadsheetImgPath == "placeHolder.png") { // Don't assume the source has it, let's get a copy from files shipped with Bloom fullSpreadsheetPath = Path.Combine(BloomFileLocator.FactoryCollectionsDirectory, "template books", "Basic Book", "placeHolder.png"); } CopyImageFileToDestination(destFileName, fullSpreadsheetPath, imgElement); } }
private void UpdateDataDivFromRow(ContentRow currentRow, string dataBookLabel) { if (dataBookLabel.Contains("branding")) { return; // branding data-div elements are complex and difficult and determined by current collection state } // Only a few of these are worth reporting string whatsUpdated = null; switch (dataBookLabel) { case "coverImage": whatsUpdated = "the image on the cover"; break; case "bookTitle": whatsUpdated = "the book title"; break; case "copyright": whatsUpdated = "copyright information"; break; } if (whatsUpdated != null) { Progress($"Updating {whatsUpdated}."); } var xPath = "div[@data-book=\"" + dataBookLabel + "\"]"; var matchingNodes = _dataDivElement.SelectNodes(xPath); XmlElement templateNode; bool templateNodeIsNew = false; if (matchingNodes.Count > 0) { templateNode = (XmlElement)matchingNodes[0]; } else { templateNodeIsNew = true; templateNode = _destinationDom.RawDom.CreateElement("div"); templateNode.SetAttribute("data-book", dataBookLabel); } var imageSrcCol = _sheet.GetColumnForTag(InternalSpreadsheet.ImageSourceColumnLabel); var imageSrc = imageSrcCol >= 0 ? currentRow.GetCell(imageSrcCol).Content : null; // includes "images" folder var imageFileName = Path.GetFileName(imageSrc); bool specificLanguageContentFound = false; bool asteriskContentFound = false; //Whether or not a data-book div has a src attribute, we found that the innerText is used to set the //src of the image in the actual pages of the document, though we haven't found a case where they differ. //So during export we put the innerText into the image source column, and want to put it into //both src and innertext on import, unless the element is in the noSrcAttribute list if (imageFileName.Length > 0) { templateNode.SetAttribute("lang", "*"); templateNode.InnerText = imageFileName; if (!SpreadsheetExporter.DataDivImagesWithNoSrcAttributes.Contains(dataBookLabel)) { templateNode.SetAttribute("src", imageFileName); } if (templateNodeIsNew) { AddDataBookNode(templateNode); } if (_pathToSpreadsheetFolder != null) { // Make sure the image gets copied over too. var fullSpreadsheetPath = Path.Combine(_pathToSpreadsheetFolder, imageSrc); CopyImageFileToDestination(imageFileName, fullSpreadsheetPath); } } else //This is not an image node { if (dataBookLabel.Equals("coverImage")) { Warn("No cover image found"); } foreach (string lang in _sheet.Languages) { var langVal = currentRow.GetCell(_sheet.GetRequiredColumnForLang(lang)).Content; var langXPath = "div[@data-book=\"" + dataBookLabel + "\" and @lang=\"" + lang + "\"]"; var langMatchingNodes = _dataDivElement.SelectNodes(langXPath).Cast <XmlElement>(); if (!string.IsNullOrEmpty(langVal)) { //Found content in spreadsheet for this language and row if (lang.Equals("*")) { asteriskContentFound = true; } else { specificLanguageContentFound = true; } if (langMatchingNodes.Count() > 0) //Found matching node in dom. Update node. { XmlElement matchingNode = langMatchingNodes.First(); matchingNode.InnerXml = langVal; if (langMatchingNodes.Count() > 1) { Warn("Found more than one " + dataBookLabel + " element for language " + lang + " in the book dom. Only the first will be updated."); } } else //No node for this language and data-book. Create one from template and add. { XmlElement newNode = (XmlElement)templateNode.CloneNode(deep: true); newNode.SetAttribute("lang", lang); newNode.InnerXml = langVal; AddDataBookNode(newNode); } } else //Spreadsheet cell for this row and language is empty. Remove the corresponding node if present. { foreach (XmlNode n in langMatchingNodes.ToArray()) { _dataDivElement.RemoveChild(n); } } } if (RemoveOtherLanguages) { HtmlDom.RemoveOtherLanguages(matchingNodes.Cast <XmlElement>().ToList(), _dataDivElement, _sheet.Languages); } if (asteriskContentFound && specificLanguageContentFound) { Warn(dataBookLabel + " information found in both * language column and other language column(s)"); } } }
public void AddRow(ContentRow row) { _rows.Add(row); row.Spreadsheet = this; }