static gboolean parse_dim(gchar **p, const gchar *name, gint *value, GError **error) { gchar *vp, *line; line = gwy_str_next_line(p); if (!line) { err_MISSING_FIELD(error, name); return FALSE; } vp = strchr(line, ':'); if (!vp) { g_set_error(error, GWY_MODULE_FILE_ERROR, GWY_MODULE_FILE_ERROR_DATA, _("Missing colon in header line.")); return FALSE; } *vp = '\0'; vp++; gwy_debug("<%s> = <%s>", name, vp); if (!gwy_strequal(line, name)) { err_MISSING_FIELD(error, name); return FALSE; } *value = strtol(vp, NULL, 10); if (err_DIMENSION(error, *value)) return FALSE; return TRUE; }
static gboolean read_dimensions(GHashTable *hash, gint *ndata, Dimensions *dimensions, GError **error) { const gchar *value; /* Number of fields */ if (!(value = g_hash_table_lookup(hash, "Channels"))) { err_MISSING_FIELD(error, "Channels"); return FALSE; } *ndata = atoi(value); if (*ndata <= 0 || *ndata > 1024) { err_INVALID(error, "Channels"); return FALSE; } /* Pixel sizes */ if (!(value = g_hash_table_lookup(hash, "Lines"))) { err_MISSING_FIELD(error, "Lines"); return FALSE; } dimensions->yres = atoi(value); if (err_DIMENSION(error, dimensions->yres)) return FALSE; if (!(value = g_hash_table_lookup(hash, "Rows"))) { err_MISSING_FIELD(error, "Rows"); return FALSE; } /* XXX: When the file says Rows, it actually means Columns-1. Bite me. */ dimensions->xres = atoi(value) + 1; if (err_DIMENSION(error, dimensions->xres)) return FALSE; /* Real sizes */ if (!(value = g_hash_table_lookup(hash, "X-Length"))) { err_MISSING_FIELD(error, "X-Length"); return FALSE; } dimensions->xreal = Micrometer * g_ascii_strtod(value, NULL); if (!((dimensions->xreal = fabs(dimensions->xreal)) > 0)) { g_warning("Real x size is 0.0, fixing to 1.0"); dimensions->xreal = 1.0; } if (!(value = g_hash_table_lookup(hash, "Y-Length"))) { err_MISSING_FIELD(error, "Y-Length"); return FALSE; } dimensions->yreal = Micrometer * g_ascii_strtod(value, NULL); if (!((dimensions->yreal = fabs(dimensions->yreal)) > 0)) { g_warning("Real y size is 0.0, fixing to 1.0"); dimensions->yreal = 1.0; } return TRUE; }
static gboolean parse_scale(gchar **p, const gchar *name, double *value, GwySIUnit **units, GError **error) { gint power10; gchar *vp, *line; line = gwy_str_next_line(p); if (!line) { err_MISSING_FIELD(error, name); return FALSE; } vp = strchr(line, ':'); if (!vp) { g_set_error(error, GWY_MODULE_FILE_ERROR, GWY_MODULE_FILE_ERROR_DATA, _("Missing colon in header line.")); return FALSE; } *vp = '\0'; vp++; gwy_debug("<%s> = <%s>", name, vp); if (!gwy_strequal(line, name)) { err_MISSING_FIELD(error, name); return FALSE; } *value = g_ascii_strtod(vp, &vp); *units = gwy_si_unit_new_parse(vp, &power10); *value *= pow10(power10); if (!*value) { g_warning("%s is 0.0, fixing to 1.0", name); *value = 1.0; } return TRUE; }
static guint read_pixel_size(GHashTable *hash, const gchar *key, GError **error) { gchar *value; guint size; if (!(value = g_hash_table_lookup(hash, key))) { err_MISSING_FIELD(error, key); return 0; } size = atoi(g_hash_table_lookup(hash, key)); if (err_DIMENSION(error, size)) return 0; return size; }
static gboolean require_keys(GHashTable *hash, GError **error, ...) { va_list ap; const gchar *key; va_start(ap, error); while ((key = va_arg(ap, const gchar *))) { if (!g_hash_table_lookup(hash, key)) { err_MISSING_FIELD(error, key); va_end(ap); return FALSE; } } va_end(ap); return TRUE; }
static GwyDataField* read_binary_data(const gchar *buffer, gsize size, GHashTable *hash, GError **error) { ShimadzuDataType data_type; gint xres, yres, i; guint expected; gdouble xreal, yreal, zscale, xoff, yoff, zoff; GwySIUnit *unitxy, *unitz; GwyDataField *dfield = NULL; gdouble *d; const gchar *s; if (!(s = g_hash_table_lookup(hash, "DataType"))) { err_MISSING_FIELD(error, "DataType"); return NULL; } if (g_ascii_strcasecmp(s, "short") == 0) data_type = SHIMADZU_SHORT; else if (g_ascii_strcasecmp(s, "float") == 0) data_type = SHIMADZU_FLOAT; else { err_UNSUPPORTED(error, "DataType"); return NULL; } unitxy = gwy_si_unit_new(NULL); unitz = gwy_si_unit_new(NULL); if (!get_scales(hash, FALSE, &xres, &yres, &xreal, &yreal, &xoff, &yoff, unitxy, &zscale, &zoff, unitz, error)) goto fail; expected = data_type*xres*yres + HEADER_SIZE; if (err_SIZE_MISMATCH(error, expected, size, FALSE)) goto fail; dfield = gwy_data_field_new(xres, yres, xreal, yreal, FALSE); gwy_data_field_set_xoffset(dfield, xoff); gwy_data_field_set_yoffset(dfield, yoff); gwy_data_field_set_si_unit_xy(dfield, unitxy); gwy_data_field_set_si_unit_z(dfield, unitz); d = gwy_data_field_get_data(dfield); if (data_type == SHIMADZU_SHORT) { const gint16 *d16 = (const gint16*)(buffer + HEADER_SIZE); for (i = 0; i < xres*yres; i++) d[i] = zscale*GUINT16_FROM_LE(d16[i]) + zoff; } else if (data_type == SHIMADZU_FLOAT) { const guchar *p = buffer + HEADER_SIZE; for (i = 0; i < xres*yres; i++) d[i] = zscale*gwy_get_gfloat_le(&p) + zoff; } else { g_assert_not_reached(); } gwy_data_field_invert(dfield, TRUE, FALSE, FALSE); fail: g_object_unref(unitxy); g_object_unref(unitz); return dfield; }
static GwyContainer* zeiss_load_tiff(const GwyTIFF *tiff, GError **error) { GwyContainer *container = NULL, *meta = NULL; GwyDataField *dfield; GwySIUnit *siunit; GwyTIFFImageReader *reader = NULL; GHashTable *hash = NULL; gint i, power10; gchar *value, *end, *comment = NULL; gdouble *data; gboolean new_file; double factor, dx; /* Comment with parameters is common for all data fields */ if (!gwy_tiff_get_string0(tiff, ZEISS_HEADER_TAG, &comment)) { err_FILE_TYPE(error, "Carl Zeiss SEM"); goto fail; } if (strstr(comment, MAGIC_COMMENT)) new_file = TRUE; else if (g_str_has_prefix(comment, SOMEWHAT_LESS_MAGIC_COMMENT)) new_file = FALSE; else { err_FILE_TYPE(error, "Carl Zeiss SEM"); goto fail; } /* Read the comment header. */ if (new_file) { hash = parse_comment(comment); if ((value = g_hash_table_lookup(hash, "Image Pixel Size"))) { gwy_debug("Using dx from Image Pixel Size: %s", value); } else if ((value = g_hash_table_lookup(hash, "Pixel Size"))) { gwy_debug("Using dx from Pixel Size: %s", value); } else { err_MISSING_FIELD(error, "Pixel Size"); goto fail; } } else { /* The first thing is the pixel size, apparently. */ value = comment + strlen(SOMEWHAT_LESS_MAGIC_COMMENT); gwy_debug("Using dx from old-style comment: %s", value); } dx = g_ascii_strtod(value, &end); /* Use negated positive conditions to catch NaNs */ if (!((dx = fabs(dx)) > 0)) { g_warning("Real pixel size is 0.0, fixing to 1.0"); dx = 1.0; } if (!new_file) end = "m"; /* Request a reader, this ensures dimensions and stuff are defined. * NB: Newer versions store the image as RGB. Not useful here; just * average the channels. */ if (!(reader = gwy_tiff_get_image_reader(tiff, 0, 3, error))) goto fail; siunit = gwy_si_unit_new_parse(end, &power10); factor = pow10(power10); dfield = gwy_data_field_new(reader->width, reader->height, reader->width * factor * dx, reader->height * factor * dx, FALSE); gwy_data_field_set_si_unit_xy(dfield, siunit); g_object_unref(siunit); data = gwy_data_field_get_data(dfield); if (reader->samples_per_pixel > 1) { gdouble *datarow = g_new(gdouble, reader->width); gint ch, j, spp = reader->samples_per_pixel; gwy_data_field_clear(dfield); for (i = 0; i < reader->height; i++) { for (ch = 0; ch < spp; ch++) { gwy_tiff_read_image_row(tiff, reader, 0, i, 1.0, 0.0, datarow); for (j = 0; j < reader->width; j++) data[i*reader->width + j] += datarow[j]; } } g_free(datarow); gwy_data_field_multiply(dfield, 1.0/spp); gwy_data_field_invalidate(dfield); } else { for (i = 0; i < reader->height; i++) gwy_tiff_read_image_row(tiff, reader, 0, i, 1.0, 0.0, data + i*reader->width); } container = gwy_container_new(); gwy_container_set_object_by_name(container, "/0/data", dfield); g_object_unref(dfield); gwy_container_set_string_by_name(container, "/0/data/title", g_strdup("Secondary electron count")); if (new_file) { meta = gwy_container_new(); g_hash_table_foreach(hash, add_meta, meta); if (gwy_container_get_n_items(meta)) gwy_container_set_object_by_name(container, "/0/meta", meta); g_object_unref(meta); } fail: if (hash) g_hash_table_destroy(hash); g_free(comment); return container; }
static gboolean rhkspm32_read_header(RHKPage *rhkpage, GError **error) { const gchar *buffer; gchar *end; guint pos; buffer = rhkpage->buffer; rhkpage->date = g_strstrip(g_strndup(buffer + MAGIC_SIZE, 0x20 - MAGIC_SIZE)); if (sscanf(buffer + 0x20, "%d %d %d %d %d %d %d", (gint*)&rhkpage->type, (gint*)&rhkpage->data_type, &rhkpage->line_type, &rhkpage->xres, &rhkpage->yres, &rhkpage->size, (gint*)&rhkpage->page_type) != 7) { g_set_error(error, GWY_MODULE_FILE_ERROR, GWY_MODULE_FILE_ERROR_DATA, _("Invalid file header.")); return FALSE; } gwy_debug("type = %u, data = %u, line = %u, image = %u", rhkpage->type, rhkpage->data_type, rhkpage->line_type, rhkpage->page_type); gwy_debug("xres = %d, yres = %d", rhkpage->xres, rhkpage->yres); if (err_DIMENSION(error, rhkpage->xres) || err_DIMENSION(error, rhkpage->yres)) return FALSE; if (!((rhkpage->type == RHK_TYPE_IMAGE) || (rhkpage->type == RHK_TYPE_LINE))) { g_set_error(error, GWY_MODULE_FILE_ERROR, GWY_MODULE_FILE_ERROR_DATA, _("Only image and line files are supported.")); return FALSE; } if ((rhkpage->type == RHK_TYPE_IMAGE) && (rhkpage->data_type != RHK_DATA_INT16)) { g_set_error(error, GWY_MODULE_FILE_ERROR, GWY_MODULE_FILE_ERROR_DATA, _("Invalid data type %d for image data."), rhkpage->data_type); return FALSE; } if ((rhkpage->type == RHK_TYPE_LINE) && !((rhkpage->data_type == RHK_DATA_INT16) || (rhkpage->data_type == RHK_DATA_SINGLE))) { g_set_error(error, GWY_MODULE_FILE_ERROR, GWY_MODULE_FILE_ERROR_DATA, _("Invalid data type %d for line data."), rhkpage->data_type); return FALSE; } if ((rhkpage->data_type) == RHK_DATA_INT8) rhkpage->item_size = 1; else if ((rhkpage->data_type) == RHK_DATA_INT16) rhkpage->item_size = 2; else if ((rhkpage->data_type) == RHK_DATA_INT32) rhkpage->item_size = 4; else if ((rhkpage->data_type) == RHK_DATA_SINGLE) rhkpage->item_size = 4; //rhkpage->item_size = rhkpage->data_type; // niv if (!rhkspm32_read_range(buffer + 0x40, "X", &rhkpage->x) || !rhkspm32_read_range(buffer + 0x60, "Y", &rhkpage->y) || !rhkspm32_read_range(buffer + 0x80, "Z", &rhkpage->z)) { err_INVALID(error, _("data ranges")); return FALSE; } /* Use negated positive conditions to catch NaNs */ // niv - modifying this - otherwise it messes with the spectra // (but i don;t really understand it) if (!((fabs(rhkpage->x.scale)) > 0)) { g_warning("Real x scale is 0.0, fixing to 1.0"); rhkpage->x.scale = 1.0; } if (!((fabs(rhkpage->y.scale)) > 0)) { /* The y scale seem unused for non-image data */ if (rhkpage->type == RHK_TYPE_IMAGE) g_warning("Real y scale is 0.0, fixing to 1.0"); rhkpage->y.scale = 1.0; } if (!g_str_has_prefix(buffer + 0xa0, "XY ")) { err_MISSING_FIELD(error, "XY"); return FALSE; } pos = 0xa0 + sizeof("XY"); rhkpage->xyskew = g_ascii_strtod(buffer + pos, &end); if (end == buffer + pos) { err_INVALID(error, "XY"); return FALSE; } pos = (end - buffer) + 2; /* Don't check failure, it seems the value is optional */ rhkpage->alpha = g_ascii_strtod(buffer + pos, &end); // not failing, but setting an existance flag, this happens for spectra, // but otherwise i want to add this to the metadata if (end == buffer + pos) rhkpage->e_alpha = FALSE; else rhkpage->e_alpha = TRUE; if (!rhkspm32_read_range(buffer + 0xc0, "IV", &rhkpage->iv)) { err_INVALID(error, "IV"); return FALSE; } if (g_str_has_prefix(buffer + 0xe0, "scan ")) pos = 0xe0 + sizeof("scan"); rhkpage->scan = strtol(buffer + pos, &end, 10); if (end == buffer + pos) { err_INVALID(error, "scan"); return FALSE; } pos = (end - buffer); rhkpage->period = g_ascii_strtod(buffer + pos, &end); if (end == buffer + pos) { err_INVALID(error, "period"); return FALSE; } if (sscanf(buffer + 0x100, "id %u %u", &rhkpage->id, &rhkpage->data_offset) != 2) { /* XXX: Some braindamaged files encountered in practice do not contain * the data offset. Cross fingers and substitute HEADER_SIZE. */ g_warning("Data offset is missing, just guessing from now..."); rhkpage->id = 0; rhkpage->data_offset = HEADER_SIZE; } gwy_debug("data_offset = %u", rhkpage->data_offset); if (rhkpage->data_offset < HEADER_SIZE) { err_INVALID(error, _("data offset")); return FALSE; } /* XXX: The same braindamaged files overwrite the label and comment part * with some XML mumbo jumbo. Sigh and ignore it. */ if (strncmp(buffer + 0x140, "\x0d\x0a<?", 4) != 0) { rhkpage->label = g_strstrip(g_strndup(buffer + 0x140, 0x20)); rhkpage->comment = g_strstrip(g_strndup(buffer + 0x160, HEADER_SIZE - 0x160)); } return TRUE; }
static gboolean read_binary_data(X3PFile *x3pfile, unzFile *zipfile, GError **error) { GwyRawDataType rawtype; gsize size; guchar *bindata; gchar *s; guint i; s = g_hash_table_lookup(x3pfile->hash, DATA_LINK_PREFIX "/PointDataLink"); if (!s) { err_NO_DATA(error); return FALSE; } gwy_debug("binary data file %s", s); if (unzLocateFile(zipfile, s, 1) != UNZ_OK) { g_set_error(error, GWY_MODULE_FILE_ERROR, GWY_MODULE_FILE_ERROR_IO, _("File %s is missing in the zip file."), s); return FALSE; } s = g_hash_table_lookup(x3pfile->hash, AXES_PREFIX "/CZ/DataType"); if (!s) { err_MISSING_FIELD(error, AXES_PREFIX "CZ/DataType"); return FALSE; } if (!x3p_file_get_data_type(s, &rawtype, error)) return FALSE; if (!(bindata = x3p_get_file_content(zipfile, &size, error))) return FALSE; if (err_SIZE_MISMATCH(error, x3pfile->ndata * gwy_raw_data_size(rawtype), size, TRUE)) { g_free(bindata); return FALSE; } gwy_convert_raw_data(bindata, x3pfile->ndata, 1, rawtype, GWY_BYTE_ORDER_LITTLE_ENDIAN, x3pfile->values, x3pfile->dz, x3pfile->zoff); g_free(bindata); for (i = 0; i < x3pfile->ndata; i++) x3pfile->valid[i] = TRUE; s = g_hash_table_lookup(x3pfile->hash, DATA_LINK_PREFIX "/ValidPointsLink"); if (!s) return TRUE; if (unzLocateFile(zipfile, s, 1) != UNZ_OK) { g_set_error(error, GWY_MODULE_FILE_ERROR, GWY_MODULE_FILE_ERROR_IO, _("File %s is missing in the zip file."), s); return FALSE; } if (!(bindata = x3p_get_file_content(zipfile, &size, error))) return FALSE; if (err_SIZE_MISMATCH(error, (x3pfile->ndata + 7)/8, size, TRUE)) { g_free(bindata); return FALSE; } for (i = 0; i < x3pfile->ndata; i++) x3pfile->valid[i] = bindata[i/8] & (1 << (i % 8)); g_free(bindata); return TRUE; }
static GwyContainer* csmfile_load(const gchar *filename, G_GNUC_UNUSED GwyRunType mode, GError **error) { GwyContainer *meta, *container = NULL; GHashTable *hash = NULL; guchar *d24, *buffer = NULL; gsize size = 0; GError *err = NULL; GwyDataField *dfield = NULL; guint xres, yres, bmpsize, header_size, maxval, i, j; gdouble real, zmin, zmax, q, z0; GwyTextHeaderParser parser; GwySIUnit *unit = NULL; gchar *value, *end, *header = NULL; gdouble *data; gint power10; if (!gwy_file_get_contents(filename, &buffer, &size, &err)) { err_GET_FILE_CONTENTS(error, &err); return NULL; } if (size < BMP_HEADER_SIZE) { err_TOO_SHORT(error); goto fail; } if (!read_bmp_header(buffer, &xres, &yres, &bmpsize) || size <= bmpsize) { err_FILE_TYPE(error, "CSM"); goto fail; } header_size = size - bmpsize; header = g_new(gchar, header_size + 1); memcpy(header, buffer + bmpsize, header_size); header[header_size] = '\0'; gwy_clear(&parser, 1); parser.key_value_separator = "="; hash = gwy_text_header_parse(header, &parser, NULL, NULL); /* Do NOT use the fields Image width, Image height from the added footer. * Even though it is specifically added by Benyuan it can disagree with the * BMP diemsions and when they disagree the BMP diemsions are apparently * right. */ if (err_DIMENSION(error, xres)) goto fail; if (err_DIMENSION(error, yres)) goto fail; if (!(value = g_hash_table_lookup(hash, "ScanSize"))) { err_MISSING_FIELD(error, "ScanSize"); goto fail; } real = g_ascii_strtod(value, NULL); /* Use negated positive conditions to catch NaNs */ if (!((real = fabs(real)) > 0)) { g_warning("Real size is 0.0, fixing to 1.0"); real = 1.0; } if (!(value = g_hash_table_lookup(hash, "HeightScale"))) { err_MISSING_FIELD(error, "HeightScale"); goto fail; } zmax = g_ascii_strtod(value, &end); unit = gwy_si_unit_new_parse(end, &power10); /* Optional stuff for which we try to fall back. */ if (!(value = g_hash_table_lookup(hash, "StartHeightScale"))) zmin = 0.0; else zmin = g_ascii_strtod(value, NULL); if (!(value = g_hash_table_lookup(hash, "MaxValue"))) maxval = 0xffff; else maxval = MAX(atoi(value), 1); dfield = gwy_data_field_new(xres, yres, real*Nanometre, real*Nanometre, FALSE); data = gwy_data_field_get_data(dfield); d24 = buffer + BMP_HEADER_SIZE; q = pow10(power10)*(zmax - zmin)/maxval; z0 = pow10(power10)*zmin; for (i = 0; i < yres; i++) { gdouble *row = data + (yres-1 - i)*xres; for (j = 0; j < xres; j++, row++, d24 += 3) *row = (d24[0] + 256.0*d24[1])*q + z0; } gwy_si_unit_set_from_string(gwy_data_field_get_si_unit_xy(dfield), "m"); gwy_data_field_set_si_unit_z(dfield, unit); container = gwy_container_new(); gwy_container_set_object_by_name(container, "/0/data", dfield); g_object_unref(dfield); meta = gwy_container_new(); g_hash_table_foreach(hash, store_meta, meta); gwy_container_set_object_by_name(container, "/0/meta", meta); g_object_unref(meta); if ((value = g_hash_table_lookup(hash, "sTitle")) && g_utf8_validate(value, -1, NULL)) { gwy_container_set_string_by_name(container, "/0/data/title", g_strdup(value)); } else gwy_app_channel_title_fall_back(container, 0); gwy_file_channel_import_log_add(container, 0, NULL, filename); fail: gwy_file_abandon_contents(buffer, size, NULL); GWY_OBJECT_UNREF(unit); if (header) g_free(header); if (hash) g_hash_table_destroy(hash); return container; }
static GwySIUnit* get_physical_scale(GHashTable *hash, GHashTable *scannerlist, GHashTable *scanlist, gboolean has_version, gdouble *scale, GError **error) { GwySIUnit *siunit, *siunit2; NanoscopeValue *val, *sval; gchar *key; gint q; /* Very old style scales (files with Version field) */ if (!has_version) { if (!(val = g_hash_table_lookup(hash, "Z magnify image"))) { err_MISSING_FIELD(error, "Z magnify image"); return NULL; } /* TODO: Luminescence */ siunit = gwy_si_unit_new("m"); /* According to Markus, the units are 1/100 nm, but his scale * calculation is raw/655.36 [nm]. We have the factor 1/65536 applied * automatically, that gives 1e-7 [m]. Whatever. */ *scale = 1e-7 * val->hard_value; return siunit; } /* XXX: This is a damned heuristics. For some value types we try to guess * a different quantity scale to look up. */ if (!(val = g_hash_table_lookup(hash, "@2:Z scale"))) { if (!(val = g_hash_table_lookup(hash, "Z scale"))) { err_MISSING_FIELD(error, "Z scale"); return NULL; } /* Old style scales */ siunit = gwy_si_unit_new_parse(val->hard_value_units, &q); *scale = val->hard_value * pow10(q); if (val->hard_scale) *scale *= 65536.0/val->hard_scale; return siunit; } key = g_strdup_printf("@%s", val->soft_scale); if (!(sval = g_hash_table_lookup(scannerlist, key)) && (!scanlist || !(sval = g_hash_table_lookup(scanlist, key)))) { g_warning("`%s' not found", key); g_free(key); /* XXX */ *scale = val->hard_value; return gwy_si_unit_new(""); } *scale = val->hard_value*sval->hard_value; if (!sval->hard_value_units || !*sval->hard_value_units) { if (gwy_strequal(val->soft_scale, "Sens. Phase")) siunit = gwy_si_unit_new("deg"); else siunit = gwy_si_unit_new("V"); } else { siunit = gwy_si_unit_new_parse(sval->hard_value_units, &q); siunit2 = gwy_si_unit_new("V"); gwy_si_unit_multiply(siunit, siunit2, siunit); gwy_debug("Scale1 = %g V/LSB", val->hard_value); gwy_debug("Scale2 = %g %s", sval->hard_value, sval->hard_value_units); *scale *= pow10(q); gwy_debug("Total scale = %g %s/LSB", *scale, gwy_si_unit_get_string(siunit, GWY_SI_UNIT_FORMAT_PLAIN)); g_object_unref(siunit2); } g_free(key); return siunit; }
static GwyContainer* micromap_load(const gchar *filename, G_GNUC_UNUSED GwyRunType mode, GError **error) { SDFile sdfile; GwyContainer *container = NULL; gchar *p, *buffer = NULL; gsize len, size = 0; GError *err = NULL; GwyDataField *dfield = NULL; gdouble objectivemag, tubemag, cameraxpixel, cameraypixel; if (!g_file_get_contents(filename, &buffer, &size, &err)) { err_GET_FILE_CONTENTS(error, &err); return NULL; } len = size; p = buffer; if (sdfile_read_header_text(&p, &len, &sdfile, error)) { if (check_params(&sdfile, len, error)) dfield = sdfile_read_data_text(&sdfile, error); } if (!dfield) { g_free(buffer); return NULL; } if (!sdfile.extras) { err_MISSING_FIELD(error, "OBJECTIVEMAG"); goto fail; } if (!require_keys(sdfile.extras, error, "OBJECTIVEMAG", "TUBEMAG", "CAMERAXPIXEL", "CAMERAYPIXEL", NULL)) goto fail; objectivemag = g_ascii_strtod(g_hash_table_lookup(sdfile.extras, "OBJECTIVEMAG"), NULL); tubemag = g_ascii_strtod(g_hash_table_lookup(sdfile.extras, "TUBEMAG"), NULL); cameraxpixel = g_ascii_strtod(g_hash_table_lookup(sdfile.extras, "CAMERAXPIXEL"), NULL); cameraypixel = g_ascii_strtod(g_hash_table_lookup(sdfile.extras, "CAMERAYPIXEL"), NULL); sdfile_set_units(&sdfile, dfield); gwy_data_field_set_xreal(dfield, Micrometer * sdfile.xres * objectivemag * tubemag * cameraxpixel); gwy_data_field_set_yreal(dfield, Micrometer * sdfile.yres * objectivemag * tubemag * cameraypixel); container = gwy_container_new(); gwy_container_set_object_by_name(container, "/0/data", dfield); gwy_container_set_string_by_name(container, "/0/data/title", g_strdup("Topography")); fail: g_object_unref(dfield); g_free(buffer); if (sdfile.extras) g_hash_table_destroy(sdfile.extras); return container; }