/* BASIC2026 portable PDF page-size normalizer.
 * Page rotation is preserved.  Content placement is calculated in displayed
 * coordinates, then converted back to the rotated page's raw coordinates. */
typedef struct {
    double width_pt,height_pt;
    int placement;                 /* 0 FIT, 1 FILL, 2 STRETCH, 3 CENTER */
    int transparent;
    unsigned int background;
} B2026PdfNormalizeSettings;

static int b2026_pdf_ns_paper(const char *name,double *w,double *h){
    if(name==NULL||*name=='\0'||b2026_strcasecmp(name,"A4")==0){*w=595.275590551181;*h=841.889763779528;}
    else if(b2026_strcasecmp(name,"A3")==0){*w=841.889763779528;*h=1190.55118110236;}
    else if(b2026_strcasecmp(name,"A5")==0){*w=419.527559055118;*h=595.275590551181;}
    else if(b2026_strcasecmp(name,"B4")==0){*w=728.503937007874;*h=1031.81102362205;}
    else if(b2026_strcasecmp(name,"B5")==0){*w=515.905511811024;*h=728.503937007874;}
    else if(b2026_strcasecmp(name,"LETTER")==0||strcmp(name,"レター")==0){*w=612.0;*h=792.0;}
    else if(b2026_strcasecmp(name,"LEGAL")==0||strcmp(name,"リーガル")==0){*w=612.0;*h=1008.0;}
    else return 0;return 1;
}

static int b2026_pdf_ns_settings(HashVariable *h,B2026PdfNormalizeSettings *s){
    const char *paper=b2026_pdf_tf_text(h,"用紙","paper"),*orientation=b2026_pdf_tf_text(h,"向き","orientation"),*place=b2026_pdf_tf_text(h,"配置","placement"),*background=b2026_pdf_tf_text(h,"背景","background");
    double w=0.0,hh=0.0,factor=b2026_pdf_tf_unit(h);int pw=0,ph=0,landscape=0,i;
    memset(s,0,sizeof(*s));s->placement=0;s->background=0xFFFFFFU;
    if(factor<=0.0||!b2026_pdf_tf_number(h,"横幅","width",&w,&pw)||!b2026_pdf_tf_number(h,"縦幅","height",&hh,&ph)||pw!=ph)return 0;
    if(pw){if(paper!=NULL||w<=0.0||hh<=0.0)return 0;s->width_pt=w*factor;s->height_pt=hh*factor;}
    else if(!b2026_pdf_ns_paper(paper,&s->width_pt,&s->height_pt))return 0;
    if(orientation!=NULL){
        if(b2026_strcasecmp(orientation,"LANDSCAPE")==0||strcmp(orientation,"横")==0||strcmp(orientation,"横向き")==0)landscape=1;
        else if(b2026_strcasecmp(orientation,"PORTRAIT")!=0&&strcmp(orientation,"縦")!=0&&strcmp(orientation,"縦向き")!=0)return 0;
        if((landscape&&s->width_pt<s->height_pt)||(!landscape&&s->width_pt>s->height_pt)){double q=s->width_pt;s->width_pt=s->height_pt;s->height_pt=q;}
    }
    if(place!=NULL){
        if(b2026_strcasecmp(place,"FIT")==0||strcmp(place,"合わせる")==0)s->placement=0;
        else if(b2026_strcasecmp(place,"FILL")==0||strcmp(place,"全面")==0)s->placement=1;
        else if(b2026_strcasecmp(place,"STRETCH")==0||strcmp(place,"引き伸ばす")==0)s->placement=2;
        else if(b2026_strcasecmp(place,"CENTER")==0||strcmp(place,"中央")==0||strcmp(place,"中央配置")==0)s->placement=3;
        else return 0;
    }
    if(background!=NULL){
        if(b2026_strcasecmp(background,"TRANSPARENT")==0||strcmp(background,"透明")==0)s->transparent=1;
        else {if(background[0]!='#'||strlen(background)!=7U)return 0;for(i=1;i<7;++i)if(!isxdigit((unsigned char)background[i]))return 0;s->background=(unsigned int)strtoul(background+1,NULL,16);}
    }
    return isfinite(s->width_pt)&&isfinite(s->height_pt)&&s->width_pt>0.0&&s->height_pt>0.0;
}

