diff --git a/R/wb_load.R b/R/wb_load.R
index ee346be9b..a043d03b4 100644
--- a/R/wb_load.R
+++ b/R/wb_load.R
@@ -1538,6 +1538,36 @@ wb_load <- function(
# correct sheet references and replace our replacement with it.
if (!data_only && length(workbookBIN)) {
+ # we need to update the order of customSheetView children. Incorrect orders
+ # causes spreadsheet software to be unable to load and recover the file.
+ for (sheet in seq_along(wb$worksheets)) {
+ if (length(wb$worksheets[[sheet]]$customSheetViews) == 0) next
+
+ cvs <- xml_node(wb$worksheets[[sheet]]$customSheetViews, "customSheetViews", "customSheetView")
+
+ # chart sheets have a reduced custom view
+ exp_attr <- c(
+ "guid", "scale", "colorId", "showPageBreaks", "showFormulas",
+ "showGridLines", "showRowCol", "outlineSymbols", "zeroValues",
+ "fitToPage", "printArea", "filter", "showAutoFilter", "hiddenRows",
+ "hiddenColumns", "state", "filterUnique", "view", "showRuler",
+ "topLeftCell", "zoomToFit"
+ )
+ exp_nams <- c(
+ "pane", "selection", "rowBreaks", "colBreaks", "pageMargins",
+ "printOptions", "pageSetup", "headerFooter", "autoFilter", "extLst"
+ )
+ cv <- read_xml2df(read_xml(cvs), "customSheetView", vec_attrs = exp_attr, vec_chlds = exp_nams)
+
+ # headerFooter cause issues. they are (a) not added to the correct node
+ # and (b) brick the entire XML structure
+ cv$headerFooter <- ""
+
+ cvs <- write_df2xml(cv[c(exp_attr, exp_nams)], "customSheetView", vec_attrs = exp_attr, vec_chlds = exp_nams)
+
+ wb$worksheets[[sheet]]$customSheetViews <- xml_node_create("customSheetViews", xml_children = cvs)
+ }
+
if (length(wb$workbook$xti)) {
# create data frame containing sheet names for Xti entries
xti <- rbindlist(xml_attr(wb$workbook$xti, "xti"))
diff --git a/src/xlsb.cpp b/src/xlsb.cpp
index 1d8650aee..17de06455 100644
--- a/src/xlsb.cpp
+++ b/src/xlsb.cpp
@@ -785,8 +785,64 @@ int32_t table_bin(std::string filePath, std::string outPath, bool debug) {
case BrtBeginCustomFilters14:
case BrtBeginCustomRichFilters:
{
- Rcpp::warning("Custom Filter found. This is not handled.");
- bin.seekg(size, bin.cur);
+ int32_t fAnd = 0;
+
+ // in xlsb it is flipped
+ fAnd = readbin(fAnd, bin, swapit) ^ 1;
+
+ out << "";
+
+ break;
+ }
+
+ case BrtCustomFilter:
+ case BrtCustomFilter14:
+ {
+ int8_t vts = 0, grbitSgn = 0;
+ double union_val = 0;
+ std::string vtsStringXls;
+
+ vts = readbin(vts, bin, swapit);
+ grbitSgn = readbin(grbitSgn, bin, swapit);
+
+ if (vts == 4) {
+ // a double
+ union_val = Xnum(bin, swapit);
+ } else if (vts == 8) {
+ // a bool
+ int8_t boolean = 0;
+ boolean = readbin(boolean, bin, swapit);
+ union_val = static_cast(readbin(boolean, bin, swapit));
+ for (int8_t blk = 0; blk < 7; ++blk) {
+ readbin(boolean, bin, swapit);
+ }
+ } else {
+ // ignore
+ readbin(union_val, bin, swapit);
+ readbin(union_val, bin, swapit);
+ }
+
+ if (vts == 6) // a string
+ vtsStringXls = XLWideString(bin, swapit);
+
+ out << "";
+
+ break;
+ }
+
+ case BrtEndCustomFilters:
+ case BrtEndCustomRichFilters:
+ {
+
+ out << "" << std::endl;
break;
}
@@ -1032,10 +1088,12 @@ int32_t comments_bin(std::string filePath, std::string outPath, bool debug) {
ref = lref + ":" + rref;
}
- guid0 = readbin(guid0, bin, swapit);
- guid1 = readbin(guid1, bin, swapit);
- guid2 = readbin(guid2, bin, swapit);
- guid3 = readbin(guid3, bin, swapit);
+ std::vector guids(4);
+ guids[0] = readbin(guid0, bin, 0);
+ guids[1] = readbin(guid1, bin, 0);
+ guids[2] = readbin(guid2, bin, 0);
+ guids[3] = readbin(guid3, bin, 0);
+
out << " defNams, xtis, reference_type;
+ std::vector defNams, xtis, reference_type, customWorkbookView;
defNams.push_back("");
xtis.push_back("");
@@ -1568,6 +1626,82 @@ int32_t workbook_bin(std::string filePath, std::string outPath, bool debug) {
break;
}
+ case BrtUserBookView:
+ {
+ if (debug) Rcpp::Rcout << "" << std::endl;
+
+ std::ostringstream cwv;
+
+ int32_t xLeft = 0, xRight = 0, yTop = 0, yBot = 0, iTabid = 0, iTabRatio = 0, guid0 = 0, guid1 = 0, guid2 = 0, guid3 = 0, flags = 0;
+ int16_t wMergeInterval = 0;
+ std::string stName;
+
+ xLeft = readbin(xLeft, bin, swapit);
+ xRight = readbin(xRight, bin, swapit);
+ yTop = readbin(yTop, bin, swapit);
+ yBot = readbin(yBot, bin, swapit);
+ iTabid = readbin(iTabid, bin, swapit);
+ iTabRatio = readbin(iTabRatio, bin, swapit);
+
+ std::vector guids(4);
+ guids[0] = readbin(guid0, bin, 0);
+ guids[1] = readbin(guid1, bin, 0);
+ guids[2] = readbin(guid2, bin, 0);
+ guids[3] = readbin(guid3, bin, 0);
+
+ wMergeInterval = readbin(wMergeInterval, bin, swapit);
+
+ flags = readbin(flags, bin, swapit);
+
+ BrtUserBookViewFields *fields = (BrtUserBookViewFields*)&flags;
+
+ stName = XLWideString(bin, swapit);
+
+ std::string showComments;
+ if (fields->mdDspNote == 0) showComments = "commNone";
+ if (fields->mdDspNote == 1) showComments = "commIndAndComment";
+ if (fields->mdDspNote == 2) showComments = "commIndicator";
+
+ std::string showObjects;
+ if (fields->mdHideObj == 0) showObjects = "all";
+ if (fields->mdHideObj == 1) showObjects = "placeholders";
+ if (fields->mdHideObj == 2) showObjects = "none";
+
+ cwv << "fTimedUpdate) cwv << " autoUpdate=\"" << fields->fTimedUpdate << "\"" << std::endl;
+ if (fields->fTimedUpdate) cwv << " mergeInterval=\"" << wMergeInterval << "\"" << std::endl;
+ if (fields->fAllMemChanges) cwv << " changesSavedWin=\"" << fields->fAllMemChanges << "\"" << std::endl;
+ if (fields->fOnlySync) cwv << " onlySync=\"" << fields->fOnlySync << "\"" << std::endl;
+ if (fields->fPersonalView) cwv << " personalView=\"" << fields->fPersonalView << "\"" << std::endl;
+ if (!fields->fPrintIncl) cwv << " includePrintSettings=\"" << fields->fPrintIncl << "\"" << std::endl;
+ // wrong?
+ // if (!fields->fRowColIncl) cwv << " includeHiddenRowCol=\"" << fields->fRowColIncl << "\"" << std::endl;
+ if (fields->fZoom) cwv << " maximized=\"" << fields->fZoom << "\"" << std::endl;
+ if (fields->fIconic) cwv << " minimized=\"" << fields->fIconic << "\"" << std::endl;
+ if (!fields->fDspHScroll) cwv << " showHorizontalScroll=\"" << fields->fDspHScroll << "\"" << std::endl;
+ if (!fields->fDspVScroll) cwv << " showVerticalScroll=\"" << fields->fDspVScroll << "\"" << std::endl;
+ if (!fields->fBotAdornment) cwv << " showSheetTabs=\"" << fields->fBotAdornment << "\"" << std::endl;
+ if (xLeft > 0) cwv << " xWindow=\"" << xLeft << "\"" << std::endl;
+ if (yTop > 0) cwv << " yWindow=\"" << yTop << "\"" << std::endl;
+ if (xRight > 0) cwv << " windowWidth=\"" << xRight << "\"" << std::endl;
+ if ((yBot - yTop) > 0) cwv << " windowHeight=\"" << (yBot - yTop) << "\"" << std::endl;
+ if (iTabRatio != 600) cwv << " tabRatio=\"" << iTabRatio << "\"" << std::endl;
+ cwv << " activeSheetId=\"" << iTabid << "\"" << std::endl;
+ if (!fields->fDspFmlaBar) cwv << " showFormulaBar=\"" << fields->fDspFmlaBar << "\"" << std::endl;
+ if (!fields->fDspStatus) cwv << " showStatusbar=\"" << fields->fDspStatus << "\"" << std::endl;
+ if (showComments != "commIndicator") cwv << " showComments=\"" << showComments << "\"" << std::endl;
+ if (showObjects != "all") cwv << " showObjects=\"" << showObjects << "\"" << std::endl;
+ cwv << "/>" << std::endl;
+
+ // Rcpp::Rcout << xLeft << ": " << xRight << ": " << yTop << ": " << yBot << std::endl;
+
+ customWorkbookView.push_back(cwv.str());
+
+ break;
+ }
+
case BrtBeginBundleShs:
{
if (debug) Rcpp::Rcout << "" << std::endl;
@@ -1941,6 +2075,17 @@ int32_t workbook_bin(std::string filePath, std::string outPath, bool debug) {
}
}
+ if (customWorkbookView.size()) {
+ out << "" << std::endl;
+ for (size_t i = 0; i < customWorkbookView.size(); ++i) {
+ if (debug)
+ Rcpp::Rcout << customWorkbookView[i] << std::endl;
+ out << customWorkbookView[i] << std::endl;
+ }
+ out << "" << std::endl;
+ }
+
+
if (debug) Rcpp::Rcout << "" << std::endl;
out << "" << std::endl;
bin.seekg(size, bin.cur);
@@ -2289,7 +2434,7 @@ int32_t worksheet_bin(std::string filePath, bool chartsheet, std::string outPath
if (colLeft > 0 || rwTop > 0)
out << " topLeftCell=\"" << int_to_col(colLeft + 1) << std::to_string(rwTop + 1) << "\"";
if (xlView)
- out << " view=\"" << xlView << "\"";
+ out << " view=\"" << XLView(xlView) << "\"";
if (fields->fWnProt)
out << " windowProtection=\"" << fields->fWnProt << "\"";
if (wScale)
@@ -2376,20 +2521,27 @@ int32_t worksheet_bin(std::string filePath, bool chartsheet, std::string outPath
std::string stHeaderFirst = XLNullableWideString(bin, swapit);
std::string stFooterFirst = XLNullableWideString(bin, swapit);
+ BrtBeginHeaderFooterFields *fields = (BrtBeginHeaderFooterFields *)&flags;
+
if (debug)
Rcpp::Rcout << stHeader<< ": " << stFooter << ": " <<
stHeaderEven << ": " << stFooterEven << ": " <<
stHeaderFirst << ": " << stFooterFirst << std::endl;
- out << "" <<
- "" << stHeader <<"" <<
- "" << stFooter <<"" <<
- "" << stHeaderFirst <<"" <<
- "" << stFooterFirst <<"" <<
- "" << stHeaderEven <<"" <<
- "" << stHeaderEven <<"" <<
- // "" << <<"" <<
- "" << std::endl;
+ out << "fHFDiffOddEven) out << " differentOddEven=\"" << fields->fHFDiffOddEven << "\"" << std::endl;
+ if (fields->fHFDiffFirst) out << " differentFirst=\"" << fields->fHFDiffFirst << "\"" << std::endl;
+ if (fields->fHFScaleWithDoc) out << " scaleWithDoc=\"" << fields->fHFScaleWithDoc << "\"" << std::endl;
+ if (fields->fHFAlignMargins) out << " alignWithMargins=\"" << fields->fHFAlignMargins << "\"" << std::endl;
+ out << ">" << std::endl;
+
+ if (!stHeader.empty()) out << "" << stHeader <<"" << std::endl;
+ if (!stFooter.empty()) out << "" << stFooter <<"" << std::endl;
+ if (!stHeaderEven.empty()) out << "" << stHeaderEven <<"" << std::endl;
+ if (!stFooterEven.empty()) out << "" << stFooterEven <<"" << std::endl;
+ if (!stHeaderFirst.empty()) out << "" << stHeaderFirst <<"" << std::endl;
+ if (!stFooterFirst.empty()) out << "" << stFooterFirst <<"" << std::endl;
+ out << "" << std::endl;
break;
}
@@ -3419,14 +3571,222 @@ int32_t worksheet_bin(std::string filePath, bool chartsheet, std::string outPath
break;
}
+
case BrtBeginCustomFilters:
case BrtBeginCustomFilters14:
case BrtBeginCustomRichFilters:
{
- if (debug) Rcpp::Rcout << "BrtBeginCustom..." << std::endl;
- Rcpp::warning("Custom Filter found. This is not handled.");
- bin.seekg(size, bin.cur);
+ int32_t fAnd = 0;
+
+ // in xlsb it is flipped
+ fAnd = readbin(fAnd, bin, swapit) ^ 1;
+
+ out << "";
+
+ break;
+ }
+
+ case BrtCustomFilter:
+ case BrtCustomFilter14:
+ {
+ int8_t vts = 0, grbitSgn = 0;
+ double union_val = 0;
+ std::string vtsStringXls;
+
+ vts = readbin(vts, bin, swapit);
+ grbitSgn = readbin(grbitSgn, bin, swapit);
+ if (vts == 4) {
+ // a double
+ union_val = Xnum(bin, swapit);
+ } else if (vts == 8) {
+ // a bool
+ int8_t boolean = 0;
+ boolean = readbin(boolean, bin, swapit);
+ union_val = static_cast(readbin(boolean, bin, swapit));
+ for (int8_t blk = 0; blk < 7; ++blk) {
+ readbin(boolean, bin, swapit);
+ }
+ } else {
+ // ignore
+ readbin(union_val, bin, swapit);
+ readbin(union_val, bin, swapit);
+ }
+
+ if (vts == 6) // a string
+ vtsStringXls = XLWideString(bin, swapit);
+
+ out << "";
+
+ break;
+ }
+
+ case BrtEndCustomFilters:
+ case BrtEndCustomRichFilters:
+ {
+
+ out << "" << std::endl;
+
+ break;
+ }
+
+ case BrtBeginUserCsViews:
+ case BrtBeginUserShViews:
+ {
+ if (debug) Rcpp::Rcout << "BrtBeginUserXXViews" << std::endl;
+ out << "" << std::endl;
+ break;
+ }
+
+ case BrtBeginUserCsView:
+ {
+ if (debug) Rcpp::Rcout << "BrtBeginUserCsView" << std::endl;
+
+ int32_t guid0 = 0, guid1 = 0, guid2 = 0, guid3 = 0, iTabId = 0, dwScale = 0, flags = 0;
+
+ std::vector guids(4);
+ guids[0] = readbin(guid0, bin, 0);
+ guids[1] = readbin(guid1, bin, 0);
+ guids[2] = readbin(guid2, bin, 0);
+ guids[3] = readbin(guid3, bin, 0);
+
+ iTabId = readbin(iTabId, bin, swapit);
+ if (iTabId < 1 || iTabId > 65535)
+ Rcpp::stop("iTabId out of range");
+
+ dwScale = readbin(dwScale, bin, swapit);
+ if (dwScale < 0 || dwScale > 400) // dialog sheet 0 else 10
+ Rcpp::stop("dwScale out of range");
+
+ flags = readbin(flags, bin, swapit);
+ // hsState
+ // fZoomToFit
+
+ out << "" << std::endl;
+
+ break;
+ }
+
+ case BrtBeginUserShView:
+ {
+ if (debug) Rcpp::Rcout << "BrtBeginUserShView" << std::endl;
+
+ int32_t guid0 = 0, guid1 = 0, guid2 = 0, guid3 = 0, iTabId = 0, dwScale = 0, icv = 0, flags = 0;
+
+ std::vector guids(4);
+ guids[0] = readbin(guid0, bin, 0);
+ guids[1] = readbin(guid1, bin, 0);
+ guids[2] = readbin(guid2, bin, 0);
+ guids[3] = readbin(guid3, bin, 0);
+
+ iTabId = readbin(iTabId, bin, swapit);
+ if (iTabId < 1 || iTabId > 65535)
+ Rcpp::stop("iTabId out of range");
+
+ dwScale = readbin(dwScale, bin, swapit);
+ if (dwScale < 0 || dwScale > 400) // dialog sheet 0 else 10
+ Rcpp::stop("dwScale out of range");
+
+ icv = readbin(icv, bin, swapit);
+ if (icv > 64)
+ Rcpp::stop("icv out of range");
+
+ flags = readbin(flags, bin, swapit);
+
+ // rfxTopLeft
+ std::vector rfx = UncheckedRfX(bin, swapit);
+
+ BrtBeginUserShViewFields *fields = (BrtBeginUserShViewFields *)&flags;
+
+ out << "fShowBrks)
+ out << " showPageBreaks=\"" << (int16_t)fields->fShowBrks << "\"";
+ if (fields->fDspFmlaSv)
+ out << " showFormulas=\"" << (int16_t)fields->fDspFmlaSv << "\"";
+ if (!fields->fDspGridSv)
+ out << " showGridLines=\"" << (int16_t)fields->fDspGridSv << "\"";
+ if (!fields->fDspRwColSv)
+ out << " showRowCol=\"" << (int16_t)fields->fDspRwColSv << "\"";
+ if (!fields->fDspGutsSv)
+ out << " outlineSymbols=\"" << (int16_t)fields->fDspGutsSv << "\"";
+ if (!fields->fDspZerosSv)
+ out << " zeroValues=\"" << (int16_t)fields->fDspZerosSv << "\"";
+ if (fields->fFitToPage)
+ out << " fitToPage=\"" << (int16_t)fields->fFitToPage << "\"";
+ if (fields->fPrintArea)
+ out << " printArea=\"" << (int16_t)fields->fPrintArea << "\"";
+ if (fields->fFilterMode)
+ out << " filter=\"" << (int16_t)fields->fFilterMode << "\"";
+ if (fields->fEzFilter)
+ out << " showAutoFilter=\"" << (int16_t)fields->fEzFilter << "\"";
+ // if (iTabId) // not used?
+ // out << " tabSelected=\"" << iTabId << "\"";
+ if (fields->fHiddenRw)
+ out << " hiddenRows=\"" << (int16_t)fields->fHiddenRw << "\"";
+ if (fields->fHiddenCol)
+ out << " hiddenColumns=\"" << (int16_t)fields->fHiddenCol << "\"";
+ if (fields->hsState)
+ out << " state=\"" << (int16_t)fields->hsState << "\"";
+ if (fields->fFilterUnique)
+ out << " filterUnique=\"" << (int16_t)fields->fFilterUnique << "\"";
+ if (fields->fSheetLayoutView)
+ out << " view=\"" << "pageBreakPreview" << "\"";
+ if (fields->fPageLayoutView)
+ out << " view=\"" << "pageLayout" << "\"";
+ if (!fields->fRuler)
+ out << " showRuler=\"" << (int16_t)fields->fRuler << "\"";
+ if (rfx[0] > 1 && rfx[2] > 0)
+ out << " topLeftCell=\"" << int_to_col(rfx[2] + 1) << std::to_string(rfx[0] + 1) << "\"";
+
+ out << ">" << std::endl;
+
+ // order matters for
+ out << "fHorizontal)
+ out << " horizontalCentered = \"" << (int16_t)fields->fHorizontal << "\"";
+ if (fields->fVertical)
+ out << " verticalCentered = \"" << (int16_t)fields->fVertical << "\"";
+ if (fields->fPrintRwCol)
+ out << " headings = \"" << (int16_t)fields->fPrintRwCol << "\"";
+ if (fields->fDspGridSv)
+ out << " gridLines = \"" << (int16_t)fields->fDspGridSv << "\"";
+ if (!fields->fPrintGrid)
+ out << " gridLinesSet = \"" << (int16_t)fields->fPrintGrid << "\"";
+ out << " />" << std::endl;
+
+ break;
+ }
+
+ case BrtEndUserCsView:
+ case BrtEndUserShView:
+ {
+ if (debug) Rcpp::Rcout << "BrtEndUserXXView" << std::endl;
+ out << "" << std::endl;
+ break;
+ }
+
+ case BrtEndUserCsViews:
+ case BrtEndUserShViews:
+ {
+ if (debug) Rcpp::Rcout << "BrtEndUserXXViews" << std::endl;
+ out << "" << std::endl;
break;
}
diff --git a/src/xlsb_defines.h b/src/xlsb_defines.h
index 73e762dfd..2e8cb0551 100644
--- a/src/xlsb_defines.h
+++ b/src/xlsb_defines.h
@@ -219,6 +219,66 @@ typedef struct {
uint8_t reserved2 : 2;
} PtgListFields;
+typedef struct {
+ bool fShowBrks : 1;
+ bool fDspFmlaSv : 1;
+ bool fDspGridSv : 1;
+ bool fDspRwColSv : 1;
+ bool fDspGutsSv : 1;
+ bool fDspZerosSv : 1;
+ bool fHorizontal : 1;
+ bool fVertical : 1;
+ bool fPrintRwCol : 1;
+ bool fPrintGrid : 1;
+ bool fFitToPage : 1;
+ bool fPrintArea : 1;
+ bool fOnePrintArea : 1;
+ bool fFilterMode : 1;
+ bool fEzFilter : 1;
+ bool reserved1 : 1;
+ bool reserved2 : 1;
+ bool fSplitV : 1;
+ bool fSplitH : 1;
+ uint8_t fHiddenRw : 2;
+ bool fHiddenCol : 1;
+ uint8_t hsState : 2;
+ bool reserved3 : 1;
+ bool fFilterUnique : 1;
+ bool fSheetLayoutView : 1;
+ bool fPageLayoutView : 1;
+ bool reserved4 : 1;
+ bool fRuler : 1;
+ bool reserved5 : 1;
+ bool reserved6 : 1;
+} BrtBeginUserShViewFields;
+
+typedef struct {
+ bool fIconic : 1;
+ bool fDspHScroll : 1;
+ bool fDspVScroll : 1;
+ bool fBotAdornment : 1;
+ bool fZoom : 1;
+ bool fDspFmlaBar : 1;
+ bool fDspStatus : 1;
+ uint8_t mdDspNote : 2;
+ uint8_t mdHideObj : 2;
+ bool fPrintIncl : 1;
+ bool fRowColIncl : 1;
+ bool fTimedUpdate : 1;
+ bool fAllMemChanges : 1;
+ bool fOnlySync : 1;
+ bool fPersonalView : 1;
+ uint16_t: 15;
+} BrtUserBookViewFields;
+
+typedef struct {
+ bool fHFDiffOddEven: 1;
+ bool fHFDiffFirst: 1;
+ bool fHFScaleWithDoc: 1;
+ bool fHFAlignMargins: 1;
+ uint16_t reserved: 12;
+} BrtBeginHeaderFooterFields;
+
enum PtgRowType
{
data = 0x00,
@@ -1220,6 +1280,15 @@ enum PtgStructure2
PtgAttrIfError = 0x80
};
+std::string XLView(const uint32_t val) {
+ switch(val) {
+ case 0x00000000: return "normal";
+ case 0x00000001: return "pageBreakPreview";
+ case 0x00000002: return "pageLayout";
+ }
+ return "";
+}
+
// #nocov start
// copied from the website
std::string Cetab(const uint16_t val) {
diff --git a/src/xlsb_funs.h b/src/xlsb_funs.h
index 71e9b3f82..47e51702c 100644
--- a/src/xlsb_funs.h
+++ b/src/xlsb_funs.h
@@ -1045,6 +1045,19 @@ std::string typOperator(uint8_t oprtr) {
return "unknown_operator";
}
+
+std::string grbitSgnOperator(uint8_t oprtr) {
+
+ if (oprtr == 0x01) return "lessThan";
+ if (oprtr == 0x02) return "equal";
+ if (oprtr == 0x03) return "lessThanOrEqual";
+ if (oprtr == 0x04) return "greaterThan";
+ if (oprtr == 0x05) return "notEqual";
+ if (oprtr == 0x06) return "greaterThanOrEqual";
+
+ return "unknown_operator";
+}
+
std::vector Xti(std::istream& sas, bool swapit) {
int32_t firstSheet = 0, lastSheet = 0;
int32_t externalLink = 0; // TODO actually uint32?
@@ -1065,6 +1078,37 @@ std::vector Xti(std::istream& sas, bool swapit) {
return out;
}
+// first half little endian, second half big endian
+std::string guid_str(const std::vector& guid_ints) {
+
+ std::ostringstream guidStream;
+
+ guidStream << std::uppercase << std::hex << std::setfill('0');
+
+ guidStream << std::setw(2) << ((guid_ints[0] >> 24) & 0xFF)
+ << std::setw(2) << ((guid_ints[0] >> 16) & 0xFF)
+ << std::setw(2) << ((guid_ints[0] >> 8) & 0xFF)
+ << std::setw(2) << (guid_ints[0] & 0xFF) << "-";
+
+ guidStream << std::setw(2) << ((guid_ints[1] >> 8) & 0xFF)
+ << std::setw(2) << (guid_ints[1] & 0xFF) << "-";
+
+ guidStream << std::setw(2) << ((guid_ints[1] >> 24) & 0xFF)
+ << std::setw(2) << ((guid_ints[1] >> 16) & 0xFF) << "-";
+
+ guidStream << std::setw(2) << (guid_ints[2] & 0xFF)
+ << std::setw(2) << ((guid_ints[2] >> 8) & 0xFF) << "-"
+ << std::setw(2) << ((guid_ints[2] >> 16) & 0xFF)
+ << std::setw(2) << ((guid_ints[2] >> 24) & 0xFF)
+ << std::setw(2) << (guid_ints[3] & 0xFF)
+ << std::setw(2) << ((guid_ints[3] >> 8) & 0xFF)
+ << std::setw(2) << ((guid_ints[3] >> 16) & 0xFF)
+ << std::setw(2) << ((guid_ints[3] >> 24) & 0xFF)
+ ;
+
+ return guidStream.str();
+}
+
// bool isOperator(const std::string& token) {
// return token == "+" || token == "-" || token == "*" || token == "/" ||
// token == "^" || token == "%" || token == "=" || token == "<>" ||
diff --git a/tests/testthat/test-read_xlsb.R b/tests/testthat/test-read_xlsb.R
index e1ea68573..9d2e7e190 100644
--- a/tests/testthat/test-read_xlsb.R
+++ b/tests/testthat/test-read_xlsb.R
@@ -163,3 +163,16 @@ test_that("shared formulas are detected correctly", {
)
})
+
+test_that("loading custom sheet view in xlsb files works", {
+
+ skip_online_checks()
+
+ fl <- testfile_path("custom_sheet_view.xlsb")
+
+ wb <- wb_load(fl)
+
+ exp <- ""
+ got <- wb$worksheets[[1]]$customSheetViews
+ expect_equal(exp, got)
+})