C3D-0362 · Embed the vicinity PNG bytes into the DWG (base64 Xrecord) so it survives a lost link [C3D-VICEMBED] done
Reads 260714-vicinity.png from disk (54433 bytes), stores it as chunked base64 in an Xrecord under named-dictionary key C3D_VICINITY_EMBED (filename + length + chunk count header, then 2000-char chunks). The RasterImage link is unchanged - the file stays in the surveydisco folder as part of the process - but a full copy now lives in the drawing. Idempotent: replaces any prior embed. Verifies by decoding the round-trip and comparing the byte count.
C# payload
// Embed the vicinity PNG bytes INSIDE the drawing so it survives the linked file going missing.
// The RasterImage stays linked (display path unchanged); a base64 copy of the file is stored,
// chunked, in an Xrecord under a named-object-dictionary key. Recovery elsewhere: read chunks,
// concat, Convert.FromBase64String, write back to the source path, Load().
const string DICTKEY = "C3D_VICINITY_EMBED";
Log("drawing: " + Db.Filename);
// find the vicinity image def + its source path
string src = null;
var nod = (DBDictionary)Tx.GetObject(Db.NamedObjectsDictionaryId, OpenMode.ForWrite);
if (nod.Contains("ACAD_IMAGE_DICT")) {
var idict = (DBDictionary)Tx.GetObject(nod.GetAt("ACAD_IMAGE_DICT"), OpenMode.ForRead);
foreach (DBDictionaryEntry de in idict) {
var def = (RasterImageDef)Tx.GetObject(de.Value, OpenMode.ForRead);
if (de.Key.IndexOf("VICINITY", StringComparison.OrdinalIgnoreCase) >= 0) { src = def.SourceFileName; break; }
if (src == null) src = def.SourceFileName;
}
}
if (src == null) { Log("no image definition found - nothing to embed"); return; }
Log("source: " + src);
if (!System.IO.File.Exists(src)) { Log("source file missing on disk - cannot embed"); return; }
byte[] bytes = System.IO.File.ReadAllBytes(src);
string b64 = System.Convert.ToBase64String(bytes);
Log(string.Format("read {0} bytes -> {1} base64 chars", bytes.Length, b64.Length));
// idempotent: replace any prior embed
if (nod.Contains(DICTKEY)) { nod.Remove(DICTKEY); Log("removed previous " + DICTKEY); }
var xrec = new Xrecord();
var rb = new ResultBuffer();
// header: original filename + byte length + chunk count
string fname = System.IO.Path.GetFileName(src);
const int CHUNK = 2000; // Xrecord Text caps at 2047; stay under
int chunks = (b64.Length + CHUNK - 1) / CHUNK;
rb.Add(new TypedValue((int)DxfCode.Text, fname));
rb.Add(new TypedValue((int)DxfCode.Int32, bytes.Length));
rb.Add(new TypedValue((int)DxfCode.Int32, chunks));
for (int i = 0; i < chunks; i++) {
int len = System.Math.Min(CHUNK, b64.Length - i * CHUNK);
rb.Add(new TypedValue((int)DxfCode.Text, b64.Substring(i * CHUNK, len)));
}
xrec.Data = rb;
nod.SetAt(DICTKEY, xrec);
Tx.AddNewlyCreatedDBObject(xrec, true);
Log(string.Format("wrote {0}: {1} in {2} chunks", DICTKEY, fname, chunks));
// verify: read it straight back and rebuild the byte count
{
var back = (Xrecord)Tx.GetObject(nod.GetAt(DICTKEY), OpenMode.ForRead);
var sb = new System.Text.StringBuilder();
string fn = ""; int declaredLen = 0, declaredChunks = 0; int idx = 0;
foreach (TypedValue tv in back.Data) {
if (idx == 0) fn = (string)tv.Value;
else if (idx == 1) declaredLen = System.Convert.ToInt32(tv.Value);
else if (idx == 2) declaredChunks = System.Convert.ToInt32(tv.Value);
else sb.Append((string)tv.Value);
idx++;
}
byte[] round = System.Convert.FromBase64String(sb.ToString());
bool ok = round.Length == bytes.Length && fn == fname;
Log(string.Format("VERIFY: fname=\"{0}\" declaredLen={1} chunks={2} decoded={3} bytes {4}",
fn, declaredLen, declaredChunks, round.Length, ok ? "MATCH" : "*** MISMATCH ***"));
}
Result
Log
drawing: C:\Users\sanja\OneDrive\_SurveyDisco\260714 - 285 Huckaby Rd, Brooks, GA 30205, USA\260714 - 285 Huckaby Rd R4.dwg source: C:\Users\sanja\OneDrive\_SurveyDisco\260714 - 285 Huckaby Rd, Brooks, GA 30205, USA\260714-vicinity.png read 54433 bytes -> 72580 base64 chars removed previous C3D_VICINITY_EMBED wrote C3D_VICINITY_EMBED: 260714-vicinity.png in 37 chunks VERIFY: fname="260714-vicinity.png" declaredLen=54433 chunks=37 decoded=54433 bytes MATCH
Notes
What worked, what didn't, job-specific gotchas — flagged notes feed the recipes.
No notes yet.