static void b2026_pdf_ns_map_point(int rot,const double source_box[4],const double target_box[4],double sx,double sy,double dx,double dy,double x,double y,double *ox,double *oy){
    double u,v;b2026_pdf_tf_raw_to_display(rot,source_box,x,y,&u,&v);u=dx+u*sx;v=dy+v*sy;b2026_pdf_tf_display_to_raw(rot,target_box,u,v,ox,oy);
}

static int b2026_pdf_ns_rect(B2026PdfDocument *doc,int oi,const char *key,double a,double b,double c,double d,double e,double f){
    B2026PdfObject *o;B2026PdfKeyRange r;size_t p;char *end;double v[4],x[4],y[4];int i;char value[256];
    if(oi<0||oi>=doc->object_count)return 0;o=&doc->objects[oi];if(!b2026_pdf_tf_find_key(o->body,o->body_length,key,&r))return 0;if(!r.found)return 1;p=b2026_pdf_tf_space(o->body,r.value_end,r.value_start);if(p>=r.value_end||o->body[p]!='[')return 1;++p;
    for(i=0;i<4;++i){p=b2026_pdf_tf_space(o->body,r.value_end,p);errno=0;v[i]=strtod((const char*)o->body+p,&end);if(end==(char*)o->body+p||errno==ERANGE||!isfinite(v[i]))return 1;p=(size_t)(end-(char*)o->body);}
    x[0]=a*v[0]+c*v[1]+e;y[0]=b*v[0]+d*v[1]+f;x[1]=a*v[2]+c*v[1]+e;y[1]=b*v[2]+d*v[1]+f;x[2]=a*v[0]+c*v[3]+e;y[2]=b*v[0]+d*v[3]+f;x[3]=a*v[2]+c*v[3]+e;y[3]=b*v[2]+d*v[3]+f;
    snprintf(value,sizeof(value),"[%.17g %.17g %.17g %.17g]",fmin(fmin(x[0],x[1]),fmin(x[2],x[3])),fmin(fmin(y[0],y[1]),fmin(y[2],y[3])),fmax(fmax(x[0],x[1]),fmax(x[2],x[3])),fmax(fmax(y[0],y[1]),fmax(y[2],y[3])));return b2026_pdf_tf_set_key(doc,oi,key,value,0);
}

static int b2026_pdf_ns_pairs(B2026PdfDocument *doc,int oi,const char *key,double a,double b,double c,double d,double e,double f){
    B2026PdfObject *o;B2026PdfKeyRange r;B2026PdfTextBuffer out;size_t p,end;double pending=0.0;int have=0;
    if(oi<0||oi>=doc->object_count)return 0;o=&doc->objects[oi];if(!b2026_pdf_tf_find_key(o->body,o->body_length,key,&r))return 0;if(!r.found)return 1;p=b2026_pdf_tf_space(o->body,r.value_end,r.value_start);if(p>=r.value_end||o->body[p]!='[')return 1;end=r.value_end;memset(&out,0,sizeof(out));
    while(p<end){char *q;double v;if(o->body[p]=='['||o->body[p]==']'){if(!b2026_pdf_text_append_char(&out,(char)o->body[p++]))goto fail;continue;}if(isspace(o->body[p])){++p;continue;}errno=0;v=strtod((const char*)o->body+p,&q);if(q!=(char*)o->body+p&&errno!=ERANGE&&isfinite(v)){if(!have){pending=v;have=1;}else{char pair[160];double x=a*pending+c*v+e,y=b*pending+d*v+f;snprintf(pair,sizeof(pair)," %.17g %.17g",x,y);if(!b2026_pdf_text_append_bytes(&out,pair,strlen(pair)))goto fail;have=0;}p=(size_t)(q-(char*)o->body);continue;}++p;}
    if(have)goto fail;if(!b2026_pdf_tf_set_key(doc,oi,key,out.data,0))goto fail;free(out.data);return 1;
fail:free(out.data);return 0;
}

