/** * SECTION:cairo-dwrite-fonts * @Title: DWrite Fonts * @Short_Description: Font support for Microsoft DirectWrite * @See_Also: #cairo_font_face_t * * The Microsoft DirectWrite font backend is primarily used to render text on * Microsoft Windows systems.
**/
/** * CAIRO_HAS_DWRITE_FONT: * * Defined if the Microsoft DWrite font backend is available. * This macro can be used to conditionally compile backend-specific code. * * Since: 1.18
**/
/** * _cairo_dwrite_error: * @hr HRESULT code * @context: context string to display along with the error * * Helper function to print a human readable form a HRESULT. * * Return value: A cairo status code for the error code
**/ static cairo_int_status_t
_cairo_dwrite_error (HRESULT hr, constchar *context)
{ void *lpMsgBuf;
RefPtr<IDWriteFontFamily> family = DWriteFactory::FindSystemFontFamily(face_name); delete[] face_name; if (!family) { /* If the family is not found, use the default that should always exist. */
face_name_len = MultiByteToWideChar(CP_UTF8, 0, CAIRO_FONT_FAMILY_DEFAULT, -1, NULL, 0);
face_name = new WCHAR[face_name_len];
MultiByteToWideChar(CP_UTF8, 0, CAIRO_FONT_FAMILY_DEFAULT, -1, face_name, face_name_len);
family = DWriteFactory::FindSystemFontFamily(face_name); delete[] face_name; if (!family) {
*font_face = (cairo_font_face_t*)&_cairo_font_face_nil; return (cairo_status_t)CAIRO_INT_STATUS_UNSUPPORTED;
}
}
DWRITE_FONT_WEIGHT weight; switch (toy_face->weight) { case CAIRO_FONT_WEIGHT_BOLD:
weight = DWRITE_FONT_WEIGHT_BOLD; break; case CAIRO_FONT_WEIGHT_NORMAL: default:
weight = DWRITE_FONT_WEIGHT_NORMAL; break;
}
DWRITE_FONT_STYLE style; switch (toy_face->slant) { case CAIRO_FONT_SLANT_ITALIC:
style = DWRITE_FONT_STYLE_ITALIC; break; case CAIRO_FONT_SLANT_OBLIQUE:
style = DWRITE_FONT_STYLE_OBLIQUE; break; case CAIRO_FONT_SLANT_NORMAL: default:
style = DWRITE_FONT_STYLE_NORMAL; break;
}
if (scaled_font->mat.xy == 0 && scaled_font->mat.yx == 0 &&
scaled_font->mat.xx == scaled_font->base.font_matrix.xx &&
scaled_font->mat.yy == scaled_font->base.font_matrix.yy) { // Fast route, don't actually use a transform but just // set the correct font size.
*transformed = 0;
for (int i = 0; i < num_glyphs; i++) {
indices[i] = (WORD) glyphs[i].index;
offsets[i].ascenderOffset = -(FLOAT)(glyphs[i].y);
offsets[i].advanceOffset = (FLOAT)(glyphs[i].x);
advances[i] = 0.0;
}
} else {
*transformed = 1; // Transforming positions by the inverse matrix, then by the original // matrix later may introduce small errors, especially because the // D2D matrix is single-precision whereas the cairo one is double. // This is a problem when glyph positions were originally at exactly // half-pixel locations, which eventually round to whole pixels for // GDI rendering - the errors introduced here cause them to round in // unpredictable directions, instead of all rounding in a consistent // way, leading to poor glyph spacing (bug 675383). // To mitigate this, nudge the positions by a tiny amount to try and // ensure that after the two transforms, they'll still round in a // consistent direction. constdouble EPSILON = 0.0001; for (int i = 0; i < num_glyphs; i++) {
indices[i] = (WORD) glyphs[i].index; double x = glyphs[i].x + EPSILON; double y = glyphs[i].y;
cairo_matrix_transform_point(&scaled_font->mat_inverse, &x, &y); // Since we will multiply by our ctm matrix later for rotation effects // and such, adjust positions by the inverse matrix now. Y-axis is // inverted! Therefor the offset is -y.
offsets[i].ascenderOffset = -(FLOAT)y;
offsets[i].advanceOffset = (FLOAT)x;
advances[i] = 0.0;
} // The font matrix takes care of the scaling if we have a transform, // emSize should be 1.
run->fontEmSize = 1.0f;
}
}
/* Must do malloc and not C++ new, since Cairo frees this. */
cairo_dwrite_scaled_font_t *dwrite_font = (cairo_dwrite_scaled_font_t*)_cairo_malloc( sizeof(cairo_dwrite_scaled_font_t)); if (unlikely(dwrite_font == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY);
*font = reinterpret_cast<cairo_scaled_font_t*>(dwrite_font);
status = _cairo_scaled_font_init (&dwrite_font->base,
&font_face->base,
font_matrix,
ctm,
options,
&_cairo_dwrite_scaled_font_backend); if (status) {
free(dwrite_font); return status;
}
// The following code detects the system quality at scaled_font creation time, // this means that if cleartype settings are changed but the scaled_fonts // are re-used, they might not adhere to the new system setting until re- // creation. switch (cairo_win32_get_system_text_quality()) { case CLEARTYPE_QUALITY:
default_quality = CAIRO_ANTIALIAS_SUBPIXEL; break; case ANTIALIASED_QUALITY:
default_quality = CAIRO_ANTIALIAS_GRAY; break; case DEFAULT_QUALITY: // _get_system_quality() seems to think aliased is default!
default_quality = CAIRO_ANTIALIAS_NONE; break;
}
if (default_quality == CAIRO_ANTIALIAS_GRAY) { if (!do_grayscale(font_face->dwriteface, (unsignedint)_cairo_round(font_matrix->yy))) {
default_quality = CAIRO_ANTIALIAS_NONE;
}
}
if ((info & CAIRO_SCALED_GLYPH_INFO_METRICS) != 0) {
status = _cairo_dwrite_scaled_font_init_glyph_metrics (scaled_dwrite_font, scaled_glyph); if (status) return status;
}
if (info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE) {
status = _cairo_dwrite_scaled_font_init_glyph_color_surface (scaled_dwrite_font, scaled_glyph, foreground_color); if (status) return status;
}
if (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) {
status = _cairo_dwrite_scaled_font_init_glyph_surface (scaled_dwrite_font, scaled_glyph); if (status) return status;
}
if ((info & CAIRO_SCALED_GLYPH_INFO_PATH) != 0) {
status = _cairo_dwrite_scaled_font_init_glyph_path (scaled_dwrite_font, scaled_glyph); if (status) return status;
}
// We pad the extents here because GetDesignGlyphMetrics returns "ideal" metrics // for the glyph outline, without accounting for hinting/gridfitting/antialiasing, // and therefore it does not always cover all pixels that will actually be touched. if (extents.width > 0 && extents.height > 0) { double x = 1, y = 1;
cairo_matrix_transform_distance (&scaled_font->mat_inverse, &x, &y);
x = fabs(x);
y = fabs(y);
extents.width += x * 2;
extents.x_bearing -= x;
extents.height += y * 2;
extents.y_bearing -= y;
}
/* * We transform by the inverse transformation here. This will put our glyph * locations in the space in which we draw. Which is later transformed by * the transformation matrix that we use. This will transform the * glyph positions back to where they were before when drawing, but the * glyph shapes will be transformed by the transformation matrix.
*/
cairo_matrix_transform_point(&scaled_font->mat_inverse, &x, &y);
offset.advanceOffset = (FLOAT)x; /* Y-axis is inverted */
offset.ascenderOffset = -(FLOAT)y;
/* The list of glyph image formats this renderer is prepared to support. */
DWRITE_GLYPH_IMAGE_FORMATS supported_formats =
DWRITE_GLYPH_IMAGE_FORMATS_COLR |
DWRITE_GLYPH_IMAGE_FORMATS_SVG |
DWRITE_GLYPH_IMAGE_FORMATS_PNG |
DWRITE_GLYPH_IMAGE_FORMATS_JPEG |
DWRITE_GLYPH_IMAGE_FORMATS_TIFF |
DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8;
if (hr == DWRITE_E_NOCOLOR) { /* No color glyphs */
scaled_glyph->color_glyph = FALSE;
scaled_glyph->color_glyph_set = TRUE; return CAIRO_INT_STATUS_UNSUPPORTED;
}
if (FAILED(hr)) return _cairo_dwrite_error (hr, "TranslateColorGlyphRun failed");
/* We have a color glyph(s). Use Direct2D to render it to a bitmap */ if (!WICImagingFactory::Instance() || !D2DFactory::Instance()) return _cairo_dwrite_error (hr, "Instance failed");
cr = cairo_create (&surface->base);
cairo_set_source_rgb (cr, 1, 1, 1);
cairo_paint (cr);
status = (cairo_int_status_t)cairo_status (cr);
cairo_destroy(cr); if (status) goto FAIL;
/* * We transform by the inverse transformation here. This will put our glyph * locations in the space in which we draw. Which is later transformed by * the transformation matrix that we use. This will transform the * glyph positions back to where they were before when drawing, but the * glyph shapes will be transformed by the transformation matrix.
*/
cairo_matrix_transform_point(&scaled_font->mat_inverse, &x, &y);
offset.advanceOffset = (FLOAT)x; /* Y-axis is inverted */
offset.ascenderOffset = -(FLOAT)y;
RefPtr<IDWriteFontFace5> fontFace5; if (FAILED(face->dwriteface->QueryInterface(&fontFace5))) { /* If IDWriteFontFace5 is not available, assume this version of * DirectWrite does not support variations.
*/
*is_synthetic = FALSE; return CAIRO_INT_STATUS_SUCCESS;
}
if (!fontFace5->HasVariations()) {
*is_synthetic = FALSE; return CAIRO_INT_STATUS_SUCCESS;
}
UINT32 axis_count = fontResource->GetFontAxisCount();
DWRITE_FONT_AXIS_VALUE *axis_defaults = new DWRITE_FONT_AXIS_VALUE[axis_count];
DWRITE_FONT_AXIS_VALUE *axis_values = new DWRITE_FONT_AXIS_VALUE[axis_count];
hr = fontResource->GetDefaultFontAxisValues(axis_defaults, axis_count); if (FAILED(hr)) {
status = _cairo_dwrite_error (hr, "GetDefaultFontAxisValues failed"); goto cleanup;
}
hr = fontFace5->GetFontAxisValues(axis_values, axis_count); if (FAILED(hr)) {
status = _cairo_dwrite_error (hr, "GetFontAxisValues failed"); goto cleanup;
}
/* The DirectWrite documentation does not state if the tags of the returned * defaults and values arrays are in the same order. So assume they are not.
*/
*is_synthetic = FALSE;
status = CAIRO_INT_STATUS_SUCCESS; for (UINT32 i = 0; i< axis_count; i++) { for (UINT32 j = 0; j < axis_count; j++) { if (axis_values[i].axisTag == axis_defaults[j].axisTag) { if (axis_values[i].value != axis_defaults[j].value) {
*is_synthetic = TRUE; goto cleanup;
} break;
}
}
}
/** * cairo_dwrite_font_face_create_for_dwrite_fontface: * @dwrite_font_face: A pointer to an #IDWriteFontFace specifying the * DWrite font to use. * * Creates a new font for the DWrite font backend based on a * DWrite font face. This font can then be used with * cairo_set_font_face() or cairo_scaled_font_create(). * * Here is an example of how this function might be used: * <informalexample><programlisting><![CDATA[ * #include <cairo-dwrite.h> * #include <dwrite.h> * * IDWriteFactory* dWriteFactory = NULL; * HRESULT hr = DWriteCreateFactory( * DWRITE_FACTORY_TYPE_SHARED, * __uuidof(IDWriteFactory), * reinterpret_cast<IUnknown**>(&dWriteFactory)); * * IDWriteFontCollection *systemCollection; * hr = dWriteFactory->GetSystemFontCollection(&systemCollection); * * UINT32 idx; * BOOL found; * systemCollection->FindFamilyName(L"Segoe UI Emoji", &idx, &found); * * IDWriteFontFamily *family; * systemCollection->GetFontFamily(idx, &family); * * IDWriteFont *dwritefont; * DWRITE_FONT_WEIGHT weight = DWRITE_FONT_WEIGHT_NORMAL; * DWRITE_FONT_STYLE style = DWRITE_FONT_STYLE_NORMAL; * hr = family->GetFirstMatchingFont(weight, DWRITE_FONT_STRETCH_NORMAL, style, &dwritefont); * * IDWriteFontFace *dwriteface; * hr = dwritefont->CreateFontFace(&dwriteface); * * cairo_font_face_t *face; * face = cairo_dwrite_font_face_create_for_dwrite_fontface(dwriteface); * cairo_set_font_face(cr, face); * cairo_set_font_size(cr, 70); * cairo_move_to(cr, 100, 100); * cairo_show_text(cr, ""); * ]]></programlisting></informalexample> * * Note: When printing a DWrite font to a * #CAIRO_SURFACE_TYPE_WIN32_PRINTING surface, the printing surface * will substitute each DWrite font with a Win32 font created from the same * underlying font file. If the matching font file can not be found, * the #CAIRO_SURFACE_TYPE_WIN32_PRINTING surface will convert each * glyph to a filled path. If a DWrite font was not created from a system * font, it is recommended that the font used to create the DWrite * font be made available to GDI to avoid the undesirable fallback * to emitting paths. This can be achieved using the GDI font loading functions * such as AddFontMemResourceEx(). * * Return value: a newly created #cairo_font_face_t. Free with * cairo_font_face_destroy() when you are done using it. * * Since: 1.18
**/
cairo_font_face_t *
cairo_dwrite_font_face_create_for_dwrite_fontface (IDWriteFontFace *dwrite_font_face)
{
IDWriteFontFace *dwriteface = static_cast<IDWriteFontFace*>(dwrite_font_face); // Must do malloc and not C++ new, since Cairo frees this.
cairo_dwrite_font_face_t *face = (cairo_dwrite_font_face_t *)_cairo_malloc(sizeof(cairo_dwrite_font_face_t)); if (unlikely (face == NULL)) {
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_font_face_t*)&_cairo_font_face_nil;
}
/* Ensure IDWriteFactory4 is available before enabling color fonts */ if (DWriteFactory::Instance4()) {
RefPtr<IDWriteFontFace2> fontFace2; if (SUCCEEDED(dwriteface->QueryInterface(&fontFace2))) { if (fontFace2->IsColorFont())
face->have_color = true;
}
}
/** * cairo_dwrite_font_face_get_rendering_params: * @font_face: The #cairo_dwrite_font_face_t object to query * * Gets the #IDWriteRenderingParams object of @font_face. * * Return value: the #IDWriteRenderingParams object or %NULL if none. * * Since: 1.18
**/
IDWriteRenderingParams *
cairo_dwrite_font_face_get_rendering_params (cairo_font_face_t *font_face)
{
cairo_dwrite_font_face_t *dwface = reinterpret_cast<cairo_dwrite_font_face_t *>(font_face); return dwface->rendering_params;
}
/** * cairo_dwrite_font_face_set_rendering_params: * @font_face: The #cairo_dwrite_font_face_t object to modify * @params: The #IDWriteRenderingParams object * * Sets the #IDWriteRenderingParams object to @font_face. * This #IDWriteRenderingParams is used to render glyphs if default values of font options are used. * If non-defalut values of font options are specified when creating a #cairo_scaled_font_t, * cairo creates a new #IDWriteRenderingParams object for the #cairo_scaled_font_t object by overwriting the corresponding parameters. * * Since: 1.18
**/ void
cairo_dwrite_font_face_set_rendering_params (cairo_font_face_t *font_face, IDWriteRenderingParams *params)
{
cairo_dwrite_font_face_t *dwface = reinterpret_cast<cairo_dwrite_font_face_t *>(font_face); if (dwface->rendering_params)
dwface->rendering_params->Release();
dwface->rendering_params = params; if (dwface->rendering_params)
dwface->rendering_params->AddRef();
}
/** * cairo_dwrite_font_face_get_measuring_mode: * @font_face: The #cairo_dwrite_font_face_t object to query * * Gets the #DWRITE_MEASURING_MODE enum of @font_face. * * Return value: The #DWRITE_MEASURING_MODE enum of @font_face. * * Since: 1.18
**/
DWRITE_MEASURING_MODE
cairo_dwrite_font_face_get_measuring_mode (cairo_font_face_t *font_face)
{
cairo_dwrite_font_face_t *dwface = reinterpret_cast<cairo_dwrite_font_face_t *>(font_face); return dwface->measuring_mode;
}
/** * cairo_dwrite_font_face_set_measuring_mode: * @font_face: The #cairo_dwrite_font_face_t object to modify * @mode: The #DWRITE_MEASURING_MODE enum. * * Sets the #DWRITE_MEASURING_MODE enum to @font_face. * * Since: 1.18
**/ void
cairo_dwrite_font_face_set_measuring_mode (cairo_font_face_t *font_face, DWRITE_MEASURING_MODE mode)
{
cairo_dwrite_font_face_t *dwface = reinterpret_cast<cairo_dwrite_font_face_t *>(font_face);
dwface->measuring_mode = mode;
}
if (FAILED(hr)) { if (hr == E_OUTOFMEMORY) { return (cairo_int_status_t)CAIRO_STATUS_NO_MEMORY;
} else { return CAIRO_INT_STATUS_UNSUPPORTED;
}
}
/* * We set the number of pixels per DIP to 1.0. This is because we always want * to draw in device pixels, and not device independent pixels. On high DPI * systems this value will be higher than 1.0 and automatically upscale * fonts, we don't want this since we do our own upscaling for various reasons.
*/
rt->SetPixelsPerDip(1.0);
// XXX don't we need to set RenderingParams on this RenderTarget?
hr = rt->BindDC(surface->dc, &area); if (FAILED(hr)) return CAIRO_INT_STATUS_UNSUPPORTED;
// D2D uses 0x00RRGGBB not 0x00BBGGRR like COLORREF.
color = (color & 0xFF) << 16 |
(color & 0xFF00) |
(color & 0xFF0000) >> 16;
RefPtr<ID2D1SolidColorBrush> brush;
hr = rt->CreateSolidColorBrush(D2D1::ColorF(color, 1.0), &brush); if (FAILED(hr)) return CAIRO_INT_STATUS_UNSUPPORTED;
float x = 0, y = 0; if (transform) {
rt->SetTransform(D2D1::Matrix3x2F(transform->m11,
transform->m12,
transform->m21,
transform->m22,
transform->dx,
transform->dy));
}
rt->BeginDraw();
rt->DrawGlyphRun(D2D1::Point2F(0, 0), run, brush);
hr = rt->EndDraw(); if (transform) {
rt->SetTransform(D2D1::Matrix3x2F::Identity());
} if (FAILED(hr)) return CAIRO_INT_STATUS_UNSUPPORTED;
return CAIRO_INT_STATUS_SUCCESS;
}
/* Surface helper function */
cairo_int_status_t
_cairo_dwrite_show_glyphs_on_surface(void *surface,
cairo_operator_t op, const cairo_pattern_t *source,
cairo_glyph_t *glyphs, int num_glyphs,
cairo_scaled_font_t *scaled_font,
cairo_clip_t *clip)
{ // TODO: Check font & surface for types.
cairo_dwrite_scaled_font_t *dwritesf = reinterpret_cast<cairo_dwrite_scaled_font_t*>(scaled_font);
cairo_dwrite_font_face_t *dwriteff = reinterpret_cast<cairo_dwrite_font_face_t*>(scaled_font->font_face);
cairo_win32_surface_t *dst = reinterpret_cast<cairo_win32_surface_t*>(surface);
cairo_int_status_t status; /* We can only handle dwrite fonts */ if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_DWRITE) return CAIRO_INT_STATUS_UNSUPPORTED;
/* We can only handle opaque solid color sources */ if (!_cairo_pattern_is_opaque_solid(source)) return CAIRO_INT_STATUS_UNSUPPORTED;
/* We can only handle operator SOURCE or OVER with the destination
* having no alpha */ if (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_OVER) return CAIRO_INT_STATUS_UNSUPPORTED;
/* It is vital that dx values for dxy_buf are calculated from the delta of * _logical_ x coordinates (not user x coordinates) or else the sum of all * previous dx values may start to diverge from the current glyph's x * coordinate due to accumulated rounding error. As a result strings could
* be painted shorter or longer than expected. */
#ifdef CAIRO_TRY_D2D_TO_GDI
status = _dwrite_draw_glyphs_to_gdi_surface_d2d(dst,
mat,
&run,
color,
copyArea);
if (status == (cairo_status_t)CAIRO_INT_STATUS_UNSUPPORTED) { #endif
status = _dwrite_draw_glyphs_to_gdi_surface_gdi(dst,
mat,
&run,
color,
dwritesf,
copyArea);
#ifdef CAIRO_TRY_D2D_TO_GDI
} #endif
return status;
}
/* Check if a specific font table in a DWrite font and a scaled font is identical */ static cairo_int_status_t
compare_font_tables (cairo_dwrite_font_face_t *dwface,
cairo_scaled_font_t *scaled_font, unsignedlong tag,
cairo_bool_t *match)
{ unsignedlong size;
cairo_int_status_t status; unsignedchar *buffer = NULL; constvoid *dw_data;
UINT32 dw_size; void *dw_tableContext = NULL; BOOL dw_exists = FALSE;
HRESULT hr;
cleanup:
free (buffer); if (dw_tableContext)
dwface->dwriteface->ReleaseFontTable(dw_tableContext);
return status;
}
/* Check if a DWrite font and a scaled font areis identical * * DWrite does not allow accessing the entire font data using tag=0 so we compare * two of the font tables: * - 'name' table * - 'head' table since this contains the checksum for the entire font
*/ static cairo_int_status_t
font_tables_match (cairo_dwrite_font_face_t *dwface,
cairo_scaled_font_t *scaled_font,
cairo_bool_t *match)
{
cairo_int_status_t status;
status = compare_font_tables (dwface, scaled_font, TT_TAG_name, match); if (unlikely(status)) return status;
if (!*match) return CAIRO_INT_STATUS_SUCCESS;
status = compare_font_tables (dwface, scaled_font, TT_TAG_head, match); if (unlikely(status)) return status;
return CAIRO_INT_STATUS_SUCCESS;
}
/* * Helper for _cairo_win32_printing_surface_show_glyphs to create a win32 equivalent * of a dwrite scaled_font so that we can print using ExtTextOut instead of drawing * paths or blitting glyph bitmaps.
*/
cairo_int_status_t
_cairo_dwrite_scaled_font_create_win32_scaled_font (cairo_scaled_font_t *scaled_font,
cairo_scaled_font_t **new_font)
{ if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_DWRITE) { return CAIRO_INT_STATUS_UNSUPPORTED;
}
RefPtr<IDWriteGdiInterop> gdiInterop;
DWriteFactory::Instance()->GetGdiInterop(&gdiInterop); if (!gdiInterop) { return CAIRO_INT_STATUS_UNSUPPORTED;
}
LOGFONTW logfont; if (FAILED(gdiInterop->ConvertFontFaceToLOGFONT (dwface->dwriteface, &logfont))) { return CAIRO_INT_STATUS_UNSUPPORTED;
}
/* DWrite must have been using an outline font, so we want GDI to use the same, * even if there's also a bitmap face available
*/
logfont.lfOutPrecision = OUT_OUTLINE_PRECIS;
if (cairo_scaled_font_status(font)) {
cairo_scaled_font_destroy (font); return CAIRO_INT_STATUS_UNSUPPORTED;
}
cairo_bool_t match;
cairo_int_status_t status;
status = font_tables_match (dwface, font, &match); if (status) {
cairo_scaled_font_destroy (font); return status;
}
/* If the font tables aren't equal, then GDI may have failed to * find the right font and substituted a different font.
*/ if (!match) { #if 0 char *ps_name; char *font_name;
status = _cairo_truetype_read_font_name (scaled_font, &ps_name, &font_name);
printf("dwrite fontname: %s PS name: %s\n", font_name, ps_name);
free (font_name);
free (ps_name);
status = _cairo_truetype_read_font_name (font, &ps_name, &font_name);
printf("win32 fontname: %s PS name: %s\n", font_name, ps_name);
free (font_name);
free (ps_name); #endif
cairo_scaled_font_destroy (font); return CAIRO_INT_STATUS_UNSUPPORTED;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.