@parametricos/bcf-js
Version:
BCF.js is a BIM Collaboration Format (BCF) reader & parser.
191 lines • 7.36 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Markup = void 0;
const fflate_1 = require("fflate");
const fast_xml_parser_1 = require("fast-xml-parser");
class BcfReader {
version;
bcf_archive;
project;
markups = [];
helpers;
constructor(version, helpers) {
this.version = version;
this.helpers = helpers;
}
read = async (src) => {
try {
const markups = [];
// Convert different input types to Uint8Array for fflate
let data;
if (src instanceof ArrayBuffer) {
data = new Uint8Array(src);
}
else if (src instanceof Uint8Array) {
data = src;
}
else if (src instanceof Blob) {
const arrayBuffer = await src.arrayBuffer();
data = new Uint8Array(arrayBuffer);
}
else if (typeof src === "string") {
// If it's a string, assume it's a base64 or URL - convert appropriately
// For now, we'll throw an error as string URLs need different handling
throw new Error("String URLs not supported in fflate version. Please provide ArrayBuffer, Uint8Array, or Blob.");
}
else {
throw new Error("Unsupported input type for fflate");
}
this.bcf_archive = (0, fflate_1.unzipSync)(data);
let projectId = "";
let projectName = "";
let projectVersion = "";
let extension_schema = undefined;
for (const [name, fileData] of Object.entries(this.bcf_archive)) {
const data = fileData;
if (name.endsWith(".bcf")) {
markups.push(name);
}
else if (name.endsWith(".version")) {
const text = new TextDecoder().decode(data);
const parsedEntry = new fast_xml_parser_1.XMLParser(this.helpers.XmlParserOptions).parse(text);
projectVersion = parsedEntry.Version.DetailedVersion;
}
else if (name.endsWith(".bcfp")) {
const text = new TextDecoder().decode(data);
const parsedEntry = new fast_xml_parser_1.XMLParser(this.helpers.XmlParserOptions).parse(text);
if (!parsedEntry.ProjectExtension ||
!parsedEntry.ProjectExtension.Project)
continue; //NOTE: Throw an error here?
projectId = parsedEntry.ProjectExtension.Project["@_ProjectId"] || ""; //NOTE: Throw an error here?
projectName = parsedEntry.ProjectExtension.Project.Name || "";
}
else if (name.endsWith("extensions.xsd")) {
const text = new TextDecoder().decode(data);
const parsedEntry = new fast_xml_parser_1.XMLParser(this.helpers.XmlParserOptions).parse(text);
extension_schema = this.helpers.XmlToJsonNotation(parsedEntry);
}
}
const purged_markups = [];
for (let i = 0; i < markups.length; i++) {
const markupName = markups[i];
const markup = new Markup(this, markupName);
await markup.read();
this.markups.push(markup);
const purged_markup = {
header: markup.header,
topic: markup.topic,
project: this.project,
viewpoints: markup.viewpoints,
};
purged_markups.push(purged_markup);
}
this.project = {
project_id: projectId,
name: projectName,
version: projectVersion,
markups: undefined,
reader: this,
extension_schema: extension_schema,
};
this.project.markups = purged_markups.map((mkp) => {
return { ...mkp, project: this.project };
});
}
catch (e) {
console.log("Error in loading BCF archive. The error below was thrown.");
console.error(e);
}
};
getEntry = (name) => {
return this.bcf_archive?.[name];
};
}
exports.default = BcfReader;
class Markup {
reader;
markup_name;
header;
topic;
viewpoints = [];
constructor(reader, markupName) {
this.reader = reader;
this.markup_name = markupName;
}
read = async () => {
await this.parseMarkup();
await this.parseViewpoints();
};
parseMarkup = async () => {
const fileData = this.reader.getEntry(this.markup_name);
if (!fileData)
throw new Error("Missing markup file");
const text = new TextDecoder().decode(fileData);
const markup = this.reader.helpers.GetMarkup(text);
this.topic = markup.topic;
this.header = markup.header;
};
parseViewpoints = async () => {
if (!this.topic)
return;
if (this.topic.viewpoints) {
const topic_viewpoints = this.topic.viewpoints;
for (let i = 0; i < topic_viewpoints.length; i++) {
const entry = topic_viewpoints[i];
const key = this.topic.guid + "/" + entry.viewpoint;
const fileData = this.reader.getEntry(key);
if (!fileData)
throw new Error("Missing Visualization Info");
const text = new TextDecoder().decode(fileData);
const viewpoint = this.reader.helpers.GetViewpoint(text);
viewpoint.snapshot = entry.snapshot;
viewpoint.getSnapshot = async () => {
if (entry.snapshot)
return await this.getSnapshot(entry.snapshot);
};
this.viewpoints.push(viewpoint);
}
}
};
/**
* Parses the png snapshot.
*
* @returns {string} The image in base64String format.
*
* @deprecated This function is deprecated and will be removed in the next version.<br>
* Please use viewpoint.getSnapshot() instead.<br>
*
*/
getViewpointSnapshot = async (viewpoint) => {
if (!viewpoint || !this.topic)
return;
const fileData = this.reader.getEntry(`${this.topic.guid}/${viewpoint.snapshot}`);
if (fileData) {
return uint8ToBase64(fileData);
}
};
/**
* Parses the png snapshot.
*
* @returns {string} The image in base64String format.
*/
getSnapshot = async (guid) => {
if (!guid || !this.topic)
return;
const fileData = this.reader.getEntry(`${this.topic.guid}/${guid}`);
if (fileData) {
return uint8ToBase64(fileData);
}
};
}
exports.Markup = Markup;
function uint8ToBase64(bytes) {
let binary = "";
const chunkSize = 0x8000; // 32k chunks
for (let i = 0; i < bytes.length; i += chunkSize) {
const chunk = bytes.subarray(i, i + chunkSize);
binary += String.fromCharCode.apply(null, chunk);
}
return btoa(binary);
}
//# sourceMappingURL=BcfReader.js.map