static int b2026_pdf_ns_annotation_refs(B2026PdfDocument *doc,const unsigned char *s,size_t n,double a,double b,double c,double d,double e,double f){
    size_t p=0U;while(p<n){char *q;long number,generation;size_t r,u;int oi;if(!isdigit(s[p])){++p;continue;}number=strtol((const char*)s+p,&q,10);r=b2026_pdf_tf_space(s,n,(size_t)(q-(char*)s));if(r>=n||!isdigit(s[r])){++p;continue;}generation=strtol((const char*)s+r,&q,10);u=b2026_pdf_tf_space(s,n,(size_t)(q-(char*)s));if(u<n&&s[u]=='R'&&number>0&&number<=INT_MAX&&generation>=0&&generation<=INT_MAX&&(oi=b2026_pdf_object_lookup(doc,(int)number,(int)generation))>=0){if(!b2026_pdf_ns_rect(doc,oi,"/Rect",a,b,c,d,e,f)||!b2026_pdf_ns_pairs(doc,oi,"/QuadPoints",a,b,c,d,e,f)||!b2026_pdf_ns_pairs(doc,oi,"/Vertices",a,b,c,d,e,f)||!b2026_pdf_ns_pairs(doc,oi,"/L",a,b,c,d,e,f)||!b2026_pdf_ns_pairs(doc,oi,"/CL",a,b,c,d,e,f)||!b2026_pdf_ns_pairs(doc,oi,"/InkList",a,b,c,d,e,f))return 0;p=u+1U;}else ++p;}return 1;
}

static int b2026_pdf_ns_annotations(B2026PdfDocument *doc,int page,double a,double b,double c,double d,double e,double f){
    B2026PdfObject *o=&doc->objects[page];B2026PdfKeyRange r;int number,generation,oi;if(!b2026_pdf_tf_find_key(o->body,o->body_length,"/Annots",&r))return 0;if(!r.found)return 1;if(b2026_pdf_tf_ref(o->body+r.value_start,r.value_end-r.value_start,&number,&generation)&&(oi=b2026_pdf_object_lookup(doc,number,generation))>=0)return b2026_pdf_ns_annotation_refs(doc,doc->objects[oi].body,doc->objects[oi].body_length,a,b,c,d,e,f);return b2026_pdf_ns_annotation_refs(doc,o->body+r.value_start,r.value_end-r.value_start,a,b,c,d,e,f);
}

static void b2026_pdf_ns_box_affine(const double in[4],double a,double b,double c,double d,double e,double f,double out[4]){
    double x[4],y[4];x[0]=a*in[0]+c*in[1]+e;y[0]=b*in[0]+d*in[1]+f;x[1]=a*in[2]+c*in[1]+e;y[1]=b*in[2]+d*in[1]+f;x[2]=a*in[0]+c*in[3]+e;y[2]=b*in[0]+d*in[3]+f;x[3]=a*in[2]+c*in[3]+e;y[3]=b*in[2]+d*in[3]+f;out[0]=fmin(fmin(x[0],x[1]),fmin(x[2],x[3]));out[1]=fmin(fmin(y[0],y[1]),fmin(y[2],y[3]));out[2]=fmax(fmax(x[0],x[1]),fmax(x[2],x[3]));out[3]=fmax(fmax(y[0],y[1]),fmax(y[2],y[3]));
}

