/* BASIC2026 portable extraction of embedded PDF image XObjects. */
typedef struct {
    int object_index,object_number,first_page,width,height,bpc,is_mask,reuse_count,extractable;
    unsigned char *pages;
    size_t stored_size;
    char colorspace[32],filters[160],format[16],status[40],path[1024];
} B2026PdfEmbeddedImage;
typedef struct {
    B2026PdfEmbeddedImage *items; int count,page_count;
} B2026PdfEmbeddedImageList;
typedef struct {
    const char *mode,*page_spec; double page_number; int page_kind,include_masks,compose_alpha,duplicates,inline_images,min_width,min_height;
} B2026PdfEmbeddedImageSettings;

static void b2026_pdf_ei_error(int code,const char *path,const char *detail){char m[4096];snprintf(m,sizeof(m),"PDF embedded-image operation failed: path=%s: %s",path!=NULL?path:"",detail!=NULL?detail:"");basic_set_error_logged(code,m,"PDF.EMBEDDEDIMAGE",current_source_path,current_line,"PDF");b2026_try_append_log_line("__PDF_ERROR.log",m);}
static void b2026_pdf_ei_pair_n(const char*t,const char*j,const char*e,double v){if(t!=NULL){hash_set_number(t,j,v);hash_set_number(t,e,v);}}
static void b2026_pdf_ei_pair_s(const char*t,const char*j,const char*e,const char*v){if(t!=NULL){hash_set_string(t,j,v!=NULL?v:"");hash_set_string(t,e,v!=NULL?v:"");}}
static int b2026_pdf_ei_bool(HashVariable*h,const char*j,const char*e,int d){HashEntry*x=b2026_pdf_tf_entry(h,j,e);if(x==NULL)return d;if(x->value.type!=TYPE_NUMBER||!isfinite(x->value.val.number))return -1;return x->value.val.number!=0.0;}
static int b2026_pdf_ei_settings(const B2026PdfArg *arg,B2026PdfEmbeddedImageSettings*s){HashVariable*h=NULL;HashEntry*e;double v;memset(s,0,sizeof(*s));s->mode="AUTO";s->page_spec="ALL";s->page_kind=1;s->include_masks=1;s->compose_alpha=1;s->inline_images=1;if(arg==NULL)return 1;if(!b2026_pdf_tf_hash(arg,&h))return 0;s->mode=b2026_pdf_tf_text(h,"抽出形式","format");if(s->mode==NULL)s->mode="AUTO";if(b2026_strcasecmp(s->mode,"AUTO")&&b2026_strcasecmp(s->mode,"RAW")&&b2026_strcasecmp(s->mode,"BOTH"))return 0;e=b2026_pdf_tf_entry(h,"対象ページ","pages");if(e!=NULL){if(e->value.type==TYPE_NUMBER){if(!isfinite(e->value.val.number)||floor(e->value.val.number)!=e->value.val.number||e->value.val.number<INT_MIN||e->value.val.number>INT_MAX)return 0;s->page_kind=0;s->page_number=e->value.val.number;}else{s->page_spec=hash_value_as_text(&e->value);if(s->page_spec==NULL)return 0;}}s->include_masks=b2026_pdf_ei_bool(h,"マスクを含める","includeMasks",1);s->compose_alpha=b2026_pdf_ei_bool(h,"透明度を合成","composeAlpha",1);s->duplicates=b2026_pdf_ei_bool(h,"重複画像を抽出","extractDuplicates",0);s->inline_images=b2026_pdf_ei_bool(h,"インライン画像","inlineImages",1);if(s->include_masks<0||s->compose_alpha<0||s->duplicates<0||s->inline_images<0)return 0;e=b2026_pdf_tf_entry(h,"最小横幅","minWidth");if(e!=NULL){if(e->value.type!=TYPE_NUMBER||(v=e->value.val.number)<0.0||v>INT_MAX||floor(v)!=v)return 0;s->min_width=(int)v;}e=b2026_pdf_tf_entry(h,"最小縦幅","minHeight");if(e!=NULL){if(e->value.type!=TYPE_NUMBER||(v=e->value.val.number)<0.0||v>INT_MAX||floor(v)!=v)return 0;s->min_height=(int)v;}return 1;}
static void b2026_pdf_ei_free(B2026PdfEmbeddedImageList*l){int i;if(l==NULL)return;for(i=0;i<l->count;++i)free(l->items[i].pages);free(l->items);memset(l,0,sizeof(*l));}
static int b2026_pdf_ei_stream(B2026PdfDocument*d,B2026PdfObject*o,const unsigned char**payload,size_t*n,size_t*dict_n);
static int b2026_pdf_ei_filter_names(B2026PdfObject*o,char names[8][24],int*count){const unsigned char*st=b2026_pdf_memfind(o->body,o->body_length,"stream",6U);B2026PdfKeyRange r;size_t p;if(st==NULL)return 0;*count=0;if(!b2026_pdf_tf_find_key(o->body,(size_t)(st-o->body),"/Filter",&r)||!r.found)return 1;p=r.value_start;while(p<r.value_end){size_t a,n;while(p<r.value_end&&o->body[p]!='/')++p;if(p>=r.value_end)break;a=++p;while(p<r.value_end&&!b2026_pdf_is_delim(o->body[p]))++p;n=p-a;if(n==0U)continue;if(*count>=8||n>=24U)return 0;memcpy(names[*count],o->body+a,n);names[*count][n]='\0';++*count;}return 1;}
static void b2026_pdf_ei_describe(B2026PdfDocument*d,B2026PdfObject*o,B2026PdfEmbeddedImage*x){char names[8][24];const unsigned char*payload,*cs=o->body;size_t stored,dict_n,cs_n=o->body_length;int n=0,i,cn,cg,ci;x->width=x->height=0;x->bpc=8;(void)b2026_pdf_dict_integer(o->body,o->body_length,"/Width",&x->width);(void)b2026_pdf_dict_integer(o->body,o->body_length,"/Height",&x->height);(void)b2026_pdf_dict_integer(o->body,o->body_length,"/BitsPerComponent",&x->bpc);if(b2026_pdf_ei_stream(d,o,&payload,&stored,&dict_n))x->stored_size=stored;x->is_mask=b2026_pdf_memfind(o->body,o->body_length,"/ImageMask true",15U)!=NULL;if(b2026_pdf_named_ref(o->body,o->body_length,"/ColorSpace",&cn,&cg)&&(ci=b2026_pdf_object_lookup(d,cn,cg))>=0){cs=d->objects[ci].body;cs_n=d->objects[ci].body_length;}if(b2026_pdf_memfind(cs,cs_n,"/DeviceRGB",10U)!=NULL)snprintf(x->colorspace,sizeof(x->colorspace),"DeviceRGB");else if(b2026_pdf_memfind(cs,cs_n,"/DeviceCMYK",11U)!=NULL)snprintf(x->colorspace,sizeof(x->colorspace),"DeviceCMYK");else if(b2026_pdf_memfind(cs,cs_n,"/DeviceGray",11U)!=NULL||x->is_mask)snprintf(x->colorspace,sizeof(x->colorspace),"DeviceGray");else if(b2026_pdf_memfind(cs,cs_n,"/Indexed",8U)!=NULL)snprintf(x->colorspace,sizeof(x->colorspace),"Indexed");else snprintf(x->colorspace,sizeof(x->colorspace),"Unknown");x->filters[0]='\0';if(b2026_pdf_ei_filter_names(o,names,&n))for(i=0;i<n;++i){if(i)strncat(x->filters,",",sizeof(x->filters)-strlen(x->filters)-1U);strncat(x->filters,names[i],sizeof(x->filters)-strlen(x->filters)-1U);}if(n==0)snprintf(x->filters,sizeof(x->filters),"None");snprintf(x->format,sizeof(x->format),"PNG");for(i=0;i<n;++i){if(!strcmp(names[i],"DCTDecode"))snprintf(x->format,sizeof(x->format),"JPEG");else if(!strcmp(names[i],"JPXDecode"))snprintf(x->format,sizeof(x->format),"JP2");else if(!strcmp(names[i],"CCITTFaxDecode"))snprintf(x->format,sizeof(x->format),"TIFF");else if(!strcmp(names[i],"JBIG2Decode"))snprintf(x->format,sizeof(x->format),"JBIG2");}x->extractable=x->width>0&&x->height>0&&x->bpc>0;snprintf(x->status,sizeof(x->status),x->extractable?"OK":"INVALID_DICTIONARY");}
static int b2026_pdf_ei_collect_objects(B2026PdfDocument*d,B2026PdfEmbeddedImageList*l,int**map){int i,c=0;int*m=(int*)malloc((size_t)d->object_count*sizeof(*m));if(m==NULL&&d->object_count)return 0;for(i=0;i<d->object_count;++i){m[i]=-1;if(b2026_pdf_dict_name_is(d->objects[i].body,d->objects[i].body_length,"/Subtype","Image"))++c;}l->items=(B2026PdfEmbeddedImage*)calloc((size_t)(c>0?c:1),sizeof(*l->items));if(l->items==NULL){free(m);return 0;}l->count=c;l->page_count=d->page_count;for(i=0,c=0;i<d->object_count;++i)if(b2026_pdf_dict_name_is(d->objects[i].body,d->objects[i].body_length,"/Subtype","Image")){B2026PdfEmbeddedImage*x=&l->items[c];m[i]=c;x->object_index=i;x->object_number=d->objects[i].old_number;x->pages=(unsigned char*)calloc((size_t)(d->page_count>0?d->page_count:1),1U);if(x->pages==NULL){free(m);return 0;}b2026_pdf_ei_describe(d,&d->objects[i],x);++c;}*map=m;return 1;}
static void b2026_pdf_ei_walk(B2026PdfDocument*d,int oi,int page,int*map,B2026PdfEmbeddedImageList*l,unsigned char*seen,int depth){B2026PdfObject*o;size_t p=0U,limit;if(depth>64||oi<0||oi>=d->object_count||seen[oi])return;seen[oi]=1U;if(map[oi]>=0){B2026PdfEmbeddedImage*x=&l->items[map[oi]];int sn,sg,si;if(!x->pages[page]){x->pages[page]=1U;++x->reuse_count;if(x->first_page==0)x->first_page=page+1;}if(b2026_pdf_named_ref(d->objects[oi].body,d->objects[oi].body_length,"/SMask",&sn,&sg)&&(si=b2026_pdf_object_lookup(d,sn,sg))>=0&&map[si]>=0){l->items[map[si]].is_mask=1;b2026_pdf_ei_walk(d,si,page,map,l,seen,depth+1);}return;}o=&d->objects[oi];limit=o->body_length;{const unsigned char*st=b2026_pdf_memfind(o->body,o->body_length,"stream",6U);if(st!=NULL)limit=(size_t)(st-o->body);}while(p<limit){int a,g,child;size_t after;if(b2026_pdf_sc_ref_at(o->body,limit,p,&a,&g,&after)){child=b2026_pdf_object_lookup(d,a,g);if(child>=0)b2026_pdf_ei_walk(d,child,page,map,l,seen,depth+1);p=after;}else ++p;}}
static int b2026_pdf_ei_map_pages(B2026PdfDocument*d,B2026PdfEmbeddedImageList*l,int*map){int p;for(p=0;p<d->page_count;++p){const unsigned char*v;size_t n;unsigned char*seen=(unsigned char*)calloc((size_t)d->object_count,1U);int a,g,oi;if(seen==NULL)return 0;if(b2026_pdf_tf_inherited_value(d,d->page_objects[p],"/Resources",&v,&n)){if(b2026_pdf_tf_ref(v,n,&a,&g)&&(oi=b2026_pdf_object_lookup(d,a,g))>=0)b2026_pdf_ei_walk(d,oi,p,map,l,seen,0);else{size_t q=0U;while(q<n){size_t after;if(b2026_pdf_sc_ref_at(v,n,q,&a,&g,&after)){oi=b2026_pdf_object_lookup(d,a,g);if(oi>=0)b2026_pdf_ei_walk(d,oi,p,map,l,seen,0);q=after;}else ++q;}}}if(b2026_pdf_named_ref(d->objects[d->page_objects[p]].body,d->objects[d->page_objects[p]].body_length,"/Thumb",&a,&g)&&(oi=b2026_pdf_object_lookup(d,a,g))>=0)b2026_pdf_ei_walk(d,oi,p,map,l,seen,0);free(seen);}return 1;}
static int b2026_pdf_ei_size_value(const unsigned char*s,size_t n,size_t*out){
    size_t p=b2026_pdf_tf_space(s,n,0U);char*end;unsigned long long v;
    if(p>=n||!isdigit(s[p]))return 0;errno=0;v=strtoull((const char*)s+p,&end,10);
    if(end==(char*)s+p||errno==ERANGE||v>(unsigned long long)SIZE_MAX)return 0;
    p=(size_t)(end-(char*)s);while(p<n&&isspace(s[p]))++p;if(p!=n)return 0;*out=(size_t)v;return 1;
}
static int b2026_pdf_ei_declared_length(B2026PdfDocument*d,B2026PdfObject*o,size_t dict_n,size_t*out){
    B2026PdfKeyRange r;int num,gen,oi;
    if(!b2026_pdf_tf_find_key(o->body,dict_n,"/Length",&r)||!r.found)return 0;
    if(b2026_pdf_tf_ref(o->body+r.value_start,r.value_end-r.value_start,&num,&gen)){
        oi=b2026_pdf_object_lookup(d,num,gen);if(oi<0)return 0;
        return b2026_pdf_ei_size_value(d->objects[oi].body,d->objects[oi].body_length,out);
    }
    return b2026_pdf_ei_size_value(o->body+r.value_start,r.value_end-r.value_start,out);
}
static int b2026_pdf_ei_stream(B2026PdfDocument*d,B2026PdfObject*o,const unsigned char**payload,size_t*n,size_t*dict_n){
    const unsigned char*st,*end;size_t declared,available;
    st=b2026_pdf_memfind(o->body,o->body_length,"stream",6U);if(st==NULL)return 0;
    *dict_n=(size_t)(st-o->body);*payload=st+6U;
    if(*payload<o->body+o->body_length&&**payload=='\r')++*payload;
    if(*payload<o->body+o->body_length&&**payload=='\n')++*payload;
    available=o->body_length-(size_t)(*payload-o->body);
    if(b2026_pdf_ei_declared_length(d,o,*dict_n,&declared)&&declared<=available){*n=declared;return 1;}
    end=b2026_pdf_memfind(*payload,available,"endstream",9U);if(end==NULL)return 0;
    *n=(size_t)(end-*payload);while(*n>0U&&((*payload)[*n-1U]=='\r'||(*payload)[*n-1U]=='\n'))--*n;return 1;
}
static int b2026_pdf_ei_runlength(const unsigned char*src,size_t n,unsigned char**out,size_t*out_n){B2026PdfTextBuffer b;size_t p=0U;memset(&b,0,sizeof(b));while(p<n){unsigned int c=src[p++];if(c==128U)break;if(c<=127U){size_t z=(size_t)c+1U;if(z>n-p||!b2026_pdf_text_append_bytes(&b,(const char*)src+p,z))goto fail;p+=z;}else{size_t z=257U-(size_t)c;if(p>=n)goto fail;while(z--)if(!b2026_pdf_text_append_char(&b,(char)src[p]))goto fail;++p;}if(b.used>=(size_t)B2026_PDF_EDIT_MAX_BYTES)goto fail;}*out=(unsigned char*)b2026_pdf_text_take(&b);*out_n=b.used;return *out!=NULL;fail:free(b.data);return 0;}
static int b2026_pdf_ei_decode_one(const char*name,const unsigned char*src,size_t n,unsigned char**out,size_t*out_n){size_t limit=(size_t)B2026_PDF_EDIT_MAX_BYTES-1U;if(!strcmp(name,"ASCIIHexDecode")||!strcmp(name,"AHx"))return b2026_pdf_sc_asciihex(src,n,out,out_n,limit);if(!strcmp(name,"ASCII85Decode")||!strcmp(name,"A85"))return b2026_pdf_sc_ascii85(src,n,out,out_n,limit);if(!strcmp(name,"FlateDecode")||!strcmp(name,"Fl"))return b2026_pdf_sc_flate(src,n,out,out_n,limit);if(!strcmp(name,"RunLengthDecode")||!strcmp(name,"RL"))return b2026_pdf_ei_runlength(src,n,out,out_n);return 0;}
static int b2026_pdf_ei_decode(B2026PdfDocument*d,B2026PdfObject*o,unsigned char**data,size_t*n,char*terminal,size_t ts){const unsigned char*p;size_t z,dn;char names[8][24];int count=0,i;if(!b2026_pdf_ei_stream(d,o,&p,&z,&dn)||!b2026_pdf_ei_filter_names(o,names,&count))return 0;*data=(unsigned char*)malloc(z+1U);if(*data==NULL)return 0;memcpy(*data,p,z);(*data)[z]=0;*n=z;terminal[0]='\0';for(i=0;i<count;++i){unsigned char*next=NULL;size_t nn=0U;if(!strcmp(names[i],"DCTDecode")||!strcmp(names[i],"DCT")||!strcmp(names[i],"JPXDecode")||!strcmp(names[i],"CCITTFaxDecode")||!strcmp(names[i],"CCF")||!strcmp(names[i],"JBIG2Decode")){snprintf(terminal,ts,"%s",names[i]);return i==count-1;}if(!b2026_pdf_ei_decode_one(names[i],*data,*n,&next,&nn)){free(*data);*data=NULL;return 0;}free(*data);*data=next;*n=nn;}return 1;}
static int b2026_pdf_ei_predict(B2026PdfObject*o,unsigned char**data,size_t*n,int w,int h,int comps,int bpc){int pred=1,cols=w,colors=comps,bits=bpc;size_t row,packed,y,x;unsigned char*src=*data,*out;(void)b2026_pdf_dict_integer(o->body,o->body_length,"/Predictor",&pred);(void)b2026_pdf_dict_integer(o->body,o->body_length,"/Columns",&cols);(void)b2026_pdf_dict_integer(o->body,o->body_length,"/Colors",&colors);(void)b2026_pdf_dict_integer(o->body,o->body_length,"/BitsPerComponent",&bits);if(pred<=1)return 1;if(cols<=0||colors<=0||bits<=0)return 0;row=((size_t)cols*(size_t)colors*(size_t)bits+7U)/8U;if(row==0U||row>(size_t)B2026_PDF_EDIT_MAX_BYTES/(size_t)h)return 0;if(pred==2){if(bits!=8||*n<row*(size_t)h)return 0;for(y=0;y<(size_t)h;++y)for(x=(size_t)colors;x<row;++x)src[y*row+x]=(unsigned char)(src[y*row+x]+src[y*row+x-(size_t)colors]);*n=row*(size_t)h;return 1;}if(pred<10||pred>15||*n<(row+1U)*(size_t)h)return 0;out=(unsigned char*)malloc(row*(size_t)h);if(out==NULL)return 0;packed=(size_t)colors*((size_t)bits+7U)/8U;if(packed==0U)packed=1U;for(y=0;y<(size_t)h;++y){unsigned char f=src[y*(row+1U)];unsigned char*cur=out+y*row;const unsigned char*in=src+y*(row+1U)+1U;const unsigned char*up=y?out+(y-1U)*row:NULL;for(x=0;x<row;++x){int a=x>=packed?cur[x-packed]:0,b=up?up[x]:0,c=(up&&x>=packed)?up[x-packed]:0,v=in[x];if(f==1)v+=a;else if(f==2)v+=b;else if(f==3)v+=(a+b)/2;else if(f==4){int p0=a+b-c,pa=abs(p0-a),pb=abs(p0-b),pc=abs(p0-c);v+=pa<=pb&&pa<=pc?a:(pb<=pc?b:c);}else if(f!=0){free(out);return 0;}cur[x]=(unsigned char)v;}}free(*data);*data=out;*n=row*(size_t)h;return 1;}
static int b2026_pdf_ei_pixels(B2026PdfDocument*doc,B2026PdfObject*o,B2026PdfEmbeddedImage*x,unsigned char**pix,int*comp){unsigned char*d=NULL,*out;size_t n,q,pixels;char term[32];int c;if(!b2026_pdf_ei_decode(doc,o,&d,&n,term,sizeof(term))||term[0]){free(d);return 0;}c=!strcmp(x->colorspace,"DeviceRGB")?3:(!strcmp(x->colorspace,"DeviceCMYK")?4:1);if(!b2026_pdf_ei_predict(o,&d,&n,x->width,x->height,c,x->bpc)){free(d);return 0;}if(x->width<=0||x->height<=0||(size_t)x->width>SIZE_MAX/(size_t)x->height){free(d);return 0;}pixels=(size_t)x->width*(size_t)x->height;if(x->bpc==8){if(n<pixels*(size_t)c){free(d);return 0;}if(c==4){out=(unsigned char*)malloc(pixels*3U);if(out==NULL){free(d);return 0;}for(q=0;q<pixels;++q){int C=d[q*4],M=d[q*4+1],Y=d[q*4+2],K=d[q*4+3];out[q*3]=(unsigned char)(255-(C+K>255?255:C+K));out[q*3+1]=(unsigned char)(255-(M+K>255?255:M+K));out[q*3+2]=(unsigned char)(255-(Y+K>255?255:Y+K));}free(d);d=out;c=3;}*pix=d;*comp=c;return 1;}if(x->bpc==1&&c==1){size_t row=((size_t)x->width+7U)/8U;if(n<row*(size_t)x->height){free(d);return 0;}out=(unsigned char*)malloc(pixels);if(out==NULL){free(d);return 0;}for(q=0;q<pixels;++q){size_t yy=q/(size_t)x->width,xx=q%(size_t)x->width;out[q]=(d[yy*row+xx/8U]&(0x80U>>(xx&7U)))?255U:0U;}free(d);*pix=out;*comp=1;return 1;}free(d);return 0;}
static int b2026_pdf_ei_png(const char*path,const unsigned char*data,int w,int h,int comp){char detail[512],*tmp;int ok=0;if(!saveimage_ensure_directories_safe(path,detail,sizeof(detail)))return 0;tmp=b2026_atomic_temp_path(path);if(tmp==NULL)return 0;if(stbi_write_png(tmp,w,h,comp,data,w*comp))ok=b2026_atomic_commit_file(tmp,path);if(!ok)b2026_atomic_discard_file(tmp);free(tmp);return ok;}
static void b2026_pdf_ei_u16(unsigned char*p,unsigned int v){p[0]=(unsigned char)v;p[1]=(unsigned char)(v>>8);}static void b2026_pdf_ei_u32(unsigned char*p,unsigned long v){p[0]=(unsigned char)v;p[1]=(unsigned char)(v>>8);p[2]=(unsigned char)(v>>16);p[3]=(unsigned char)(v>>24);}
static int b2026_pdf_ei_ccitt_tiff(const char*path,const unsigned char*data,size_t n,int w,int h,B2026PdfObject*o){unsigned char*b;size_t ifd=8U,entries=10U,header=ifd+2U+entries*12U+4U,offset=header;int k=-1,black=0,i=0;(void)b2026_pdf_dict_integer(o->body,o->body_length,"/K",&k);black=b2026_pdf_memfind(o->body,o->body_length,"/BlackIs1 true",14U)!=NULL;if(n>SIZE_MAX-header)return 0;b=(unsigned char*)calloc(header+n,1U);if(b==NULL)return 0;b[0]='I';b[1]='I';b2026_pdf_ei_u16(b+2,42);b2026_pdf_ei_u32(b+4,(unsigned long)ifd);b2026_pdf_ei_u16(b+ifd,(unsigned int)entries);
#define EI_T(TAG,TYPE,COUNT,VALUE) do{unsigned char*e=b+ifd+2U+(size_t)i++*12U;b2026_pdf_ei_u16(e,TAG);b2026_pdf_ei_u16(e+2,TYPE);b2026_pdf_ei_u32(e+4,COUNT);b2026_pdf_ei_u32(e+8,VALUE);}while(0)
EI_T(256,4,1,(unsigned long)w);EI_T(257,4,1,(unsigned long)h);EI_T(258,3,1,1);EI_T(259,3,1,k<0?4:3);EI_T(262,3,1,black?1:0);EI_T(273,4,1,(unsigned long)offset);EI_T(277,3,1,1);EI_T(278,4,1,(unsigned long)h);EI_T(279,4,1,(unsigned long)n);EI_T(296,3,1,2);
#undef EI_T
memcpy(b+offset,data,n);i=b2026_pdf_attachment_write(path,b,header+n);free(b);return i;}
static char*b2026_pdf_ei_name(const char*dir,int ordinal,B2026PdfEmbeddedImage*x,const char*ext,const char*suffix){char n[256];snprintf(n,sizeof(n),"image_%04d_p%04d_obj%04d%s.%s",ordinal,x->first_page,x->object_number,suffix!=NULL?suffix:"",ext);return b2026_pdf_attachment_join(dir,n);}
static int b2026_pdf_ei_page_selected(B2026PdfEmbeddedImage*x,unsigned char*selected,int pages){int i;for(i=0;i<pages;++i)if(x->pages[i]&&selected[i])return 1;return 0;}
static void b2026_pdf_ei_info_fields(const char*t,B2026PdfEmbeddedImageList*l,int success,int saved){int i,shown=0,extractable=0,masks=0;char k[128],pages[1024];b2026_pdf_ei_pair_n(t,"成功","ok",success?1.0:0.0);b2026_pdf_ei_pair_n(t,"画像数","count",(double)l->count);b2026_pdf_ei_pair_n(t,"保存数","savedCount",(double)saved);for(i=0;i<l->count;++i){B2026PdfEmbeddedImage*x=&l->items[i];int p,used=0;++shown;if(x->extractable)++extractable;if(x->is_mask)++masks;pages[0]='\0';for(p=0;p<l->page_count;++p)if(x->pages[p]){char z[32];snprintf(z,sizeof(z),"%s%d",used?",":"",p+1);strncat(pages,z,sizeof(pages)-strlen(pages)-1U);used=1;}
#define EI_N(J,E,V) do{snprintf(k,sizeof(k),"%d,%s",shown,J);hash_set_number(t,k,(double)(V));snprintf(k,sizeof(k),"%d,%s",shown,E);hash_set_number(t,k,(double)(V));}while(0)
#define EI_S(J,E,V) do{snprintf(k,sizeof(k),"%d,%s",shown,J);hash_set_string(t,k,(V));snprintf(k,sizeof(k),"%d,%s",shown,E);hash_set_string(t,k,(V));}while(0)
EI_N("ページ","page",x->first_page);EI_N("オブジェクト","object",x->object_number);EI_N("横幅","width",x->width);EI_N("縦幅","height",x->height);EI_N("ビット数","bitsPerComponent",x->bpc);EI_N("マスク","mask",x->is_mask);EI_N("再利用数","reuseCount",x->reuse_count);EI_N("抽出可能","extractable",x->extractable);EI_N("格納サイズ","storedSize",x->stored_size);EI_S("ページ一覧","pages",pages);EI_S("色空間","colorSpace",x->colorspace);EI_S("フィルター","filter",x->filters);EI_S("形式","format",x->format);EI_S("状態","status",x->status);EI_S("保存先","path",x->path);
#undef EI_N
#undef EI_S
}b2026_pdf_ei_pair_n(t,"抽出可能数","extractableCount",(double)extractable);b2026_pdf_ei_pair_n(t,"マスク数","maskCount",(double)masks);b2026_pdf_ei_pair_n(t,"インライン画像数","inlineCount",0.0);}
static int b2026_pdf_ei_prepare(const char*path,const B2026PdfArg*settings_arg,B2026PdfDocument*d,B2026PdfEmbeddedImageList*l,B2026PdfEmbeddedImageSettings*s,unsigned char**selected){int rc,*map=NULL;B2026PdfArg a;memset(d,0,sizeof(*d));memset(l,0,sizeof(*l));if(!b2026_pdf_ei_settings(settings_arg,s))return -3;rc=b2026_pdf_load(d,path,0);if(rc<=0)return rc;if(!b2026_pdf_ei_collect_objects(d,l,&map)||!b2026_pdf_ei_map_pages(d,l,map)){free(map);return -3;}free(map);memset(&a,0,sizeof(a));a.kind=s->page_kind;if(a.kind==0)a.number=s->page_number;else a.text=s->page_spec;if(!b2026_pdf_parse_page_spec(&a,d->page_count,selected))return -3;return 1;}
static int b2026_pdf_embedded_image_info(const char*target,int argc,const B2026PdfArg*args){B2026PdfDocument d;B2026PdfEmbeddedImageList l;B2026PdfEmbeddedImageSettings s;unsigned char*sel=NULL;HashVariable*h;int rc;if(target==NULL||(h=ensure_local_hash(get_hash_frame(target),target))==NULL)return 0;clear_hash_entries(h);if(argc<1||argc>2||args[0].kind!=1||args[0].text==NULL){b2026_pdf_ei_error(4380,"","invalid embedded-image information parameters / 埋込画像情報の引数が不正です");return 0;}rc=b2026_pdf_ei_prepare(args[0].text,argc==2?&args[1]:NULL,&d,&l,&s,&sel);if(rc<=0){b2026_pdf_ei_pair_n(target,"成功","ok",0.0);b2026_pdf_ei_pair_s(target,"状態","status",rc==-2?"ENCRYPTED":"FAILED");b2026_pdf_ei_error(4381,args[0].text,rc==-2?"encrypted PDF is not supported / 暗号化PDFには対応していません":"PDF could not be read or settings are invalid / PDFを読み込めないか設定が不正です");b2026_pdf_ei_free(&l);b2026_pdf_document_free(&d);free(sel);return 0;}b2026_pdf_ei_info_fields(target,&l,1,0);b2026_pdf_ei_pair_s(target,"状態","status","OK");b2026_pdf_ei_free(&l);b2026_pdf_document_free(&d);free(sel);return 1;}
static double b2026_pdf_embedded_image_extract(const char*array_target,const char*hash_target,int argc,const B2026PdfArg*args){B2026PdfDocument d;B2026PdfEmbeddedImageList l;B2026PdfEmbeddedImageSettings s;unsigned char*sel=NULL;char**paths=NULL;int rc,i,saved=0,ordinal=0;if(array_target!=NULL&&!b2026_pdf_attachment_array_commit(array_target,NULL,0)){b2026_pdf_ei_error(4385,"","out of memory while initializing the result array / 結果配列の初期化に必要なメモリを確保できません");return -1.0;}if(hash_target!=NULL){HashVariable*h=ensure_local_hash(get_hash_frame(hash_target),hash_target);if(h!=NULL)clear_hash_entries(h);}if(argc<2||argc>3||args[0].kind!=1||args[1].kind!=1||args[0].text==NULL||args[1].text==NULL){b2026_pdf_ei_error(4382,"","invalid embedded-image extraction parameters / 埋込画像抽出の引数が不正です");return -1.0;}rc=b2026_pdf_ei_prepare(args[0].text,argc==3?&args[2]:NULL,&d,&l,&s,&sel);if(rc<=0){b2026_pdf_ei_error(4383,args[0].text,rc==-2?"encrypted PDF is not supported / 暗号化PDFには対応していません":"PDF could not be read or settings are invalid / PDFを読み込めないか設定が不正です");b2026_pdf_ei_free(&l);b2026_pdf_document_free(&d);free(sel);return -1.0;}paths=(char**)calloc((size_t)(l.count*2+1),sizeof(*paths));if(paths==NULL)goto oom;for(i=0;i<l.count;++i){B2026PdfEmbeddedImage*x=&l.items[i];B2026PdfObject*o=&d.objects[x->object_index];const unsigned char*raw;size_t raw_n,dict_n;int want_auto=b2026_strcasecmp(s.mode,"RAW")!=0,want_raw=b2026_strcasecmp(s.mode,"AUTO")!=0;if(!b2026_pdf_ei_page_selected(x,sel,d.page_count)||(!s.include_masks&&x->is_mask)||x->width<s.min_width||x->height<s.min_height)continue;++ordinal;if(!b2026_pdf_ei_stream(&d,o,&raw,&raw_n,&dict_n)){snprintf(x->status,sizeof(x->status),"DAMAGED_STREAM");continue;}x->stored_size=raw_n;if(want_auto){unsigned char*decoded=NULL,*pix=NULL;size_t n=0U;char term[32],*dest=NULL;int comp=0,ok=0;if(b2026_pdf_ei_decode(&d,o,&decoded,&n,term,sizeof(term))){if(!strcmp(term,"DCTDecode")||!strcmp(term,"DCT")){dest=b2026_pdf_ei_name(args[1].text,ordinal,x,"jpg",NULL);ok=dest!=NULL&&b2026_pdf_attachment_write(dest,decoded,n);}else if(!strcmp(term,"JPXDecode")){dest=b2026_pdf_ei_name(args[1].text,ordinal,x,"jp2",NULL);ok=dest!=NULL&&b2026_pdf_attachment_write(dest,decoded,n);}else if(!strcmp(term,"CCITTFaxDecode")||!strcmp(term,"CCF")){dest=b2026_pdf_ei_name(args[1].text,ordinal,x,"tif",NULL);ok=dest!=NULL&&b2026_pdf_ei_ccitt_tiff(dest,decoded,n,x->width,x->height,o);}else if(term[0]=='\0'){free(decoded);decoded=NULL;if(b2026_pdf_ei_pixels(&d,o,x,&pix,&comp)){dest=b2026_pdf_ei_name(args[1].text,ordinal,x,"png",NULL);ok=dest!=NULL&&b2026_pdf_ei_png(dest,pix,x->width,x->height,comp);}}}free(decoded);free(pix);if(ok){paths[saved++]=dest;snprintf(x->path,sizeof(x->path),"%s",dest);snprintf(x->status,sizeof(x->status),"OK");}else{free(dest);snprintf(x->status,sizeof(x->status),"UNSUPPORTED_OR_DAMAGED");b2026_pdf_ei_error(4384,args[0].text,"an embedded image could not be decoded; processing continued / 埋込画像を展開できなかったため処理を継続しました");}}if(want_raw){char*dest=b2026_pdf_ei_name(args[1].text,ordinal,x,"bin",want_auto?"_raw":NULL);if(dest!=NULL&&b2026_pdf_attachment_write(dest,raw,raw_n)){paths[saved++]=dest;if(!want_auto)snprintf(x->path,sizeof(x->path),"%s",dest);}else free(dest);}}
if(array_target!=NULL&&!b2026_pdf_attachment_array_commit(array_target,paths,saved))goto oom;if(hash_target!=NULL){b2026_pdf_ei_info_fields(hash_target,&l,1,saved);b2026_pdf_ei_pair_s(hash_target,"状態","status","OK");}for(i=0;i<saved;++i)free(paths[i]);free(paths);b2026_pdf_ei_free(&l);b2026_pdf_document_free(&d);free(sel);return (double)saved;
oom:b2026_pdf_ei_error(4385,args[0].text,"out of memory / メモリ不足です");if(paths!=NULL)for(i=0;i<saved;++i)free(paths[i]);free(paths);b2026_pdf_ei_free(&l);b2026_pdf_document_free(&d);free(sel);return -1.0;}