static int b2026_pdf_ns_page(B2026PdfDocument *doc,int page,const B2026PdfNormalizeSettings *s){
    int rot=b2026_pdf_page_inherited_integer(doc,page,"/Rotate",0),pre,post,k;double uu=1.0,source[4],target[4],sw,sh,tw,th,sx,sy,dx,dy,p0x,p0y,p1x,p1y,p2x,p2y,a,b,c,d,e,f;char payload[1024],box[160],rotation[32];const char *sub[3]={"/TrimBox","/BleedBox","/ArtBox"};
    if(rot%90!=0)return 0;rot=((rot%360)+360)%360;if(b2026_pdf_page_direct_number(doc,page,"/UserUnit",&uu)<=0)uu=1.0;if(!isfinite(uu)||uu<=0.0)return 0;if(!b2026_pdf_page_box(doc,page,"/CropBox",source)&&!b2026_pdf_page_box(doc,page,"/MediaBox",source))return 0;
    sw=(rot==90||rot==270)?source[3]-source[1]:source[2]-source[0];sh=(rot==90||rot==270)?source[2]-source[0]:source[3]-source[1];tw=s->width_pt/uu;th=s->height_pt/uu;if(sw<=0.0||sh<=0.0||tw<=0.0||th<=0.0)return 0;
    if(s->placement==2){sx=tw/sw;sy=th/sh;}else if(s->placement==3){sx=sy=1.0;}else{sx=sy=s->placement==1?fmax(tw/sw,th/sh):fmin(tw/sw,th/sh);}dx=(tw-sw*sx)/2.0;dy=(th-sh*sy)/2.0;
    target[0]=target[1]=0.0;if(rot==90||rot==270){target[2]=th;target[3]=tw;}else{target[2]=tw;target[3]=th;}
    b2026_pdf_ns_map_point(rot,source,target,sx,sy,dx,dy,source[0],source[1],&p0x,&p0y);b2026_pdf_ns_map_point(rot,source,target,sx,sy,dx,dy,source[0]+1.0,source[1],&p1x,&p1y);b2026_pdf_ns_map_point(rot,source,target,sx,sy,dx,dy,source[0],source[1]+1.0,&p2x,&p2y);a=p1x-p0x;b=p1y-p0y;c=p2x-p0x;d=p2y-p0y;e=p0x-a*source[0]-c*source[1];f=p0y-b*source[0]-d*source[1];if(fabs(a)<1.0e-12)a=0.0;if(fabs(b)<1.0e-12)b=0.0;if(fabs(c)<1.0e-12)c=0.0;if(fabs(d)<1.0e-12)d=0.0;if(fabs(e)<1.0e-12)e=0.0;if(fabs(f)<1.0e-12)f=0.0;
    /* PDF real numbers do not permit exponent notation.  Fixed decimals also
     * avoid tiny rotation round-off values being emitted as e-15 tokens. */
    if(s->transparent)snprintf(payload,sizeof(payload),"q %.12f %.12f %.12f %.12f re W n %.12f %.12f %.12f %.12f %.12f %.12f cm %.12f %.12f %.12f %.12f re W n",target[0],target[1],target[2]-target[0],target[3]-target[1],a,b,c,d,e,f,source[0],source[1],source[2]-source[0],source[3]-source[1]);
    else snprintf(payload,sizeof(payload),"q %.6f %.6f %.6f rg %.12f %.12f %.12f %.12f re f Q\nq %.12f %.12f %.12f %.12f re W n %.12f %.12f %.12f %.12f %.12f %.12f cm %.12f %.12f %.12f %.12f re W n",((s->background>>16)&255U)/255.0,((s->background>>8)&255U)/255.0,(s->background&255U)/255.0,target[0],target[1],target[2]-target[0],target[3]-target[1],target[0],target[1],target[2]-target[0],target[3]-target[1],a,b,c,d,e,f,source[0],source[1],source[2]-source[0],source[3]-source[1]);
    pre=b2026_pdf_tf_add_stream(doc,payload);post=b2026_pdf_tf_add_stream(doc,"Q");if(pre<0||post<0||!b2026_pdf_tf_append_contents(doc,page,pre,post)||!b2026_pdf_ns_annotations(doc,page,a,b,c,d,e,f))return 0;
    b2026_pdf_tf_box_text(box,target);if(!b2026_pdf_tf_set_key(doc,page,"/MediaBox",box,0)||!b2026_pdf_tf_set_key(doc,page,"/CropBox",box,0))return 0;
    for(k=0;k<3;++k){double old[4],q[4];if(b2026_pdf_page_box(doc,page,sub[k],old)){b2026_pdf_ns_box_affine(old,a,b,c,d,e,f,q);q[0]=fmax(q[0],target[0]);q[1]=fmax(q[1],target[1]);q[2]=fmin(q[2],target[2]);q[3]=fmin(q[3],target[3]);if(q[2]<=q[0]||q[3]<=q[1])memcpy(q,target,sizeof(q));b2026_pdf_tf_box_text(box,q);if(!b2026_pdf_tf_set_key(doc,page,sub[k],box,0))return 0;}}
    snprintf(rotation,sizeof(rotation),"%d",rot);return b2026_pdf_tf_set_key(doc,page,"/Rotate",rotation,0);
}

static int b2026_pdf_ns_verify(const char *path,const B2026PdfNormalizeSettings *s,int expected){
    B2026PdfDocument doc;int rc,i;memset(&doc,0,sizeof(doc));rc=b2026_pdf_load(&doc,path,0);if(rc<=0||doc.page_count!=expected){b2026_pdf_document_free(&doc);return 0;}for(i=0;i<doc.page_count;++i){double box[4],uu=1.0,w,h;int oi=doc.page_objects[i],rot=b2026_pdf_page_inherited_integer(&doc,oi,"/Rotate",0);if(!b2026_pdf_page_box(&doc,oi,"/CropBox",box)){b2026_pdf_document_free(&doc);return 0;}if(b2026_pdf_page_direct_number(&doc,oi,"/UserUnit",&uu)<=0)uu=1.0;w=((rot%180)==0?box[2]-box[0]:box[3]-box[1])*uu;h=((rot%180)==0?box[3]-box[1]:box[2]-box[0])*uu;if(fabs(w-s->width_pt)>0.02||fabs(h-s->height_pt)>0.02){b2026_pdf_document_free(&doc);return 0;}}b2026_pdf_document_free(&doc);return 1;
}

static const char *b2026_pdf_normalize_page_size(const char *operation,int argc,const B2026PdfArg *args){
    int separate,source_index,rc,i;const char *source;char *output=NULL,*stage=NULL;HashVariable *settings=NULL;B2026PdfNormalizeSettings values;B2026PdfDocument doc;B2026PdfPageList pages;memset(&doc,0,sizeof(doc));memset(&pages,0,sizeof(pages));(void)operation;
    if(argc!=2&&argc!=3)return b2026_pdf_fail(4200,"","NORMALIZEPAGESIZE requires path/settings or output/source/settings / ページサイズ統一の引数が正しくありません");separate=argc==3;source_index=separate?1:0;if(args[0].kind!=1||(separate&&args[1].kind!=1)||args[argc-1].kind!=3||!b2026_pdf_tf_hash(&args[argc-1],&settings)||!b2026_pdf_ns_settings(settings,&values))return b2026_pdf_fail(4200,"","invalid page-size normalization settings / ページサイズ統一の設定が正しくありません");
    source=args[source_index].text;output=b2026_pdf_output_path(args[0].text);if(source==NULL||output==NULL)goto fail;rc=b2026_pdf_load(&doc,source,1);if(rc<=0){b2026_pdf_fail(4201,source,b2026_pdf_load_error(rc));goto fail;}for(i=0;i<doc.page_count;++i)if(!b2026_pdf_ns_page(&doc,doc.page_objects[i],&values)){b2026_pdf_fail(4202,source,"page content, rotation, annotation, or page box could not be normalized / ページ内容・回転・注釈・ページボックスを統一できません");goto fail;}for(i=0;i<doc.page_count;++i)if(!b2026_pdf_pages_push(&pages,&doc,doc.page_objects[i],-1))goto memory_fail;
    stage=b2026_atomic_temp_path(output);if(stage==NULL||!b2026_pdf_write_output(stage,&doc,1,&pages,0,0,0)||!b2026_pdf_ns_verify(stage,&values,doc.page_count)){if(stage!=NULL)b2026_atomic_discard_file(stage);b2026_pdf_fail(4203,output,"normalized PDF could not be written or failed post-write verification / 統一PDFを保存できないか保存後検証に失敗しました");goto fail;}if(!b2026_atomic_commit_file(stage,output)){b2026_atomic_discard_file(stage);b2026_pdf_fail(4203,output,"verified PDF could not be committed / 検証済みPDFを保存先へ反映できません");goto fail;}free(stage);stage=NULL;
    {const char *result=runtime_temp_string_copy(output);free(output);free(pages.items);b2026_pdf_document_free(&doc);return result;}
memory_fail:b2026_pdf_fail(4199,source,"out of memory / メモリ不足です");
fail:if(stage!=NULL){b2026_atomic_discard_file(stage);free(stage);}free(output);free(pages.items);b2026_pdf_document_free(&doc);return runtime_temp_string_copy("");
}
